From eb3fd811544562a37a1d1f6f5f4285ed68847968 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 29 Jun 2021 21:30:33 +0800 Subject: [PATCH] querystring performance --- convert/time.go | 30 +++++++++++++++++++ scan.go | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ session_query.go | 39 +++++------------------- 3 files changed, 116 insertions(+), 31 deletions(-) create mode 100644 convert/time.go create mode 100644 scan.go diff --git a/convert/time.go b/convert/time.go new file mode 100644 index 00000000..8901279b --- /dev/null +++ b/convert/time.go @@ -0,0 +1,30 @@ +// Copyright 2021 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package convert + +import ( + "fmt" + "time" +) + +// String2Time converts a string to time with original location +func String2Time(s string, originalLocation *time.Location, convertedLocation *time.Location) (*time.Time, error) { + if len(s) == 19 { + dt, err := time.ParseInLocation("2006-01-02 15:04:05", s, originalLocation) + if err != nil { + return nil, err + } + dt = dt.In(convertedLocation) + return &dt, nil + } else if len(s) == 20 && s[10] == 'T' && s[19] == 'Z' { + dt, err := time.ParseInLocation("2006-01-02T15:04:05Z", s, originalLocation) + if err != nil { + return nil, err + } + dt = dt.In(convertedLocation) + return &dt, nil + } + return nil, fmt.Errorf("unsupported convertion from %s to time", s) +} diff --git a/scan.go b/scan.go new file mode 100644 index 00000000..e79d6a14 --- /dev/null +++ b/scan.go @@ -0,0 +1,78 @@ +// Copyright 2021 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "database/sql" + "time" + + "xorm.io/xorm/convert" + "xorm.io/xorm/core" +) + +func (session *Session) scan(rows *core.Rows, types []*sql.ColumnType, v ...interface{}) error { + var v2 = make([]interface{}, 0, len(v)) + var turnBackIdxes = make([]int, 0, 5) + for i, vv := range v { + switch vv.(type) { + case *time.Time: + v2 = append(v2, &sql.NullString{}) + turnBackIdxes = append(turnBackIdxes, i) + case *sql.NullTime: + v2 = append(v2, &sql.NullString{}) + turnBackIdxes = append(turnBackIdxes, i) + default: + v2 = append(v2, v[i]) + } + } + if err := rows.Scan(v2...); err != nil { + return err + } + for _, i := range turnBackIdxes { + switch t := v[i].(type) { + case *time.Time: + var s = *(v2[i].(*sql.NullString)) + if !s.Valid { + break + } + dt, err := convert.String2Time(s.String, session.engine.DatabaseTZ, session.engine.TZLocation) + if err != nil { + return err + } + *t = *dt + case *sql.NullTime: + var s = *(v2[i].(*sql.NullString)) + if !s.Valid { + break + } + dt, err := convert.String2Time(s.String, session.engine.DatabaseTZ, session.engine.TZLocation) + if err != nil { + return err + } + t.Time = *dt + t.Valid = true + } + } + return nil +} + +func (engine *Engine) row2mapStr(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]string, error) { + var scanResults = make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { + var s sql.NullString + scanResults[i] = &s + } + + if err := rows.Scan(scanResults...); err != nil { + return nil, err + } + + result := make(map[string]string, len(fields)) + for ii, key := range fields { + s := scanResults[ii].(*sql.NullString) + result[key] = s.String + } + return result, nil +} diff --git a/session_query.go b/session_query.go index 12136466..eafe8002 100644 --- a/session_query.go +++ b/session_query.go @@ -75,34 +75,6 @@ func value2String(rawValue *reflect.Value) (str string, err error) { return } -func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) { - result := make(map[string]string) - scanResultContainers := make([]interface{}, len(fields)) - for i := 0; i < len(fields); i++ { - var scanResultContainer interface{} - scanResultContainers[i] = &scanResultContainer - } - if err := rows.Scan(scanResultContainers...); err != nil { - return nil, err - } - - for ii, key := range fields { - rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) - // if row is null then as empty string - if rawValue.Interface() == nil { - result[key] = "" - continue - } - - if data, err := value2String(&rawValue); err == nil { - result[key] = data - } else { - return nil, err - } - } - return result, nil -} - func row2sliceStr(rows *core.Rows, fields []string) (results []string, err error) { result := make([]string, 0, len(fields)) scanResultContainers := make([]interface{}, len(fields)) @@ -131,13 +103,18 @@ func row2sliceStr(rows *core.Rows, fields []string) (results []string, err error return result, nil } -func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) { +func (session *Session) rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) { fields, err := rows.Columns() if err != nil { return nil, err } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + for rows.Next() { - result, err := row2mapStr(rows, fields) + result, err := session.engine.row2mapStr(rows, types, fields) if err != nil { return nil, err } @@ -180,7 +157,7 @@ func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]stri } defer rows.Close() - return rows2Strings(rows) + return session.rows2Strings(rows) } // QuerySliceString runs a raw sql and return records as [][]string