Improve QueryString performance (#1962)
As title. Reviewed-on: https://gitea.com/xorm/xorm/pulls/1962 Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
8f8195a86b
commit
65846bacc3
10
engine.go
10
engine.go
|
@ -444,7 +444,7 @@ func (engine *Engine) DumpTables(tables []*schemas.Table, w io.Writer, tp ...sch
|
||||||
return engine.dumpTables(tables, w, tp...)
|
return engine.dumpTables(tables, w, tp...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatColumnValue(dstDialect dialects.Dialect, d interface{}, col *schemas.Column) string {
|
func formatColumnValue(dbLocation *time.Location, dstDialect dialects.Dialect, d interface{}, col *schemas.Column) string {
|
||||||
if d == nil {
|
if d == nil {
|
||||||
return "NULL"
|
return "NULL"
|
||||||
}
|
}
|
||||||
|
@ -473,10 +473,8 @@ func formatColumnValue(dstDialect dialects.Dialect, d interface{}, col *schemas.
|
||||||
|
|
||||||
return "'" + strings.Replace(v, "'", "''", -1) + "'"
|
return "'" + strings.Replace(v, "'", "''", -1) + "'"
|
||||||
} else if col.SQLType.IsTime() {
|
} else if col.SQLType.IsTime() {
|
||||||
if dstDialect.URI().DBType == schemas.MSSQL && col.SQLType.Name == schemas.DateTime {
|
|
||||||
if t, ok := d.(time.Time); ok {
|
if t, ok := d.(time.Time); ok {
|
||||||
return "'" + t.UTC().Format("2006-01-02 15:04:05") + "'"
|
return "'" + t.In(dbLocation).Format("2006-01-02 15:04:05") + "'"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var v = fmt.Sprintf("%s", d)
|
var v = fmt.Sprintf("%s", d)
|
||||||
if strings.HasSuffix(v, " +0000 UTC") {
|
if strings.HasSuffix(v, " +0000 UTC") {
|
||||||
|
@ -653,7 +651,7 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch
|
||||||
}
|
}
|
||||||
|
|
||||||
field := dataStruct.FieldByIndex(col.FieldIndex)
|
field := dataStruct.FieldByIndex(col.FieldIndex)
|
||||||
temp += "," + formatColumnValue(dstDialect, field.Interface(), col)
|
temp += "," + formatColumnValue(engine.DatabaseTZ, dstDialect, field.Interface(), col)
|
||||||
}
|
}
|
||||||
_, err = io.WriteString(w, temp[1:]+");\n")
|
_, err = io.WriteString(w, temp[1:]+");\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -680,7 +678,7 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch
|
||||||
return errors.New("unknow column error")
|
return errors.New("unknow column error")
|
||||||
}
|
}
|
||||||
|
|
||||||
temp += "," + formatColumnValue(dstDialect, d, col)
|
temp += "," + formatColumnValue(engine.DatabaseTZ, dstDialect, d, col)
|
||||||
}
|
}
|
||||||
_, err = io.WriteString(w, temp[1:]+");\n")
|
_, err = io.WriteString(w, temp[1:]+");\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -406,16 +406,16 @@ func TestFindMapPtrString(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindBit(t *testing.T) {
|
func TestFindBool(t *testing.T) {
|
||||||
type FindBitStruct struct {
|
type FindBoolStruct struct {
|
||||||
Id int64
|
Id int64
|
||||||
Msg bool `xorm:"bit"`
|
Msg bool
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, PrepareEngine())
|
assert.NoError(t, PrepareEngine())
|
||||||
assertSync(t, new(FindBitStruct))
|
assertSync(t, new(FindBoolStruct))
|
||||||
|
|
||||||
cnt, err := testEngine.Insert([]FindBitStruct{
|
cnt, err := testEngine.Insert([]FindBoolStruct{
|
||||||
{
|
{
|
||||||
Msg: false,
|
Msg: false,
|
||||||
},
|
},
|
||||||
|
@ -426,14 +426,13 @@ func TestFindBit(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 2, cnt)
|
assert.EqualValues(t, 2, cnt)
|
||||||
|
|
||||||
var results = make([]FindBitStruct, 0, 2)
|
var results = make([]FindBoolStruct, 0, 2)
|
||||||
err = testEngine.Find(&results)
|
err = testEngine.Find(&results)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 2, len(results))
|
assert.EqualValues(t, 2, len(results))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindMark(t *testing.T) {
|
func TestFindMark(t *testing.T) {
|
||||||
|
|
||||||
type Mark struct {
|
type Mark struct {
|
||||||
Mark1 string `xorm:"VARCHAR(1)"`
|
Mark1 string `xorm:"VARCHAR(1)"`
|
||||||
Mark2 string `xorm:"VARCHAR(1)"`
|
Mark2 string `xorm:"VARCHAR(1)"`
|
||||||
|
@ -468,7 +467,7 @@ func TestFindAndCountOneFunc(t *testing.T) {
|
||||||
type FindAndCountStruct struct {
|
type FindAndCountStruct struct {
|
||||||
Id int64
|
Id int64
|
||||||
Content string
|
Content string
|
||||||
Msg bool `xorm:"bit"`
|
Msg bool
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, PrepareEngine())
|
assert.NoError(t, PrepareEngine())
|
||||||
|
|
|
@ -168,6 +168,9 @@ func TestInsertAutoIncr(t *testing.T) {
|
||||||
assert.Greater(t, user.Uid, int64(0))
|
assert.Greater(t, user.Uid, int64(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInsertDefault(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareEngine())
|
||||||
|
|
||||||
type DefaultInsert struct {
|
type DefaultInsert struct {
|
||||||
Id int64
|
Id int64
|
||||||
Status int `xorm:"default -1"`
|
Status int `xorm:"default -1"`
|
||||||
|
@ -176,9 +179,6 @@ type DefaultInsert struct {
|
||||||
Updated time.Time `xorm:"updated"`
|
Updated time.Time `xorm:"updated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertDefault(t *testing.T) {
|
|
||||||
assert.NoError(t, PrepareEngine())
|
|
||||||
|
|
||||||
di := new(DefaultInsert)
|
di := new(DefaultInsert)
|
||||||
err := testEngine.Sync2(di)
|
err := testEngine.Sync2(di)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -195,6 +195,9 @@ func TestInsertDefault(t *testing.T) {
|
||||||
assert.EqualValues(t, di2.Created.Unix(), di.Created.Unix())
|
assert.EqualValues(t, di2.Created.Unix(), di.Created.Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInsertDefault2(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareEngine())
|
||||||
|
|
||||||
type DefaultInsert2 struct {
|
type DefaultInsert2 struct {
|
||||||
Id int64
|
Id int64
|
||||||
Name string
|
Name string
|
||||||
|
@ -202,9 +205,6 @@ type DefaultInsert2 struct {
|
||||||
CheckTime time.Time `xorm:"not null default '2000-01-01 00:00:00' TIMESTAMP"`
|
CheckTime time.Time `xorm:"not null default '2000-01-01 00:00:00' TIMESTAMP"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertDefault2(t *testing.T) {
|
|
||||||
assert.NoError(t, PrepareEngine())
|
|
||||||
|
|
||||||
di := new(DefaultInsert2)
|
di := new(DefaultInsert2)
|
||||||
err := testEngine.Sync2(di)
|
err := testEngine.Sync2(di)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -52,7 +52,7 @@ func TestQueryString2(t *testing.T) {
|
||||||
|
|
||||||
type GetVar3 struct {
|
type GetVar3 struct {
|
||||||
Id int64 `xorm:"autoincr pk"`
|
Id int64 `xorm:"autoincr pk"`
|
||||||
Msg bool `xorm:"bit"`
|
Msg bool
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, testEngine.Sync2(new(GetVar3)))
|
assert.NoError(t, testEngine.Sync2(new(GetVar3)))
|
||||||
|
@ -192,7 +192,7 @@ func TestQueryStringNoParam(t *testing.T) {
|
||||||
|
|
||||||
type GetVar4 struct {
|
type GetVar4 struct {
|
||||||
Id int64 `xorm:"autoincr pk"`
|
Id int64 `xorm:"autoincr pk"`
|
||||||
Msg bool `xorm:"bit"`
|
Msg bool
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, testEngine.Sync2(new(GetVar4)))
|
assert.NoError(t, testEngine.Sync2(new(GetVar4)))
|
||||||
|
@ -229,7 +229,7 @@ func TestQuerySliceStringNoParam(t *testing.T) {
|
||||||
|
|
||||||
type GetVar6 struct {
|
type GetVar6 struct {
|
||||||
Id int64 `xorm:"autoincr pk"`
|
Id int64 `xorm:"autoincr pk"`
|
||||||
Msg bool `xorm:"bit"`
|
Msg bool
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, testEngine.Sync2(new(GetVar6)))
|
assert.NoError(t, testEngine.Sync2(new(GetVar6)))
|
||||||
|
@ -266,7 +266,7 @@ func TestQueryInterfaceNoParam(t *testing.T) {
|
||||||
|
|
||||||
type GetVar5 struct {
|
type GetVar5 struct {
|
||||||
Id int64 `xorm:"autoincr pk"`
|
Id int64 `xorm:"autoincr pk"`
|
||||||
Msg bool `xorm:"bit"`
|
Msg bool
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, testEngine.Sync2(new(GetVar5)))
|
assert.NoError(t, testEngine.Sync2(new(GetVar5)))
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
"xorm.io/xorm/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (engine *Engine) row2sliceStr(rows *core.Rows, types []*sql.ColumnType, fields []string) ([]string, error) {
|
||||||
|
results := make([]string, 0, len(fields))
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(fields); i++ {
|
||||||
|
results = append(results, scanResults[i].(*sql.NullString).String)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
|
@ -75,69 +75,18 @@ func value2String(rawValue *reflect.Value) (str string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) {
|
func (session *Session) rows2Strings(rows *core.Rows) (resultsSlice []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))
|
|
||||||
for i := 0; i < len(fields); i++ {
|
|
||||||
var scanResultContainer interface{}
|
|
||||||
scanResultContainers[i] = &scanResultContainer
|
|
||||||
}
|
|
||||||
if err := rows.Scan(scanResultContainers...); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(fields); i++ {
|
|
||||||
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[i]))
|
|
||||||
// if row is null then as empty string
|
|
||||||
if rawValue.Interface() == nil {
|
|
||||||
result = append(result, "")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if data, err := value2String(&rawValue); err == nil {
|
|
||||||
result = append(result, data)
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) {
|
|
||||||
fields, err := rows.Columns()
|
fields, err := rows.Columns()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
types, err := rows.ColumnTypes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
result, err := row2mapStr(rows, fields)
|
result, err := session.engine.row2mapStr(rows, types, fields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -147,13 +96,18 @@ func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error)
|
||||||
return resultsSlice, nil
|
return resultsSlice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) {
|
func (session *Session) rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) {
|
||||||
fields, err := rows.Columns()
|
fields, err := rows.Columns()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
types, err := rows.ColumnTypes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
record, err := row2sliceStr(rows, fields)
|
record, err := session.engine.row2sliceStr(rows, types, fields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -180,7 +134,7 @@ func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]stri
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
return rows2Strings(rows)
|
return session.rows2Strings(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QuerySliceString runs a raw sql and return records as [][]string
|
// QuerySliceString runs a raw sql and return records as [][]string
|
||||||
|
@ -200,7 +154,7 @@ func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string,
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
return rows2SliceString(rows)
|
return session.rows2SliceString(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func row2mapInterface(rows *core.Rows, fields []string) (resultsMap map[string]interface{}, err error) {
|
func row2mapInterface(rows *core.Rows, fields []string) (resultsMap map[string]interface{}, err error) {
|
||||||
|
|
Loading…
Reference in New Issue