From 4383669bcdfd90a1169b9a9a3739dac0844d56b5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 29 Jul 2021 11:04:43 +0800 Subject: [PATCH 001/116] Fix DBMetas returned unsigned tinyint (#2017) Fix #2011 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2017 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- dialects/mssql.go | 2 +- dialects/mysql.go | 14 +++++++++++- dialects/postgres.go | 4 ++-- dialects/sqlite3.go | 5 +++-- integrations/types_test.go | 39 ++++++++++++++++++++++++++++++++ schemas/type.go | 46 +++++++++++++++++++++----------------- tags/parser.go | 4 ++++ tags/tag.go | 8 +++++++ 8 files changed, 96 insertions(+), 26 deletions(-) diff --git a/dialects/mssql.go b/dialects/mssql.go index 3b935396..0eeb1bcd 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -318,7 +318,7 @@ func (db *mssql) SQLType(c *schemas.Column) string { case schemas.TimeStampz: res = "DATETIMEOFFSET" c.Length = 7 - case schemas.MediumInt: + case schemas.MediumInt, schemas.TinyInt, schemas.SmallInt, schemas.UnsignedMediumInt, schemas.UnsignedTinyInt, schemas.UnsignedSmallInt: res = schemas.Int case schemas.Text, schemas.MediumText, schemas.TinyText, schemas.LongText, schemas.Json: res = db.defaultVarchar + "(MAX)" diff --git a/dialects/mysql.go b/dialects/mysql.go index 0ad68833..21128527 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -263,6 +263,7 @@ func (db *mysql) SetParams(params map[string]string) { func (db *mysql) SQLType(c *schemas.Column) string { var res string + var isUnsigned bool switch t := c.SQLType.Name; t { case schemas.Bool: res = schemas.TinyInt @@ -309,8 +310,19 @@ func (db *mysql) SQLType(c *schemas.Column) string { res = schemas.Text case schemas.UnsignedInt: res = schemas.Int + isUnsigned = true case schemas.UnsignedBigInt: res = schemas.BigInt + isUnsigned = true + case schemas.UnsignedMediumInt: + res = schemas.MediumInt + isUnsigned = true + case schemas.UnsignedSmallInt: + res = schemas.SmallInt + isUnsigned = true + case schemas.UnsignedTinyInt: + res = schemas.TinyInt + isUnsigned = true default: res = t } @@ -329,7 +341,7 @@ func (db *mysql) SQLType(c *schemas.Column) string { res += "(" + strconv.Itoa(c.Length) + ")" } - if c.SQLType.Name == schemas.UnsignedBigInt || c.SQLType.Name == schemas.UnsignedInt { + if isUnsigned { res += " UNSIGNED" } diff --git a/dialects/postgres.go b/dialects/postgres.go index cf760e18..96ebfc85 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -879,13 +879,13 @@ func (db *postgres) SetQuotePolicy(quotePolicy QuotePolicy) { func (db *postgres) SQLType(c *schemas.Column) string { var res string switch t := c.SQLType.Name; t { - case schemas.TinyInt: + case schemas.TinyInt, schemas.UnsignedTinyInt: res = schemas.SmallInt return res case schemas.Bit: res = schemas.Boolean return res - case schemas.MediumInt, schemas.Int, schemas.Integer: + case schemas.MediumInt, schemas.Int, schemas.Integer, schemas.UnsignedMediumInt, schemas.UnsignedSmallInt: if c.IsAutoIncrement { return schemas.Serial } diff --git a/dialects/sqlite3.go b/dialects/sqlite3.go index dae6bf93..ac17fd92 100644 --- a/dialects/sqlite3.go +++ b/dialects/sqlite3.go @@ -217,8 +217,9 @@ func (db *sqlite3) SQLType(c *schemas.Column) string { case schemas.Char, schemas.Varchar, schemas.NVarchar, schemas.TinyText, schemas.Text, schemas.MediumText, schemas.LongText, schemas.Json: return schemas.Text - case schemas.Bit, schemas.TinyInt, schemas.SmallInt, schemas.MediumInt, schemas.Int, schemas.Integer, schemas.BigInt, - schemas.UnsignedBigInt, schemas.UnsignedInt: + case schemas.Bit, schemas.TinyInt, schemas.UnsignedTinyInt, schemas.SmallInt, + schemas.UnsignedSmallInt, schemas.MediumInt, schemas.Int, schemas.UnsignedInt, + schemas.BigInt, schemas.UnsignedBigInt, schemas.Integer: return schemas.Integer case schemas.Float, schemas.Double, schemas.Real: return schemas.Real diff --git a/integrations/types_test.go b/integrations/types_test.go index 9d4e46a0..4bdbb6fe 100644 --- a/integrations/types_test.go +++ b/integrations/types_test.go @@ -493,6 +493,45 @@ func TestUnsignedUint32(t *testing.T) { assert.EqualValues(t, uint64(math.MaxUint32), v.Id) } +func TestUnsignedTinyInt(t *testing.T) { + type MyUnsignedTinyIntStruct struct { + Id uint8 `xorm:"unsigned tinyint"` + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(MyUnsignedTinyIntStruct)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + assert.EqualValues(t, 1, len(tables)) + assert.EqualValues(t, 1, len(tables[0].Columns())) + + switch testEngine.Dialect().URI().DBType { + case schemas.SQLITE: + assert.EqualValues(t, "INTEGER", tables[0].Columns()[0].SQLType.Name) + case schemas.MYSQL: + assert.EqualValues(t, "UNSIGNED TINYINT", tables[0].Columns()[0].SQLType.Name) + case schemas.POSTGRES: + assert.EqualValues(t, "SMALLINT", tables[0].Columns()[0].SQLType.Name) + case schemas.MSSQL: + assert.EqualValues(t, "INT", tables[0].Columns()[0].SQLType.Name) + default: + assert.False(t, true, "Unsigned is not implemented") + } + + cnt, err := testEngine.Insert(&MyUnsignedTinyIntStruct{ + Id: math.MaxUint8, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var v MyUnsignedTinyIntStruct + has, err := testEngine.Get(&v) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, uint64(math.MaxUint32), v.Id) +} + type MyDecimal big.Int func (d *MyDecimal) FromDB(data []byte) error { diff --git a/schemas/type.go b/schemas/type.go index d64251bf..d799db08 100644 --- a/schemas/type.go +++ b/schemas/type.go @@ -92,16 +92,19 @@ func (s *SQLType) IsXML() bool { // enumerates all the database column types var ( - Bit = "BIT" - UnsignedBit = "UNSIGNED BIT" - TinyInt = "TINYINT" - SmallInt = "SMALLINT" - MediumInt = "MEDIUMINT" - Int = "INT" - UnsignedInt = "UNSIGNED INT" - Integer = "INTEGER" - BigInt = "BIGINT" - UnsignedBigInt = "UNSIGNED BIGINT" + Bit = "BIT" + UnsignedBit = "UNSIGNED BIT" + TinyInt = "TINYINT" + UnsignedTinyInt = "UNSIGNED TINYINT" + SmallInt = "SMALLINT" + UnsignedSmallInt = "UNSIGNED SMALLINT" + MediumInt = "MEDIUMINT" + UnsignedMediumInt = "UNSIGNED MEDIUMINT" + Int = "INT" + UnsignedInt = "UNSIGNED INT" + Integer = "INTEGER" + BigInt = "BIGINT" + UnsignedBigInt = "UNSIGNED BIGINT" Enum = "ENUM" Set = "SET" @@ -158,16 +161,19 @@ var ( Array = "ARRAY" SqlTypes = map[string]int{ - Bit: NUMERIC_TYPE, - UnsignedBit: NUMERIC_TYPE, - TinyInt: NUMERIC_TYPE, - SmallInt: NUMERIC_TYPE, - MediumInt: NUMERIC_TYPE, - Int: NUMERIC_TYPE, - UnsignedInt: NUMERIC_TYPE, - Integer: NUMERIC_TYPE, - BigInt: NUMERIC_TYPE, - UnsignedBigInt: NUMERIC_TYPE, + Bit: NUMERIC_TYPE, + UnsignedBit: NUMERIC_TYPE, + TinyInt: NUMERIC_TYPE, + UnsignedTinyInt: NUMERIC_TYPE, + SmallInt: NUMERIC_TYPE, + UnsignedSmallInt: NUMERIC_TYPE, + MediumInt: NUMERIC_TYPE, + UnsignedMediumInt: NUMERIC_TYPE, + Int: NUMERIC_TYPE, + UnsignedInt: NUMERIC_TYPE, + Integer: NUMERIC_TYPE, + BigInt: NUMERIC_TYPE, + UnsignedBigInt: NUMERIC_TYPE, Enum: TEXT_TYPE, Set: TEXT_TYPE, diff --git a/tags/parser.go b/tags/parser.go index 7d8c3bd6..e6245a68 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -217,6 +217,10 @@ func (parser *Parser) parseFieldWithTags(table *schemas.Table, fieldIndex int, f if col.SQLType.Name == "" { col.SQLType = schemas.Type2SQLType(field.Type) } + if ctx.isUnsigned && col.SQLType.IsNumeric() && !strings.HasPrefix(col.SQLType.Name, "UNSIGNED") { + col.SQLType.Name = "UNSIGNED " + col.SQLType.Name + } + parser.dialect.SQLType(col) if col.Length == 0 { col.Length = col.SQLType.DefaultLength diff --git a/tags/tag.go b/tags/tag.go index 32cdb37c..4e1f1ce7 100644 --- a/tags/tag.go +++ b/tags/tag.go @@ -93,6 +93,7 @@ type Context struct { hasCacheTag bool hasNoCacheTag bool ignoreNext bool + isUnsigned bool } // Handler describes tag handler for XORM @@ -122,6 +123,7 @@ var ( "NOCACHE": NoCacheTagHandler, "COMMENT": CommentTagHandler, "EXTENDS": ExtendsTagHandler, + "UNSIGNED": UnsignedTagHandler, } ) @@ -268,6 +270,12 @@ func UniqueTagHandler(ctx *Context) error { return nil } +// UnsignedTagHandler represents the column is unsigned +func UnsignedTagHandler(ctx *Context) error { + ctx.isUnsigned = true + return nil +} + // CommentTagHandler add comment to column func CommentTagHandler(ctx *Context) error { if len(ctx.params) > 0 { From aeed22016fb9c8abac9f8b049f8b219e66fafa89 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 29 Jul 2021 16:12:09 +0800 Subject: [PATCH 002/116] Support batch insert map (#2019) Fix #1767 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2019 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- integrations/session_insert_test.go | 79 ++++++++++++++ internal/statements/insert.go | 53 ++++++++++ session_insert.go | 159 +++++++++++++++++++--------- 3 files changed, 239 insertions(+), 52 deletions(-) diff --git a/integrations/session_insert_test.go b/integrations/session_insert_test.go index ce52d3c4..cd56a958 100644 --- a/integrations/session_insert_test.go +++ b/integrations/session_insert_test.go @@ -1065,3 +1065,82 @@ func TestInsertDeleted(t *testing.T) { assert.NoError(t, err) assert.True(t, has) } + +func TestInsertMultipleMap(t *testing.T) { + type InsertMultipleMap struct { + Id int64 + Width uint32 + Height uint32 + Name string + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(InsertMultipleMap)) + + cnt, err := testEngine.Table(new(InsertMultipleMap)).Insert([]map[string]interface{}{ + { + "width": 20, + "height": 10, + "name": "lunny", + }, + { + "width": 30, + "height": 20, + "name": "xiaolunwen", + }, + }) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + var res []InsertMultipleMap + err = testEngine.Find(&res) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(res)) + assert.EqualValues(t, InsertMultipleMap{ + Id: 1, + Width: 20, + Height: 10, + Name: "lunny", + }, res[0]) + assert.EqualValues(t, InsertMultipleMap{ + Id: 2, + Width: 30, + Height: 20, + Name: "xiaolunwen", + }, res[1]) + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(InsertMultipleMap)) + + cnt, err = testEngine.Table(new(InsertMultipleMap)).Insert([]map[string]string{ + { + "width": "20", + "height": "10", + "name": "lunny", + }, + { + "width": "30", + "height": "20", + "name": "xiaolunwen", + }, + }) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + res = make([]InsertMultipleMap, 0, 2) + err = testEngine.Find(&res) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(res)) + assert.EqualValues(t, InsertMultipleMap{ + Id: 1, + Width: 20, + Height: 10, + Name: "lunny", + }, res[0]) + assert.EqualValues(t, InsertMultipleMap{ + Id: 2, + Width: 30, + Height: 20, + Name: "xiaolunwen", + }, res[1]) +} diff --git a/internal/statements/insert.go b/internal/statements/insert.go index 4e43c5bd..84547cdf 100644 --- a/internal/statements/insert.go +++ b/internal/statements/insert.go @@ -5,6 +5,7 @@ package statements import ( + "errors" "fmt" "strings" @@ -205,3 +206,55 @@ func (statement *Statement) GenInsertMapSQL(columns []string, args []interface{} return buf.String(), buf.Args(), nil } + +func (statement *Statement) GenInsertMultipleMapSQL(columns []string, argss [][]interface{}) (string, []interface{}, error) { + var ( + buf = builder.NewWriter() + exprs = statement.ExprColumns + tableName = statement.TableName() + ) + + if _, err := buf.WriteString(fmt.Sprintf("INSERT INTO %s (", statement.quote(tableName))); err != nil { + return "", nil, err + } + + if err := statement.dialect.Quoter().JoinWrite(buf.Builder, append(columns, exprs.ColNames()...), ","); err != nil { + return "", nil, err + } + + // if insert where + if statement.Conds().IsValid() { + return "", nil, errors.New("batch insert don't support with where") + } + + if _, err := buf.WriteString(") VALUES "); err != nil { + return "", nil, err + } + for i, args := range argss { + if _, err := buf.WriteString("("); err != nil { + return "", nil, err + } + if err := statement.WriteArgs(buf, args); err != nil { + return "", nil, err + } + + if len(exprs) > 0 { + if _, err := buf.WriteString(","); err != nil { + return "", nil, err + } + if err := exprs.WriteArgs(buf); err != nil { + return "", nil, err + } + } + if _, err := buf.WriteString(")"); err != nil { + return "", nil, err + } + if i < len(argss)-1 { + if _, err := buf.WriteString(","); err != nil { + return "", nil, err + } + } + } + + return buf.String(), buf.Args(), nil +} diff --git a/session_insert.go b/session_insert.go index 1583858e..b116b9ff 100644 --- a/session_insert.go +++ b/session_insert.go @@ -18,7 +18,7 @@ import ( ) // ErrNoElementsOnSlice represents an error there is no element when insert -var ErrNoElementsOnSlice = errors.New("No element on slice when insert") +var ErrNoElementsOnSlice = errors.New("no element on slice when insert") // Insert insert one or more beans func (session *Session) Insert(beans ...interface{}) (int64, error) { @@ -36,71 +36,42 @@ func (session *Session) Insert(beans ...interface{}) (int64, error) { }() for _, bean := range beans { - switch bean.(type) { + var cnt int64 + var err error + switch v := bean.(type) { case map[string]interface{}: - cnt, err := session.insertMapInterface(bean.(map[string]interface{})) - if err != nil { - return affected, err - } - affected += cnt + cnt, err = session.insertMapInterface(v) case []map[string]interface{}: - s := bean.([]map[string]interface{}) - for i := 0; i < len(s); i++ { - cnt, err := session.insertMapInterface(s[i]) - if err != nil { - return affected, err - } - affected += cnt - } + cnt, err = session.insertMultipleMapInterface(v) case map[string]string: - cnt, err := session.insertMapString(bean.(map[string]string)) - if err != nil { - return affected, err - } - affected += cnt + cnt, err = session.insertMapString(v) case []map[string]string: - s := bean.([]map[string]string) - for i := 0; i < len(s); i++ { - cnt, err := session.insertMapString(s[i]) - if err != nil { - return affected, err - } - affected += cnt - } + cnt, err = session.insertMultipleMapString(v) default: sliceValue := reflect.Indirect(reflect.ValueOf(bean)) if sliceValue.Kind() == reflect.Slice { - size := sliceValue.Len() - if size <= 0 { - return 0, ErrNoElementsOnSlice - } - - cnt, err := session.innerInsertMulti(bean) - if err != nil { - return affected, err - } - affected += cnt + cnt, err = session.insertMultipleStruct(bean) } else { - cnt, err := session.innerInsert(bean) - if err != nil { - return affected, err - } - affected += cnt + cnt, err = session.insertStruct(bean) } } + if err != nil { + return affected, err + } + affected += cnt } return affected, err } -func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error) { +func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, error) { sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) if sliceValue.Kind() != reflect.Slice { return 0, errors.New("needs a pointer to a slice") } if sliceValue.Len() <= 0 { - return 0, errors.New("could not insert a empty slice") + return 0, ErrNoElementsOnSlice } if err := session.statement.SetRefBean(sliceValue.Index(0).Interface()); err != nil { @@ -269,14 +240,10 @@ func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) { return 0, ErrPtrSliceType } - if sliceValue.Len() <= 0 { - return 0, ErrNoElementsOnSlice - } - - return session.innerInsertMulti(rowsSlicePtr) + return session.insertMultipleStruct(rowsSlicePtr) } -func (session *Session) innerInsert(bean interface{}) (int64, error) { +func (session *Session) insertStruct(bean interface{}) (int64, error) { if err := session.statement.SetRefBean(bean); err != nil { return 0, err } @@ -434,7 +401,7 @@ func (session *Session) InsertOne(bean interface{}) (int64, error) { defer session.Close() } - return session.innerInsert(bean) + return session.insertStruct(bean) } func (session *Session) cacheInsert(table string) error { @@ -561,6 +528,37 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err return session.insertMap(columns, args) } +func (session *Session) insertMultipleMapInterface(maps []map[string]interface{}) (int64, error) { + if len(maps) <= 0 { + return 0, ErrNoElementsOnSlice + } + + tableName := session.statement.TableName() + if len(tableName) <= 0 { + return 0, ErrTableNotFound + } + + var columns = make([]string, 0, len(maps[0])) + exprs := session.statement.ExprColumns + for k := range maps[0] { + if !exprs.IsColExist(k) { + columns = append(columns, k) + } + } + sort.Strings(columns) + + var argss = make([][]interface{}, 0, len(maps)) + for _, m := range maps { + var args = make([]interface{}, 0, len(m)) + for _, colName := range columns { + args = append(args, m[colName]) + } + argss = append(argss, args) + } + + return session.insertMultipleMap(columns, argss) +} + func (session *Session) insertMapString(m map[string]string) (int64, error) { if len(m) == 0 { return 0, ErrParamsType @@ -589,6 +587,37 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { return session.insertMap(columns, args) } +func (session *Session) insertMultipleMapString(maps []map[string]string) (int64, error) { + if len(maps) <= 0 { + return 0, ErrNoElementsOnSlice + } + + tableName := session.statement.TableName() + if len(tableName) <= 0 { + return 0, ErrTableNotFound + } + + var columns = make([]string, 0, len(maps[0])) + exprs := session.statement.ExprColumns + for k := range maps[0] { + if !exprs.IsColExist(k) { + columns = append(columns, k) + } + } + sort.Strings(columns) + + var argss = make([][]interface{}, 0, len(maps)) + for _, m := range maps { + var args = make([]interface{}, 0, len(m)) + for _, colName := range columns { + args = append(args, m[colName]) + } + argss = append(argss, args) + } + + return session.insertMultipleMap(columns, argss) +} + func (session *Session) insertMap(columns []string, args []interface{}) (int64, error) { tableName := session.statement.TableName() if len(tableName) <= 0 { @@ -614,3 +643,29 @@ func (session *Session) insertMap(columns []string, args []interface{}) (int64, } return affected, nil } + +func (session *Session) insertMultipleMap(columns []string, argss [][]interface{}) (int64, error) { + tableName := session.statement.TableName() + if len(tableName) <= 0 { + return 0, ErrTableNotFound + } + + sql, args, err := session.statement.GenInsertMultipleMapSQL(columns, argss) + if err != nil { + return 0, err + } + + if err := session.cacheInsert(tableName); err != nil { + return 0, err + } + + res, err := session.exec(sql, args...) + if err != nil { + return 0, err + } + affected, err := res.RowsAffected() + if err != nil { + return 0, err + } + return affected, nil +} From 2afa222871ad0dfa2caccc0614c99ce8b7a41107 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 29 Jul 2021 19:51:10 +0800 Subject: [PATCH 003/116] Fix deleted column (#2014) Fix #2013 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2014 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- tags/parser.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tags/parser.go b/tags/parser.go index e6245a68..5f9fd528 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -300,11 +300,5 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) { table.AddColumn(col) } // end for - deletedColumn := table.DeletedColumn() - // check columns - if deletedColumn != nil { - deletedColumn.Nullable = true - } - return table, nil } From f22b0cc3697539dbc460c11d99380f16d8d50d8e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 4 Aug 2021 09:20:54 +0800 Subject: [PATCH 004/116] Update changelog for 1.2.0 --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd567b27..f669da8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,40 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log. +## [1.2.0](https://gitea.com/xorm/xorm/releases/tag/1.2.0) - 2021-08-04 + +* BREAKING + * Exec with time arg now will obey time zone settings on engine (#1989) + * Query interface (#1965) + * Support delete with no bean (#1926) + * Nil ptr is nullable (#1919) +* FEATURES + * Support batch insert map (#2019) + * Support big.Float (#1973) +* BUGFIXES + * fix possible null dereference in internal/statements/query.go (#1988) + * Fix bug on dumptable (#1984) +* ENHANCEMENTS + * Move assign functions to convert package (#2015) + * refactor conversion (#2001) + * refactor some code (#2000) + * refactor insert condition generation (#1998) + * refactor and add setjson function (#1997) + * Get struct and Find support big.Float (#1976) + * refactor slice2Bean (#1974, #1975) + * refactor get (#1967) + * Replace #1044 (#1935) + * Support Get time.Time (#1933) +* TESTING + * Add benchmark tests (#1978) + * Add tests for github.com/shopspring/decimal support (#1977) + * Add test for get map with NULL column (#1948) + * Add test for limit with query (#1787) +* MISC + * Fix DBMetas returned unsigned tinyint (#2017) + * Fix deleted column (#2014) + * Add database alias table and fix wrong warning (#1947) + ## [1.1.2](https://gitea.com/xorm/xorm/releases/tag/1.1.2) - 2021-07-04 * BUILD From 42c2f158e80bafffa9488f1d572d8fcea5688ea0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 4 Aug 2021 16:12:10 +0800 Subject: [PATCH 005/116] Fix timesatmp (#2021) Now `Datetime` support `Datime(6)` to indicate the time scale. For different database , the max scale is not different. `Datetime` means `Datetime(0)`. Reviewed-on: https://gitea.com/xorm/xorm/pulls/2021 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- dialects/mssql.go | 16 +++++-- dialects/time.go | 73 +++++++++++++++++--------------- engine.go | 10 ++--- integrations/time_test.go | 58 ++++++++++++++++++++++++- internal/convert/time.go | 9 ++++ internal/statements/statement.go | 6 ++- internal/statements/update.go | 5 ++- internal/statements/values.go | 4 +- session_delete.go | 5 ++- session_insert.go | 38 ++++++++--------- session_update.go | 10 ++++- 11 files changed, 165 insertions(+), 69 deletions(-) diff --git a/dialects/mssql.go b/dialects/mssql.go index 0eeb1bcd..1c56e7a4 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -313,8 +313,12 @@ func (db *mssql) SQLType(c *schemas.Column) string { if c.Length == 0 { res += "(MAX)" } - case schemas.TimeStamp: - res = schemas.DateTime + case schemas.TimeStamp, schemas.DateTime: + if c.Length > 3 { + res = "DATETIME2" + } else { + return schemas.DateTime + } case schemas.TimeStampz: res = "DATETIMEOFFSET" c.Length = 7 @@ -357,7 +361,7 @@ func (db *mssql) SQLType(c *schemas.Column) string { res = t } - if res == schemas.Int || res == schemas.Bit || res == schemas.DateTime { + if res == schemas.Int || res == schemas.Bit { return res } @@ -498,6 +502,12 @@ func (db *mssql) GetColumns(queryer core.Queryer, ctx context.Context, tableName col.Length /= 2 col.Length2 /= 2 } + case "DATETIME2": + col.SQLType = schemas.SQLType{Name: schemas.DateTime, DefaultLength: 7, DefaultLength2: 0} + col.Length = scale + case "DATETIME": + col.SQLType = schemas.SQLType{Name: schemas.DateTime, DefaultLength: 3, DefaultLength2: 0} + col.Length = scale case "IMAGE": col.SQLType = schemas.SQLType{Name: schemas.VarBinary, DefaultLength: 0, DefaultLength2: 0} case "NCHAR": diff --git a/dialects/time.go b/dialects/time.go index 5aee0c10..f0bbb765 100644 --- a/dialects/time.go +++ b/dialects/time.go @@ -5,50 +5,57 @@ package dialects import ( + "strings" "time" "xorm.io/xorm/schemas" ) -// FormatTime format time as column type -func FormatTime(dialect Dialect, sqlTypeName string, t time.Time) (v interface{}) { - switch sqlTypeName { - case schemas.Time: - s := t.Format("2006-01-02 15:04:05") // time.RFC3339 - v = s[11:19] - case schemas.Date: - v = t.Format("2006-01-02") - case schemas.DateTime, schemas.TimeStamp, schemas.Varchar: // !DarthPestilane! format time when sqlTypeName is schemas.Varchar. - if dialect.URI().DBType == schemas.ORACLE { - v = t - } else { - v = t.Format("2006-01-02 15:04:05") - } - case schemas.TimeStampz: - if dialect.URI().DBType == schemas.MSSQL { - v = t.Format("2006-01-02T15:04:05.9999999Z07:00") - } else { - v = t.Format(time.RFC3339Nano) - } - case schemas.BigInt, schemas.Int: - v = t.Unix() - default: - v = t - } - return -} - // FormatColumnTime format column time -func FormatColumnTime(dialect Dialect, defaultTimeZone *time.Location, col *schemas.Column, t time.Time) (v interface{}) { +func FormatColumnTime(dialect Dialect, dbLocation *time.Location, col *schemas.Column, t time.Time) (interface{}, error) { if t.IsZero() { if col.Nullable { - return nil + return nil, nil + } + + if col.SQLType.IsNumeric() { + return 0, nil } - return "" } + var tmZone = dbLocation if col.TimeZone != nil { - return FormatTime(dialect, col.SQLType.Name, t.In(col.TimeZone)) + tmZone = col.TimeZone + } + + t = t.In(tmZone) + + switch col.SQLType.Name { + case schemas.Date: + return t.Format("2006-01-02"), nil + case schemas.Time: + var layout = "15:04:05" + if col.Length > 0 { + layout += "." + strings.Repeat("0", col.Length) + } + return t.Format(layout), nil + case schemas.DateTime, schemas.TimeStamp: + var layout = "2006-01-02 15:04:05" + if col.Length > 0 { + layout += "." + strings.Repeat("0", col.Length) + } + return t.Format(layout), nil + case schemas.Varchar: + return t.Format("2006-01-02 15:04:05"), nil + case schemas.TimeStampz: + if dialect.URI().DBType == schemas.MSSQL { + return t.Format("2006-01-02T15:04:05.9999999Z07:00"), nil + } else { + return t.Format(time.RFC3339Nano), nil + } + case schemas.BigInt, schemas.Int: + return t.Unix(), nil + default: + return t, nil } - return FormatTime(dialect, col.SQLType.Name, t.In(defaultTimeZone)) } diff --git a/engine.go b/engine.go index 20c07e13..133e9553 100644 --- a/engine.go +++ b/engine.go @@ -1226,13 +1226,13 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) { } // nowTime return current time -func (engine *Engine) nowTime(col *schemas.Column) (interface{}, time.Time) { +func (engine *Engine) nowTime(col *schemas.Column) (interface{}, time.Time, error) { t := time.Now() - var tz = engine.DatabaseTZ - if !col.DisableTimeZone && col.TimeZone != nil { - tz = col.TimeZone + result, err := dialects.FormatColumnTime(engine.dialect, engine.DatabaseTZ, col, t) + if err != nil { + return nil, time.Time{}, err } - return dialects.FormatTime(engine.dialect, col.SQLType.Name, t.In(tz)), t.In(engine.TZLocation) + return result, t.In(engine.TZLocation), nil } // GetColumnMapper returns the column name mapper diff --git a/integrations/time_test.go b/integrations/time_test.go index 50fd1847..cd2e879f 100644 --- a/integrations/time_test.go +++ b/integrations/time_test.go @@ -15,8 +15,12 @@ import ( "github.com/stretchr/testify/assert" ) -func formatTime(t time.Time) string { - return t.Format("2006-01-02 15:04:05") +func formatTime(t time.Time, scales ...int) string { + var layout = "2006-01-02 15:04:05" + if len(scales) > 0 && scales[0] > 0 { + layout += "." + strings.Repeat("0", scales[0]) + } + return t.Format(layout) } func TestTimeUserTime(t *testing.T) { @@ -565,3 +569,53 @@ func TestDeletedInt64(t *testing.T) { assert.True(t, has) assert.EqualValues(t, d1, d4) } + +func TestTimestamp(t *testing.T) { + { + assert.NoError(t, PrepareEngine()) + + type TimestampStruct struct { + Id int64 + InsertTime time.Time `xorm:"DATETIME(6)"` + } + + assertSync(t, new(TimestampStruct)) + + var d1 = TimestampStruct{ + InsertTime: time.Now(), + } + cnt, err := testEngine.Insert(&d1) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var d2 TimestampStruct + has, err := testEngine.ID(d1.Id).Get(&d2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, formatTime(d1.InsertTime, 6), formatTime(d2.InsertTime, 6)) + } + + /*{ + assert.NoError(t, PrepareEngine()) + + type TimestampzStruct struct { + Id int64 + InsertTime time.Time `xorm:"TIMESTAMPZ"` + } + + assertSync(t, new(TimestampzStruct)) + + var d3 = TimestampzStruct{ + InsertTime: time.Now(), + } + cnt, err := testEngine.Insert(&d3) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var d4 TimestampzStruct + has, err := testEngine.ID(d3.Id).Get(&d4) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, formatTime(d3.InsertTime, 6), formatTime(d4.InsertTime, 6)) + }*/ +} diff --git a/internal/convert/time.go b/internal/convert/time.go index ecb30a3f..e53a19cd 100644 --- a/internal/convert/time.go +++ b/internal/convert/time.go @@ -8,6 +8,7 @@ import ( "database/sql" "fmt" "strconv" + "strings" "time" "xorm.io/xorm/internal/utils" @@ -39,6 +40,14 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t } dt = dt.In(convertedLocation) return &dt, nil + } else if len(s) >= 21 && s[19] == '.' { + var layout = "2006-01-02 15:04:05." + strings.Repeat("0", len(s)-20) + dt, err := time.ParseInLocation(layout, s, originalLocation) + if err != nil { + return nil, err + } + dt = dt.In(convertedLocation) + return &dt, nil } else { i, err := strconv.ParseInt(s, 10, 64) if err == nil { diff --git a/internal/statements/statement.go b/internal/statements/statement.go index 8e3c083c..adbeb1c2 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -734,7 +734,11 @@ func (statement *Statement) asDBCond(fieldValue reflect.Value, fieldType reflect if !requiredField && (t.IsZero() || !fieldValue.IsValid()) { return nil, false, nil } - return dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t), true, nil + res, err := dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) + if err != nil { + return nil, false, err + } + return res, true, nil } else if fieldType.ConvertibleTo(schemas.BigFloatType) { t := fieldValue.Convert(schemas.BigFloatType).Interface().(big.Float) v := t.String() diff --git a/internal/statements/update.go b/internal/statements/update.go index 39a7f829..a8a174f9 100644 --- a/internal/statements/update.go +++ b/internal/statements/update.go @@ -208,7 +208,10 @@ func (statement *Statement) BuildUpdates(tableValue reflect.Value, if !requiredField && (t.IsZero() || !fieldValue.IsValid()) { continue } - val = dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) + val, err = dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) + if err != nil { + return nil, nil, err + } } else if nulType, ok := fieldValue.Interface().(driver.Valuer); ok { val, _ = nulType.Value() if val == nil && !requiredField { diff --git a/internal/statements/values.go b/internal/statements/values.go index ada01755..7351fb79 100644 --- a/internal/statements/values.go +++ b/internal/statements/values.go @@ -87,8 +87,8 @@ func (statement *Statement) Value2Interface(col *schemas.Column, fieldValue refl case reflect.Struct: if fieldType.ConvertibleTo(schemas.TimeType) { t := fieldValue.Convert(schemas.TimeType).Interface().(time.Time) - tf := dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) - return tf, nil + tf, err := dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) + return tf, err } else if fieldType.ConvertibleTo(nullFloatType) { t := fieldValue.Convert(nullFloatType).Interface().(sql.NullFloat64) if !t.Valid { diff --git a/session_delete.go b/session_delete.go index baabb558..37b9c1cd 100644 --- a/session_delete.go +++ b/session_delete.go @@ -212,7 +212,10 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) { paramsLen := len(condArgs) copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1]) - val, t := session.engine.nowTime(deletedColumn) + val, t, err := session.engine.nowTime(deletedColumn) + if err != nil { + return 0, err + } condArgs[0] = val var colName = deletedColumn.Name diff --git a/session_insert.go b/session_insert.go index b116b9ff..78f0e555 100644 --- a/session_insert.go +++ b/session_insert.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "xorm.io/xorm/dialects" "xorm.io/xorm/internal/convert" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" @@ -137,7 +138,10 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e continue } if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { - val, t := session.engine.nowTime(col) + val, t, err := session.engine.nowTime(col) + if err != nil { + return 0, err + } args = append(args, val) var colName = col.Name @@ -427,29 +431,12 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac if col.MapType == schemas.ONLYFROMDB { continue } - - if col.IsDeleted { - colNames = append(colNames, col.Name) - if !col.Nullable { - if col.SQLType.IsNumeric() { - args = append(args, 0) - } else { - args = append(args, time.Time{}.Format("2006-01-02 15:04:05")) - } - } else { - args = append(args, nil) - } - continue - } - if session.statement.OmitColumnMap.Contain(col.Name) { continue } - if len(session.statement.ColumnMap) > 0 && !session.statement.ColumnMap.Contain(col.Name) { continue } - if session.statement.IncrColumns.IsColExist(col.Name) { continue } else if session.statement.DecrColumns.IsColExist(col.Name) { @@ -458,6 +445,16 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac continue } + if col.IsDeleted { + arg, err := dialects.FormatColumnTime(session.engine.dialect, session.engine.DatabaseTZ, col, time.Time{}) + if err != nil { + return nil, nil, err + } + args = append(args, arg) + colNames = append(colNames, col.Name) + continue + } + fieldValuePtr, err := col.ValueOf(bean) if err != nil { return nil, nil, err @@ -478,7 +475,10 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { // if time is non-empty, then set to auto time - val, t := session.engine.nowTime(col) + val, t, err := session.engine.nowTime(col) + if err != nil { + return nil, nil, err + } args = append(args, val) var colName = col.Name diff --git a/session_update.go b/session_update.go index 4f8e6961..7d91346e 100644 --- a/session_update.go +++ b/session_update.go @@ -215,7 +215,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 !session.statement.OmitColumnMap.Contain(table.Updated) { colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?") col := table.UpdatedColumn() - val, t := session.engine.nowTime(col) + val, t, err := session.engine.nowTime(col) + if err != nil { + return 0, err + } if session.engine.dialect.URI().DBType == schemas.ORACLE { args = append(args, t) } else { @@ -521,7 +524,10 @@ func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interfac if col.IsUpdated && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { // if time is non-empty, then set to auto time - val, t := session.engine.nowTime(col) + val, t, err := session.engine.nowTime(col) + if err != nil { + return nil, nil, err + } args = append(args, val) var colName = col.Name From 0b3cc25a102c6e4a643d117bed24ab42d9495747 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 5 Aug 2021 14:04:11 +0800 Subject: [PATCH 006/116] Add pgx driver support (#1795) Fix #858 Reviewed-on: https://gitea.com/xorm/xorm/pulls/1795 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- .drone.yml | 37 +++ Makefile | 14 +- dialects/mssql.go | 2 +- engine.go | 1 + go.mod | 2 +- go.sum | 476 ++++++++++++++++++++++++++-- integrations/engine_test.go | 1 + integrations/session_update_test.go | 4 +- schemas/type.go | 132 +++----- tags/parser.go | 32 +- 10 files changed, 576 insertions(+), 125 deletions(-) diff --git a/.drone.yml b/.drone.yml index 869d0114..34b9a514 100644 --- a/.drone.yml +++ b/.drone.yml @@ -205,6 +205,43 @@ steps: commands: - TEST_QUOTE_POLICY=reserved make test-postgres +- name: test-pgx + pull: never + image: golang:1.15 + volumes: + - name: cache + path: /go/pkg/mod + depends_on: + - test-postgres-schema + environment: + TEST_PGSQL_HOST: pgsql + TEST_PGSQL_DBNAME: xorm_test + TEST_PGSQL_USERNAME: postgres + TEST_PGSQL_PASSWORD: postgres + commands: + - make test-pgx + - TEST_CACHE_ENABLE=true make test-pgx + - TEST_QUOTE_POLICY=reserved make test-pgx + +- name: test-pgx-schema + pull: never + image: golang:1.15 + volumes: + - name: cache + path: /go/pkg/mod + depends_on: + - test-pgx + environment: + TEST_PGSQL_HOST: pgsql + TEST_PGSQL_SCHEMA: xorm + TEST_PGSQL_DBNAME: xorm_test + TEST_PGSQL_USERNAME: postgres + TEST_PGSQL_PASSWORD: postgres + commands: + - make test-pgx + - TEST_CACHE_ENABLE=true make test-pgx + - TEST_QUOTE_POLICY=reserved make test-pgx + volumes: - name: cache host: diff --git a/Makefile b/Makefile index 5675589d..e986082e 100644 --- a/Makefile +++ b/Makefile @@ -211,7 +211,19 @@ test-sqlite3-schema: go-check .PHONY: test-sqlite3\#% test-sqlite3\#%: go-check $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -cache=$(TEST_CACHE_ENABLE) -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" \ - -quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite3.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic + -quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite3.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m + +.PNONY: test-pgx +test-pgx: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=pgx -schema='$(TEST_PGSQL_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ + -conn_str="postgres://$(TEST_PGSQL_USERNAME):$(TEST_PGSQL_PASSWORD)@$(TEST_PGSQL_HOST)/$(TEST_PGSQL_DBNAME)?sslmode=disable" \ + -quote=$(TEST_QUOTE_POLICY) -coverprofile=postgres.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m + +.PHONY: test-pgx\#% +test-pgx\#%: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -db=pgx -schema='$(TEST_PGSQL_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ + -conn_str="postgres://$(TEST_PGSQL_USERNAME):$(TEST_PGSQL_PASSWORD)@$(TEST_PGSQL_HOST)/$(TEST_PGSQL_DBNAME)?sslmode=disable" \ + -quote=$(TEST_QUOTE_POLICY) -coverprofile=postgres.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m .PHONY: test-sqlite test-sqlite: go-check diff --git a/dialects/mssql.go b/dialects/mssql.go index 1c56e7a4..2121e71d 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -285,7 +285,7 @@ func (db *mssql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Ve func (db *mssql) SQLType(c *schemas.Column) string { var res string switch t := c.SQLType.Name; t { - case schemas.Bool: + case schemas.Bool, schemas.Boolean: res = schemas.Bit if strings.EqualFold(c.Default, "true") { c.Default = "1" diff --git a/engine.go b/engine.go index 133e9553..ec066109 100644 --- a/engine.go +++ b/engine.go @@ -471,6 +471,7 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch destURI := dialects.URI{ DBType: tp[0], DBName: uri.DBName, + Schema: uri.Schema, } dstDialect.Init(&destURI) } diff --git a/go.mod b/go.mod index 1b3baf0c..d645011c 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/denisenkom/go-mssqldb v0.10.0 github.com/go-sql-driver/mysql v1.6.0 github.com/goccy/go-json v0.7.4 + github.com/jackc/pgx/v4 v4.12.0 github.com/json-iterator/go v1.1.11 github.com/lib/pq v1.10.2 github.com/mattn/go-sqlite3 v1.14.8 @@ -13,7 +14,6 @@ require ( github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 github.com/ziutek/mymysql v1.5.4 - gopkg.in/yaml.v2 v2.2.2 // indirect modernc.org/sqlite v1.11.2 xorm.io/builder v0.3.9 ) diff --git a/go.sum b/go.sum index 3d4b72a6..e8024945 100644 --- a/go.sum +++ b/go.sum @@ -1,141 +1,555 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk= -github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k= github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= +github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g= +github.com/jackc/pgconn v1.9.0 h1:gqibKSTJup/ahCsNKyMZAniPuZEfIqfXFc8FOWVYR+Q= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd h1:eDErF6V/JPJON/B7s68BxwHgfmyOntHJQ8IOaz0x4R8= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= +github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= +github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE= +github.com/jackc/pgtype v1.8.0 h1:iFVCcVhYlw0PulYCVoguRGm0SE9guIcPcccnLzHj8bA= +github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= +github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= +github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= +github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc= +github.com/jackc/pgx/v4 v4.12.0 h1:xiP3TdnkwyslWNp77yE5XAPfxAsU9RMFDe0c1SwN8h4= +github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= -github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0= -modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= modernc.org/cc/v3 v3.33.6 h1:r63dgSzVzRxUpAJFPQWHy1QeZeY1ydNENUDaBx1GqYc= modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA= -modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY= modernc.org/ccgo/v3 v3.9.5 h1:dEuUSf8WN51rDkprFuAqjfchKEzN0WttP/Py3enBwjk= modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.8.0 h1:Pp4uv9g0csgBMpGPABKtkieF6O5MGhfGo6ZiOdlYfR8= -modernc.org/libc v1.8.0/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.11 h1:QUxZMs48Ahg2F7SN41aERvMfGLY2HU/ADnB9DC4Yts8= modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.0 h1:GCjoRaBew8ECCKINQA2nYjzvufFW9YiEuuB+rQ9bn2E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= @@ -143,23 +557,17 @@ modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84 h1:rgEUzE849tFlHSoeCrKyS9cZAljC+DY7MdMHKq6R6sY= -modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU= modernc.org/sqlite v1.11.2 h1:ShWQpeD3ag/bmx6TqidBlIWonWmQaSQKls3aenCbt+w= modernc.org/sqlite v1.11.2/go.mod h1:+mhs/P1ONd+6G7hcAs6irwDi/bjTQ7nLW6LHRBsEa3A= -modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= -modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/tcl v1.5.0 h1:euZSUNfE0Fd4W8VqXI1Ly1v7fqDJoBuAV88Ea+SnaSs= -modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc= +modernc.org/tcl v1.5.5 h1:N03RwthgTR/l/eQvz3UjfYnvVVj1G2sZqzFGfoD4HE4= modernc.org/tcl v1.5.5/go.mod h1:ADkaTUuwukkrlhqwERyq0SM8OvyXo7+TjFz7yAF56EI= modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc= modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -xorm.io/builder v0.3.8 h1:P/wPgRqa9kX5uE0aA1/ukJ23u9KH0aSRpHLwDKXigSE= -xorm.io/builder v0.3.8/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc= xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= diff --git a/integrations/engine_test.go b/integrations/engine_test.go index b5ecb2c2..cd309d6a 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -16,6 +16,7 @@ import ( _ "github.com/denisenkom/go-mssqldb" _ "github.com/go-sql-driver/mysql" + _ "github.com/jackc/pgx/v4/stdlib" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" "github.com/stretchr/testify/assert" diff --git a/integrations/session_update_test.go b/integrations/session_update_test.go index cc1042b6..bbcc7600 100644 --- a/integrations/session_update_test.go +++ b/integrations/session_update_test.go @@ -349,7 +349,7 @@ func TestUpdate1(t *testing.T) { And("height = ?", user.Height). And("departname = ?", ""). And("detail_id = ?", 0). - And("is_man = ?", 0). + And("is_man = ?", false). Get(&Userinfo{}) assert.NoError(t, err) assert.True(t, has, "cannot insert properly") @@ -825,7 +825,7 @@ func TestNewUpdate(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 0, af) - af, err = testEngine.Table(new(TbUserInfo)).Where("phone=?", 13126564922).Update(&changeUsr) + af, err = testEngine.Table(new(TbUserInfo)).Where("phone=?", "13126564922").Update(&changeUsr) assert.NoError(t, err) assert.EqualValues(t, 0, af) } diff --git a/schemas/type.go b/schemas/type.go index d799db08..cf730134 100644 --- a/schemas/type.go +++ b/schemas/type.go @@ -5,9 +5,9 @@ package schemas import ( + "database/sql" "math/big" "reflect" - "sort" "strings" "time" ) @@ -227,90 +227,44 @@ var ( Serial: NUMERIC_TYPE, BigSerial: NUMERIC_TYPE, + "INT8": NUMERIC_TYPE, + Array: ARRAY_TYPE, } - - intTypes = sort.StringSlice{"*int", "*int16", "*int32", "*int8"} - uintTypes = sort.StringSlice{"*uint", "*uint16", "*uint32", "*uint8"} -) - -// !nashtsai! treat following var as interal const values, these are used for reflect.TypeOf comparison -var ( - emptyString string - boolDefault bool - byteDefault byte - complex64Default complex64 - complex128Default complex128 - float32Default float32 - float64Default float64 - int64Default int64 - uint64Default uint64 - int32Default int32 - uint32Default uint32 - int16Default int16 - uint16Default uint16 - int8Default int8 - uint8Default uint8 - intDefault int - uintDefault uint - timeDefault time.Time - bigFloatDefault big.Float ) // enumerates all types var ( - IntType = reflect.TypeOf(intDefault) - Int8Type = reflect.TypeOf(int8Default) - Int16Type = reflect.TypeOf(int16Default) - Int32Type = reflect.TypeOf(int32Default) - Int64Type = reflect.TypeOf(int64Default) + IntType = reflect.TypeOf((*int)(nil)).Elem() + Int8Type = reflect.TypeOf((*int8)(nil)).Elem() + Int16Type = reflect.TypeOf((*int16)(nil)).Elem() + Int32Type = reflect.TypeOf((*int32)(nil)).Elem() + Int64Type = reflect.TypeOf((*int64)(nil)).Elem() - UintType = reflect.TypeOf(uintDefault) - Uint8Type = reflect.TypeOf(uint8Default) - Uint16Type = reflect.TypeOf(uint16Default) - Uint32Type = reflect.TypeOf(uint32Default) - Uint64Type = reflect.TypeOf(uint64Default) + UintType = reflect.TypeOf((*uint)(nil)).Elem() + Uint8Type = reflect.TypeOf((*uint8)(nil)).Elem() + Uint16Type = reflect.TypeOf((*uint16)(nil)).Elem() + Uint32Type = reflect.TypeOf((*uint32)(nil)).Elem() + Uint64Type = reflect.TypeOf((*uint64)(nil)).Elem() - Float32Type = reflect.TypeOf(float32Default) - Float64Type = reflect.TypeOf(float64Default) + Float32Type = reflect.TypeOf((*float32)(nil)).Elem() + Float64Type = reflect.TypeOf((*float64)(nil)).Elem() - Complex64Type = reflect.TypeOf(complex64Default) - Complex128Type = reflect.TypeOf(complex128Default) + Complex64Type = reflect.TypeOf((*complex64)(nil)).Elem() + Complex128Type = reflect.TypeOf((*complex128)(nil)).Elem() - StringType = reflect.TypeOf(emptyString) - BoolType = reflect.TypeOf(boolDefault) - ByteType = reflect.TypeOf(byteDefault) + StringType = reflect.TypeOf((*string)(nil)).Elem() + BoolType = reflect.TypeOf((*bool)(nil)).Elem() + ByteType = reflect.TypeOf((*byte)(nil)).Elem() BytesType = reflect.SliceOf(ByteType) - TimeType = reflect.TypeOf(timeDefault) - BigFloatType = reflect.TypeOf(bigFloatDefault) -) - -// enumerates all types -var ( - PtrIntType = reflect.PtrTo(IntType) - PtrInt8Type = reflect.PtrTo(Int8Type) - PtrInt16Type = reflect.PtrTo(Int16Type) - PtrInt32Type = reflect.PtrTo(Int32Type) - PtrInt64Type = reflect.PtrTo(Int64Type) - - PtrUintType = reflect.PtrTo(UintType) - PtrUint8Type = reflect.PtrTo(Uint8Type) - PtrUint16Type = reflect.PtrTo(Uint16Type) - PtrUint32Type = reflect.PtrTo(Uint32Type) - PtrUint64Type = reflect.PtrTo(Uint64Type) - - PtrFloat32Type = reflect.PtrTo(Float32Type) - PtrFloat64Type = reflect.PtrTo(Float64Type) - - PtrComplex64Type = reflect.PtrTo(Complex64Type) - PtrComplex128Type = reflect.PtrTo(Complex128Type) - - PtrStringType = reflect.PtrTo(StringType) - PtrBoolType = reflect.PtrTo(BoolType) - PtrByteType = reflect.PtrTo(ByteType) - - PtrTimeType = reflect.PtrTo(TimeType) + TimeType = reflect.TypeOf((*time.Time)(nil)).Elem() + BigFloatType = reflect.TypeOf((*big.Float)(nil)).Elem() + NullFloat64Type = reflect.TypeOf((*sql.NullFloat64)(nil)).Elem() + NullStringType = reflect.TypeOf((*sql.NullString)(nil)).Elem() + NullInt32Type = reflect.TypeOf((*sql.NullInt32)(nil)).Elem() + NullInt64Type = reflect.TypeOf((*sql.NullInt64)(nil)).Elem() + NullBoolType = reflect.TypeOf((*sql.NullBool)(nil)).Elem() ) // Type2SQLType generate SQLType acorrding Go's type @@ -331,7 +285,7 @@ func Type2SQLType(t reflect.Type) (st SQLType) { case reflect.Complex64, reflect.Complex128: st = SQLType{Varchar, 64, 0} case reflect.Array, reflect.Slice, reflect.Map: - if t.Elem() == reflect.TypeOf(byteDefault) { + if t.Elem() == ByteType { st = SQLType{Blob, 0, 0} } else { st = SQLType{Text, 0, 0} @@ -343,6 +297,16 @@ func Type2SQLType(t reflect.Type) (st SQLType) { case reflect.Struct: if t.ConvertibleTo(TimeType) { st = SQLType{DateTime, 0, 0} + } else if t.ConvertibleTo(NullFloat64Type) { + st = SQLType{Double, 0, 0} + } else if t.ConvertibleTo(NullStringType) { + st = SQLType{Varchar, 255, 0} + } else if t.ConvertibleTo(NullInt32Type) { + st = SQLType{Integer, 0, 0} + } else if t.ConvertibleTo(NullInt64Type) { + st = SQLType{BigInt, 0, 0} + } else if t.ConvertibleTo(NullBoolType) { + st = SQLType{Boolean, 0, 0} } else { // TODO need to handle association struct st = SQLType{Text, 0, 0} @@ -360,25 +324,25 @@ func SQLType2Type(st SQLType) reflect.Type { name := strings.ToUpper(st.Name) switch name { case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, Serial: - return reflect.TypeOf(1) + return IntType case BigInt, BigSerial: - return reflect.TypeOf(int64(1)) + return Int64Type case Float, Real: - return reflect.TypeOf(float32(1)) + return Float32Type case Double: - return reflect.TypeOf(float64(1)) + return Float64Type case Char, NChar, Varchar, NVarchar, TinyText, Text, NText, MediumText, LongText, Enum, Set, Uuid, Clob, SysName: - return reflect.TypeOf("") + return StringType case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary, UniqueIdentifier: - return reflect.TypeOf([]byte{}) + return BytesType case Bool: - return reflect.TypeOf(true) + return BoolType case DateTime, Date, Time, TimeStamp, TimeStampz, SmallDateTime, Year: - return reflect.TypeOf(timeDefault) + return TimeType case Decimal, Numeric, Money, SmallMoney: - return reflect.TypeOf("") + return StringType default: - return reflect.TypeOf("") + return StringType } } diff --git a/tags/parser.go b/tags/parser.go index 5f9fd528..989ac7f9 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -7,6 +7,7 @@ package tags import ( "encoding/gob" "errors" + "fmt" "reflect" "strings" "sync" @@ -127,6 +128,25 @@ func addIndex(indexName string, table *schemas.Table, col *schemas.Column, index // ErrIgnoreField represents an error to ignore field var ErrIgnoreField = errors.New("field will be ignored") +func (parser *Parser) getSQLTypeByType(t reflect.Type) (schemas.SQLType, error) { + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() == reflect.Struct { + v, ok := parser.tableCache.Load(t) + if ok { + pkCols := v.(*schemas.Table).PKColumns() + if len(pkCols) == 1 { + return pkCols[0].SQLType, nil + } + if len(pkCols) > 1 { + return schemas.SQLType{}, fmt.Errorf("unsupported mulitiple primary key on cascade") + } + } + } + return schemas.Type2SQLType(t), nil +} + func (parser *Parser) parseFieldWithNoTag(fieldIndex int, field reflect.StructField, fieldValue reflect.Value) (*schemas.Column, error) { var sqlType schemas.SQLType if fieldValue.CanAddr() { @@ -137,7 +157,11 @@ func (parser *Parser) parseFieldWithNoTag(fieldIndex int, field reflect.StructFi if _, ok := fieldValue.Interface().(convert.Conversion); ok { sqlType = schemas.SQLType{Name: schemas.Text} } else { - sqlType = schemas.Type2SQLType(field.Type) + var err error + sqlType, err = parser.getSQLTypeByType(field.Type) + if err != nil { + return nil, err + } } col := schemas.NewColumn(parser.columnMapper.Obj2Table(field.Name), field.Name, sqlType, sqlType.DefaultLength, @@ -215,7 +239,11 @@ func (parser *Parser) parseFieldWithTags(table *schemas.Table, fieldIndex int, f } if col.SQLType.Name == "" { - col.SQLType = schemas.Type2SQLType(field.Type) + var err error + col.SQLType, err = parser.getSQLTypeByType(field.Type) + if err != nil { + return nil, err + } } if ctx.isUnsigned && col.SQLType.IsNumeric() && !strings.HasPrefix(col.SQLType.Name, "UNSIGNED") { col.SQLType.Name = "UNSIGNED " + col.SQLType.Name From bd1ca33ea5f5b6ebe964f52da3661a356918f356 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 5 Aug 2021 14:47:25 +0800 Subject: [PATCH 007/116] Update docs for pgx (#2024) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2024 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- README.md | 1 + README_CN.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index d7217fa2..2312b78e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Drivers for Go's sql package which currently support database/sql includes: * [Postgres](https://github.com/postgres/postgres) / [Cockroach](https://github.com/cockroachdb/cockroach) - [github.com/lib/pq](https://github.com/lib/pq) + - [github.com/jackc/pgx](https://github.com/jackc/pgx) * [SQLite](https://sqlite.org) - [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) diff --git a/README_CN.md b/README_CN.md index 36930faa..1f3b75e2 100644 --- a/README_CN.md +++ b/README_CN.md @@ -40,6 +40,7 @@ v1.0.0 相对于 v0.8.2 有以下不兼容的变更: * [Postgres](https://github.com/postgres/postgres) / [Cockroach](https://github.com/cockroachdb/cockroach) - [github.com/lib/pq](https://github.com/lib/pq) + - [github.com/jackc/pgx](https://github.com/jackc/pgx) * [SQLite](https://sqlite.org) - [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) From 4499c8c5b56d99b43cc4c7729d8beb549af89f2b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 6 Aug 2021 11:26:22 +0800 Subject: [PATCH 008/116] Fix import file bug (#2025) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2025 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- integrations/engine_test.go | 5 +++++ integrations/testdata/import2.sql | 3 +++ session_schema.go | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 integrations/testdata/import2.sql diff --git a/integrations/engine_test.go b/integrations/engine_test.go index cd309d6a..02b35a2c 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -229,6 +229,11 @@ func TestImport(t *testing.T) { _, err := sess.ImportFile("./testdata/import1.sql") assert.NoError(t, err) assert.NoError(t, sess.Commit()) + + assert.NoError(t, sess.Begin()) + _, err = sess.ImportFile("./testdata/import2.sql") + assert.NoError(t, err) + assert.NoError(t, sess.Commit()) } func TestDBVersion(t *testing.T) { diff --git a/integrations/testdata/import2.sql b/integrations/testdata/import2.sql new file mode 100644 index 00000000..469b096b --- /dev/null +++ b/integrations/testdata/import2.sql @@ -0,0 +1,3 @@ +CREATE TABLE IF NOT EXISTS `Core_Goods` (`Id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL, `GoodsSN` VARCHAR(20) NULL COMMENT '商品序列号', `GoodsSort` INT(11) NULL COMMENT '商品排序', `GoodsName` VARCHAR(100) NULL COMMENT '商品名称', `GoodsThumb` VARCHAR(500) NULL, `GoodsUnit` VARCHAR(255) NULL, `PerUnitNum` BIGINT(20) NULL, `GoodsState` TINYINT(4) NULL COMMENT '商品状态', `IsClose` TINYINT(1) DEFAULT 0 NULL COMMENT '关闭下单', `GoodsDesc` VARCHAR(200) NULL COMMENT '商品简介', `GoodsContent` TEXT NULL COMMENT '商品详情', `GoodsImages` TEXT NULL COMMENT '商品图片', `MinOrderNum` INT(11) NULL COMMENT '最少下单数', `MaxOrderNum` INT(11) NULL COMMENT '最大下单数', `CategoryId` INT(11) NULL COMMENT '商品分类', `SupplyPrice` BIGINT(20) NULL COMMENT '供货单价', `StockNum` INT(11) NULL COMMENT '库存数量,小于0不限制', `HandleRemarks` VARCHAR(255) NULL COMMENT '处理备注', `ParamsTemplate` TEXT NULL COMMENT '下单参数模板', `PriceTemplateId` INT(11) NULL COMMENT '加价模板id', `GoodsSnapshotId` INT(11) NULL COMMENT '当前快照id', `SupplierUserId` INT(11) NULL COMMENT '供货商用户id', `CanTui` TINYINT(1) DEFAULT 0 NULL COMMENT '是否可以申请退款', `CanRepeat` TINYINT(1) DEFAULT 1 NULL COMMENT '是否可以申请退款', `GoodsType` TINYINT(4) DEFAULT 1 NULL COMMENT '商品类型', `CreatedAt` DATETIME NULL COMMENT '创建时间', `UpdatedAt` DATETIME NULL COMMENT '修改时间', `LastHandlerAdminUserId` INT(11) NULL COMMENT '最后操作管理员', `GoodsMode` TINYINT(4) DEFAULT 0 NULL COMMENT '商品属性', `SaleTotal` INT(11) NULL COMMENT '总销量', `SaleMonth` INT(11) NULL COMMENT '月销量', `Notice` TEXT NULL COMMENT '商品公告', `AfterLunchOrderState` TINYINT(4) NULL COMMENT '下单后状态', `JoinMode` TINYINT(4) DEFAULT 1 NULL COMMENT '对接模式', `ApiOrderLunchConfig` TEXT NULL COMMENT '提交订单配置', `Version` BIGINT(20) DEFAULT 1 NULL) ENGINE=InnoDB; +INSERT INTO `Core_Goods` (`Id`, `GoodsSN`, `GoodsSort`, `GoodsName`, `GoodsThumb`, `GoodsUnit`, `PerUnitNum`, `GoodsState`, `IsClose`, `GoodsDesc`, `GoodsContent`, `GoodsImages`, `MinOrderNum`, `MaxOrderNum`, `CategoryId`, `SupplyPrice`, `StockNum`, `HandleRemarks`, `ParamsTemplate`, `PriceTemplateId`, `GoodsSnapshotId`, `SupplierUserId`, `CanTui`, `CanRepeat`, `GoodsType`, `CreatedAt`, `UpdatedAt`, `LastHandlerAdminUserId`, `GoodsMode`, `SaleTotal`, `SaleMonth`, `Notice`, `AfterLunchOrderState`, `JoinMode`, `ApiOrderLunchConfig`, `Version`) VALUES (42,'2107290140000432',91,' - ','','',1,2,0,'','--','[]',3,10000,10,5974060,-1,'','',1,0,10001,0,1,1,'2021-07-29 01:40:55','2021-07-30 18:28:59',10000,2,0,0,'',0,0,'{"url":"","method":"","type":"","succContactStr":"","data":null}',35); +INSERT INTO `Core_Goods` (`Id`, `GoodsSN`, `GoodsSort`, `GoodsName`, `GoodsThumb`, `GoodsUnit`, `PerUnitNum`, `GoodsState`, `IsClose`, `GoodsDesc`, `GoodsContent`, `GoodsImages`, `MinOrderNum`, `MaxOrderNum`, `CategoryId`, `SupplyPrice`, `StockNum`, `HandleRemarks`, `ParamsTemplate`, `PriceTemplateId`, `GoodsSnapshotId`, `SupplierUserId`, `CanTui`, `CanRepeat`, `GoodsType`, `CreatedAt`, `UpdatedAt`, `LastHandlerAdminUserId`, `GoodsMode`, `SaleTotal`, `SaleMonth`, `Notice`, `AfterLunchOrderState`, `JoinMode`, `ApiOrderLunchConfig`, `Version`) VALUES (43,'2107290140000433',90,' - ','','',1,2,0,'','','[]',3,10000,10,9064091,-1,'','',1,0,10001,0,1,1,'2021-07-29 01:40:55','2021-07-30 18:28:59',10000,2,0,0,'',0,0,'{"url":"","method":"","type":"","succContactStr":"","data":null}',39); \ No newline at end of file diff --git a/session_schema.go b/session_schema.go index 7cfcb626..2e64350f 100644 --- a/session_schema.go +++ b/session_schema.go @@ -469,7 +469,7 @@ func (session *Session) Import(r io.Reader) ([]sql.Result, error) { startComment = false } } else { - if i > 0 && data[i-1] == '-' && data[i] == '-' { + if !inSingleQuote && i > 0 && data[i-1] == '-' && data[i] == '-' { startComment = true continue } From ad1a386b5ef5b738448ea6a0f6996482103a85aa Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 8 Aug 2021 11:00:11 +0800 Subject: [PATCH 009/116] Fix wrong comment (#2027) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2027 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- integrations/session_find_test.go | 40 +++++++++++++++++++++++++++++++ session.go | 9 +++---- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index 80f3b72c..1cbf5e42 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -1045,3 +1045,43 @@ func TestUpdateFind(t *testing.T) { err = session.Where("id = ?", tuf.Id).Find(&tufs) assert.NoError(t, err) } + +func TestFindAnonymousStruct(t *testing.T) { + type FindAnonymousStruct struct { + Id int64 + Name string + Age int + IsMan bool + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(FindAnonymousStruct)) + + cnt, err := testEngine.Insert(&FindAnonymousStruct{ + Name: "xlw", + Age: 42, + IsMan: true, + }) + assert.EqualValues(t, 1, cnt) + assert.NoError(t, err) + + var findRes = make([]struct { + Id int64 + Name string + }, 0) + err = testEngine.Table(new(FindAnonymousStruct)).Find(&findRes) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(findRes)) + assert.EqualValues(t, 1, findRes[0].Id) + assert.EqualValues(t, "xlw", findRes[0].Name) + + findRes = make([]struct { + Id int64 + Name string + }, 0) + err = testEngine.Select("`id`,`name`").Table(new(FindAnonymousStruct)).Find(&findRes) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(findRes)) + assert.EqualValues(t, 1, findRes[0].Id) + assert.EqualValues(t, "xlw", findRes[0].Name) +} diff --git a/session.go b/session.go index 304d1079..339aa73f 100644 --- a/session.go +++ b/session.go @@ -34,7 +34,7 @@ type ErrFieldIsNotExist struct { } func (e ErrFieldIsNotExist) Error() string { - return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName) + return fmt.Sprintf("field %s is not exist on table %s", e.FieldName, e.TableName) } // ErrFieldIsNotValid is not valid @@ -677,10 +677,11 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b col, fieldValue, err := session.getField(dataStruct, table, colName, idx) if err != nil { - if _, ok := err.(ErrFieldIsNotValid); !ok { - session.engine.logger.Warnf("%v", err) + if _, ok := err.(ErrFieldIsNotExist); ok { + continue + } else { + return nil, err } - continue } if fieldValue == nil { continue From 3a8ae761c543a33fa62e69b6ffb4936a542c9bca Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 8 Aug 2021 11:01:02 +0800 Subject: [PATCH 010/116] Add changelog for v1.2.1 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f669da8e..e6a9b3a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log. +## [1.2.1](https://gitea.com/xorm/xorm/releases/tag/1.2.1) - 2021-08-08 + +* FEATURES + * Add pgx driver support (#1795) +* BUGFIXES + * Fix wrong comment (#2027) + * Fix import file bug (#2025) +* ENHANCEMENTS + * Fix timesatmp (#2021) + ## [1.2.0](https://gitea.com/xorm/xorm/releases/tag/1.2.0) - 2021-08-04 * BREAKING From fae164488c3b4058657a0d6c7d73aa3c7973e4a2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 8 Aug 2021 15:35:44 +0800 Subject: [PATCH 011/116] Fix problem on README (#2028) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2028 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- README.md | 2 +- README_CN.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2312b78e..f4bee6b6 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Drivers for Go's sql package which currently support database/sql includes: - [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) * Oracle - - [github.com/godror/godror)](https://github.com/godror/godror) (experiment) + - [github.com/godror/godror](https://github.com/godror/godror) (experiment) - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) ## Installation diff --git a/README_CN.md b/README_CN.md index 1f3b75e2..500bb1fb 100644 --- a/README_CN.md +++ b/README_CN.md @@ -50,7 +50,7 @@ v1.0.0 相对于 v0.8.2 有以下不兼容的变更: - [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) * Oracle - - [github.com/godror/godror)](https://github.com/godror/godror) (试验性支持) + - [github.com/godror/godror](https://github.com/godror/godror) (试验性支持) - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持) ## 安装 @@ -65,7 +65,7 @@ v1.0.0 相对于 v0.8.2 有以下不兼容的变更: # 快速开始 -* 第一步创建引擎,driverName, dataSourceName和database/sql接口相同 +* 第一步创建引擎,`driverName`, `dataSourceName` 和 `database/sql` 接口相同 ```Go engine, err := xorm.NewEngine(driverName, dataSourceName) @@ -103,7 +103,7 @@ engineGroup, err := xorm.NewEngineGroup(masterEngine, []*Engine{slave1Engine, sl 所有使用 `engine` 都可以简单的用 `engineGroup` 来替换。 -* `Query` 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte。`QueryString` 返回 []map[string]string, `QueryInterface` 返回 `[]map[string]interface{}`. +* `Query` 最原始的也支持SQL语句查询,返回的结果类型为 `[]map[string][]byte`。`QueryString` 返回 `[]map[string]string`, `QueryInterface` 返回 `[]map[string]interface{}`. ```Go results, err := engine.Query("select * from user") From cace6862e297b44505543f91efa73c92c2d636ed Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 10 Aug 2021 23:20:53 +0800 Subject: [PATCH 012/116] Move convert back to xorm.io/xorm/convert (#2030) Since `conversion.Conversion` has been referenced by external package, it should not be moved as internal package. Reviewed-on: https://gitea.com/xorm/xorm/pulls/2030 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- {internal/convert => convert}/bool.go | 0 {internal/convert => convert}/conversion.go | 0 {internal/convert => convert}/float.go | 0 {internal/convert => convert}/int.go | 0 {internal/convert => convert}/interface.go | 0 {internal/convert => convert}/scanner.go | 0 {internal/convert => convert}/string.go | 0 {internal/convert => convert}/time.go | 0 {internal/convert => convert}/time_test.go | 0 integrations/session_get_test.go | 2 +- integrations/types_test.go | 2 +- internal/statements/statement.go | 2 +- internal/statements/update.go | 2 +- internal/statements/values.go | 2 +- scan.go | 2 +- session.go | 2 +- session_find.go | 2 +- session_get.go | 2 +- session_insert.go | 2 +- tags/parser.go | 2 +- 20 files changed, 11 insertions(+), 11 deletions(-) rename {internal/convert => convert}/bool.go (100%) rename {internal/convert => convert}/conversion.go (100%) rename {internal/convert => convert}/float.go (100%) rename {internal/convert => convert}/int.go (100%) rename {internal/convert => convert}/interface.go (100%) rename {internal/convert => convert}/scanner.go (100%) rename {internal/convert => convert}/string.go (100%) rename {internal/convert => convert}/time.go (100%) rename {internal/convert => convert}/time_test.go (100%) diff --git a/internal/convert/bool.go b/convert/bool.go similarity index 100% rename from internal/convert/bool.go rename to convert/bool.go diff --git a/internal/convert/conversion.go b/convert/conversion.go similarity index 100% rename from internal/convert/conversion.go rename to convert/conversion.go diff --git a/internal/convert/float.go b/convert/float.go similarity index 100% rename from internal/convert/float.go rename to convert/float.go diff --git a/internal/convert/int.go b/convert/int.go similarity index 100% rename from internal/convert/int.go rename to convert/int.go diff --git a/internal/convert/interface.go b/convert/interface.go similarity index 100% rename from internal/convert/interface.go rename to convert/interface.go diff --git a/internal/convert/scanner.go b/convert/scanner.go similarity index 100% rename from internal/convert/scanner.go rename to convert/scanner.go diff --git a/internal/convert/string.go b/convert/string.go similarity index 100% rename from internal/convert/string.go rename to convert/string.go diff --git a/internal/convert/time.go b/convert/time.go similarity index 100% rename from internal/convert/time.go rename to convert/time.go diff --git a/internal/convert/time_test.go b/convert/time_test.go similarity index 100% rename from internal/convert/time_test.go rename to convert/time_test.go diff --git a/integrations/session_get_test.go b/integrations/session_get_test.go index c0376c70..99ecd462 100644 --- a/integrations/session_get_test.go +++ b/integrations/session_get_test.go @@ -14,7 +14,7 @@ import ( "xorm.io/xorm" "xorm.io/xorm/contexts" - "xorm.io/xorm/internal/convert" + "xorm.io/xorm/convert" "xorm.io/xorm/schemas" "github.com/shopspring/decimal" diff --git a/integrations/types_test.go b/integrations/types_test.go index 4bdbb6fe..b08dfec5 100644 --- a/integrations/types_test.go +++ b/integrations/types_test.go @@ -13,7 +13,7 @@ import ( "testing" "xorm.io/xorm" - "xorm.io/xorm/internal/convert" + "xorm.io/xorm/convert" "xorm.io/xorm/internal/json" "xorm.io/xorm/schemas" diff --git a/internal/statements/statement.go b/internal/statements/statement.go index adbeb1c2..1fcc0bba 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -15,8 +15,8 @@ import ( "xorm.io/builder" "xorm.io/xorm/contexts" + "xorm.io/xorm/convert" "xorm.io/xorm/dialects" - "xorm.io/xorm/internal/convert" "xorm.io/xorm/internal/json" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" diff --git a/internal/statements/update.go b/internal/statements/update.go index a8a174f9..40159e0c 100644 --- a/internal/statements/update.go +++ b/internal/statements/update.go @@ -11,8 +11,8 @@ import ( "reflect" "time" + "xorm.io/xorm/convert" "xorm.io/xorm/dialects" - "xorm.io/xorm/internal/convert" "xorm.io/xorm/internal/json" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" diff --git a/internal/statements/values.go b/internal/statements/values.go index 7351fb79..4c1360ed 100644 --- a/internal/statements/values.go +++ b/internal/statements/values.go @@ -12,8 +12,8 @@ import ( "reflect" "time" + "xorm.io/xorm/convert" "xorm.io/xorm/dialects" - "xorm.io/xorm/internal/convert" "xorm.io/xorm/internal/json" "xorm.io/xorm/schemas" ) diff --git a/scan.go b/scan.go index b712f18a..56d3c9d6 100644 --- a/scan.go +++ b/scan.go @@ -11,9 +11,9 @@ import ( "reflect" "time" + "xorm.io/xorm/convert" "xorm.io/xorm/core" "xorm.io/xorm/dialects" - "xorm.io/xorm/internal/convert" "xorm.io/xorm/schemas" ) diff --git a/session.go b/session.go index 339aa73f..563dfaf2 100644 --- a/session.go +++ b/session.go @@ -19,8 +19,8 @@ import ( "strings" "xorm.io/xorm/contexts" + "xorm.io/xorm/convert" "xorm.io/xorm/core" - "xorm.io/xorm/internal/convert" "xorm.io/xorm/internal/json" "xorm.io/xorm/internal/statements" "xorm.io/xorm/log" diff --git a/session_find.go b/session_find.go index 82a302b7..df3bd85d 100644 --- a/session_find.go +++ b/session_find.go @@ -10,7 +10,7 @@ import ( "xorm.io/builder" "xorm.io/xorm/caches" - "xorm.io/xorm/internal/convert" + "xorm.io/xorm/convert" "xorm.io/xorm/internal/statements" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" diff --git a/session_get.go b/session_get.go index 617ca169..08172524 100644 --- a/session_get.go +++ b/session_get.go @@ -15,8 +15,8 @@ import ( "time" "xorm.io/xorm/caches" + "xorm.io/xorm/convert" "xorm.io/xorm/core" - "xorm.io/xorm/internal/convert" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) diff --git a/session_insert.go b/session_insert.go index 78f0e555..a8f365c7 100644 --- a/session_insert.go +++ b/session_insert.go @@ -12,8 +12,8 @@ import ( "strings" "time" + "xorm.io/xorm/convert" "xorm.io/xorm/dialects" - "xorm.io/xorm/internal/convert" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) diff --git a/tags/parser.go b/tags/parser.go index 989ac7f9..efee11e7 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -15,8 +15,8 @@ import ( "unicode" "xorm.io/xorm/caches" + "xorm.io/xorm/convert" "xorm.io/xorm/dialects" - "xorm.io/xorm/internal/convert" "xorm.io/xorm/names" "xorm.io/xorm/schemas" ) From 289c27ebec863c01d9542322114a144a863f464a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 11 Aug 2021 09:29:27 +0800 Subject: [PATCH 013/116] add changelog for 1.2.2 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6a9b3a5..0f0f93e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log. +## [1.2.2](https://gitea.com/xorm/xorm/releases/tag/1.2.2) - 2021-08-11 + +* MISC + * Move convert back to xorm.io/xorm/convert (#2030) + ## [1.2.1](https://gitea.com/xorm/xorm/releases/tag/1.2.1) - 2021-08-08 * FEATURES From dbc2de380b1614e4ef8f0b7ba3d5618fdfec1cb0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 11 Aug 2021 21:17:23 +0800 Subject: [PATCH 014/116] Refactor find (#2031) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2031 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- convert/conversion.go | 3 +++ internal/utils/new.go | 25 +++++++++++++++++ session_find.go | 62 +++++++++++++++---------------------------- 3 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 internal/utils/new.go diff --git a/convert/conversion.go b/convert/conversion.go index 096fcfaf..78a9fd78 100644 --- a/convert/conversion.go +++ b/convert/conversion.go @@ -325,6 +325,9 @@ func AssignValue(dv reflect.Value, src interface{}) error { if src == nil { return nil } + if v, ok := src.(*interface{}); ok { + return AssignValue(dv, *v) + } if dv.Type().Implements(scannerType) { return dv.Interface().(sql.Scanner).Scan(src) diff --git a/internal/utils/new.go b/internal/utils/new.go new file mode 100644 index 00000000..e3b4eae8 --- /dev/null +++ b/internal/utils/new.go @@ -0,0 +1,25 @@ +// 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 utils + +import "reflect" + +// New creates a value according type +func New(tp reflect.Type, length, cap int) reflect.Value { + switch tp.Kind() { + case reflect.Slice: + slice := reflect.MakeSlice(tp, length, cap) + x := reflect.New(slice.Type()) + x.Elem().Set(slice) + return x + case reflect.Map: + mp := reflect.MakeMapWithSize(tp, cap) + x := reflect.New(mp.Type()) + x.Elem().Set(mp) + return x + default: + return reflect.New(tp) + } +} diff --git a/session_find.go b/session_find.go index df3bd85d..47a3d308 100644 --- a/session_find.go +++ b/session_find.go @@ -161,6 +161,16 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect.Value, sqlStr string, args ...interface{}) error { + elemType := containerValue.Type().Elem() + var isPointer bool + if elemType.Kind() == reflect.Ptr { + isPointer = true + elemType = elemType.Elem() + } + if elemType.Kind() == reflect.Ptr { + return errors.New("pointer to pointer is not supported") + } + rows, err := session.queryRows(sqlStr, args...) if err != nil { return err @@ -177,31 +187,8 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect return err } - var newElemFunc func(fields []string) reflect.Value - elemType := containerValue.Type().Elem() - var isPointer bool - if elemType.Kind() == reflect.Ptr { - isPointer = true - elemType = elemType.Elem() - } - if elemType.Kind() == reflect.Ptr { - return errors.New("pointer to pointer is not supported") - } - - newElemFunc = func(fields []string) reflect.Value { - switch elemType.Kind() { - case reflect.Slice: - slice := reflect.MakeSlice(elemType, len(fields), len(fields)) - x := reflect.New(slice.Type()) - x.Elem().Set(slice) - return x - case reflect.Map: - mp := reflect.MakeMap(elemType) - x := reflect.New(mp.Type()) - x.Elem().Set(mp) - return x - } - return reflect.New(elemType) + var newElemFunc = func(fields []string) reflect.Value { + return utils.New(elemType, len(fields), len(fields)) } var containerValueSetFunc func(*reflect.Value, schemas.PK) error @@ -226,10 +213,15 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect containerValueSetFunc = func(newValue *reflect.Value, pk schemas.PK) error { keyValue := reflect.New(keyType) - err := convertPKToValue(table, keyValue.Interface(), pk) - if err != nil { - return err + cols := table.PKColumns() + if len(cols) == 1 { + if err := convert.AssignValue(keyValue, pk[0]); err != nil { + return err + } + } else { + keyValue.Set(reflect.ValueOf(&pk)) } + if isPointer { containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem().Addr()) } else { @@ -241,8 +233,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect if elemType.Kind() == reflect.Struct { var newValue = newElemFunc(fields) - dataStruct := utils.ReflectValue(newValue.Interface()) - tb, err := session.engine.tagParser.ParseWithCache(dataStruct) + tb, err := session.engine.tagParser.ParseWithCache(newValue) if err != nil { return err } @@ -266,7 +257,6 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect default: err = rows.Scan(bean) } - if err != nil { return err } @@ -278,16 +268,6 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect return rows.Err() } -func convertPKToValue(table *schemas.Table, dst interface{}, pk schemas.PK) error { - cols := table.PKColumns() - if len(cols) == 1 { - return convert.Assign(dst, pk[0], nil, nil) - } - - dst = pk - return nil -} - func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) { if !session.canCache() || utils.IndexNoCase(sqlStr, "having") != -1 || From 7d458b4fcf3cfce01719223ffdf563f5768257d5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 14 Aug 2021 10:57:47 +0800 Subject: [PATCH 015/116] refactor create table for postgres (#2034) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2034 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- dialects/dialect.go | 33 +++++++++++++++++++++++++ dialects/mssql.go | 39 ++++++++++++++++-------------- dialects/mysql.go | 57 +++++++++++++++++++++++--------------------- dialects/postgres.go | 35 --------------------------- dialects/sqlite3.go | 35 --------------------------- 5 files changed, 84 insertions(+), 115 deletions(-) diff --git a/dialects/dialect.go b/dialects/dialect.go index fc11eac1..b6c0853a 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -103,6 +103,39 @@ func (db *Base) URI() *URI { return db.uri } +// CreateTableSQL implements Dialect +func (db *Base) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { + if tableName == "" { + tableName = table.Name + } + + quoter := db.dialect.Quoter() + var b strings.Builder + b.WriteString("CREATE TABLE IF NOT EXISTS ") + quoter.QuoteTo(&b, tableName) + b.WriteString(" (") + + for i, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + b.WriteString(s) + + if i != len(table.ColumnsSeq())-1 { + b.WriteString(", ") + } + } + + if len(table.PrimaryKeys) > 1 { + b.WriteString(", PRIMARY KEY (") + b.WriteString(quoter.Join(table.PrimaryKeys, ",")) + b.WriteString(")") + } + + b.WriteString(")") + + return []string{b.String()}, false +} + // DropTableSQL returns drop table SQL func (db *Base) DropTableSQL(tableName string) (string, bool) { quote := db.dialect.Quoter().Quote diff --git a/dialects/mssql.go b/dialects/mssql.go index 2121e71d..ab010eb0 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -626,34 +626,37 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? } func (db *mssql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { - var sql string if tableName == "" { tableName = table.Name } - sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE " + quoter := db.dialect.Quoter() + var b strings.Builder + b.WriteString("IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '") + quoter.QuoteTo(&b, tableName) + b.WriteString("' ) CREATE TABLE ") + quoter.QuoteTo(&b, tableName) + b.WriteString(" (") - sql += db.Quoter().Quote(tableName) + " (" - - pkList := table.PrimaryKeys - - for _, colName := range table.ColumnsSeq() { + for i, colName := range table.ColumnsSeq() { col := table.GetColumn(colName) - s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1) - sql += s - sql = strings.TrimSpace(sql) - sql += ", " + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + b.WriteString(s) + + if i != len(table.ColumnsSeq())-1 { + b.WriteString(", ") + } } - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += strings.Join(pkList, ",") - sql += " ), " + if len(table.PrimaryKeys) > 1 { + b.WriteString(", PRIMARY KEY (") + b.WriteString(quoter.Join(table.PrimaryKeys, ",")) + b.WriteString(")") } - sql = sql[:len(sql)-2] + ")" - sql += ";" - return []string{sql}, true + b.WriteString(")") + + return []string{b.String()}, true } func (db *mssql) ForUpdateSQL(query string) string { diff --git a/dialects/mysql.go b/dialects/mysql.go index 21128527..0489904a 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -626,42 +626,43 @@ func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName } func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { - var sql = "CREATE TABLE IF NOT EXISTS " if tableName == "" { tableName = table.Name } - quoter := db.Quoter() + quoter := db.dialect.Quoter() + var b strings.Builder + b.WriteString("CREATE TABLE IF NOT EXISTS ") + quoter.QuoteTo(&b, tableName) + b.WriteString(" (") - sql += quoter.Quote(tableName) - sql += " (" + for i, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + b.WriteString(s) - if len(table.ColumnsSeq()) > 0 { - pkList := table.PrimaryKeys - - for _, colName := range table.ColumnsSeq() { - col := table.GetColumn(colName) - s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1) - sql += s - sql = strings.TrimSpace(sql) - if len(col.Comment) > 0 { - sql += " COMMENT '" + col.Comment + "'" - } - sql += ", " + if len(col.Comment) > 0 { + b.WriteString(" COMMENT '") + b.WriteString(col.Comment) + b.WriteString("'") } - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += quoter.Join(pkList, ",") - sql += " ), " + if i != len(table.ColumnsSeq())-1 { + b.WriteString(", ") } - - sql = sql[:len(sql)-2] } - sql += ")" + + if len(table.PrimaryKeys) > 1 { + b.WriteString(", PRIMARY KEY (") + b.WriteString(quoter.Join(table.PrimaryKeys, ",")) + b.WriteString(")") + } + + b.WriteString(")") if table.StoreEngine != "" { - sql += " ENGINE=" + table.StoreEngine + b.WriteString(" ENGINE=") + b.WriteString(table.StoreEngine) } var charset = table.Charset @@ -669,13 +670,15 @@ func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]strin charset = db.URI().Charset } if len(charset) != 0 { - sql += " DEFAULT CHARSET " + charset + b.WriteString(" DEFAULT CHARSET ") + b.WriteString(charset) } if db.rowFormat != "" { - sql += " ROW_FORMAT=" + db.rowFormat + b.WriteString(" ROW_FORMAT=") + b.WriteString(db.rowFormat) } - return []string{sql}, true + return []string{b.String()}, true } func (db *mysql) Filters() []Filter { diff --git a/dialects/postgres.go b/dialects/postgres.go index 96ebfc85..6b5a8b2f 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -965,41 +965,6 @@ func (db *postgres) AutoIncrStr() string { return "" } -func (db *postgres) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { - var sql string - sql = "CREATE TABLE IF NOT EXISTS " - if tableName == "" { - tableName = table.Name - } - - quoter := db.Quoter() - sql += quoter.Quote(tableName) - sql += " (" - - if len(table.ColumnsSeq()) > 0 { - pkList := table.PrimaryKeys - - for _, colName := range table.ColumnsSeq() { - col := table.GetColumn(colName) - s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1) - sql += s - sql = strings.TrimSpace(sql) - sql += ", " - } - - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += quoter.Join(pkList, ",") - sql += " ), " - } - - sql = sql[:len(sql)-2] - } - sql += ")" - - return []string{sql}, true -} - func (db *postgres) IndexCheckSQL(tableName, idxName string) (string, []interface{}) { if len(db.getSchema()) == 0 { args := []interface{}{tableName, idxName} diff --git a/dialects/sqlite3.go b/dialects/sqlite3.go index ac17fd92..4eba8dad 100644 --- a/dialects/sqlite3.go +++ b/dialects/sqlite3.go @@ -285,41 +285,6 @@ func (db *sqlite3) DropIndexSQL(tableName string, index *schemas.Index) string { return fmt.Sprintf("DROP INDEX %v", db.Quoter().Quote(idxName)) } -func (db *sqlite3) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { - var sql string - sql = "CREATE TABLE IF NOT EXISTS " - if tableName == "" { - tableName = table.Name - } - - quoter := db.Quoter() - sql += quoter.Quote(tableName) - sql += " (" - - if len(table.ColumnsSeq()) > 0 { - pkList := table.PrimaryKeys - - for _, colName := range table.ColumnsSeq() { - col := table.GetColumn(colName) - s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1) - sql += s - sql = strings.TrimSpace(sql) - sql += ", " - } - - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += quoter.Join(pkList, ",") - sql += " ), " - } - - sql = sql[:len(sql)-2] - } - sql += ")" - - return []string{sql}, true -} - func (db *sqlite3) ForUpdateSQL(query string) string { return query } From 7cd6a74c9fe06f6a2c7424a71ca692d985a0796c Mon Sep 17 00:00:00 2001 From: yedf Date: Sat, 21 Aug 2021 11:30:31 +0800 Subject: [PATCH 016/116] expose sql.Tx (#2036) Co-authored-by: yedongfu Reviewed-on: https://gitea.com/xorm/xorm/pulls/2036 Co-authored-by: yedf Co-committed-by: yedf --- session.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/session.go b/session.go index 563dfaf2..499b7df4 100644 --- a/session.go +++ b/session.go @@ -174,6 +174,11 @@ func (session *Session) Engine() *Engine { return session.engine } +// Tx returns session tx +func (session *Session) Tx() *core.Tx { + return session.tx +} + func (session *Session) getQueryer() core.Queryer { if session.tx != nil { return session.tx From c29b9649a91dedda70d1f69bf9072debf440a993 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Aug 2021 13:46:08 +0800 Subject: [PATCH 017/116] Add dameng support (#2007) driver: https://gitee.com/travelliu/dm docker: https://download.dameng.com/eco/dm8/dm8_docker.tar fix #1837 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2007 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- .drone.yml | 36 + Makefile | 17 +- convert/conversion.go | 4 +- dialects/dameng.go | 1181 ++++++++++++++++++++++++++ dialects/dialect.go | 72 +- dialects/mssql.go | 10 +- dialects/mysql.go | 11 +- dialects/oracle.go | 10 +- dialects/postgres.go | 6 + dialects/sqlite3.go | 6 + engine.go | 76 +- go.mod | 1 + go.sum | 5 +- integrations/cache_test.go | 10 +- integrations/engine_dm_test.go | 13 + integrations/engine_test.go | 5 +- integrations/performance_test.go | 2 +- integrations/session_cols_test.go | 8 +- integrations/session_cond_test.go | 29 +- integrations/session_count_test.go | 12 +- integrations/session_exist_test.go | 48 +- integrations/session_find_test.go | 104 +-- integrations/session_get_test.go | 85 +- integrations/session_insert_test.go | 35 +- integrations/session_iterate_test.go | 2 +- integrations/session_query_test.go | 59 +- integrations/session_raw_test.go | 10 +- integrations/session_schema_test.go | 5 +- integrations/session_test.go | 2 +- integrations/session_tx_test.go | 14 +- integrations/session_update_test.go | 56 +- integrations/tags_test.go | 8 +- integrations/time_test.go | 8 +- integrations/types_null_test.go | 68 +- integrations/types_test.go | 10 +- internal/statements/insert.go | 47 +- internal/statements/statement.go | 14 +- internal/utils/name.go | 6 + internal/utils/slice.go | 13 +- scan.go | 5 +- schemas/quote_test.go | 4 + schemas/type.go | 5 + session.go | 3 + session_get.go | 3 - session_insert.go | 66 +- session_schema.go | 50 +- session_update.go | 6 +- 47 files changed, 1852 insertions(+), 398 deletions(-) create mode 100644 dialects/dameng.go create mode 100644 integrations/engine_dm_test.go diff --git a/.drone.yml b/.drone.yml index 34b9a514..faaeca17 100644 --- a/.drone.yml +++ b/.drone.yml @@ -363,6 +363,41 @@ services: commands: - /cockroach/cockroach start --insecure +# --- +# kind: pipeline +# name: test-dameng +# depends_on: +# - test-cockroach +# trigger: +# ref: +# - refs/heads/master +# - refs/pull/*/head +# steps: +# - name: test-dameng +# pull: never +# image: golang:1.15 +# volumes: +# - name: cache +# path: /go/pkg/mod +# environment: +# TEST_DAMENG_HOST: "dameng:5236" +# TEST_DAMENG_USERNAME: SYSDBA +# TEST_DAMENG_PASSWORD: SYSDBA +# commands: +# - sleep 30 +# - make test-dameng + +# volumes: +# - name: cache +# host: +# path: /tmp/cache + +# services: +# - name: dameng +# image: lunny/dm:v1.0 +# commands: +# - /bin/bash /startDm.sh + --- kind: pipeline name: merge_coverage @@ -374,6 +409,7 @@ depends_on: - test-mssql - test-tidb - test-cockroach + #- test-dameng trigger: ref: - refs/heads/master diff --git a/Makefile b/Makefile index e986082e..e9bd4129 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,10 @@ TEST_TIDB_DBNAME ?= xorm_test TEST_TIDB_USERNAME ?= root TEST_TIDB_PASSWORD ?= +TEST_DAMENG_HOST ?= dameng:5236 +TEST_DAMENG_USERNAME ?= SYSDBA +TEST_DAMENG_PASSWORD ?= SYSDBA + TEST_CACHE_ENABLE ?= false TEST_QUOTE_POLICY ?= always @@ -240,7 +244,6 @@ test-sqlite\#%: go-check $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -cache=$(TEST_CACHE_ENABLE) -db=sqlite -conn_str="./test.db?cache=shared&mode=rwc" \ -quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic - .PNONY: test-tidb test-tidb: go-check $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=mysql -cache=$(TEST_CACHE_ENABLE) -ignore_select_update=true \ @@ -253,6 +256,18 @@ test-tidb\#%: go-check -conn_str="$(TEST_TIDB_USERNAME):$(TEST_TIDB_PASSWORD)@tcp($(TEST_TIDB_HOST))/$(TEST_TIDB_DBNAME)" \ -quote=$(TEST_QUOTE_POLICY) -coverprofile=tidb.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic +.PNONY: test-dameng +test-dameng: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=dm -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \ + -conn_str="dm://$(TEST_DAMENG_USERNAME):$(TEST_DAMENG_PASSWORD)@$(TEST_DAMENG_HOST)" \ + -coverprofile=dameng.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m + +.PHONY: test-dameng\#% +test-dameng\#%: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -db=dm -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \ + -conn_str="dm://$(TEST_DAMENG_USERNAME):$(TEST_DAMENG_PASSWORD)@$(TEST_DAMENG_HOST)" \ + -coverprofile=dameng.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m + .PHONY: vet vet: $(GO) vet $(shell $(GO) list ./...) diff --git a/convert/conversion.go b/convert/conversion.go index 78a9fd78..b69e345c 100644 --- a/convert/conversion.go +++ b/convert/conversion.go @@ -283,11 +283,9 @@ func Assign(dest, src interface{}, originalLocation *time.Location, convertedLoc } } - var sv reflect.Value - switch d := dest.(type) { case *string: - sv = reflect.ValueOf(src) + var sv = reflect.ValueOf(src) switch sv.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, diff --git a/dialects/dameng.go b/dialects/dameng.go new file mode 100644 index 00000000..5ba0cfb5 --- /dev/null +++ b/dialects/dameng.go @@ -0,0 +1,1181 @@ +// 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 dialects + +import ( + "context" + "database/sql" + "errors" + "fmt" + "net/url" + "strconv" + "strings" + + "xorm.io/xorm/convert" + "xorm.io/xorm/core" + "xorm.io/xorm/internal/utils" + "xorm.io/xorm/schemas" +) + +func init() { + RegisterDriver("dm", &damengDriver{}) + RegisterDialect(schemas.DAMENG, func() Dialect { + return &dameng{} + }) +} + +var ( + damengReservedWords = map[string]bool{ + "ACCESS": true, + "ACCOUNT": true, + "ACTIVATE": true, + "ADD": true, + "ADMIN": true, + "ADVISE": true, + "AFTER": true, + "ALL": true, + "ALL_ROWS": true, + "ALLOCATE": true, + "ALTER": true, + "ANALYZE": true, + "AND": true, + "ANY": true, + "ARCHIVE": true, + "ARCHIVELOG": true, + "ARRAY": true, + "AS": true, + "ASC": true, + "AT": true, + "AUDIT": true, + "AUTHENTICATED": true, + "AUTHORIZATION": true, + "AUTOEXTEND": true, + "AUTOMATIC": true, + "BACKUP": true, + "BECOME": true, + "BEFORE": true, + "BEGIN": true, + "BETWEEN": true, + "BFILE": true, + "BITMAP": true, + "BLOB": true, + "BLOCK": true, + "BODY": true, + "BY": true, + "CACHE": true, + "CACHE_INSTANCES": true, + "CANCEL": true, + "CASCADE": true, + "CAST": true, + "CFILE": true, + "CHAINED": true, + "CHANGE": true, + "CHAR": true, + "CHAR_CS": true, + "CHARACTER": true, + "CHECK": true, + "CHECKPOINT": true, + "CHOOSE": true, + "CHUNK": true, + "CLEAR": true, + "CLOB": true, + "CLONE": true, + "CLOSE": true, + "CLOSE_CACHED_OPEN_CURSORS": true, + "CLUSTER": true, + "COALESCE": true, + "COLUMN": true, + "COLUMNS": true, + "COMMENT": true, + "COMMIT": true, + "COMMITTED": true, + "COMPATIBILITY": true, + "COMPILE": true, + "COMPLETE": true, + "COMPOSITE_LIMIT": true, + "COMPRESS": true, + "COMPUTE": true, + "CONNECT": true, + "CONNECT_TIME": true, + "CONSTRAINT": true, + "CONSTRAINTS": true, + "CONTENTS": true, + "CONTINUE": true, + "CONTROLFILE": true, + "CONVERT": true, + "COST": true, + "CPU_PER_CALL": true, + "CPU_PER_SESSION": true, + "CREATE": true, + "CURRENT": true, + "CURRENT_SCHEMA": true, + "CURREN_USER": true, + "CURSOR": true, + "CYCLE": true, + "DANGLING": true, + "DATABASE": true, + "DATAFILE": true, + "DATAFILES": true, + "DATAOBJNO": true, + "DATE": true, + "DBA": true, + "DBHIGH": true, + "DBLOW": true, + "DBMAC": true, + "DEALLOCATE": true, + "DEBUG": true, + "DEC": true, + "DECIMAL": true, + "DECLARE": true, + "DEFAULT": true, + "DEFERRABLE": true, + "DEFERRED": true, + "DEGREE": true, + "DELETE": true, + "DEREF": true, + "DESC": true, + "DIRECTORY": true, + "DISABLE": true, + "DISCONNECT": true, + "DISMOUNT": true, + "DISTINCT": true, + "DISTRIBUTED": true, + "DML": true, + "DOUBLE": true, + "DROP": true, + "DUMP": true, + "EACH": true, + "ELSE": true, + "ENABLE": true, + "END": true, + "ENFORCE": true, + "ENTRY": true, + "ESCAPE": true, + "EXCEPT": true, + "EXCEPTIONS": true, + "EXCHANGE": true, + "EXCLUDING": true, + "EXCLUSIVE": true, + "EXECUTE": true, + "EXISTS": true, + "EXPIRE": true, + "EXPLAIN": true, + "EXTENT": true, + "EXTENTS": true, + "EXTERNALLY": true, + "FAILED_LOGIN_ATTEMPTS": true, + "FALSE": true, + "FAST": true, + "FILE": true, + "FIRST_ROWS": true, + "FLAGGER": true, + "FLOAT": true, + "FLOB": true, + "FLUSH": true, + "FOR": true, + "FORCE": true, + "FOREIGN": true, + "FREELIST": true, + "FREELISTS": true, + "FROM": true, + "FULL": true, + "FUNCTION": true, + "GLOBAL": true, + "GLOBALLY": true, + "GLOBAL_NAME": true, + "GRANT": true, + "GROUP": true, + "GROUPS": true, + "HASH": true, + "HASHKEYS": true, + "HAVING": true, + "HEADER": true, + "HEAP": true, + "IDENTIFIED": true, + "IDGENERATORS": true, + "IDLE_TIME": true, + "IF": true, + "IMMEDIATE": true, + "IN": true, + "INCLUDING": true, + "INCREMENT": true, + "INDEX": true, + "INDEXED": true, + "INDEXES": true, + "INDICATOR": true, + "IND_PARTITION": true, + "INITIAL": true, + "INITIALLY": true, + "INITRANS": true, + "INSERT": true, + "INSTANCE": true, + "INSTANCES": true, + "INSTEAD": true, + "INT": true, + "INTEGER": true, + "INTERMEDIATE": true, + "INTERSECT": true, + "INTO": true, + "IS": true, + "ISOLATION": true, + "ISOLATION_LEVEL": true, + "KEEP": true, + "KEY": true, + "KILL": true, + "LABEL": true, + "LAYER": true, + "LESS": true, + "LEVEL": true, + "LIBRARY": true, + "LIKE": true, + "LIMIT": true, + "LINK": true, + "LIST": true, + "LOB": true, + "LOCAL": true, + "LOCK": true, + "LOCKED": true, + "LOG": true, + "LOGFILE": true, + "LOGGING": true, + "LOGICAL_READS_PER_CALL": true, + "LOGICAL_READS_PER_SESSION": true, + "LONG": true, + "MANAGE": true, + "MASTER": true, + "MAX": true, + "MAXARCHLOGS": true, + "MAXDATAFILES": true, + "MAXEXTENTS": true, + "MAXINSTANCES": true, + "MAXLOGFILES": true, + "MAXLOGHISTORY": true, + "MAXLOGMEMBERS": true, + "MAXSIZE": true, + "MAXTRANS": true, + "MAXVALUE": true, + "MIN": true, + "MEMBER": true, + "MINIMUM": true, + "MINEXTENTS": true, + "MINUS": true, + "MINVALUE": true, + "MLSLABEL": true, + "MLS_LABEL_FORMAT": true, + "MODE": true, + "MODIFY": true, + "MOUNT": true, + "MOVE": true, + "MTS_DISPATCHERS": true, + "MULTISET": true, + "NATIONAL": true, + "NCHAR": true, + "NCHAR_CS": true, + "NCLOB": true, + "NEEDED": true, + "NESTED": true, + "NETWORK": true, + "NEW": true, + "NEXT": true, + "NOARCHIVELOG": true, + "NOAUDIT": true, + "NOCACHE": true, + "NOCOMPRESS": true, + "NOCYCLE": true, + "NOFORCE": true, + "NOLOGGING": true, + "NOMAXVALUE": true, + "NOMINVALUE": true, + "NONE": true, + "NOORDER": true, + "NOOVERRIDE": true, + "NOPARALLEL": true, + "NOREVERSE": true, + "NORMAL": true, + "NOSORT": true, + "NOT": true, + "NOTHING": true, + "NOWAIT": true, + "NULL": true, + "NUMBER": true, + "NUMERIC": true, + "NVARCHAR2": true, + "OBJECT": true, + "OBJNO": true, + "OBJNO_REUSE": true, + "OF": true, + "OFF": true, + "OFFLINE": true, + "OID": true, + "OIDINDEX": true, + "OLD": true, + "ON": true, + "ONLINE": true, + "ONLY": true, + "OPCODE": true, + "OPEN": true, + "OPTIMAL": true, + "OPTIMIZER_GOAL": true, + "OPTION": true, + "OR": true, + "ORDER": true, + "ORGANIZATION": true, + "OSLABEL": true, + "OVERFLOW": true, + "OWN": true, + "PACKAGE": true, + "PARALLEL": true, + "PARTITION": true, + "PASSWORD": true, + "PASSWORD_GRACE_TIME": true, + "PASSWORD_LIFE_TIME": true, + "PASSWORD_LOCK_TIME": true, + "PASSWORD_REUSE_MAX": true, + "PASSWORD_REUSE_TIME": true, + "PASSWORD_VERIFY_FUNCTION": true, + "PCTFREE": true, + "PCTINCREASE": true, + "PCTTHRESHOLD": true, + "PCTUSED": true, + "PCTVERSION": true, + "PERCENT": true, + "PERMANENT": true, + "PLAN": true, + "PLSQL_DEBUG": true, + "POST_TRANSACTION": true, + "PRECISION": true, + "PRESERVE": true, + "PRIMARY": true, + "PRIOR": true, + "PRIVATE": true, + "PRIVATE_SGA": true, + "PRIVILEGE": true, + "PRIVILEGES": true, + "PROCEDURE": true, + "PROFILE": true, + "PUBLIC": true, + "PURGE": true, + "QUEUE": true, + "QUOTA": true, + "RANGE": true, + "RAW": true, + "RBA": true, + "READ": true, + "READUP": true, + "REAL": true, + "REBUILD": true, + "RECOVER": true, + "RECOVERABLE": true, + "RECOVERY": true, + "REF": true, + "REFERENCES": true, + "REFERENCING": true, + "REFRESH": true, + "RENAME": true, + "REPLACE": true, + "RESET": true, + "RESETLOGS": true, + "RESIZE": true, + "RESOURCE": true, + "RESTRICTED": true, + "RETURN": true, + "RETURNING": true, + "REUSE": true, + "REVERSE": true, + "REVOKE": true, + "ROLE": true, + "ROLES": true, + "ROLLBACK": true, + "ROW": true, + "ROWID": true, + "ROWNUM": true, + "ROWS": true, + "RULE": true, + "SAMPLE": true, + "SAVEPOINT": true, + "SB4": true, + "SCAN_INSTANCES": true, + "SCHEMA": true, + "SCN": true, + "SCOPE": true, + "SD_ALL": true, + "SD_INHIBIT": true, + "SD_SHOW": true, + "SEGMENT": true, + "SEG_BLOCK": true, + "SEG_FILE": true, + "SELECT": true, + "SEQUENCE": true, + "SERIALIZABLE": true, + "SESSION": true, + "SESSION_CACHED_CURSORS": true, + "SESSIONS_PER_USER": true, + "SET": true, + "SHARE": true, + "SHARED": true, + "SHARED_POOL": true, + "SHRINK": true, + "SIZE": true, + "SKIP": true, + "SKIP_UNUSABLE_INDEXES": true, + "SMALLINT": true, + "SNAPSHOT": true, + "SOME": true, + "SORT": true, + "SPECIFICATION": true, + "SPLIT": true, + "SQL_TRACE": true, + "STANDBY": true, + "START": true, + "STATEMENT_ID": true, + "STATISTICS": true, + "STOP": true, + "STORAGE": true, + "STORE": true, + "STRUCTURE": true, + "SUCCESSFUL": true, + "SWITCH": true, + "SYS_OP_ENFORCE_NOT_NULL$": true, + "SYS_OP_NTCIMG$": true, + "SYNONYM": true, + "SYSDATE": true, + "SYSDBA": true, + "SYSOPER": true, + "SYSTEM": true, + "TABLE": true, + "TABLES": true, + "TABLESPACE": true, + "TABLESPACE_NO": true, + "TABNO": true, + "TEMPORARY": true, + "THAN": true, + "THE": true, + "THEN": true, + "THREAD": true, + "TIMESTAMP": true, + "TIME": true, + "TO": true, + "TOPLEVEL": true, + "TRACE": true, + "TRACING": true, + "TRANSACTION": true, + "TRANSITIONAL": true, + "TRIGGER": true, + "TRIGGERS": true, + "TRUE": true, + "TRUNCATE": true, + "TX": true, + "TYPE": true, + "UB2": true, + "UBA": true, + "UID": true, + "UNARCHIVED": true, + "UNDO": true, + "UNION": true, + "UNIQUE": true, + "UNLIMITED": true, + "UNLOCK": true, + "UNRECOVERABLE": true, + "UNTIL": true, + "UNUSABLE": true, + "UNUSED": true, + "UPDATABLE": true, + "UPDATE": true, + "USAGE": true, + "USE": true, + "USER": true, + "USING": true, + "VALIDATE": true, + "VALIDATION": true, + "VALUE": true, + "VALUES": true, + "VARCHAR": true, + "VARCHAR2": true, + "VARYING": true, + "VIEW": true, + "WHEN": true, + "WHENEVER": true, + "WHERE": true, + "WITH": true, + "WITHOUT": true, + "WORK": true, + "WRITE": true, + "WRITEDOWN": true, + "WRITEUP": true, + "XID": true, + "YEAR": true, + "ZONE": true, + } + + damengQuoter = schemas.Quoter{ + Prefix: '"', + Suffix: '"', + IsReserved: schemas.AlwaysReserve, + } +) + +type dameng struct { + Base +} + +func (db *dameng) Init(uri *URI) error { + db.quoter = damengQuoter + return db.Base.Init(db, uri) +} + +func (db *dameng) Version(ctx context.Context, queryer core.Queryer) (*schemas.Version, error) { + rows, err := queryer.QueryContext(ctx, "SELECT * FROM V$VERSION") // select id_code + if err != nil { + return nil, err + } + defer rows.Close() + + var version string + if !rows.Next() { + if rows.Err() != nil { + return nil, rows.Err() + } + return nil, errors.New("unknow version") + } + + if err := rows.Scan(&version); err != nil { + return nil, err + } + return &schemas.Version{ + Number: version, + }, nil +} + +func (db *dameng) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: SequenceAutoincrMode, + } +} + +// DropIndexSQL returns a SQL to drop index +func (db *dameng) DropIndexSQL(tableName string, index *schemas.Index) string { + quote := db.dialect.Quoter().Quote + var name string + if index.IsRegular { + name = index.XName(tableName) + } else { + name = index.Name + } + return fmt.Sprintf("DROP INDEX %v", quote(name)) +} + +func (db *dameng) SQLType(c *schemas.Column) string { + var res string + switch t := c.SQLType.Name; t { + case schemas.TinyInt, "BYTE": + return "TINYINT" + case schemas.SmallInt, schemas.MediumInt, schemas.Int, schemas.Integer, schemas.UnsignedTinyInt: + return "INTEGER" + case schemas.BigInt, + schemas.UnsignedBigInt, schemas.UnsignedBit, schemas.UnsignedInt, + schemas.Serial, schemas.BigSerial: + return "BIGINT" + case schemas.Bit, schemas.Bool, schemas.Boolean: + return schemas.Bit + case schemas.Uuid: + res = schemas.Varchar + c.Length = 40 + case schemas.Binary: + if c.Length == 0 { + return schemas.Binary + "(MAX)" + } + case schemas.VarBinary, schemas.Blob, schemas.TinyBlob, schemas.MediumBlob, schemas.LongBlob, schemas.Bytea: + return schemas.VarBinary + case schemas.Date: + return schemas.Date + case schemas.Time: + if c.Length > 0 { + return fmt.Sprintf("%s(%d)", schemas.Time, c.Length) + } + return schemas.Time + case schemas.DateTime, schemas.TimeStamp: + res = schemas.TimeStamp + case schemas.TimeStampz: + if c.Length > 0 { + return fmt.Sprintf("TIMESTAMP(%d) WITH TIME ZONE", c.Length) + } + return "TIMESTAMP WITH TIME ZONE" + case schemas.Float: + res = "FLOAT" + case schemas.Real, schemas.Double: + res = "REAL" + case schemas.Numeric, schemas.Decimal, "NUMBER": + res = "NUMERIC" + case schemas.Text, schemas.Json: + return "TEXT" + case schemas.MediumText, schemas.LongText: + res = "CLOB" + case schemas.Char, schemas.Varchar, schemas.TinyText: + res = "VARCHAR2" + default: + res = t + } + + hasLen1 := (c.Length > 0) + hasLen2 := (c.Length2 > 0) + + if hasLen2 { + res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + } else if hasLen1 { + res += "(" + strconv.Itoa(c.Length) + ")" + } + return res +} + +func (db *dameng) ColumnTypeKind(t string) int { + switch strings.ToUpper(t) { + case "DATE": + return schemas.TIME_TYPE + case "CHAR", "NCHAR", "VARCHAR", "VARCHAR2", "NVARCHAR2", "LONG", "CLOB", "NCLOB": + return schemas.TEXT_TYPE + case "NUMBER": + return schemas.NUMERIC_TYPE + case "BLOB": + return schemas.BLOB_TYPE + default: + return schemas.UNKNOW_TYPE + } +} + +func (db *dameng) AutoIncrStr() string { + return "IDENTITY" +} + +func (db *dameng) IsReserved(name string) bool { + _, ok := damengReservedWords[strings.ToUpper(name)] + return ok +} + +func (db *dameng) DropTableSQL(tableName string) (string, bool) { + return fmt.Sprintf("DROP TABLE %s", db.quoter.Quote(tableName)), false +} + +// ModifyColumnSQL returns a SQL to modify SQL +func (db *dameng) ModifyColumnSQL(tableName string, col *schemas.Column) string { + s, _ := ColumnString(db.dialect, col, false) + return fmt.Sprintf("ALTER TABLE %s MODIFY %s", db.quoter.Quote(tableName), s) +} + +func (db *dameng) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { + if tableName == "" { + tableName = table.Name + } + + quoter := db.Quoter() + var b strings.Builder + b.WriteString("CREATE TABLE ") + quoter.QuoteTo(&b, tableName) + b.WriteString(" (") + + pkList := table.PrimaryKeys + + for i, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + if col.SQLType.IsBool() && !col.DefaultIsEmpty { + if col.Default == "true" { + col.Default = "1" + } else if col.Default == "false" { + col.Default = "0" + } + } + + s, _ := ColumnString(db, col, false) + b.WriteString(s) + if i != len(table.ColumnsSeq())-1 { + b.WriteString(", ") + } + } + + if len(pkList) > 0 { + if len(table.ColumnsSeq()) > 0 { + b.WriteString(", ") + } + b.WriteString(fmt.Sprintf("CONSTRAINT PK_%s PRIMARY KEY (", tableName)) + quoter.JoinWrite(&b, pkList, ",") + b.WriteString(")") + } + b.WriteString(")") + + return b.String(), false, nil +} + +func (db *dameng) SetQuotePolicy(quotePolicy QuotePolicy) { + switch quotePolicy { + case QuotePolicyNone: + var q = damengQuoter + q.IsReserved = schemas.AlwaysNoReserve + db.quoter = q + case QuotePolicyReserved: + var q = damengQuoter + q.IsReserved = db.IsReserved + db.quoter = q + case QuotePolicyAlways: + fallthrough + default: + db.quoter = damengQuoter + } +} + +func (db *dameng) IndexCheckSQL(tableName, idxName string) (string, []interface{}) { + args := []interface{}{tableName, idxName} + return `SELECT INDEX_NAME FROM USER_INDEXES ` + + `WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args +} + +func (db *dameng) IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error) { + return db.HasRecords(queryer, ctx, `SELECT table_name FROM user_tables WHERE table_name = ?`, tableName) +} + +func (db *dameng) IsSequenceExist(ctx context.Context, queryer core.Queryer, seqName string) (bool, error) { + var cnt int + rows, err := queryer.QueryContext(ctx, "SELECT COUNT(*) FROM user_sequences WHERE sequence_name = ?", seqName) + if err != nil { + return false, err + } + defer rows.Close() + if !rows.Next() { + if rows.Err() != nil { + return false, rows.Err() + } + return false, errors.New("query sequence failed") + } + + if err := rows.Scan(&cnt); err != nil { + return false, err + } + return cnt > 0, nil +} + +func (db *dameng) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName, colName string) (bool, error) { + args := []interface{}{tableName, colName} + query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" + + " AND column_name = ?" + return db.HasRecords(queryer, ctx, query, args...) +} + +var _ sql.Scanner = &dmClobScanner{} + +type dmClobScanner struct { + valid bool + data string +} + +type dmClobObject interface { + GetLength() (int64, error) + ReadString(int, int) (string, error) +} + +//var _ dmClobObject = &dm.DmClob{} + +func (d *dmClobScanner) Scan(data interface{}) error { + if data == nil { + return nil + } + + switch t := data.(type) { + case dmClobObject: // *dm.DmClob + if t == nil { + return nil + } + l, err := t.GetLength() + if err != nil { + return err + } + if l == 0 { + d.valid = true + return nil + } + d.data, err = t.ReadString(1, int(l)) + if err != nil { + return err + } + d.valid = true + return nil + case []byte: + if t == nil { + return nil + } + d.data = string(t) + d.valid = true + return nil + default: + return fmt.Errorf("cannot convert %T as dmClobScanner", data) + } +} + +func addSingleQuote(name string) string { + if len(name) < 2 { + return name + } + if name[0] == '\'' && name[len(name)-1] == '\'' { + return name + } + return fmt.Sprintf("'%s'", name) +} + +func (db *dameng) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) { + s := `select column_name from user_cons_columns + where constraint_name = (select constraint_name from user_constraints + where table_name = ? and constraint_type ='P')` + rows, err := queryer.QueryContext(ctx, s, tableName) + if err != nil { + return nil, nil, err + } + defer rows.Close() + + var pkNames []string + for rows.Next() { + var pkName string + err = rows.Scan(&pkName) + if err != nil { + return nil, nil, err + } + pkNames = append(pkNames, pkName) + } + if rows.Err() != nil { + return nil, nil, rows.Err() + } + rows.Close() + + s = `SELECT USER_TAB_COLS.COLUMN_NAME, USER_TAB_COLS.DATA_DEFAULT, USER_TAB_COLS.DATA_TYPE, USER_TAB_COLS.DATA_LENGTH, + USER_TAB_COLS.data_precision, USER_TAB_COLS.data_scale, USER_TAB_COLS.NULLABLE, + user_col_comments.comments + FROM USER_TAB_COLS + LEFT JOIN user_col_comments on user_col_comments.TABLE_NAME=USER_TAB_COLS.TABLE_NAME + AND user_col_comments.COLUMN_NAME=USER_TAB_COLS.COLUMN_NAME + WHERE USER_TAB_COLS.table_name = ?` + rows, err = queryer.QueryContext(ctx, s, tableName) + if err != nil { + return nil, nil, err + } + defer rows.Close() + + cols := make(map[string]*schemas.Column) + colSeq := make([]string, 0) + for rows.Next() { + col := new(schemas.Column) + col.Indexes = make(map[string]int) + + var colDefault dmClobScanner + var colName, nullable, dataType, dataPrecision, comment sql.NullString + var dataScale, dataLen sql.NullInt64 + + err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision, + &dataScale, &nullable, &comment) + if err != nil { + return nil, nil, err + } + + if !colName.Valid { + return nil, nil, errors.New("column name is nil") + } + + col.Name = strings.Trim(colName.String, `" `) + if colDefault.valid { + col.Default = colDefault.data + } else { + col.DefaultIsEmpty = true + } + + if nullable.String == "Y" { + col.Nullable = true + } else { + col.Nullable = false + } + + if !comment.Valid { + col.Comment = comment.String + } + if utils.IndexSlice(pkNames, col.Name) > -1 { + col.IsPrimaryKey = true + has, err := db.HasRecords(queryer, ctx, "SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME = ?", utils.SeqName(tableName)) + if err != nil { + return nil, nil, err + } + if has { + col.IsAutoIncrement = true + } + } + + var ( + ignore bool + dt string + len1, len2 int + ) + + dts := strings.Split(dataType.String, "(") + dt = dts[0] + if len(dts) > 1 { + lens := strings.Split(dts[1][:len(dts[1])-1], ",") + if len(lens) > 1 { + len1, _ = strconv.Atoi(lens[0]) + len2, _ = strconv.Atoi(lens[1]) + } else { + len1, _ = strconv.Atoi(lens[0]) + } + } + + switch dt { + case "VARCHAR2": + col.SQLType = schemas.SQLType{Name: "VARCHAR2", DefaultLength: len1, DefaultLength2: len2} + case "VARCHAR": + col.SQLType = schemas.SQLType{Name: schemas.Varchar, DefaultLength: len1, DefaultLength2: len2} + case "TIMESTAMP WITH TIME ZONE": + col.SQLType = schemas.SQLType{Name: schemas.TimeStampz, DefaultLength: 0, DefaultLength2: 0} + case "NUMBER": + col.SQLType = schemas.SQLType{Name: "NUMBER", DefaultLength: len1, DefaultLength2: len2} + case "LONG", "LONG RAW", "NCLOB", "CLOB", "TEXT": + col.SQLType = schemas.SQLType{Name: schemas.Text, DefaultLength: 0, DefaultLength2: 0} + case "RAW": + col.SQLType = schemas.SQLType{Name: schemas.Binary, DefaultLength: 0, DefaultLength2: 0} + case "ROWID": + col.SQLType = schemas.SQLType{Name: schemas.Varchar, DefaultLength: 18, DefaultLength2: 0} + case "AQ$_SUBSCRIBERS": + ignore = true + default: + col.SQLType = schemas.SQLType{Name: strings.ToUpper(dt), DefaultLength: len1, DefaultLength2: len2} + } + + if ignore { + continue + } + + if _, ok := schemas.SqlTypes[col.SQLType.Name]; !ok { + return nil, nil, fmt.Errorf("unknown colType %v %v", dataType.String, col.SQLType) + } + + if col.SQLType.Name == "TIMESTAMP" { + col.Length = int(dataScale.Int64) + } else { + col.Length = int(dataLen.Int64) + } + + if col.SQLType.IsTime() { + if !col.DefaultIsEmpty && !strings.EqualFold(col.Default, "CURRENT_TIMESTAMP") { + col.Default = addSingleQuote(col.Default) + } + } + cols[col.Name] = col + colSeq = append(colSeq, col.Name) + } + if rows.Err() != nil { + return nil, nil, rows.Err() + } + + return colSeq, cols, nil +} + +func (db *dameng) GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) { + s := "SELECT table_name FROM user_tables WHERE temporary = 'N' AND table_name NOT LIKE ?" + args := []interface{}{strings.ToUpper(db.uri.User), "%$%"} + + rows, err := queryer.QueryContext(ctx, s, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + tables := make([]*schemas.Table, 0) + for rows.Next() { + table := schemas.NewEmptyTable() + err = rows.Scan(&table.Name) + if err != nil { + return nil, err + } + + tables = append(tables, table) + } + if rows.Err() != nil { + return nil, rows.Err() + } + return tables, nil +} + +func (db *dameng) GetIndexes(queryer core.Queryer, ctx context.Context, tableName string) (map[string]*schemas.Index, error) { + args := []interface{}{tableName, tableName} + s := "SELECT t.column_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " + + "WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =?" + + " AND t.index_name not in (SELECT index_name FROM ALL_CONSTRAINTS WHERE CONSTRAINT_TYPE='P' AND table_name = ?)" + + rows, err := queryer.QueryContext(ctx, s, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + indexes := make(map[string]*schemas.Index) + for rows.Next() { + var indexType int + var indexName, colName, uniqueness string + + err = rows.Scan(&colName, &uniqueness, &indexName) + if err != nil { + return nil, err + } + + indexName = strings.Trim(indexName, `" `) + + var isRegular bool + if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { + indexName = indexName[5+len(tableName):] + isRegular = true + } + + if uniqueness == "UNIQUE" { + indexType = schemas.UniqueType + } else { + indexType = schemas.IndexType + } + + var index *schemas.Index + var ok bool + if index, ok = indexes[indexName]; !ok { + index = new(schemas.Index) + index.Type = indexType + index.Name = indexName + index.IsRegular = isRegular + indexes[indexName] = index + } + index.AddColumn(colName) + } + if rows.Err() != nil { + return nil, rows.Err() + } + return indexes, nil +} + +func (db *dameng) Filters() []Filter { + return []Filter{} +} + +type damengDriver struct { + baseDriver +} + +// Features return features +func (d *damengDriver) Features() *DriverFeatures { + return &DriverFeatures{ + SupportReturnInsertedID: false, + } +} + +// Parse parse the datasource +// dm://userName:password@ip:port +func (d *damengDriver) Parse(driverName, dataSourceName string) (*URI, error) { + u, err := url.Parse(dataSourceName) + if err != nil { + return nil, err + } + + if u.User == nil { + return nil, errors.New("user/password needed") + } + + passwd, _ := u.User.Password() + return &URI{ + DBType: schemas.DAMENG, + Proto: u.Scheme, + Host: u.Hostname(), + Port: u.Port(), + DBName: u.User.Username(), + User: u.User.Username(), + Passwd: passwd, + }, nil +} + +func (d *damengDriver) GenScanResult(colType string) (interface{}, error) { + switch colType { + case "CHAR", "NCHAR", "VARCHAR", "VARCHAR2", "NVARCHAR2", "LONG", "CLOB", "NCLOB": + var s sql.NullString + return &s, nil + case "NUMBER": + var s sql.NullString + return &s, nil + case "BIGINT": + var s sql.NullInt64 + return &s, nil + case "INTEGER": + var s sql.NullInt32 + return &s, nil + case "DATE", "TIMESTAMP": + var s sql.NullString + return &s, nil + case "BLOB": + var r sql.RawBytes + return &r, nil + case "FLOAT": + var s sql.NullFloat64 + return &s, nil + default: + var r sql.RawBytes + return &r, nil + } +} + +func (d *damengDriver) Scan(ctx *ScanContext, rows *core.Rows, types []*sql.ColumnType, vv ...interface{}) error { + var scanResults = make([]interface{}, 0, len(types)) + var replaces = make([]bool, 0, len(types)) + var err error + for i, v := range vv { + var replaced bool + var scanResult interface{} + switch types[i].DatabaseTypeName() { + case "CLOB", "TEXT": + scanResult = &dmClobScanner{} + replaced = true + case "TIMESTAMP": + scanResult = &sql.NullString{} + replaced = true + default: + scanResult = v + } + + scanResults = append(scanResults, scanResult) + replaces = append(replaces, replaced) + } + + if err = rows.Scan(scanResults...); err != nil { + return err + } + + for i, replaced := range replaces { + if replaced { + switch t := scanResults[i].(type) { + case *dmClobScanner: + var d interface{} + if t.valid { + d = t.data + } else { + d = nil + } + if err := convert.Assign(vv[i], d, ctx.DBLocation, ctx.UserLocation); err != nil { + return err + } + default: + switch types[i].DatabaseTypeName() { + case "TIMESTAMP": + ns := t.(*sql.NullString) + if !ns.Valid { + return nil + } + s := ns.String + fields := strings.Split(s, "+") + if err := convert.Assign(vv[i], strings.Replace(fields[0], "T", " ", -1), ctx.DBLocation, ctx.UserLocation); err != nil { + return err + } + default: + return fmt.Errorf("don't support convert %T to %T", t, vv[i]) + } + } + } + } + + return nil +} diff --git a/dialects/dialect.go b/dialects/dialect.go index b6c0853a..460ab56a 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -38,11 +38,21 @@ func (uri *URI) SetSchema(schema string) { } } +const ( + IncrAutoincrMode = iota + SequenceAutoincrMode +) + +type DialectFeatures struct { + AutoincrMode int // 0 autoincrement column, 1 sequence +} + // Dialect represents a kind of database type Dialect interface { Init(*URI) error URI() *URI Version(ctx context.Context, queryer core.Queryer) (*schemas.Version, error) + Features() *DialectFeatures SQLType(*schemas.Column) string Alias(string) string // return what a sql type's alias of @@ -61,9 +71,13 @@ type Dialect interface { GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error) - CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) + CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) DropTableSQL(tableName string) (string, bool) + CreateSequenceSQL(ctx context.Context, queryer core.Queryer, seqName string) (string, error) + IsSequenceExist(ctx context.Context, queryer core.Queryer, seqName string) (bool, error) + DropSequenceSQL(seqName string) (string, error) + GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName string, colName string) (bool, error) AddColumnSQL(tableName string, col *schemas.Column) string @@ -104,7 +118,7 @@ func (db *Base) URI() *URI { } // CreateTableSQL implements Dialect -func (db *Base) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { +func (db *Base) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { if tableName == "" { tableName = table.Name } @@ -133,7 +147,25 @@ func (db *Base) CreateTableSQL(table *schemas.Table, tableName string) ([]string b.WriteString(")") - return []string{b.String()}, false + return b.String(), false, nil +} + +func (db *Base) CreateSequenceSQL(ctx context.Context, queryer core.Queryer, seqName string) (string, error) { + return fmt.Sprintf(`CREATE SEQUENCE %s + minvalue 1 + nomaxvalue + start with 1 + increment by 1 + nocycle + nocache`, seqName), nil +} + +func (db *Base) IsSequenceExist(ctx context.Context, queryer core.Queryer, seqName string) (bool, error) { + return false, fmt.Errorf("unsupported sequence feature") +} + +func (db *Base) DropSequenceSQL(seqName string) (string, error) { + return fmt.Sprintf("DROP SEQUENCE %s", seqName), nil } // DropTableSQL returns drop table SQL @@ -285,43 +317,41 @@ func ColumnString(dialect Dialect, col *schemas.Column, includePrimaryKey bool) return "", err } - if err := bd.WriteByte(' '); err != nil { - return "", err - } - if includePrimaryKey && col.IsPrimaryKey { - if _, err := bd.WriteString("PRIMARY KEY "); err != nil { + if _, err := bd.WriteString(" PRIMARY KEY"); err != nil { return "", err } - if col.IsAutoIncrement { - if _, err := bd.WriteString(dialect.AutoIncrStr()); err != nil { - return "", err - } if err := bd.WriteByte(' '); err != nil { return "", err } + if _, err := bd.WriteString(dialect.AutoIncrStr()); err != nil { + return "", err + } } } - if col.Default != "" { - if _, err := bd.WriteString("DEFAULT "); err != nil { + if !col.DefaultIsEmpty { + if _, err := bd.WriteString(" DEFAULT "); err != nil { return "", err } - if _, err := bd.WriteString(col.Default); err != nil { - return "", err - } - if err := bd.WriteByte(' '); err != nil { - return "", err + if col.Default == "" { + if _, err := bd.WriteString("''"); err != nil { + return "", err + } + } else { + if _, err := bd.WriteString(col.Default); err != nil { + return "", err + } } } if col.Nullable { - if _, err := bd.WriteString("NULL "); err != nil { + if _, err := bd.WriteString(" NULL"); err != nil { return "", err } } else { - if _, err := bd.WriteString("NOT NULL "); err != nil { + if _, err := bd.WriteString(" NOT NULL"); err != nil { return "", err } } diff --git a/dialects/mssql.go b/dialects/mssql.go index ab010eb0..cd19afb9 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -282,6 +282,12 @@ func (db *mssql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Ve }, nil } +func (db *mssql) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: IncrAutoincrMode, + } +} + func (db *mssql) SQLType(c *schemas.Column) string { var res string switch t := c.SQLType.Name; t { @@ -625,7 +631,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? return indexes, nil } -func (db *mssql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { +func (db *mssql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { if tableName == "" { tableName = table.Name } @@ -656,7 +662,7 @@ func (db *mssql) CreateTableSQL(table *schemas.Table, tableName string) ([]strin b.WriteString(")") - return []string{b.String()}, true + return b.String(), true, nil } func (db *mssql) ForUpdateSQL(query string) string { diff --git a/dialects/mysql.go b/dialects/mysql.go index 0489904a..ce3bd705 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -244,6 +244,12 @@ func (db *mysql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Ve }, nil } +func (db *mysql) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: IncrAutoincrMode, + } +} + func (db *mysql) SetParams(params map[string]string) { rowFormat, ok := params["rowFormat"] if ok { @@ -625,7 +631,7 @@ func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName return indexes, nil } -func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { +func (db *mysql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { if tableName == "" { tableName = table.Name } @@ -678,7 +684,8 @@ func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]strin b.WriteString(" ROW_FORMAT=") b.WriteString(db.rowFormat) } - return []string{b.String()}, true + + return b.String(), true, nil } func (db *mysql) Filters() []Filter { diff --git a/dialects/oracle.go b/dialects/oracle.go index 11a6653b..04652bd6 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -539,6 +539,12 @@ func (db *oracle) Version(ctx context.Context, queryer core.Queryer) (*schemas.V }, nil } +func (db *oracle) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: SequenceAutoincrMode, + } +} + func (db *oracle) SQLType(c *schemas.Column) string { var res string switch t := c.SQLType.Name; t { @@ -599,7 +605,7 @@ func (db *oracle) DropTableSQL(tableName string) (string, bool) { return fmt.Sprintf("DROP TABLE `%s`", tableName), false } -func (db *oracle) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { +func (db *oracle) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { var sql = "CREATE TABLE " if tableName == "" { tableName = table.Name @@ -629,7 +635,7 @@ func (db *oracle) CreateTableSQL(table *schemas.Table, tableName string) ([]stri } sql = sql[:len(sql)-2] + ")" - return []string{sql}, false + return sql, false, nil } func (db *oracle) SetQuotePolicy(quotePolicy QuotePolicy) { diff --git a/dialects/postgres.go b/dialects/postgres.go index 6b5a8b2f..822d3a70 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -941,6 +941,12 @@ func (db *postgres) SQLType(c *schemas.Column) string { return res } +func (db *postgres) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: IncrAutoincrMode, + } +} + func (db *postgres) ColumnTypeKind(t string) int { switch strings.ToUpper(t) { case "DATETIME", "TIMESTAMP": diff --git a/dialects/sqlite3.go b/dialects/sqlite3.go index 4eba8dad..4ff9a39e 100644 --- a/dialects/sqlite3.go +++ b/dialects/sqlite3.go @@ -184,6 +184,12 @@ func (db *sqlite3) Version(ctx context.Context, queryer core.Queryer) (*schemas. }, nil } +func (db *sqlite3) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: IncrAutoincrMode, + } +} + func (db *sqlite3) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: diff --git a/engine.go b/engine.go index ec066109..3681ff3c 100644 --- a/engine.go +++ b/engine.go @@ -248,11 +248,6 @@ func (engine *Engine) SQLType(c *schemas.Column) string { return engine.dialect.SQLType(c) } -// AutoIncrStr Database's autoincrement statement -func (engine *Engine) AutoIncrStr() string { - return engine.dialect.AutoIncrStr() -} - // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. func (engine *Engine) SetConnMaxLifetime(d time.Duration) { engine.DB().SetConnMaxLifetime(d) @@ -441,7 +436,7 @@ func (engine *Engine) DumpTablesToFile(tables []*schemas.Table, fp string, tp .. // DumpTables dump specify tables to io.Writer func (engine *Engine) DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error { - return engine.dumpTables(tables, w, tp...) + return engine.dumpTables(context.Background(), tables, w, tp...) } func formatBool(s string, dstDialect dialects.Dialect) string { @@ -457,7 +452,7 @@ func formatBool(s string, dstDialect dialects.Dialect) string { } // dumpTables dump database all table structs and data to w with specify db type -func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error { +func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error { var dstDialect dialects.Dialect if len(tp) == 0 { dstDialect = engine.dialect @@ -494,9 +489,12 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch } } - dstTableName := dstTable.Name + var dstTableName = dstTable.Name + var quoter = dstDialect.Quoter().Quote + var quotedDstTableName = quoter(dstTable.Name) if dstDialect.URI().Schema != "" { dstTableName = fmt.Sprintf("%s.%s", dstDialect.URI().Schema, dstTable.Name) + quotedDstTableName = fmt.Sprintf("%s.%s", quoter(dstDialect.URI().Schema), quoter(dstTable.Name)) } originalTableName := table.Name if engine.dialect.URI().Schema != "" { @@ -509,13 +507,26 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch } } - sqls, _ := dstDialect.CreateTableSQL(dstTable, dstTableName) - for _, s := range sqls { - _, err = io.WriteString(w, s+";\n") + if dstTable.AutoIncrement != "" && dstDialect.Features().AutoincrMode == dialects.SequenceAutoincrMode { + sqlstr, err := dstDialect.CreateSequenceSQL(ctx, engine.db, utils.SeqName(dstTableName)) + if err != nil { + return err + } + _, err = io.WriteString(w, sqlstr+";\n") if err != nil { return err } } + + sqlstr, _, err := dstDialect.CreateTableSQL(ctx, engine.db, dstTable, dstTableName) + if err != nil { + return err + } + _, err = io.WriteString(w, sqlstr+";\n") + if err != nil { + return err + } + if len(dstTable.PKColumns()) > 0 && dstDialect.URI().DBType == schemas.MSSQL { fmt.Fprintf(w, "SET IDENTITY_INSERT [%s] ON;\n", dstTable.Name) } @@ -552,7 +563,7 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch sess := engine.NewSession() defer sess.Close() for rows.Next() { - _, err = io.WriteString(w, "INSERT INTO "+dstDialect.Quoter().Quote(dstTableName)+" ("+destColNames+") VALUES (") + _, err = io.WriteString(w, "INSERT INTO "+quotedDstTableName+" ("+destColNames+") VALUES (") if err != nil { return err } @@ -563,36 +574,27 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch } for i, scanResult := range scanResults { stp := schemas.SQLType{Name: types[i].DatabaseTypeName()} - if stp.IsNumeric() { - s := scanResult.(*sql.NullString) - if s.Valid { - if _, err = io.WriteString(w, formatBool(s.String, dstDialect)); err != nil { - return err - } - } else { - if _, err = io.WriteString(w, "NULL"); err != nil { - return err - } - } - } else if stp.IsBool() { - s := scanResult.(*sql.NullString) - if s.Valid { - if _, err = io.WriteString(w, formatBool(s.String, dstDialect)); err != nil { - return err - } - } else { - if _, err = io.WriteString(w, "NULL"); err != nil { - return err - } + s := scanResult.(*sql.NullString) + if !s.Valid { + if _, err = io.WriteString(w, "NULL"); err != nil { + return err } } else { - s := scanResult.(*sql.NullString) - if s.Valid { - if _, err = io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { + if stp.IsBool() || (dstDialect.URI().DBType == schemas.MSSQL && strings.EqualFold(stp.Name, schemas.Bit)) { + if _, err = io.WriteString(w, formatBool(s.String, dstDialect)); err != nil { + return err + } + } else if stp.IsNumeric() { + if _, err = io.WriteString(w, s.String); err != nil { + return err + } + } else if sess.engine.dialect.URI().DBType == schemas.DAMENG && stp.IsTime() && len(s.String) == 25 { + r := strings.Replace(s.String[:19], "T", " ", -1) + if _, err = io.WriteString(w, "'"+r+"'"); err != nil { return err } } else { - if _, err = io.WriteString(w, "NULL"); err != nil { + if _, err = io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { return err } } diff --git a/go.mod b/go.mod index d645011c..98b5617c 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module xorm.io/xorm go 1.13 require ( + gitee.com/travelliu/dm v1.8.11192 github.com/denisenkom/go-mssqldb v0.10.0 github.com/go-sql-driver/mysql v1.6.0 github.com/goccy/go-json v0.7.4 diff --git a/go.sum b/go.sum index e8024945..4596f326 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +gitee.com/travelliu/dm v1.8.11192 h1:aqJT0xhodZjRutIfEXxKYv0CxqmHUHzsbz6SFaRL6OY= +gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= @@ -95,8 +97,9 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= diff --git a/integrations/cache_test.go b/integrations/cache_test.go index 44e817b1..6fdaa5cb 100644 --- a/integrations/cache_test.go +++ b/integrations/cache_test.go @@ -62,7 +62,8 @@ func TestCacheFind(t *testing.T) { } boxes = make([]MailBox, 0, 2) - assert.NoError(t, testEngine.Alias("a").Where("a.id > -1").Asc("a.id").Find(&boxes)) + assert.NoError(t, testEngine.Alias("a").Where("`a`.`id`> -1"). + Asc("`a`.`id`").Find(&boxes)) assert.EqualValues(t, 2, len(boxes)) for i, box := range boxes { assert.Equal(t, inserts[i].Id, box.Id) @@ -77,7 +78,8 @@ func TestCacheFind(t *testing.T) { } boxes2 := make([]MailBox4, 0, 2) - assert.NoError(t, testEngine.Table("mail_box").Where("mail_box.id > -1").Asc("mail_box.id").Find(&boxes2)) + assert.NoError(t, testEngine.Table("mail_box").Where("`mail_box`.`id` > -1"). + Asc("mail_box.id").Find(&boxes2)) assert.EqualValues(t, 2, len(boxes2)) for i, box := range boxes2 { assert.Equal(t, inserts[i].Id, box.Id) @@ -164,14 +166,14 @@ func TestCacheGet(t *testing.T) { assert.NoError(t, err) var box1 MailBox3 - has, err := testEngine.Where("id = ?", inserts[0].Id).Get(&box1) + has, err := testEngine.Where("`id` = ?", inserts[0].Id).Get(&box1) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "user1", box1.Username) assert.EqualValues(t, "pass1", box1.Password) var box2 MailBox3 - has, err = testEngine.Where("id = ?", inserts[0].Id).Get(&box2) + has, err = testEngine.Where("`id` = ?", inserts[0].Id).Get(&box2) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "user1", box2.Username) diff --git a/integrations/engine_dm_test.go b/integrations/engine_dm_test.go new file mode 100644 index 00000000..6c2f6103 --- /dev/null +++ b/integrations/engine_dm_test.go @@ -0,0 +1,13 @@ +// 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. + +// +build dm + +package integrations + +import "xorm.io/xorm/schemas" + +func init() { + dbtypes = append(dbtypes, schemas.DAMENG) +} diff --git a/integrations/engine_test.go b/integrations/engine_test.go index 02b35a2c..f45d6859 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -14,6 +14,7 @@ import ( "xorm.io/xorm" "xorm.io/xorm/schemas" + _ "gitee.com/travelliu/dm" _ "github.com/denisenkom/go-mssqldb" _ "github.com/go-sql-driver/mysql" _ "github.com/jackc/pgx/v4/stdlib" @@ -134,6 +135,8 @@ func TestDump(t *testing.T) { } } +var dbtypes = []schemas.DBType{schemas.SQLITE, schemas.MYSQL, schemas.POSTGRES, schemas.MSSQL} + func TestDumpTables(t *testing.T) { assert.NoError(t, PrepareEngine()) @@ -169,7 +172,7 @@ func TestDumpTables(t *testing.T) { assert.NoError(t, err) assert.NoError(t, sess.Commit()) - for _, tp := range []schemas.DBType{schemas.SQLITE, schemas.MYSQL, schemas.POSTGRES, schemas.MSSQL} { + for _, tp := range dbtypes { name := fmt.Sprintf("dump_%v-table.sql", tp) t.Run(name, func(t *testing.T) { assert.NoError(t, testEngine.(*xorm.Engine).DumpTablesToFile([]*schemas.Table{tb}, name, tp)) diff --git a/integrations/performance_test.go b/integrations/performance_test.go index 4b54b40c..241dc7b9 100644 --- a/integrations/performance_test.go +++ b/integrations/performance_test.go @@ -32,7 +32,7 @@ func BenchmarkGetVars(b *testing.B) { b.StartTimer() var myname string for i := 0; i < b.N; i++ { - has, err := testEngine.Cols("name").Table("benchmark_get_vars").Where("id=?", v.Id).Get(&myname) + has, err := testEngine.Cols("name").Table("benchmark_get_vars").Where("`id`=?", v.Id).Get(&myname) b.StopTimer() myname = "" assert.True(b, has) diff --git a/integrations/session_cols_test.go b/integrations/session_cols_test.go index b74c6f8a..18ba4001 100644 --- a/integrations/session_cols_test.go +++ b/integrations/session_cols_test.go @@ -45,7 +45,7 @@ func TestSetExpr(t *testing.T) { assert.EqualValues(t, 1, cnt) var not = "NOT" - if testEngine.Dialect().URI().DBType == schemas.MSSQL { + if testEngine.Dialect().URI().DBType == schemas.MSSQL || testEngine.Dialect().URI().DBType == schemas.DAMENG { not = "~" } cnt, err = testEngine.SetExpr("show", not+" `show`").ID(1).Update(new(UserExpr)) @@ -54,9 +54,9 @@ func TestSetExpr(t *testing.T) { tableName := testEngine.TableName(new(UserExprIssue), true) cnt, err = testEngine.SetExpr("issue_id", - builder.Select("id"). - From(tableName). - Where(builder.Eq{"id": issue.Id})). + builder.Select("`id`"). + From(testEngine.Quote(tableName)). + Where(builder.Eq{"`id`": issue.Id})). ID(1). Update(new(UserExpr)) assert.NoError(t, err) diff --git a/integrations/session_cond_test.go b/integrations/session_cond_test.go index a0a91cad..72a4caf5 100644 --- a/integrations/session_cond_test.go +++ b/integrations/session_cond_test.go @@ -37,49 +37,50 @@ func TestBuilder(t *testing.T) { assert.NoError(t, err) var cond Condition - has, err := testEngine.Where(builder.Eq{"col_name": "col1"}).Get(&cond) + var q = testEngine.Quote + has, err := testEngine.Where(builder.Eq{q("col_name"): "col1"}).Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Eq{"op": OpEqual})). + has, err = testEngine.Where(builder.Eq{q("col_name"): "col1"}. + And(builder.Eq{q("op"): OpEqual})). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1", "op": OpEqual, "value": "1"}). + has, err = testEngine.Where(builder.Eq{q("col_name"): "col1", q("op"): OpEqual, q("value"): "1"}). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Neq{"op": OpEqual})). + has, err = testEngine.Where(builder.Eq{q("col_name"): "col1"}. + And(builder.Neq{q("op"): OpEqual})). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, false, has, "records should not exist") var conds []Condition - err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Eq{"op": OpEqual})). + err = testEngine.Where(builder.Eq{q("col_name"): "col1"}. + And(builder.Eq{q("op"): OpEqual})). Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.Like{"col_name", "col"}).Find(&conds) + err = testEngine.Where(builder.Like{q("col_name"), "col"}).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.Expr("col_name = ?", "col1")).Find(&conds) + err = testEngine.Where(builder.Expr(q("col_name")+" = ?", "col1")).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.In("col_name", "col1", "col2")).Find(&conds) + err = testEngine.Where(builder.In(q("col_name"), "col1", "col2")).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") @@ -91,8 +92,8 @@ func TestBuilder(t *testing.T) { // complex condtions var where = builder.NewCond() if true { - where = where.And(builder.Eq{"col_name": "col1"}) - where = where.Or(builder.And(builder.In("col_name", "col1", "col2"), builder.Expr("col_name = ?", "col1"))) + where = where.And(builder.Eq{q("col_name"): "col1"}) + where = where.Or(builder.And(builder.In(q("col_name"), "col1", "col2"), builder.Expr(q("col_name")+" = ?", "col1"))) } conds = make([]Condition, 0) @@ -215,7 +216,7 @@ func TestFindAndCount(t *testing.T) { assert.NoError(t, err) var results []FindAndCount - sess := testEngine.Where("name = ?", "test1") + sess := testEngine.Where("`name` = ?", "test1") conds := sess.Conds() err = sess.Find(&results) assert.NoError(t, err) diff --git a/integrations/session_count_test.go b/integrations/session_count_test.go index 1517dede..e49b6045 100644 --- a/integrations/session_count_test.go +++ b/integrations/session_count_test.go @@ -63,7 +63,7 @@ func TestSQLCount(t *testing.T) { assertSync(t, new(UserinfoCount2), new(UserinfoBooks)) - total, err := testEngine.SQL("SELECT count(id) FROM " + testEngine.TableName("userinfo_count2", true)). + total, err := testEngine.SQL("SELECT count(`id`) FROM " + testEngine.Quote(testEngine.TableName("userinfo_count2", true))). Count() assert.NoError(t, err) assert.EqualValues(t, 0, total) @@ -89,7 +89,7 @@ func TestCountWithOthers(t *testing.T) { }) assert.NoError(t, err) - total, err := testEngine.OrderBy("id desc").Limit(1).Count(new(CountWithOthers)) + total, err := testEngine.OrderBy("`id` desc").Limit(1).Count(new(CountWithOthers)) assert.NoError(t, err) assert.EqualValues(t, 2, total) } @@ -118,11 +118,11 @@ func TestWithTableName(t *testing.T) { }) assert.NoError(t, err) - total, err := testEngine.OrderBy("id desc").Count(new(CountWithTableName)) + total, err := testEngine.OrderBy("`id` desc").Count(new(CountWithTableName)) assert.NoError(t, err) assert.EqualValues(t, 2, total) - total, err = testEngine.OrderBy("id desc").Count(CountWithTableName{}) + total, err = testEngine.OrderBy("`id` desc").Count(CountWithTableName{}) assert.NoError(t, err) assert.EqualValues(t, 2, total) } @@ -146,7 +146,7 @@ func TestCountWithSelectCols(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, total) - total, err = testEngine.Select("count(id)").Count(CountWithTableName{}) + total, err = testEngine.Select("count(`id`)").Count(CountWithTableName{}) assert.NoError(t, err) assert.EqualValues(t, 2, total) } @@ -166,7 +166,7 @@ func TestCountWithGroupBy(t *testing.T) { }) assert.NoError(t, err) - cnt, err := testEngine.GroupBy("name").Count(new(CountWithTableName)) + cnt, err := testEngine.GroupBy("`name`").Count(new(CountWithTableName)) assert.NoError(t, err) assert.EqualValues(t, 2, cnt) } diff --git a/integrations/session_exist_test.go b/integrations/session_exist_test.go index 29546376..a0f65211 100644 --- a/integrations/session_exist_test.go +++ b/integrations/session_exist_test.go @@ -48,19 +48,19 @@ func TestExistStruct(t *testing.T) { assert.NoError(t, err) assert.False(t, has) - has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{}) + has, err = testEngine.Where("`name` = ?", "test1").Exist(&RecordExist{}) assert.NoError(t, err) assert.True(t, has) - has, err = testEngine.Where("name = ?", "test2").Exist(&RecordExist{}) + has, err = testEngine.Where("`name` = ?", "test2").Exist(&RecordExist{}) assert.NoError(t, err) assert.False(t, has) - has, err = testEngine.SQL("select * from "+testEngine.TableName("record_exist", true)+" where name = ?", "test1").Exist() + has, err = testEngine.SQL("select * from "+testEngine.Quote(testEngine.TableName("record_exist", true))+" where `name` = ?", "test1").Exist() assert.NoError(t, err) assert.True(t, has) - has, err = testEngine.SQL("select * from "+testEngine.TableName("record_exist", true)+" where name = ?", "test2").Exist() + has, err = testEngine.SQL("select * from "+testEngine.Quote(testEngine.TableName("record_exist", true))+" where `name` = ?", "test2").Exist() assert.NoError(t, err) assert.False(t, has) @@ -68,11 +68,11 @@ func TestExistStruct(t *testing.T) { assert.NoError(t, err) assert.True(t, has) - has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist() + has, err = testEngine.Table("record_exist").Where("`name` = ?", "test1").Exist() assert.NoError(t, err) assert.True(t, has) - has, err = testEngine.Table("record_exist").Where("name = ?", "test2").Exist() + has, err = testEngine.Table("record_exist").Where("`name` = ?", "test2").Exist() assert.NoError(t, err) assert.False(t, has) @@ -124,43 +124,43 @@ func TestExistStructForJoin(t *testing.T) { defer session.Close() session.Table("number"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid"). - Where("number.lid = ?", 1) + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`"). + Where("`number`.`lid` = ?", 1) has, err := session.Exist() assert.NoError(t, err) assert.True(t, has) session.Table("number"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid"). - Where("number.lid = ?", 2) + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`"). + Where("`number`.`lid` = ?", 2) has, err = session.Exist() assert.NoError(t, err) assert.False(t, has) session.Table("number"). - Select("order_list.id"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid"). - Where("order_list.id = ?", 1) + Select("`order_list`.`id`"). + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`"). + Where("`order_list`.`id` = ?", 1) has, err = session.Exist() assert.NoError(t, err) assert.True(t, has) session.Table("number"). Select("player.id"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid"). - Where("player.id = ?", 2) + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`"). + Where("`player`.`id` = ?", 2) has, err = session.Exist() assert.NoError(t, err) assert.False(t, has) session.Table("number"). Select("player.id"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid") + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`") has, err = session.Exist() assert.NoError(t, err) assert.True(t, has) @@ -174,15 +174,15 @@ func TestExistStructForJoin(t *testing.T) { session.Table("number"). Select("player.id"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid") + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`") has, err = session.Exist() assert.Error(t, err) assert.False(t, has) session.Table("number"). Select("player.id"). - Join("LEFT", "player", "player.id = number.lid") + Join("LEFT", "player", "`player`.`id` = `number`.`lid`") has, err = session.Exist() assert.NoError(t, err) assert.True(t, has) diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index 1cbf5e42..9b503f25 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -56,8 +56,8 @@ func TestJoinLimit(t *testing.T) { var salaries []Salary err = testEngine.Table("salary"). - Join("INNER", "check_list", "check_list.id = salary.lid"). - Join("LEFT", "empsetting", "empsetting.id = check_list.eid"). + Join("INNER", "check_list", "`check_list`.`id` = `salary`.`lid`"). + Join("LEFT", "empsetting", "`empsetting`.`id` = `check_list`.`eid`"). Limit(10, 0). Find(&salaries) assert.NoError(t, err) @@ -69,10 +69,10 @@ func TestWhere(t *testing.T) { assertSync(t, new(Userinfo)) users := make([]Userinfo, 0) - err := testEngine.Where("id > ?", 2).Find(&users) + err := testEngine.Where("`id` > ?", 2).Find(&users) assert.NoError(t, err) - err = testEngine.Where("id > ?", 2).And("id < ?", 10).Find(&users) + err = testEngine.Where("`id` > ?", 2).And("`id` < ?", 10).Find(&users) assert.NoError(t, err) } @@ -125,50 +125,50 @@ func TestFind3(t *testing.T) { assert.NoError(t, err) var teams []Team - err = testEngine.Cols("`team`.id"). - Where("`team_user`.org_id=?", 1). - And("`team_user`.uid=?", 2). - Join("INNER", "`team_user`", "`team_user`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`team_user`.`org_id`=?", 1). + And("`team_user`.`uid`=?", 2). + Join("INNER", "`team_user`", "`team_user`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) err = testEngine.Cols("`team`.id"). - Where("`team_user`.org_id=?", 1). - And("`team_user`.uid=?", 2). - Join("INNER", teamUser, "`team_user`.team_id=`team`.id"). + Where("`team_user`.`org_id`=?", 1). + And("`team_user`.`uid`=?", 2). + Join("INNER", teamUser, "`team_user`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) - err = testEngine.Cols("`team`.id"). - Where("`team_user`.org_id=?", 1). - And("`team_user`.uid=?", 2). - Join("INNER", []interface{}{teamUser}, "`team_user`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`team_user`.`org_id`=?", 1). + And("`team_user`.`uid`=?", 2). + Join("INNER", []interface{}{teamUser}, "`team_user`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) - err = testEngine.Cols("`team`.id"). - Where("`tu`.org_id=?", 1). - And("`tu`.uid=?", 2). - Join("INNER", []string{"team_user", "tu"}, "`tu`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`tu`.`org_id`=?", 1). + And("`tu`.`uid`=?", 2). + Join("INNER", []string{"team_user", "tu"}, "`tu`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) - err = testEngine.Cols("`team`.id"). - Where("`tu`.org_id=?", 1). - And("`tu`.uid=?", 2). - Join("INNER", []interface{}{"team_user", "tu"}, "`tu`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`tu`.`org_id`=?", 1). + And("`tu`.`uid`=?", 2). + Join("INNER", []interface{}{"team_user", "tu"}, "`tu`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) - err = testEngine.Cols("`team`.id"). - Where("`tu`.org_id=?", 1). - And("`tu`.uid=?", 2). - Join("INNER", []interface{}{teamUser, "tu"}, "`tu`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`tu`.`org_id`=?", 1). + And("`tu`.`uid`=?", 2). + Join("INNER", []interface{}{teamUser, "tu"}, "`tu`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) } @@ -241,7 +241,7 @@ func TestOrder(t *testing.T) { assertSync(t, new(Userinfo)) users := make([]Userinfo, 0) - err := testEngine.OrderBy("id desc").Find(&users) + err := testEngine.OrderBy("`id` desc").Find(&users) assert.NoError(t, err) users2 := make([]Userinfo, 0) @@ -254,7 +254,7 @@ func TestGroupBy(t *testing.T) { assertSync(t, new(Userinfo)) users := make([]Userinfo, 0) - err := testEngine.GroupBy("id, username").Find(&users) + err := testEngine.GroupBy("`id`, `username`").Find(&users) assert.NoError(t, err) } @@ -263,7 +263,7 @@ func TestHaving(t *testing.T) { assertSync(t, new(Userinfo)) users := make([]Userinfo, 0) - err := testEngine.GroupBy("username").Having("username='xlw'").Find(&users) + err := testEngine.GroupBy("`username`").Having("`username`='xlw'").Find(&users) assert.NoError(t, err) } @@ -499,7 +499,7 @@ func TestFindAndCountOneFunc(t *testing.T) { assert.EqualValues(t, 2, cnt) results = make([]FindAndCountStruct, 0, 1) - cnt, err = testEngine.Where("msg = ?", true).FindAndCount(&results) + cnt, err = testEngine.Where("`msg` = ?", true).FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) assert.EqualValues(t, 1, cnt) @@ -549,21 +549,21 @@ func TestFindAndCountOneFunc(t *testing.T) { }, results[0]) results = make([]FindAndCountStruct, 0, 1) - cnt, err = testEngine.Where("msg = ?", true).Select("id, content, msg"). + cnt, err = testEngine.Where("`msg` = ?", true).Select("`id`, `content`, `msg`"). Limit(1).FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) assert.EqualValues(t, 1, cnt) results = make([]FindAndCountStruct, 0, 1) - cnt, err = testEngine.Where("msg = ?", true).Cols("id", "content", "msg"). + cnt, err = testEngine.Where("`msg` = ?", true).Cols("id", "content", "msg"). Limit(1).FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) assert.EqualValues(t, 1, cnt) results = make([]FindAndCountStruct, 0, 1) - cnt, err = testEngine.Where("msg = ?", true).Desc("id"). + cnt, err = testEngine.Where("`msg` = ?", true).Desc("id"). Limit(1).Cols("content").FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) @@ -649,7 +649,7 @@ func TestFindAndCount2(t *testing.T) { cnt, err = testEngine. Table(new(TestFindAndCountHotel)). Alias("t"). - Where("t.region like '6501%'"). + Where("`t`.`region` like '6501%'"). Limit(10, 0). FindAndCount(&hotels) assert.NoError(t, err) @@ -705,7 +705,7 @@ func TestFindAndCountWithGroupBy(t *testing.T) { assert.NoError(t, err) var results []FindAndCountWithGroupBy - cnt, err := testEngine.GroupBy("age").FindAndCount(&results) + cnt, err := testEngine.GroupBy("`age`").FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 2, cnt) assert.EqualValues(t, 2, len(results)) @@ -735,14 +735,14 @@ func TestFindMapStringId(t *testing.T) { deviceMaps := make(map[string]*FindMapDevice, len(deviceIDs)) err = testEngine. - Where("status = ?", 1). + Where("`status` = ?", 1). In("deviceid", deviceIDs). Find(&deviceMaps) assert.NoError(t, err) deviceMaps2 := make(map[string]FindMapDevice, len(deviceIDs)) err = testEngine. - Where("status = ?", 1). + Where("`status` = ?", 1). In("deviceid", deviceIDs). Find(&deviceMaps2) assert.NoError(t, err) @@ -919,17 +919,17 @@ func TestFindJoin(t *testing.T) { assertSync(t, new(SceneItem), new(DeviceUserPrivrels), new(Order)) var scenes []SceneItem - err := testEngine.Join("LEFT OUTER", "device_user_privrels", "device_user_privrels.device_id=scene_item.device_id"). - Where("scene_item.type=?", 3).Or("device_user_privrels.user_id=?", 339).Find(&scenes) + err := testEngine.Join("LEFT OUTER", "device_user_privrels", "`device_user_privrels`.`device_id`=`scene_item`.`device_id`"). + Where("`scene_item`.`type`=?", 3).Or("`device_user_privrels`.`user_id`=?", 339).Find(&scenes) assert.NoError(t, err) scenes = make([]SceneItem, 0) - err = testEngine.Join("LEFT OUTER", new(DeviceUserPrivrels), "device_user_privrels.device_id=scene_item.device_id"). - Where("scene_item.type=?", 3).Or("device_user_privrels.user_id=?", 339).Find(&scenes) + err = testEngine.Join("LEFT OUTER", new(DeviceUserPrivrels), "`device_user_privrels`.`device_id`=`scene_item`.`device_id`"). + Where("`scene_item`.`type`=?", 3).Or("`device_user_privrels`.`user_id`=?", 339).Find(&scenes) assert.NoError(t, err) scenes = make([]SceneItem, 0) - err = testEngine.Join("INNER", "order", "`scene_item`.device_id=`order`.id").Find(&scenes) + err = testEngine.Join("INNER", "order", "`scene_item`.`device_id`=`order`.`id`").Find(&scenes) assert.NoError(t, err) } @@ -949,7 +949,7 @@ func TestJoinFindLimit(t *testing.T) { assertSync(t, new(JoinFindLimit1), new(JoinFindLimit2)) var finds []JoinFindLimit1 - err := testEngine.Join("INNER", new(JoinFindLimit2), "join_find_limit2.eid=join_find_limit1.id"). + err := testEngine.Join("INNER", new(JoinFindLimit2), "`join_find_limit2`.`eid`=`join_find_limit1`.`id`"). Limit(10, 10).Find(&finds) assert.NoError(t, err) } @@ -981,9 +981,9 @@ func TestMoreExtends(t *testing.T) { assertSync(t, new(MoreExtendsUsers), new(MoreExtendsBooks)) var books []MoreExtendsBooksExtend - err := testEngine.Table("more_extends_books").Select("more_extends_books.*, more_extends_users.*"). - Join("INNER", "more_extends_users", "more_extends_books.user_id = more_extends_users.id"). - Where("more_extends_books.name LIKE ?", "abc"). + err := testEngine.Table("more_extends_books").Select("`more_extends_books`.*, `more_extends_users`.*"). + Join("INNER", "more_extends_users", "`more_extends_books`.`user_id` = `more_extends_users`.`id`"). + Where("`more_extends_books`.`name` LIKE ?", "abc"). Limit(10, 10). Find(&books) assert.NoError(t, err) @@ -991,9 +991,9 @@ func TestMoreExtends(t *testing.T) { books = make([]MoreExtendsBooksExtend, 0, len(books)) err = testEngine.Table("more_extends_books"). Alias("m"). - Select("m.*, more_extends_users.*"). - Join("INNER", "more_extends_users", "m.user_id = more_extends_users.id"). - Where("m.name LIKE ?", "abc"). + Select("`m`.*, `more_extends_users`.*"). + Join("INNER", "more_extends_users", "`m`.`user_id` = `more_extends_users`.`id`"). + Where("`m`.`name` LIKE ?", "abc"). Limit(10, 10). Find(&books) assert.NoError(t, err) @@ -1038,11 +1038,11 @@ func TestUpdateFind(t *testing.T) { } _, err := session.Insert(&tuf) assert.NoError(t, err) - _, err = session.Where("id = ?", tuf.Id).Update(&TestUpdateFind{}) + _, err = session.Where("`id` = ?", tuf.Id).Update(&TestUpdateFind{}) assert.EqualError(t, xorm.ErrNoColumnsTobeUpdated, err.Error()) var tufs []TestUpdateFind - err = session.Where("id = ?", tuf.Id).Find(&tufs) + err = session.Where("`id` = ?", tuf.Id).Find(&tufs) assert.NoError(t, err) } diff --git a/integrations/session_get_test.go b/integrations/session_get_test.go index 99ecd462..4fc30adb 100644 --- a/integrations/session_get_test.go +++ b/integrations/session_get_test.go @@ -15,6 +15,7 @@ import ( "xorm.io/xorm" "xorm.io/xorm/contexts" "xorm.io/xorm/convert" + "xorm.io/xorm/dialects" "xorm.io/xorm/schemas" "github.com/shopspring/decimal" @@ -55,15 +56,15 @@ func TestGetVar(t *testing.T) { assert.Equal(t, 28, age) var ageMax int - has, err = testEngine.SQL("SELECT max(age) FROM "+testEngine.TableName("get_var", true)+" WHERE `id` = ?", data.Id).Get(&ageMax) + has, err = testEngine.SQL("SELECT max(`age`) FROM "+testEngine.Quote(testEngine.TableName("get_var", true))+" WHERE `id` = ?", data.Id).Get(&ageMax) assert.NoError(t, err) assert.Equal(t, true, has) assert.Equal(t, 28, ageMax) var age2 int64 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age2) assert.NoError(t, err) assert.Equal(t, true, has) @@ -77,8 +78,8 @@ func TestGetVar(t *testing.T) { var age4 int16 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age4) assert.NoError(t, err) assert.Equal(t, true, has) @@ -86,8 +87,8 @@ func TestGetVar(t *testing.T) { var age5 int32 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age5) assert.NoError(t, err) assert.Equal(t, true, has) @@ -101,8 +102,8 @@ func TestGetVar(t *testing.T) { var age7 int64 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age7) assert.NoError(t, err) assert.Equal(t, true, has) @@ -116,8 +117,8 @@ func TestGetVar(t *testing.T) { var age9 int16 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age9) assert.NoError(t, err) assert.Equal(t, true, has) @@ -125,8 +126,8 @@ func TestGetVar(t *testing.T) { var age10 int32 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age10) assert.NoError(t, err) assert.Equal(t, true, has) @@ -161,16 +162,16 @@ func TestGetVar(t *testing.T) { var money2 float64 if testEngine.Dialect().URI().DBType == schemas.MSSQL { - has, err = testEngine.SQL("SELECT TOP 1 money FROM " + testEngine.TableName("get_var", true)).Get(&money2) + has, err = testEngine.SQL("SELECT TOP 1 `money` FROM " + testEngine.Quote(testEngine.TableName("get_var", true))).Get(&money2) } else { - has, err = testEngine.SQL("SELECT money FROM " + testEngine.TableName("get_var", true) + " LIMIT 1").Get(&money2) + has, err = testEngine.SQL("SELECT `money` FROM " + testEngine.Quote(testEngine.TableName("get_var", true)) + " LIMIT 1").Get(&money2) } assert.NoError(t, err) assert.Equal(t, true, has) assert.Equal(t, "1.5", fmt.Sprintf("%.1f", money2)) var money3 float64 - has, err = testEngine.SQL("SELECT money FROM " + testEngine.TableName("get_var", true) + " WHERE money > 20").Get(&money3) + has, err = testEngine.SQL("SELECT `money` FROM " + testEngine.Quote(testEngine.TableName("get_var", true)) + " WHERE `money` > 20").Get(&money3) assert.NoError(t, err) assert.Equal(t, false, has) @@ -187,7 +188,7 @@ func TestGetVar(t *testing.T) { // for mymysql driver, interface{} will be []byte, so ignore it currently if testEngine.DriverName() != "mymysql" { var valuesInter = make(map[string]interface{}) - has, err = testEngine.Table("get_var").Where("id = ?", 1).Select("*").Get(&valuesInter) + has, err = testEngine.Table("get_var").Where("`id` = ?", 1).Select("*").Get(&valuesInter) assert.NoError(t, err) assert.Equal(t, true, has) assert.Equal(t, 5, len(valuesInter)) @@ -243,7 +244,7 @@ func TestGetStruct(t *testing.T) { if testEngine.Dialect().URI().DBType == schemas.MSSQL { err = session.Begin() assert.NoError(t, err) - _, err = session.Exec("SET IDENTITY_INSERT userinfo_get ON") + _, err = session.Exec("SET IDENTITY_INSERT `userinfo_get` ON") assert.NoError(t, err) } cnt, err := session.Insert(&UserinfoGet{Uid: 2}) @@ -300,6 +301,11 @@ func TestGetSlice(t *testing.T) { func TestGetMap(t *testing.T) { assert.NoError(t, PrepareEngine()) + if testEngine.Dialect().Features().AutoincrMode == dialects.SequenceAutoincrMode { + t.SkipNow() + return + } + type UserinfoMap struct { Uid int `xorm:"pk autoincr"` IsMan bool @@ -308,7 +314,7 @@ func TestGetMap(t *testing.T) { assertSync(t, new(UserinfoMap)) tableName := testEngine.Quote(testEngine.TableName("userinfo_map", true)) - _, err := testEngine.Exec(fmt.Sprintf("INSERT INTO %s (is_man) VALUES (NULL)", tableName)) + _, err := testEngine.Exec(fmt.Sprintf("INSERT INTO %s (`is_man`) VALUES (NULL)", tableName)) assert.NoError(t, err) var valuesString = make(map[string]string) @@ -479,7 +485,7 @@ func TestGetStructId(t *testing.T) { //var id int64 var maxid maxidst - sql := "select max(id) as id from " + testEngine.TableName(&TestGetStruct{}, true) + sql := "select max(`id`) as id from " + testEngine.Quote(testEngine.TableName(&TestGetStruct{}, true)) has, err := testEngine.SQL(sql).Get(&maxid) assert.NoError(t, err) assert.True(t, has) @@ -597,73 +603,78 @@ func TestGetNullVar(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestGetNullVarStruct)) - affected, err := testEngine.Exec("insert into " + testEngine.TableName(new(TestGetNullVarStruct), true) + " (name,age) values (null,null)") + if testEngine.Dialect().Features().AutoincrMode == dialects.SequenceAutoincrMode { + t.SkipNow() + return + } + + affected, err := testEngine.Exec("insert into " + testEngine.Quote(testEngine.TableName(new(TestGetNullVarStruct), true)) + " (`name`,`age`) values (null,null)") assert.NoError(t, err) a, _ := affected.RowsAffected() assert.EqualValues(t, 1, a) var name string - has, err := testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("name").Get(&name) + has, err := testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("name").Get(&name) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "", name) var age int - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age) var age2 int8 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age2) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age2) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age2) var age3 int16 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age3) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age3) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age3) var age4 int32 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age4) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age4) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age4) var age5 int64 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age5) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age5) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age5) var age6 uint - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age6) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age6) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age6) var age7 uint8 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age7) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age7) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age7) var age8 int16 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age8) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age8) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age8) var age9 int32 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age9) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age9) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age9) var age10 int64 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age10) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age10) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age10) @@ -697,7 +708,7 @@ func TestCustomTypes(t *testing.T) { assert.EqualValues(t, "test", name) var age MyInt - has, err = testEngine.Table(new(TestCustomizeStruct)).ID(s.Id).Select("age").Get(&age) + has, err = testEngine.Table(new(TestCustomizeStruct)).ID(s.Id).Select("`age`").Get(&age) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 32, age) @@ -759,7 +770,7 @@ func TestGetBigFloat(t *testing.T) { assert.NoError(t, err) var m big.Float - has, err := testEngine.Table("get_big_float").Cols("money").Where("id=?", gf.Id).Get(&m) + has, err := testEngine.Table("get_big_float").Cols("money").Where("`id`=?", gf.Id).Get(&m) assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) @@ -785,7 +796,7 @@ func TestGetBigFloat(t *testing.T) { assert.NoError(t, err) var m2 big.Float - has, err := testEngine.Table("get_big_float2").Cols("money").Where("id=?", gf2.Id).Get(&m2) + has, err := testEngine.Table("get_big_float2").Cols("money").Where("`id`=?", gf2.Id).Get(&m2) assert.NoError(t, err) assert.True(t, has) assert.True(t, m2.String() == gf2.Money.String(), "%v != %v", m2.String(), gf2.Money.String()) @@ -825,7 +836,7 @@ func TestGetDecimal(t *testing.T) { assert.NoError(t, err) var m decimal.Decimal - has, err := testEngine.Table("get_decimal").Cols("money").Where("id=?", gf.Id).Get(&m) + has, err := testEngine.Table("get_decimal").Cols("money").Where("`id`=?", gf.Id).Get(&m) assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) @@ -850,7 +861,7 @@ func TestGetDecimal(t *testing.T) { assert.NoError(t, err) var m decimal.Decimal - has, err := testEngine.Table("get_decimal2").Cols("money").Where("id=?", gf.Id).Get(&m) + has, err := testEngine.Table("get_decimal2").Cols("money").Where("`id`=?", gf.Id).Get(&m) assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) diff --git a/integrations/session_insert_test.go b/integrations/session_insert_test.go index cd56a958..70ec13f3 100644 --- a/integrations/session_insert_test.go +++ b/integrations/session_insert_test.go @@ -11,6 +11,7 @@ import ( "time" "xorm.io/xorm" + "xorm.io/xorm/schemas" "github.com/stretchr/testify/assert" ) @@ -191,8 +192,8 @@ func TestInsertDefault(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, -1, di.Status) - assert.EqualValues(t, di2.Updated.Unix(), di.Updated.Unix()) - assert.EqualValues(t, di2.Created.Unix(), di.Created.Unix()) + assert.EqualValues(t, di2.Updated.Unix(), di.Updated.Unix(), di.Updated) + assert.EqualValues(t, di2.Created.Unix(), di.Created.Unix(), di.Created) } func TestInsertDefault2(t *testing.T) { @@ -624,6 +625,11 @@ func TestAnonymousStruct(t *testing.T) { } func TestInsertMap(t *testing.T) { + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + type InsertMap struct { Id int64 Width uint32 @@ -727,7 +733,7 @@ func TestInsertWhere(t *testing.T) { } inserted, err := testEngine.SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). - Where("repo_id=?", 1). + Where("`repo_id`=?", 1). Insert(&i) assert.NoError(t, err) assert.EqualValues(t, 1, inserted) @@ -740,7 +746,12 @@ func TestInsertWhere(t *testing.T) { i.Index = 1 assert.EqualValues(t, i, j) - inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + + inserted, err = testEngine.Table(new(InsertWhere)).Where("`repo_id`=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). Insert(map[string]interface{}{ "repo_id": 1, @@ -761,7 +772,7 @@ func TestInsertWhere(t *testing.T) { assert.EqualValues(t, "trest2", j2.Name) assert.EqualValues(t, 2, j2.Index) - inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + inserted, err = testEngine.Table(new(InsertWhere)).Where("`repo_id`=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). SetExpr("repo_id", "1"). Insert(map[string]string{ @@ -777,7 +788,7 @@ func TestInsertWhere(t *testing.T) { assert.EqualValues(t, "trest3", j3.Name) assert.EqualValues(t, 3, j3.Index) - inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + inserted, err = testEngine.Table(new(InsertWhere)).Where("`repo_id`=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). Insert(map[string]interface{}{ "repo_id": 1, @@ -793,7 +804,7 @@ func TestInsertWhere(t *testing.T) { assert.EqualValues(t, "10';delete * from insert_where; --", j4.Name) assert.EqualValues(t, 4, j4.Index) - inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + inserted, err = testEngine.Table(new(InsertWhere)).Where("`repo_id`=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). Insert(map[string]interface{}{ "repo_id": 1, @@ -846,6 +857,11 @@ func TestInsertExpr2(t *testing.T) { assert.EqualValues(t, 1, ie2.RepoId) assert.EqualValues(t, true, ie2.IsTag) + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + inserted, err = testEngine.Table(new(InsertExprsRelease)). SetExpr("is_draft", true). SetExpr("num_commits", 0). @@ -1067,6 +1083,11 @@ func TestInsertDeleted(t *testing.T) { } func TestInsertMultipleMap(t *testing.T) { + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + type InsertMultipleMap struct { Id int64 Width uint32 diff --git a/integrations/session_iterate_test.go b/integrations/session_iterate_test.go index 564f457b..95f5a282 100644 --- a/integrations/session_iterate_test.go +++ b/integrations/session_iterate_test.go @@ -91,7 +91,7 @@ func TestBufferIterate(t *testing.T) { assert.EqualValues(t, 7, cnt) cnt = 0 - err = testEngine.Where("id <= 10").BufferSize(2).Iterate(new(UserBufferIterate), func(i int, bean interface{}) error { + err = testEngine.Where("`id` <= 10").BufferSize(2).Iterate(new(UserBufferIterate), func(i int, bean interface{}) error { user := bean.(*UserBufferIterate) assert.EqualValues(t, cnt+1, user.Id) assert.EqualValues(t, true, user.IsMan) diff --git a/integrations/session_query_test.go b/integrations/session_query_test.go index ef2ccdd6..edc77aec 100644 --- a/integrations/session_query_test.go +++ b/integrations/session_query_test.go @@ -5,7 +5,6 @@ package integrations import ( - "fmt" "strconv" "testing" "time" @@ -37,7 +36,7 @@ func TestQueryString(t *testing.T) { _, err := testEngine.InsertOne(data) assert.NoError(t, err) - records, err := testEngine.QueryString("select * from " + testEngine.TableName("get_var2", true)) + records, err := testEngine.QueryString("select * from " + testEngine.Quote(testEngine.TableName("get_var2", true))) assert.NoError(t, err) assert.Equal(t, 1, len(records)) assert.Equal(t, 5, len(records[0])) @@ -63,7 +62,7 @@ func TestQueryString2(t *testing.T) { _, err := testEngine.Insert(data) assert.NoError(t, err) - records, err := testEngine.QueryString("select * from " + testEngine.TableName("get_var3", true)) + records, err := testEngine.QueryString("select * from " + testEngine.Quote(testEngine.TableName("get_var3", true))) assert.NoError(t, err) assert.Equal(t, 1, len(records)) assert.Equal(t, 2, len(records[0])) @@ -71,42 +70,6 @@ func TestQueryString2(t *testing.T) { assert.True(t, "0" == records[0]["msg"] || "false" == records[0]["msg"]) } -func toString(i interface{}) string { - switch i.(type) { - case []byte: - return string(i.([]byte)) - case string: - return i.(string) - } - return fmt.Sprintf("%v", i) -} - -func toInt64(i interface{}) int64 { - switch i.(type) { - case []byte: - n, _ := strconv.ParseInt(string(i.([]byte)), 10, 64) - return n - case int: - return int64(i.(int)) - case int64: - return i.(int64) - } - return 0 -} - -func toFloat64(i interface{}) float64 { - switch i.(type) { - case []byte: - n, _ := strconv.ParseFloat(string(i.([]byte)), 64) - return n - case float64: - return i.(float64) - case float32: - return float64(i.(float32)) - } - return 0 -} - func toBool(i interface{}) bool { switch t := i.(type) { case int32: @@ -138,7 +101,7 @@ func TestQueryInterface(t *testing.T) { _, err := testEngine.InsertOne(data) assert.NoError(t, err) - records, err := testEngine.QueryInterface("select * from " + testEngine.TableName("get_var_interface", true)) + records, err := testEngine.QueryInterface("select * from " + testEngine.Quote(testEngine.TableName("get_var_interface", true))) assert.NoError(t, err) assert.Equal(t, 1, len(records)) assert.Equal(t, 5, len(records[0])) @@ -192,7 +155,7 @@ func TestQueryNoParams(t *testing.T) { assert.NoError(t, err) assertResult(t, results) - results, err = testEngine.SQL("select * from " + testEngine.TableName("query_no_params", true)).Query() + results, err = testEngine.SQL("select * from " + testEngine.Quote(testEngine.TableName("query_no_params", true))).Query() assert.NoError(t, err) assertResult(t, results) } @@ -223,7 +186,7 @@ func TestQueryStringNoParam(t *testing.T) { assert.EqualValues(t, "0", records[0]["msg"]) } - records, err = testEngine.Table("get_var4").Where(builder.Eq{"id": 1}).QueryString() + records, err = testEngine.Table("get_var4").Where(builder.Eq{"`id`": 1}).QueryString() assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, "1", records[0]["id"]) @@ -260,7 +223,7 @@ func TestQuerySliceStringNoParam(t *testing.T) { assert.EqualValues(t, "0", records[0][1]) } - records, err = testEngine.Table("get_var6").Where(builder.Eq{"id": 1}).QuerySliceString() + records, err = testEngine.Table("get_var6").Where(builder.Eq{"`id`": 1}).QuerySliceString() assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, "1", records[0][0]) @@ -293,7 +256,7 @@ func TestQueryInterfaceNoParam(t *testing.T) { assert.EqualValues(t, 1, records[0]["id"]) assert.False(t, toBool(records[0]["msg"])) - records, err = testEngine.Table("get_var5").Where(builder.Eq{"id": 1}).QueryInterface() + records, err = testEngine.Table("get_var5").Where(builder.Eq{"`id`": 1}).QueryInterface() assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, 1, records[0]["id"]) @@ -340,7 +303,7 @@ func TestQueryWithBuilder(t *testing.T) { assert.EqualValues(t, 3000, money) } - results, err := testEngine.Query(builder.Select("*").From(testEngine.TableName("query_with_builder", true))) + results, err := testEngine.Query(builder.Select("*").From(testEngine.Quote(testEngine.TableName("query_with_builder", true)))) assert.NoError(t, err) assertResult(t, results) } @@ -383,14 +346,14 @@ func TestJoinWithSubQuery(t *testing.T) { tbName := testEngine.Quote(testEngine.TableName("join_with_sub_query_depart", true)) var querys []JoinWithSubQuery1 - err = testEngine.Join("INNER", builder.Select("id").From(tbName), - "join_with_sub_query_depart.id = join_with_sub_query1.depart_id").Find(&querys) + err = testEngine.Join("INNER", builder.Select("`id`").From(tbName), + "`join_with_sub_query_depart`.`id` = `join_with_sub_query1`.`depart_id`").Find(&querys) assert.NoError(t, err) assert.EqualValues(t, 1, len(querys)) assert.EqualValues(t, q, querys[0]) querys = make([]JoinWithSubQuery1, 0, 1) - err = testEngine.Join("INNER", "(SELECT id FROM "+tbName+") join_with_sub_query_depart", "join_with_sub_query_depart.id = join_with_sub_query1.depart_id"). + err = testEngine.Join("INNER", "(SELECT `id` FROM "+tbName+") `a`", "`a`.`id` = `join_with_sub_query1`.`depart_id`"). Find(&querys) assert.NoError(t, err) assert.EqualValues(t, 1, len(querys)) diff --git a/integrations/session_raw_test.go b/integrations/session_raw_test.go index 36677683..44718f46 100644 --- a/integrations/session_raw_test.go +++ b/integrations/session_raw_test.go @@ -22,13 +22,13 @@ func TestExecAndQuery(t *testing.T) { assert.NoError(t, testEngine.Sync2(new(UserinfoQuery))) - res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_query`", true)+" (uid, name) VALUES (?, ?)", 1, "user") + res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_query`", true)+" (`uid`, `name`) VALUES (?, ?)", 1, "user") assert.NoError(t, err) cnt, err := res.RowsAffected() assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - results, err := testEngine.Query("select * from " + testEngine.TableName("userinfo_query", true)) + results, err := testEngine.Query("select * from " + testEngine.Quote(testEngine.TableName("userinfo_query", true))) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) id, err := strconv.Atoi(string(results[0]["uid"])) @@ -48,19 +48,19 @@ func TestExecTime(t *testing.T) { assert.NoError(t, testEngine.Sync2(new(UserinfoExecTime))) now := time.Now() - res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_exec_time`", true)+" (uid, name, created) VALUES (?, ?, ?)", 1, "user", now) + res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_exec_time`", true)+" (`uid`, `name`, `created`) VALUES (?, ?, ?)", 1, "user", now) assert.NoError(t, err) cnt, err := res.RowsAffected() assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - results, err := testEngine.QueryString("SELECT * FROM " + testEngine.TableName("`userinfo_exec_time`", true)) + results, err := testEngine.QueryString("SELECT * FROM " + testEngine.Quote(testEngine.TableName("userinfo_exec_time", true))) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) assert.EqualValues(t, now.In(testEngine.GetTZLocation()).Format("2006-01-02 15:04:05"), results[0]["created"]) var uet UserinfoExecTime - has, err := testEngine.Where("uid=?", 1).Get(&uet) + has, err := testEngine.Where("`uid`=?", 1).Get(&uet) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, now.In(testEngine.GetTZLocation()).Format("2006-01-02 15:04:05"), uet.Created.Format("2006-01-02 15:04:05")) diff --git a/integrations/session_schema_test.go b/integrations/session_schema_test.go index 9cbebcbf..98ad9657 100644 --- a/integrations/session_schema_test.go +++ b/integrations/session_schema_test.go @@ -526,8 +526,9 @@ func TestModifyColum(t *testing.T) { SQLType: schemas.SQLType{ Name: "VARCHAR", }, - Length: 16, - Nullable: false, + Length: 16, + Nullable: false, + DefaultIsEmpty: true, }) _, err := testEngine.Exec(alterSQL) assert.NoError(t, err) diff --git a/integrations/session_test.go b/integrations/session_test.go index c3ef0513..eacf2ff5 100644 --- a/integrations/session_test.go +++ b/integrations/session_test.go @@ -18,7 +18,7 @@ func TestClose(t *testing.T) { sess1.Close() assert.True(t, sess1.IsClosed()) - sess2 := testEngine.Where("a = ?", 1) + sess2 := testEngine.Where("`a` = ?", 1) sess2.Close() assert.True(t, sess2.IsClosed()) } diff --git a/integrations/session_tx_test.go b/integrations/session_tx_test.go index 4cff5610..8d6519d0 100644 --- a/integrations/session_tx_test.go +++ b/integrations/session_tx_test.go @@ -37,7 +37,7 @@ func TestTransaction(t *testing.T) { assert.NoError(t, err) user2 := Userinfo{Username: "yyy"} - _, err = session.Where("id = ?", 0).Update(&user2) + _, err = session.Where("`id` = ?", 0).Update(&user2) assert.NoError(t, err) _, err = session.Delete(&user2) @@ -70,10 +70,10 @@ func TestCombineTransaction(t *testing.T) { assert.NoError(t, err) user2 := Userinfo{Username: "zzz"} - _, err = session.Where("id = ?", 0).Update(&user2) + _, err = session.Where("`id` = ?", 0).Update(&user2) assert.NoError(t, err) - _, err = session.Exec("delete from "+testEngine.TableName("userinfo", true)+" where username = ?", user2.Username) + _, err = session.Exec("delete from "+testEngine.Quote(testEngine.TableName("userinfo", true))+" where `username` = ?", user2.Username) assert.NoError(t, err) err = session.Commit() @@ -113,10 +113,10 @@ func TestCombineTransactionSameMapper(t *testing.T) { assert.NoError(t, err) user2 := Userinfo{Username: "zzz"} - _, err = session.Where("id = ?", 0).Update(&user2) + _, err = session.Where("`id` = ?", 0).Update(&user2) assert.NoError(t, err) - _, err = session.Exec("delete from "+testEngine.TableName("`Userinfo`", true)+" where `Username` = ?", user2.Username) + _, err = session.Exec("delete from "+testEngine.Quote(testEngine.TableName("Userinfo", true))+" where `Username` = ?", user2.Username) assert.NoError(t, err) err = session.Commit() @@ -144,7 +144,7 @@ func TestMultipleTransaction(t *testing.T) { assert.NoError(t, err) user2 := MultipleTransaction{Name: "zzz"} - _, err = session.Where("id = ?", 0).Update(&user2) + _, err = session.Where("`id` = ?", 0).Update(&user2) assert.NoError(t, err) err = session.Commit() @@ -158,7 +158,7 @@ func TestMultipleTransaction(t *testing.T) { err = session.Begin() assert.NoError(t, err) - _, err = session.Where("id=?", m1.Id).Delete(new(MultipleTransaction)) + _, err = session.Where("`id`=?", m1.Id).Delete(new(MultipleTransaction)) assert.NoError(t, err) err = session.Commit() diff --git a/integrations/session_update_test.go b/integrations/session_update_test.go index bbcc7600..4312d0e0 100644 --- a/integrations/session_update_test.go +++ b/integrations/session_update_test.go @@ -35,7 +35,7 @@ func TestUpdateMap(t *testing.T) { _, err := testEngine.Insert(&tb) assert.NoError(t, err) - cnt, err := testEngine.Table("update_table").Where("id = ?", tb.Id).Update(map[string]interface{}{ + cnt, err := testEngine.Table("update_table").Where("`id` = ?", tb.Id).Update(map[string]interface{}{ "name": "test2", "age": 36, }) @@ -93,7 +93,12 @@ func TestUpdateLimit(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - cnt, err = testEngine.OrderBy("name desc").Limit(1).Update(&UpdateTable2{ + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + + cnt, err = testEngine.OrderBy("`name` desc").Limit(1).Update(&UpdateTable2{ Age: 30, }) assert.NoError(t, err) @@ -166,7 +171,7 @@ func TestForUpdate(t *testing.T) { // use lock fList := make([]ForUpdate, 0) session1.ForUpdate() - session1.Where("id = ?", 1) + session1.Where("`id` = ?", 1) err = session1.Find(&fList) switch { case err != nil: @@ -187,7 +192,7 @@ func TestForUpdate(t *testing.T) { wg.Add(1) go func() { f2 := new(ForUpdate) - session2.Where("id = ?", 1).ForUpdate() + session2.Where("`id` = ?", 1).ForUpdate() has, err := session2.Get(f2) // wait release lock switch { case err != nil: @@ -207,7 +212,7 @@ func TestForUpdate(t *testing.T) { wg2.Add(1) go func() { f3 := new(ForUpdate) - session3.Where("id = ?", 1) + session3.Where("`id` = ?", 1) has, err := session3.Get(f3) // wait release lock switch { case err != nil: @@ -225,7 +230,7 @@ func TestForUpdate(t *testing.T) { f := new(ForUpdate) f.Name = "updated by session1" - session1.Where("id = ?", 1) + session1.Where("`id` = ?", 1) session1.Update(f) // release lock @@ -300,7 +305,7 @@ func TestUpdateMap2(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(UpdateMustCols)) - _, err := testEngine.Table("update_must_cols").Where("id =?", 1).Update(map[string]interface{}{ + _, err := testEngine.Table("update_must_cols").Where("`id` =?", 1).Update(map[string]interface{}{ "bool": true, }) assert.NoError(t, err) @@ -345,11 +350,11 @@ func TestUpdate1(t *testing.T) { userID := user.Uid has, err := testEngine.ID(userID). - And("username = ?", user.Username). - And("height = ?", user.Height). - And("departname = ?", ""). - And("detail_id = ?", 0). - And("is_man = ?", false). + And("`username` = ?", user.Username). + And("`height` = ?", user.Height). + And("`departname` = ?", ""). + And("`detail_id` = ?", 0). + And("`is_man` = ?", false). Get(&Userinfo{}) assert.NoError(t, err) assert.True(t, has, "cannot insert properly") @@ -362,12 +367,12 @@ func TestUpdate1(t *testing.T) { assert.EqualValues(t, 1, cnt, "update not returned 1") has, err = testEngine.ID(userID). - And("username = ?", updatedUser.Username). - And("height IS NULL"). - And("departname IS NULL"). - And("is_man IS NULL"). - And("created IS NULL"). - And("detail_id = ?", 0). + And("`username` = ?", updatedUser.Username). + And("`height` IS NULL"). + And("`departname` IS NULL"). + And("`is_man` IS NULL"). + And("`created` IS NULL"). + And("`detail_id` = ?", 0). Get(&Userinfo{}) assert.NoError(t, err) assert.True(t, has, "cannot update with null properly") @@ -825,7 +830,7 @@ func TestNewUpdate(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 0, af) - af, err = testEngine.Table(new(TbUserInfo)).Where("phone=?", "13126564922").Update(&changeUsr) + af, err = testEngine.Table(new(TbUserInfo)).Where("`phone`=?", "13126564922").Update(&changeUsr) assert.NoError(t, err) assert.EqualValues(t, 0, af) } @@ -1166,7 +1171,7 @@ func TestUpdateExprs(t *testing.T) { }) assert.NoError(t, err) - _, err = testEngine.SetExpr("num_issues", "num_issues+1").AllCols().Update(&UpdateExprs{ + _, err = testEngine.SetExpr("num_issues", "`num_issues`+1").AllCols().Update(&UpdateExprs{ NumIssues: 3, Name: "lunny xiao", }) @@ -1197,7 +1202,7 @@ func TestUpdateAlias(t *testing.T) { }) assert.NoError(t, err) - _, err = testEngine.Alias("ua").Where("ua.id = ?", 1).Update(&UpdateAlias{ + _, err = testEngine.Alias("ua").Where("ua.`id` = ?", 1).Update(&UpdateAlias{ NumIssues: 2, Name: "lunny xiao", }) @@ -1237,7 +1242,7 @@ func TestUpdateExprs2(t *testing.T) { assert.EqualValues(t, 1, inserted) updated, err := testEngine. - Where("repo_id = ? AND is_tag = ?", 1, false). + Where("`repo_id` = ? AND `is_tag` = ?", 1, false). SetExpr("is_draft", true). SetExpr("num_commits", 0). SetExpr("sha1", ""). @@ -1257,6 +1262,11 @@ func TestUpdateExprs2(t *testing.T) { } func TestUpdateMap3(t *testing.T) { + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + assert.NoError(t, PrepareEngine()) type UpdateMapUser struct { @@ -1308,7 +1318,7 @@ func TestUpdateIgnoreOnlyFromDBFields(t *testing.T) { assertGetRecord := func() *TestOnlyFromDBField { var record TestOnlyFromDBField - has, err := testEngine.Where("id = ?", 1).Get(&record) + has, err := testEngine.Where("`id` = ?", 1).Get(&record) assert.NoError(t, err) assert.EqualValues(t, true, has) assert.EqualValues(t, "", record.OnlyFromDBField) diff --git a/integrations/tags_test.go b/integrations/tags_test.go index fc7b505e..b5bf222e 100644 --- a/integrations/tags_test.go +++ b/integrations/tags_test.go @@ -458,7 +458,7 @@ func TestExtends5(t *testing.T) { list := make([]Book, 0) err = session. Select(fmt.Sprintf( - "%s.%s, sc.%s AS %s, sc.%s AS %s, s.%s, s.%s", + "%s.%s, `sc`.%s AS %s, `sc`.%s AS %s, `s`.%s, `s`.%s", quote(bookTableName), quote("id"), quote("Width"), @@ -472,12 +472,12 @@ func TestExtends5(t *testing.T) { Join( "LEFT", sizeTableName+" AS `sc`", - bookTableName+".`SizeClosed`=sc.`id`", + bookTableName+".`SizeClosed`=`sc`.`id`", ). Join( "LEFT", sizeTableName+" AS `s`", - bookTableName+".`Size`=s.`id`", + bookTableName+".`Size`=`s`.`id`", ). Find(&list) assert.NoError(t, err) @@ -730,7 +730,7 @@ func TestLowerCase(t *testing.T) { err := testEngine.Sync2(&Lowercase{}) assert.NoError(t, err) - _, err = testEngine.Where("id > 0").Delete(&Lowercase{}) + _, err = testEngine.Where("`id` > 0").Delete(&Lowercase{}) assert.NoError(t, err) _, err = testEngine.Insert(&Lowercase{ended: 1}) diff --git a/integrations/time_test.go b/integrations/time_test.go index cd2e879f..a8447eea 100644 --- a/integrations/time_test.go +++ b/integrations/time_test.go @@ -324,7 +324,7 @@ func TestTimeUserDeleted(t *testing.T) { fmt.Println("user2 str", user2.CreatedAtStr, user2.UpdatedAtStr) var user3 UserDeleted - cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + cnt, err = testEngine.Where("`id` = ?", "lunny").Delete(&user3) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) assert.True(t, !utils.IsTimeZero(user3.DeletedAt)) @@ -386,7 +386,7 @@ func TestTimeUserDeletedDiffLoc(t *testing.T) { fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) var user3 UserDeleted2 - cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + cnt, err = testEngine.Where("`id` = ?", "lunny").Delete(&user3) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) assert.True(t, !utils.IsTimeZero(user3.DeletedAt)) @@ -457,7 +457,7 @@ func TestCustomTimeUserDeleted(t *testing.T) { fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) var user3 UserDeleted3 - cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + cnt, err = testEngine.Where("`id` = ?", "lunny").Delete(&user3) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) assert.True(t, !utils.IsTimeZero(time.Time(user3.DeletedAt))) @@ -519,7 +519,7 @@ func TestCustomTimeUserDeletedDiffLoc(t *testing.T) { fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) var user3 UserDeleted4 - cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + cnt, err = testEngine.Where("`id` = ?", "lunny").Delete(&user3) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) assert.True(t, !utils.IsTimeZero(time.Time(user3.DeletedAt))) diff --git a/integrations/types_null_test.go b/integrations/types_null_test.go index 86ce1939..8d98b456 100644 --- a/integrations/types_null_test.go +++ b/integrations/types_null_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" ) -type NullType struct { +type NullStruct struct { Id int `xorm:"pk autoincr"` Name sql.NullString Age sql.NullInt64 @@ -65,26 +65,26 @@ func (m CustomStruct) Value() (driver.Value, error) { func TestCreateNullStructTable(t *testing.T) { assert.NoError(t, PrepareEngine()) - err := testEngine.CreateTables(new(NullType)) + err := testEngine.CreateTables(new(NullStruct)) assert.NoError(t, err) } func TestDropNullStructTable(t *testing.T) { assert.NoError(t, PrepareEngine()) - err := testEngine.DropTables(new(NullType)) + err := testEngine.DropTables(new(NullStruct)) assert.NoError(t, err) } func TestNullStructInsert(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - item1 := new(NullType) + item1 := new(NullStruct) _, err := testEngine.Insert(item1) assert.NoError(t, err) assert.EqualValues(t, 1, item1.Id) - item := NullType{ + item := NullStruct{ Name: sql.NullString{String: "haolei", Valid: true}, Age: sql.NullInt64{Int64: 34, Valid: true}, Height: sql.NullFloat64{Float64: 1.72, Valid: true}, @@ -95,9 +95,9 @@ func TestNullStructInsert(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, item.Id) - items := []NullType{} + items := []NullStruct{} for i := 0; i < 5; i++ { - item := NullType{ + item := NullStruct{ Name: sql.NullString{String: "haolei_" + fmt.Sprint(i+1), Valid: true}, Age: sql.NullInt64{Int64: 30 + int64(i), Valid: true}, Height: sql.NullFloat64{Float64: 1.5 + 1.1*float64(i), Valid: true}, @@ -111,7 +111,7 @@ func TestNullStructInsert(t *testing.T) { _, err = testEngine.Insert(&items) assert.NoError(t, err) - items = make([]NullType, 0, 7) + items = make([]NullStruct, 0, 7) err = testEngine.Find(&items) assert.NoError(t, err) assert.EqualValues(t, 7, len(items)) @@ -119,9 +119,9 @@ func TestNullStructInsert(t *testing.T) { func TestNullStructUpdate(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - _, err := testEngine.Insert([]NullType{ + _, err := testEngine.Insert([]NullStruct{ { Name: sql.NullString{ String: "name1", @@ -150,7 +150,7 @@ func TestNullStructUpdate(t *testing.T) { assert.NoError(t, err) if true { // 测试可插入NULL - item := new(NullType) + item := new(NullStruct) item.Age = sql.NullInt64{Int64: 23, Valid: true} item.Height = sql.NullFloat64{Float64: 0, Valid: false} // update to NULL @@ -160,7 +160,7 @@ func TestNullStructUpdate(t *testing.T) { } if true { // 测试In update - item := new(NullType) + item := new(NullStruct) item.Age = sql.NullInt64{Int64: 23, Valid: true} affected, err := testEngine.In("id", 3, 4).Cols("age", "height", "is_man").Update(item) assert.NoError(t, err) @@ -168,17 +168,17 @@ func TestNullStructUpdate(t *testing.T) { } if true { // 测试where - item := new(NullType) + item := new(NullStruct) item.Name = sql.NullString{String: "nullname", Valid: true} item.IsMan = sql.NullBool{Bool: true, Valid: true} item.Age = sql.NullInt64{Int64: 34, Valid: true} - _, err := testEngine.Where("age > ?", 34).Update(item) + _, err := testEngine.Where("`age` > ?", 34).Update(item) assert.NoError(t, err) } if true { // 修改全部时,插入空值 - item := &NullType{ + item := &NullStruct{ Name: sql.NullString{String: "winxxp", Valid: true}, Age: sql.NullInt64{Int64: 30, Valid: true}, Height: sql.NullFloat64{Float64: 1.72, Valid: true}, @@ -192,9 +192,9 @@ func TestNullStructUpdate(t *testing.T) { func TestNullStructFind(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - _, err := testEngine.Insert([]NullType{ + _, err := testEngine.Insert([]NullStruct{ { Name: sql.NullString{ String: "name1", @@ -223,7 +223,7 @@ func TestNullStructFind(t *testing.T) { assert.NoError(t, err) if true { - item := new(NullType) + item := new(NullStruct) has, err := testEngine.ID(1).Get(item) assert.NoError(t, err) assert.True(t, has) @@ -235,7 +235,7 @@ func TestNullStructFind(t *testing.T) { } if true { - item := new(NullType) + item := new(NullStruct) item.Id = 2 has, err := testEngine.Get(item) assert.NoError(t, err) @@ -243,13 +243,13 @@ func TestNullStructFind(t *testing.T) { } if true { - item := make([]NullType, 0) + item := make([]NullStruct, 0) err := testEngine.ID(2).Find(&item) assert.NoError(t, err) } if true { - item := make([]NullType, 0) + item := make([]NullStruct, 0) err := testEngine.Asc("age").Find(&item) assert.NoError(t, err) } @@ -257,12 +257,12 @@ func TestNullStructFind(t *testing.T) { func TestNullStructIterate(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) if true { - err := testEngine.Where("age IS NOT NULL").OrderBy("age").Iterate(new(NullType), + err := testEngine.Where("`age` IS NOT NULL").OrderBy("age").Iterate(new(NullStruct), func(i int, bean interface{}) error { - nultype := bean.(*NullType) + nultype := bean.(*NullStruct) fmt.Println(i, nultype) return nil }) @@ -272,21 +272,21 @@ func TestNullStructIterate(t *testing.T) { func TestNullStructCount(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) if true { - item := new(NullType) - _, err := testEngine.Where("age IS NOT NULL").Count(item) + item := new(NullStruct) + _, err := testEngine.Where("`age` IS NOT NULL").Count(item) assert.NoError(t, err) } } func TestNullStructRows(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - item := new(NullType) - rows, err := testEngine.Where("id > ?", 1).Rows(item) + item := new(NullStruct) + rows, err := testEngine.Where("`id` > ?", 1).Rows(item) assert.NoError(t, err) defer rows.Close() @@ -298,13 +298,13 @@ func TestNullStructRows(t *testing.T) { func TestNullStructDelete(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - item := new(NullType) + item := new(NullStruct) _, err := testEngine.ID(1).Delete(item) assert.NoError(t, err) - _, err = testEngine.Where("id > ?", 1).Delete(item) + _, err = testEngine.Where("`id` > ?", 1).Delete(item) assert.NoError(t, err) } diff --git a/integrations/types_test.go b/integrations/types_test.go index b08dfec5..48facb21 100644 --- a/integrations/types_test.go +++ b/integrations/types_test.go @@ -428,7 +428,7 @@ func TestUnsignedUint64(t *testing.T) { assert.EqualValues(t, "INTEGER", tables[0].Columns()[0].SQLType.Name) case schemas.MYSQL: assert.EqualValues(t, "UNSIGNED BIGINT", tables[0].Columns()[0].SQLType.Name) - case schemas.POSTGRES: + case schemas.POSTGRES, schemas.DAMENG: assert.EqualValues(t, "BIGINT", tables[0].Columns()[0].SQLType.Name) case schemas.MSSQL: assert.EqualValues(t, "BIGINT", tables[0].Columns()[0].SQLType.Name) @@ -472,9 +472,7 @@ func TestUnsignedUint32(t *testing.T) { assert.EqualValues(t, "INTEGER", tables[0].Columns()[0].SQLType.Name) case schemas.MYSQL: assert.EqualValues(t, "UNSIGNED INT", tables[0].Columns()[0].SQLType.Name) - case schemas.POSTGRES: - assert.EqualValues(t, "BIGINT", tables[0].Columns()[0].SQLType.Name) - case schemas.MSSQL: + case schemas.POSTGRES, schemas.MSSQL, schemas.DAMENG: assert.EqualValues(t, "BIGINT", tables[0].Columns()[0].SQLType.Name) default: assert.False(t, true, "Unsigned is not implemented") @@ -507,7 +505,7 @@ func TestUnsignedTinyInt(t *testing.T) { assert.EqualValues(t, 1, len(tables[0].Columns())) switch testEngine.Dialect().URI().DBType { - case schemas.SQLITE: + case schemas.SQLITE, schemas.DAMENG: assert.EqualValues(t, "INTEGER", tables[0].Columns()[0].SQLType.Name) case schemas.MYSQL: assert.EqualValues(t, "UNSIGNED TINYINT", tables[0].Columns()[0].SQLType.Name) @@ -516,7 +514,7 @@ func TestUnsignedTinyInt(t *testing.T) { case schemas.MSSQL: assert.EqualValues(t, "INT", tables[0].Columns()[0].SQLType.Name) default: - assert.False(t, true, "Unsigned is not implemented") + assert.False(t, true, fmt.Sprintf("Unsigned is not implemented, returned %s", tables[0].Columns()[0].SQLType.Name)) } cnt, err := testEngine.Insert(&MyUnsignedTinyIntStruct{ diff --git a/internal/statements/insert.go b/internal/statements/insert.go index 84547cdf..91a33319 100644 --- a/internal/statements/insert.go +++ b/internal/statements/insert.go @@ -10,6 +10,7 @@ import ( "strings" "xorm.io/builder" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -42,7 +43,19 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } - if len(colNames) <= 0 { + var hasInsertColumns = len(colNames) > 0 + var needSeq = len(table.AutoIncrement) > 0 && (statement.dialect.URI().DBType == schemas.ORACLE || statement.dialect.URI().DBType == schemas.DAMENG) + if needSeq { + for _, col := range colNames { + if strings.EqualFold(col, table.AutoIncrement) { + needSeq = false + break + } + } + } + + if !hasInsertColumns && statement.dialect.URI().DBType != schemas.ORACLE && + statement.dialect.URI().DBType != schemas.DAMENG { if statement.dialect.URI().DBType == schemas.MYSQL { if _, err := buf.WriteString(" VALUES ()"); err != nil { return "", nil, err @@ -60,6 +73,10 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } + if needSeq { + colNames = append(colNames, table.AutoIncrement) + } + if err := statement.dialect.Quoter().JoinWrite(buf.Builder, append(colNames, exprs.ColNames()...), ","); err != nil { return "", nil, err } @@ -80,13 +97,23 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } + if needSeq { + if len(args) > 0 { + if _, err := buf.WriteString(","); err != nil { + return "", nil, err + } + } + if _, err := buf.WriteString(utils.SeqName(tableName) + ".nextval"); err != nil { + return "", nil, err + } + } if len(exprs) > 0 { if _, err := buf.WriteString(","); err != nil { return "", nil, err } - } - if err := exprs.WriteArgs(buf); err != nil { - return "", nil, err + if err := exprs.WriteArgs(buf); err != nil { + return "", nil, err + } } if _, err := buf.WriteString(" FROM "); err != nil { @@ -113,6 +140,18 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } + // Insert tablename (id) Values(seq_tablename.nextval) + if needSeq { + if hasInsertColumns { + if _, err := buf.WriteString(","); err != nil { + return "", nil, err + } + } + if _, err := buf.WriteString(utils.SeqName(tableName) + ".nextval"); err != nil { + return "", nil, err + } + } + if len(exprs) > 0 { if _, err := buf.WriteString(","); err != nil { return "", nil, err diff --git a/internal/statements/statement.go b/internal/statements/statement.go index 1fcc0bba..b85773db 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -539,7 +539,7 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) aliasName = schemas.CommonQuoter.Trim(aliasName) - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), aliasName, statement.ReplaceQuote(condition)) + fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) statement.joinArgs = append(statement.joinArgs, subQueryArgs...) case *builder.Builder: subSQL, subQueryArgs, err := tp.ToSQL() @@ -552,7 +552,7 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) aliasName = schemas.CommonQuoter.Trim(aliasName) - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), aliasName, statement.ReplaceQuote(condition)) + fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) statement.joinArgs = append(statement.joinArgs, subQueryArgs...) default: tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true) @@ -560,6 +560,8 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition var buf strings.Builder statement.dialect.Quoter().QuoteTo(&buf, tbName) tbName = buf.String() + } else { + tbName = statement.ReplaceQuote(tbName) } fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condition)) } @@ -642,14 +644,6 @@ func (statement *Statement) genColumnStr() string { return buf.String() } -// GenCreateTableSQL generated create table SQL -func (statement *Statement) GenCreateTableSQL() []string { - statement.RefTable.StoreEngine = statement.StoreEngine - statement.RefTable.Charset = statement.Charset - s, _ := statement.dialect.CreateTableSQL(statement.RefTable, statement.TableName()) - return s -} - // GenIndexSQL generated create index SQL func (statement *Statement) GenIndexSQL() []string { var sqls []string diff --git a/internal/utils/name.go b/internal/utils/name.go index 840dd9e8..aeef683d 100644 --- a/internal/utils/name.go +++ b/internal/utils/name.go @@ -6,9 +6,15 @@ package utils import ( "fmt" + "strings" ) // IndexName returns index name func IndexName(tableName, idxName string) string { return fmt.Sprintf("IDX_%v_%v", tableName, idxName) } + +// SeqName returns sequence name for some table +func SeqName(tableName string) string { + return "SEQ_" + strings.ToUpper(tableName) +} diff --git a/internal/utils/slice.go b/internal/utils/slice.go index 89685706..06a1a006 100644 --- a/internal/utils/slice.go +++ b/internal/utils/slice.go @@ -11,8 +11,8 @@ func SliceEq(left, right []string) bool { if len(left) != len(right) { return false } - sort.Sort(sort.StringSlice(left)) - sort.Sort(sort.StringSlice(right)) + sort.Strings(left) + sort.Strings(right) for i := 0; i < len(left); i++ { if left[i] != right[i] { return false @@ -20,3 +20,12 @@ func SliceEq(left, right []string) bool { } return true } + +func IndexSlice(s []string, c string) int { + for i, ss := range s { + if c == ss { + return i + } + } + return -1 +} diff --git a/scan.go b/scan.go index 56d3c9d6..2788453e 100644 --- a/scan.go +++ b/scan.go @@ -136,7 +136,10 @@ func (engine *Engine) row2mapStr(rows *core.Rows, types []*sql.ColumnType, field scanResults[i] = &s } - if err := rows.Scan(scanResults...); err != nil { + if err := engine.driver.Scan(&dialects.ScanContext{ + DBLocation: engine.DatabaseTZ, + UserLocation: engine.TZLocation, + }, rows, types, scanResults...); err != nil { return nil, err } diff --git a/schemas/quote_test.go b/schemas/quote_test.go index 8e351dc0..061a6ea2 100644 --- a/schemas/quote_test.go +++ b/schemas/quote_test.go @@ -176,6 +176,10 @@ func TestReplace(t *testing.T) { "UPDATE table SET `a` = ~ `a`, `b`='abc`'", "UPDATE table SET [a] = ~ [a], [b]='abc`'", }, + { + "INSERT INTO `insert_where` (`height`,`name`,`repo_id`,`width`,`index`) SELECT $1,$2,$3,$4,coalesce(MAX(`index`),0)+1 FROM `insert_where` WHERE (`repo_id`=$5)", + "INSERT INTO [insert_where] ([height],[name],[repo_id],[width],[index]) SELECT $1,$2,$3,$4,coalesce(MAX([index]),0)+1 FROM [insert_where] WHERE ([repo_id]=$5)", + }, } for _, kase := range kases { diff --git a/schemas/type.go b/schemas/type.go index cf730134..d192bac6 100644 --- a/schemas/type.go +++ b/schemas/type.go @@ -22,6 +22,7 @@ const ( MYSQL DBType = "mysql" MSSQL DBType = "mssql" ORACLE DBType = "oracle" + DAMENG DBType = "dameng" ) // SQLType represents SQL types @@ -105,12 +106,14 @@ var ( Integer = "INTEGER" BigInt = "BIGINT" UnsignedBigInt = "UNSIGNED BIGINT" + Number = "NUMBER" Enum = "ENUM" Set = "SET" Char = "CHAR" Varchar = "VARCHAR" + VARCHAR2 = "VARCHAR2" NChar = "NCHAR" NVarchar = "NVARCHAR" TinyText = "TINYTEXT" @@ -174,6 +177,7 @@ var ( Integer: NUMERIC_TYPE, BigInt: NUMERIC_TYPE, UnsignedBigInt: NUMERIC_TYPE, + Number: NUMERIC_TYPE, Enum: TEXT_TYPE, Set: TEXT_TYPE, @@ -185,6 +189,7 @@ var ( Char: TEXT_TYPE, NChar: TEXT_TYPE, Varchar: TEXT_TYPE, + VARCHAR2: TEXT_TYPE, NVarchar: TEXT_TYPE, TinyText: TEXT_TYPE, Text: TEXT_TYPE, diff --git a/session.go b/session.go index 499b7df4..a96d2fc9 100644 --- a/session.go +++ b/session.go @@ -524,6 +524,9 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec if !ok { return fmt.Errorf("cannot convert %#v as bytes", scanResult) } + if data == nil { + return nil + } return structConvert.FromDB(data) } } diff --git a/session_get.go b/session_get.go index 08172524..22b116a9 100644 --- a/session_get.go +++ b/session_get.go @@ -130,9 +130,6 @@ var ( valuerTypePlaceHolder driver.Valuer valuerType = reflect.TypeOf(&valuerTypePlaceHolder).Elem() - scannerTypePlaceHolder sql.Scanner - scannerType = reflect.TypeOf(&scannerTypePlaceHolder).Elem() - conversionTypePlaceHolder convert.Conversion conversionType = reflect.TypeOf(&conversionTypePlaceHolder).Elem() ) diff --git a/session_insert.go b/session_insert.go index a8f365c7..43a4118b 100644 --- a/session_insert.go +++ b/session_insert.go @@ -123,6 +123,12 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e } fieldValue := *ptrFieldValue if col.IsAutoIncrement && utils.IsZero(fieldValue.Interface()) { + if session.engine.dialect.Features().AutoincrMode == dialects.SequenceAutoincrMode { + if i == 0 { + colNames = append(colNames, col.Name) + } + colPlaces = append(colPlaces, utils.SeqName(tableName)+".nextval") + } continue } if col.MapType == schemas.ONLYFROMDB { @@ -277,6 +283,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { if err != nil { return 0, err } + sqlStr = session.engine.dialect.Quoter().Replace(sqlStr) handleAfterInsertProcessorFunc := func(bean interface{}) { if session.isAutoCommit { @@ -307,16 +314,49 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { // if there is auto increment column and driver don't support return it if len(table.AutoIncrement) > 0 && !session.engine.driver.Features().SupportReturnInsertedID { - var sql = sqlStr - if session.engine.dialect.URI().DBType == schemas.ORACLE { - sql = "select seq_atable.currval from dual" + var sql string + var newArgs []interface{} + var needCommit bool + var id int64 + if session.engine.dialect.URI().DBType == schemas.ORACLE || session.engine.dialect.URI().DBType == schemas.DAMENG { + if session.isAutoCommit { // if it's not in transaction + if err := session.Begin(); err != nil { + return 0, err + } + needCommit = true + } + _, err := session.exec(sqlStr, args...) + if err != nil { + return 0, err + } + i := utils.IndexSlice(colNames, table.AutoIncrement) + if i > -1 { + id, err = convert.AsInt64(args[i]) + if err != nil { + return 0, err + } + } else { + sql = fmt.Sprintf("select %s.currval from dual", utils.SeqName(tableName)) + } + } else { + sql = sqlStr + newArgs = args } - rows, err := session.queryRows(sql, args...) - if err != nil { - return 0, err + if id == 0 { + err := session.queryRow(sql, newArgs...).Scan(&id) + if err != nil { + return 0, err + } + if needCommit { + if err := session.Commit(); err != nil { + return 0, err + } + } + if id == 0 { + return 0, errors.New("insert successfully but not returned id") + } } - defer rows.Close() defer handleAfterInsertProcessorFunc(bean) @@ -331,16 +371,6 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { } } - var id int64 - if !rows.Next() { - if rows.Err() != nil { - return 0, rows.Err() - } - return 0, errors.New("insert successfully but not returned id") - } - if err := rows.Scan(&id); err != nil { - return 1, err - } aiValue, err := table.AutoIncrColumn().ValueOf(bean) if err != nil { session.engine.logger.Errorf("%v", err) @@ -628,6 +658,7 @@ func (session *Session) insertMap(columns []string, args []interface{}) (int64, if err != nil { return 0, err } + sql = session.engine.dialect.Quoter().Replace(sql) if err := session.cacheInsert(tableName); err != nil { return 0, err @@ -654,6 +685,7 @@ func (session *Session) insertMultipleMap(columns []string, argss [][]interface{ if err != nil { return 0, err } + sql = session.engine.dialect.Quoter().Replace(sql) if err := session.cacheInsert(tableName); err != nil { return 0, err diff --git a/session_schema.go b/session_schema.go index 2e64350f..73352135 100644 --- a/session_schema.go +++ b/session_schema.go @@ -6,12 +6,14 @@ package xorm import ( "bufio" + "context" "database/sql" "fmt" "io" "os" "strings" + "xorm.io/xorm/dialects" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -40,13 +42,28 @@ func (session *Session) createTable(bean interface{}) error { return err } - sqlStrs := session.statement.GenCreateTableSQL() - for _, s := range sqlStrs { - _, err := session.exec(s) + session.statement.RefTable.StoreEngine = session.statement.StoreEngine + session.statement.RefTable.Charset = session.statement.Charset + tableName := session.statement.TableName() + refTable := session.statement.RefTable + if refTable.AutoIncrement != "" && session.engine.dialect.Features().AutoincrMode == dialects.SequenceAutoincrMode { + sqlStr, err := session.engine.dialect.CreateSequenceSQL(context.Background(), session.engine.db, utils.SeqName(tableName)) if err != nil { return err } + if _, err := session.exec(sqlStr); err != nil { + return err + } } + + sqlStr, _, err := session.engine.dialect.CreateTableSQL(context.Background(), session.engine.db, refTable, tableName) + if err != nil { + return err + } + if _, err := session.exec(sqlStr); err != nil { + return err + } + return nil } @@ -141,11 +158,32 @@ func (session *Session) dropTable(beanOrTableName interface{}) error { checkIfExist = exist } - if checkIfExist { - _, err := session.exec(sqlStr) + if !checkIfExist { + return nil + } + if _, err := session.exec(sqlStr); err != nil { return err } - return nil + + if session.engine.dialect.Features().AutoincrMode == dialects.IncrAutoincrMode { + return nil + } + + var seqName = utils.SeqName(tableName) + exist, err := session.engine.dialect.IsSequenceExist(session.ctx, session.getQueryer(), seqName) + if err != nil { + return err + } + if !exist { + return nil + } + + sqlStr, err = session.engine.dialect.DropSequenceSQL(seqName) + if err != nil { + return err + } + _, err = session.exec(sqlStr) + return err } // IsTableExist if a table is exist diff --git a/session_update.go b/session_update.go index 7d91346e..4fd45a53 100644 --- a/session_update.go +++ b/session_update.go @@ -278,7 +278,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 condBeanIsStruct := false if len(condiBean) > 0 { if c, ok := condiBean[0].(map[string]interface{}); ok { - autoCond = builder.Eq(c) + var eq = make(builder.Eq) + for k, v := range c { + eq[session.engine.Quote(k)] = v + } + autoCond = builder.Eq(eq) } else { ct := reflect.TypeOf(condiBean[0]) k := ct.Kind() From d92fb412ee645ffe4a101995c163486c67184d3d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Aug 2021 15:42:34 +0800 Subject: [PATCH 018/116] Make Get and Rows.Scan accept multiple parameters (#2029) Now the below behaviours are allowed. ```Go var id int64 var name string has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) // SELECT id, name FROM user LIMIT 1 ``` ```Go rows, err := engine.Cols("name", "age").Rows(&User{Name:name}) // SELECT * FROM user defer rows.Close() for rows.Next() { var name string var age int err = rows.Scan(&name, &age) } ``` Reviewed-on: https://gitea.com/xorm/xorm/pulls/2029 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- README.md | 22 ++++++ README_CN.md | 22 ++++++ doc.go | 16 +++++ engine.go | 4 +- integrations/rows_test.go | 44 ++++++++++++ integrations/session_get_test.go | 25 +++++++ interface.go | 2 +- rows.go | 29 ++++---- session_get.go | 117 ++++++++++++++++--------------- 9 files changed, 209 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index f4bee6b6..4cb4ac13 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,11 @@ has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id) has, err := engine.SQL("select id from user").Get(&id) // SELECT id FROM user WHERE name = ? +var id int64 +var name string +has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) +// SELECT id, name FROM user LIMIT 1 + var valuesMap = make(map[string]string) has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap) // SELECT * FROM user WHERE id = ? @@ -234,7 +239,11 @@ err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean inter }) // SELECT * FROM user Limit 0, 100 // SELECT * FROM user Limit 101, 100 +``` +You can use rows which is similiar with `sql.Rows` + +```Go rows, err := engine.Rows(&User{Name:name}) // SELECT * FROM user defer rows.Close() @@ -244,6 +253,19 @@ for rows.Next() { } ``` +or + +```Go +rows, err := engine.Cols("name", "age").Rows(&User{Name:name}) +// SELECT * FROM user +defer rows.Close() +for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) +} +``` + * `Update` update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on. ```Go diff --git a/README_CN.md b/README_CN.md index 500bb1fb..c87aa079 100644 --- a/README_CN.md +++ b/README_CN.md @@ -158,6 +158,11 @@ has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id) has, err := engine.SQL("select id from user").Get(&id) // SELECT id FROM user WHERE name = ? +var id int64 +var name string +has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) +// SELECT id, name FROM user LIMIT 1 + var valuesMap = make(map[string]string) has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap) // SELECT * FROM user WHERE id = ? @@ -231,7 +236,11 @@ err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean inter }) // SELECT * FROM user Limit 0, 100 // SELECT * FROM user Limit 101, 100 +``` +Rows 的用法类似 `sql.Rows`。 + +```Go rows, err := engine.Rows(&User{Name:name}) // SELECT * FROM user defer rows.Close() @@ -241,6 +250,19 @@ for rows.Next() { } ``` +或者 + +```Go +rows, err := engine.Cols("name", "age").Rows(&User{Name:name}) +// SELECT * FROM user +defer rows.Close() +for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) +} +``` + * `Update` 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段 ```Go diff --git a/doc.go b/doc.go index d0653232..a1565806 100644 --- a/doc.go +++ b/doc.go @@ -67,6 +67,11 @@ There are 8 major ORM methods and many helpful methods to use to operate databas has, err := engine.Table("user").Where("name = ?", name).Get(&id) // SELECT id FROM user WHERE name = ? LIMIT 1 + var id int64 + var name string + has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) + // SELECT id, name FROM user LIMIT 1 + 3. Query multiple records from database var sliceOfStructs []Struct @@ -97,6 +102,17 @@ another is Rows err = rows.Scan(bean) } +or + + rows, err := engine.Cols("name", "age").Rows(...) + // SELECT * FROM user + defer rows.Close() + for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) + } + 5. Update one or more records affected, err := engine.ID(...).Update(&user) diff --git a/engine.go b/engine.go index 3681ff3c..7d72dea3 100644 --- a/engine.go +++ b/engine.go @@ -1135,10 +1135,10 @@ func (engine *Engine) Delete(beans ...interface{}) (int64, error) { // Get retrieve one record from table, bean's non-empty fields // are conditions -func (engine *Engine) Get(bean interface{}) (bool, error) { +func (engine *Engine) Get(beans ...interface{}) (bool, error) { session := engine.NewSession() defer session.Close() - return session.Get(bean) + return session.Get(beans...) } // Exist returns true if the record exist otherwise return false diff --git a/integrations/rows_test.go b/integrations/rows_test.go index f68030a4..a5648675 100644 --- a/integrations/rows_test.go +++ b/integrations/rows_test.go @@ -160,5 +160,49 @@ func TestRowsSpecTableName(t *testing.T) { assert.NoError(t, err) cnt++ } + assert.NoError(t, rows.Err()) assert.EqualValues(t, 1, cnt) } + +func TestRowsScanVars(t *testing.T) { + type RowsScanVars struct { + Id int64 + Name string + Age int + } + + assert.NoError(t, PrepareEngine()) + assert.NoError(t, testEngine.Sync2(new(RowsScanVars))) + + cnt, err := testEngine.Insert(&RowsScanVars{ + Name: "xlw", + Age: 42, + }, &RowsScanVars{ + Name: "xlw2", + Age: 24, + }) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + rows, err := testEngine.Cols("name", "age").Rows(new(RowsScanVars)) + assert.NoError(t, err) + defer rows.Close() + + cnt = 0 + for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) + assert.NoError(t, err) + if cnt == 0 { + assert.EqualValues(t, "xlw", name) + assert.EqualValues(t, 42, age) + } else if cnt == 1 { + assert.EqualValues(t, "xlw2", name) + assert.EqualValues(t, 24, age) + } + cnt++ + } + assert.NoError(t, rows.Err()) + assert.EqualValues(t, 2, cnt) +} diff --git a/integrations/session_get_test.go b/integrations/session_get_test.go index 4fc30adb..6f4c1dbe 100644 --- a/integrations/session_get_test.go +++ b/integrations/session_get_test.go @@ -890,3 +890,28 @@ func TestGetTime(t *testing.T) { assert.True(t, has) assert.EqualValues(t, gts.CreateTime.Format(time.RFC3339), gn.Format(time.RFC3339)) } + +func TestGetVars(t *testing.T) { + type GetVars struct { + Id int64 + Name string + Age int + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(GetVars)) + + _, err := testEngine.Insert(&GetVars{ + Name: "xlw", + Age: 42, + }) + assert.NoError(t, err) + + var name string + var age int + has, err := testEngine.Table(new(GetVars)).Cols("name", "age").Get(&name, &age) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "xlw", name) + assert.EqualValues(t, 42, age) +} diff --git a/interface.go b/interface.go index 5d68f536..42dc9a0a 100644 --- a/interface.go +++ b/interface.go @@ -37,7 +37,7 @@ type Interface interface { Exist(bean ...interface{}) (bool, error) Find(interface{}, ...interface{}) error FindAndCount(interface{}, ...interface{}) (int64, error) - Get(interface{}) (bool, error) + Get(...interface{}) (bool, error) GroupBy(keys string) *Session ID(interface{}) *Session In(string, ...interface{}) *Session diff --git a/rows.go b/rows.go index 8e7cc075..76fc1e90 100644 --- a/rows.go +++ b/rows.go @@ -11,7 +11,6 @@ import ( "xorm.io/builder" "xorm.io/xorm/core" - "xorm.io/xorm/internal/utils" ) // Rows rows wrapper a rows to @@ -93,17 +92,26 @@ func (rows *Rows) Err() error { } // Scan row record to bean properties -func (rows *Rows) Scan(bean interface{}) error { +func (rows *Rows) Scan(beans ...interface{}) error { if rows.Err() != nil { return rows.Err() } - if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { - return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) + var bean = beans[0] + var tp = reflect.TypeOf(bean) + if tp.Kind() == reflect.Ptr { + tp = tp.Elem() } + var beanKind = tp.Kind() - if err := rows.session.statement.SetRefBean(bean); err != nil { - return err + if len(beans) == 1 { + if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { + return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) + } + + if err := rows.session.statement.SetRefBean(bean); err != nil { + return err + } } fields, err := rows.rows.Columns() @@ -115,14 +123,7 @@ func (rows *Rows) Scan(bean interface{}) error { return err } - scanResults, err := rows.session.row2Slice(rows.rows, fields, types, bean) - if err != nil { - return err - } - - dataStruct := utils.ReflectValue(bean) - _, err = rows.session.slice2Bean(scanResults, fields, bean, &dataStruct, rows.session.statement.RefTable) - if err != nil { + if err := rows.session.scan(rows.rows, rows.session.statement.RefTable, beanKind, beans, types, fields); err != nil { return err } diff --git a/session_get.go b/session_get.go index 22b116a9..a82cae92 100644 --- a/session_get.go +++ b/session_get.go @@ -28,11 +28,11 @@ var ( // Get retrieve one record from database, bean's non-empty fields // will be as conditions -func (session *Session) Get(bean interface{}) (bool, error) { +func (session *Session) Get(beans ...interface{}) (bool, error) { if session.isAutoClose { defer session.Close() } - return session.get(bean) + return session.get(beans...) } func isPtrOfTime(v interface{}) bool { @@ -48,14 +48,17 @@ func isPtrOfTime(v interface{}) bool { return el.Type().ConvertibleTo(schemas.TimeType) } -func (session *Session) get(bean interface{}) (bool, error) { +func (session *Session) get(beans ...interface{}) (bool, error) { defer session.resetStatement() if session.statement.LastError != nil { return false, session.statement.LastError } + if len(beans) == 0 { + return false, errors.New("needs at least one parameter for get") + } - beanValue := reflect.ValueOf(bean) + beanValue := reflect.ValueOf(beans[0]) if beanValue.Kind() != reflect.Ptr { return false, errors.New("needs a pointer to a value") } else if beanValue.Elem().Kind() == reflect.Ptr { @@ -64,8 +67,9 @@ func (session *Session) get(bean interface{}) (bool, error) { return false, ErrObjectIsNil } - if beanValue.Elem().Kind() == reflect.Struct && !isPtrOfTime(bean) { - if err := session.statement.SetRefBean(bean); err != nil { + var isStruct = beanValue.Elem().Kind() == reflect.Struct && !isPtrOfTime(beans[0]) + if isStruct { + if err := session.statement.SetRefBean(beans[0]); err != nil { return false, err } } @@ -79,7 +83,7 @@ func (session *Session) get(bean interface{}) (bool, error) { return false, ErrTableNotFound } session.statement.Limit(1) - sqlStr, args, err = session.statement.GenGetSQL(bean) + sqlStr, args, err = session.statement.GenGetSQL(beans[0]) if err != nil { return false, err } @@ -90,10 +94,10 @@ func (session *Session) get(bean interface{}) (bool, error) { table := session.statement.RefTable - if session.statement.ColumnMap.IsEmpty() && session.canCache() && beanValue.Elem().Kind() == reflect.Struct { + if session.statement.ColumnMap.IsEmpty() && session.canCache() && isStruct { if cacher := session.engine.GetCacher(session.statement.TableName()); cacher != nil && !session.statement.GetUnscoped() { - has, err := session.cacheGet(bean, sqlStr, args...) + has, err := session.cacheGet(beans[0], sqlStr, args...) if err != ErrCacheFailed { return has, err } @@ -101,12 +105,12 @@ func (session *Session) get(bean interface{}) (bool, error) { } context := session.statement.Context - if context != nil { + if context != nil && isStruct { res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args)) if res != nil { session.engine.logger.Debugf("hit context cache: %s", sqlStr) - structValue := reflect.Indirect(reflect.ValueOf(bean)) + structValue := reflect.Indirect(reflect.ValueOf(beans[0])) structValue.Set(reflect.Indirect(reflect.ValueOf(res))) session.lastSQL = "" session.lastSQLArgs = nil @@ -114,13 +118,13 @@ func (session *Session) get(bean interface{}) (bool, error) { } } - has, err := session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...) + has, err := session.nocacheGet(beanValue.Elem().Kind(), table, beans, sqlStr, args...) if err != nil || !has { return has, err } - if context != nil { - context.Put(fmt.Sprintf("%v-%v", sqlStr, args), bean) + if context != nil && isStruct { + context.Put(fmt.Sprintf("%v-%v", sqlStr, args), beans[0]) } return true, nil @@ -148,7 +152,7 @@ func isScannableStruct(bean interface{}, typeLen int) bool { return true } -func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, bean interface{}, sqlStr string, args ...interface{}) (bool, error) { +func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, beans []interface{}, sqlStr string, args ...interface{}) (bool, error) { rows, err := session.queryRows(sqlStr, args...) if err != nil { return false, err @@ -168,27 +172,39 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, if err != nil { return true, err } - switch beanKind { - case reflect.Struct: - if !isScannableStruct(bean, len(types)) { - break - } - return session.getStruct(rows, types, fields, table, bean) - case reflect.Slice: - return session.getSlice(rows, types, fields, bean) - case reflect.Map: - return session.getMap(rows, types, fields, bean) - } - return session.getVars(rows, types, fields, bean) + return true, session.scan(rows, table, beanKind, beans, types, fields) } -func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) (bool, error) { +func (session *Session) scan(rows *core.Rows, table *schemas.Table, firstBeanKind reflect.Kind, beans []interface{}, types []*sql.ColumnType, fields []string) error { + if len(beans) == 1 { + bean := beans[0] + switch firstBeanKind { + case reflect.Struct: + if !isScannableStruct(bean, len(types)) { + break + } + return session.getStruct(rows, types, fields, table, bean) + case reflect.Slice: + return session.getSlice(rows, types, fields, bean) + case reflect.Map: + return session.getMap(rows, types, fields, bean) + } + } + + if len(beans) != len(types) { + return fmt.Errorf("expected columns %d, but only %d variables", len(types), len(beans)) + } + + return session.engine.scan(rows, fields, types, beans...) +} + +func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) error { switch t := bean.(type) { case *[]string: res, err := session.engine.scanStringInterface(rows, fields, types) if err != nil { - return true, err + return err } var needAppend = len(*t) == 0 // both support slice is empty or has been initlized @@ -199,17 +215,17 @@ func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, field (*t)[i] = r.(*sql.NullString).String } } - return true, nil + return nil case *[]interface{}: scanResults, err := session.engine.scanInterfaces(rows, fields, types) if err != nil { - return true, err + return err } var needAppend = len(*t) == 0 for ii := range fields { s, err := convert.Interface2Interface(session.engine.DatabaseTZ, scanResults[ii]) if err != nil { - return true, err + return err } if needAppend { *t = append(*t, s) @@ -217,54 +233,45 @@ func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, field (*t)[ii] = s } } - return true, nil + return nil default: - return true, fmt.Errorf("unspoorted slice type: %t", t) + return fmt.Errorf("unspoorted slice type: %t", t) } } -func (session *Session) getMap(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) (bool, error) { +func (session *Session) getMap(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) error { switch t := bean.(type) { case *map[string]string: scanResults, err := session.engine.scanStringInterface(rows, fields, types) if err != nil { - return true, err + return err } for ii, key := range fields { (*t)[key] = scanResults[ii].(*sql.NullString).String } - return true, nil + return nil case *map[string]interface{}: scanResults, err := session.engine.scanInterfaces(rows, fields, types) if err != nil { - return true, err + return err } for ii, key := range fields { s, err := convert.Interface2Interface(session.engine.DatabaseTZ, scanResults[ii]) if err != nil { - return true, err + return err } (*t)[key] = s } - return true, nil + return nil default: - return true, fmt.Errorf("unspoorted map type: %t", t) + return fmt.Errorf("unspoorted map type: %t", t) } } -func (session *Session) getVars(rows *core.Rows, types []*sql.ColumnType, fields []string, beans ...interface{}) (bool, error) { - if len(beans) != len(types) { - return false, fmt.Errorf("expected columns %d, but only %d variables", len(types), len(beans)) - } - - err := session.engine.scan(rows, fields, types, beans...) - return true, err -} - -func (session *Session) getStruct(rows *core.Rows, types []*sql.ColumnType, fields []string, table *schemas.Table, bean interface{}) (bool, error) { +func (session *Session) getStruct(rows *core.Rows, types []*sql.ColumnType, fields []string, table *schemas.Table, bean interface{}) error { scanResults, err := session.row2Slice(rows, fields, types, bean) if err != nil { - return false, err + return err } // close it before convert data rows.Close() @@ -272,10 +279,10 @@ func (session *Session) getStruct(rows *core.Rows, types []*sql.ColumnType, fiel dataStruct := utils.ReflectValue(bean) _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table) if err != nil { - return true, err + return err } - return true, session.executeProcessors() + return session.executeProcessors() } func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) { @@ -354,7 +361,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf cacheBean := cacher.GetBean(tableName, sid) if cacheBean == nil { cacheBean = bean - has, err = session.nocacheGet(reflect.Struct, table, cacheBean, sqlStr, args...) + has, err = session.nocacheGet(reflect.Struct, table, []interface{}{cacheBean}, sqlStr, args...) if err != nil || !has { return has, err } From 0a429a241d245b442a6a6ddc1fc2817aff8e8d0c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Aug 2021 17:13:17 +0800 Subject: [PATCH 019/116] Drop sync function and rename sync2 to sync (#2018) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2018 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- README.md | 2 +- README_CN.md | 2 +- engine.go | 98 +--------------------------- integrations/cache_test.go | 6 +- integrations/engine_test.go | 4 +- integrations/performance_test.go | 6 +- integrations/processors_test.go | 4 +- integrations/rows_test.go | 6 +- integrations/session_cols_test.go | 4 +- integrations/session_cond_test.go | 4 +- integrations/session_count_test.go | 2 +- integrations/session_delete_test.go | 4 +- integrations/session_exist_test.go | 2 +- integrations/session_find_test.go | 6 +- integrations/session_get_test.go | 8 +-- integrations/session_insert_test.go | 40 ++++++------ integrations/session_iterate_test.go | 4 +- integrations/session_pk_test.go | 2 +- integrations/session_query_test.go | 20 +++--- integrations/session_raw_test.go | 4 +- integrations/session_schema_test.go | 38 +++++------ integrations/session_sum_test.go | 6 +- integrations/session_test.go | 2 +- integrations/session_update_test.go | 16 ++--- integrations/tags_test.go | 8 +-- integrations/types_test.go | 6 +- migrate/migrate_test.go | 8 +-- session_schema.go | 6 ++ 28 files changed, 116 insertions(+), 202 deletions(-) diff --git a/README.md b/README.md index 4cb4ac13..8c85938a 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ type User struct { Updated time.Time `xorm:"updated"` } -err := engine.Sync2(new(User)) +err := engine.Sync(new(User)) ``` * Create Engine Group diff --git a/README_CN.md b/README_CN.md index c87aa079..f2445019 100644 --- a/README_CN.md +++ b/README_CN.md @@ -84,7 +84,7 @@ type User struct { Updated time.Time `xorm:"updated"` } -err := engine.Sync2(new(User)) +err := engine.Sync(new(User)) ``` * 创建Engine组 diff --git a/engine.go b/engine.go index 7d72dea3..7a57b08a 100644 --- a/engine.go +++ b/engine.go @@ -7,7 +7,6 @@ package xorm import ( "context" "database/sql" - "errors" "fmt" "io" "os" @@ -925,104 +924,13 @@ func (engine *Engine) UnMapType(t reflect.Type) { func (engine *Engine) Sync(beans ...interface{}) error { session := engine.NewSession() defer session.Close() - - for _, bean := range beans { - v := utils.ReflectValue(bean) - tableNameNoSchema := dialects.FullTableName(engine.dialect, engine.GetTableMapper(), bean) - table, err := engine.tagParser.ParseWithCache(v) - if err != nil { - return err - } - - isExist, err := session.Table(bean).isTableExist(tableNameNoSchema) - if err != nil { - return err - } - if !isExist { - err = session.createTable(bean) - if err != nil { - return err - } - } - /*isEmpty, err := engine.IsEmptyTable(bean) - if err != nil { - return err - }*/ - var isEmpty bool - if isEmpty { - err = session.dropTable(bean) - if err != nil { - return err - } - err = session.createTable(bean) - if err != nil { - return err - } - } else { - for _, col := range table.Columns() { - isExist, err := engine.dialect.IsColumnExist(engine.db, session.ctx, tableNameNoSchema, col.Name) - if err != nil { - return err - } - if !isExist { - if err := session.statement.SetRefBean(bean); err != nil { - return err - } - err = session.addColumn(col.Name) - if err != nil { - return err - } - } - } - - for name, index := range table.Indexes { - if err := session.statement.SetRefBean(bean); err != nil { - return err - } - if index.Type == schemas.UniqueType { - isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, true) - if err != nil { - return err - } - if !isExist { - if err := session.statement.SetRefBean(bean); err != nil { - return err - } - - err = session.addUnique(tableNameNoSchema, name) - if err != nil { - return err - } - } - } else if index.Type == schemas.IndexType { - isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, false) - if err != nil { - return err - } - if !isExist { - if err := session.statement.SetRefBean(bean); err != nil { - return err - } - - err = session.addIndex(tableNameNoSchema, name) - if err != nil { - return err - } - } - } else { - return errors.New("unknow index type") - } - } - } - } - return nil + return session.Sync(beans...) } // Sync2 synchronize structs to database tables +// Depricated func (engine *Engine) Sync2(beans ...interface{}) error { - s := engine.NewSession() - defer s.Close() - return s.Sync2(beans...) + return engine.Sync(beans...) } // CreateTables create tabls according bean diff --git a/integrations/cache_test.go b/integrations/cache_test.go index 6fdaa5cb..2caeaa34 100644 --- a/integrations/cache_test.go +++ b/integrations/cache_test.go @@ -26,7 +26,7 @@ func TestCacheFind(t *testing.T) { cacher := caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) - assert.NoError(t, testEngine.Sync2(new(MailBox))) + assert.NoError(t, testEngine.Sync(new(MailBox))) var inserts = []*MailBox{ { @@ -103,7 +103,7 @@ func TestCacheFind2(t *testing.T) { cacher := caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) - assert.NoError(t, testEngine.Sync2(new(MailBox2))) + assert.NoError(t, testEngine.Sync(new(MailBox2))) var inserts = []*MailBox2{ { @@ -154,7 +154,7 @@ func TestCacheGet(t *testing.T) { cacher := caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) - assert.NoError(t, testEngine.Sync2(new(MailBox3))) + assert.NoError(t, testEngine.Sync(new(MailBox3))) var inserts = []*MailBox3{ { diff --git a/integrations/engine_test.go b/integrations/engine_test.go index f45d6859..74e1f903 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -53,7 +53,7 @@ func TestAutoTransaction(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestTx))) + assert.NoError(t, testEngine.Sync(new(TestTx))) engine := testEngine.(*xorm.Engine) @@ -87,7 +87,7 @@ func assertSync(t *testing.T, beans ...interface{}) { for _, bean := range beans { t.Run(testEngine.TableName(bean, true), func(t *testing.T) { assert.NoError(t, testEngine.DropTables(bean)) - assert.NoError(t, testEngine.Sync2(bean)) + assert.NoError(t, testEngine.Sync(bean)) }) } } diff --git a/integrations/performance_test.go b/integrations/performance_test.go index 241dc7b9..4646afa2 100644 --- a/integrations/performance_test.go +++ b/integrations/performance_test.go @@ -21,7 +21,7 @@ func BenchmarkGetVars(b *testing.B) { Name string } - assert.NoError(b, testEngine.Sync2(new(BenchmarkGetVars))) + assert.NoError(b, testEngine.Sync(new(BenchmarkGetVars))) var v = BenchmarkGetVars{ Name: "myname", @@ -52,7 +52,7 @@ func BenchmarkGetStruct(b *testing.B) { Name string } - assert.NoError(b, testEngine.Sync2(new(BenchmarkGetStruct))) + assert.NoError(b, testEngine.Sync(new(BenchmarkGetStruct))) var v = BenchmarkGetStruct{ Name: "myname", @@ -84,7 +84,7 @@ func BenchmarkFindStruct(b *testing.B) { Name string } - assert.NoError(b, testEngine.Sync2(new(BenchmarkFindStruct))) + assert.NoError(b, testEngine.Sync(new(BenchmarkFindStruct))) var v = BenchmarkFindStruct{ Name: "myname", diff --git a/integrations/processors_test.go b/integrations/processors_test.go index e349988d..b32f6fbb 100644 --- a/integrations/processors_test.go +++ b/integrations/processors_test.go @@ -23,7 +23,7 @@ func TestBefore_Get(t *testing.T) { Val string `xorm:"-"` } - assert.NoError(t, testEngine.Sync2(new(BeforeTable))) + assert.NoError(t, testEngine.Sync(new(BeforeTable))) cnt, err := testEngine.Insert(&BeforeTable{ Name: "test", @@ -50,7 +50,7 @@ func TestBefore_Find(t *testing.T) { Val string `xorm:"-"` } - assert.NoError(t, testEngine.Sync2(new(BeforeTable2))) + assert.NoError(t, testEngine.Sync(new(BeforeTable2))) cnt, err := testEngine.Insert([]BeforeTable2{ {Name: "test1"}, diff --git a/integrations/rows_test.go b/integrations/rows_test.go index a5648675..10f11453 100644 --- a/integrations/rows_test.go +++ b/integrations/rows_test.go @@ -18,7 +18,7 @@ func TestRows(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserRows))) + assert.NoError(t, testEngine.Sync(new(UserRows))) cnt, err := testEngine.Insert(&UserRows{ IsMan: true, @@ -94,7 +94,7 @@ func TestRowsMyTableName(t *testing.T) { var tableName = "user_rows_my_table_name" - assert.NoError(t, testEngine.Table(tableName).Sync2(new(UserRowsMyTable))) + assert.NoError(t, testEngine.Table(tableName).Sync(new(UserRowsMyTable))) cnt, err := testEngine.Table(tableName).Insert(&UserRowsMyTable{ IsMan: true, @@ -141,7 +141,7 @@ func (UserRowsSpecTable) TableName() string { func TestRowsSpecTableName(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(UserRowsSpecTable))) + assert.NoError(t, testEngine.Sync(new(UserRowsSpecTable))) cnt, err := testEngine.Insert(&UserRowsSpecTable{ IsMan: true, diff --git a/integrations/session_cols_test.go b/integrations/session_cols_test.go index 18ba4001..462ea7c7 100644 --- a/integrations/session_cols_test.go +++ b/integrations/session_cols_test.go @@ -20,7 +20,7 @@ func TestSetExpr(t *testing.T) { Title string } - assert.NoError(t, testEngine.Sync2(new(UserExprIssue))) + assert.NoError(t, testEngine.Sync(new(UserExprIssue))) var issue = UserExprIssue{ Title: "my issue", @@ -36,7 +36,7 @@ func TestSetExpr(t *testing.T) { Show bool } - assert.NoError(t, testEngine.Sync2(new(UserExpr))) + assert.NoError(t, testEngine.Sync(new(UserExpr))) cnt, err = testEngine.Insert(&UserExpr{ Show: true, diff --git a/integrations/session_cond_test.go b/integrations/session_cond_test.go index 72a4caf5..0597d74e 100644 --- a/integrations/session_cond_test.go +++ b/integrations/session_cond_test.go @@ -104,7 +104,7 @@ func TestBuilder(t *testing.T) { func TestIn(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(Userinfo))) + assert.NoError(t, testEngine.Sync(new(Userinfo))) cnt, err := testEngine.Insert([]Userinfo{ { @@ -203,7 +203,7 @@ func TestFindAndCount(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(FindAndCount))) + assert.NoError(t, testEngine.Sync(new(FindAndCount))) _, err := testEngine.Insert([]FindAndCount{ { diff --git a/integrations/session_count_test.go b/integrations/session_count_test.go index e49b6045..13d84edb 100644 --- a/integrations/session_count_test.go +++ b/integrations/session_count_test.go @@ -17,7 +17,7 @@ func TestCount(t *testing.T) { type UserinfoCount struct { Departname string } - assert.NoError(t, testEngine.Sync2(new(UserinfoCount))) + assert.NoError(t, testEngine.Sync(new(UserinfoCount))) colName := testEngine.GetColumnMapper().Obj2Table("Departname") var cond builder.Cond = builder.Eq{ diff --git a/integrations/session_delete_test.go b/integrations/session_delete_test.go index 56f6f5b8..e8761acf 100644 --- a/integrations/session_delete_test.go +++ b/integrations/session_delete_test.go @@ -22,7 +22,7 @@ func TestDelete(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserinfoDelete))) + assert.NoError(t, testEngine.Sync(new(UserinfoDelete))) session := testEngine.NewSession() defer session.Close() @@ -250,7 +250,7 @@ func TestDelete2(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserinfoDelete2))) + assert.NoError(t, testEngine.Sync(new(UserinfoDelete2))) user := UserinfoDelete2{} cnt, err := testEngine.Insert(&user) diff --git a/integrations/session_exist_test.go b/integrations/session_exist_test.go index a0f65211..ca1e66ad 100644 --- a/integrations/session_exist_test.go +++ b/integrations/session_exist_test.go @@ -99,7 +99,7 @@ func TestExistStructForJoin(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(Number), new(OrderList), new(Player))) + assert.NoError(t, testEngine.Sync(new(Number), new(OrderList), new(Player))) var ply Player cnt, err := testEngine.Insert(&ply) diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index 9b503f25..f7af45fa 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -33,7 +33,7 @@ func TestJoinLimit(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(Salary), new(CheckList), new(Empsetting))) + assert.NoError(t, testEngine.Sync(new(Salary), new(CheckList), new(Empsetting))) var emp Empsetting cnt, err := testEngine.Insert(&emp) @@ -121,7 +121,7 @@ func (TeamUser) TableName() string { func TestFind3(t *testing.T) { var teamUser = new(TeamUser) assert.NoError(t, PrepareEngine()) - err := testEngine.Sync2(new(Team), teamUser) + err := testEngine.Sync(new(Team), teamUser) assert.NoError(t, err) var teams []Team @@ -690,7 +690,7 @@ func TestFindAndCountWithGroupBy(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(FindAndCountWithGroupBy))) + assert.NoError(t, testEngine.Sync(new(FindAndCountWithGroupBy))) _, err := testEngine.Insert([]FindAndCountWithGroupBy{ { diff --git a/integrations/session_get_test.go b/integrations/session_get_test.go index 6f4c1dbe..601d4a26 100644 --- a/integrations/session_get_test.go +++ b/integrations/session_get_test.go @@ -33,7 +33,7 @@ func TestGetVar(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(GetVar))) + assert.NoError(t, testEngine.Sync(new(GetVar))) var data = GetVar{ Msg: "hi", @@ -235,7 +235,7 @@ func TestGetStruct(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserinfoGet))) + assert.NoError(t, testEngine.Sync(new(UserinfoGet))) session := testEngine.NewSession() defer session.Close() @@ -266,7 +266,7 @@ func TestGetStruct(t *testing.T) { Total int64 } - assert.NoError(t, testEngine.Sync2(&NoIdUser{})) + assert.NoError(t, testEngine.Sync(&NoIdUser{})) userCol := testEngine.GetColumnMapper().Obj2Table("User") _, err = testEngine.Where("`"+userCol+"` = ?", "xlw").Delete(&NoIdUser{}) @@ -576,7 +576,7 @@ func (MyGetCustomTableImpletation) TableName() string { func TestGetCustomTableInterface(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Table(getCustomTableName).Sync2(new(MyGetCustomTableImpletation))) + assert.NoError(t, testEngine.Table(getCustomTableName).Sync(new(MyGetCustomTableImpletation))) exist, err := testEngine.IsTableExist(getCustomTableName) assert.NoError(t, err) diff --git a/integrations/session_insert_test.go b/integrations/session_insert_test.go index 70ec13f3..fb0ee2d1 100644 --- a/integrations/session_insert_test.go +++ b/integrations/session_insert_test.go @@ -25,7 +25,7 @@ func TestInsertOne(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(Test))) + assert.NoError(t, testEngine.Sync(new(Test))) data := Test{Msg: "hi"} _, err := testEngine.InsertOne(data) @@ -39,7 +39,7 @@ func TestInsertMulti(t *testing.T) { Name string `xorm:"varchar(255)"` } - assert.NoError(t, testEngine.Sync2(new(TestMulti))) + assert.NoError(t, testEngine.Sync(new(TestMulti))) num, err := insertMultiDatas(1, append([]TestMulti{}, TestMulti{1, "test1"}, TestMulti{2, "test2"}, TestMulti{3, "test3"})) @@ -115,7 +115,7 @@ func TestInsertOneIfPkIsPoint(t *testing.T) { Created *time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestPoint))) + assert.NoError(t, testEngine.Sync(new(TestPoint))) msg := "hi" data := TestPoint{Msg: &msg} _, err := testEngine.InsertOne(&data) @@ -131,7 +131,7 @@ func TestInsertOneIfPkIsPointRename(t *testing.T) { Created *time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestPoint2))) + assert.NoError(t, testEngine.Sync(new(TestPoint2))) msg := "hi" data := TestPoint2{Msg: &msg} _, err := testEngine.InsertOne(&data) @@ -181,7 +181,7 @@ func TestInsertDefault(t *testing.T) { } di := new(DefaultInsert) - err := testEngine.Sync2(di) + err := testEngine.Sync(di) assert.NoError(t, err) var di2 = DefaultInsert{Name: "test"} @@ -207,7 +207,7 @@ func TestInsertDefault2(t *testing.T) { } di := new(DefaultInsert2) - err := testEngine.Sync2(di) + err := testEngine.Sync(di) assert.NoError(t, err) var di2 = DefaultInsert2{Name: "test"} @@ -258,7 +258,7 @@ func TestInsertCreated(t *testing.T) { assert.NoError(t, PrepareEngine()) di := new(CreatedInsert) - err := testEngine.Sync2(di) + err := testEngine.Sync(di) assert.NoError(t, err) ci := &CreatedInsert{} @@ -271,7 +271,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci.Created.Unix(), di.Created.Unix()) di2 := new(CreatedInsert2) - err = testEngine.Sync2(di2) + err = testEngine.Sync(di2) assert.NoError(t, err) ci2 := &CreatedInsert2{} @@ -284,7 +284,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci2.Created, di2.Created) di3 := new(CreatedInsert3) - err = testEngine.Sync2(di3) + err = testEngine.Sync(di3) assert.NoError(t, err) ci3 := &CreatedInsert3{} @@ -297,7 +297,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci3.Created, di3.Created) di4 := new(CreatedInsert4) - err = testEngine.Sync2(di4) + err = testEngine.Sync(di4) assert.NoError(t, err) ci4 := &CreatedInsert4{} @@ -310,7 +310,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci4.Created, di4.Created) di5 := new(CreatedInsert5) - err = testEngine.Sync2(di5) + err = testEngine.Sync(di5) assert.NoError(t, err) ci5 := &CreatedInsert5{} @@ -323,7 +323,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci5.Created.Unix(), di5.Created.Unix()) di6 := new(CreatedInsert6) - err = testEngine.Sync2(di6) + err = testEngine.Sync(di6) assert.NoError(t, err) oldTime := time.Now().Add(-time.Hour) @@ -390,7 +390,7 @@ func TestCreatedJsonTime(t *testing.T) { assert.NoError(t, PrepareEngine()) di5 := new(MyJSONTime) - err := testEngine.Sync2(di5) + err := testEngine.Sync(di5) assert.NoError(t, err) ci5 := &MyJSONTime{} @@ -489,7 +489,7 @@ func TestInsertCreatedInt64(t *testing.T) { Created int64 `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestCreatedInt64))) + assert.NoError(t, testEngine.Sync(new(TestCreatedInt64))) data := TestCreatedInt64{Msg: "hi"} now := time.Now() @@ -896,7 +896,7 @@ func TestMultipleInsertTableName(t *testing.T) { assert.NoError(t, PrepareEngine()) tableName := `prd_nightly_rate_16` - assert.NoError(t, testEngine.Table(tableName).Sync2(new(NightlyRate))) + assert.NoError(t, testEngine.Table(tableName).Sync(new(NightlyRate))) trans := testEngine.NewSession() defer trans.Close() @@ -932,7 +932,7 @@ func TestInsertMultiWithOmit(t *testing.T) { Omitted string `xorm:"varchar(255) 'omitted'"` } - assert.NoError(t, testEngine.Sync2(new(TestMultiOmit))) + assert.NoError(t, testEngine.Sync(new(TestMultiOmit))) l := []interface{}{ TestMultiOmit{Id: 1, Name: "1", Omitted: "1"}, @@ -977,7 +977,7 @@ func TestInsertTwice(t *testing.T) { FieldB int } - assert.NoError(t, testEngine.Sync2(new(InsertStructA), new(InsertStructB))) + assert.NoError(t, testEngine.Sync(new(InsertStructA), new(InsertStructB))) var sliceA []InsertStructA // sliceA is empty sliceB := []InsertStructB{ @@ -1008,7 +1008,7 @@ func TestInsertIntSlice(t *testing.T) { NameIDs []int `xorm:"json notnull"` } - assert.NoError(t, testEngine.Sync2(new(InsertIntSlice))) + assert.NoError(t, testEngine.Sync(new(InsertIntSlice))) var v = InsertIntSlice{ NameIDs: []int{1, 2}, @@ -1049,7 +1049,7 @@ func TestInsertDeleted(t *testing.T) { DeletedAt time.Time `xorm:"'DELETED_AT' deleted notnull"` } // notnull tag will be ignored - err := testEngine.Sync2(new(InsertDeletedStructNotRight)) + err := testEngine.Sync(new(InsertDeletedStructNotRight)) assert.NoError(t, err) type InsertDeletedStruct struct { @@ -1057,7 +1057,7 @@ func TestInsertDeleted(t *testing.T) { DeletedAt time.Time `xorm:"'DELETED_AT' deleted"` } - assert.NoError(t, testEngine.Sync2(new(InsertDeletedStruct))) + assert.NoError(t, testEngine.Sync(new(InsertDeletedStruct))) var v InsertDeletedStruct _, err = testEngine.Insert(&v) diff --git a/integrations/session_iterate_test.go b/integrations/session_iterate_test.go index 95f5a282..fa394d17 100644 --- a/integrations/session_iterate_test.go +++ b/integrations/session_iterate_test.go @@ -18,7 +18,7 @@ func TestIterate(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserIterate))) + assert.NoError(t, testEngine.Sync(new(UserIterate))) cnt, err := testEngine.Insert(&UserIterate{ IsMan: true, @@ -46,7 +46,7 @@ func TestBufferIterate(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserBufferIterate))) + assert.NoError(t, testEngine.Sync(new(UserBufferIterate))) var size = 20 for i := 0; i < size; i++ { diff --git a/integrations/session_pk_test.go b/integrations/session_pk_test.go index 8f7dcb55..e1aa8ed4 100644 --- a/integrations/session_pk_test.go +++ b/integrations/session_pk_test.go @@ -607,7 +607,7 @@ func TestCompositePK(t *testing.T) { assert.NoError(t, err) assertSync(t, new(TaskSolution)) - assert.NoError(t, testEngine.Sync2(new(TaskSolution))) + assert.NoError(t, testEngine.Sync(new(TaskSolution))) tables2, err := testEngine.DBMetas() assert.NoError(t, err) diff --git a/integrations/session_query_test.go b/integrations/session_query_test.go index edc77aec..b72f7ef2 100644 --- a/integrations/session_query_test.go +++ b/integrations/session_query_test.go @@ -26,7 +26,7 @@ func TestQueryString(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(GetVar2))) + assert.NoError(t, testEngine.Sync(new(GetVar2))) var data = GetVar2{ Msg: "hi", @@ -54,7 +54,7 @@ func TestQueryString2(t *testing.T) { Msg bool } - assert.NoError(t, testEngine.Sync2(new(GetVar3))) + assert.NoError(t, testEngine.Sync(new(GetVar3))) var data = GetVar3{ Msg: false, @@ -91,7 +91,7 @@ func TestQueryInterface(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(GetVarInterface))) + assert.NoError(t, testEngine.Sync(new(GetVarInterface))) var data = GetVarInterface{ Msg: "hi", @@ -124,7 +124,7 @@ func TestQueryNoParams(t *testing.T) { testEngine.ShowSQL(true) - assert.NoError(t, testEngine.Sync2(new(QueryNoParams))) + assert.NoError(t, testEngine.Sync(new(QueryNoParams))) var q = QueryNoParams{ Msg: "message", @@ -168,7 +168,7 @@ func TestQueryStringNoParam(t *testing.T) { Msg bool } - assert.NoError(t, testEngine.Sync2(new(GetVar4))) + assert.NoError(t, testEngine.Sync(new(GetVar4))) var data = GetVar4{ Msg: false, @@ -205,7 +205,7 @@ func TestQuerySliceStringNoParam(t *testing.T) { Msg bool } - assert.NoError(t, testEngine.Sync2(new(GetVar6))) + assert.NoError(t, testEngine.Sync(new(GetVar6))) var data = GetVar6{ Msg: false, @@ -242,7 +242,7 @@ func TestQueryInterfaceNoParam(t *testing.T) { Msg bool } - assert.NoError(t, testEngine.Sync2(new(GetVar5))) + assert.NoError(t, testEngine.Sync(new(GetVar5))) var data = GetVar5{ Msg: false, @@ -276,7 +276,7 @@ func TestQueryWithBuilder(t *testing.T) { testEngine.ShowSQL(true) - assert.NoError(t, testEngine.Sync2(new(QueryWithBuilder))) + assert.NoError(t, testEngine.Sync(new(QueryWithBuilder))) var q = QueryWithBuilder{ Msg: "message", @@ -325,7 +325,7 @@ func TestJoinWithSubQuery(t *testing.T) { testEngine.ShowSQL(true) - assert.NoError(t, testEngine.Sync2(new(JoinWithSubQuery1), new(JoinWithSubQueryDepart))) + assert.NoError(t, testEngine.Sync(new(JoinWithSubQuery1), new(JoinWithSubQueryDepart))) var depart = JoinWithSubQueryDepart{ Name: "depart1", @@ -375,7 +375,7 @@ func TestQueryStringWithLimit(t *testing.T) { Money float32 } - assert.NoError(t, testEngine.Sync2(new(QueryWithLimit))) + assert.NoError(t, testEngine.Sync(new(QueryWithLimit))) data, err := testEngine.Table("query_with_limit").Limit(20, 20).QueryString() assert.NoError(t, err) diff --git a/integrations/session_raw_test.go b/integrations/session_raw_test.go index 44718f46..5fa48d6e 100644 --- a/integrations/session_raw_test.go +++ b/integrations/session_raw_test.go @@ -20,7 +20,7 @@ func TestExecAndQuery(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(UserinfoQuery))) + assert.NoError(t, testEngine.Sync(new(UserinfoQuery))) res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_query`", true)+" (`uid`, `name`) VALUES (?, ?)", 1, "user") assert.NoError(t, err) @@ -46,7 +46,7 @@ func TestExecTime(t *testing.T) { Created time.Time } - assert.NoError(t, testEngine.Sync2(new(UserinfoExecTime))) + assert.NoError(t, testEngine.Sync(new(UserinfoExecTime))) now := time.Now() res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_exec_time`", true)+" (`uid`, `name`, `created`) VALUES (?, ?, ?)", 1, "user", now) assert.NoError(t, err) diff --git a/integrations/session_schema_test.go b/integrations/session_schema_test.go index 98ad9657..c4ba4c7d 100644 --- a/integrations/session_schema_test.go +++ b/integrations/session_schema_test.go @@ -173,7 +173,7 @@ func (s *SyncTable3) TableName() string { func TestSyncTable(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SyncTable1))) + assert.NoError(t, testEngine.Sync(new(SyncTable1))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -183,7 +183,7 @@ func TestSyncTable(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, testEngine.Dialect().SQLType(tables[0].GetColumn("name")), testEngine.Dialect().SQLType(tableInfo.GetColumn("name"))) - assert.NoError(t, testEngine.Sync2(new(SyncTable2))) + assert.NoError(t, testEngine.Sync(new(SyncTable2))) tables, err = testEngine.DBMetas() assert.NoError(t, err) @@ -193,7 +193,7 @@ func TestSyncTable(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, testEngine.Dialect().SQLType(tables[0].GetColumn("name")), testEngine.Dialect().SQLType(tableInfo.GetColumn("name"))) - assert.NoError(t, testEngine.Sync2(new(SyncTable3))) + assert.NoError(t, testEngine.Sync(new(SyncTable3))) tables, err = testEngine.DBMetas() assert.NoError(t, err) @@ -207,7 +207,7 @@ func TestSyncTable(t *testing.T) { func TestSyncTable2(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Table("sync_tablex").Sync2(new(SyncTable1))) + assert.NoError(t, testEngine.Table("sync_tablex").Sync(new(SyncTable1))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -220,7 +220,7 @@ func TestSyncTable2(t *testing.T) { NewCol string } - assert.NoError(t, testEngine.Table("sync_tablex").Sync2(new(SyncTable4))) + assert.NoError(t, testEngine.Table("sync_tablex").Sync(new(SyncTable4))) tables, err = testEngine.DBMetas() assert.NoError(t, err) assert.EqualValues(t, 1, len(tables)) @@ -241,7 +241,7 @@ func TestSyncTable3(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SyncTable5))) + assert.NoError(t, testEngine.Sync(new(SyncTable5))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -272,7 +272,7 @@ func TestSyncTable3(t *testing.T) { }() assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SyncTable5))) + assert.NoError(t, testEngine.Sync(new(SyncTable5))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -294,9 +294,9 @@ func TestSyncTable4(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SyncTable6))) + assert.NoError(t, testEngine.Sync(new(SyncTable6))) - assert.NoError(t, testEngine.Sync2(new(SyncTable6))) + assert.NoError(t, testEngine.Sync(new(SyncTable6))) } func TestIsTableExist(t *testing.T) { @@ -335,7 +335,7 @@ func TestIsTableEmpty(t *testing.T) { assert.NoError(t, testEngine.DropTables(&PictureEmpty{}, &NumericEmpty{})) - assert.NoError(t, testEngine.Sync2(new(PictureEmpty), new(NumericEmpty))) + assert.NoError(t, testEngine.Sync(new(PictureEmpty), new(NumericEmpty))) isEmpty, err := testEngine.IsTableEmpty(&PictureEmpty{}) assert.NoError(t, err) @@ -393,7 +393,7 @@ func TestIndexAndUnique(t *testing.T) { func TestMetaInfo(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(CustomTableName), new(IndexOrUnique))) + assert.NoError(t, testEngine.Sync(new(CustomTableName), new(IndexOrUnique))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -423,8 +423,8 @@ func TestSync2_1(t *testing.T) { assert.NoError(t, PrepareEngine()) assert.NoError(t, testEngine.DropTables("wx_test")) - assert.NoError(t, testEngine.Sync2(new(WxTest))) - assert.NoError(t, testEngine.Sync2(new(WxTest))) + assert.NoError(t, testEngine.Sync(new(WxTest))) + assert.NoError(t, testEngine.Sync(new(WxTest))) } func TestUnique_1(t *testing.T) { @@ -440,7 +440,7 @@ func TestUnique_1(t *testing.T) { assert.NoError(t, PrepareEngine()) assert.NoError(t, testEngine.DropTables("user_unique")) - assert.NoError(t, testEngine.Sync2(new(UserUnique))) + assert.NoError(t, testEngine.Sync(new(UserUnique))) assert.NoError(t, testEngine.DropTables("user_unique")) assert.NoError(t, testEngine.CreateTables(new(UserUnique))) @@ -459,7 +459,7 @@ func TestSync2_2(t *testing.T) { for i := 0; i < 10; i++ { tableName := fmt.Sprintf("test_sync2_index_%d", i) tableNames[tableName] = true - assert.NoError(t, testEngine.Table(tableName).Sync2(new(TestSync2Index))) + assert.NoError(t, testEngine.Table(tableName).Sync(new(TestSync2Index))) exist, err := testEngine.IsTableExist(tableName) assert.NoError(t, err) @@ -484,7 +484,7 @@ func TestSync2_Default(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestSync2Default)) - assert.NoError(t, testEngine.Sync2(new(TestSync2Default))) + assert.NoError(t, testEngine.Sync(new(TestSync2Default))) } func TestSync2_Default2(t *testing.T) { @@ -497,9 +497,9 @@ func TestSync2_Default2(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestSync2Default2)) - assert.NoError(t, testEngine.Sync2(new(TestSync2Default2))) - assert.NoError(t, testEngine.Sync2(new(TestSync2Default2))) - assert.NoError(t, testEngine.Sync2(new(TestSync2Default2))) + assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) + assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) + assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) diff --git a/integrations/session_sum_test.go b/integrations/session_sum_test.go index b447c699..e000233b 100644 --- a/integrations/session_sum_test.go +++ b/integrations/session_sum_test.go @@ -23,7 +23,7 @@ func TestSum(t *testing.T) { } assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SumStruct))) + assert.NoError(t, testEngine.Sync(new(SumStruct))) var ( cases = []SumStruct{ @@ -82,7 +82,7 @@ func (s SumStructWithTableName) TableName() string { func TestSumWithTableName(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SumStructWithTableName))) + assert.NoError(t, testEngine.Sync(new(SumStructWithTableName))) var ( cases = []SumStructWithTableName{ @@ -146,7 +146,7 @@ func TestSumCustomColumn(t *testing.T) { } ) - assert.NoError(t, testEngine.Sync2(new(SumStruct2))) + assert.NoError(t, testEngine.Sync(new(SumStruct2))) cnt, err := testEngine.Insert(cases) assert.NoError(t, err) diff --git a/integrations/session_test.go b/integrations/session_test.go index eacf2ff5..a36b81bf 100644 --- a/integrations/session_test.go +++ b/integrations/session_test.go @@ -32,7 +32,7 @@ func TestNullFloatStruct(t *testing.T) { } assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(MyNullFloatStruct))) + assert.NoError(t, testEngine.Sync(new(MyNullFloatStruct))) _, err := testEngine.Insert(&MyNullFloatStruct{ Uuid: "111111", diff --git a/integrations/session_update_test.go b/integrations/session_update_test.go index 4312d0e0..30183382 100644 --- a/integrations/session_update_test.go +++ b/integrations/session_update_test.go @@ -27,7 +27,7 @@ func TestUpdateMap(t *testing.T) { Age int } - assert.NoError(t, testEngine.Sync2(new(UpdateTable))) + assert.NoError(t, testEngine.Sync(new(UpdateTable))) var tb = UpdateTable{ Name: "test", Age: 35, @@ -78,7 +78,7 @@ func TestUpdateLimit(t *testing.T) { Age int } - assert.NoError(t, testEngine.Sync2(new(UpdateTable2))) + assert.NoError(t, testEngine.Sync(new(UpdateTable2))) var tb = UpdateTable2{ Name: "test1", Age: 35, @@ -382,7 +382,7 @@ func TestUpdate1(t *testing.T) { assert.EqualValues(t, 1, cnt, "delete not returned 1") } - err = testEngine.StoreEngine("Innodb").Sync2(&Article{}) + err = testEngine.StoreEngine("Innodb").Sync(&Article{}) assert.NoError(t, err) defer func() { @@ -513,7 +513,7 @@ func TestUpdateUpdated(t *testing.T) { assert.NoError(t, PrepareEngine()) di := new(UpdatedUpdate) - err := testEngine.Sync2(di) + err := testEngine.Sync(di) assert.NoError(t, err) _, err = testEngine.Insert(&UpdatedUpdate{}) @@ -529,7 +529,7 @@ func TestUpdateUpdated(t *testing.T) { assert.EqualValues(t, ci.Updated.Unix(), di.Updated.Unix()) di2 := new(UpdatedUpdate2) - err = testEngine.Sync2(di2) + err = testEngine.Sync(di2) assert.NoError(t, err) now := time.Now() @@ -556,7 +556,7 @@ func TestUpdateUpdated(t *testing.T) { assert.True(t, ci2.Updated >= di21.Updated) di3 := new(UpdatedUpdate3) - err = testEngine.Sync2(di3) + err = testEngine.Sync(di3) assert.NoError(t, err) _, err = testEngine.Insert(&UpdatedUpdate3{}) @@ -572,7 +572,7 @@ func TestUpdateUpdated(t *testing.T) { assert.EqualValues(t, ci3.Updated, di3.Updated) di4 := new(UpdatedUpdate4) - err = testEngine.Sync2(di4) + err = testEngine.Sync(di4) assert.NoError(t, err) _, err = testEngine.Insert(&UpdatedUpdate4{}) @@ -588,7 +588,7 @@ func TestUpdateUpdated(t *testing.T) { assert.EqualValues(t, ci4.Updated, di4.Updated) di5 := new(UpdatedUpdate5) - err = testEngine.Sync2(di5) + err = testEngine.Sync(di5) assert.NoError(t, err) _, err = testEngine.Insert(&UpdatedUpdate5{}) diff --git a/integrations/tags_test.go b/integrations/tags_test.go index b5bf222e..c05a8d99 100644 --- a/integrations/tags_test.go +++ b/integrations/tags_test.go @@ -673,7 +673,7 @@ func TestCreatedUpdated(t *testing.T) { Updated time.Time `xorm:"updated"` } - err := testEngine.Sync2(&CreatedUpdated{}) + err := testEngine.Sync(&CreatedUpdated{}) assert.NoError(t, err) c := &CreatedUpdated{Name: "test"} @@ -728,7 +728,7 @@ type Lowercase struct { func TestLowerCase(t *testing.T) { assert.NoError(t, PrepareEngine()) - err := testEngine.Sync2(&Lowercase{}) + err := testEngine.Sync(&Lowercase{}) assert.NoError(t, err) _, err = testEngine.Where("`id` > 0").Delete(&Lowercase{}) assert.NoError(t, err) @@ -827,7 +827,7 @@ func TestTagComment(t *testing.T) { assert.True(t, cols[0].DefaultIsEmpty) assert.EqualValues(t, "", cols[0].Default) - assert.NoError(t, testEngine.Sync2(new(TestComment1))) + assert.NoError(t, testEngine.Sync(new(TestComment1))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -851,7 +851,7 @@ func TestTagComment(t *testing.T) { assert.True(t, cols[0].DefaultIsEmpty) assert.EqualValues(t, "", cols[0].Default) - assert.NoError(t, testEngine.Sync2(new(TestComment2))) + assert.NoError(t, testEngine.Sync(new(TestComment2))) tables, err = testEngine.DBMetas() assert.NoError(t, err) diff --git a/integrations/types_test.go b/integrations/types_test.go index 48facb21..d166845e 100644 --- a/integrations/types_test.go +++ b/integrations/types_test.go @@ -28,7 +28,7 @@ func TestArrayField(t *testing.T) { Name [20]byte `xorm:"char(80)"` } - assert.NoError(t, testEngine.Sync2(new(ArrayStruct))) + assert.NoError(t, testEngine.Sync(new(ArrayStruct))) var as = ArrayStruct{ Name: [20]byte{ @@ -90,7 +90,7 @@ func TestGetBytes(t *testing.T) { Data []byte `xorm:"VARBINARY(250)"` } - err := testEngine.Sync2(new(Varbinary)) + err := testEngine.Sync(new(Varbinary)) assert.NoError(t, err) cnt, err := testEngine.Insert(&Varbinary{ @@ -193,7 +193,7 @@ func TestConversion(t *testing.T) { c := new(ConvStruct) assert.NoError(t, testEngine.DropTables(c)) - assert.NoError(t, testEngine.Sync2(c)) + assert.NoError(t, testEngine.Sync(c)) var s ConvString = "sssss" c.Conv = "tttt" diff --git a/migrate/migrate_test.go b/migrate/migrate_test.go index 3d7aa189..8f15a025 100644 --- a/migrate/migrate_test.go +++ b/migrate/migrate_test.go @@ -31,7 +31,7 @@ var ( { ID: "201608301400", Migrate: func(tx *xorm.Engine) error { - return tx.Sync2(&Person{}) + return tx.Sync(&Person{}) }, Rollback: func(tx *xorm.Engine) error { return tx.DropTables(&Person{}) @@ -40,7 +40,7 @@ var ( { ID: "201608301430", Migrate: func(tx *xorm.Engine) error { - return tx.Sync2(&Pet{}) + return tx.Sync(&Pet{}) }, Rollback: func(tx *xorm.Engine) error { return tx.DropTables(&Pet{}) @@ -103,10 +103,10 @@ func TestInitSchema(t *testing.T) { m := New(db, DefaultOptions, migrations) m.InitSchema(func(tx *xorm.Engine) error { - if err := tx.Sync2(&Person{}); err != nil { + if err := tx.Sync(&Person{}); err != nil { return err } - return tx.Sync2(&Pet{}) + return tx.Sync(&Pet{}) }) err = m.Migrate() diff --git a/session_schema.go b/session_schema.go index 73352135..e9ed9ec5 100644 --- a/session_schema.go +++ b/session_schema.go @@ -263,7 +263,13 @@ func (session *Session) addUnique(tableName, uqeName string) error { } // Sync2 synchronize structs to database tables +// Depricated func (session *Session) Sync2(beans ...interface{}) error { + return session.Sync(beans...) +} + +// Sync synchronize structs to database tables +func (session *Session) Sync(beans ...interface{}) error { engine := session.engine if session.isAutoClose { From e5c89cf55e83b2f0d9e97cd6adb54a03b3f19237 Mon Sep 17 00:00:00 2001 From: finelog Date: Thu, 26 Aug 2021 00:03:18 +0800 Subject: [PATCH 020/116] fix panic when convert sql and args with nil time.Time pointer (#2038) this pr fix a panic, when using nil time.Time pointer for input. not sure if there are others similar code. please review it, thanks! Co-authored-by: finelog Reviewed-on: https://gitea.com/xorm/xorm/pulls/2038 Reviewed-by: Lunny Xiao Co-authored-by: finelog Co-committed-by: finelog --- internal/statements/statement.go | 4 ++-- internal/statements/statement_test.go | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/internal/statements/statement.go b/internal/statements/statement.go index b85773db..80451f50 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -962,9 +962,9 @@ func (statement *Statement) convertSQLOrArgs(sqlOrArgs ...interface{}) (string, if len(sqlOrArgs) > 1 { var newArgs = make([]interface{}, 0, len(sqlOrArgs)-1) for _, arg := range sqlOrArgs[1:] { - if v, ok := arg.(*time.Time); ok { + if v, ok := arg.(time.Time); ok { newArgs = append(newArgs, v.In(statement.defaultTimeZone).Format("2006-01-02 15:04:05")) - } else if v, ok := arg.(time.Time); ok { + } else if v, ok := arg.(*time.Time); ok && v != nil { newArgs = append(newArgs, v.In(statement.defaultTimeZone).Format("2006-01-02 15:04:05")) } else { newArgs = append(newArgs, arg) diff --git a/internal/statements/statement_test.go b/internal/statements/statement_test.go index ba92330e..abe0e0f2 100644 --- a/internal/statements/statement_test.go +++ b/internal/statements/statement_test.go @@ -77,6 +77,23 @@ func TestColumnsStringGeneration(t *testing.T) { } } +func TestConvertSQLOrArgs(t *testing.T) { + statement, err := createTestStatement() + assert.NoError(t, err) + + // example orm struct + // type Table struct { + // ID int + // del *time.Time `xorm:"deleted"` + // } + args := []interface{}{ + "INSERT `table` (`id`, `del`) VALUES (?, ?)", 1, (*time.Time)(nil), + } + // before fix, here will panic + _, _, err = statement.convertSQLOrArgs(args...) + assert.NoError(t, err) +} + func BenchmarkGetFlagForColumnWithICKey_ContainsKey(b *testing.B) { b.StopTimer() From 045abb07df05ff6a1633d60cd00daa0821631779 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 27 Aug 2021 17:10:16 +0800 Subject: [PATCH 021/116] Expose ScanString / ScanInterface and etc (#2039) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2039 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- scan.go | 288 ++++++++++++++++++++++++++++++++-------------- session.go | 17 ++- session_delete.go | 12 +- session_query.go | 154 ------------------------- session_raw.go | 74 +++++++++++- session_stats.go | 4 +- 6 files changed, 290 insertions(+), 259 deletions(-) delete mode 100644 session_query.go diff --git a/scan.go b/scan.go index 2788453e..d562abbc 100644 --- a/scan.go +++ b/scan.go @@ -129,60 +129,6 @@ func genScanResultsByBean(bean interface{}) (interface{}, bool, error) { } } -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 := engine.driver.Scan(&dialects.ScanContext{ - DBLocation: engine.DatabaseTZ, - UserLocation: engine.TZLocation, - }, rows, types, scanResults...); err != nil { - return nil, err - } - - result := make(map[string]string, len(fields)) - for i, key := range fields { - s := scanResults[i].(*sql.NullString) - if s.String == "" { - result[key] = "" - continue - } - - if schemas.TIME_TYPE == engine.dialect.ColumnTypeKind(types[i].DatabaseTypeName()) { - t, err := convert.String2Time(s.String, engine.DatabaseTZ, engine.TZLocation) - if err != nil { - return nil, err - } - result[key] = t.Format("2006-01-02 15:04:05") - } else { - result[key] = s.String - } - } - return result, nil -} - -func row2mapBytes(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string][]byte, 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][]byte, len(fields)) - for ii, key := range fields { - s := scanResults[ii].(*sql.NullString) - result[key] = []byte(s.String) - } - return result, nil -} - func (engine *Engine) scanStringInterface(rows *core.Rows, fields []string, types []*sql.ColumnType) ([]interface{}, error) { var scanResults = make([]interface{}, len(types)) for i := 0; i < len(types); i++ { @@ -262,41 +208,8 @@ func (engine *Engine) scanInterfaces(rows *core.Rows, fields []string, types []* return scanResultContainers, nil } -func (engine *Engine) row2sliceStr(rows *core.Rows, types []*sql.ColumnType, fields []string) ([]string, error) { - scanResults, err := engine.scanStringInterface(rows, fields, types) - if err != nil { - return nil, err - } - - var results = make([]string, 0, len(fields)) - for i := 0; i < len(fields); i++ { - results = append(results, scanResults[i].(*sql.NullString).String) - } - return results, nil -} - -func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, 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 := row2mapBytes(rows, types, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, result) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return resultsSlice, nil -} +//////////////////// +// row -> map[string]interface{} func (engine *Engine) row2mapInterface(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]interface{}, error) { var resultsMap = make(map[string]interface{}, len(fields)) @@ -321,3 +234,200 @@ func (engine *Engine) row2mapInterface(rows *core.Rows, types []*sql.ColumnType, } return resultsMap, nil } + +func (engine *Engine) ScanInterfaceMap(rows *core.Rows) (map[string]interface{}, error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + + return engine.row2mapInterface(rows, types, fields) +} + +func (engine *Engine) ScanInterfaceMaps(rows *core.Rows) (resultsSlice []map[string]interface{}, 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 := engine.row2mapInterface(rows, types, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, result) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return resultsSlice, nil +} + +//////////////////// +// row -> map[string]string + +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 := engine.driver.Scan(&dialects.ScanContext{ + DBLocation: engine.DatabaseTZ, + UserLocation: engine.TZLocation, + }, rows, types, scanResults...); err != nil { + return nil, err + } + + result := make(map[string]string, len(fields)) + for i, key := range fields { + s := scanResults[i].(*sql.NullString) + if s.String == "" { + result[key] = "" + continue + } + + if schemas.TIME_TYPE == engine.dialect.ColumnTypeKind(types[i].DatabaseTypeName()) { + t, err := convert.String2Time(s.String, engine.DatabaseTZ, engine.TZLocation) + if err != nil { + return nil, err + } + result[key] = t.Format("2006-01-02 15:04:05") + } else { + result[key] = s.String + } + } + return result, nil +} + +func (engine *Engine) ScanStringMap(rows *core.Rows) (map[string]string, error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + return engine.row2mapStr(rows, types, fields) +} + +func (engine *Engine) ScanStringMaps(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 := engine.row2mapStr(rows, types, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, result) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return resultsSlice, nil +} + +//////////////////// +// row -> map[string][]byte + +func convertMapStr2Bytes(m map[string]string) map[string][]byte { + var r = make(map[string][]byte, len(m)) + for k, v := range m { + r[k] = []byte(v) + } + return r +} + +func (engine *Engine) scanByteMaps(rows *core.Rows) (resultsSlice []map[string][]byte, 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 := engine.row2mapStr(rows, types, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, convertMapStr2Bytes(result)) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return resultsSlice, nil +} + +//////////////////// +// row -> []string + +func (engine *Engine) row2sliceStr(rows *core.Rows, types []*sql.ColumnType, fields []string) ([]string, error) { + scanResults, err := engine.scanStringInterface(rows, fields, types) + if err != nil { + return nil, err + } + + var results = make([]string, 0, len(fields)) + for i := 0; i < len(fields); i++ { + results = append(results, scanResults[i].(*sql.NullString).String) + } + return results, nil +} + +func (engine *Engine) ScanStringSlice(rows *core.Rows) ([]string, error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + + return engine.row2sliceStr(rows, types, fields) +} + +func (engine *Engine) ScanStringSlices(rows *core.Rows) (resultsSlice [][]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() { + record, err := engine.row2sliceStr(rows, types, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, record) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return resultsSlice, nil +} diff --git a/session.go b/session.go index a96d2fc9..0e2c48ca 100644 --- a/session.go +++ b/session.go @@ -123,9 +123,9 @@ func newSession(engine *Engine) *Session { autoResetStatement: true, prepareStmt: false, - afterInsertBeans: make(map[interface{}]*[]func(interface{}), 0), - afterUpdateBeans: make(map[interface{}]*[]func(interface{}), 0), - afterDeleteBeans: make(map[interface{}]*[]func(interface{}), 0), + afterInsertBeans: make(map[interface{}]*[]func(interface{})), + afterUpdateBeans: make(map[interface{}]*[]func(interface{})), + afterDeleteBeans: make(map[interface{}]*[]func(interface{})), beforeClosures: make([]func(interface{}), 0), afterClosures: make([]func(interface{}), 0), afterProcessors: make([]executedProcessor, 0), @@ -684,13 +684,12 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b tempMap[lKey] = idx col, fieldValue, err := session.getField(dataStruct, table, colName, idx) - if err != nil { - if _, ok := err.(ErrFieldIsNotExist); ok { - continue - } else { - return nil, err - } + if _, ok := err.(ErrFieldIsNotExist); ok { + continue + } else if err != nil { + return nil, err } + if fieldValue == nil { continue } diff --git a/session_delete.go b/session_delete.go index 37b9c1cd..9c19e43e 100644 --- a/session_delete.go +++ b/session_delete.go @@ -40,7 +40,13 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri pkColumns := table.PKColumns() ids, err := caches.GetCacheSql(cacher, tableName, newsql, args) if err != nil { - resultsSlice, err := session.queryBytes(newsql, args...) + rows, err := session.queryRows(newsql, args...) + if err != nil { + return err + } + defer rows.Close() + + resultsSlice, err := session.engine.ScanStringMaps(rows) if err != nil { return err } @@ -53,9 +59,9 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri if v, ok := data[col.Name]; !ok { return errors.New("no id") } else if col.SQLType.IsText() { - pk = append(pk, string(v)) + pk = append(pk, v) } else if col.SQLType.IsNumeric() { - id, err = strconv.ParseInt(string(v), 10, 64) + id, err = strconv.ParseInt(v, 10, 64) if err != nil { return err } diff --git a/session_query.go b/session_query.go deleted file mode 100644 index a4070985..00000000 --- a/session_query.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2017 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 ( - "xorm.io/xorm/core" -) - -// Query runs a raw sql and return records as []map[string][]byte -func (session *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, error) { - if session.isAutoClose { - defer session.Close() - } - - sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) - if err != nil { - return nil, err - } - - return session.queryBytes(sqlStr, args...) -} - -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 := session.engine.row2mapStr(rows, types, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, result) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return resultsSlice, nil -} - -func (session *Session) rows2SliceString(rows *core.Rows) (resultsSlice [][]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() { - record, err := session.engine.row2sliceStr(rows, types, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, record) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return resultsSlice, nil -} - -// QueryString runs a raw sql and return records as []map[string]string -func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) { - if session.isAutoClose { - defer session.Close() - } - - sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) - if err != nil { - return nil, err - } - - rows, err := session.queryRows(sqlStr, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - return session.rows2Strings(rows) -} - -// QuerySliceString runs a raw sql and return records as [][]string -func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string, error) { - if session.isAutoClose { - defer session.Close() - } - - sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) - if err != nil { - return nil, err - } - - rows, err := session.queryRows(sqlStr, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - return session.rows2SliceString(rows) -} - -func (session *Session) rows2Interfaces(rows *core.Rows) (resultsSlice []map[string]interface{}, 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 := session.engine.row2mapInterface(rows, types, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, result) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return resultsSlice, nil -} - -// QueryInterface runs a raw sql and return records as []map[string]interface{} -func (session *Session) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) { - if session.isAutoClose { - defer session.Close() - } - - sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) - if err != nil { - return nil, err - } - - rows, err := session.queryRows(sqlStr, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - return session.rows2Interfaces(rows) -} diff --git a/session_raw.go b/session_raw.go index 2b488988..cee29fc7 100644 --- a/session_raw.go +++ b/session_raw.go @@ -71,14 +71,84 @@ func (session *Session) queryRow(sqlStr string, args ...interface{}) *core.Row { return core.NewRow(session.queryRows(sqlStr, args...)) } -func (session *Session) queryBytes(sqlStr string, args ...interface{}) ([]map[string][]byte, error) { +// Query runs a raw sql and return records as []map[string][]byte +func (session *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) + if err != nil { + return nil, err + } + rows, err := session.queryRows(sqlStr, args...) if err != nil { return nil, err } defer rows.Close() - return rows2maps(rows) + return session.engine.scanByteMaps(rows) +} + +// QueryString runs a raw sql and return records as []map[string]string +func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) + if err != nil { + return nil, err + } + + rows, err := session.queryRows(sqlStr, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + return session.engine.ScanStringMaps(rows) +} + +// QuerySliceString runs a raw sql and return records as [][]string +func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) + if err != nil { + return nil, err + } + + rows, err := session.queryRows(sqlStr, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + return session.engine.ScanStringSlices(rows) +} + +// QueryInterface runs a raw sql and return records as []map[string]interface{} +func (session *Session) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) + if err != nil { + return nil, err + } + + rows, err := session.queryRows(sqlStr, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + return session.engine.ScanInterfaceMaps(rows) } func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) { diff --git a/session_stats.go b/session_stats.go index 17d0a675..5d0da5e9 100644 --- a/session_stats.go +++ b/session_stats.go @@ -70,12 +70,12 @@ func (session *Session) SumInt(bean interface{}, columnName string) (res int64, // Sums call sum some columns. bean's non-empty fields are conditions. func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) { - var res = make([]float64, len(columnNames), len(columnNames)) + var res = make([]float64, len(columnNames)) return res, session.sum(&res, bean, columnNames...) } // SumsInt sum specify columns and return as []int64 instead of []float64 func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { - var res = make([]int64, len(columnNames), len(columnNames)) + var res = make([]int64, len(columnNames)) return res, session.sum(&res, bean, columnNames...) } From 78309606d4bb234507f8c9560ff82326932d6aab Mon Sep 17 00:00:00 2001 From: daisuzu Date: Tue, 7 Sep 2021 09:23:16 +0800 Subject: [PATCH 022/116] fix panic when `Iterate()` fails (#2040) not to call `rows.Err()` Reviewed-on: https://gitea.com/xorm/xorm/pulls/2040 Reviewed-by: Lunny Xiao Co-authored-by: daisuzu Co-committed-by: daisuzu --- rows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rows.go b/rows.go index 76fc1e90..b7984b99 100644 --- a/rows.go +++ b/rows.go @@ -140,5 +140,5 @@ func (rows *Rows) Close() error { return rows.rows.Close() } - return rows.Err() + return nil } From 16cf2442f6bec5543f611e93ea32e991f1501791 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 7 Sep 2021 16:03:08 +0800 Subject: [PATCH 023/116] Some performance optimization for get (#2043) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2043 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- integrations/performance_test.go | 2 +- integrations/session_delete_test.go | 4 +- internal/statements/query.go | 61 +++++++++++++++++------------ internal/statements/statement.go | 2 +- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/integrations/performance_test.go b/integrations/performance_test.go index 4646afa2..49183717 100644 --- a/integrations/performance_test.go +++ b/integrations/performance_test.go @@ -92,8 +92,8 @@ func BenchmarkFindStruct(b *testing.B) { _, err := testEngine.Insert(&v) assert.NoError(b, err) - b.StartTimer() var mynames = make([]BenchmarkFindStruct, 0, 1) + b.StartTimer() for i := 0; i < b.N; i++ { err := testEngine.Find(&mynames) b.StopTimer() diff --git a/integrations/session_delete_test.go b/integrations/session_delete_test.go index e8761acf..b4e40edb 100644 --- a/integrations/session_delete_test.go +++ b/integrations/session_delete_test.go @@ -213,7 +213,7 @@ func TestUnscopeDelete(t *testing.T) { cnt, err = testEngine.ID(1).Delete(&s) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - assert.EqualValues(t, nowUnix, s.DeletedAt.Unix()) + assert.LessOrEqual(t, int(s.DeletedAt.Unix()-nowUnix), 1) var s1 UnscopeDeleteStruct has, err := testEngine.ID(1).Get(&s1) @@ -225,7 +225,7 @@ func TestUnscopeDelete(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "test", s2.Name) - assert.EqualValues(t, nowUnix, s2.DeletedAt.Unix()) + assert.LessOrEqual(t, int(s2.DeletedAt.Unix()-nowUnix), 1) cnt, err = testEngine.ID(1).Unscoped().Delete(new(UnscopeDeleteStruct)) assert.NoError(t, err) diff --git a/internal/statements/query.go b/internal/statements/query.go index 76946cbd..c1ff8833 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -203,14 +203,42 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa return sqlStr, append(statement.joinArgs, condArgs...), nil } +func (statement *Statement) fromBuilder() *strings.Builder { + var builder strings.Builder + var quote = statement.quote + var dialect = statement.dialect + + builder.WriteString(" FROM ") + + if dialect.URI().DBType == schemas.MSSQL && strings.Contains(statement.TableName(), "..") { + builder.WriteString(statement.TableName()) + } else { + builder.WriteString(quote(statement.TableName())) + } + + if statement.TableAlias != "" { + if dialect.URI().DBType == schemas.ORACLE { + builder.WriteString(" ") + } else { + builder.WriteString(" AS ") + } + builder.WriteString(quote(statement.TableAlias)) + } + if statement.JoinStr != "" { + builder.WriteString(" ") + builder.WriteString(statement.JoinStr) + } + return &builder +} + func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderBy bool) (string, []interface{}, error) { var ( distinct string dialect = statement.dialect - quote = statement.quote - fromStr = " FROM " + fromStr = statement.fromBuilder().String() top, mssqlCondi, whereStr string ) + if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { distinct = "DISTINCT " } @@ -220,24 +248,7 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB return "", nil, err } if len(condSQL) > 0 { - whereStr = " WHERE " + condSQL - } - - if dialect.URI().DBType == schemas.MSSQL && strings.Contains(statement.TableName(), "..") { - fromStr += statement.TableName() - } else { - fromStr += quote(statement.TableName()) - } - - if statement.TableAlias != "" { - if dialect.URI().DBType == schemas.ORACLE { - fromStr += " " + quote(statement.TableAlias) - } else { - fromStr += " AS " + quote(statement.TableAlias) - } - } - if statement.JoinStr != "" { - fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr) + whereStr = fmt.Sprintf(" WHERE %s", condSQL) } pLimitN := statement.LimitN @@ -266,20 +277,20 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB } if statement.needTableName() { if len(statement.TableAlias) > 0 { - column = statement.TableAlias + "." + column + column = fmt.Sprintf("%s.%s", statement.TableAlias, column) } else { - column = statement.TableName() + "." + column + column = fmt.Sprintf("%s.%s", statement.TableName(), column) } } var orderStr string if needOrderBy && len(statement.OrderStr) > 0 { - orderStr = " ORDER BY " + statement.OrderStr + orderStr = fmt.Sprintf(" ORDER BY %s", statement.OrderStr) } var groupStr string if len(statement.GroupByStr) > 0 { - groupStr = " GROUP BY " + statement.GroupByStr + groupStr = fmt.Sprintf(" GROUP BY %s", statement.GroupByStr) } mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s%s%s))", column, statement.Start, column, fromStr, whereStr, orderStr, groupStr) @@ -311,7 +322,7 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB if pLimitN != nil { fmt.Fprintf(&buf, " LIMIT %v OFFSET %v", *pLimitN, statement.Start) } else { - fmt.Fprintf(&buf, "LIMIT 0 OFFSET %v", statement.Start) + fmt.Fprintf(&buf, " LIMIT 0 OFFSET %v", statement.Start) } } else if pLimitN != nil { fmt.Fprint(&buf, " LIMIT ", *pLimitN) diff --git a/internal/statements/statement.go b/internal/statements/statement.go index 80451f50..c9882a47 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -308,7 +308,7 @@ func (statement *Statement) colName(col *schemas.Column, tableName string) strin if len(statement.TableAlias) > 0 { nm = statement.TableAlias } - return statement.quote(nm) + "." + statement.quote(col.Name) + return fmt.Sprintf("%s.%s", statement.quote(nm), statement.quote(col.Name)) } return statement.quote(col.Name) } From 4656a87f2f8715d6088dac3965ab5a0500dd2fee Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 13 Sep 2021 15:19:59 +0800 Subject: [PATCH 024/116] Fix bug (#2046) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2046 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- rows.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rows.go b/rows.go index b7984b99..110f41b6 100644 --- a/rows.go +++ b/rows.go @@ -83,12 +83,18 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { // Next move cursor to next record, return false if end has reached func (rows *Rows) Next() bool { - return rows.rows.Next() + if rows.rows != nil { + return rows.rows.Next() + } + return false } // Err returns the error, if any, that was encountered during iteration. Err may be called after an explicit or implicit Close. func (rows *Rows) Err() error { - return rows.rows.Err() + if rows.rows != nil { + return rows.rows.Err() + } + return nil } // Scan row record to bean properties From 67616990363485db11ef14e372ccb1c42643d698 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 16 Sep 2021 23:59:36 +0800 Subject: [PATCH 025/116] Fix bug of Rows (#2048) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2048 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- integrations/session_iterate_test.go | 17 +++++++++++--- session.go | 10 ++++----- session_get.go | 33 ++++++++++++---------------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/integrations/session_iterate_test.go b/integrations/session_iterate_test.go index fa394d17..c5ecc593 100644 --- a/integrations/session_iterate_test.go +++ b/integrations/session_iterate_test.go @@ -26,16 +26,27 @@ func TestIterate(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) + cnt, err = testEngine.Insert(&UserIterate{ + IsMan: false, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + cnt = 0 err = testEngine.Iterate(new(UserIterate), func(i int, bean interface{}) error { user := bean.(*UserIterate) - assert.EqualValues(t, 1, user.Id) - assert.EqualValues(t, true, user.IsMan) + if cnt == 0 { + assert.EqualValues(t, 1, user.Id) + assert.EqualValues(t, true, user.IsMan) + } else { + assert.EqualValues(t, 2, user.Id) + assert.EqualValues(t, false, user.IsMan) + } cnt++ return nil }) assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) + assert.EqualValues(t, 2, cnt) } func TestBufferIterate(t *testing.T) { diff --git a/session.go b/session.go index 0e2c48ca..f5b45a73 100644 --- a/session.go +++ b/session.go @@ -370,7 +370,7 @@ func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, return } -func (session *Session) getField(dataStruct *reflect.Value, table *schemas.Table, colName string, idx int) (*schemas.Column, *reflect.Value, error) { +func getField(dataStruct *reflect.Value, table *schemas.Table, colName string, idx int) (*schemas.Column, *reflect.Value, error) { var col = table.GetColumnIdx(colName, idx) if col == nil { return nil, nil, ErrFieldIsNotExist{colName, table.Name} @@ -440,7 +440,7 @@ func (session *Session) row2Slice(rows *core.Rows, fields []string, types []*sql return scanResults, nil } -func (session *Session) setJSON(fieldValue *reflect.Value, fieldType reflect.Type, scanResult interface{}) error { +func setJSON(fieldValue *reflect.Value, fieldType reflect.Type, scanResult interface{}) error { bs, ok := convert.AsBytes(scanResult) if !ok { return fmt.Errorf("unsupported database data type: %#v", scanResult) @@ -551,7 +551,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec fieldType := fieldValue.Type() if col.IsJSON { - return session.setJSON(fieldValue, fieldType, scanResult) + return setJSON(fieldValue, fieldType, scanResult) } switch fieldType.Kind() { @@ -570,7 +570,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } return nil case reflect.Complex64, reflect.Complex128: - return session.setJSON(fieldValue, fieldType, scanResult) + return setJSON(fieldValue, fieldType, scanResult) case reflect.Slice, reflect.Array: bs, ok := convert.AsBytes(scanResult) if ok && fieldType.Elem().Kind() == reflect.Uint8 { @@ -683,7 +683,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b } tempMap[lKey] = idx - col, fieldValue, err := session.getField(dataStruct, table, colName, idx) + col, fieldValue, err := getField(dataStruct, table, colName, idx) if _, ok := err.(ErrFieldIsNotExist); ok { continue } else if err != nil { diff --git a/session_get.go b/session_get.go index a82cae92..48616a6b 100644 --- a/session_get.go +++ b/session_get.go @@ -173,7 +173,12 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, return true, err } - return true, session.scan(rows, table, beanKind, beans, types, fields) + if err := session.scan(rows, table, beanKind, beans, types, fields); err != nil { + return true, err + } + rows.Close() + + return true, session.executeProcessors() } func (session *Session) scan(rows *core.Rows, table *schemas.Table, firstBeanKind reflect.Kind, beans []interface{}, types []*sql.ColumnType, fields []string) error { @@ -184,7 +189,14 @@ func (session *Session) scan(rows *core.Rows, table *schemas.Table, firstBeanKin if !isScannableStruct(bean, len(types)) { break } - return session.getStruct(rows, types, fields, table, bean) + scanResults, err := session.row2Slice(rows, fields, types, bean) + if err != nil { + return err + } + + dataStruct := utils.ReflectValue(bean) + _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table) + return err case reflect.Slice: return session.getSlice(rows, types, fields, bean) case reflect.Map: @@ -268,23 +280,6 @@ func (session *Session) getMap(rows *core.Rows, types []*sql.ColumnType, fields } } -func (session *Session) getStruct(rows *core.Rows, types []*sql.ColumnType, fields []string, table *schemas.Table, bean interface{}) error { - scanResults, err := session.row2Slice(rows, fields, types, bean) - if err != nil { - return err - } - // close it before convert data - rows.Close() - - dataStruct := utils.ReflectValue(bean) - _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table) - if err != nil { - return err - } - - return session.executeProcessors() -} - func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) { // if has no reftable, then don't use cache currently if !session.canCache() { From fd26f415ca07db048c77723c092e454212567b95 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 17 Sep 2021 18:47:30 +0800 Subject: [PATCH 026/116] Add test for mysql tls (#2049) Fix #1495 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2049 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- .drone.yml | 2 +- Makefile | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index faaeca17..210572b0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -73,7 +73,7 @@ steps: TEST_MYSQL_PASSWORD: commands: - make test-mysql - - TEST_QUOTE_POLICY=reserved make test-mysql + - TEST_QUOTE_POLICY=reserved make test-mysql-tls volumes: - name: cache diff --git a/Makefile b/Makefile index e9bd4129..ba5427d6 100644 --- a/Makefile +++ b/Makefile @@ -190,6 +190,18 @@ test-mysql\#%: go-check -conn_str="$(TEST_MYSQL_USERNAME):$(TEST_MYSQL_PASSWORD)@tcp($(TEST_MYSQL_HOST))/$(TEST_MYSQL_DBNAME)?charset=$(TEST_MYSQL_CHARSET)" \ -coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic +.PNONY: test-mysql-tls +test-mysql-tls: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=mysql -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \ + -conn_str="$(TEST_MYSQL_USERNAME):$(TEST_MYSQL_PASSWORD)@tcp($(TEST_MYSQL_HOST))/$(TEST_MYSQL_DBNAME)?charset=$(TEST_MYSQL_CHARSET)&tls=skip-verify" \ + -coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m + +.PHONY: test-mysql-tls\#% +test-mysql-tls\#%: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -db=mysql -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \ + -conn_str="$(TEST_MYSQL_USERNAME):$(TEST_MYSQL_PASSWORD)@tcp($(TEST_MYSQL_HOST))/$(TEST_MYSQL_DBNAME)?charset=$(TEST_MYSQL_CHARSET)&tls=skip-verify" \ + -coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic + .PNONY: test-postgres test-postgres: go-check $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=postgres -schema='$(TEST_PGSQL_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ From 0de285680bed16dacdf63a755d32d0edf0a5b794 Mon Sep 17 00:00:00 2001 From: undefined_ss Date: Thu, 23 Sep 2021 20:22:14 +0800 Subject: [PATCH 027/116] fix ctx override bug (#2053) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 详情请参考工单:https://gitea.com/xorm/xorm/issues/2052 Co-authored-by: undefined_ss Reviewed-on: https://gitea.com/xorm/xorm/pulls/2053 Reviewed-by: Lunny Xiao Co-authored-by: undefined_ss Co-committed-by: undefined_ss --- session.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/session.go b/session.go index f5b45a73..f51fd41b 100644 --- a/session.go +++ b/session.go @@ -732,6 +732,12 @@ func (session *Session) incrVersionFieldValue(fieldValue *reflect.Value) { // Context sets the context on this session func (session *Session) Context(ctx context.Context) *Session { + if session.ctx != nil { + ctx = context.WithValue(ctx, log.SessionIDKey, session.ctx.Value(log.SessionIDKey)) + ctx = context.WithValue(ctx, log.SessionKey, session.ctx.Value(log.SessionKey)) + ctx = context.WithValue(ctx, log.SessionShowSQLKey, session.ctx.Value(log.SessionShowSQLKey)) + } + session.ctx = ctx return session } From bd5cd8cab725293299e201e7a8e864a689baf6b1 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 24 Sep 2021 21:45:47 +0800 Subject: [PATCH 028/116] Fix bug of dameng scan (#2056) Fix #2055 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2056 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- dialects/dameng.go | 2 +- integrations/session_insert_test.go | 36 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/dialects/dameng.go b/dialects/dameng.go index 5ba0cfb5..a8e7db70 100644 --- a/dialects/dameng.go +++ b/dialects/dameng.go @@ -1163,7 +1163,7 @@ func (d *damengDriver) Scan(ctx *ScanContext, rows *core.Rows, types []*sql.Colu case "TIMESTAMP": ns := t.(*sql.NullString) if !ns.Valid { - return nil + break } s := ns.String fields := strings.Split(s, "+") diff --git a/integrations/session_insert_test.go b/integrations/session_insert_test.go index fb0ee2d1..394e7e51 100644 --- a/integrations/session_insert_test.go +++ b/integrations/session_insert_test.go @@ -337,6 +337,42 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci6.Created.Unix(), di6.Created.Unix()) } +func TestInsertTime(t *testing.T) { + type InsertTimeStruct struct { + Id int64 + CreatedAt time.Time `xorm:"created"` + UpdatedAt time.Time `xorm:"updated"` + DeletedAt time.Time `xorm:"deleted"` + Stime time.Time + Etime time.Time + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(InsertTimeStruct)) + + its := &InsertTimeStruct{ + Stime: time.Now(), + Etime: time.Now(), + } + cnt, err := testEngine.Insert(its) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var itsGet InsertTimeStruct + has, err := testEngine.ID(1).Get(&itsGet) + assert.NoError(t, err) + assert.True(t, has) + assert.False(t, itsGet.Stime.IsZero()) + assert.False(t, itsGet.Etime.IsZero()) + + var itsFind []*InsertTimeStruct + err = testEngine.Find(&itsFind) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(itsFind)) + assert.False(t, itsFind[0].Stime.IsZero()) + assert.False(t, itsFind[0].Etime.IsZero()) +} + type JSONTime time.Time func (j JSONTime) format() string { From b350c289f82808cc387715938c14eb6ccea52277 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 29 Sep 2021 20:07:55 +0800 Subject: [PATCH 029/116] Fix missing quote on modifycolumnSQL (#2058) Fix #2054 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2058 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- dialects/dialect.go | 2 +- dialects/mssql.go | 2 +- dialects/postgres.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dialects/dialect.go b/dialects/dialect.go index 460ab56a..f3aa7470 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -238,7 +238,7 @@ func (db *Base) DropIndexSQL(tableName string, index *schemas.Index) string { // ModifyColumnSQL returns a SQL to modify SQL func (db *Base) ModifyColumnSQL(tableName string, col *schemas.Column) string { s, _ := ColumnString(db.dialect, col, false) - return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", tableName, s) + return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", db.quoter.Quote(tableName), s) } // ForUpdateSQL returns for updateSQL diff --git a/dialects/mssql.go b/dialects/mssql.go index cd19afb9..706a754a 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -429,7 +429,7 @@ func (db *mssql) DropTableSQL(tableName string) (string, bool) { func (db *mssql) ModifyColumnSQL(tableName string, col *schemas.Column) string { s, _ := ColumnString(db.dialect, col, false) - return fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s", tableName, s) + return fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s", db.quoter.Quote(tableName), s) } func (db *mssql) IndexCheckSQL(tableName, idxName string) (string, []interface{}) { diff --git a/dialects/postgres.go b/dialects/postgres.go index 822d3a70..1e99cd9d 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -994,10 +994,10 @@ func (db *postgres) IsTableExist(queryer core.Queryer, ctx context.Context, tabl func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string { if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") { return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", - tableName, col.Name, db.SQLType(col)) + db.quoter.Quote(tableName), db.quoter.Quote(col.Name), db.SQLType(col)) } return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s", - db.getSchema(), tableName, col.Name, db.SQLType(col)) + db.quoter.Quote(db.getSchema()), db.quoter.Quote(tableName), db.quoter.Quote(col.Name), db.SQLType(col)) } func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string { From 40a135948bf7bb4bb7f24dc0f2d39e2ea9ee0595 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 20 Oct 2021 08:53:30 +0800 Subject: [PATCH 030/116] New Prepare useage (#2061) Fix #2060, Three ways to use the `Prepare`. The first ```go engine.Prepare().Where().Get() ``` The second ```go sess := engine.NewSession() defer sess.Close() sess.Prepare().Where().Get() sess.Prepare().Where().Get() ``` The third ```go sess := engine.NewSession() defer sess.Close() sess.Begin() sess.Prepare().Where().Get() sess.Prepare().Where().Get() sess.Commit() ``` Or ```go sess := engine.NewSession() defer sess.Close() sess.Begin() sess.Prepare().Insert() sess.Prepare().Insert() sess.Commit() ``` Reviewed-on: https://gitea.com/xorm/xorm/pulls/2061 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- core/stmt.go | 2 +- integrations/session_get_test.go | 61 ++++++++++++++++++++++++++++++++ interface.go | 1 + session.go | 27 +++++++++++++- session_raw.go | 35 +++++++++--------- 5 files changed, 106 insertions(+), 20 deletions(-) diff --git a/core/stmt.go b/core/stmt.go index 260843d5..3247efed 100644 --- a/core/stmt.go +++ b/core/stmt.go @@ -93,7 +93,7 @@ func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result if err != nil { return nil, err } - res, err := s.Stmt.ExecContext(ctx, args) + res, err := s.Stmt.ExecContext(ctx, args...) hookCtx.End(ctx, res, err) if err := s.db.afterProcess(hookCtx); err != nil { return nil, err diff --git a/integrations/session_get_test.go b/integrations/session_get_test.go index 601d4a26..5d1558f4 100644 --- a/integrations/session_get_test.go +++ b/integrations/session_get_test.go @@ -915,3 +915,64 @@ func TestGetVars(t *testing.T) { assert.EqualValues(t, "xlw", name) assert.EqualValues(t, 42, age) } + +func TestGetWithPrepare(t *testing.T) { + type GetVarsWithPrepare struct { + Id int64 + Name string + Age int + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(GetVarsWithPrepare)) + + _, err := testEngine.Insert(&GetVarsWithPrepare{ + Name: "xlw", + Age: 42, + }) + assert.NoError(t, err) + + var v1 GetVarsWithPrepare + has, err := testEngine.Prepare().ID(1).Get(&v1) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "xlw", v1.Name) + assert.EqualValues(t, 42, v1.Age) + + sess := testEngine.NewSession() + defer sess.Close() + + var v2 GetVarsWithPrepare + has, err = sess.Prepare().ID(1).Get(&v2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "xlw", v2.Name) + assert.EqualValues(t, 42, v2.Age) + + var v3 GetVarsWithPrepare + has, err = sess.Prepare().ID(1).Get(&v3) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "xlw", v3.Name) + assert.EqualValues(t, 42, v3.Age) + + err = sess.Begin() + assert.NoError(t, err) + + cnt, err := sess.Prepare().Insert(&GetVarsWithPrepare{ + Name: "xlw2", + Age: 12, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + cnt, err = sess.Prepare().Insert(&GetVarsWithPrepare{ + Name: "xlw3", + Age: 13, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + err = sess.Commit() + assert.NoError(t, err) +} diff --git a/interface.go b/interface.go index 42dc9a0a..b9e88505 100644 --- a/interface.go +++ b/interface.go @@ -99,6 +99,7 @@ type EngineInterface interface { MapCacher(interface{}, caches.Cacher) error NewSession() *Session NoAutoTime() *Session + Prepare() *Session Quote(string) string SetCacher(string, caches.Cacher) SetConnMaxLifetime(time.Duration) diff --git a/session.go b/session.go index f51fd41b..2c916335 100644 --- a/session.go +++ b/session.go @@ -79,7 +79,8 @@ type Session struct { afterClosures []func(interface{}) afterProcessors []executedProcessor - stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) + stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) + txStmtCache map[uint32]*core.Stmt // for tx statement lastSQL string lastSQLArgs []interface{} @@ -130,6 +131,7 @@ func newSession(engine *Engine) *Session { afterClosures: make([]func(interface{}), 0), afterProcessors: make([]executedProcessor, 0), stmtCache: make(map[uint32]*core.Stmt), + txStmtCache: make(map[uint32]*core.Stmt), lastSQL: "", lastSQLArgs: make([]interface{}, 0), @@ -150,6 +152,12 @@ func (session *Session) Close() error { } } + for _, v := range session.txStmtCache { + if err := v.Close(); err != nil { + return err + } + } + if !session.isClosed { // When Close be called, if session is a transaction and do not call // Commit or Rollback, then call Rollback. @@ -160,6 +168,7 @@ func (session *Session) Close() error { } session.tx = nil session.stmtCache = nil + session.txStmtCache = nil session.isClosed = true } return nil @@ -200,6 +209,7 @@ func (session *Session) IsClosed() bool { func (session *Session) resetStatement() { if session.autoResetStatement { session.statement.Reset() + session.prepareStmt = false } } @@ -370,6 +380,21 @@ func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, return } +func (session *Session) doPrepareTx(sqlStr string) (stmt *core.Stmt, err error) { + crc := crc32.ChecksumIEEE([]byte(sqlStr)) + // TODO try hash(sqlStr+len(sqlStr)) + var has bool + stmt, has = session.txStmtCache[crc] + if !has { + stmt, err = session.tx.PrepareContext(session.ctx, sqlStr) + if err != nil { + return nil, err + } + session.txStmtCache[crc] = stmt + } + return +} + func getField(dataStruct *reflect.Value, table *schemas.Table, colName string, idx int) (*schemas.Column, *reflect.Value, error) { var col = table.GetColumnIdx(colName, idx) if col == nil { diff --git a/session_raw.go b/session_raw.go index cee29fc7..acb106a5 100644 --- a/session_raw.go +++ b/session_raw.go @@ -46,25 +46,22 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row return nil, err } - rows, err := stmt.QueryContext(session.ctx, args...) - if err != nil { - return nil, err - } - return rows, nil + return stmt.QueryContext(session.ctx, args...) } - rows, err := db.QueryContext(session.ctx, sqlStr, args...) + return db.QueryContext(session.ctx, sqlStr, args...) + } + + if session.prepareStmt { + stmt, err := session.doPrepareTx(sqlStr) if err != nil { return nil, err } - return rows, nil + + return stmt.QueryContext(session.ctx, args...) } - rows, err := session.tx.QueryContext(session.ctx, sqlStr, args...) - if err != nil { - return nil, err - } - return rows, nil + return session.tx.QueryContext(session.ctx, sqlStr, args...) } func (session *Session) queryRow(sqlStr string, args ...interface{}) *core.Row { @@ -160,6 +157,13 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er session.lastSQLArgs = args if !session.isAutoCommit { + if session.prepareStmt { + stmt, err := session.doPrepareTx(sqlStr) + if err != nil { + return nil, err + } + return stmt.ExecContext(session.ctx, args...) + } return session.tx.ExecContext(session.ctx, sqlStr, args...) } @@ -168,12 +172,7 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er if err != nil { return nil, err } - - res, err := stmt.ExecContext(session.ctx, args...) - if err != nil { - return nil, err - } - return res, nil + return stmt.ExecContext(session.ctx, args...) } return session.DB().ExecContext(session.ctx, sqlStr, args...) From 26f9d619cc75ae89785f876c52286144eab7708a Mon Sep 17 00:00:00 2001 From: rennnosuke Date: Thu, 28 Oct 2021 21:21:38 +0800 Subject: [PATCH 031/116] Fix new-lined query execution in master DB node. (#2066) # Issue Such a following query is executed in master DB node with EngineGroup. ```go s := engineGroup.NewSession(); // create session from EngineGroup. sql := ` SELECT * FROM USER; `; type User struct { ... }; var users []User; err := s.Sql(sql).Find(&users); // executed to master DB node. ``` Reviewed-on: https://gitea.com/xorm/xorm/pulls/2066 Reviewed-by: Lunny Xiao Co-authored-by: rennnosuke Co-committed-by: rennnosuke --- session_raw.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session_raw.go b/session_raw.go index acb106a5..0d20dd60 100644 --- a/session_raw.go +++ b/session_raw.go @@ -33,7 +33,7 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row if session.isAutoCommit { var db *core.DB - if session.sessionType == groupSession && strings.EqualFold(sqlStr[:6], "select") { + if session.sessionType == groupSession && strings.EqualFold(strings.TrimSpace(sqlStr)[:6], "select") { db = session.engine.engineGroup.Slave().DB() } else { db = session.DB() From 5feff03a1744041d3e013281b4fcf642eb29d6fe Mon Sep 17 00:00:00 2001 From: RenKanai Date: Fri, 29 Oct 2021 17:31:07 +0800 Subject: [PATCH 032/116] Fix README.md: Sync2 -> Sync (#2065) Co-authored-by: rennnosuke Co-authored-by: RenKanai Reviewed-on: https://gitea.com/xorm/xorm/pulls/2065 Reviewed-by: Lunny Xiao Co-authored-by: RenKanai Co-committed-by: RenKanai --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c85938a..60a4c7ff 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Firstly, we should new an engine for a database. engine, err := xorm.NewEngine(driverName, dataSourceName) ``` -* Define a struct and Sync2 table struct to database +* Define a struct and Sync table struct to database ```Go type User struct { From a22f5dce838b9cf41de0e8778570e6f147cfbab7 Mon Sep 17 00:00:00 2001 From: stepbystep2 Date: Tue, 9 Nov 2021 17:55:37 +0800 Subject: [PATCH 033/116] =?UTF-8?q?=E5=B0=91=E4=BA=86=E4=B8=AA`.`=20(#2068?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://gitea.com/xorm/xorm/pulls/2068 Reviewed-by: Lunny Xiao Co-authored-by: stepbystep2 Co-committed-by: stepbystep2 --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index f2445019..78ff34ee 100644 --- a/README_CN.md +++ b/README_CN.md @@ -214,7 +214,7 @@ type UserDetail struct { } var users []UserDetail -err := engine.Table("user").Select("user.*, detail.*") +err := engine.Table("user").Select("user.*, detail.*"). Join("INNER", "detail", "detail.user_id = user.id"). Where("user.name = ?", name).Limit(10, 0). Find(&users) From aea91cc7dede96f071514cea007bd792613ae748 Mon Sep 17 00:00:00 2001 From: fanybook Date: Fri, 12 Nov 2021 20:58:05 +0800 Subject: [PATCH 034/116] =?UTF-8?q?add=20table=20&=20column=20comment=20fo?= =?UTF-8?q?r=20postgres=EF=BC=88add=20table=20comment=20for=20mysql?= =?UTF-8?q?=EF=BC=89=20(#2067)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 让 postgres 支持字段注释,只在 v1.2.5 上测试过(不知道怎么 import master) 发现 master 分支好像大改了?模式表名带 schema 了 使用方式和 mysql 相同 ```go type User struct { Id int64 `xorm:"pk autoincr"` Name string `json:"name" xorm:"not null default '' varchar(50) index(name_age) comment('用户 (it''s) 1; 名')"` Salt string Age int `json:"age" xorm:"not null default 0 int(10) index(name_age) comment('年龄')"` Passwd string `xorm:"varchar(200)"` CreatedAt time.Time `xorm:"created"` UpdatedAt time.Time `xorm:"updated"` } _ = engine.Sync(new(User)) func (model User) TableComment() string { return "表注释" } ``` Co-authored-by: fanybook Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2067 Co-authored-by: fanybook Co-committed-by: fanybook --- dialects/dialect.go | 2 +- dialects/mysql.go | 6 +++++ dialects/postgres.go | 54 ++++++++++++++++++++++++++++++++++++++++---- names/table_name.go | 47 ++++++++++++++++++++++++++++++++++++-- session_schema.go | 2 ++ tags/parser.go | 1 + tags/parser_test.go | 44 ++++++++++++++++++++++++++++++++++++ 7 files changed, 148 insertions(+), 8 deletions(-) diff --git a/dialects/dialect.go b/dialects/dialect.go index f3aa7470..2d772411 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -206,7 +206,7 @@ func (db *Base) IsColumnExist(queryer core.Queryer, ctx context.Context, tableNa // AddColumnSQL returns a SQL to add a column func (db *Base) AddColumnSQL(tableName string, col *schemas.Column) string { s, _ := ColumnString(db.dialect, col, true) - return fmt.Sprintf("ALTER TABLE %v ADD %v", db.dialect.Quoter().Quote(tableName), s) + return fmt.Sprintf("ALTER TABLE %s ADD %s", db.dialect.Quoter().Quote(tableName), s) } // CreateIndexSQL returns a SQL to create index diff --git a/dialects/mysql.go b/dialects/mysql.go index ce3bd705..9cc695ef 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -685,6 +685,12 @@ func (db *mysql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table b.WriteString(db.rowFormat) } + if table.Comment != "" { + b.WriteString(" COMMENT='") + b.WriteString(table.Comment) + b.WriteString("'") + } + return b.String(), true, nil } diff --git a/dialects/postgres.go b/dialects/postgres.go index 1e99cd9d..3595f6c5 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -991,13 +991,37 @@ func (db *postgres) IsTableExist(queryer core.Queryer, ctx context.Context, tabl db.getSchema(), tableName) } -func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string { +func (db *postgres) AddColumnSQL(tableName string, col *schemas.Column) string { + s, _ := ColumnString(db.dialect, col, true) + + quoter := db.dialect.Quoter() + addColumnSQL := "" + commentSQL := "; " if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") { - return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", - db.quoter.Quote(tableName), db.quoter.Quote(col.Name), db.SQLType(col)) + addColumnSQL = fmt.Sprintf("ALTER TABLE %s ADD %s", quoter.Quote(tableName), s) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + return addColumnSQL + commentSQL } - return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s", - db.quoter.Quote(db.getSchema()), db.quoter.Quote(tableName), db.quoter.Quote(col.Name), db.SQLType(col)) + + addColumnSQL = fmt.Sprintf("ALTER TABLE %s.%s ADD %s", quoter.Quote(db.getSchema()), quoter.Quote(tableName), s) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s.%s IS '%s'", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + return addColumnSQL + commentSQL +} + +func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string { + quoter := db.dialect.Quoter() + modifyColumnSQL := "" + commentSQL := "; " + + if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") { + modifyColumnSQL = fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s TYPE %s", quoter.Quote(tableName), quoter.Quote(col.Name), db.SQLType(col)) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + return modifyColumnSQL + commentSQL + } + + modifyColumnSQL = fmt.Sprintf("ALTER TABLE %s.%s ALTER COLUMN %s TYPE %s", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), db.SQLType(col)) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s.%s IS '%s'", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + return modifyColumnSQL + commentSQL } func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string { @@ -1302,6 +1326,26 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN return indexes, nil } +func (db *postgres) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { + quoter := db.dialect.Quoter() + if len(db.getSchema()) != 0 && !strings.Contains(tableName, ".") { + tableName = fmt.Sprintf("%s.%s", db.getSchema(), tableName) + } + + createTableSQL, ok, err := db.Base.CreateTableSQL(ctx, queryer, table, tableName) + if err != nil { + return "", ok, err + } + + commentSql := "; " + if table.Comment != "" { + // support schema.table -> "schema"."table" + commentSql += fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", quoter.Quote(tableName), table.Comment) + } + + return createTableSQL + commentSql, true, nil +} + func (db *postgres) Filters() []Filter { return []Filter{&SeqFilter{Prefix: "$", Start: 1}} } diff --git a/names/table_name.go b/names/table_name.go index cc0e9274..d7d71b51 100644 --- a/names/table_name.go +++ b/names/table_name.go @@ -14,9 +14,15 @@ type TableName interface { TableName() string } +type TableComment interface { + TableComment() string +} + var ( - tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() - tvCache sync.Map + tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() + tpTableComment = reflect.TypeOf((*TableComment)(nil)).Elem() + tvCache sync.Map + tcCache sync.Map ) // GetTableName returns table name @@ -55,3 +61,40 @@ func GetTableName(mapper Mapper, v reflect.Value) string { return mapper.Obj2Table(v.Type().Name()) } + +// GetTableComment returns table comment +func GetTableComment(v reflect.Value) string { + if v.Type().Implements(tpTableComment) { + return v.Interface().(TableComment).TableComment() + } + + if v.Kind() == reflect.Ptr { + v = v.Elem() + if v.Type().Implements(tpTableComment) { + return v.Interface().(TableComment).TableComment() + } + } else if v.CanAddr() { + v1 := v.Addr() + if v1.Type().Implements(tpTableComment) { + return v1.Interface().(TableComment).TableComment() + } + } else { + comment, ok := tcCache.Load(v.Type()) + if ok { + if comment.(string) != "" { + return comment.(string) + } + } else { + v2 := reflect.New(v.Type()) + if v2.Type().Implements(tpTableComment) { + tableComment := v2.Interface().(TableComment).TableComment() + tcCache.Store(v.Type(), tableComment) + return tableComment + } + + tcCache.Store(v.Type(), "") + } + } + + return "" +} diff --git a/session_schema.go b/session_schema.go index e9ed9ec5..75140426 100644 --- a/session_schema.go +++ b/session_schema.go @@ -394,6 +394,8 @@ func (session *Session) Sync(beans ...interface{}) error { _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) } } + } else if col.Comment != oriCol.Comment { + _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) } if col.Default != oriCol.Default { diff --git a/tags/parser.go b/tags/parser.go index efee11e7..83026862 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -316,6 +316,7 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) { table := schemas.NewEmptyTable() table.Type = t table.Name = names.GetTableName(parser.tableMapper, v) + table.Comment = names.GetTableComment(v) for i := 0; i < t.NumField(); i++ { col, err := parser.parseField(table, i, t.Field(i), v.Field(i)) diff --git a/tags/parser_test.go b/tags/parser_test.go index 70c57692..83c81a1e 100644 --- a/tags/parser_test.go +++ b/tags/parser_test.go @@ -26,6 +26,20 @@ func (p ParseTableName2) TableName() string { return "p_parseTableName" } +type ParseTableComment struct{} + +type ParseTableComment1 struct{} + +type ParseTableComment2 struct{} + +func (p ParseTableComment1) TableComment() string { + return "p_parseTableComment1" +} + +func (p *ParseTableComment2) TableComment() string { + return "p_parseTableComment2" +} + func TestParseTableName(t *testing.T) { parser := NewParser( "xorm", @@ -47,6 +61,36 @@ func TestParseTableName(t *testing.T) { assert.EqualValues(t, "p_parseTableName", table.Name) } +func TestParseTableComment(t *testing.T) { + parser := NewParser( + "xorm", + dialects.QueryDialect("mysql"), + names.SnakeMapper{}, + names.SnakeMapper{}, + caches.NewManager(), + ) + + table, err := parser.Parse(reflect.ValueOf(new(ParseTableComment))) + assert.NoError(t, err) + assert.EqualValues(t, "", table.Comment) + + table, err = parser.Parse(reflect.ValueOf(new(ParseTableComment1))) + assert.NoError(t, err) + assert.EqualValues(t, "p_parseTableComment1", table.Comment) + + table, err = parser.Parse(reflect.ValueOf(ParseTableComment1{})) + assert.NoError(t, err) + assert.EqualValues(t, "p_parseTableComment1", table.Comment) + + table, err = parser.Parse(reflect.ValueOf(new(ParseTableComment2))) + assert.NoError(t, err) + assert.EqualValues(t, "p_parseTableComment2", table.Comment) + + table, err = parser.Parse(reflect.ValueOf(ParseTableComment2{})) + assert.NoError(t, err) + assert.EqualValues(t, "p_parseTableComment2", table.Comment) +} + func TestUnexportField(t *testing.T) { parser := NewParser( "xorm", From f35ff7c2ebd4f0442f4494dcd519f0333d5c744a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 19 Nov 2021 12:32:55 +0800 Subject: [PATCH 035/116] Make SQLType2Type support uint (#2071) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2071 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- schemas/type.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/schemas/type.go b/schemas/type.go index d192bac6..8702862a 100644 --- a/schemas/type.go +++ b/schemas/type.go @@ -332,6 +332,10 @@ func SQLType2Type(st SQLType) reflect.Type { return IntType case BigInt, BigSerial: return Int64Type + case UnsignedBit, UnsignedTinyInt, UnsignedSmallInt, UnsignedMediumInt, UnsignedInt: + return UintType + case UnsignedBigInt: + return Uint64Type case Float, Real: return Float32Type case Double: From 885f582677dd3f3bbd127924b05763e730573300 Mon Sep 17 00:00:00 2001 From: RenKanai Date: Wed, 24 Nov 2021 10:29:39 +0800 Subject: [PATCH 036/116] Fix to add session.statement.IsForUpdate check in Session.queryRows() (#2064) # Issue The 'for-update' query is executed to slave DB node. # Example ```go s := engineGroup.NewSession(); // create session from EngineGroup. ... s.ForUpdate(); type User struct { ... }; var user User; has, err := s.Get(&user); // executed to slave DB node. ... ``` Co-authored-by: rennnosuke Co-authored-by: RenKanai Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2064 Reviewed-by: Lunny Xiao Co-authored-by: RenKanai Co-committed-by: RenKanai --- session_raw.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session_raw.go b/session_raw.go index 0d20dd60..add584d0 100644 --- a/session_raw.go +++ b/session_raw.go @@ -33,7 +33,7 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row if session.isAutoCommit { var db *core.DB - if session.sessionType == groupSession && strings.EqualFold(strings.TrimSpace(sqlStr)[:6], "select") { + if session.sessionType == groupSession && strings.EqualFold(strings.TrimSpace(sqlStr)[:6], "select") && !session.statement.IsForUpdate { db = session.engine.engineGroup.Slave().DB() } else { db = session.DB() From 4f8f829913c02a087d80ee30519e6c7c22e5e29c Mon Sep 17 00:00:00 2001 From: linbaozhong Date: Wed, 1 Dec 2021 13:54:47 +0800 Subject: [PATCH 037/116] =?UTF-8?q?exist=E6=96=B9=E6=B3=95sql=E8=AF=AD?= =?UTF-8?q?=E5=8F=A5=E4=BC=98=E5=8C=96=20(#2075)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本地没有mssql和oracle,所以没法测试,但是,mysql 使用select 1 from TABLENAME where CONDITION limit 1可能更好 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2075 Reviewed-by: Lunny Xiao Co-authored-by: linbaozhong Co-committed-by: linbaozhong --- internal/statements/query.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/statements/query.go b/internal/statements/query.go index c1ff8833..dd38a9a2 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -392,7 +392,7 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac } else if statement.dialect.URI().DBType == schemas.ORACLE { sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE (%s) %s AND ROWNUM=1", tableName, joinStr, condSQL) } else { - sqlStr = fmt.Sprintf("SELECT * FROM %s %s WHERE %s LIMIT 1", tableName, joinStr, condSQL) + sqlStr = fmt.Sprintf("SELECT 1 FROM %s %s WHERE %s LIMIT 1", tableName, joinStr, condSQL) } args = condArgs } else { @@ -401,7 +401,7 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac } else if statement.dialect.URI().DBType == schemas.ORACLE { sqlStr = fmt.Sprintf("SELECT * FROM %s %s WHERE ROWNUM=1", tableName, joinStr) } else { - sqlStr = fmt.Sprintf("SELECT * FROM %s %s LIMIT 1", tableName, joinStr) + sqlStr = fmt.Sprintf("SELECT 1 FROM %s %s LIMIT 1", tableName, joinStr) } args = []interface{}{} } From 303b1aeb7772ebd6253c72ba4853ce8cd3dadb44 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 6 Dec 2021 13:13:47 +0800 Subject: [PATCH 038/116] Upgrade dependencies modules (#2078) - github.com/goccy/go-json v0.7.4 -> v0.8.1 - github.com/json-iterator/go v1.1.11 -> v1.1.12 - github.com/mattn/go-sqlite3 v1.14.8 -> v1.14.9 - modernc.org/sqlite v1.11.2 -> v1.14.2 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2078 Co-authored-by: Lunny Xiao Co-committed-by: Lunny Xiao --- go.mod | 9 ++-- go.sum | 133 +++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 115 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 98b5617c..0764d73a 100644 --- a/go.mod +++ b/go.mod @@ -6,15 +6,16 @@ require ( gitee.com/travelliu/dm v1.8.11192 github.com/denisenkom/go-mssqldb v0.10.0 github.com/go-sql-driver/mysql v1.6.0 - github.com/goccy/go-json v0.7.4 + github.com/goccy/go-json v0.8.1 + github.com/golang/snappy v0.0.4 // indirect github.com/jackc/pgx/v4 v4.12.0 - github.com/json-iterator/go v1.1.11 + github.com/json-iterator/go v1.1.12 github.com/lib/pq v1.10.2 - github.com/mattn/go-sqlite3 v1.14.8 + github.com/mattn/go-sqlite3 v1.14.9 github.com/shopspring/decimal v1.2.0 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 github.com/ziutek/mymysql v1.5.4 - modernc.org/sqlite v1.11.2 + modernc.org/sqlite v1.14.2 xorm.io/builder v0.3.9 ) diff --git a/go.sum b/go.sum index 4596f326..8e7ac44b 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k= -github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI= +github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -98,8 +98,9 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -110,6 +111,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -206,8 +209,8 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= @@ -245,9 +248,8 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= -github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -261,8 +263,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -460,9 +463,10 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -542,34 +546,117 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.33.6 h1:r63dgSzVzRxUpAJFPQWHy1QeZeY1ydNENUDaBx1GqYc= modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/ccgo/v3 v3.9.5 h1:dEuUSf8WN51rDkprFuAqjfchKEzN0WttP/Py3enBwjk= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8= +modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.11 h1:QUxZMs48Ahg2F7SN41aERvMfGLY2HU/ADnB9DC4Yts8= modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.0 h1:GCjoRaBew8ECCKINQA2nYjzvufFW9YiEuuB+rQ9bn2E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.11.2 h1:ShWQpeD3ag/bmx6TqidBlIWonWmQaSQKls3aenCbt+w= -modernc.org/sqlite v1.11.2/go.mod h1:+mhs/P1ONd+6G7hcAs6irwDi/bjTQ7nLW6LHRBsEa3A= +modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE= +modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8= modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/tcl v1.5.5 h1:N03RwthgTR/l/eQvz3UjfYnvVVj1G2sZqzFGfoD4HE4= -modernc.org/tcl v1.5.5/go.mod h1:ADkaTUuwukkrlhqwERyq0SM8OvyXo7+TjFz7yAF56EI= +modernc.org/tcl v1.8.13 h1:V0sTNBw0Re86PvXZxuCub3oO9WrSTqALgrwNZNvLFGw= +modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY= modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc= -modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= +modernc.org/z v1.2.19 h1:BGyRFWhDVn5LFS5OcX4Yd/MlpRTOc7hOPTdcIpCiUao= +modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc= From a2d3669edf9201a1dad307d8505de315211ab07a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 14 Dec 2021 09:00:35 +0800 Subject: [PATCH 039/116] Add README and fix some lints (#2079) as title. Reviewed-on: https://gitea.com/xorm/xorm/pulls/2079 --- .golangci.yml | 24 ++++++++++++++++ Makefile | 22 +++++++++++++-- README.md | 18 ++++++++++++ README_CN.md | 18 ++++++++++++ caches/encode.go | 6 ++-- caches/lru.go | 24 +++++++--------- contexts/hook.go | 2 +- core/db.go | 2 +- core/db_test.go | 14 +++++----- core/rows.go | 2 +- dialects/dameng.go | 40 ++++++++++++++++++++------- dialects/dialect.go | 6 +++- dialects/mysql.go | 24 +++++----------- dialects/postgres.go | 12 ++++---- dialects/table_name.go | 21 ++++++-------- engine.go | 10 ++++--- integrations/engine_test.go | 6 ++-- integrations/processors_test.go | 4 +-- integrations/session_insert_test.go | 2 +- integrations/session_pk_test.go | 16 +++++------ integrations/session_schema_test.go | 2 +- integrations/session_update_test.go | 14 ++++++---- integrations/tags_test.go | 6 ++-- integrations/tests.go | 6 ++-- internal/statements/query.go | 12 ++++++-- internal/statements/statement.go | 18 ++---------- internal/statements/statement_test.go | 2 ++ internal/utils/slice.go | 1 + log/logger.go | 16 +++++------ log/syslogger.go | 17 ++++++------ migrate/migrate.go | 2 +- migrate/migrate_test.go | 2 +- rows.go | 2 +- scan.go | 6 ++++ schemas/index.go | 2 +- schemas/quote.go | 10 ++++--- schemas/quote_test.go | 11 ++++---- session.go | 2 +- session_delete.go | 2 +- session_find.go | 7 +++-- session_get.go | 11 +------- session_insert.go | 29 ++++++++++--------- session_iterate.go | 2 +- session_schema.go | 18 ------------ session_tx.go | 2 +- session_update.go | 11 ++++---- 46 files changed, 278 insertions(+), 208 deletions(-) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..7b91f22d --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,24 @@ +linters: + enable: + - gosimple + - deadcode + - typecheck + - govet + - errcheck + - staticcheck + - unused + - structcheck + - varcheck + - dupl + #- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time. + - gofmt + - misspell + - gocritic + - bidichk + - ineffassign + enable-all: false + disable-all: true + fast: false + +run: + timeout: 3m \ No newline at end of file diff --git a/Makefile b/Makefile index ba5427d6..220c8592 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ help: @echo " - build creates the entire project" @echo " - clean delete integration files and build files but not css and js files" @echo " - fmt format the code" - @echo " - lint run code linter revive" + @echo " - lint run code linter" @echo " - misspell check if a word is written wrong" @echo " - test run default unit test" @echo " - test-cockroach run integration tests for cockroach" @@ -111,7 +111,25 @@ help: @echo " - vet examines Go source code and reports suspicious constructs" .PHONY: lint -lint: revive +lint: golangci-lint + +.PHONY: golangci-lint +golangci-lint: golangci-lint-check + golangci-lint run --timeout 10m + +.PHONY: golangci-lint-check +golangci-lint-check: + $(eval GOLANGCI_LINT_VERSION := $(shell printf "%03d%03d%03d" $(shell golangci-lint --version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');)) + $(eval MIN_GOLANGCI_LINT_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_GOLANGCI_LINT_VERSION) | grep -o ...))) + @hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ + echo "Downloading golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \ + export BINARY="golangci-lint"; \ + curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \ + elif [ "$(GOLANGCI_LINT_VERSION)" -lt "$(MIN_GOLANGCI_LINT_VERSION)" ]; then \ + echo "Downloading newer version of golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \ + export BINARY="golangci-lint"; \ + curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \ + fi .PHONY: revive revive: diff --git a/README.md b/README.md index 60a4c7ff..ccf49348 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,24 @@ affected, err := engine.Insert(&users) affected, err := engine.Insert(&user1, &users) // INSERT INTO struct1 () values () // INSERT INTO struct2 () values (),(),() + +affected, err := engine.Table("user").Insert(map[string]interface{}{ + "name": "lunny", + "age": 18, +}) +// INSERT INTO user (name, age) values (?,?) + +affected, err := engine.Table("user").Insert([]map[string]interface{}{ + { + "name": "lunny", + "age": 18, + }, + { + "name": "lunny2", + "age": 19, + }, +}) +// INSERT INTO user (name, age) values (?,?),(?,?) ``` * `Get` query one record from database diff --git a/README_CN.md b/README_CN.md index 78ff34ee..a5aaae66 100644 --- a/README_CN.md +++ b/README_CN.md @@ -138,6 +138,24 @@ affected, err := engine.Insert(&users) affected, err := engine.Insert(&user1, &users) // INSERT INTO struct1 () values () // INSERT INTO struct2 () values (),(),() + +affected, err := engine.Table("user").Insert(map[string]interface{}{ + "name": "lunny", + "age": 18, +}) +// INSERT INTO user (name, age) values (?,?) + +affected, err := engine.Table("user").Insert([]map[string]interface{}{ + { + "name": "lunny", + "age": 18, + }, + { + "name": "lunny2", + "age": 19, + }, +}) +// INSERT INTO user (name, age) values (?,?),(?,?) ``` * `Get` 查询单条记录 diff --git a/caches/encode.go b/caches/encode.go index 95536d7e..8659668c 100644 --- a/caches/encode.go +++ b/caches/encode.go @@ -16,19 +16,19 @@ import ( // Md5 return md5 hash string func Md5(str string) string { m := md5.New() - io.WriteString(m, str) + _, _ = io.WriteString(m, str) return fmt.Sprintf("%x", m.Sum(nil)) } // Encode Encode data func Encode(data interface{}) ([]byte, error) { - //return JsonEncode(data) + // return JsonEncode(data) return GobEncode(data) } // Decode decode data func Decode(data []byte, to interface{}) error { - //return JsonDecode(data, to) + // return JsonDecode(data, to) return GobDecode(data, to) } diff --git a/caches/lru.go b/caches/lru.go index 6b45ac94..885f02d6 100644 --- a/caches/lru.go +++ b/caches/lru.go @@ -56,7 +56,7 @@ func (m *LRUCacher) GC() { var removedNum int for e := m.idList.Front(); e != nil; { if removedNum <= CacheGcMaxRemoved && - time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired { + time.Since(e.Value.(*idNode).lastVisit) > m.Expired { removedNum++ next := e.Next() node := e.Value.(*idNode) @@ -70,7 +70,7 @@ func (m *LRUCacher) GC() { removedNum = 0 for e := m.sqlList.Front(); e != nil; { if removedNum <= CacheGcMaxRemoved && - time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired { + time.Since(e.Value.(*sqlNode).lastVisit) > m.Expired { removedNum++ next := e.Next() node := e.Value.(*sqlNode) @@ -96,7 +96,7 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} { } else { lastTime := el.Value.(*sqlNode).lastVisit // if expired, remove the node and return nil - if time.Now().Sub(lastTime) > m.Expired { + if time.Since(lastTime) > m.Expired { m.delIds(tableName, sql) return nil } @@ -122,7 +122,7 @@ func (m *LRUCacher) GetBean(tableName string, id string) interface{} { if el, ok := m.idIndex[tableName][id]; ok { lastTime := el.Value.(*idNode).lastVisit // if expired, remove the node and return nil - if time.Now().Sub(lastTime) > m.Expired { + if time.Since(lastTime) > m.Expired { m.delBean(tableName, id) return nil } @@ -145,7 +145,7 @@ func (m *LRUCacher) clearIds(tableName string) { if tis, ok := m.sqlIndex[tableName]; ok { for sql, v := range tis { m.sqlList.Remove(v) - m.store.Del(sql) + _ = m.store.Del(sql) } } m.sqlIndex[tableName] = make(map[string]*list.Element) @@ -163,7 +163,7 @@ func (m *LRUCacher) clearBeans(tableName string) { for id, v := range tis { m.idList.Remove(v) tid := genID(tableName, id) - m.store.Del(tid) + _ = m.store.Del(tid) } } m.idIndex[tableName] = make(map[string]*list.Element) @@ -188,7 +188,7 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { } else { el.Value.(*sqlNode).lastVisit = time.Now() } - m.store.Put(sql, ids) + _ = m.store.Put(sql, ids) if m.sqlList.Len() > m.MaxElementSize { e := m.sqlList.Front() node := e.Value.(*sqlNode) @@ -210,7 +210,7 @@ func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) { el.Value.(*idNode).lastVisit = time.Now() } - m.store.Put(genID(tableName, id), obj) + _ = m.store.Put(genID(tableName, id), obj) if m.idList.Len() > m.MaxElementSize { e := m.idList.Front() node := e.Value.(*idNode) @@ -226,7 +226,7 @@ func (m *LRUCacher) delIds(tableName, sql string) { m.sqlList.Remove(el) } } - m.store.Del(sql) + _ = m.store.Del(sql) } // DelIds deletes ids @@ -243,7 +243,7 @@ func (m *LRUCacher) delBean(tableName string, id string) { m.idList.Remove(el) m.clearIds(tableName) } - m.store.Del(tid) + _ = m.store.Del(tid) } // DelBean deletes beans in some table @@ -265,10 +265,6 @@ type sqlNode struct { lastVisit time.Time } -func genSQLKey(sql string, args interface{}) string { - return fmt.Sprintf("%s-%v", sql, args) -} - func genID(prefix string, id string) string { return fmt.Sprintf("%s-%s", prefix, id) } diff --git a/contexts/hook.go b/contexts/hook.go index 70f114dd..f6d86cfc 100644 --- a/contexts/hook.go +++ b/contexts/hook.go @@ -36,7 +36,7 @@ func (c *ContextHook) End(ctx context.Context, result sql.Result, err error) { c.Ctx = ctx c.Result = result c.Err = err - c.ExecuteTime = time.Now().Sub(c.start) + c.ExecuteTime = time.Since(c.start) } // Hook represents a hook behaviour diff --git a/core/db.go b/core/db.go index ef5ab227..b476ef9a 100644 --- a/core/db.go +++ b/core/db.go @@ -136,7 +136,7 @@ func (db *DB) reflectNew(typ reflect.Type) reflect.Value { cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), DefaultCacheSize, DefaultCacheSize), 0} db.reflectCache[typ] = cs } else { - cs.idx = cs.idx + 1 + cs.idx++ } return cs.value.Index(cs.idx).Addr() } diff --git a/core/db_test.go b/core/db_test.go index e9c2d82d..a9c19392 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -96,7 +96,7 @@ func BenchmarkOriQuery(b *testing.B) { if err != nil { b.Error(err) } - //fmt.Println(Id, Name, Title, Age, Alias, NickName) + // fmt.Println(Id, Name, Title, Age, Alias, NickName) } rows.Close() } @@ -245,13 +245,13 @@ func BenchmarkSliceInterfaceQuery(b *testing.B) { b.Error(err) } b.Log(slice) - switch slice[1].(type) { + switch st := slice[1].(type) { case *string: - if *slice[1].(*string) != "xlw" { + if *st != "xlw" { b.Error(errors.New("name should be xlw")) } case []byte: - if string(slice[1].([]byte)) != "xlw" { + if string(st) != "xlw" { b.Error(errors.New("name should be xlw")) } } @@ -399,14 +399,14 @@ func BenchmarkMapInterfaceQuery(b *testing.B) { if err != nil { b.Error(err) } - switch m["name"].(type) { + switch t := m["name"].(type) { case string: - if m["name"].(string) != "xlw" { + if t != "xlw" { b.Log(m) b.Error(errors.New("name should be xlw")) } case []byte: - if string(m["name"].([]byte)) != "xlw" { + if string(t) != "xlw" { b.Log(m) b.Error(errors.New("name should be xlw")) } diff --git a/core/rows.go b/core/rows.go index c15a59a3..75d6ebf0 100644 --- a/core/rows.go +++ b/core/rows.go @@ -62,7 +62,7 @@ func (rs *Rows) ScanStructByIndex(dest ...interface{}) error { for _, vvv := range vvvs { for j := 0; j < vvv.NumField(); j++ { newDest[i] = vvv.Field(j).Addr().Interface() - i = i + 1 + i++ } } diff --git a/dialects/dameng.go b/dialects/dameng.go index a8e7db70..f4a075d5 100644 --- a/dialects/dameng.go +++ b/dialects/dameng.go @@ -670,9 +670,15 @@ func (db *dameng) CreateTableSQL(ctx context.Context, queryer core.Queryer, tabl quoter := db.Quoter() var b strings.Builder - b.WriteString("CREATE TABLE ") - quoter.QuoteTo(&b, tableName) - b.WriteString(" (") + if _, err := b.WriteString("CREATE TABLE "); err != nil { + return "", false, err + } + if err := quoter.QuoteTo(&b, tableName); err != nil { + return "", false, err + } + if _, err := b.WriteString(" ("); err != nil { + return "", false, err + } pkList := table.PrimaryKeys @@ -687,21 +693,35 @@ func (db *dameng) CreateTableSQL(ctx context.Context, queryer core.Queryer, tabl } s, _ := ColumnString(db, col, false) - b.WriteString(s) + if _, err := b.WriteString(s); err != nil { + return "", false, err + } if i != len(table.ColumnsSeq())-1 { - b.WriteString(", ") + if _, err := b.WriteString(", "); err != nil { + return "", false, err + } } } if len(pkList) > 0 { if len(table.ColumnsSeq()) > 0 { - b.WriteString(", ") + if _, err := b.WriteString(", "); err != nil { + return "", false, err + } + } + if _, err := b.WriteString(fmt.Sprintf("CONSTRAINT PK_%s PRIMARY KEY (", tableName)); err != nil { + return "", false, err + } + if err := quoter.JoinWrite(&b, pkList, ","); err != nil { + return "", false, err + } + if _, err := b.WriteString(")"); err != nil { + return "", false, err } - b.WriteString(fmt.Sprintf("CONSTRAINT PK_%s PRIMARY KEY (", tableName)) - quoter.JoinWrite(&b, pkList, ",") - b.WriteString(")") } - b.WriteString(")") + if _, err := b.WriteString(")"); err != nil { + return "", false, err + } return b.String(), false, nil } diff --git a/dialects/dialect.go b/dialects/dialect.go index 2d772411..555d96c6 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -38,11 +38,13 @@ func (uri *URI) SetSchema(schema string) { } } +// enumerates all autoincr mode const ( IncrAutoincrMode = iota SequenceAutoincrMode ) +// DialectFeatures represents a dialect parameters type DialectFeatures struct { AutoincrMode int // 0 autoincrement column, 1 sequence } @@ -126,7 +128,9 @@ func (db *Base) CreateTableSQL(ctx context.Context, queryer core.Queryer, table quoter := db.dialect.Quoter() var b strings.Builder b.WriteString("CREATE TABLE IF NOT EXISTS ") - quoter.QuoteTo(&b, tableName) + if err := quoter.QuoteTo(&b, tableName); err != nil { + return "", false, err + } b.WriteString(" (") for i, colName := range table.ColumnsSeq() { diff --git a/dialects/mysql.go b/dialects/mysql.go index 9cc695ef..1fad3fee 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -6,7 +6,6 @@ package dialects import ( "context" - "crypto/tls" "database/sql" "errors" "fmt" @@ -172,16 +171,7 @@ var ( type mysql struct { Base - net string - addr string - params map[string]string - loc *time.Location - timeout time.Duration - tls *tls.Config - allowAllFiles bool - allowOldPasswords bool - clientFoundRows bool - rowFormat string + rowFormat string } func (db *mysql) Init(uri *URI) error { @@ -497,15 +487,15 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName if _, ok := schemas.SqlTypes[colType]; ok { col.SQLType = schemas.SQLType{Name: colType, DefaultLength: len1, DefaultLength2: len2} } else { - return nil, nil, fmt.Errorf("Unknown colType %v", colType) + return nil, nil, fmt.Errorf("unknown colType %v", colType) } if colKey == "PRI" { col.IsPrimaryKey = true } - if colKey == "UNI" { - // col.is - } + // if colKey == "UNI" { + // col.is + // } if extra == "auto_increment" { col.IsAutoIncrement = true @@ -785,7 +775,7 @@ func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) { // Parse protocol part of URI p := strings.SplitN(pd[0], ":", 2) if len(p) != 2 { - return nil, errors.New("Wrong protocol part of URI") + return nil, errors.New("wrong protocol part of URI") } uri.Proto = p[0] options := strings.Split(p[1], ",") @@ -808,7 +798,7 @@ func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) { } uri.Timeout = to default: - return nil, errors.New("Unknown option: " + k) + return nil, errors.New("unknown option: " + k) } } // Remove protocol part diff --git a/dialects/postgres.go b/dialects/postgres.go index 3595f6c5..76279d32 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -1206,9 +1206,7 @@ WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s A col.Default = "'" + col.Default + "'" } } else if col.SQLType.IsTime() { - if strings.HasSuffix(col.Default, "::timestamp without time zone") { - col.Default = strings.TrimSuffix(col.Default, "::timestamp without time zone") - } + col.Default = strings.TrimSuffix(col.Default, "::timestamp without time zone") } } cols[col.Name] = col @@ -1269,7 +1267,7 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN s := "SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1" if len(db.getSchema()) != 0 { args = append(args, db.getSchema()) - s = s + " AND schemaname=$2" + s += " AND schemaname=$2" } rows, err := queryer.QueryContext(ctx, s, args...) @@ -1337,13 +1335,13 @@ func (db *postgres) CreateTableSQL(ctx context.Context, queryer core.Queryer, ta return "", ok, err } - commentSql := "; " + commentSQL := "; " if table.Comment != "" { // support schema.table -> "schema"."table" - commentSql += fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", quoter.Quote(tableName), table.Comment) + commentSQL += fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", quoter.Quote(tableName), table.Comment) } - return createTableSQL + commentSql, true, nil + return createTableSQL + commentSQL, true, nil } func (db *postgres) Filters() []Filter { diff --git a/dialects/table_name.go b/dialects/table_name.go index e190cd4b..48b44de2 100644 --- a/dialects/table_name.go +++ b/dialects/table_name.go @@ -17,8 +17,7 @@ import ( func TableNameWithSchema(dialect Dialect, tableName string) string { // Add schema name as prefix of table name. // Only for postgres database. - if dialect.URI().Schema != "" && - strings.Index(tableName, ".") == -1 { + if dialect.URI().Schema != "" && !strings.Contains(tableName, ".") { return fmt.Sprintf("%s.%s", dialect.URI().Schema, tableName) } return tableName @@ -27,20 +26,18 @@ func TableNameWithSchema(dialect Dialect, tableName string) string { // TableNameNoSchema returns table name with given tableName func TableNameNoSchema(dialect Dialect, mapper names.Mapper, tableName interface{}) string { quote := dialect.Quoter().Quote - switch tableName.(type) { + switch tt := tableName.(type) { case []string: - t := tableName.([]string) - if len(t) > 1 { - return fmt.Sprintf("%v AS %v", quote(t[0]), quote(t[1])) - } else if len(t) == 1 { - return quote(t[0]) + if len(tt) > 1 { + return fmt.Sprintf("%v AS %v", quote(tt[0]), quote(tt[1])) + } else if len(tt) == 1 { + return quote(tt[0]) } case []interface{}: - t := tableName.([]interface{}) - l := len(t) + l := len(tt) var table string if l > 0 { - f := t[0] + f := tt[0] switch f.(type) { case string: table = f.(string) @@ -57,7 +54,7 @@ func TableNameNoSchema(dialect Dialect, mapper names.Mapper, tableName interface } } if l > 1 { - return fmt.Sprintf("%v AS %v", quote(table), quote(fmt.Sprintf("%v", t[1]))) + return fmt.Sprintf("%v AS %v", quote(table), quote(fmt.Sprintf("%v", tt[1]))) } else if l == 1 { return quote(table) } diff --git a/engine.go b/engine.go index 7a57b08a..709cc384 100644 --- a/engine.go +++ b/engine.go @@ -467,7 +467,9 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w DBName: uri.DBName, Schema: uri.Schema, } - dstDialect.Init(&destURI) + if err := dstDialect.Init(&destURI); err != nil { + return err + } } cacherMgr := caches.NewManager() dstTableCache := tags.NewParser("xorm", dstDialect, engine.GetTableMapper(), engine.GetColumnMapper(), cacherMgr) @@ -588,7 +590,7 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w return err } } else if sess.engine.dialect.URI().DBType == schemas.DAMENG && stp.IsTime() && len(s.String) == 25 { - r := strings.Replace(s.String[:19], "T", " ", -1) + r := strings.ReplaceAll(s.String[:19], "T", " ") if _, err = io.WriteString(w, "'"+r+"'"); err != nil { return err } @@ -946,7 +948,7 @@ func (engine *Engine) CreateTables(beans ...interface{}) error { for _, bean := range beans { err = session.createTable(bean) if err != nil { - session.Rollback() + _ = session.Rollback() return err } } @@ -966,7 +968,7 @@ func (engine *Engine) DropTables(beans ...interface{}) error { for _, bean := range beans { err = session.dropTable(bean) if err != nil { - session.Rollback() + _ = session.Rollback() return err } } diff --git a/integrations/engine_test.go b/integrations/engine_test.go index 74e1f903..dbe17571 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -58,12 +58,13 @@ func TestAutoTransaction(t *testing.T) { engine := testEngine.(*xorm.Engine) // will success - engine.Transaction(func(session *xorm.Session) (interface{}, error) { + _, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) { _, err := session.Insert(TestTx{Msg: "hi"}) assert.NoError(t, err) return nil, nil }) + assert.NoError(t, err) has, err := engine.Exist(&TestTx{Msg: "hi"}) assert.NoError(t, err) @@ -149,13 +150,14 @@ func TestDumpTables(t *testing.T) { assertSync(t, new(TestDumpTableStruct)) - testEngine.Insert([]TestDumpTableStruct{ + _, err := testEngine.Insert([]TestDumpTableStruct{ {Name: "1", IsMan: true}, {Name: "2\n"}, {Name: "3;"}, {Name: "4\n;\n''"}, {Name: "5'\n"}, }) + assert.NoError(t, err) fp := fmt.Sprintf("%v-table.sql", testEngine.Dialect().URI().DBType) os.Remove(fp) diff --git a/integrations/processors_test.go b/integrations/processors_test.go index b32f6fbb..4c383437 100644 --- a/integrations/processors_test.go +++ b/integrations/processors_test.go @@ -104,7 +104,7 @@ func (p *ProcessorsStruct) BeforeDelete() { } func (p *ProcessorsStruct) BeforeSet(col string, cell xorm.Cell) { - p.BeforeSetFlag = p.BeforeSetFlag + 1 + p.BeforeSetFlag++ } func (p *ProcessorsStruct) AfterInsert() { @@ -120,7 +120,7 @@ func (p *ProcessorsStruct) AfterDelete() { } func (p *ProcessorsStruct) AfterSet(col string, cell xorm.Cell) { - p.AfterSetFlag = p.AfterSetFlag + 1 + p.AfterSetFlag++ } func TestProcessors(t *testing.T) { diff --git a/integrations/session_insert_test.go b/integrations/session_insert_test.go index 394e7e51..2495c1df 100644 --- a/integrations/session_insert_test.go +++ b/integrations/session_insert_test.go @@ -101,7 +101,7 @@ func callbackLooper(datas interface{}, step int, actionFunc func(interface{}) er if err = actionFunc(tempInterface); err != nil { return } - processedLen = processedLen - step + processedLen -= step } return } diff --git a/integrations/session_pk_test.go b/integrations/session_pk_test.go index e1aa8ed4..0244937f 100644 --- a/integrations/session_pk_test.go +++ b/integrations/session_pk_test.go @@ -121,7 +121,7 @@ func TestInt16Id(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(beans)) - beans2 := make(map[int16]Int16Id, 0) + beans2 := make(map[int16]Int16Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -154,7 +154,7 @@ func TestInt32Id(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(beans)) - beans2 := make(map[int32]Int32Id, 0) + beans2 := make(map[int32]Int32Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -205,7 +205,7 @@ func TestUintId(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 3, len(beans)) - beans2 := make(map[uint]UintId, 0) + beans2 := make(map[uint]UintId) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 3, len(beans2)) @@ -239,7 +239,7 @@ func TestUint16Id(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(beans)) - beans2 := make(map[uint16]Uint16Id, 0) + beans2 := make(map[uint16]Uint16Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -273,7 +273,7 @@ func TestUint32Id(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(beans)) - beans2 := make(map[uint32]Uint32Id, 0) + beans2 := make(map[uint32]Uint32Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -310,7 +310,7 @@ func TestUint64Id(t *testing.T) { assert.EqualValues(t, 1, len(beans)) assert.EqualValues(t, *bean, beans[0]) - beans2 := make(map[uint64]Uint64Id, 0) + beans2 := make(map[uint64]Uint64Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -533,7 +533,7 @@ func TestMyIntId(t *testing.T) { assert.EqualValues(t, 1, len(beans)) assert.EqualValues(t, *bean, beans[0]) - beans2 := make(map[ID]MyIntPK, 0) + beans2 := make(map[ID]MyIntPK) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -570,7 +570,7 @@ func TestMyStringId(t *testing.T) { assert.EqualValues(t, 1, len(beans)) assert.EqualValues(t, *bean, beans[0]) - beans2 := make(map[StrID]MyStringPK, 0) + beans2 := make(map[StrID]MyStringPK) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) diff --git a/integrations/session_schema_test.go b/integrations/session_schema_test.go index c4ba4c7d..7dc0af76 100644 --- a/integrations/session_schema_test.go +++ b/integrations/session_schema_test.go @@ -328,7 +328,7 @@ func TestIsTableEmpty(t *testing.T) { Created time.Time `xorm:"created"` ILike int PageView int - From_url string + From_url string // nolint Pre_url string `xorm:"unique"` //pre view image's url Uid int64 } diff --git a/integrations/session_update_test.go b/integrations/session_update_test.go index 30183382..45338cad 100644 --- a/integrations/session_update_test.go +++ b/integrations/session_update_test.go @@ -231,14 +231,12 @@ func TestForUpdate(t *testing.T) { f := new(ForUpdate) f.Name = "updated by session1" session1.Where("`id` = ?", 1) - session1.Update(f) + _, err = session1.Update(f) + assert.NoError(t, err) // release lock err = session1.Commit() - if err != nil { - t.Error(err) - return - } + assert.NoError(t, err) wg.Wait() } @@ -253,7 +251,7 @@ func TestWithIn(t *testing.T) { assert.NoError(t, PrepareEngine()) assert.NoError(t, testEngine.Sync(new(temp3))) - testEngine.Insert(&[]temp3{ + _, err := testEngine.Insert(&[]temp3{ { Name: "user1", }, @@ -264,6 +262,7 @@ func TestWithIn(t *testing.T) { Name: "user1", }, }) + assert.NoError(t, err) cnt, err := testEngine.In("Id", 1, 2, 3, 4).Update(&temp3{Name: "aa"}, &temp3{Name: "user1"}) assert.NoError(t, err) @@ -318,6 +317,7 @@ func TestUpdate1(t *testing.T) { _, err := testEngine.Insert(&Userinfo{ Username: "user1", }) + assert.NoError(t, err) var ori Userinfo has, err := testEngine.Get(&ori) @@ -925,6 +925,7 @@ func TestDeletedUpdate(t *testing.T) { var s1 DeletedUpdatedStruct has, err := testEngine.ID(s.Id).Get(&s1) + assert.NoError(t, err) assert.EqualValues(t, true, has) cnt, err = testEngine.ID(s.Id).Delete(&DeletedUpdatedStruct{}) @@ -941,6 +942,7 @@ func TestDeletedUpdate(t *testing.T) { var s2 DeletedUpdatedStruct has, err = testEngine.ID(s.Id).Get(&s2) + assert.NoError(t, err) assert.EqualValues(t, true, has) } diff --git a/integrations/tags_test.go b/integrations/tags_test.go index c05a8d99..247a64e8 100644 --- a/integrations/tags_test.go +++ b/integrations/tags_test.go @@ -1202,7 +1202,7 @@ func TestTagTime(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, s.Created.UTC().Format("2006-01-02 15:04:05"), - strings.Replace(strings.Replace(tm, "T", " ", -1), "Z", "", -1)) + strings.ReplaceAll(strings.ReplaceAll(tm, "T", " "), "Z", "")) } func TestTagAutoIncr(t *testing.T) { @@ -1287,7 +1287,7 @@ func TestVersion1(t *testing.T) { assert.EqualValues(t, newVer.Ver, 2) newVer = new(VersionS) - has, err = testEngine.ID(ver.Id).Get(newVer) + _, err = testEngine.ID(ver.Id).Get(newVer) assert.NoError(t, err) assert.EqualValues(t, newVer.Ver, 2) } @@ -1345,7 +1345,7 @@ func TestVersion3(t *testing.T) { assert.EqualValues(t, newVer.Ver, 2) newVer = new(VersionUintS) - has, err = testEngine.ID(ver.Id).Get(newVer) + _, err = testEngine.ID(ver.Id).Get(newVer) assert.NoError(t, err) assert.EqualValues(t, newVer.Ver, 2) } diff --git a/integrations/tests.go b/integrations/tests.go index 8b14b0f4..59f4b29a 100644 --- a/integrations/tests.go +++ b/integrations/tests.go @@ -51,7 +51,7 @@ func createEngine(dbType, connStr string) error { if !*cluster { switch schemas.DBType(strings.ToLower(dbType)) { case schemas.MSSQL: - db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "master", -1)) + db, err := sql.Open(dbType, strings.ReplaceAll(connStr, "xorm_test", "master")) if err != nil { return err } @@ -61,7 +61,7 @@ func createEngine(dbType, connStr string) error { db.Close() *ignoreSelectUpdate = true case schemas.POSTGRES: - db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "postgres", -1)) + db, err := sql.Open(dbType, strings.ReplaceAll(connStr, "xorm_test", "postgres")) if err != nil { return err } @@ -90,7 +90,7 @@ func createEngine(dbType, connStr string) error { db.Close() *ignoreSelectUpdate = true case schemas.MYSQL: - db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "mysql", -1)) + db, err := sql.Open(dbType, strings.ReplaceAll(connStr, "xorm_test", "mysql")) if err != nil { return err } diff --git a/internal/statements/query.go b/internal/statements/query.go index dd38a9a2..16253417 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -79,7 +79,9 @@ func (statement *Statement) GenSumSQL(bean interface{}, columns ...string) (stri return statement.GenRawSQL(), statement.RawParams, nil } - statement.SetRefBean(bean) + if err := statement.SetRefBean(bean); err != nil { + return "", nil, err + } var sumStrs = make([]string, 0, len(columns)) for _, colName := range columns { @@ -111,7 +113,9 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{}, v := rValue(bean) isStruct = v.Kind() == reflect.Struct if isStruct { - statement.SetRefBean(bean) + if err := statement.SetRefBean(bean); err != nil { + return "", nil, err + } } } @@ -168,7 +172,9 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa var condArgs []interface{} var err error if len(beans) > 0 { - statement.SetRefBean(beans[0]) + if err := statement.SetRefBean(beans[0]); err != nil { + return "", nil, err + } if err := statement.mergeConds(beans[0]); err != nil { return "", nil, err } diff --git a/internal/statements/statement.go b/internal/statements/statement.go index c9882a47..2a7ae8b0 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -473,7 +473,7 @@ func (statement *Statement) Desc(colNames ...string) *Statement { if i > 0 { fmt.Fprint(&buf, ", ") } - statement.dialect.Quoter().QuoteTo(&buf, col) + _ = statement.dialect.Quoter().QuoteTo(&buf, col) fmt.Fprint(&buf, " DESC") } statement.OrderStr = buf.String() @@ -490,7 +490,7 @@ func (statement *Statement) Asc(colNames ...string) *Statement { if i > 0 { fmt.Fprint(&buf, ", ") } - statement.dialect.Quoter().QuoteTo(&buf, col) + _ = statement.dialect.Quoter().QuoteTo(&buf, col) fmt.Fprint(&buf, " ASC") } statement.OrderStr = buf.String() @@ -558,7 +558,7 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true) if !utils.IsSubQuery(tbName) { var buf strings.Builder - statement.dialect.Quoter().QuoteTo(&buf, tbName) + _ = statement.dialect.Quoter().QuoteTo(&buf, tbName) tbName = buf.String() } else { tbName = statement.ReplaceQuote(tbName) @@ -571,15 +571,6 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition return statement } -// tbNameNoSchema get some table's table name -func (statement *Statement) tbNameNoSchema(table *schemas.Table) string { - if len(statement.AltTableName) > 0 { - return statement.AltTableName - } - - return table.Name -} - // GroupBy generate "Group By keys" statement func (statement *Statement) GroupBy(keys string) *Statement { statement.GroupByStr = statement.ReplaceQuote(keys) @@ -857,9 +848,6 @@ func (statement *Statement) buildConds2(table *schemas.Table, bean interface{}, fieldValuePtr, err := col.ValueOf(bean) if err != nil { - if !strings.Contains(err.Error(), "is not valid") { - //engine.logger.Warn(err) - } continue } else if fieldValuePtr == nil { continue diff --git a/internal/statements/statement_test.go b/internal/statements/statement_test.go index abe0e0f2..31428efa 100644 --- a/internal/statements/statement_test.go +++ b/internal/statements/statement_test.go @@ -5,6 +5,7 @@ package statements import ( + "os" "reflect" "strings" "testing" @@ -37,6 +38,7 @@ func TestMain(m *testing.M) { panic("tags parser is nil") } m.Run() + os.Exit(0) } var colStrTests = []struct { diff --git a/internal/utils/slice.go b/internal/utils/slice.go index 06a1a006..82289b1a 100644 --- a/internal/utils/slice.go +++ b/internal/utils/slice.go @@ -21,6 +21,7 @@ func SliceEq(left, right []string) bool { return true } +// IndexSlice search c in slice s and return the index, return -1 if s don't contain c func IndexSlice(s []string, c string) int { for i, ss := range s { if c == ss { diff --git a/log/logger.go b/log/logger.go index 3b6db34e..b8798c3f 100644 --- a/log/logger.go +++ b/log/logger.go @@ -130,56 +130,56 @@ func NewSimpleLogger3(out io.Writer, prefix string, flag int, l LogLevel) *Simpl // Error implement ILogger func (s *SimpleLogger) Error(v ...interface{}) { if s.level <= LOG_ERR { - s.ERR.Output(2, fmt.Sprintln(v...)) + _ = s.ERR.Output(2, fmt.Sprintln(v...)) } } // Errorf implement ILogger func (s *SimpleLogger) Errorf(format string, v ...interface{}) { if s.level <= LOG_ERR { - s.ERR.Output(2, fmt.Sprintf(format, v...)) + _ = s.ERR.Output(2, fmt.Sprintf(format, v...)) } } // Debug implement ILogger func (s *SimpleLogger) Debug(v ...interface{}) { if s.level <= LOG_DEBUG { - s.DEBUG.Output(2, fmt.Sprintln(v...)) + _ = s.DEBUG.Output(2, fmt.Sprintln(v...)) } } // Debugf implement ILogger func (s *SimpleLogger) Debugf(format string, v ...interface{}) { if s.level <= LOG_DEBUG { - s.DEBUG.Output(2, fmt.Sprintf(format, v...)) + _ = s.DEBUG.Output(2, fmt.Sprintf(format, v...)) } } // Info implement ILogger func (s *SimpleLogger) Info(v ...interface{}) { if s.level <= LOG_INFO { - s.INFO.Output(2, fmt.Sprintln(v...)) + _ = s.INFO.Output(2, fmt.Sprintln(v...)) } } // Infof implement ILogger func (s *SimpleLogger) Infof(format string, v ...interface{}) { if s.level <= LOG_INFO { - s.INFO.Output(2, fmt.Sprintf(format, v...)) + _ = s.INFO.Output(2, fmt.Sprintf(format, v...)) } } // Warn implement ILogger func (s *SimpleLogger) Warn(v ...interface{}) { if s.level <= LOG_WARNING { - s.WARN.Output(2, fmt.Sprintln(v...)) + _ = s.WARN.Output(2, fmt.Sprintln(v...)) } } // Warnf implement ILogger func (s *SimpleLogger) Warnf(format string, v ...interface{}) { if s.level <= LOG_WARNING { - s.WARN.Output(2, fmt.Sprintf(format, v...)) + _ = s.WARN.Output(2, fmt.Sprintf(format, v...)) } } diff --git a/log/syslogger.go b/log/syslogger.go index 0b3e381c..44272586 100644 --- a/log/syslogger.go +++ b/log/syslogger.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows && !nacl && !plan9 // +build !windows,!nacl,!plan9 package log @@ -26,42 +27,42 @@ func NewSyslogLogger(w *syslog.Writer) *SyslogLogger { // Debug log content as Debug func (s *SyslogLogger) Debug(v ...interface{}) { - s.w.Debug(fmt.Sprint(v...)) + _ = s.w.Debug(fmt.Sprint(v...)) } // Debugf log content as Debug and format func (s *SyslogLogger) Debugf(format string, v ...interface{}) { - s.w.Debug(fmt.Sprintf(format, v...)) + _ = s.w.Debug(fmt.Sprintf(format, v...)) } // Error log content as Error func (s *SyslogLogger) Error(v ...interface{}) { - s.w.Err(fmt.Sprint(v...)) + _ = s.w.Err(fmt.Sprint(v...)) } // Errorf log content as Errorf and format func (s *SyslogLogger) Errorf(format string, v ...interface{}) { - s.w.Err(fmt.Sprintf(format, v...)) + _ = s.w.Err(fmt.Sprintf(format, v...)) } // Info log content as Info func (s *SyslogLogger) Info(v ...interface{}) { - s.w.Info(fmt.Sprint(v...)) + _ = s.w.Info(fmt.Sprint(v...)) } // Infof log content as Infof and format func (s *SyslogLogger) Infof(format string, v ...interface{}) { - s.w.Info(fmt.Sprintf(format, v...)) + _ = s.w.Info(fmt.Sprintf(format, v...)) } // Warn log content as Warn func (s *SyslogLogger) Warn(v ...interface{}) { - s.w.Warning(fmt.Sprint(v...)) + _ = s.w.Warning(fmt.Sprint(v...)) } // Warnf log content as Warnf and format func (s *SyslogLogger) Warnf(format string, v ...interface{}) { - s.w.Warning(fmt.Sprintf(format, v...)) + _ = s.w.Warning(fmt.Sprintf(format, v...)) } // Level shows log level diff --git a/migrate/migrate.go b/migrate/migrate.go index 19b4afe0..5c259627 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -203,7 +203,7 @@ func (m *Migrate) migrationDidRun(mig *Migration) (bool, error) { func (m *Migrate) isFirstRun() bool { row := m.db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s", m.options.TableName)) var count int - row.Scan(&count) + _ = row.Scan(&count) return count == 0 } diff --git a/migrate/migrate_test.go b/migrate/migrate_test.go index 8f15a025..750afb28 100644 --- a/migrate/migrate_test.go +++ b/migrate/migrate_test.go @@ -142,6 +142,6 @@ func TestMissingID(t *testing.T) { func tableCount(db *xorm.Engine, tableName string) (count int) { row := db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName)) - row.Scan(&count) + _ = row.Scan(&count) return } diff --git a/rows.go b/rows.go index 110f41b6..4801c300 100644 --- a/rows.go +++ b/rows.go @@ -40,7 +40,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { return nil, err } - if len(session.statement.TableName()) <= 0 { + if len(session.statement.TableName()) == 0 { return nil, ErrTableNotFound } diff --git a/scan.go b/scan.go index d562abbc..10988bdb 100644 --- a/scan.go +++ b/scan.go @@ -235,6 +235,7 @@ func (engine *Engine) row2mapInterface(rows *core.Rows, types []*sql.ColumnType, return resultsMap, nil } +// ScanInterfaceMap scan result from *core.Rows and return a map func (engine *Engine) ScanInterfaceMap(rows *core.Rows) (map[string]interface{}, error) { fields, err := rows.Columns() if err != nil { @@ -248,6 +249,7 @@ func (engine *Engine) ScanInterfaceMap(rows *core.Rows) (map[string]interface{}, return engine.row2mapInterface(rows, types, fields) } +// ScanInterfaceMaps scan results from *core.Rows and return a slice of map func (engine *Engine) ScanInterfaceMaps(rows *core.Rows) (resultsSlice []map[string]interface{}, err error) { fields, err := rows.Columns() if err != nil { @@ -309,6 +311,7 @@ func (engine *Engine) row2mapStr(rows *core.Rows, types []*sql.ColumnType, field return result, nil } +// ScanStringMap scan results from *core.Rows and return a map func (engine *Engine) ScanStringMap(rows *core.Rows) (map[string]string, error) { fields, err := rows.Columns() if err != nil { @@ -321,6 +324,7 @@ func (engine *Engine) ScanStringMap(rows *core.Rows) (map[string]string, error) return engine.row2mapStr(rows, types, fields) } +// ScanStringMaps scan results from *core.Rows and return a slice of map func (engine *Engine) ScanStringMaps(rows *core.Rows) (resultsSlice []map[string]string, err error) { fields, err := rows.Columns() if err != nil { @@ -395,6 +399,7 @@ func (engine *Engine) row2sliceStr(rows *core.Rows, types []*sql.ColumnType, fie return results, nil } +// ScanStringSlice scan results from *core.Rows and return a slice of one row func (engine *Engine) ScanStringSlice(rows *core.Rows) ([]string, error) { fields, err := rows.Columns() if err != nil { @@ -408,6 +413,7 @@ func (engine *Engine) ScanStringSlice(rows *core.Rows) ([]string, error) { return engine.row2sliceStr(rows, types, fields) } +// ScanStringSlices scan results from *core.Rows and return a slice of all rows func (engine *Engine) ScanStringSlices(rows *core.Rows) (resultsSlice [][]string, err error) { fields, err := rows.Columns() if err != nil { diff --git a/schemas/index.go b/schemas/index.go index 8f31af52..47027ea4 100644 --- a/schemas/index.go +++ b/schemas/index.go @@ -32,7 +32,7 @@ func NewIndex(name string, indexType int) *Index { func (index *Index) XName(tableName string) string { if !strings.HasPrefix(index.Name, "UQE_") && !strings.HasPrefix(index.Name, "IDX_") { - tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".") + tableParts := strings.Split(strings.ReplaceAll(tableName, `"`, ""), ".") tableName = tableParts[len(tableParts)-1] if index.Type == UniqueType { return fmt.Sprintf("UQE_%v_%v", tableName, index.Name) diff --git a/schemas/quote.go b/schemas/quote.go index 71040ad9..4cab30fe 100644 --- a/schemas/quote.go +++ b/schemas/quote.go @@ -37,7 +37,7 @@ func (q Quoter) IsEmpty() bool { // Quote quote a string func (q Quoter) Quote(s string) string { var buf strings.Builder - q.QuoteTo(&buf, s) + _ = q.QuoteTo(&buf, s) return buf.String() } @@ -64,7 +64,7 @@ func (q Quoter) Trim(s string) string { // Join joins a slice with quoters func (q Quoter) Join(a []string, sep string) string { var b strings.Builder - q.JoinWrite(&b, a, sep) + _ = q.JoinWrite(&b, a, sep) return b.String() } @@ -86,7 +86,9 @@ func (q Quoter) JoinWrite(b *strings.Builder, a []string, sep string) error { return err } } - q.QuoteTo(b, strings.TrimSpace(s)) + if err := q.QuoteTo(b, strings.TrimSpace(s)); err != nil { + return err + } } return nil } @@ -121,7 +123,7 @@ func findStart(value string, start int) int { } if (value[k] == 'A' || value[k] == 'a') && (value[k+1] == 'S' || value[k+1] == 's') { - k = k + 2 + k += 2 } for j := k; j < len(value); j++ { diff --git a/schemas/quote_test.go b/schemas/quote_test.go index 061a6ea2..f84dfb7d 100644 --- a/schemas/quote_test.go +++ b/schemas/quote_test.go @@ -45,7 +45,8 @@ func TestAlwaysQuoteTo(t *testing.T) { for _, v := range kases { t.Run(v.value, func(t *testing.T) { buf := &strings.Builder{} - quoter.QuoteTo(buf, v.value) + err := quoter.QuoteTo(buf, v.value) + assert.NoError(t, err) assert.EqualValues(t, v.expected, buf.String()) }) } @@ -54,10 +55,7 @@ func TestAlwaysQuoteTo(t *testing.T) { func TestReversedQuoteTo(t *testing.T) { var ( quoter = Quoter{'[', ']', func(s string) bool { - if s == "mytable" { - return true - } - return false + return s == "mytable" }} kases = []struct { expected string @@ -118,7 +116,8 @@ func TestNoQuoteTo(t *testing.T) { for _, v := range kases { t.Run(v.value, func(t *testing.T) { buf := &strings.Builder{} - quoter.QuoteTo(buf, v.value) + err := quoter.QuoteTo(buf, v.value) + assert.NoError(t, err) assert.EqualValues(t, v.expected, buf.String()) }) } diff --git a/session.go b/session.go index 2c916335..21bbe6e1 100644 --- a/session.go +++ b/session.go @@ -704,7 +704,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b if idx, ok = tempMap[lKey]; !ok { idx = 0 } else { - idx = idx + 1 + idx++ } tempMap[lKey] = idx diff --git a/session_delete.go b/session_delete.go index 9c19e43e..a0f420b1 100644 --- a/session_delete.go +++ b/session_delete.go @@ -232,7 +232,7 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) { } if cacher := session.engine.GetCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache { - session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) + _ = session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) } session.statement.RefTable = table diff --git a/session_find.go b/session_find.go index 47a3d308..dcac93b7 100644 --- a/session_find.go +++ b/session_find.go @@ -71,7 +71,11 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte } // session has stored the conditions so we use `unscoped` to avoid duplicated condition. - return session.Unscoped().Count(reflect.New(sliceElementType).Interface()) + if sliceElementType.Kind() == reflect.Struct { + return session.Unscoped().Count(reflect.New(sliceElementType).Interface()) + } + + return session.Unscoped().Count() } func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error { @@ -152,7 +156,6 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) if err != ErrCacheFailed { return err } - err = nil // !nashtsai! reset err to nil for ErrCacheFailed session.engine.logger.Warnf("Cache Find Failed") } } diff --git a/session_get.go b/session_get.go index 48616a6b..9bb92a8b 100644 --- a/session_get.go +++ b/session_get.go @@ -6,7 +6,6 @@ package xorm import ( "database/sql" - "database/sql/driver" "errors" "fmt" "math/big" @@ -79,7 +78,7 @@ func (session *Session) get(beans ...interface{}) (bool, error) { var err error if session.statement.RawSQL == "" { - if len(session.statement.TableName()) <= 0 { + if len(session.statement.TableName()) == 0 { return false, ErrTableNotFound } session.statement.Limit(1) @@ -130,14 +129,6 @@ func (session *Session) get(beans ...interface{}) (bool, error) { return true, nil } -var ( - valuerTypePlaceHolder driver.Valuer - valuerType = reflect.TypeOf(&valuerTypePlaceHolder).Elem() - - conversionTypePlaceHolder convert.Conversion - conversionType = reflect.TypeOf(&conversionTypePlaceHolder).Elem() -) - func isScannableStruct(bean interface{}, typeLen int) bool { switch bean.(type) { case *time.Time: diff --git a/session_insert.go b/session_insert.go index 43a4118b..4835eb14 100644 --- a/session_insert.go +++ b/session_insert.go @@ -80,7 +80,7 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -90,7 +90,6 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e colNames []string colMultiPlaces []string args []interface{} - cols []*schemas.Column ) for i := 0; i < size; i++ { @@ -172,7 +171,6 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e if i == 0 { colNames = append(colNames, col.Name) - cols = append(cols, col) } colPlaces = append(colPlaces, "?") } @@ -203,7 +201,7 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e return 0, err } - session.cacheInsert(tableName) + _ = session.cacheInsert(tableName) lenAfterClosures := len(session.afterClosures) for i := 0; i < size; i++ { @@ -257,7 +255,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { if err := session.statement.SetRefBean(bean); err != nil { return 0, err } - if len(session.statement.TableName()) <= 0 { + if len(session.statement.TableName()) == 0 { return 0, ErrTableNotFound } @@ -360,7 +358,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { defer handleAfterInsertProcessorFunc(bean) - session.cacheInsert(tableName) + _ = session.cacheInsert(tableName) if table.Version != "" && session.statement.CheckVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -390,7 +388,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { defer handleAfterInsertProcessorFunc(bean) - session.cacheInsert(tableName) + _ = session.cacheInsert(tableName) if table.Version != "" && session.statement.CheckVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -430,6 +428,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { // InsertOne insert only one struct into database as a record. // The in parameter bean must a struct or a point to struct. The return // parameter is inserted and error +// Deprecated: Please use Insert directly func (session *Session) InsertOne(bean interface{}) (int64, error) { if session.isAutoClose { defer session.Close() @@ -537,7 +536,7 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -559,12 +558,12 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err } func (session *Session) insertMultipleMapInterface(maps []map[string]interface{}) (int64, error) { - if len(maps) <= 0 { + if len(maps) == 0 { return 0, ErrNoElementsOnSlice } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -595,7 +594,7 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -618,12 +617,12 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { } func (session *Session) insertMultipleMapString(maps []map[string]string) (int64, error) { - if len(maps) <= 0 { + if len(maps) == 0 { return 0, ErrNoElementsOnSlice } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -650,7 +649,7 @@ func (session *Session) insertMultipleMapString(maps []map[string]string) (int64 func (session *Session) insertMap(columns []string, args []interface{}) (int64, error) { tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -677,7 +676,7 @@ func (session *Session) insertMap(columns []string, args []interface{}) (int64, func (session *Session) insertMultipleMap(columns []string, argss [][]interface{}) (int64, error) { tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } diff --git a/session_iterate.go b/session_iterate.go index f6301009..afb9a7cc 100644 --- a/session_iterate.go +++ b/session_iterate.go @@ -95,7 +95,7 @@ func (session *Session) bufferIterate(bean interface{}, fun IterFunc) error { break } - start = start + slice.Elem().Len() + start += slice.Elem().Len() if pLimitN != nil && start+bufferSize > *pLimitN { bufferSize = *pLimitN - start } diff --git a/session_schema.go b/session_schema.go index 75140426..e66c3b42 100644 --- a/session_schema.go +++ b/session_schema.go @@ -223,24 +223,6 @@ func (session *Session) isTableEmpty(tableName string) (bool, error) { return total == 0, nil } -// find if index is exist according cols -func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) { - indexes, err := session.engine.dialect.GetIndexes(session.getQueryer(), session.ctx, tableName) - if err != nil { - return false, err - } - - for _, index := range indexes { - if utils.SliceEq(index.Cols, cols) { - if unique { - return index.Type == schemas.UniqueType, nil - } - return index.Type == schemas.IndexType, nil - } - } - return false, nil -} - func (session *Session) addColumn(colName string) error { col := session.statement.RefTable.GetColumn(colName) sql := session.engine.dialect.AddColumnSQL(session.statement.TableName(), col) diff --git a/session_tx.go b/session_tx.go index 8763784c..4fa56891 100644 --- a/session_tx.go +++ b/session_tx.go @@ -75,7 +75,7 @@ func (session *Session) Commit() error { } cleanUpFunc := func(slices *map[interface{}]*[]func(interface{})) { if len(*slices) > 0 { - *slices = make(map[interface{}]*[]func(interface{}), 0) + *slices = make(map[interface{}]*[]func(interface{})) } } cleanUpFunc(&session.afterInsertBeans) diff --git a/session_update.go b/session_update.go index 4fd45a53..fefbee90 100644 --- a/session_update.go +++ b/session_update.go @@ -22,6 +22,7 @@ var ( ErrNoColumnsTobeUpdated = errors.New("no columns found to be updated") ) +//revive:disable func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr string, args ...interface{}) error { if table == nil || session.tx != nil { @@ -39,7 +40,7 @@ func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr stri var nStart int if len(args) > 0 { - if strings.Index(sqlStr, "?") > -1 { + if strings.Contains(sqlStr, "?") { nStart = strings.Count(oldhead, "?") } else { // only for pq, TODO: if any other databse? @@ -182,7 +183,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return 0, err } - if len(session.statement.TableName()) <= 0 { + if len(session.statement.TableName()) == 0 { return 0, ErrTableNotFound } @@ -342,7 +343,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } - if len(colNames) <= 0 { + if len(colNames) == 0 { return 0, ErrNoColumnsTobeUpdated } @@ -356,7 +357,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } if st.OrderStr != "" { - condSQL = condSQL + fmt.Sprintf(" ORDER BY %v", st.OrderStr) + condSQL += fmt.Sprintf(" ORDER BY %v", st.OrderStr) } var tableName = session.statement.TableName() @@ -366,7 +367,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 limitValue := *st.LimitN switch session.engine.dialect.URI().DBType { case schemas.MYSQL: - condSQL = condSQL + fmt.Sprintf(" LIMIT %d", limitValue) + condSQL += fmt.Sprintf(" LIMIT %d", limitValue) case schemas.SQLITE: tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", limitValue) cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)", From 57365108ae98aefd57a732f0e2e65fa38f865798 Mon Sep 17 00:00:00 2001 From: satorunooshie Date: Mon, 27 Dec 2021 10:11:44 +0800 Subject: [PATCH 040/116] Fix insertMultipleStruct to insert null value under certain circumstances (#2077) The behavior of multi insertion of null differs from that of single insertion. On the other hand, behavior between single and bulk insertion of null using pointer of struct field is identical. Please see the example below. ```go s := engineGroup.NewSession() type User struct { ID int `xorm:"not null pk autoincr INT(10)"` Name string `xorm:"not null VARCHAR(191)"` Age int64 `xorm:"null BIGINT(20)"` } list := []*User{ { Name: "John", }, { Name: "Wick", }, } s.Nullable("age") // Single insertion works _, err := s.Insert(list[0]) // [table] `user` INSERT INTO `user` (`name`,`age`) VALUES (?, ?) [John ] s.Nullable("age") // Bulk insertion does not work _, err := s.Insert(list) // [table] `user` INSERT INTO `user` (`name,`age`) VALUES (?, ?),(?, ?) [John, 0, Wick, 0] //--------------------------------- //Using pointer, which is nullable, the generated sql has no difference. //--------------------------------- type User struct { ID int `xorm:"not null pk autoincr INT(10)"` Name string `xorm:"not null VARCHAR(191)"` Age *int64 `xorm:"null BIGINT(20)"` } list := []*User{ { Name: "John", }, { Name: "Wick", }, } s.Nullable("age") // Single insertion works _, err := s.Insert(list[0]) // [table] `user` INSERT INTO `user` (`name`,`age`) VALUES (?, ?) [John ] s.Nullable("age") // Bulk insertion does not work _, err := s.Insert(list) // [table] `user` INSERT INTO `user` (`name,`age`) VALUES (?, ?),(?, ?) [John, , Wick, ] ``` Reviewed-on: https://gitea.com/xorm/xorm/pulls/2077 Co-authored-by: satorunooshie Co-committed-by: satorunooshie --- session_insert.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/session_insert.go b/session_insert.go index 4835eb14..fc025613 100644 --- a/session_insert.go +++ b/session_insert.go @@ -142,6 +142,13 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e if len(session.statement.ColumnMap) > 0 && !session.statement.ColumnMap.Contain(col.Name) { continue } + // !satorunooshie! set fieldValue as nil when column is nullable and zero-value + if _, ok := getFlagForColumn(session.statement.NullableMap, col); ok { + if col.Nullable && utils.IsValueZero(fieldValue) { + var nilValue *int + fieldValue = reflect.ValueOf(nilValue) + } + } if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { val, t, err := session.engine.nowTime(col) if err != nil { From 470807151d0a443bcdd49a13d3ec108e1eb65bd2 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Wed, 5 Jan 2022 09:37:18 +0800 Subject: [PATCH 041/116] fix cross db dumping of bools (#2089) When dumping booleans these need to be converted from the original DB representation to the new db representation. In the case of most DBs this is simply to 0 or 1 but for postgres these have to be false or true. Signed-off-by: Andrew Thornton Reviewed-on: https://gitea.com/xorm/xorm/pulls/2089 Reviewed-by: Lunny Xiao Co-authored-by: Andrew Thornton Co-committed-by: Andrew Thornton --- engine.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/engine.go b/engine.go index 709cc384..1257de20 100644 --- a/engine.go +++ b/engine.go @@ -12,6 +12,7 @@ import ( "os" "reflect" "runtime" + "strconv" "strings" "time" @@ -438,16 +439,14 @@ func (engine *Engine) DumpTables(tables []*schemas.Table, w io.Writer, tp ...sch return engine.dumpTables(context.Background(), tables, w, tp...) } -func formatBool(s string, dstDialect dialects.Dialect) string { - if dstDialect.URI().DBType == schemas.MSSQL { - switch s { - case "true": +func formatBool(s bool, dstDialect dialects.Dialect) string { + if dstDialect.URI().DBType != schemas.POSTGRES { + if s { return "1" - case "false": - return "0" } + return "0" } - return s + return strconv.FormatBool(s) } // dumpTables dump database all table structs and data to w with specify db type @@ -581,8 +580,13 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w return err } } else { - if stp.IsBool() || (dstDialect.URI().DBType == schemas.MSSQL && strings.EqualFold(stp.Name, schemas.Bit)) { - if _, err = io.WriteString(w, formatBool(s.String, dstDialect)); err != nil { + if table.Columns()[i].SQLType.IsBool() || stp.IsBool() || (dstDialect.URI().DBType == schemas.MSSQL && strings.EqualFold(stp.Name, schemas.Bit)) { + val, err := strconv.ParseBool(s.String) + if err != nil { + return err + } + + if _, err = io.WriteString(w, formatBool(val, dstDialect)); err != nil { return err } } else if stp.IsNumeric() { From 2fa71307041bd5c181cd37f48492bdc4620d6674 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 6 Jan 2022 19:43:06 +0800 Subject: [PATCH 042/116] Improve find interface (#2092) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2092 --- session_find.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/session_find.go b/session_find.go index dcac93b7..57ac7bb7 100644 --- a/session_find.go +++ b/session_find.go @@ -254,9 +254,9 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect switch elemType.Kind() { case reflect.Slice: - err = rows.ScanSlice(bean) + err = session.getSlice(rows, types, fields, bean) case reflect.Map: - err = rows.ScanMap(bean) + err = session.getMap(rows, types, fields, bean) default: err = rows.Scan(bean) } From cd36b112ae482f4fa9f0c590e103e32211a2ad80 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Fri, 7 Jan 2022 16:04:01 +0800 Subject: [PATCH 043/116] Escape string and blob results from dump more correctly (#2091) dumpTables currently badly handles BLOB and TEXT data containing control characters: * MySQL will interpret and unescape string literals e.g.`\r` will become carriage return. * Postgres will not allow string literals to contain NUL nor will SQLite so BLOBs will not dump correctly. * Schemas should not be set on the destination dump * MSSQL needs the N prefix to correctly ensure that UTF-8 data is correctly transferred. Signed-off-by: Andrew Thornton Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2091 Reviewed-by: Lunny Xiao Co-authored-by: Andrew Thornton Co-committed-by: Andrew Thornton --- engine.go | 191 +++++++++++++++++++++++++++++++++++- integrations/engine_test.go | 13 ++- 2 files changed, 199 insertions(+), 5 deletions(-) diff --git a/engine.go b/engine.go index 1257de20..b7dcf5a2 100644 --- a/engine.go +++ b/engine.go @@ -11,6 +11,7 @@ import ( "io" "os" "reflect" + "regexp" "runtime" "strconv" "strings" @@ -449,6 +450,8 @@ func formatBool(s bool, dstDialect dialects.Dialect) string { return strconv.FormatBool(s) } +var controlCharactersRe = regexp.MustCompile(`[\x00-\x1f\x7f]+`) + // dumpTables dump database all table structs and data to w with specify db type func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error { var dstDialect dialects.Dialect @@ -464,7 +467,10 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w destURI := dialects.URI{ DBType: tp[0], DBName: uri.DBName, - Schema: uri.Schema, + // DO NOT SET SCHEMA HERE + } + if tp[0] == schemas.POSTGRES { + destURI.Schema = engine.dialect.URI().Schema } if err := dstDialect.Init(&destURI); err != nil { return err @@ -479,6 +485,13 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w return err } + if dstDialect.URI().DBType == schemas.MYSQL { + // For MySQL set NO_BACKLASH_ESCAPES so that strings work properly + if _, err := io.WriteString(w, "SET sql_mode='NO_BACKSLASH_ESCAPES';\n"); err != nil { + return err + } + } + for i, table := range tables { dstTable := table if table.Type != nil { @@ -598,6 +611,182 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w if _, err = io.WriteString(w, "'"+r+"'"); err != nil { return err } + } else if len(s.String) == 0 { + if _, err := io.WriteString(w, "''"); err != nil { + return err + } + } else if dstDialect.URI().DBType == schemas.POSTGRES { + if dstTable.Columns()[i].SQLType.IsBlob() { + // Postgres has the escape format and we should use that for bytea data + if _, err := fmt.Fprintf(w, "'\\x%x'", s.String); err != nil { + return err + } + } else { + // Postgres concatentates strings using || (NOTE: a NUL byte in a text segment will fail) + toCheck := strings.ReplaceAll(s.String, "'", "''") + for len(toCheck) > 0 { + loc := controlCharactersRe.FindStringIndex(toCheck) + if loc == nil { + if _, err := io.WriteString(w, "'"+toCheck+"'"); err != nil { + return err + } + break + } + if loc[0] > 0 { + if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"' || "); err != nil { + return err + } + } + if _, err := io.WriteString(w, "e'"); err != nil { + return err + } + for i := loc[0]; i < loc[1]; i++ { + if _, err := fmt.Fprintf(w, "\\x%02x", toCheck[i]); err != nil { + return err + } + } + toCheck = toCheck[loc[1]:] + if len(toCheck) > 0 { + if _, err := io.WriteString(w, "' || "); err != nil { + return err + } + } else { + if _, err := io.WriteString(w, "'"); err != nil { + return err + } + } + } + } + } else if dstDialect.URI().DBType == schemas.MYSQL { + loc := controlCharactersRe.FindStringIndex(s.String) + if loc == nil { + if _, err := io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { + return err + } + } else { + if _, err := io.WriteString(w, "CONCAT("); err != nil { + return err + } + toCheck := strings.ReplaceAll(s.String, "'", "''") + for len(toCheck) > 0 { + loc := controlCharactersRe.FindStringIndex(toCheck) + if loc == nil { + if _, err := io.WriteString(w, "'"+toCheck+"')"); err != nil { + return err + } + break + } + if loc[0] > 0 { + if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"', "); err != nil { + return err + } + } + for i := loc[0]; i < loc[1]-1; i++ { + if _, err := io.WriteString(w, "CHAR("+strconv.Itoa(int(toCheck[i]))+"), "); err != nil { + return err + } + } + char := toCheck[loc[1]-1] + toCheck = toCheck[loc[1]:] + if len(toCheck) > 0 { + if _, err := io.WriteString(w, "CHAR("+strconv.Itoa(int(char))+"), "); err != nil { + return err + } + } else { + if _, err = io.WriteString(w, "CHAR("+strconv.Itoa(int(char))+"))"); err != nil { + return err + } + } + } + } + } else if dstDialect.URI().DBType == schemas.SQLITE { + if dstTable.Columns()[i].SQLType.IsBlob() { + // SQLite has its escape format + if _, err := fmt.Fprintf(w, "X'%x'", s.String); err != nil { + return err + } + } else { + // SQLite concatentates strings using || (NOTE: a NUL byte in a text segment will fail) + toCheck := strings.ReplaceAll(s.String, "'", "''") + for len(toCheck) > 0 { + loc := controlCharactersRe.FindStringIndex(toCheck) + if loc == nil { + if _, err := io.WriteString(w, "'"+toCheck+"'"); err != nil { + return err + } + break + } + if loc[0] > 0 { + if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"' || "); err != nil { + return err + } + } + if _, err := fmt.Fprintf(w, "X'%x'", toCheck[loc[0]:loc[1]]); err != nil { + return err + } + toCheck = toCheck[loc[1]:] + if len(toCheck) > 0 { + if _, err := io.WriteString(w, " || "); err != nil { + return err + } + } + } + } + } else if dstDialect.URI().DBType == schemas.DAMENG || dstDialect.URI().DBType == schemas.ORACLE { + if dstTable.Columns()[i].SQLType.IsBlob() { + // ORACLE/DAMENG uses HEXTORAW + if _, err := fmt.Fprintf(w, "HEXTORAW('%x')", s.String); err != nil { + return err + } + } else { + // ORACLE/DAMENG concatentates strings in multiple ways but uses CHAR and has CONCAT + // (NOTE: a NUL byte in a text segment will fail) + if _, err := io.WriteString(w, "CONCAT("); err != nil { + return err + } + toCheck := strings.ReplaceAll(s.String, "'", "''") + for len(toCheck) > 0 { + loc := controlCharactersRe.FindStringIndex(toCheck) + if loc == nil { + if _, err := io.WriteString(w, "'"+toCheck+"')"); err != nil { + return err + } + break + } + if loc[0] > 0 { + if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"', "); err != nil { + return err + } + } + for i := loc[0]; i < loc[1]-1; i++ { + if _, err := io.WriteString(w, "CHAR("+strconv.Itoa(int(toCheck[i]))+"), "); err != nil { + return err + } + } + char := toCheck[loc[1]-1] + toCheck = toCheck[loc[1]:] + if len(toCheck) > 0 { + if _, err := io.WriteString(w, "CHAR("+strconv.Itoa(int(char))+"), "); err != nil { + return err + } + } else { + if _, err = io.WriteString(w, "CHAR("+strconv.Itoa(int(char))+"))"); err != nil { + return err + } + } + } + } + } else if dstDialect.URI().DBType == schemas.MSSQL { + if dstTable.Columns()[i].SQLType.IsBlob() { + // MSSQL uses CONVERT(VARBINARY(MAX), '0xDEADBEEF', 1) + if _, err := fmt.Fprintf(w, "CONVERT(VARBINARY(MAX), '0x%x', 1)", s.String); err != nil { + return err + } + } else { + if _, err = io.WriteString(w, "N'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { + return err + } + } } else { if _, err = io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { return err diff --git a/integrations/engine_test.go b/integrations/engine_test.go index dbe17571..cdcdd6be 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -143,6 +143,7 @@ func TestDumpTables(t *testing.T) { type TestDumpTableStruct struct { Id int64 + Data []byte `xorm:"BLOB"` Name string IsMan bool Created time.Time `xorm:"created"` @@ -152,10 +153,14 @@ func TestDumpTables(t *testing.T) { _, err := testEngine.Insert([]TestDumpTableStruct{ {Name: "1", IsMan: true}, - {Name: "2\n"}, - {Name: "3;"}, - {Name: "4\n;\n''"}, - {Name: "5'\n"}, + {Name: "2\n", Data: []byte{'\000', '\001', '\002'}}, + {Name: "3;", Data: []byte("0x000102")}, + {Name: "4\n;\n''", Data: []byte("Help")}, + {Name: "5'\n", Data: []byte("0x48656c70")}, + {Name: "6\\n'\n", Data: []byte("48656c70")}, + {Name: "7\\n'\r\n", Data: []byte("7\\n'\r\n")}, + {Name: "x0809ee"}, + {Name: "090a10"}, }) assert.NoError(t, err) From 3d1c9fb761325f98ea2c5493bbb28e0431936784 Mon Sep 17 00:00:00 2001 From: jixianlqb Date: Sun, 16 Jan 2022 16:36:07 +0800 Subject: [PATCH 044/116] =?UTF-8?q?=E5=AF=B9=20DATE=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E8=BD=AC=E6=8D=A2=20(#2093)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对 DATE格式进行转换 Co-authored-by: laiqiangbin Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2093 Reviewed-by: Lunny Xiao Co-authored-by: jixianlqb Co-committed-by: jixianlqb --- convert/time.go | 10 ++++++++++ convert/time_test.go | 1 + 2 files changed, 11 insertions(+) diff --git a/convert/time.go b/convert/time.go index e53a19cd..cc2e0a10 100644 --- a/convert/time.go +++ b/convert/time.go @@ -48,6 +48,16 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t } dt = dt.In(convertedLocation) return &dt, nil + } else if len(s) == 10 && s[4] == '-' { + if s == "0000-00-00" || s == "0001-01-01" { + return &time.Time{}, nil + } + dt, err := time.ParseInLocation("2006-01-02", s, originalLocation) + if err != nil { + return nil, err + } + dt = dt.In(convertedLocation) + return &dt, nil } else { i, err := strconv.ParseInt(s, 10, 64) if err == nil { diff --git a/convert/time_test.go b/convert/time_test.go index ef01b362..5ddceb64 100644 --- a/convert/time_test.go +++ b/convert/time_test.go @@ -16,6 +16,7 @@ func TestString2Time(t *testing.T) { assert.NoError(t, err) var kases = map[string]time.Time{ + "2021-08-10": time.Date(2021, 8, 10, 8, 0, 0, 0, expectedLoc), "2021-06-06T22:58:20+08:00": time.Date(2021, 6, 6, 22, 58, 20, 0, expectedLoc), "2021-07-11 10:44:00": time.Date(2021, 7, 11, 18, 44, 0, 0, expectedLoc), "2021-08-10T10:33:04Z": time.Date(2021, 8, 10, 18, 33, 04, 0, expectedLoc), From d13b607d75d50c494b1ffa24552f3733f2dde8af Mon Sep 17 00:00:00 2001 From: fuge Date: Sun, 16 Jan 2022 18:04:24 +0800 Subject: [PATCH 045/116] =?UTF-8?q?oracle=E5=88=86=E9=A1=B5=EF=BC=8Cstart?= =?UTF-8?q?=20=E4=B8=BA=200=20=E7=9A=84bug=20(#2098)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除前面判断即可。 Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2098 Reviewed-by: Lunny Xiao Co-authored-by: fuge Co-committed-by: fuge --- internal/statements/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/statements/query.go b/internal/statements/query.go index 16253417..8b383866 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -334,7 +334,7 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB fmt.Fprint(&buf, " LIMIT ", *pLimitN) } } else if dialect.URI().DBType == schemas.ORACLE { - if statement.Start != 0 && pLimitN != nil { + if pLimitN != nil { oldString := buf.String() buf.Reset() rawColStr := columnStr From e4e830bc78d321154b739d86b2ac05854ba2f6e5 Mon Sep 17 00:00:00 2001 From: appleboy Date: Sun, 16 Jan 2022 19:04:15 +0800 Subject: [PATCH 046/116] chore(lint): remove revive and misspell command from makefile (#2088) replace revive and misspell with golangci lint Signed-off-by: Bo-Yi Wu Co-authored-by: Bo-Yi Wu Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2088 Co-authored-by: appleboy Co-committed-by: appleboy --- .revive.toml | 29 ----------------------------- Makefile | 22 ---------------------- 2 files changed, 51 deletions(-) delete mode 100644 .revive.toml diff --git a/.revive.toml b/.revive.toml deleted file mode 100644 index 9e3b629d..00000000 --- a/.revive.toml +++ /dev/null @@ -1,29 +0,0 @@ -ignoreGeneratedHeader = false -severity = "warning" -confidence = 0.8 -errorCode = 1 -warningCode = 1 - -[rule.blank-imports] -[rule.context-as-argument] -[rule.context-keys-type] -[rule.dot-imports] -[rule.empty-lines] -[rule.errorf] -[rule.error-return] -[rule.error-strings] -[rule.error-naming] -[rule.exported] -[rule.if-return] -[rule.increment-decrement] -[rule.indent-error-flow] -[rule.package-comments] -[rule.range] -[rule.receiver-naming] -[rule.struct-tag] -[rule.time-naming] -[rule.unexported-return] -[rule.unnecessary-stmt] -[rule.var-declaration] -[rule.var-naming] - arguments = [["ID", "UID", "UUID", "URL", "JSON"], []] \ No newline at end of file diff --git a/Makefile b/Makefile index 220c8592..b43c4a4c 100644 --- a/Makefile +++ b/Makefile @@ -99,7 +99,6 @@ help: @echo " - clean delete integration files and build files but not css and js files" @echo " - fmt format the code" @echo " - lint run code linter" - @echo " - misspell check if a word is written wrong" @echo " - test run default unit test" @echo " - test-cockroach run integration tests for cockroach" @echo " - test-mysql run integration tests for mysql" @@ -131,27 +130,6 @@ golangci-lint-check: curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \ fi -.PHONY: revive -revive: - @hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - $(GO) get -u github.com/mgechev/revive; \ - fi - revive -config .revive.toml -exclude=./vendor/... ./... || exit 1 - -.PHONY: misspell -misspell: - @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - $(GO) get -u github.com/client9/misspell/cmd/misspell; \ - fi - misspell -w -i unknwon $(GOFILES) - -.PHONY: misspell-check -misspell-check: - @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - $(GO) get -u github.com/client9/misspell/cmd/misspell; \ - fi - misspell -error -i unknwon,destory $(GOFILES) - .PHONY: test test: go-check $(GO) test $(PACKAGES) From 7802393d01519f2e64537dfd1c8b9900ad4db9b5 Mon Sep 17 00:00:00 2001 From: finelog Date: Tue, 25 Jan 2022 11:09:41 +0800 Subject: [PATCH 047/116] fix reset colmap when counting distinct cols (#2096) when using distinct cols with FindAndCount, reset statement.ColumnMap will make the counting sql an syntax error, this pr try fix this. is this pr ok for merge? error sql logging: ```sql [SQL] SELECT DISTINCT `Fid` FROM `table_demo` WHERE Fkey = ? [val] [SQL] SELECT count(DISTINCT ) FROM `table_demo` WHERE Fkey = ? [val] ``` after fix: ```sql [SQL] SELECT DISTINCT `Fid` FROM `table_demo` WHERE Fkey = ? [val] [SQL] SELECT count(DISTINCT `Fid`) FROM `table_demo` WHERE Fkey = ? [val] ``` Co-authored-by: finelog Reviewed-on: https://gitea.com/xorm/xorm/pulls/2096 Reviewed-by: Lunny Xiao Co-authored-by: finelog Co-committed-by: finelog --- integrations/session_find_test.go | 30 ++++++++++++++++++++++++++++++ session_find.go | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index f7af45fa..0e0c7bb4 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -711,6 +711,36 @@ func TestFindAndCountWithGroupBy(t *testing.T) { assert.EqualValues(t, 2, len(results)) } +func TestFindAndCountWithDistinct(t *testing.T) { + assert.NoError(t, PrepareEngine()) + + type FindAndCountWithDistinct struct { + Id int64 + Age int `xorm:"index"` + Name string + } + + assert.NoError(t, testEngine.Sync(new(FindAndCountWithDistinct))) + + _, err := testEngine.Insert([]FindAndCountWithDistinct{ + { + Name: "test1", + Age: 10, + }, + { + Name: "test2", + Age: 20, + }, + }) + assert.NoError(t, err) + + var results []FindAndCountWithDistinct + cnt, err := testEngine.Distinct("`age`").FindAndCount(&results) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt) + assert.EqualValues(t, 2, len(results)) +} + type FindMapDevice struct { Deviceid string `xorm:"pk"` Status int diff --git a/session_find.go b/session_find.go index 57ac7bb7..caf79ee3 100644 --- a/session_find.go +++ b/session_find.go @@ -57,7 +57,7 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte if session.statement.SelectStr != "" { session.statement.SelectStr = "" } - if len(session.statement.ColumnMap) > 0 { + if len(session.statement.ColumnMap) > 0 && !session.statement.IsDistinct { session.statement.ColumnMap = []string{} } if session.statement.OrderStr != "" { From 3180c418c2454771181690a360a88204a2514826 Mon Sep 17 00:00:00 2001 From: fuge Date: Tue, 25 Jan 2022 13:28:46 +0800 Subject: [PATCH 048/116] bugfix :Oid It's a special index. You can't put it in (#2105) Co-authored-by: fuge <8342337@qq.com> Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2105 Co-authored-by: fuge Co-committed-by: fuge --- dialects/postgres.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dialects/postgres.go b/dialects/postgres.go index 76279d32..a5b080aa 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -1300,6 +1300,19 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN indexType = schemas.IndexType } colNames = getIndexColName(indexdef) + + isSkip := false + //Oid It's a special index. You can't put it in + for _, element := range colNames { + if "oid" == element { + isSkip = true + break + } + } + if isSkip { + continue + } + var isRegular bool if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { newIdxName := indexName[5+len(tableName):] From 79a21b68aafacfaa11b47f45ce595dc336c51036 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bonicoli Date: Thu, 31 Mar 2022 14:26:05 +0800 Subject: [PATCH 049/116] replace GitHub links: xorm has been moved to gitea.com (#2126) Co-authored-by: Pierre-Louis Bonicoli Reviewed-on: https://gitea.com/xorm/xorm/pulls/2126 Reviewed-by: Lunny Xiao Co-authored-by: Pierre-Louis Bonicoli Co-committed-by: Pierre-Louis Bonicoli --- CONTRIBUTING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6925a5c..27e6929b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,13 @@ ## Contributing to xorm -`xorm` has a backlog of [pull requests](https://help.github.com/articles/using-pull-requests), but contributions are still very -much welcome. You can help with patch review, submitting bug reports, +`xorm` has a backlog of [pull requests](https://gitea.com/xorm/xorm/pulls), but contributions are still very +much welcome. You can help with patch review, submitting [bug reports](https://gitea.com/xorm/xorm/issues), or adding new functionality. There is no formal style guide, but please conform to the style of existing code and general Go formatting conventions when submitting patches. -* [fork a repo](https://help.github.com/articles/fork-a-repo) -* [creating a pull request ](https://help.github.com/articles/creating-a-pull-request) +* [fork the repo](https://gitea.com/repo/fork/2038) +* [creating a pull request ](https://docs.gitea.io/en-us/pull-request/) ### Language @@ -15,7 +15,7 @@ Since `xorm` is a world-wide open source project, please describe your issues or ### Sign your codes with comments ``` -// !! your comments +// !! your comments e.g., @@ -65,7 +65,7 @@ And if your branch is related with cache, you could also enable it via `TEST_CAC ### Patch review -Help review existing open [pull requests](https://help.github.com/articles/using-pull-requests) by commenting on the code or +Help review existing open [pull requests](https://gitea.com/xorm/xorm/pulls) by commenting on the code or proposed functionality. ### Bug reports From d195040cb941db63e82ba4f54a6c194bab0f614f Mon Sep 17 00:00:00 2001 From: finelog Date: Thu, 31 Mar 2022 17:20:29 +0800 Subject: [PATCH 050/116] fix session context overwrite when logSessionId not set (#2115) ref pr https://gitea.com/xorm/xorm/pulls/2053 i think the previous fix has some issue for example, i'm using session like this: ```go // logSessionID == false engine := NewEngine() // use ctx.SessionId to distinguish uniq request id cxt := context.WithValue(parent, log.SessionIDKey, "some unique request id") session := engine.NewSession().Context(ctx) ``` however, with pr 2053, `session.Context` can't get SessionId from ctx. this pr fix abrove issue, overwrite `session.Context()` only when `engine.logSessionID == true` please check it out,thanks! Co-authored-by: finelog Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2115 Co-authored-by: finelog Co-committed-by: finelog --- session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.go b/session.go index 21bbe6e1..3fc53e23 100644 --- a/session.go +++ b/session.go @@ -757,7 +757,7 @@ func (session *Session) incrVersionFieldValue(fieldValue *reflect.Value) { // Context sets the context on this session func (session *Session) Context(ctx context.Context) *Session { - if session.ctx != nil { + if session.engine.logSessionID && session.ctx != nil { ctx = context.WithValue(ctx, log.SessionIDKey, session.ctx.Value(log.SessionIDKey)) ctx = context.WithValue(ctx, log.SessionKey, session.ctx.Value(log.SessionKey)) ctx = context.WithValue(ctx, log.SessionShowSQLKey, session.ctx.Value(log.SessionShowSQLKey)) From b3f9c53d8abeb8870c579312d1a28293813e92fd Mon Sep 17 00:00:00 2001 From: getsu Date: Thu, 31 Mar 2022 23:57:40 +0800 Subject: [PATCH 051/116] =?UTF-8?q?oracle=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E6=8B=BC=E6=8E=A5AS=20(#2109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复 #2108 Co-authored-by: chendy Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2109 Co-authored-by: getsu Co-committed-by: getsu --- dialects/table_name.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dialects/table_name.go b/dialects/table_name.go index 48b44de2..8a0baeac 100644 --- a/dialects/table_name.go +++ b/dialects/table_name.go @@ -11,6 +11,7 @@ import ( "xorm.io/xorm/internal/utils" "xorm.io/xorm/names" + "xorm.io/xorm/schemas" ) // TableNameWithSchema will add schema prefix on table name if possible @@ -29,6 +30,9 @@ func TableNameNoSchema(dialect Dialect, mapper names.Mapper, tableName interface switch tt := tableName.(type) { case []string: if len(tt) > 1 { + if dialect.URI().DBType == schemas.ORACLE { + return fmt.Sprintf("%v %v", quote(tt[0]), quote(tt[1])) + } return fmt.Sprintf("%v AS %v", quote(tt[0]), quote(tt[1])) } else if len(tt) == 1 { return quote(tt[0]) @@ -54,6 +58,9 @@ func TableNameNoSchema(dialect Dialect, mapper names.Mapper, tableName interface } } if l > 1 { + if dialect.URI().DBType == schemas.ORACLE { + return fmt.Sprintf("%v %v", quote(table), quote(fmt.Sprintf("%v", tt[1]))) + } return fmt.Sprintf("%v AS %v", quote(table), quote(fmt.Sprintf("%v", tt[1]))) } else if l == 1 { return quote(table) From e858b75756dc6b0340d9096e91f1007cba2f8b3c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 14 Apr 2022 10:12:39 +0800 Subject: [PATCH 052/116] Update changelog for 1.3.0 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f0f93e7..fed4e261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,32 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log. +## [1.3.0](https://gitea.com/xorm/xorm/releases/tag/1.3.0) - 2022-04-14 + +* BREAKING + * New Prepare useage (#2061) + * Make Get and Rows.Scan accept multiple parameters (#2029) + * Drop sync function and rename sync2 to sync (#2018) +* FEATURES + * Add dameng support (#2007) +* BUGFIXES + * bugfix :Oid It's a special index. You can't put it in (#2105) + * Fix new-lined query execution in master DB node. (#2066) + * Fix bug of Rows (#2048) + * Fix bug (#2046) + * fix panic when `Iterate()` fails (#2040) + * fix panic when convert sql and args with nil time.Time pointer (#2038) +* ENHANCEMENTS + * Fix to add session.statement.IsForUpdate check in Session.queryRows() (#2064) + * Expose ScanString / ScanInterface and etc (#2039) +* TESTING + * Add test for mysql tls (#2049) +* BUILD + * Upgrade dependencies modules (#2078) +* MISC + * Fix oracle keyword AS (#2109) + * Some performance optimization for get (#2043) + ## [1.2.2](https://gitea.com/xorm/xorm/releases/tag/1.2.2) - 2021-08-11 * MISC From ea9bba0d145211974a5bbe9d6b79324c257b6fc4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bonicoli Date: Sun, 17 Apr 2022 18:03:29 +0800 Subject: [PATCH 053/116] PostgreSQL: enable comment on column (#2131) The [oldest unsupported version documentation](https://www.postgresql.org/docs/7.1/sql-comment.html) states that comment on a column is supported. Update `TestGetColumnsComment` in order to check both MySQL/MariaDB and PostgreSQL. Co-authored-by: Pierre-Louis Bonicoli Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2131 Reviewed-by: Lunny Xiao Co-authored-by: Pierre-Louis Bonicoli Co-committed-by: Pierre-Louis Bonicoli --- dialects/postgres.go | 8 ++++++++ integrations/engine_test.go | 18 ++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/dialects/postgres.go b/dialects/postgres.go index a5b080aa..83e4187f 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -1354,6 +1354,14 @@ func (db *postgres) CreateTableSQL(ctx context.Context, queryer core.Queryer, ta commentSQL += fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", quoter.Quote(tableName), table.Comment) } + for _, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + + if len(col.Comment) > 0 { + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + } + } + return createTableSQL + commentSQL, true, nil } diff --git a/integrations/engine_test.go b/integrations/engine_test.go index cdcdd6be..997c8962 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -255,33 +255,31 @@ func TestDBVersion(t *testing.T) { fmt.Println(testEngine.Dialect().URI().DBType, "version is", version) } -func TestGetColumns(t *testing.T) { - if testEngine.Dialect().URI().DBType != schemas.POSTGRES { +func TestGetColumnsComment(t *testing.T) { + switch testEngine.Dialect().URI().DBType { + case schemas.POSTGRES, schemas.MYSQL: + default: t.Skip() return } + comment := "this is a comment" type TestCommentStruct struct { - HasComment int + HasComment int `xorm:"comment('this is a comment')"` NoComment int } assertSync(t, new(TestCommentStruct)) - comment := "this is a comment" - sql := fmt.Sprintf("comment on column %s.%s is '%s'", testEngine.TableName(new(TestCommentStruct), true), "has_comment", comment) - _, err := testEngine.Exec(sql) - assert.NoError(t, err) - tables, err := testEngine.DBMetas() assert.NoError(t, err) tableName := testEngine.GetColumnMapper().Obj2Table("TestCommentStruct") var hasComment, noComment string for _, table := range tables { if table.Name == tableName { - col := table.GetColumn("has_comment") + col := table.GetColumn(testEngine.GetColumnMapper().Obj2Table("HasComment")) assert.NotNil(t, col) hasComment = col.Comment - col2 := table.GetColumn("no_comment") + col2 := table.GetColumn(testEngine.GetColumnMapper().Obj2Table("NoComment")) assert.NotNil(t, col2) noComment = col2.Comment break From 8f2596bf64d4a91c0996c29e5266e01cbb2fce84 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 22 Apr 2022 10:16:35 +0800 Subject: [PATCH 054/116] some improvement (#2136) Fix #2134 and replace #2135 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2136 --- integrations/rows_test.go | 76 +++++++++++++++++++++++++++- integrations/session_find_test.go | 66 ++++++++++++++++++++---- integrations/session_get_test.go | 83 ++++++++++++++++++++++--------- scan.go | 24 ++++----- session.go | 56 ++++++++++++++------- 5 files changed, 238 insertions(+), 67 deletions(-) diff --git a/integrations/rows_test.go b/integrations/rows_test.go index 10f11453..e354b75e 100644 --- a/integrations/rows_test.go +++ b/integrations/rows_test.go @@ -70,7 +70,7 @@ func TestRows(t *testing.T) { } assert.EqualValues(t, 1, cnt) - var tbName = testEngine.Quote(testEngine.TableName(user, true)) + tbName := testEngine.Quote(testEngine.TableName(user, true)) rows2, err := testEngine.SQL("SELECT * FROM " + tbName).Rows(new(UserRows)) assert.NoError(t, err) defer rows2.Close() @@ -92,7 +92,7 @@ func TestRowsMyTableName(t *testing.T) { IsMan bool } - var tableName = "user_rows_my_table_name" + tableName := "user_rows_my_table_name" assert.NoError(t, testEngine.Table(tableName).Sync(new(UserRowsMyTable))) @@ -206,3 +206,75 @@ func TestRowsScanVars(t *testing.T) { assert.NoError(t, rows.Err()) assert.EqualValues(t, 2, cnt) } + +func TestRowsScanBytes(t *testing.T) { + type RowsScanBytes struct { + Id int64 + Bytes1 []byte + Bytes2 []byte + } + + assert.NoError(t, PrepareEngine()) + assert.NoError(t, testEngine.Sync(new(RowsScanBytes))) + + cnt, err := testEngine.Insert(&RowsScanBytes{ + Bytes1: []byte("bytes1"), + Bytes2: []byte("bytes2"), + }, &RowsScanBytes{ + Bytes1: []byte("bytes1-1"), + Bytes2: []byte("bytes2-2"), + }) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + { + rows, err := testEngine.Cols("bytes1, bytes2").Rows(new(RowsScanBytes)) + assert.NoError(t, err) + defer rows.Close() + + cnt = 0 + var bytes1 []byte + var bytes2 []byte + for rows.Next() { + err = rows.Scan(&bytes1, &bytes2) + assert.NoError(t, err) + if cnt == 0 { + assert.EqualValues(t, []byte("bytes1"), bytes1) + assert.EqualValues(t, []byte("bytes2"), bytes2) + } else if cnt == 1 { + // bytes1 now should be `bytes1` but will be override + assert.EqualValues(t, []byte("bytes1-1"), bytes1) + assert.EqualValues(t, []byte("bytes2-2"), bytes2) + } + cnt++ + } + assert.NoError(t, rows.Err()) + assert.EqualValues(t, 2, cnt) + rows.Close() + } + + { + rows, err := testEngine.Cols("bytes1, bytes2").Rows(new(RowsScanBytes)) + assert.NoError(t, err) + defer rows.Close() + + cnt = 0 + var rsb RowsScanBytes + for rows.Next() { + err = rows.Scan(&rsb) + assert.NoError(t, err) + if cnt == 0 { + assert.EqualValues(t, []byte("bytes1"), rsb.Bytes1) + assert.EqualValues(t, []byte("bytes2"), rsb.Bytes2) + } else if cnt == 1 { + // bytes1 now should be `bytes1` but will be override + assert.EqualValues(t, []byte("bytes1-1"), rsb.Bytes1) + assert.EqualValues(t, []byte("bytes2-2"), rsb.Bytes2) + } + cnt++ + } + assert.NoError(t, rows.Err()) + assert.EqualValues(t, 2, cnt) + rows.Close() + } +} diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index 0e0c7bb4..ae8779ff 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -40,14 +40,14 @@ func TestJoinLimit(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var checklist = CheckList{ + checklist := CheckList{ Eid: emp.Id, } cnt, err = testEngine.Insert(&checklist) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var salary = Salary{ + salary := Salary{ Lid: checklist.Id, } cnt, err = testEngine.Insert(&salary) @@ -89,7 +89,7 @@ func TestFind(t *testing.T) { assert.NoError(t, err) users2 := make([]Userinfo, 0) - var tbName = testEngine.Quote(testEngine.TableName(new(Userinfo), true)) + tbName := testEngine.Quote(testEngine.TableName(new(Userinfo), true)) err = testEngine.SQL("select * from " + tbName).Find(&users2) assert.NoError(t, err) } @@ -119,7 +119,7 @@ func (TeamUser) TableName() string { } func TestFind3(t *testing.T) { - var teamUser = new(TeamUser) + teamUser := new(TeamUser) assert.NoError(t, PrepareEngine()) err := testEngine.Sync(new(Team), teamUser) assert.NoError(t, err) @@ -426,7 +426,7 @@ func TestFindBool(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, cnt) - var results = make([]FindBoolStruct, 0, 2) + results := make([]FindBoolStruct, 0, 2) err = testEngine.Find(&results) assert.NoError(t, err) assert.EqualValues(t, 2, len(results)) @@ -457,7 +457,7 @@ func TestFindMark(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, cnt) - var results = make([]Mark, 0, 2) + results := make([]Mark, 0, 2) err = testEngine.Find(&results) assert.NoError(t, err) assert.EqualValues(t, 2, len(results)) @@ -486,7 +486,7 @@ func TestFindAndCountOneFunc(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, cnt) - var results = make([]FindAndCountStruct, 0, 2) + results := make([]FindAndCountStruct, 0, 2) cnt, err = testEngine.Limit(1).FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) @@ -611,14 +611,14 @@ func TestFindAndCount2(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestFindAndCountUser), new(TestFindAndCountHotel)) - var u = TestFindAndCountUser{ + u := TestFindAndCountUser{ Name: "myname", } cnt, err := testEngine.Insert(&u) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var hotel = TestFindAndCountHotel{ + hotel := TestFindAndCountHotel{ Name: "myhotel", Code: "111", Region: "222", @@ -1063,7 +1063,7 @@ func TestUpdateFind(t *testing.T) { session := testEngine.NewSession() defer session.Close() - var tuf = TestUpdateFind{ + tuf := TestUpdateFind{ Name: "test", } _, err := session.Insert(&tuf) @@ -1095,7 +1095,7 @@ func TestFindAnonymousStruct(t *testing.T) { assert.EqualValues(t, 1, cnt) assert.NoError(t, err) - var findRes = make([]struct { + findRes := make([]struct { Id int64 Name string }, 0) @@ -1115,3 +1115,47 @@ func TestFindAnonymousStruct(t *testing.T) { assert.EqualValues(t, 1, findRes[0].Id) assert.EqualValues(t, "xlw", findRes[0].Name) } + +func TestFindBytesVars(t *testing.T) { + type FindBytesVars struct { + Id int64 + Bytes1 []byte + Bytes2 []byte + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(FindBytesVars)) + + _, err := testEngine.Insert([]FindBytesVars{ + { + Bytes1: []byte("bytes1"), + Bytes2: []byte("bytes2"), + }, + { + Bytes1: []byte("bytes1-1"), + Bytes2: []byte("bytes2-2"), + }, + }) + assert.NoError(t, err) + + var gbv []FindBytesVars + err = testEngine.Find(&gbv) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(gbv)) + assert.EqualValues(t, []byte("bytes1"), gbv[0].Bytes1) + assert.EqualValues(t, []byte("bytes2"), gbv[0].Bytes2) + assert.EqualValues(t, []byte("bytes1-1"), gbv[1].Bytes1) + assert.EqualValues(t, []byte("bytes2-2"), gbv[1].Bytes2) + + err = testEngine.Find(&gbv) + assert.NoError(t, err) + assert.EqualValues(t, 4, len(gbv)) + assert.EqualValues(t, []byte("bytes1"), gbv[0].Bytes1) + assert.EqualValues(t, []byte("bytes2"), gbv[0].Bytes2) + assert.EqualValues(t, []byte("bytes1-1"), gbv[1].Bytes1) + assert.EqualValues(t, []byte("bytes2-2"), gbv[1].Bytes2) + assert.EqualValues(t, []byte("bytes1"), gbv[2].Bytes1) + assert.EqualValues(t, []byte("bytes2"), gbv[2].Bytes2) + assert.EqualValues(t, []byte("bytes1-1"), gbv[3].Bytes1) + assert.EqualValues(t, []byte("bytes2-2"), gbv[3].Bytes2) +} diff --git a/integrations/session_get_test.go b/integrations/session_get_test.go index 5d1558f4..841ec709 100644 --- a/integrations/session_get_test.go +++ b/integrations/session_get_test.go @@ -35,7 +35,7 @@ func TestGetVar(t *testing.T) { assert.NoError(t, testEngine.Sync(new(GetVar))) - var data = GetVar{ + data := GetVar{ Msg: "hi", Age: 28, Money: 1.5, @@ -175,7 +175,7 @@ func TestGetVar(t *testing.T) { assert.NoError(t, err) assert.Equal(t, false, has) - var valuesString = make(map[string]string) + valuesString := make(map[string]string) has, err = testEngine.Table("get_var").Get(&valuesString) assert.NoError(t, err) assert.Equal(t, true, has) @@ -187,7 +187,7 @@ func TestGetVar(t *testing.T) { // for mymysql driver, interface{} will be []byte, so ignore it currently if testEngine.DriverName() != "mymysql" { - var valuesInter = make(map[string]interface{}) + valuesInter := make(map[string]interface{}) has, err = testEngine.Table("get_var").Where("`id` = ?", 1).Select("*").Get(&valuesInter) assert.NoError(t, err) assert.Equal(t, true, has) @@ -198,7 +198,7 @@ func TestGetVar(t *testing.T) { assert.Equal(t, "1.5", fmt.Sprintf("%v", valuesInter["money"])) } - var valuesSliceString = make([]string, 5) + valuesSliceString := make([]string, 5) has, err = testEngine.Table("get_var").Get(&valuesSliceString) assert.NoError(t, err) assert.Equal(t, true, has) @@ -207,7 +207,7 @@ func TestGetVar(t *testing.T) { assert.Equal(t, "28", valuesSliceString[2]) assert.Equal(t, "1.5", valuesSliceString[3]) - var valuesSliceInter = make([]interface{}, 5) + valuesSliceInter := make([]interface{}, 5) has, err = testEngine.Table("get_var").Get(&valuesSliceInter) assert.NoError(t, err) assert.Equal(t, true, has) @@ -317,7 +317,7 @@ func TestGetMap(t *testing.T) { _, err := testEngine.Exec(fmt.Sprintf("INSERT INTO %s (`is_man`) VALUES (NULL)", tableName)) assert.NoError(t, err) - var valuesString = make(map[string]string) + valuesString := make(map[string]string) has, err := testEngine.Table("userinfo_map").Get(&valuesString) assert.NoError(t, err) assert.Equal(t, true, has) @@ -336,7 +336,7 @@ func TestGetError(t *testing.T) { assertSync(t, new(GetError)) - var info = new(GetError) + info := new(GetError) has, err := testEngine.Get(&info) assert.False(t, has) assert.Error(t, err) @@ -456,7 +456,7 @@ func TestGetActionMapping(t *testing.T) { }) assert.NoError(t, err) - var valuesSlice = make([]string, 2) + valuesSlice := make([]string, 2) has, err := testEngine.Table(new(ActionMapping)). Cols("script_id", "rollback_id"). ID("1").Get(&valuesSlice) @@ -483,7 +483,7 @@ func TestGetStructId(t *testing.T) { Id int64 } - //var id int64 + // var id int64 var maxid maxidst sql := "select max(`id`) as id from " + testEngine.Quote(testEngine.TableName(&TestGetStruct{}, true)) has, err := testEngine.SQL(sql).Get(&maxid) @@ -693,7 +693,7 @@ func TestCustomTypes(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestCustomizeStruct)) - var s = TestCustomizeStruct{ + s := TestCustomizeStruct{ Name: "test", Age: 32, } @@ -763,7 +763,7 @@ func TestGetBigFloat(t *testing.T) { assertSync(t, new(GetBigFloat)) { - var gf = GetBigFloat{ + gf := GetBigFloat{ Money: big.NewFloat(999999.99), } _, err := testEngine.Insert(&gf) @@ -774,8 +774,8 @@ func TestGetBigFloat(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) - //fmt.Println(m.Cmp(gf.Money)) - //assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String()) + // fmt.Println(m.Cmp(gf.Money)) + // assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String()) } type GetBigFloat2 struct { @@ -788,7 +788,7 @@ func TestGetBigFloat(t *testing.T) { assertSync(t, new(GetBigFloat2)) { - var gf2 = GetBigFloat2{ + gf2 := GetBigFloat2{ Money: big.NewFloat(9999999.99), Money2: *big.NewFloat(99.99), } @@ -800,8 +800,8 @@ func TestGetBigFloat(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.True(t, m2.String() == gf2.Money.String(), "%v != %v", m2.String(), gf2.Money.String()) - //fmt.Println(m.Cmp(gf.Money)) - //assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String()) + // fmt.Println(m.Cmp(gf.Money)) + // assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String()) var gf3 GetBigFloat2 has, err = testEngine.ID(gf2.Id).Get(&gf3) @@ -829,7 +829,7 @@ func TestGetDecimal(t *testing.T) { assertSync(t, new(GetDecimal)) { - var gf = GetDecimal{ + gf := GetDecimal{ Money: decimal.NewFromFloat(999999.99), } _, err := testEngine.Insert(&gf) @@ -840,8 +840,8 @@ func TestGetDecimal(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) - //fmt.Println(m.Cmp(gf.Money)) - //assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String()) + // fmt.Println(m.Cmp(gf.Money)) + // assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String()) } type GetDecimal2 struct { @@ -854,7 +854,7 @@ func TestGetDecimal(t *testing.T) { { v := decimal.NewFromFloat(999999.99) - var gf = GetDecimal2{ + gf := GetDecimal2{ Money: &v, } _, err := testEngine.Insert(&gf) @@ -865,10 +865,11 @@ func TestGetDecimal(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) - //fmt.Println(m.Cmp(gf.Money)) - //assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String()) + // fmt.Println(m.Cmp(gf.Money)) + // assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String()) } } + func TestGetTime(t *testing.T) { type GetTimeStruct struct { Id int64 @@ -878,7 +879,7 @@ func TestGetTime(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(GetTimeStruct)) - var gts = GetTimeStruct{ + gts := GetTimeStruct{ CreateTime: time.Now().In(testEngine.GetTZLocation()), } _, err := testEngine.Insert(>s) @@ -976,3 +977,39 @@ func TestGetWithPrepare(t *testing.T) { err = sess.Commit() assert.NoError(t, err) } + +func TestGetBytesVars(t *testing.T) { + type GetBytesVars struct { + Id int64 + Bytes1 []byte + Bytes2 []byte + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(GetBytesVars)) + + _, err := testEngine.Insert([]GetBytesVars{ + { + Bytes1: []byte("bytes1"), + Bytes2: []byte("bytes2"), + }, + { + Bytes1: []byte("bytes1-1"), + Bytes2: []byte("bytes2-2"), + }, + }) + assert.NoError(t, err) + + var gbv GetBytesVars + has, err := testEngine.Asc("id").Get(&gbv) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, []byte("bytes1"), gbv.Bytes1) + assert.EqualValues(t, []byte("bytes2"), gbv.Bytes2) + + has, err = testEngine.Desc("id").NoAutoCondition().Get(&gbv) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, []byte("bytes1-1"), gbv.Bytes1) + assert.EqualValues(t, []byte("bytes2-2"), gbv.Bytes2) +} diff --git a/scan.go b/scan.go index 10988bdb..00cee4d7 100644 --- a/scan.go +++ b/scan.go @@ -22,7 +22,7 @@ func genScanResultsByBeanNullable(bean interface{}) (interface{}, bool, error) { switch t := bean.(type) { case *interface{}: return t, false, nil - case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString, *sql.RawBytes: + case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString, *sql.RawBytes, *[]byte: return t, false, nil case *time.Time: return &sql.NullString{}, true, nil @@ -67,7 +67,7 @@ func genScanResultsByBeanNullable(bean interface{}) (interface{}, bool, error) { case reflect.Uint32, reflect.Uint, reflect.Uint16, reflect.Uint8: return &convert.NullUint32{}, true, nil default: - return nil, false, fmt.Errorf("unsupported type: %#v", bean) + return nil, false, fmt.Errorf("genScanResultsByBeanNullable: unsupported type: %#v", bean) } } @@ -125,12 +125,12 @@ func genScanResultsByBean(bean interface{}) (interface{}, bool, error) { case reflect.Float64: return new(float64), true, nil default: - return nil, false, fmt.Errorf("unsupported type: %#v", bean) + return nil, false, fmt.Errorf("genScanResultsByBean: unsupported type: %#v", bean) } } func (engine *Engine) scanStringInterface(rows *core.Rows, fields []string, types []*sql.ColumnType) ([]interface{}, error) { - var scanResults = make([]interface{}, len(types)) + scanResults := make([]interface{}, len(types)) for i := 0; i < len(types); i++ { var s sql.NullString scanResults[i] = &s @@ -144,8 +144,8 @@ func (engine *Engine) scanStringInterface(rows *core.Rows, fields []string, type // scan is a wrap of driver.Scan but will automatically change the input values according requirements func (engine *Engine) scan(rows *core.Rows, fields []string, types []*sql.ColumnType, vv ...interface{}) error { - var scanResults = make([]interface{}, 0, len(types)) - var replaces = make([]bool, 0, len(types)) + scanResults := make([]interface{}, 0, len(types)) + replaces := make([]bool, 0, len(types)) var err error for _, v := range vv { var replaced bool @@ -194,7 +194,7 @@ func (engine *Engine) scan(rows *core.Rows, fields []string, types []*sql.Column } func (engine *Engine) scanInterfaces(rows *core.Rows, fields []string, types []*sql.ColumnType) ([]interface{}, error) { - var scanResultContainers = make([]interface{}, len(types)) + scanResultContainers := make([]interface{}, len(types)) for i := 0; i < len(types); i++ { scanResult, err := engine.driver.GenScanResult(types[i].DatabaseTypeName()) if err != nil { @@ -212,8 +212,8 @@ func (engine *Engine) scanInterfaces(rows *core.Rows, fields []string, types []* // row -> map[string]interface{} func (engine *Engine) row2mapInterface(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]interface{}, error) { - var resultsMap = make(map[string]interface{}, len(fields)) - var scanResultContainers = make([]interface{}, len(fields)) + resultsMap := make(map[string]interface{}, len(fields)) + scanResultContainers := make([]interface{}, len(fields)) for i := 0; i < len(fields); i++ { scanResult, err := engine.driver.GenScanResult(types[i].DatabaseTypeName()) if err != nil { @@ -277,7 +277,7 @@ func (engine *Engine) ScanInterfaceMaps(rows *core.Rows) (resultsSlice []map[str // row -> map[string]string func (engine *Engine) row2mapStr(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]string, error) { - var scanResults = make([]interface{}, len(fields)) + scanResults := make([]interface{}, len(fields)) for i := 0; i < len(fields); i++ { var s sql.NullString scanResults[i] = &s @@ -353,7 +353,7 @@ func (engine *Engine) ScanStringMaps(rows *core.Rows) (resultsSlice []map[string // row -> map[string][]byte func convertMapStr2Bytes(m map[string]string) map[string][]byte { - var r = make(map[string][]byte, len(m)) + r := make(map[string][]byte, len(m)) for k, v := range m { r[k] = []byte(v) } @@ -392,7 +392,7 @@ func (engine *Engine) row2sliceStr(rows *core.Rows, types []*sql.ColumnType, fie return nil, err } - var results = make([]string, 0, len(fields)) + results := make([]string, 0, len(fields)) for i := 0; i < len(fields); i++ { results = append(results, scanResults[i].(*sql.NullString).String) } diff --git a/session.go b/session.go index 3fc53e23..64b98bfe 100644 --- a/session.go +++ b/session.go @@ -79,7 +79,7 @@ type Session struct { afterClosures []func(interface{}) afterProcessors []executedProcessor - stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) + stmtCache map[uint32]*core.Stmt // key: hash.Hash32 of (queryStr, len(queryStr)) txStmtCache map[uint32]*core.Stmt // for tx statement lastSQL string @@ -314,7 +314,7 @@ func (session *Session) Cascade(trueOrFalse ...bool) *Session { // MustLogSQL means record SQL or not and don't follow engine's setting func (session *Session) MustLogSQL(logs ...bool) *Session { - var showSQL = true + showSQL := true if len(logs) > 0 { showSQL = logs[0] } @@ -396,7 +396,7 @@ func (session *Session) doPrepareTx(sqlStr string) (stmt *core.Stmt, err error) } func getField(dataStruct *reflect.Value, table *schemas.Table, colName string, idx int) (*schemas.Column, *reflect.Value, error) { - var col = table.GetColumnIdx(colName, idx) + col := table.GetColumnIdx(colName, idx) if col == nil { return nil, nil, ErrFieldIsNotExist{colName, table.Name} } @@ -420,9 +420,10 @@ type Cell *interface{} func (session *Session) rows2Beans(rows *core.Rows, fields []string, types []*sql.ColumnType, table *schemas.Table, newElemFunc func([]string) reflect.Value, - sliceValueSetFunc func(*reflect.Value, schemas.PK) error) error { + sliceValueSetFunc func(*reflect.Value, schemas.PK) error, +) error { for rows.Next() { - var newValue = newElemFunc(fields) + newValue := newElemFunc(fields) bean := newValue.Interface() dataStruct := newValue.Elem() @@ -533,8 +534,11 @@ func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) { return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv) } +var uint8ZeroValue = reflect.ValueOf(uint8(0)) + func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflect.Value, - scanResult interface{}, table *schemas.Table) error { + scanResult interface{}, table *schemas.Table, +) error { v, ok := scanResult.(*interface{}) if ok { scanResult = *v @@ -596,7 +600,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec return nil case reflect.Complex64, reflect.Complex128: return setJSON(fieldValue, fieldType, scanResult) - case reflect.Slice, reflect.Array: + case reflect.Slice: bs, ok := convert.AsBytes(scanResult) if ok && fieldType.Elem().Kind() == reflect.Uint8 { if col.SQLType.IsText() { @@ -607,15 +611,29 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } fieldValue.Set(x.Elem()) } else { - if fieldValue.Len() > 0 { - for i := 0; i < fieldValue.Len(); i++ { - if i < vv.Len() { - fieldValue.Index(i).Set(vv.Index(i)) - } - } - } else { - for i := 0; i < vv.Len(); i++ { - fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i))) + fieldValue.Set(reflect.ValueOf(bs)) + } + return nil + } + case reflect.Array: + bs, ok := convert.AsBytes(scanResult) + if ok && fieldType.Elem().Kind() == reflect.Uint8 { + if col.SQLType.IsText() { + x := reflect.New(fieldType) + err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface()) + if err != nil { + return err + } + fieldValue.Set(x.Elem()) + } else { + if fieldValue.Len() < vv.Len() { + return fmt.Errorf("Set field %s[Array] failed because of data too long", col.Name) + } + for i := 0; i < fieldValue.Len(); i++ { + if i < vv.Len() { + fieldValue.Index(i).Set(vv.Index(i)) + } else { + fieldValue.Index(i).Set(uint8ZeroValue) } } } @@ -659,7 +677,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec if len(table.PrimaryKeys) != 1 { return errors.New("unsupported non or composited primary key cascade") } - var pk = make(schemas.PK, len(table.PrimaryKeys)) + pk := make(schemas.PK, len(table.PrimaryKeys)) pk[0], err = asKind(vv, reflect.TypeOf(scanResult)) if err != nil { return err @@ -694,11 +712,11 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b buildAfterProcessors(session, bean) - var tempMap = make(map[string]int) + tempMap := make(map[string]int) var pk schemas.PK for i, colName := range fields { var idx int - var lKey = strings.ToLower(colName) + lKey := strings.ToLower(colName) var ok bool if idx, ok = tempMap[lKey]; !ok { From e1d43656672824d61332eeec7d529402dcc5e0bf Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bonicoli Date: Fri, 22 Apr 2022 10:48:53 +0800 Subject: [PATCH 055/116] MySQL/MariaDB: return max length for text columns (#2133) MySQL/MariaDB: return max length for text columns using `CHARACTER_MAXIMUM_LENGTH`. Tests: * add an integration test: `TestGetColumnsLength` * update `TestSyncTable3` since `TableInfo` isn't able to provide the column size Co-authored-by: Pierre-Louis Bonicoli Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2133 Reviewed-by: Lunny Xiao Co-authored-by: Pierre-Louis Bonicoli Co-committed-by: Pierre-Louis Bonicoli --- dialects/mysql.go | 14 +++++++++--- integrations/engine_test.go | 33 +++++++++++++++++++++++++++++ integrations/session_schema_test.go | 5 ++++- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/dialects/mysql.go b/dialects/mysql.go index 1fad3fee..56ba66c7 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -399,7 +399,7 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName "(SUBSTRING_INDEX(SUBSTRING(VERSION(), 4), '.', 1) = 2 && " + "SUBSTRING_INDEX(SUBSTRING(VERSION(), 6), '-', 1) >= 7)))))" s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," + - " `COLUMN_KEY`, `EXTRA`, `COLUMN_COMMENT`, " + + " `COLUMN_KEY`, `EXTRA`, `COLUMN_COMMENT`, `CHARACTER_MAXIMUM_LENGTH`, " + alreadyQuoted + " AS NEEDS_QUOTE " + "FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" + " ORDER BY `COLUMNS`.ORDINAL_POSITION" @@ -418,8 +418,8 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName var columnName, nullableStr, colType, colKey, extra, comment string var alreadyQuoted, isUnsigned bool - var colDefault *string - err = rows.Scan(&columnName, &nullableStr, &colDefault, &colType, &colKey, &extra, &comment, &alreadyQuoted) + var colDefault, maxLength *string + err = rows.Scan(&columnName, &nullableStr, &colDefault, &colType, &colKey, &extra, &comment, &maxLength, &alreadyQuoted) if err != nil { return nil, nil, err } @@ -478,6 +478,14 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName } } } + } else { + switch colType { + case "MEDIUMTEXT", "LONGTEXT", "TEXT": + len1, err = strconv.Atoi(*maxLength) + if err != nil { + return nil, nil, err + } + } } if isUnsigned { colType = "UNSIGNED " + colType diff --git a/integrations/engine_test.go b/integrations/engine_test.go index 997c8962..905c4f24 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -288,3 +288,36 @@ func TestGetColumnsComment(t *testing.T) { assert.Equal(t, comment, hasComment) assert.Zero(t, noComment) } + +func TestGetColumnsLength(t *testing.T) { + var max_length int + switch testEngine.Dialect().URI().DBType { + case + schemas.POSTGRES: + max_length = 0 + case + schemas.MYSQL: + max_length = 65535 + default: + t.Skip() + return + } + + type TestLengthStringStruct struct { + Content string `xorm:"TEXT NOT NULL"` + } + + assertSync(t, new(TestLengthStringStruct)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + tableLengthStringName := testEngine.GetColumnMapper().Obj2Table("TestLengthStringStruct") + for _, table := range tables { + if table.Name == tableLengthStringName { + col := table.GetColumn("content") + assert.Equal(t, col.Length, max_length) + assert.Zero(t, col.Length2) + break + } + } +} diff --git a/integrations/session_schema_test.go b/integrations/session_schema_test.go index 7dc0af76..3212d027 100644 --- a/integrations/session_schema_test.go +++ b/integrations/session_schema_test.go @@ -6,6 +6,7 @@ package integrations import ( "fmt" + "strings" "testing" "time" @@ -248,7 +249,9 @@ func TestSyncTable3(t *testing.T) { tableInfo, err := testEngine.TableInfo(new(SyncTable5)) assert.NoError(t, err) assert.EqualValues(t, testEngine.Dialect().SQLType(tableInfo.GetColumn("name")), testEngine.Dialect().SQLType(tables[0].GetColumn("name"))) - assert.EqualValues(t, testEngine.Dialect().SQLType(tableInfo.GetColumn("text")), testEngine.Dialect().SQLType(tables[0].GetColumn("text"))) + /* Engine.DBMetas() returns the size of the column from the database but Engine.TableInfo() might not be able to guess the column size. + For example using MySQL/MariaDB: when utf-8 charset is used, "`xorm:"TEXT(21846)`" creates a MEDIUMTEXT column not a TEXT column. */ + assert.True(t, testEngine.Dialect().SQLType(tables[0].GetColumn("text")) == testEngine.Dialect().SQLType(tableInfo.GetColumn("text")) || strings.HasPrefix(testEngine.Dialect().SQLType(tables[0].GetColumn("text")), testEngine.Dialect().SQLType(tableInfo.GetColumn("text"))+"(")) assert.EqualValues(t, testEngine.Dialect().SQLType(tableInfo.GetColumn("char")), testEngine.Dialect().SQLType(tables[0].GetColumn("char"))) assert.EqualValues(t, testEngine.Dialect().SQLType(tableInfo.GetColumn("ten_char")), testEngine.Dialect().SQLType(tables[0].GetColumn("ten_char"))) assert.EqualValues(t, testEngine.Dialect().SQLType(tableInfo.GetColumn("ten_var_char")), testEngine.Dialect().SQLType(tables[0].GetColumn("ten_var_char"))) From 2c064b6da69c93795c29074590f5f7c6d5820b4a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 22 Apr 2022 14:56:26 +0800 Subject: [PATCH 056/116] Add test for find date (#2121) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2121 --- integrations/session_find_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index ae8779ff..7f42d096 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -1159,3 +1159,31 @@ func TestFindBytesVars(t *testing.T) { assert.EqualValues(t, []byte("bytes1-1"), gbv[3].Bytes1) assert.EqualValues(t, []byte("bytes2-2"), gbv[3].Bytes2) } + +func TestUpdateFindDate(t *testing.T) { + type TestUpdateFindDate struct { + Id int64 + Name string + Tm time.Time `xorm:"DATE created"` + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(TestUpdateFindDate)) + + session := testEngine.NewSession() + defer session.Close() + + tuf := TestUpdateFindDate{ + Name: "test", + } + _, err := session.Insert(&tuf) + assert.NoError(t, err) + _, err = session.Where("`id` = ?", tuf.Id).Update(&TestUpdateFindDate{}) + assert.EqualError(t, xorm.ErrNoColumnsTobeUpdated, err.Error()) + + var tufs []TestUpdateFindDate + err = session.Find(&tufs) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(tufs)) + assert.EqualValues(t, tuf.Tm.Format("2006-01-02"), tufs[0].Tm.Format("2006-01-02")) +} From f7e9fb74acc97a9e4fedc7a97f444de5f3f235c4 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 23 Apr 2022 17:19:37 +0800 Subject: [PATCH 057/116] return a clear error for set TEXT type as compare condition (#2062) Fix #523 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2062 --- integrations/types_test.go | 23 ++++++++++++++++------- internal/statements/statement.go | 32 +++++++++++++++++++------------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/integrations/types_test.go b/integrations/types_test.go index d166845e..1c815b7a 100644 --- a/integrations/types_test.go +++ b/integrations/types_test.go @@ -30,7 +30,7 @@ func TestArrayField(t *testing.T) { assert.NoError(t, testEngine.Sync(new(ArrayStruct))) - var as = ArrayStruct{ + as := ArrayStruct{ Name: [20]byte{ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, @@ -54,7 +54,7 @@ func TestArrayField(t *testing.T) { assert.EqualValues(t, 1, len(arrs)) assert.Equal(t, as.Name, arrs[0].Name) - var newName = [20]byte{ + newName := [20]byte{ 90, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, @@ -252,9 +252,11 @@ func TestConversion(t *testing.T) { assert.Nil(t, c1.Nullable2) } -type MyInt int -type MyUInt uint -type MyFloat float64 +type ( + MyInt int + MyUInt uint + MyFloat float64 +) type MyStruct struct { Type MyInt @@ -273,7 +275,7 @@ type MyStruct struct { UIA32 []uint32 UIA64 []uint64 UI uint - //C64 complex64 + // C64 complex64 MSS map[string]string } @@ -304,6 +306,13 @@ func TestCustomType1(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) + // since mssql don't support use text as index condition, we have to ignore below + // get and find tests + if testEngine.Dialect().URI().DBType == schemas.MSSQL { + t.Skip() + return + } + fmt.Println(i) i.NameArray = []string{} i.MSS = map[string]string{} @@ -598,7 +607,7 @@ func TestMyArray(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(MyArrayStruct)) - var v = [20]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + v := [20]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} _, err := testEngine.Insert(&MyArrayStruct{ Content: v, }) diff --git a/internal/statements/statement.go b/internal/statements/statement.go index 2a7ae8b0..3069561e 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -304,7 +304,7 @@ func (statement *Statement) needTableName() bool { func (statement *Statement) colName(col *schemas.Column, tableName string) string { if statement.needTableName() { - var nm = tableName + nm := tableName if len(statement.TableAlias) > 0 { nm = statement.TableAlias } @@ -765,7 +765,7 @@ func (statement *Statement) asDBCond(fieldValue reflect.Value, fieldType reflect if len(table.PrimaryKeys) == 1 { pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) // fix non-int pk issues - //if pkField.Int() != 0 { + // if pkField.Int() != 0 { if pkField.IsValid() && !utils.IsZero(pkField.Interface()) { return pkField.Interface(), true, nil } @@ -814,7 +814,8 @@ func (statement *Statement) asDBCond(fieldValue reflect.Value, fieldType reflect func (statement *Statement) buildConds2(table *schemas.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool, - mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) { + mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool, +) (builder.Cond, error) { var conds []builder.Cond for _, col := range table.Columns() { if !includeVersion && col.IsVersion { @@ -827,17 +828,13 @@ func (statement *Statement) buildConds2(table *schemas.Table, bean interface{}, continue } - if statement.dialect.URI().DBType == schemas.MSSQL && (col.SQLType.Name == schemas.Text || - col.SQLType.IsBlob() || col.SQLType.Name == schemas.TimeStampz) { - continue - } if col.IsJSON { continue } var colName string if addedTableName { - var nm = tableName + nm := tableName if len(aliasName) > 0 { nm = aliasName } @@ -862,6 +859,15 @@ func (statement *Statement) buildConds2(table *schemas.Table, bean interface{}, continue } + if statement.dialect.URI().DBType == schemas.MSSQL && (col.SQLType.Name == schemas.Text || + col.SQLType.IsBlob() || col.SQLType.Name == schemas.TimeStampz) { + if utils.IsValueZero(fieldValue) { + continue + } + + return nil, fmt.Errorf("column %s is a TEXT type with data %#v which cannot be as compare condition", col.Name, fieldValue.Interface()) + } + requiredField := useAllCols if b, ok := getFlagForColumn(mustColumnMap, col); ok { if b { @@ -910,7 +916,7 @@ func (statement *Statement) BuildConds(table *schemas.Table, bean interface{}, i func (statement *Statement) mergeConds(bean interface{}) error { if !statement.NoAutoCondition && statement.RefTable != nil { - var addedTableName = (len(statement.JoinStr) > 0) + addedTableName := (len(statement.JoinStr) > 0) autoCond, err := statement.BuildConds(statement.RefTable, bean, true, true, false, true, addedTableName) if err != nil { return err @@ -948,7 +954,7 @@ func (statement *Statement) convertSQLOrArgs(sqlOrArgs ...interface{}) (string, switch sqlOrArgs[0].(type) { case string: if len(sqlOrArgs) > 1 { - var newArgs = make([]interface{}, 0, len(sqlOrArgs)-1) + newArgs := make([]interface{}, 0, len(sqlOrArgs)-1) for _, arg := range sqlOrArgs[1:] { if v, ok := arg.(time.Time); ok { newArgs = append(newArgs, v.In(statement.defaultTimeZone).Format("2006-01-02 15:04:05")) @@ -972,7 +978,7 @@ func (statement *Statement) convertSQLOrArgs(sqlOrArgs ...interface{}) (string, } func (statement *Statement) joinColumns(cols []*schemas.Column, includeTableName bool) string { - var colnames = make([]string, len(cols)) + colnames := make([]string, len(cols)) for i, col := range cols { if includeTableName { colnames[i] = statement.quote(statement.TableName()) + @@ -986,7 +992,7 @@ func (statement *Statement) joinColumns(cols []*schemas.Column, includeTableName // CondDeleted returns the conditions whether a record is soft deleted. func (statement *Statement) CondDeleted(col *schemas.Column) builder.Cond { - var colName = statement.quote(col.Name) + colName := statement.quote(col.Name) if statement.JoinStr != "" { var prefix string if statement.TableAlias != "" { @@ -996,7 +1002,7 @@ func (statement *Statement) CondDeleted(col *schemas.Column) builder.Cond { } colName = statement.quote(prefix) + "." + statement.quote(col.Name) } - var cond = builder.NewCond() + cond := builder.NewCond() if col.SQLType.IsNumeric() { cond = builder.Eq{colName: 0} } else { From 26d291bbc311274388045768386f015a06b3be10 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sun, 24 Apr 2022 19:34:27 +0800 Subject: [PATCH 058/116] Add interface to allow structs to provide specific index information (#2137) The current mechanism for adding information about indices cannot express the ordering of the columns in the index or add sorting information. Here we add a new interface TableIndices which a struct would implement to provide a slice of *schema.Index to provide additional indices to that gleaned from the tags. Signed-off-by: Andrew Thornton Reviewed-on: https://gitea.com/xorm/xorm/pulls/2137 Reviewed-by: Lunny Xiao Co-authored-by: Andrew Thornton Co-committed-by: Andrew Thornton --- integrations/tags_test.go | 108 ++++++++++++++++++++++++++++---------- tags/parser.go | 58 +++++++++++++++++--- 2 files changed, 132 insertions(+), 34 deletions(-) diff --git a/integrations/tags_test.go b/integrations/tags_test.go index 247a64e8..4c33d56c 100644 --- a/integrations/tags_test.go +++ b/integrations/tags_test.go @@ -165,7 +165,7 @@ func TestExtends(t *testing.T) { assert.True(t, info2.Userinfo.Uid > 0, "all of the id should has value") assert.True(t, info2.Userdetail.Id > 0, "all of the id should has value") - var infos2 = make([]UserAndDetail, 0) + infos2 := make([]UserAndDetail, 0) err = testEngine.Table(&Userinfo{}). Join("LEFT", qt(ud), qt(ui)+"."+qt("detail_id")+" = "+qt(ud)+"."+qt(uiid)). NoCascade(). @@ -219,9 +219,9 @@ func TestExtends2(t *testing.T) { err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) assert.NoError(t, err) - var sender = MessageUser{Name: "sender"} - var receiver = MessageUser{Name: "receiver"} - var msgtype = MessageType{Name: "type"} + sender := MessageUser{Name: "sender"} + receiver := MessageUser{Name: "receiver"} + msgtype := MessageType{Name: "type"} _, err = testEngine.Insert(&sender, &receiver, &msgtype) assert.NoError(t, err) @@ -254,8 +254,8 @@ func TestExtends2(t *testing.T) { assert.NoError(t, err) } - var mapper = testEngine.GetTableMapper().Obj2Table - var quote = testEngine.Quote + mapper := testEngine.GetTableMapper().Obj2Table + quote := testEngine.Quote userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) msgTableName := quote(testEngine.TableName(mapper("Message"), true)) @@ -280,9 +280,9 @@ func TestExtends3(t *testing.T) { err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) assert.NoError(t, err) - var sender = MessageUser{Name: "sender"} - var receiver = MessageUser{Name: "receiver"} - var msgtype = MessageType{Name: "type"} + sender := MessageUser{Name: "sender"} + receiver := MessageUser{Name: "receiver"} + msgtype := MessageType{Name: "type"} _, err = testEngine.Insert(&sender, &receiver, &msgtype) assert.NoError(t, err) @@ -314,8 +314,8 @@ func TestExtends3(t *testing.T) { assert.NoError(t, err) } - var mapper = testEngine.GetTableMapper().Obj2Table - var quote = testEngine.Quote + mapper := testEngine.GetTableMapper().Obj2Table + quote := testEngine.Quote userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) msgTableName := quote(testEngine.TableName(mapper("Message"), true)) @@ -345,8 +345,8 @@ func TestExtends4(t *testing.T) { err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) assert.NoError(t, err) - var sender = MessageUser{Name: "sender"} - var msgtype = MessageType{Name: "type"} + sender := MessageUser{Name: "sender"} + msgtype := MessageType{Name: "type"} _, err = testEngine.Insert(&sender, &msgtype) assert.NoError(t, err) @@ -377,8 +377,8 @@ func TestExtends4(t *testing.T) { assert.NoError(t, err) } - var mapper = testEngine.GetTableMapper().Obj2Table - var quote = testEngine.Quote + mapper := testEngine.GetTableMapper().Obj2Table + quote := testEngine.Quote userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) msgTableName := quote(testEngine.TableName(mapper("Message"), true)) @@ -417,29 +417,29 @@ func TestExtends5(t *testing.T) { err = testEngine.CreateTables(&Size{}, &Book{}) assert.NoError(t, err) - var sc = Size{Width: 0.2, Height: 0.4} - var so = Size{Width: 0.2, Height: 0.8} - var s = Size{Width: 0.15, Height: 1.5} - var bk1 = Book{ + sc := Size{Width: 0.2, Height: 0.4} + so := Size{Width: 0.2, Height: 0.8} + s := Size{Width: 0.15, Height: 1.5} + bk1 := Book{ SizeOpen: &so, SizeClosed: &sc, Size: &s, } - var bk2 = Book{ + bk2 := Book{ SizeOpen: &so, } - var bk3 = Book{ + bk3 := Book{ SizeClosed: &sc, Size: &s, } - var bk4 = Book{} - var bk5 = Book{Size: &s} + bk4 := Book{} + bk5 := Book{Size: &s} _, err = testEngine.Insert(&sc, &so, &s, &bk1, &bk2, &bk3, &bk4, &bk5) if err != nil { t.Fatal(err) } - var books = map[int64]Book{ + books := map[int64]Book{ bk1.ID: bk1, bk2.ID: bk2, bk3.ID: bk3, @@ -450,8 +450,8 @@ func TestExtends5(t *testing.T) { session := testEngine.NewSession() defer session.Close() - var mapper = testEngine.GetTableMapper().Obj2Table - var quote = testEngine.Quote + mapper := testEngine.GetTableMapper().Obj2Table + quote := testEngine.Quote bookTableName := quote(testEngine.TableName(mapper("Book"), true)) sizeTableName := quote(testEngine.TableName(mapper("Size"), true)) @@ -1301,7 +1301,7 @@ func TestVersion2(t *testing.T) { err = testEngine.CreateTables(new(VersionS)) assert.NoError(t, err) - var vers = []VersionS{ + vers := []VersionS{ {Name: "sfsfdsfds"}, {Name: "xxxxx"}, } @@ -1359,7 +1359,7 @@ func TestVersion4(t *testing.T) { err = testEngine.CreateTables(new(VersionUintS)) assert.NoError(t, err) - var vers = []VersionUintS{ + vers := []VersionUintS{ {Name: "sfsfdsfds"}, {Name: "xxxxx"}, } @@ -1400,3 +1400,55 @@ func TestIndexes(t *testing.T) { assert.EqualValues(t, slice1, slice2) assert.EqualValues(t, 3, len(tables[0].Indexes)) } + +type TestTableIndicesStruct struct { + Id int64 + Name string `xorm:"index index(f_one_f_two) unique(s)"` // we're going to override the index f_one_f_two in TableIndices and remove it from this column + Email string `xorm:"index unique(s)"` + FTwo string `xorm:"index(f_two_f_one) index(f_one_f_two) f_two"` + FOne string `xorm:"index(f_two_f_one) f_one"` +} + +func (t *TestTableIndicesStruct) TableIndices() []*schemas.Index { + newIndex := schemas.NewIndex("f_one_f_two", schemas.IndexType) + newIndex.AddColumn("f_one", "f_two") + + return []*schemas.Index{newIndex} +} + +func TestTableIndices(t *testing.T) { + assert.NoError(t, PrepareEngine()) + + assertSync(t, new(TestTableIndicesStruct)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + assert.EqualValues(t, 1, len(tables)) + assert.EqualValues(t, 5, len(tables[0].Columns())) + slice1 := []string{ + testEngine.GetColumnMapper().Obj2Table("Id"), + testEngine.GetColumnMapper().Obj2Table("Name"), + testEngine.GetColumnMapper().Obj2Table("Email"), + testEngine.GetColumnMapper().Obj2Table("FTwo"), + testEngine.GetColumnMapper().Obj2Table("FOne"), + } + slice2 := []string{ + tables[0].Columns()[0].Name, + tables[0].Columns()[1].Name, + tables[0].Columns()[2].Name, + tables[0].Columns()[3].Name, + tables[0].Columns()[4].Name, + } + sort.Strings(slice1) + sort.Strings(slice2) + assert.EqualValues(t, slice1, slice2) + assert.EqualValues(t, 5, len(tables[0].Indexes)) + index, ok := tables[0].Indexes["f_one_f_two"] + if assert.True(t, ok) { + assert.EqualValues(t, []string{"f_one", "f_two"}, index.Cols) + } + index, ok = tables[0].Indexes["f_two_f_one"] + if assert.True(t, ok) { + assert.EqualValues(t, []string{"f_two", "f_one"}, index.Cols) + } +} diff --git a/tags/parser.go b/tags/parser.go index 83026862..028f8d0b 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -21,10 +21,15 @@ import ( "xorm.io/xorm/schemas" ) -var ( - // ErrUnsupportedType represents an unsupported type error - ErrUnsupportedType = errors.New("unsupported type") -) +// ErrUnsupportedType represents an unsupported type error +var ErrUnsupportedType = errors.New("unsupported type") + +// TableIndices is an interface that describes structs that provide additional index information above that which is automatically parsed +type TableIndices interface { + TableIndices() []*schemas.Index +} + +var tpTableIndices = reflect.TypeOf((*TableIndices)(nil)).Elem() // Parser represents a parser for xorm tag type Parser struct { @@ -177,7 +182,7 @@ func (parser *Parser) parseFieldWithNoTag(fieldIndex int, field reflect.StructFi } func (parser *Parser) parseFieldWithTags(table *schemas.Table, fieldIndex int, field reflect.StructField, fieldValue reflect.Value, tags []tag) (*schemas.Column, error) { - var col = &schemas.Column{ + col := &schemas.Column{ FieldName: field.Name, FieldIndex: []int{fieldIndex}, Nullable: true, @@ -188,7 +193,7 @@ func (parser *Parser) parseFieldWithTags(table *schemas.Table, fieldIndex int, f DefaultIsEmpty: true, } - var ctx = Context{ + ctx := Context{ table: table, col: col, fieldValue: fieldValue, @@ -329,5 +334,46 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) { table.AddColumn(col) } // end for + indices := tableIndices(v) + for _, index := range indices { + // Override old information + if oldIndex, ok := table.Indexes[index.Name]; ok { + for _, colName := range oldIndex.Cols { + col := table.GetColumn(colName) + if col == nil { + return nil, ErrUnsupportedType + } + delete(col.Indexes, index.Name) + } + } + table.AddIndex(index) + for _, colName := range index.Cols { + col := table.GetColumn(colName) + if col == nil { + return nil, ErrUnsupportedType + } + col.Indexes[index.Name] = index.Type + } + } + return table, nil } + +func tableIndices(v reflect.Value) []*schemas.Index { + if v.Type().Implements(tpTableIndices) { + return v.Interface().(TableIndices).TableIndices() + } + + if v.Kind() == reflect.Ptr { + v = v.Elem() + if v.Type().Implements(tpTableIndices) { + return v.Interface().(TableIndices).TableIndices() + } + } else if v.CanAddr() { + v1 := v.Addr() + if v1.Type().Implements(tpTableIndices) { + return v1.Interface().(TableIndices).TableIndices() + } + } + return nil +} From 60540cbabee50b897803ff700f3629225713f8d6 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Mon, 30 May 2022 00:29:03 +0800 Subject: [PATCH 059/116] Remove const insertSelectPlaceholder and associated dead code (#2151) `insertSelectPlaceholder` is an unexported const set at true. No code changes this nor can any build environment change it. Therefore we should remove it and the associated dead code. Close #2146 Signed-off-by: Andrew Thornton Reviewed-on: https://gitea.com/xorm/xorm/pulls/2151 Reviewed-by: Lunny Xiao Co-authored-by: Andrew Thornton Co-committed-by: Andrew Thornton --- internal/statements/statement_args.go | 94 +++------------------------ 1 file changed, 8 insertions(+), 86 deletions(-) diff --git a/internal/statements/statement_args.go b/internal/statements/statement_args.go index 64089c1e..727d5977 100644 --- a/internal/statements/statement_args.go +++ b/internal/statements/statement_args.go @@ -5,78 +5,10 @@ package statements import ( - "fmt" - "reflect" - "strings" - "time" - "xorm.io/builder" "xorm.io/xorm/schemas" ) -func quoteNeeded(a interface{}) bool { - switch a.(type) { - case int, int8, int16, int32, int64: - return false - case uint, uint8, uint16, uint32, uint64: - return false - case float32, float64: - return false - case bool: - return false - case string: - return true - case time.Time, *time.Time: - return true - case builder.Builder, *builder.Builder: - return false - } - - t := reflect.TypeOf(a) - switch t.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return false - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return false - case reflect.Float32, reflect.Float64: - return false - case reflect.Bool: - return false - case reflect.String: - return true - } - - return true -} - -func convertStringSingleQuote(arg string) string { - return "'" + strings.Replace(arg, "'", "''", -1) + "'" -} - -func convertString(arg string) string { - var buf strings.Builder - buf.WriteRune('\'') - for _, c := range arg { - if c == '\\' || c == '\'' { - buf.WriteRune('\\') - } - buf.WriteRune(c) - } - buf.WriteRune('\'') - return buf.String() -} - -func convertArg(arg interface{}, convertFunc func(string) string) string { - if quoteNeeded(arg) { - argv := fmt.Sprintf("%v", arg) - return convertFunc(argv) - } - - return fmt.Sprintf("%v", arg) -} - -const insertSelectPlaceHolder = true - // WriteArg writes an arg func (statement *Statement) WriteArg(w *builder.BytesWriter, arg interface{}) error { switch argv := arg.(type) { @@ -91,27 +23,17 @@ func (statement *Statement) WriteArg(w *builder.BytesWriter, arg interface{}) er return err } default: - if insertSelectPlaceHolder { - if err := w.WriteByte('?'); err != nil { - return err - } - if v, ok := arg.(bool); ok && statement.dialect.URI().DBType == schemas.MSSQL { - if v { - w.Append(1) - } else { - w.Append(0) - } + if err := w.WriteByte('?'); err != nil { + return err + } + if v, ok := arg.(bool); ok && statement.dialect.URI().DBType == schemas.MSSQL { + if v { + w.Append(1) } else { - w.Append(arg) + w.Append(0) } } else { - var convertFunc = convertStringSingleQuote - if statement.dialect.URI().DBType == schemas.MYSQL { - convertFunc = convertString - } - if _, err := w.WriteString(convertArg(arg, convertFunc)); err != nil { - return err - } + w.Append(arg) } } return nil From eeb7fcf22cd39c074607643a5d258ce7ea882045 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Mon, 30 May 2022 18:36:23 +0800 Subject: [PATCH 060/116] Add ORDER BY SEQ_IN_INDEX to MySQL GetIndexes to Fix IndexTests (#2152) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2152 Reviewed-by: Lunny Xiao Co-authored-by: Andrew Thornton Co-committed-by: Andrew Thornton --- dialects/mysql.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/dialects/mysql.go b/dialects/mysql.go index 56ba66c7..82df04dd 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -179,11 +179,9 @@ func (db *mysql) Init(uri *URI) error { return db.Base.Init(db, uri) } -var ( - mysqlColAliases = map[string]string{ - "numeric": "decimal", - } -) +var mysqlColAliases = map[string]string{ + "numeric": "decimal", +} // Alias returns a alias of column func (db *mysql) Alias(col string) string { @@ -243,7 +241,7 @@ func (db *mysql) Features() *DialectFeatures { func (db *mysql) SetParams(params map[string]string) { rowFormat, ok := params["rowFormat"] if ok { - var t = strings.ToUpper(rowFormat) + t := strings.ToUpper(rowFormat) switch t { case "COMPACT": fallthrough @@ -562,11 +560,11 @@ func (db *mysql) GetTables(queryer core.Queryer, ctx context.Context) ([]*schema func (db *mysql) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: - var q = mysqlQuoter + q := mysqlQuoter q.IsReserved = schemas.AlwaysNoReserve db.quoter = q case QuotePolicyReserved: - var q = mysqlQuoter + q := mysqlQuoter q.IsReserved = db.IsReserved db.quoter = q case QuotePolicyAlways: @@ -578,7 +576,7 @@ func (db *mysql) SetQuotePolicy(quotePolicy QuotePolicy) { func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName string) (map[string]*schemas.Index, error) { args := []interface{}{db.uri.DBName, tableName} - s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" + s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? ORDER BY `SEQ_IN_INDEX`" rows, err := queryer.QueryContext(ctx, s, args...) if err != nil { @@ -669,7 +667,7 @@ func (db *mysql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table b.WriteString(table.StoreEngine) } - var charset = table.Charset + charset := table.Charset if len(charset) == 0 { charset = db.URI().Charset } From f9a6990ecb22a83eebd359944453dead0b72a8c5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 31 May 2022 11:00:28 +0800 Subject: [PATCH 061/116] Refactor orderby and support arguments (#2150) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2150 --- dialects/mysql.go | 2 +- engine.go | 12 +- go.mod | 2 +- go.sum | 4 +- integrations/session_find_test.go | 4 + interface.go | 2 +- internal/statements/cond.go | 111 +++++++++ internal/statements/join.go | 78 +++++++ internal/statements/order_by.go | 90 ++++++++ internal/statements/query.go | 302 +++++++++++++------------ internal/statements/select.go | 137 +++++++++++ internal/statements/statement.go | 362 ++---------------------------- internal/statements/table_name.go | 56 +++++ internal/utils/builder.go | 27 +++ session.go | 4 +- session_delete.go | 133 +++++------ session_find.go | 22 +- session_update.go | 97 ++++---- 18 files changed, 813 insertions(+), 632 deletions(-) create mode 100644 internal/statements/cond.go create mode 100644 internal/statements/join.go create mode 100644 internal/statements/order_by.go create mode 100644 internal/statements/select.go create mode 100644 internal/statements/table_name.go create mode 100644 internal/utils/builder.go diff --git a/dialects/mysql.go b/dialects/mysql.go index 82df04dd..31e7b788 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -400,7 +400,7 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName " `COLUMN_KEY`, `EXTRA`, `COLUMN_COMMENT`, `CHARACTER_MAXIMUM_LENGTH`, " + alreadyQuoted + " AS NEEDS_QUOTE " + "FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" + - " ORDER BY `COLUMNS`.ORDINAL_POSITION" + " ORDER BY `COLUMNS`.ORDINAL_POSITION ASC" rows, err := queryer.QueryContext(ctx, s, args...) if err != nil { diff --git a/engine.go b/engine.go index b7dcf5a2..81cfc7a9 100644 --- a/engine.go +++ b/engine.go @@ -380,7 +380,7 @@ func (engine *Engine) loadTableInfo(table *schemas.Table) error { seq = 0 } } - var colName = strings.Trim(parts[0], `"`) + colName := strings.Trim(parts[0], `"`) if col := table.GetColumn(colName); col != nil { col.Indexes[index.Name] = index.Type } else { @@ -502,9 +502,9 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w } } - var dstTableName = dstTable.Name - var quoter = dstDialect.Quoter().Quote - var quotedDstTableName = quoter(dstTable.Name) + dstTableName := dstTable.Name + quoter := dstDialect.Quoter().Quote + quotedDstTableName := quoter(dstTable.Name) if dstDialect.URI().Schema != "" { dstTableName = fmt.Sprintf("%s.%s", dstDialect.URI().Schema, dstTable.Name) quotedDstTableName = fmt.Sprintf("%s.%s", quoter(dstDialect.URI().Schema), quoter(dstTable.Name)) @@ -1006,10 +1006,10 @@ func (engine *Engine) Asc(colNames ...string) *Session { } // OrderBy will generate "ORDER BY order" -func (engine *Engine) OrderBy(order string) *Session { +func (engine *Engine) OrderBy(order interface{}, args ...interface{}) *Session { session := engine.NewSession() session.isAutoClose = true - return session.OrderBy(order) + return session.OrderBy(order, args...) } // Prepare enables prepare statement diff --git a/go.mod b/go.mod index 0764d73a..7bde41ae 100644 --- a/go.mod +++ b/go.mod @@ -17,5 +17,5 @@ require ( github.com/syndtr/goleveldb v1.0.0 github.com/ziutek/mymysql v1.5.4 modernc.org/sqlite v1.14.2 - xorm.io/builder v0.3.9 + xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 ) diff --git a/go.sum b/go.sum index 8e7ac44b..8bdc9798 100644 --- a/go.sum +++ b/go.sum @@ -659,5 +659,5 @@ modernc.org/z v1.2.19 h1:BGyRFWhDVn5LFS5OcX4Yd/MlpRTOc7hOPTdcIpCiUao= modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc= -xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= +xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM= +xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index 7f42d096..6701b1b5 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -247,6 +247,10 @@ func TestOrder(t *testing.T) { users2 := make([]Userinfo, 0) err = testEngine.Asc("id", "username").Desc("height").Find(&users2) assert.NoError(t, err) + + users = make([]Userinfo, 0) + err = testEngine.OrderBy("CASE WHEN username LIKE ? THEN 0 ELSE 1 END DESC", "a").Find(&users) + assert.NoError(t, err) } func TestGroupBy(t *testing.T) { diff --git a/interface.go b/interface.go index b9e88505..55ffebe4 100644 --- a/interface.go +++ b/interface.go @@ -54,7 +54,7 @@ type Interface interface { Nullable(...string) *Session Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session Omit(columns ...string) *Session - OrderBy(order string) *Session + OrderBy(order interface{}, args ...interface{}) *Session Ping() error Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) diff --git a/internal/statements/cond.go b/internal/statements/cond.go new file mode 100644 index 00000000..dfc6c208 --- /dev/null +++ b/internal/statements/cond.go @@ -0,0 +1,111 @@ +// Copyright 2022 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 statements + +import ( + "xorm.io/builder" + "xorm.io/xorm/schemas" +) + +type QuoteReplacer struct { + *builder.BytesWriter + quoter schemas.Quoter +} + +func (q *QuoteReplacer) Write(p []byte) (n int, err error) { + c := q.quoter.Replace(string(p)) + return q.BytesWriter.Builder.WriteString(c) +} + +func (statement *Statement) QuoteReplacer(w *builder.BytesWriter) *QuoteReplacer { + return &QuoteReplacer{ + BytesWriter: w, + quoter: statement.dialect.Quoter(), + } +} + +// Where add Where statement +func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement { + return statement.And(query, args...) +} + +// And add Where & and statement +func (statement *Statement) And(query interface{}, args ...interface{}) *Statement { + switch qr := query.(type) { + case string: + cond := builder.Expr(qr, args...) + statement.cond = statement.cond.And(cond) + case map[string]interface{}: + cond := make(builder.Eq) + for k, v := range qr { + cond[statement.quote(k)] = v + } + statement.cond = statement.cond.And(cond) + case builder.Cond: + statement.cond = statement.cond.And(qr) + for _, v := range args { + if vv, ok := v.(builder.Cond); ok { + statement.cond = statement.cond.And(vv) + } + } + default: + statement.LastError = ErrConditionType + } + + return statement +} + +// Or add Where & Or statement +func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement { + switch qr := query.(type) { + case string: + cond := builder.Expr(qr, args...) + statement.cond = statement.cond.Or(cond) + case map[string]interface{}: + cond := make(builder.Eq) + for k, v := range qr { + cond[statement.quote(k)] = v + } + statement.cond = statement.cond.Or(cond) + case builder.Cond: + statement.cond = statement.cond.Or(qr) + for _, v := range args { + if vv, ok := v.(builder.Cond); ok { + statement.cond = statement.cond.Or(vv) + } + } + default: + statement.LastError = ErrConditionType + } + return statement +} + +// In generate "Where column IN (?) " statement +func (statement *Statement) In(column string, args ...interface{}) *Statement { + in := builder.In(statement.quote(column), args...) + statement.cond = statement.cond.And(in) + return statement +} + +// NotIn generate "Where column NOT IN (?) " statement +func (statement *Statement) NotIn(column string, args ...interface{}) *Statement { + notIn := builder.NotIn(statement.quote(column), args...) + statement.cond = statement.cond.And(notIn) + return statement +} + +// SetNoAutoCondition if you do not want convert bean's field as query condition, then use this function +func (statement *Statement) SetNoAutoCondition(no ...bool) *Statement { + statement.NoAutoCondition = true + if len(no) > 0 { + statement.NoAutoCondition = no[0] + } + return statement +} + +// Conds returns condtions +func (statement *Statement) Conds() builder.Cond { + return statement.cond +} diff --git a/internal/statements/join.go b/internal/statements/join.go new file mode 100644 index 00000000..45fc2441 --- /dev/null +++ b/internal/statements/join.go @@ -0,0 +1,78 @@ +// Copyright 2022 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 statements + +import ( + "fmt" + "strings" + + "xorm.io/builder" + "xorm.io/xorm/dialects" + "xorm.io/xorm/internal/utils" + "xorm.io/xorm/schemas" +) + +// Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN +func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement { + var buf strings.Builder + if len(statement.JoinStr) > 0 { + fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) + } else { + fmt.Fprintf(&buf, "%v JOIN ", joinOP) + } + + switch tp := tablename.(type) { + case builder.Builder: + subSQL, subQueryArgs, err := tp.ToSQL() + if err != nil { + statement.LastError = err + return statement + } + + fields := strings.Split(tp.TableName(), ".") + aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) + aliasName = schemas.CommonQuoter.Trim(aliasName) + + fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) + statement.joinArgs = append(statement.joinArgs, subQueryArgs...) + case *builder.Builder: + subSQL, subQueryArgs, err := tp.ToSQL() + if err != nil { + statement.LastError = err + return statement + } + + fields := strings.Split(tp.TableName(), ".") + aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) + aliasName = schemas.CommonQuoter.Trim(aliasName) + + fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) + statement.joinArgs = append(statement.joinArgs, subQueryArgs...) + default: + tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true) + if !utils.IsSubQuery(tbName) { + var buf strings.Builder + _ = statement.dialect.Quoter().QuoteTo(&buf, tbName) + tbName = buf.String() + } else { + tbName = statement.ReplaceQuote(tbName) + } + fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condition)) + } + + statement.JoinStr = buf.String() + statement.joinArgs = append(statement.joinArgs, args...) + return statement +} + +func (statement *Statement) writeJoin(w builder.Writer) error { + if statement.JoinStr != "" { + if _, err := fmt.Fprint(w, " ", statement.JoinStr); err != nil { + return err + } + w.Append(statement.joinArgs...) + } + return nil +} diff --git a/internal/statements/order_by.go b/internal/statements/order_by.go new file mode 100644 index 00000000..08a8263b --- /dev/null +++ b/internal/statements/order_by.go @@ -0,0 +1,90 @@ +// Copyright 2022 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 statements + +import ( + "fmt" + "strings" + + "xorm.io/builder" +) + +func (statement *Statement) HasOrderBy() bool { + return statement.orderStr != "" +} + +// ResetOrderBy reset ordery conditions +func (statement *Statement) ResetOrderBy() { + statement.orderStr = "" + statement.orderArgs = nil +} + +// WriteOrderBy write order by to writer +func (statement *Statement) WriteOrderBy(w builder.Writer) error { + if len(statement.orderStr) > 0 { + if _, err := fmt.Fprintf(w, " ORDER BY %s", statement.orderStr); err != nil { + return err + } + w.Append(statement.orderArgs...) + } + return nil +} + +// OrderBy generate "Order By order" statement +func (statement *Statement) OrderBy(order interface{}, args ...interface{}) *Statement { + if len(statement.orderStr) > 0 { + statement.orderStr += ", " + } + var rawOrder string + switch t := order.(type) { + case (*builder.Expression): + rawOrder = t.Content() + args = t.Args() + case string: + rawOrder = t + default: + statement.LastError = ErrUnSupportedSQLType + return statement + } + statement.orderStr += statement.ReplaceQuote(rawOrder) + if len(args) > 0 { + statement.orderArgs = append(statement.orderArgs, args...) + } + return statement +} + +// Desc generate `ORDER BY xx DESC` +func (statement *Statement) Desc(colNames ...string) *Statement { + var buf strings.Builder + if len(statement.orderStr) > 0 { + fmt.Fprint(&buf, statement.orderStr, ", ") + } + for i, col := range colNames { + if i > 0 { + fmt.Fprint(&buf, ", ") + } + _ = statement.dialect.Quoter().QuoteTo(&buf, col) + fmt.Fprint(&buf, " DESC") + } + statement.orderStr = buf.String() + return statement +} + +// Asc provide asc order by query condition, the input parameters are columns. +func (statement *Statement) Asc(colNames ...string) *Statement { + var buf strings.Builder + if len(statement.orderStr) > 0 { + fmt.Fprint(&buf, statement.orderStr, ", ") + } + for i, col := range colNames { + if i > 0 { + fmt.Fprint(&buf, ", ") + } + _ = statement.dialect.Quoter().QuoteTo(&buf, col) + fmt.Fprint(&buf, " ASC") + } + statement.orderStr = buf.String() + return statement +} diff --git a/internal/statements/query.go b/internal/statements/query.go index 8b383866..f72c8602 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -11,6 +11,7 @@ import ( "strings" "xorm.io/builder" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -28,7 +29,7 @@ func (statement *Statement) GenQuerySQL(sqlOrArgs ...interface{}) (string, []int return "", nil, ErrTableNotFound } - var columnStr = statement.ColumnStr() + columnStr := statement.ColumnStr() if len(statement.SelectStr) > 0 { columnStr = statement.SelectStr } else { @@ -58,19 +59,7 @@ func (statement *Statement) GenQuerySQL(sqlOrArgs ...interface{}) (string, []int return "", nil, err } - sqlStr, condArgs, err := statement.genSelectSQL(columnStr, true, true) - if err != nil { - return "", nil, err - } - args := append(statement.joinArgs, condArgs...) - - // for mssql and use limit - qs := strings.Count(sqlStr, "?") - if len(args)*2 == qs { - args = append(args, args...) - } - - return sqlStr, args, nil + return statement.genSelectSQL(columnStr, true, true) } // GenSumSQL generates sum SQL @@ -83,7 +72,7 @@ func (statement *Statement) GenSumSQL(bean interface{}, columns ...string) (stri return "", nil, err } - var sumStrs = make([]string, 0, len(columns)) + sumStrs := make([]string, 0, len(columns)) for _, colName := range columns { if !strings.Contains(colName, " ") && !strings.Contains(colName, "(") { colName = statement.quote(colName) @@ -94,16 +83,11 @@ func (statement *Statement) GenSumSQL(bean interface{}, columns ...string) (stri } sumSelect := strings.Join(sumStrs, ", ") - if err := statement.mergeConds(bean); err != nil { + if err := statement.MergeConds(bean); err != nil { return "", nil, err } - sqlStr, condArgs, err := statement.genSelectSQL(sumSelect, true, true) - if err != nil { - return "", nil, err - } - - return sqlStr, append(statement.joinArgs, condArgs...), nil + return statement.genSelectSQL(sumSelect, true, true) } // GenGetSQL generates Get SQL @@ -119,7 +103,7 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{}, } } - var columnStr = statement.ColumnStr() + columnStr := statement.ColumnStr() if len(statement.SelectStr) > 0 { columnStr = statement.SelectStr } else { @@ -146,7 +130,7 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{}, } if isStruct { - if err := statement.mergeConds(bean); err != nil { + if err := statement.MergeConds(bean); err != nil { return "", nil, err } } else { @@ -155,12 +139,7 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{}, } } - sqlStr, condArgs, err := statement.genSelectSQL(columnStr, true, true) - if err != nil { - return "", nil, err - } - - return sqlStr, append(statement.joinArgs, condArgs...), nil + return statement.genSelectSQL(columnStr, true, true) } // GenCountSQL generates the SQL for counting @@ -175,12 +154,12 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa if err := statement.SetRefBean(beans[0]); err != nil { return "", nil, err } - if err := statement.mergeConds(beans[0]); err != nil { + if err := statement.MergeConds(beans[0]); err != nil { return "", nil, err } } - var selectSQL = statement.SelectStr + selectSQL := statement.SelectStr if len(selectSQL) <= 0 { if statement.IsDistinct { selectSQL = fmt.Sprintf("count(DISTINCT %s)", statement.ColumnStr()) @@ -206,55 +185,58 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa sqlStr = fmt.Sprintf("SELECT %s FROM (%s) sub", selectSQL, sqlStr) } - return sqlStr, append(statement.joinArgs, condArgs...), nil + return sqlStr, condArgs, nil } -func (statement *Statement) fromBuilder() *strings.Builder { - var builder strings.Builder - var quote = statement.quote - var dialect = statement.dialect - - builder.WriteString(" FROM ") - - if dialect.URI().DBType == schemas.MSSQL && strings.Contains(statement.TableName(), "..") { - builder.WriteString(statement.TableName()) - } else { - builder.WriteString(quote(statement.TableName())) +func (statement *Statement) writeFrom(w builder.Writer) error { + if _, err := fmt.Fprint(w, " FROM "); err != nil { + return err } + if err := statement.writeTableName(w); err != nil { + return err + } + if err := statement.writeAlias(w); err != nil { + return err + } + return statement.writeJoin(w) +} - if statement.TableAlias != "" { - if dialect.URI().DBType == schemas.ORACLE { - builder.WriteString(" ") - } else { - builder.WriteString(" AS ") +func (statement *Statement) writeLimitOffset(w builder.Writer) error { + if statement.Start > 0 { + if statement.LimitN != nil { + _, err := fmt.Fprintf(w, " LIMIT %v OFFSET %v", *statement.LimitN, statement.Start) + return err } - builder.WriteString(quote(statement.TableAlias)) + _, err := fmt.Fprintf(w, " LIMIT 0 OFFSET %v", statement.Start) + return err } - if statement.JoinStr != "" { - builder.WriteString(" ") - builder.WriteString(statement.JoinStr) + if statement.LimitN != nil { + _, err := fmt.Fprint(w, " LIMIT ", *statement.LimitN) + return err } - return &builder + // no limit statement + return nil } func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderBy bool) (string, []interface{}, error) { var ( - distinct string - dialect = statement.dialect - fromStr = statement.fromBuilder().String() - top, mssqlCondi, whereStr string + distinct string + dialect = statement.dialect + top, whereStr string + mssqlCondi = builder.NewWriter() ) if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { distinct = "DISTINCT " } - condSQL, condArgs, err := statement.GenCondSQL(statement.cond) - if err != nil { + condWriter := builder.NewWriter() + if err := statement.cond.WriteTo(statement.QuoteReplacer(condWriter)); err != nil { return "", nil, err } - if len(condSQL) > 0 { - whereStr = fmt.Sprintf(" WHERE %s", condSQL) + + if condWriter.Len() > 0 { + whereStr = " WHERE " } pLimitN := statement.LimitN @@ -289,49 +271,81 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB } } - var orderStr string - if needOrderBy && len(statement.OrderStr) > 0 { - orderStr = fmt.Sprintf(" ORDER BY %s", statement.OrderStr) + if _, err := fmt.Fprintf(mssqlCondi, "(%s NOT IN (SELECT TOP %d %s", + column, statement.Start, column); err != nil { + return "", nil, err } - - var groupStr string - if len(statement.GroupByStr) > 0 { - groupStr = fmt.Sprintf(" GROUP BY %s", statement.GroupByStr) + if err := statement.writeFrom(mssqlCondi); err != nil { + return "", nil, err + } + if whereStr != "" { + if _, err := fmt.Fprint(mssqlCondi, whereStr); err != nil { + return "", nil, err + } + if err := utils.WriteBuilder(mssqlCondi, statement.QuoteReplacer(condWriter)); err != nil { + return "", nil, err + } + } + if needOrderBy { + if err := statement.WriteOrderBy(mssqlCondi); err != nil { + return "", nil, err + } + } + if err := statement.WriteGroupBy(mssqlCondi); err != nil { + return "", nil, err + } + if _, err := fmt.Fprint(mssqlCondi, "))"); err != nil { + return "", nil, err } - mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s%s%s))", - column, statement.Start, column, fromStr, whereStr, orderStr, groupStr) } } - var buf strings.Builder - fmt.Fprintf(&buf, "SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr) - if len(mssqlCondi) > 0 { + buf := builder.NewWriter() + if _, err := fmt.Fprintf(buf, "SELECT %v%v%v", distinct, top, columnStr); err != nil { + return "", nil, err + } + if err := statement.writeFrom(buf); err != nil { + return "", nil, err + } + if whereStr != "" { + if _, err := fmt.Fprint(buf, whereStr); err != nil { + return "", nil, err + } + if err := utils.WriteBuilder(buf, statement.QuoteReplacer(condWriter)); err != nil { + return "", nil, err + } + } + if mssqlCondi.Len() > 0 { if len(whereStr) > 0 { - fmt.Fprint(&buf, " AND ", mssqlCondi) + if _, err := fmt.Fprint(buf, " AND "); err != nil { + return "", nil, err + } } else { - fmt.Fprint(&buf, " WHERE ", mssqlCondi) + if _, err := fmt.Fprint(buf, " WHERE "); err != nil { + return "", nil, err + } + } + + if err := utils.WriteBuilder(buf, mssqlCondi); err != nil { + return "", nil, err } } - if statement.GroupByStr != "" { - fmt.Fprint(&buf, " GROUP BY ", statement.GroupByStr) + if err := statement.WriteGroupBy(buf); err != nil { + return "", nil, err } - if statement.HavingStr != "" { - fmt.Fprint(&buf, " ", statement.HavingStr) + if err := statement.writeHaving(buf); err != nil { + return "", nil, err } - if needOrderBy && statement.OrderStr != "" { - fmt.Fprint(&buf, " ORDER BY ", statement.OrderStr) + if needOrderBy { + if err := statement.WriteOrderBy(buf); err != nil { + return "", nil, err + } } if needLimit { if dialect.URI().DBType != schemas.MSSQL && dialect.URI().DBType != schemas.ORACLE { - if statement.Start > 0 { - if pLimitN != nil { - fmt.Fprintf(&buf, " LIMIT %v OFFSET %v", *pLimitN, statement.Start) - } else { - fmt.Fprintf(&buf, " LIMIT 0 OFFSET %v", statement.Start) - } - } else if pLimitN != nil { - fmt.Fprint(&buf, " LIMIT ", *pLimitN) + if err := statement.writeLimitOffset(buf); err != nil { + return "", nil, err } } else if dialect.URI().DBType == schemas.ORACLE { if pLimitN != nil { @@ -341,16 +355,16 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB if rawColStr == "*" { rawColStr = "at.*" } - fmt.Fprintf(&buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", + fmt.Fprintf(buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, rawColStr, oldString, statement.Start+*pLimitN, statement.Start) } } } if statement.IsForUpdate { - return dialect.ForUpdateSQL(buf.String()), condArgs, nil + return dialect.ForUpdateSQL(buf.String()), buf.Args(), nil } - return buf.String(), condArgs, nil + return buf.String(), buf.Args(), nil } // GenExistSQL generates Exist SQL @@ -359,10 +373,6 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac return statement.GenRawSQL(), statement.RawParams, nil } - var sqlStr string - var args []interface{} - var joinStr string - var err error var b interface{} if len(bean) > 0 { b = bean[0] @@ -381,45 +391,70 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac if len(tableName) <= 0 { return "", nil, ErrTableNotFound } - if statement.RefTable == nil { - tableName = statement.quote(tableName) - if len(statement.JoinStr) > 0 { - joinStr = statement.JoinStr - } + if statement.RefTable != nil { + return statement.Limit(1).GenGetSQL(b) + } + tableName = statement.quote(tableName) + + buf := builder.NewWriter() + if statement.dialect.URI().DBType == schemas.MSSQL { + if _, err := fmt.Fprintf(buf, "SELECT TOP 1 * FROM %s", tableName); err != nil { + return "", nil, err + } + if err := statement.writeJoin(buf); err != nil { + return "", nil, err + } if statement.Conds().IsValid() { - condSQL, condArgs, err := statement.GenCondSQL(statement.Conds()) - if err != nil { + if _, err := fmt.Fprintf(buf, " WHERE "); err != nil { return "", nil, err } - - if statement.dialect.URI().DBType == schemas.MSSQL { - sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s %s WHERE %s", tableName, joinStr, condSQL) - } else if statement.dialect.URI().DBType == schemas.ORACLE { - sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE (%s) %s AND ROWNUM=1", tableName, joinStr, condSQL) - } else { - sqlStr = fmt.Sprintf("SELECT 1 FROM %s %s WHERE %s LIMIT 1", tableName, joinStr, condSQL) + if err := statement.Conds().WriteTo(statement.QuoteReplacer(buf)); err != nil { + return "", nil, err } - args = condArgs - } else { - if statement.dialect.URI().DBType == schemas.MSSQL { - sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s %s", tableName, joinStr) - } else if statement.dialect.URI().DBType == schemas.ORACLE { - sqlStr = fmt.Sprintf("SELECT * FROM %s %s WHERE ROWNUM=1", tableName, joinStr) - } else { - sqlStr = fmt.Sprintf("SELECT 1 FROM %s %s LIMIT 1", tableName, joinStr) + } + } else if statement.dialect.URI().DBType == schemas.ORACLE { + if _, err := fmt.Fprintf(buf, "SELECT * FROM %s", tableName); err != nil { + return "", nil, err + } + if err := statement.writeJoin(buf); err != nil { + return "", nil, err + } + if _, err := fmt.Fprintf(buf, " WHERE "); err != nil { + return "", nil, err + } + if statement.Conds().IsValid() { + if err := statement.Conds().WriteTo(statement.QuoteReplacer(buf)); err != nil { + return "", nil, err } - args = []interface{}{} + if _, err := fmt.Fprintf(buf, " AND "); err != nil { + return "", nil, err + } + } + if _, err := fmt.Fprintf(buf, "ROWNUM=1"); err != nil { + return "", nil, err } } else { - statement.Limit(1) - sqlStr, args, err = statement.GenGetSQL(b) - if err != nil { + if _, err := fmt.Fprintf(buf, "SELECT 1 FROM %s", tableName); err != nil { + return "", nil, err + } + if err := statement.writeJoin(buf); err != nil { + return "", nil, err + } + if statement.Conds().IsValid() { + if _, err := fmt.Fprintf(buf, " WHERE "); err != nil { + return "", nil, err + } + if err := statement.Conds().WriteTo(statement.QuoteReplacer(buf)); err != nil { + return "", nil, err + } + } + if _, err := fmt.Fprintf(buf, " LIMIT 1"); err != nil { return "", nil, err } } - return sqlStr, args, nil + return buf.String(), buf.Args(), nil } // GenFindSQL generates Find SQL @@ -428,15 +463,11 @@ func (statement *Statement) GenFindSQL(autoCond builder.Cond) (string, []interfa return statement.GenRawSQL(), statement.RawParams, nil } - var sqlStr string - var args []interface{} - var err error - if len(statement.TableName()) <= 0 { return "", nil, ErrTableNotFound } - var columnStr = statement.ColumnStr() + columnStr := statement.ColumnStr() if len(statement.SelectStr) > 0 { columnStr = statement.SelectStr } else { @@ -464,16 +495,5 @@ func (statement *Statement) GenFindSQL(autoCond builder.Cond) (string, []interfa statement.cond = statement.cond.And(autoCond) - sqlStr, condArgs, err := statement.genSelectSQL(columnStr, true, true) - if err != nil { - return "", nil, err - } - args = append(statement.joinArgs, condArgs...) - // for mssql and use limit - qs := strings.Count(sqlStr, "?") - if len(args)*2 == qs { - args = append(args, args...) - } - - return sqlStr, args, nil + return statement.genSelectSQL(columnStr, true, true) } diff --git a/internal/statements/select.go b/internal/statements/select.go new file mode 100644 index 00000000..2bd2e94d --- /dev/null +++ b/internal/statements/select.go @@ -0,0 +1,137 @@ +// Copyright 2022 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 statements + +import ( + "fmt" + "strings" + + "xorm.io/xorm/schemas" +) + +// Select replace select +func (statement *Statement) Select(str string) *Statement { + statement.SelectStr = statement.ReplaceQuote(str) + return statement +} + +func col2NewCols(columns ...string) []string { + newColumns := make([]string, 0, len(columns)) + for _, col := range columns { + col = strings.Replace(col, "`", "", -1) + col = strings.Replace(col, `"`, "", -1) + ccols := strings.Split(col, ",") + for _, c := range ccols { + newColumns = append(newColumns, strings.TrimSpace(c)) + } + } + return newColumns +} + +// Cols generate "col1, col2" statement +func (statement *Statement) Cols(columns ...string) *Statement { + cols := col2NewCols(columns...) + for _, nc := range cols { + statement.ColumnMap.Add(nc) + } + return statement +} + +// ColumnStr returns column string +func (statement *Statement) ColumnStr() string { + return statement.dialect.Quoter().Join(statement.ColumnMap, ", ") +} + +// AllCols update use only: update all columns +func (statement *Statement) AllCols() *Statement { + statement.useAllCols = true + return statement +} + +// MustCols update use only: must update columns +func (statement *Statement) MustCols(columns ...string) *Statement { + newColumns := col2NewCols(columns...) + for _, nc := range newColumns { + statement.MustColumnMap[strings.ToLower(nc)] = true + } + return statement +} + +// UseBool indicates that use bool fields as update contents and query contiditions +func (statement *Statement) UseBool(columns ...string) *Statement { + if len(columns) > 0 { + statement.MustCols(columns...) + } else { + statement.allUseBool = true + } + return statement +} + +// Omit do not use the columns +func (statement *Statement) Omit(columns ...string) { + newColumns := col2NewCols(columns...) + for _, nc := range newColumns { + statement.OmitColumnMap = append(statement.OmitColumnMap, nc) + } +} + +func (statement *Statement) genColumnStr() string { + if statement.RefTable == nil { + return "" + } + + var buf strings.Builder + columns := statement.RefTable.Columns() + + for _, col := range columns { + if statement.OmitColumnMap.Contain(col.Name) { + continue + } + + if len(statement.ColumnMap) > 0 && !statement.ColumnMap.Contain(col.Name) { + continue + } + + if col.MapType == schemas.ONLYTODB { + continue + } + + if buf.Len() != 0 { + buf.WriteString(", ") + } + + if statement.JoinStr != "" { + if statement.TableAlias != "" { + buf.WriteString(statement.TableAlias) + } else { + buf.WriteString(statement.TableName()) + } + + buf.WriteString(".") + } + + statement.dialect.Quoter().QuoteTo(&buf, col.Name) + } + + return buf.String() +} + +func (statement *Statement) colName(col *schemas.Column, tableName string) string { + if statement.needTableName() { + nm := tableName + if len(statement.TableAlias) > 0 { + nm = statement.TableAlias + } + return fmt.Sprintf("%s.%s", statement.quote(nm), statement.quote(col.Name)) + } + return statement.quote(col.Name) +} + +// Distinct generates "DISTINCT col1, col2 " statement +func (statement *Statement) Distinct(columns ...string) *Statement { + statement.IsDistinct = true + statement.Cols(columns...) + return statement +} diff --git a/internal/statements/statement.go b/internal/statements/statement.go index 3069561e..a8fe34fa 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -43,7 +43,8 @@ type Statement struct { Start int LimitN *int idParam schemas.PK - OrderStr string + orderStr string + orderArgs []interface{} JoinStr string joinArgs []interface{} GroupByStr string @@ -101,15 +102,6 @@ func (statement *Statement) GenRawSQL() string { return statement.ReplaceQuote(statement.RawSQL) } -// GenCondSQL generates condition SQL -func (statement *Statement) GenCondSQL(condOrBuilder interface{}) (string, []interface{}, error) { - condSQL, condArgs, err := builder.ToSQL(condOrBuilder) - if err != nil { - return "", nil, err - } - return statement.ReplaceQuote(condSQL), condArgs, nil -} - // ReplaceQuote replace sql key words with quote func (statement *Statement) ReplaceQuote(sql string) string { if sql == "" || statement.dialect.URI().DBType == schemas.MYSQL || @@ -129,7 +121,7 @@ func (statement *Statement) Reset() { statement.RefTable = nil statement.Start = 0 statement.LimitN = nil - statement.OrderStr = "" + statement.ResetOrderBy() statement.UseCascade = true statement.JoinStr = "" statement.joinArgs = make([]interface{}, 0) @@ -164,21 +156,6 @@ func (statement *Statement) Reset() { statement.LastError = nil } -// SetNoAutoCondition if you do not want convert bean's field as query condition, then use this function -func (statement *Statement) SetNoAutoCondition(no ...bool) *Statement { - statement.NoAutoCondition = true - if len(no) > 0 { - statement.NoAutoCondition = no[0] - } - return statement -} - -// Alias set the table alias -func (statement *Statement) Alias(alias string) *Statement { - statement.TableAlias = alias - return statement -} - // SQL adds raw sql statement func (statement *Statement) SQL(query interface{}, args ...interface{}) *Statement { switch query.(type) { @@ -198,80 +175,10 @@ func (statement *Statement) SQL(query interface{}, args ...interface{}) *Stateme return statement } -// Where add Where statement -func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement { - return statement.And(query, args...) -} - func (statement *Statement) quote(s string) string { return statement.dialect.Quoter().Quote(s) } -// And add Where & and statement -func (statement *Statement) And(query interface{}, args ...interface{}) *Statement { - switch qr := query.(type) { - case string: - cond := builder.Expr(qr, args...) - statement.cond = statement.cond.And(cond) - case map[string]interface{}: - cond := make(builder.Eq) - for k, v := range qr { - cond[statement.quote(k)] = v - } - statement.cond = statement.cond.And(cond) - case builder.Cond: - statement.cond = statement.cond.And(qr) - for _, v := range args { - if vv, ok := v.(builder.Cond); ok { - statement.cond = statement.cond.And(vv) - } - } - default: - statement.LastError = ErrConditionType - } - - return statement -} - -// Or add Where & Or statement -func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement { - switch qr := query.(type) { - case string: - cond := builder.Expr(qr, args...) - statement.cond = statement.cond.Or(cond) - case map[string]interface{}: - cond := make(builder.Eq) - for k, v := range qr { - cond[statement.quote(k)] = v - } - statement.cond = statement.cond.Or(cond) - case builder.Cond: - statement.cond = statement.cond.Or(qr) - for _, v := range args { - if vv, ok := v.(builder.Cond); ok { - statement.cond = statement.cond.Or(vv) - } - } - default: - statement.LastError = ErrConditionType - } - return statement -} - -// In generate "Where column IN (?) " statement -func (statement *Statement) In(column string, args ...interface{}) *Statement { - in := builder.In(statement.quote(column), args...) - statement.cond = statement.cond.And(in) - return statement -} - -// NotIn generate "Where column NOT IN (?) " statement -func (statement *Statement) NotIn(column string, args ...interface{}) *Statement { - notIn := builder.NotIn(statement.quote(column), args...) - statement.cond = statement.cond.And(notIn) - return statement -} - // SetRefValue set ref value func (statement *Statement) SetRefValue(v reflect.Value) error { var err error @@ -302,26 +209,6 @@ func (statement *Statement) needTableName() bool { return len(statement.JoinStr) > 0 } -func (statement *Statement) colName(col *schemas.Column, tableName string) string { - if statement.needTableName() { - nm := tableName - if len(statement.TableAlias) > 0 { - nm = statement.TableAlias - } - return fmt.Sprintf("%s.%s", statement.quote(nm), statement.quote(col.Name)) - } - return statement.quote(col.Name) -} - -// TableName return current tableName -func (statement *Statement) TableName() string { - if statement.AltTableName != "" { - return statement.AltTableName - } - - return statement.tableName -} - // Incr Generate "Update ... Set column = column + arg" statement func (statement *Statement) Incr(column string, arg ...interface{}) *Statement { if len(arg) > 0 { @@ -352,85 +239,12 @@ func (statement *Statement) SetExpr(column string, expression interface{}) *Stat return statement } -// Distinct generates "DISTINCT col1, col2 " statement -func (statement *Statement) Distinct(columns ...string) *Statement { - statement.IsDistinct = true - statement.Cols(columns...) - return statement -} - // ForUpdate generates "SELECT ... FOR UPDATE" statement func (statement *Statement) ForUpdate() *Statement { statement.IsForUpdate = true return statement } -// Select replace select -func (statement *Statement) Select(str string) *Statement { - statement.SelectStr = statement.ReplaceQuote(str) - return statement -} - -func col2NewCols(columns ...string) []string { - newColumns := make([]string, 0, len(columns)) - for _, col := range columns { - col = strings.Replace(col, "`", "", -1) - col = strings.Replace(col, `"`, "", -1) - ccols := strings.Split(col, ",") - for _, c := range ccols { - newColumns = append(newColumns, strings.TrimSpace(c)) - } - } - return newColumns -} - -// Cols generate "col1, col2" statement -func (statement *Statement) Cols(columns ...string) *Statement { - cols := col2NewCols(columns...) - for _, nc := range cols { - statement.ColumnMap.Add(nc) - } - return statement -} - -// ColumnStr returns column string -func (statement *Statement) ColumnStr() string { - return statement.dialect.Quoter().Join(statement.ColumnMap, ", ") -} - -// AllCols update use only: update all columns -func (statement *Statement) AllCols() *Statement { - statement.useAllCols = true - return statement -} - -// MustCols update use only: must update columns -func (statement *Statement) MustCols(columns ...string) *Statement { - newColumns := col2NewCols(columns...) - for _, nc := range newColumns { - statement.MustColumnMap[strings.ToLower(nc)] = true - } - return statement -} - -// UseBool indicates that use bool fields as update contents and query contiditions -func (statement *Statement) UseBool(columns ...string) *Statement { - if len(columns) > 0 { - statement.MustCols(columns...) - } else { - statement.allUseBool = true - } - return statement -} - -// Omit do not use the columns -func (statement *Statement) Omit(columns ...string) { - newColumns := col2NewCols(columns...) - for _, nc := range newColumns { - statement.OmitColumnMap = append(statement.OmitColumnMap, nc) - } -} - // Nullable Update use only: update columns to null when value is nullable and zero-value func (statement *Statement) Nullable(columns ...string) { newColumns := col2NewCols(columns...) @@ -454,54 +268,6 @@ func (statement *Statement) Limit(limit int, start ...int) *Statement { return statement } -// OrderBy generate "Order By order" statement -func (statement *Statement) OrderBy(order string) *Statement { - if len(statement.OrderStr) > 0 { - statement.OrderStr += ", " - } - statement.OrderStr += statement.ReplaceQuote(order) - return statement -} - -// Desc generate `ORDER BY xx DESC` -func (statement *Statement) Desc(colNames ...string) *Statement { - var buf strings.Builder - if len(statement.OrderStr) > 0 { - fmt.Fprint(&buf, statement.OrderStr, ", ") - } - for i, col := range colNames { - if i > 0 { - fmt.Fprint(&buf, ", ") - } - _ = statement.dialect.Quoter().QuoteTo(&buf, col) - fmt.Fprint(&buf, " DESC") - } - statement.OrderStr = buf.String() - return statement -} - -// Asc provide asc order by query condition, the input parameters are columns. -func (statement *Statement) Asc(colNames ...string) *Statement { - var buf strings.Builder - if len(statement.OrderStr) > 0 { - fmt.Fprint(&buf, statement.OrderStr, ", ") - } - for i, col := range colNames { - if i > 0 { - fmt.Fprint(&buf, ", ") - } - _ = statement.dialect.Quoter().QuoteTo(&buf, col) - fmt.Fprint(&buf, " ASC") - } - statement.OrderStr = buf.String() - return statement -} - -// Conds returns condtions -func (statement *Statement) Conds() builder.Cond { - return statement.cond -} - // SetTable tempororily set table name, the parameter could be a string or a pointer of struct func (statement *Statement) SetTable(tableNameOrBean interface{}) error { v := rValue(tableNameOrBean) @@ -518,71 +284,34 @@ func (statement *Statement) SetTable(tableNameOrBean interface{}) error { return nil } -// Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement { - var buf strings.Builder - if len(statement.JoinStr) > 0 { - fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) - } else { - fmt.Fprintf(&buf, "%v JOIN ", joinOP) - } - - switch tp := tablename.(type) { - case builder.Builder: - subSQL, subQueryArgs, err := tp.ToSQL() - if err != nil { - statement.LastError = err - return statement - } - - fields := strings.Split(tp.TableName(), ".") - aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) - aliasName = schemas.CommonQuoter.Trim(aliasName) - - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) - statement.joinArgs = append(statement.joinArgs, subQueryArgs...) - case *builder.Builder: - subSQL, subQueryArgs, err := tp.ToSQL() - if err != nil { - statement.LastError = err - return statement - } - - fields := strings.Split(tp.TableName(), ".") - aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) - aliasName = schemas.CommonQuoter.Trim(aliasName) - - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) - statement.joinArgs = append(statement.joinArgs, subQueryArgs...) - default: - tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true) - if !utils.IsSubQuery(tbName) { - var buf strings.Builder - _ = statement.dialect.Quoter().QuoteTo(&buf, tbName) - tbName = buf.String() - } else { - tbName = statement.ReplaceQuote(tbName) - } - fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condition)) - } - - statement.JoinStr = buf.String() - statement.joinArgs = append(statement.joinArgs, args...) - return statement -} - // GroupBy generate "Group By keys" statement func (statement *Statement) GroupBy(keys string) *Statement { statement.GroupByStr = statement.ReplaceQuote(keys) return statement } +func (statement *Statement) WriteGroupBy(w builder.Writer) error { + if statement.GroupByStr == "" { + return nil + } + _, err := fmt.Fprintf(w, " GROUP BY %s", statement.GroupByStr) + return err +} + // Having generate "Having conditions" statement func (statement *Statement) Having(conditions string) *Statement { statement.HavingStr = fmt.Sprintf("HAVING %v", statement.ReplaceQuote(conditions)) return statement } +func (statement *Statement) writeHaving(w builder.Writer) error { + if statement.HavingStr == "" { + return nil + } + _, err := fmt.Fprint(w, " ", statement.HavingStr) + return err +} + // SetUnscoped always disable struct tag "deleted" func (statement *Statement) SetUnscoped() *Statement { statement.unscoped = true @@ -594,47 +323,6 @@ func (statement *Statement) GetUnscoped() bool { return statement.unscoped } -func (statement *Statement) genColumnStr() string { - if statement.RefTable == nil { - return "" - } - - var buf strings.Builder - columns := statement.RefTable.Columns() - - for _, col := range columns { - if statement.OmitColumnMap.Contain(col.Name) { - continue - } - - if len(statement.ColumnMap) > 0 && !statement.ColumnMap.Contain(col.Name) { - continue - } - - if col.MapType == schemas.ONLYTODB { - continue - } - - if buf.Len() != 0 { - buf.WriteString(", ") - } - - if statement.JoinStr != "" { - if statement.TableAlias != "" { - buf.WriteString(statement.TableAlias) - } else { - buf.WriteString(statement.TableName()) - } - - buf.WriteString(".") - } - - statement.dialect.Quoter().QuoteTo(&buf, col.Name) - } - - return buf.String() -} - // GenIndexSQL generated create index SQL func (statement *Statement) GenIndexSQL() []string { var sqls []string @@ -914,7 +602,8 @@ func (statement *Statement) BuildConds(table *schemas.Table, bean interface{}, i statement.unscoped, statement.MustColumnMap, statement.TableName(), statement.TableAlias, addedTableName) } -func (statement *Statement) mergeConds(bean interface{}) error { +// MergeConds merge conditions from bean and id +func (statement *Statement) MergeConds(bean interface{}) error { if !statement.NoAutoCondition && statement.RefTable != nil { addedTableName := (len(statement.JoinStr) > 0) autoCond, err := statement.BuildConds(statement.RefTable, bean, true, true, false, true, addedTableName) @@ -927,15 +616,6 @@ func (statement *Statement) mergeConds(bean interface{}) error { return statement.ProcessIDParam() } -// GenConds generates conditions -func (statement *Statement) GenConds(bean interface{}) (string, []interface{}, error) { - if err := statement.mergeConds(bean); err != nil { - return "", nil, err - } - - return statement.GenCondSQL(statement.cond) -} - func (statement *Statement) quoteColumnStr(columnStr string) string { columns := strings.Split(columnStr, ",") return statement.dialect.Quoter().Join(columns, ",") diff --git a/internal/statements/table_name.go b/internal/statements/table_name.go new file mode 100644 index 00000000..8072a99d --- /dev/null +++ b/internal/statements/table_name.go @@ -0,0 +1,56 @@ +// Copyright 2022 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 statements + +import ( + "fmt" + "strings" + + "xorm.io/builder" + "xorm.io/xorm/schemas" +) + +// TableName return current tableName +func (statement *Statement) TableName() string { + if statement.AltTableName != "" { + return statement.AltTableName + } + + return statement.tableName +} + +// Alias set the table alias +func (statement *Statement) Alias(alias string) *Statement { + statement.TableAlias = alias + return statement +} + +func (statement *Statement) writeAlias(w builder.Writer) error { + if statement.TableAlias != "" { + if statement.dialect.URI().DBType == schemas.ORACLE { + if _, err := fmt.Fprint(w, " ", statement.quote(statement.TableAlias)); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(w, " AS ", statement.quote(statement.TableAlias)); err != nil { + return err + } + } + } + return nil +} + +func (statement *Statement) writeTableName(w builder.Writer) error { + if statement.dialect.URI().DBType == schemas.MSSQL && strings.Contains(statement.TableName(), "..") { + if _, err := fmt.Fprint(w, statement.TableName()); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(w, statement.quote(statement.TableName())); err != nil { + return err + } + } + return nil +} diff --git a/internal/utils/builder.go b/internal/utils/builder.go new file mode 100644 index 00000000..bc97526f --- /dev/null +++ b/internal/utils/builder.go @@ -0,0 +1,27 @@ +// Copyright 2022 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 utils + +import ( + "fmt" + + "xorm.io/builder" +) + +type BuildReader interface { + String() string + Args() []interface{} +} + +// WriteBuilder writes writers to one +func WriteBuilder(w *builder.BytesWriter, inputs ...BuildReader) error { + for _, input := range inputs { + if _, err := fmt.Fprint(w, input.String()); err != nil { + return err + } + w.Append(input.Args()...) + } + return nil +} diff --git a/session.go b/session.go index 64b98bfe..388678cd 100644 --- a/session.go +++ b/session.go @@ -275,8 +275,8 @@ func (session *Session) Limit(limit int, start ...int) *Session { // OrderBy provide order by query condition, the input parameter is the content // after order by on a sql statement. -func (session *Session) OrderBy(order string) *Session { - session.statement.OrderBy(order) +func (session *Session) OrderBy(order interface{}, args ...interface{}) *Session { + session.statement.OrderBy(order, args...) return session } diff --git a/session_delete.go b/session_delete.go index a0f420b1..322d5a44 100644 --- a/session_delete.go +++ b/session_delete.go @@ -9,7 +9,9 @@ import ( "fmt" "strconv" + "xorm.io/builder" "xorm.io/xorm/caches" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -99,10 +101,9 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) { } var ( - condSQL string - condArgs []interface{} - err error - bean interface{} + condWriter = builder.NewWriter() + err error + bean interface{} ) if len(beans) > 0 { bean = beans[0] @@ -116,115 +117,97 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) { processor.BeforeDelete() } - condSQL, condArgs, err = session.statement.GenConds(bean) - } else { - condSQL, condArgs, err = session.statement.GenCondSQL(session.statement.Conds()) + if err = session.statement.MergeConds(bean); err != nil { + return 0, err + } } - if err != nil { + + if err = session.statement.Conds().WriteTo(session.statement.QuoteReplacer(condWriter)); err != nil { return 0, err } pLimitN := session.statement.LimitN - if len(condSQL) == 0 && (pLimitN == nil || *pLimitN == 0) { + if condWriter.Len() == 0 && (pLimitN == nil || *pLimitN == 0) { return 0, ErrNeedDeletedCond } - var tableNameNoQuote = session.statement.TableName() - var tableName = session.engine.Quote(tableNameNoQuote) - var table = session.statement.RefTable - var deleteSQL string - if len(condSQL) > 0 { - deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL) - } else { - deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName) + tableNameNoQuote := session.statement.TableName() + tableName := session.engine.Quote(tableNameNoQuote) + table := session.statement.RefTable + deleteSQLWriter := builder.NewWriter() + fmt.Fprintf(deleteSQLWriter, "DELETE FROM %v", tableName) + if condWriter.Len() > 0 { + fmt.Fprintf(deleteSQLWriter, " WHERE %v", condWriter.String()) + deleteSQLWriter.Append(condWriter.Args()...) } - var orderSQL string - if len(session.statement.OrderStr) > 0 { - orderSQL += fmt.Sprintf(" ORDER BY %s", session.statement.OrderStr) + orderSQLWriter := builder.NewWriter() + if err := session.statement.WriteOrderBy(orderSQLWriter); err != nil { + return 0, err } + if pLimitN != nil && *pLimitN > 0 { limitNValue := *pLimitN - orderSQL += fmt.Sprintf(" LIMIT %d", limitNValue) + if _, err := fmt.Fprintf(orderSQLWriter, " LIMIT %d", limitNValue); err != nil { + return 0, err + } } - if len(orderSQL) > 0 { + orderCondWriter := builder.NewWriter() + if orderSQLWriter.Len() > 0 { switch session.engine.dialect.URI().DBType { case schemas.POSTGRES: - inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL) - if len(condSQL) > 0 { - deleteSQL += " AND " + inSQL + if condWriter.Len() > 0 { + fmt.Fprintf(orderCondWriter, " AND ") } else { - deleteSQL += " WHERE " + inSQL + fmt.Fprintf(orderCondWriter, " WHERE ") } + fmt.Fprintf(orderCondWriter, "ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQLWriter.String()) + orderCondWriter.Append(orderSQLWriter.Args()...) case schemas.SQLITE: - inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL) - if len(condSQL) > 0 { - deleteSQL += " AND " + inSQL + if condWriter.Len() > 0 { + fmt.Fprintf(orderCondWriter, " AND ") } else { - deleteSQL += " WHERE " + inSQL + fmt.Fprintf(orderCondWriter, " WHERE ") } + fmt.Fprintf(orderCondWriter, "rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQLWriter.String()) // TODO: how to handle delete limit on mssql? case schemas.MSSQL: return 0, ErrNotImplemented default: - deleteSQL += orderSQL + fmt.Fprint(orderCondWriter, orderSQLWriter.String()) + orderCondWriter.Append(orderSQLWriter.Args()...) } } - var realSQL string - argsForCache := make([]interface{}, 0, len(condArgs)*2) + realSQLWriter := builder.NewWriter() + argsForCache := make([]interface{}, 0, len(deleteSQLWriter.Args())*2) + copy(argsForCache, deleteSQLWriter.Args()) + argsForCache = append(deleteSQLWriter.Args(), argsForCache...) if session.statement.GetUnscoped() || table == nil || table.DeletedColumn() == nil { // tag "deleted" is disabled - realSQL = deleteSQL - copy(argsForCache, condArgs) - argsForCache = append(condArgs, argsForCache...) + if err := utils.WriteBuilder(realSQLWriter, deleteSQLWriter, orderCondWriter); err != nil { + return 0, err + } } else { - // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for caches. - copy(argsForCache, condArgs) - argsForCache = append(condArgs, argsForCache...) - deletedColumn := table.DeletedColumn() - realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v", + if _, err := fmt.Fprintf(realSQLWriter, "UPDATE %v SET %v = ? WHERE %v", session.engine.Quote(session.statement.TableName()), session.engine.Quote(deletedColumn.Name), - condSQL) - - if len(orderSQL) > 0 { - switch session.engine.dialect.URI().DBType { - case schemas.POSTGRES: - inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL) - if len(condSQL) > 0 { - realSQL += " AND " + inSQL - } else { - realSQL += " WHERE " + inSQL - } - case schemas.SQLITE: - inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL) - if len(condSQL) > 0 { - realSQL += " AND " + inSQL - } else { - realSQL += " WHERE " + inSQL - } - // TODO: how to handle delete limit on mssql? - case schemas.MSSQL: - return 0, ErrNotImplemented - default: - realSQL += orderSQL - } + condWriter.String()); err != nil { + return 0, err } - - // !oinume! Insert nowTime to the head of session.statement.Params - condArgs = append(condArgs, "") - paramsLen := len(condArgs) - copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1]) - val, t, err := session.engine.nowTime(deletedColumn) if err != nil { return 0, err } - condArgs[0] = val + realSQLWriter.Append(val) + realSQLWriter.Append(condWriter.Args()...) - var colName = deletedColumn.Name + if err := utils.WriteBuilder(realSQLWriter, orderCondWriter); err != nil { + return 0, err + } + + colName := deletedColumn.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { col := table.GetColumn(colName) setColumnTime(bean, col, t) @@ -232,11 +215,11 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) { } if cacher := session.engine.GetCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache { - _ = session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) + _ = session.cacheDelete(table, tableNameNoQuote, deleteSQLWriter.String(), argsForCache...) } session.statement.RefTable = table - res, err := session.exec(realSQL, condArgs...) + res, err := session.exec(realSQLWriter.String(), realSQLWriter.Args()...) if err != nil { return 0, err } diff --git a/session_find.go b/session_find.go index caf79ee3..2270454b 100644 --- a/session_find.go +++ b/session_find.go @@ -60,9 +60,7 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte if len(session.statement.ColumnMap) > 0 && !session.statement.IsDistinct { session.statement.ColumnMap = []string{} } - if session.statement.OrderStr != "" { - session.statement.OrderStr = "" - } + session.statement.ResetOrderBy() if session.statement.LimitN != nil { session.statement.LimitN = nil } @@ -85,15 +83,15 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) - var isSlice = sliceValue.Kind() == reflect.Slice - var isMap = sliceValue.Kind() == reflect.Map + isSlice := sliceValue.Kind() == reflect.Slice + isMap := sliceValue.Kind() == reflect.Map if !isSlice && !isMap { return errors.New("needs a pointer to a slice or a map") } sliceElementType := sliceValue.Type().Elem() - var tp = tpStruct + tp := tpStruct if session.statement.RefTable == nil { if sliceElementType.Kind() == reflect.Ptr { if sliceElementType.Elem().Kind() == reflect.Struct { @@ -190,7 +188,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect return err } - var newElemFunc = func(fields []string) reflect.Value { + newElemFunc := func(fields []string) reflect.Value { return utils.New(elemType, len(fields), len(fields)) } @@ -235,7 +233,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect } if elemType.Kind() == reflect.Struct { - var newValue = newElemFunc(fields) + newValue := newElemFunc(fields) tb, err := session.engine.tagParser.ParseWithCache(newValue) if err != nil { return err @@ -249,7 +247,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect } for rows.Next() { - var newValue = newElemFunc(fields) + newValue := newElemFunc(fields) bean := newValue.Interface() switch elemType.Kind() { @@ -310,7 +308,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in session.engine.logger.Debugf("[cacheFind] ids length > 500, no cache") return ErrCacheFailed } - var res = make([]string, len(table.PrimaryKeys)) + res := make([]string, len(table.PrimaryKeys)) err = rows.ScanSlice(&res) if err != nil { return err @@ -342,7 +340,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in ididxes := make(map[string]int) var ides []schemas.PK - var temps = make([]interface{}, len(ids)) + temps := make([]interface{}, len(ids)) for idx, id := range ids { sid, err := id.ToString() @@ -457,7 +455,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(bean)))) } } else if sliceValue.Kind() == reflect.Map { - var key = ids[j] + key := ids[j] keyType := sliceValue.Type().Key() keyValue := reflect.New(keyType) var ikey interface{} diff --git a/session_update.go b/session_update.go index fefbee90..76f311d6 100644 --- a/session_update.go +++ b/session_update.go @@ -60,7 +60,7 @@ func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr stri ids = make([]schemas.PK, 0) for rows.Next() { - var res = make([]string, len(table.PrimaryKeys)) + res := make([]string, len(table.PrimaryKeys)) err = rows.ScanSlice(&res) if err != nil { return err @@ -176,8 +176,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 // -- var err error - var isMap = t.Kind() == reflect.Map - var isStruct = t.Kind() == reflect.Struct + isMap := t.Kind() == reflect.Map + isStruct := t.Kind() == reflect.Struct if isStruct { if err := session.statement.SetRefBean(bean); err != nil { return 0, err @@ -226,7 +226,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 args = append(args, val) } - var colName = col.Name + colName := col.Name if isStruct { session.afterClosures = append(session.afterClosures, func(bean interface{}) { col := table.GetColumn(colName) @@ -258,10 +258,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } colNames = append(colNames, session.engine.Quote(expr.ColName)+"="+tp) case *builder.Builder: - subQuery, subArgs, err := session.statement.GenCondSQL(tp) + subQuery, subArgs, err := builder.ToSQL(tp) if err != nil { return 0, err } + subQuery = session.statement.ReplaceQuote(subQuery) colNames = append(colNames, session.engine.Quote(expr.ColName)+"=("+subQuery+")") args = append(args, subArgs...) default: @@ -279,7 +280,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 condBeanIsStruct := false if len(condiBean) > 0 { if c, ok := condiBean[0].(map[string]interface{}); ok { - var eq = make(builder.Eq) + eq := make(builder.Eq) for k, v := range c { eq[session.engine.Quote(k)] = v } @@ -323,11 +324,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 st := session.statement var ( - sqlStr string - condArgs []interface{} - condSQL string cond = session.statement.Conds().And(autoCond) - doIncVer = isStruct && (table != nil && table.Version != "" && session.statement.CheckVersion) verValue *reflect.Value ) @@ -347,70 +344,65 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return 0, ErrNoColumnsTobeUpdated } - condSQL, condArgs, err = session.statement.GenCondSQL(cond) - if err != nil { + whereWriter := builder.NewWriter() + if cond.IsValid() { + fmt.Fprint(whereWriter, "WHERE ") + } + if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil { + return 0, err + } + if err := st.WriteOrderBy(whereWriter); err != nil { return 0, err } - if len(condSQL) > 0 { - condSQL = "WHERE " + condSQL - } - - if st.OrderStr != "" { - condSQL += fmt.Sprintf(" ORDER BY %v", st.OrderStr) - } - - var tableName = session.statement.TableName() + tableName := session.statement.TableName() // TODO: Oracle support needed var top string if st.LimitN != nil { limitValue := *st.LimitN switch session.engine.dialect.URI().DBType { case schemas.MYSQL: - condSQL += fmt.Sprintf(" LIMIT %d", limitValue) + fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) case schemas.SQLITE: - tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", limitValue) + fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) + cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)", - session.engine.Quote(tableName), tempCondSQL), condArgs...)) - condSQL, condArgs, err = session.statement.GenCondSQL(cond) - if err != nil { + session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...)) + + whereWriter = builder.NewWriter() + fmt.Fprint(whereWriter, "WHERE ") + if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil { return 0, err } - if len(condSQL) > 0 { - condSQL = "WHERE " + condSQL - } case schemas.POSTGRES: - tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", limitValue) + fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) + cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)", - session.engine.Quote(tableName), tempCondSQL), condArgs...)) - condSQL, condArgs, err = session.statement.GenCondSQL(cond) - if err != nil { + session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...)) + + whereWriter = builder.NewWriter() + fmt.Fprint(whereWriter, "WHERE ") + if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil { return 0, err } - - if len(condSQL) > 0 { - condSQL = "WHERE " + condSQL - } case schemas.MSSQL: - if st.OrderStr != "" && table != nil && len(table.PrimaryKeys) == 1 { + if st.HasOrderBy() && table != nil && len(table.PrimaryKeys) == 1 { cond = builder.Expr(fmt.Sprintf("%s IN (SELECT TOP (%d) %s FROM %v%v)", table.PrimaryKeys[0], limitValue, table.PrimaryKeys[0], - session.engine.Quote(tableName), condSQL), condArgs...) + session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...) - condSQL, condArgs, err = session.statement.GenCondSQL(cond) - if err != nil { + whereWriter = builder.NewWriter() + fmt.Fprint(whereWriter, "WHERE ") + if err := cond.WriteTo(whereWriter); err != nil { return 0, err } - if len(condSQL) > 0 { - condSQL = "WHERE " + condSQL - } } else { top = fmt.Sprintf("TOP (%d) ", limitValue) } } } - var tableAlias = session.engine.Quote(tableName) + tableAlias := session.engine.Quote(tableName) var fromSQL string if session.statement.TableAlias != "" { switch session.engine.dialect.URI().DBType { @@ -422,14 +414,19 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } - sqlStr = fmt.Sprintf("UPDATE %v%v SET %v %v%v", + updateWriter := builder.NewWriter() + if _, err := fmt.Fprintf(updateWriter, "UPDATE %v%v SET %v %v", top, tableAlias, strings.Join(colNames, ", "), - fromSQL, - condSQL) + fromSQL); err != nil { + return 0, err + } + if err := utils.WriteBuilder(updateWriter, whereWriter); err != nil { + return 0, err + } - res, err := session.exec(sqlStr, append(args, condArgs...)...) + res, err := session.exec(updateWriter.String(), append(args, updateWriter.Args()...)...) if err != nil { return 0, err } else if doIncVer { @@ -535,7 +532,7 @@ func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interfac } args = append(args, val) - var colName = col.Name + colName := col.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { col := table.GetColumn(colName) setColumnTime(bean, col, t) From c98930f8f2a5827f54376b5e23467d29acca6ef5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 3 Jun 2022 15:24:24 +0800 Subject: [PATCH 062/116] Fix oid index for postgres (#2154) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2154 --- dialects/postgres.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/dialects/postgres.go b/dialects/postgres.go index 83e4187f..ba73aad7 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -862,11 +862,11 @@ func (db *postgres) needQuote(name string) bool { func (db *postgres) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: - var q = postgresQuoter + q := postgresQuoter q.IsReserved = schemas.AlwaysNoReserve db.quoter = q case QuotePolicyReserved: - var q = postgresQuoter + q := postgresQuoter q.IsReserved = db.needQuote db.quoter = q case QuotePolicyAlways: @@ -1125,7 +1125,7 @@ WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s A col.Name = strings.Trim(colName, `" `) if colDefault != nil { - var theDefault = *colDefault + theDefault := *colDefault // cockroach has type with the default value with ::: // and postgres with ::, we should remove them before store them idx := strings.Index(theDefault, ":::") @@ -1301,15 +1301,8 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN } colNames = getIndexColName(indexdef) - isSkip := false - //Oid It's a special index. You can't put it in - for _, element := range colNames { - if "oid" == element { - isSkip = true - break - } - } - if isSkip { + // Oid It's a special index. You can't put it in. TODO: This is not perfect. + if indexName == tableName+"_oid_index" && len(colNames) == 1 && colNames[0] == "oid" { continue } From f469d8816644fdb21ad3c6b6da85514daa23219f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 3 Jun 2022 17:44:49 +0800 Subject: [PATCH 063/116] Update changelog for 1.3.1 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fed4e261..ae213c99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log. +## [1.3.1](https://gitea.com/xorm/xorm/releases/tag/1.3.1) - 2022-06-03 + +* BREAKING + * Refactor orderby and support arguments (#2150) + * return a clear error for set TEXT type as compare condition (#2062) +* BUGFIXES + * Fix oid index for postgres (#2154) + * Add ORDER BY SEQ_IN_INDEX to MySQL GetIndexes to Fix IndexTests (#2152) + * some improvement (#2136) +* ENHANCEMENTS + * Add interface to allow structs to provide specific index information (#2137) + * MySQL/MariaDB: return max length for text columns (#2133) + * PostgreSQL: enable comment on column (#2131) +* TESTING + * Add test for find date (#2121) + ## [1.3.0](https://gitea.com/xorm/xorm/releases/tag/1.3.0) - 2022-04-14 * BREAKING From c3bce556200f3356803beec9147210ba46319f99 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Thu, 14 Jul 2022 13:55:24 +0800 Subject: [PATCH 064/116] Change schemas.Column to use int64 (#2160) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2160 Reviewed-by: Lunny Xiao Co-authored-by: Andrew Thornton Co-committed-by: Andrew Thornton --- dialects/dameng.go | 26 +++++++-------- dialects/mssql.go | 14 ++++---- dialects/mysql.go | 12 +++---- dialects/oracle.go | 20 +++++------ dialects/postgres.go | 10 +++--- dialects/time.go | 12 ++++--- integrations/engine_test.go | 8 ++--- schemas/column.go | 8 ++--- schemas/type.go | 4 +-- tags/tag.go | 66 ++++++++++++++++++------------------- 10 files changed, 89 insertions(+), 91 deletions(-) diff --git a/dialects/dameng.go b/dialects/dameng.go index f4a075d5..5e92ec2f 100644 --- a/dialects/dameng.go +++ b/dialects/dameng.go @@ -622,9 +622,9 @@ func (db *dameng) SQLType(c *schemas.Column) string { hasLen2 := (c.Length2 > 0) if hasLen2 { - res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + "," + strconv.FormatInt(c.Length2, 10) + ")" } else if hasLen1 { - res += "(" + strconv.Itoa(c.Length) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + ")" } return res } @@ -729,11 +729,11 @@ func (db *dameng) CreateTableSQL(ctx context.Context, queryer core.Queryer, tabl func (db *dameng) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: - var q = damengQuoter + q := damengQuoter q.IsReserved = schemas.AlwaysNoReserve db.quoter = q case QuotePolicyReserved: - var q = damengQuoter + q := damengQuoter q.IsReserved = db.IsReserved db.quoter = q case QuotePolicyAlways: @@ -792,7 +792,7 @@ type dmClobObject interface { ReadString(int, int) (string, error) } -//var _ dmClobObject = &dm.DmClob{} +// var _ dmClobObject = &dm.DmClob{} func (d *dmClobScanner) Scan(data interface{}) error { if data == nil { @@ -927,7 +927,7 @@ func (db *dameng) GetColumns(queryer core.Queryer, ctx context.Context, tableNam var ( ignore bool dt string - len1, len2 int + len1, len2 int64 ) dts := strings.Split(dataType.String, "(") @@ -935,10 +935,10 @@ func (db *dameng) GetColumns(queryer core.Queryer, ctx context.Context, tableNam if len(dts) > 1 { lens := strings.Split(dts[1][:len(dts[1])-1], ",") if len(lens) > 1 { - len1, _ = strconv.Atoi(lens[0]) - len2, _ = strconv.Atoi(lens[1]) + len1, _ = strconv.ParseInt(lens[0], 10, 64) + len2, _ = strconv.ParseInt(lens[1], 10, 64) } else { - len1, _ = strconv.Atoi(lens[0]) + len1, _ = strconv.ParseInt(lens[0], 10, 64) } } @@ -972,9 +972,9 @@ func (db *dameng) GetColumns(queryer core.Queryer, ctx context.Context, tableNam } if col.SQLType.Name == "TIMESTAMP" { - col.Length = int(dataScale.Int64) + col.Length = dataScale.Int64 } else { - col.Length = int(dataLen.Int64) + col.Length = dataLen.Int64 } if col.SQLType.IsTime() { @@ -1140,8 +1140,8 @@ func (d *damengDriver) GenScanResult(colType string) (interface{}, error) { } func (d *damengDriver) Scan(ctx *ScanContext, rows *core.Rows, types []*sql.ColumnType, vv ...interface{}) error { - var scanResults = make([]interface{}, 0, len(types)) - var replaces = make([]bool, 0, len(types)) + scanResults := make([]interface{}, 0, len(types)) + replaces := make([]bool, 0, len(types)) var err error for i, v := range vv { var replaced bool diff --git a/dialects/mssql.go b/dialects/mssql.go index 706a754a..1b6fe692 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -229,7 +229,7 @@ func (db *mssql) Init(uri *URI) error { func (db *mssql) SetParams(params map[string]string) { defaultVarchar, ok := params["DEFAULT_VARCHAR"] if ok { - var t = strings.ToUpper(defaultVarchar) + t := strings.ToUpper(defaultVarchar) switch t { case "NVARCHAR", "VARCHAR": db.defaultVarchar = t @@ -242,7 +242,7 @@ func (db *mssql) SetParams(params map[string]string) { defaultChar, ok := params["DEFAULT_CHAR"] if ok { - var t = strings.ToUpper(defaultChar) + t := strings.ToUpper(defaultChar) switch t { case "NCHAR", "CHAR": db.defaultChar = t @@ -375,9 +375,9 @@ func (db *mssql) SQLType(c *schemas.Column) string { hasLen2 := (c.Length2 > 0) if hasLen2 { - res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + "," + strconv.FormatInt(c.Length2, 10) + ")" } else if hasLen1 { - res += "(" + strconv.Itoa(c.Length) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + ")" } return res } @@ -403,11 +403,11 @@ func (db *mssql) IsReserved(name string) bool { func (db *mssql) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: - var q = mssqlQuoter + q := mssqlQuoter q.IsReserved = schemas.AlwaysNoReserve db.quoter = q case QuotePolicyReserved: - var q = mssqlQuoter + q := mssqlQuoter q.IsReserved = db.IsReserved db.quoter = q case QuotePolicyAlways: @@ -475,7 +475,7 @@ func (db *mssql) GetColumns(queryer core.Queryer, ctx context.Context, tableName colSeq := make([]string, 0) for rows.Next() { var name, ctype, vdefault string - var maxLen, precision, scale int + var maxLen, precision, scale int64 var nullable, isPK, defaultIsNull, isIncrement bool err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &defaultIsNull, &vdefault, &isPK, &isIncrement) if err != nil { diff --git a/dialects/mysql.go b/dialects/mysql.go index 31e7b788..6ed4a1be 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -330,9 +330,9 @@ func (db *mysql) SQLType(c *schemas.Column) string { } if hasLen2 { - res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + "," + strconv.FormatInt(c.Length2, 10) + ")" } else if hasLen1 { - res += "(" + strconv.Itoa(c.Length) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + ")" } if isUnsigned { @@ -444,7 +444,7 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName // Remove the /* mariadb-5.3 */ suffix from coltypes colName = strings.TrimSuffix(colName, "/* mariadb-5.3 */") colType = strings.ToUpper(colName) - var len1, len2 int + var len1, len2 int64 if len(cts) == 2 { idx := strings.Index(cts[1], ")") if colType == schemas.Enum && cts[1][0] == '\'' { // enum @@ -465,12 +465,12 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName } } else { lens := strings.Split(cts[1][0:idx], ",") - len1, err = strconv.Atoi(strings.TrimSpace(lens[0])) + len1, err = strconv.ParseInt(strings.TrimSpace(lens[0]), 10, 64) if err != nil { return nil, nil, err } if len(lens) == 2 { - len2, err = strconv.Atoi(lens[1]) + len2, err = strconv.ParseInt(lens[1], 10, 64) if err != nil { return nil, nil, err } @@ -479,7 +479,7 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName } else { switch colType { case "MEDIUMTEXT", "LONGTEXT", "TEXT": - len1, err = strconv.Atoi(*maxLength) + len1, err = strconv.ParseInt(*maxLength, 10, 64) if err != nil { return nil, nil, err } diff --git a/dialects/oracle.go b/dialects/oracle.go index 04652bd6..8328ff15 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -570,9 +570,9 @@ func (db *oracle) SQLType(c *schemas.Column) string { hasLen2 := (c.Length2 > 0) if hasLen2 { - res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + "," + strconv.FormatInt(c.Length2, 10) + ")" } else if hasLen1 { - res += "(" + strconv.Itoa(c.Length) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + ")" } return res } @@ -606,7 +606,7 @@ func (db *oracle) DropTableSQL(tableName string) (string, bool) { } func (db *oracle) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { - var sql = "CREATE TABLE " + sql := "CREATE TABLE " if tableName == "" { tableName = table.Name } @@ -641,11 +641,11 @@ func (db *oracle) CreateTableSQL(ctx context.Context, queryer core.Queryer, tabl func (db *oracle) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: - var q = oracleQuoter + q := oracleQuoter q.IsReserved = schemas.AlwaysNoReserve db.quoter = q case QuotePolicyReserved: - var q = oracleQuoter + q := oracleQuoter q.IsReserved = db.IsReserved db.quoter = q case QuotePolicyAlways: @@ -690,7 +690,7 @@ func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableNam col.Indexes = make(map[string]int) var colName, colDefault, nullable, dataType, dataPrecision, dataScale *string - var dataLen int + var dataLen int64 err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision, &dataScale, &nullable) @@ -713,16 +713,16 @@ func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableNam var ignore bool var dt string - var len1, len2 int + var len1, len2 int64 dts := strings.Split(*dataType, "(") dt = dts[0] if len(dts) > 1 { lens := strings.Split(dts[1][:len(dts[1])-1], ",") if len(lens) > 1 { - len1, _ = strconv.Atoi(lens[0]) - len2, _ = strconv.Atoi(lens[1]) + len1, _ = strconv.ParseInt(lens[0], 10, 64) + len2, _ = strconv.ParseInt(lens[1], 10, 64) } else { - len1, _ = strconv.Atoi(lens[0]) + len1, _ = strconv.ParseInt(lens[0], 10, 64) } } diff --git a/dialects/postgres.go b/dialects/postgres.go index ba73aad7..3c7ecb35 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -934,9 +934,9 @@ func (db *postgres) SQLType(c *schemas.Column) string { hasLen2 := (c.Length2 > 0) if hasLen2 { - res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + "," + strconv.FormatInt(c.Length2, 10) + ")" } else if hasLen1 { - res += "(" + strconv.Itoa(c.Length) + ")" + res += "(" + strconv.FormatInt(c.Length, 10) + ")" } return res } @@ -1110,9 +1110,9 @@ WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s A return nil, nil, err } - var maxLen int + var maxLen int64 if maxLenStr != nil { - maxLen, err = strconv.Atoi(*maxLenStr) + maxLen, err = strconv.ParseInt(*maxLenStr, 10, 64) if err != nil { return nil, nil, err } @@ -1186,7 +1186,7 @@ WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s A startIdx := strings.Index(strings.ToLower(dataType), "string(") if startIdx != -1 && strings.HasSuffix(dataType, ")") { length := dataType[startIdx+8 : len(dataType)-1] - l, _ := strconv.Atoi(length) + l, _ := strconv.ParseInt(length, 10, 64) col.SQLType = schemas.SQLType{Name: "STRING", DefaultLength: l, DefaultLength2: 0} } else { col.SQLType = schemas.SQLType{Name: strings.ToUpper(dataType), DefaultLength: 0, DefaultLength2: 0} diff --git a/dialects/time.go b/dialects/time.go index f0bbb765..cdc896be 100644 --- a/dialects/time.go +++ b/dialects/time.go @@ -23,7 +23,7 @@ func FormatColumnTime(dialect Dialect, dbLocation *time.Location, col *schemas.C } } - var tmZone = dbLocation + tmZone := dbLocation if col.TimeZone != nil { tmZone = col.TimeZone } @@ -34,15 +34,17 @@ func FormatColumnTime(dialect Dialect, dbLocation *time.Location, col *schemas.C case schemas.Date: return t.Format("2006-01-02"), nil case schemas.Time: - var layout = "15:04:05" + layout := "15:04:05" if col.Length > 0 { - layout += "." + strings.Repeat("0", col.Length) + // we can use int(...) casting here as it's very unlikely to a huge sized field + layout += "." + strings.Repeat("0", int(col.Length)) } return t.Format(layout), nil case schemas.DateTime, schemas.TimeStamp: - var layout = "2006-01-02 15:04:05" + layout := "2006-01-02 15:04:05" if col.Length > 0 { - layout += "." + strings.Repeat("0", col.Length) + // we can use int(...) casting here as it's very unlikely to a huge sized field + layout += "." + strings.Repeat("0", int(col.Length)) } return t.Format(layout), nil case schemas.Varchar: diff --git a/integrations/engine_test.go b/integrations/engine_test.go index 905c4f24..730a424e 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -290,13 +290,11 @@ func TestGetColumnsComment(t *testing.T) { } func TestGetColumnsLength(t *testing.T) { - var max_length int + var max_length int64 switch testEngine.Dialect().URI().DBType { - case - schemas.POSTGRES: + case schemas.POSTGRES: max_length = 0 - case - schemas.MYSQL: + case schemas.MYSQL: max_length = 65535 default: t.Skip() diff --git a/schemas/column.go b/schemas/column.go index 4bbb6c2d..001769cd 100644 --- a/schemas/column.go +++ b/schemas/column.go @@ -26,8 +26,8 @@ type Column struct { FieldIndex []int // Available only when parsed from a struct SQLType SQLType IsJSON bool - Length int - Length2 int + Length int64 + Length2 int64 Nullable bool Default string Indexes map[string]int @@ -48,7 +48,7 @@ type Column struct { } // NewColumn creates a new column -func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column { +func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int64, nullable bool) *Column { return &Column{ Name: name, IsJSON: sqlType.IsJson(), @@ -82,7 +82,7 @@ func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) { // ValueOfV returns column's filed of struct's value accept reflevt value func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { - var v = *dataStruct + v := *dataStruct for _, i := range col.FieldIndex { if v.Kind() == reflect.Ptr { if v.IsNil() { diff --git a/schemas/type.go b/schemas/type.go index 8702862a..b8b30851 100644 --- a/schemas/type.go +++ b/schemas/type.go @@ -28,8 +28,8 @@ const ( // SQLType represents SQL types type SQLType struct { Name string - DefaultLength int - DefaultLength2 int + DefaultLength int64 + DefaultLength2 int64 } // enumerates all columns types diff --git a/tags/tag.go b/tags/tag.go index 4e1f1ce7..55f5f4cf 100644 --- a/tags/tag.go +++ b/tags/tag.go @@ -99,33 +99,31 @@ type Context struct { // Handler describes tag handler for XORM type Handler func(ctx *Context) error -var ( - // defaultTagHandlers enumerates all the default tag handler - defaultTagHandlers = map[string]Handler{ - "-": IgnoreHandler, - "<-": OnlyFromDBTagHandler, - "->": OnlyToDBTagHandler, - "PK": PKTagHandler, - "NULL": NULLTagHandler, - "NOT": NotTagHandler, - "AUTOINCR": AutoIncrTagHandler, - "DEFAULT": DefaultTagHandler, - "CREATED": CreatedTagHandler, - "UPDATED": UpdatedTagHandler, - "DELETED": DeletedTagHandler, - "VERSION": VersionTagHandler, - "UTC": UTCTagHandler, - "LOCAL": LocalTagHandler, - "NOTNULL": NotNullTagHandler, - "INDEX": IndexTagHandler, - "UNIQUE": UniqueTagHandler, - "CACHE": CacheTagHandler, - "NOCACHE": NoCacheTagHandler, - "COMMENT": CommentTagHandler, - "EXTENDS": ExtendsTagHandler, - "UNSIGNED": UnsignedTagHandler, - } -) +// defaultTagHandlers enumerates all the default tag handler +var defaultTagHandlers = map[string]Handler{ + "-": IgnoreHandler, + "<-": OnlyFromDBTagHandler, + "->": OnlyToDBTagHandler, + "PK": PKTagHandler, + "NULL": NULLTagHandler, + "NOT": NotTagHandler, + "AUTOINCR": AutoIncrTagHandler, + "DEFAULT": DefaultTagHandler, + "CREATED": CreatedTagHandler, + "UPDATED": UpdatedTagHandler, + "DELETED": DeletedTagHandler, + "VERSION": VersionTagHandler, + "UTC": UTCTagHandler, + "LOCAL": LocalTagHandler, + "NOTNULL": NotNullTagHandler, + "INDEX": IndexTagHandler, + "UNIQUE": UniqueTagHandler, + "CACHE": CacheTagHandler, + "NOCACHE": NoCacheTagHandler, + "COMMENT": CommentTagHandler, + "EXTENDS": ExtendsTagHandler, + "UNSIGNED": UnsignedTagHandler, +} func init() { for k := range schemas.SqlTypes { @@ -312,16 +310,16 @@ func SQLTypeTagHandler(ctx *Context) error { default: var err error if len(ctx.params) == 2 { - ctx.col.Length, err = strconv.Atoi(ctx.params[0]) + ctx.col.Length, err = strconv.ParseInt(ctx.params[0], 10, 64) if err != nil { return err } - ctx.col.Length2, err = strconv.Atoi(ctx.params[1]) + ctx.col.Length2, err = strconv.ParseInt(ctx.params[1], 10, 64) if err != nil { return err } } else if len(ctx.params) == 1 { - ctx.col.Length, err = strconv.Atoi(ctx.params[0]) + ctx.col.Length, err = strconv.ParseInt(ctx.params[0], 10, 64) if err != nil { return err } @@ -332,8 +330,8 @@ func SQLTypeTagHandler(ctx *Context) error { // ExtendsTagHandler describes extends tag handler func ExtendsTagHandler(ctx *Context) error { - var fieldValue = ctx.fieldValue - var isPtr = false + fieldValue := ctx.fieldValue + isPtr := false switch fieldValue.Kind() { case reflect.Ptr: f := fieldValue.Type().Elem() @@ -355,7 +353,7 @@ func ExtendsTagHandler(ctx *Context) error { col.FieldName = fmt.Sprintf("%v.%v", ctx.col.FieldName, col.FieldName) col.FieldIndex = append(ctx.col.FieldIndex, col.FieldIndex...) - var tagPrefix = ctx.col.FieldName + tagPrefix := ctx.col.FieldName if len(ctx.params) > 0 { col.Nullable = isPtr tagPrefix = strings.Trim(ctx.params[0], "'") @@ -378,7 +376,7 @@ func ExtendsTagHandler(ctx *Context) error { } } default: - //TODO: warning + // TODO: warning } return ErrIgnoreField } From c900ecc87fbd1e29a496a27d103adb07cfa9971f Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sat, 3 Sep 2022 10:12:17 +0800 Subject: [PATCH 065/116] Prevent Sync failure with non-regular indexes on Postgres (#2174) When dropping indexes in Postgres if the index is non-regular we should not attempt to regularise the index name as it is already correct. Signed-off-by: Andrew Thornton Reviewed-on: https://gitea.com/xorm/xorm/pulls/2174 Reviewed-by: Lunny Xiao Co-authored-by: Andrew Thornton Co-committed-by: Andrew Thornton --- dialects/postgres.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dialects/postgres.go b/dialects/postgres.go index 3c7ecb35..f9de5859 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -1030,11 +1030,10 @@ func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".") tableName = tableParts[len(tableParts)-1] - if !strings.HasPrefix(idxName, "UQE_") && - !strings.HasPrefix(idxName, "IDX_") { - if index.Type == schemas.UniqueType { + if index.IsRegular { + if index.Type == schemas.UniqueType && !strings.HasPrefix(idxName, "UQE_") { idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name) - } else { + } else if index.Type == schemas.IndexType && !strings.HasPrefix(idxName, "IDX_") { idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name) } } From bd58520020dfb5bd6b7f5779e871d53aa9ee4c71 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 3 Sep 2022 15:14:19 +0800 Subject: [PATCH 066/116] add changelog for 1.3.2 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae213c99..6887cb97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log. +## [1.3.2](https://gitea.com/xorm/xorm/releases/tag/1.3.2) - 2022-09-03 + +* BUGFIXES + * Change schemas.Column to use int64 (#2160) +* MISC + * Prevent Sync failure with non-regular indexes on Postgres (#2174) + ## [1.3.1](https://gitea.com/xorm/xorm/releases/tag/1.3.1) - 2022-06-03 * BREAKING From 3acabdaf26fcf58fa51e9067235c0c1d456e66b5 Mon Sep 17 00:00:00 2001 From: stevefan1999 Date: Mon, 24 Oct 2022 11:29:54 +0800 Subject: [PATCH 067/116] Fix Oracle Table creation default value (#2190) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2190 Reviewed-by: Lunny Xiao Co-authored-by: stevefan1999 Co-committed-by: stevefan1999 --- dialects/oracle.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dialects/oracle.go b/dialects/oracle.go index 8328ff15..ce91cd5d 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -548,7 +548,14 @@ func (db *oracle) Features() *DialectFeatures { func (db *oracle) SQLType(c *schemas.Column) string { var res string switch t := c.SQLType.Name; t { - case schemas.Bit, schemas.TinyInt, schemas.SmallInt, schemas.MediumInt, schemas.Int, schemas.Integer, schemas.BigInt, schemas.Bool, schemas.Serial, schemas.BigSerial: + case schemas.Bool: + if c.Default == "true" { + c.Default = "1" + } else if c.Default == "false" { + c.Default = "0" + } + res = "NUMBER(1,0)" + case schemas.Bit, schemas.TinyInt, schemas.SmallInt, schemas.MediumInt, schemas.Int, schemas.Integer, schemas.BigInt, schemas.Serial, schemas.BigSerial: res = "NUMBER" case schemas.Binary, schemas.VarBinary, schemas.Blob, schemas.TinyBlob, schemas.MediumBlob, schemas.LongBlob, schemas.Bytea: return schemas.Blob From 71a5939c65fa40906fa8bc5b194349e7cd018524 Mon Sep 17 00:00:00 2001 From: tylerthail2019 Date: Wed, 16 Nov 2022 13:22:04 +0800 Subject: [PATCH 068/116] add disable version check func (#2197) Co-authored-by: tyler Reviewed-on: https://gitea.com/xorm/xorm/pulls/2197 Reviewed-by: Lunny Xiao Co-authored-by: tylerthail2019 Co-committed-by: tylerthail2019 --- session.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/session.go b/session.go index 388678cd..854a125a 100644 --- a/session.go +++ b/session.go @@ -794,3 +794,9 @@ func (session *Session) PingContext(ctx context.Context) error { session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName()) return session.DB().PingContext(ctx) } + +// disable version check +func (session *Session) NoVersionCheck() *Session { + session.statement.CheckVersion = false + return session +} From f1bfc5ce983063d6262c125e68f59599541a2e6b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 9 Dec 2022 23:37:26 +0800 Subject: [PATCH 069/116] join support condition (#2201) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2201 --- engine.go | 16 ++++++++-------- integrations/session_find_test.go | 5 +++++ interface.go | 2 +- internal/statements/join.go | 30 ++++++++++++++++++++++++------ session.go | 2 +- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/engine.go b/engine.go index 81cfc7a9..a42519ee 100644 --- a/engine.go +++ b/engine.go @@ -330,7 +330,7 @@ func (engine *Engine) Ping() error { // SQL method let's you manually write raw SQL and operate // For example: // -// engine.SQL("select * from user").Find(&users) +// engine.SQL("select * from user").Find(&users) // // This code will execute "select * from user" and set the records to users func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session { @@ -996,9 +996,8 @@ func (engine *Engine) Desc(colNames ...string) *Session { // Asc will generate "ORDER BY column1,column2 Asc" // This method can chainable use. // -// engine.Desc("name").Asc("age").Find(&users) -// // SELECT * FROM user ORDER BY name DESC, age ASC -// +// engine.Desc("name").Asc("age").Find(&users) +// // SELECT * FROM user ORDER BY name DESC, age ASC func (engine *Engine) Asc(colNames ...string) *Session { session := engine.NewSession() session.isAutoClose = true @@ -1020,7 +1019,7 @@ func (engine *Engine) Prepare() *Session { } // Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session { +func (engine *Engine) Join(joinOperator string, tablename interface{}, condition interface{}, args ...interface{}) *Session { session := engine.NewSession() session.isAutoClose = true return session.Join(joinOperator, tablename, condition, args...) @@ -1220,9 +1219,10 @@ func (engine *Engine) InsertOne(bean interface{}) (int64, error) { // Update records, bean's non-empty fields are updated contents, // condiBean' non-empty filds are conditions // CAUTION: -// 1.bool will defaultly be updated content nor conditions -// You should call UseBool if you have bool to use. -// 2.float32 & float64 may be not inexact as conditions +// +// 1.bool will defaultly be updated content nor conditions +// You should call UseBool if you have bool to use. +// 2.float32 & float64 may be not inexact as conditions func (engine *Engine) Update(bean interface{}, condiBeans ...interface{}) (int64, error) { session := engine.NewSession() defer session.Close() diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index 6701b1b5..5c2a4c68 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "xorm.io/builder" "xorm.io/xorm" "xorm.io/xorm/internal/utils" "xorm.io/xorm/names" @@ -965,6 +966,10 @@ func TestFindJoin(t *testing.T) { scenes = make([]SceneItem, 0) err = testEngine.Join("INNER", "order", "`scene_item`.`device_id`=`order`.`id`").Find(&scenes) assert.NoError(t, err) + + scenes = make([]SceneItem, 0) + err = testEngine.Join("INNER", "order", builder.Expr("`scene_item`.`device_id`=`order`.`id`")).Find(&scenes) + assert.NoError(t, err) } func TestJoinFindLimit(t *testing.T) { diff --git a/interface.go b/interface.go index 55ffebe4..6ad0577a 100644 --- a/interface.go +++ b/interface.go @@ -52,7 +52,7 @@ type Interface interface { NoAutoCondition(...bool) *Session NotIn(string, ...interface{}) *Session Nullable(...string) *Session - Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session + Join(joinOperator string, tablename interface{}, condition interface{}, args ...interface{}) *Session Omit(columns ...string) *Session OrderBy(order interface{}, args ...interface{}) *Session Ping() error diff --git a/internal/statements/join.go b/internal/statements/join.go index 45fc2441..adf349e7 100644 --- a/internal/statements/join.go +++ b/internal/statements/join.go @@ -15,7 +15,7 @@ import ( ) // Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement { +func (statement *Statement) Join(joinOP string, tablename interface{}, condition interface{}, args ...interface{}) *Statement { var buf strings.Builder if len(statement.JoinStr) > 0 { fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) @@ -23,6 +23,23 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition fmt.Fprintf(&buf, "%v JOIN ", joinOP) } + condStr := "" + condArgs := []interface{}{} + switch condTp := condition.(type) { + case string: + condStr = condTp + case builder.Cond: + var err error + condStr, condArgs, err = builder.ToSQL(condTp) + if err != nil { + statement.LastError = err + return statement + } + default: + statement.LastError = fmt.Errorf("unsupported join condition type: %v", condTp) + return statement + } + switch tp := tablename.(type) { case builder.Builder: subSQL, subQueryArgs, err := tp.ToSQL() @@ -35,8 +52,8 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) aliasName = schemas.CommonQuoter.Trim(aliasName) - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) - statement.joinArgs = append(statement.joinArgs, subQueryArgs...) + fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condStr)) + statement.joinArgs = append(append(statement.joinArgs, subQueryArgs...), condArgs...) case *builder.Builder: subSQL, subQueryArgs, err := tp.ToSQL() if err != nil { @@ -48,8 +65,8 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) aliasName = schemas.CommonQuoter.Trim(aliasName) - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) - statement.joinArgs = append(statement.joinArgs, subQueryArgs...) + fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condStr)) + statement.joinArgs = append(append(statement.joinArgs, subQueryArgs...), condArgs...) default: tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true) if !utils.IsSubQuery(tbName) { @@ -59,7 +76,8 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition } else { tbName = statement.ReplaceQuote(tbName) } - fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condition)) + fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condStr)) + statement.joinArgs = append(statement.joinArgs, condArgs...) } statement.JoinStr = buf.String() diff --git a/session.go b/session.go index 854a125a..e1a16e5b 100644 --- a/session.go +++ b/session.go @@ -330,7 +330,7 @@ func (session *Session) NoCache() *Session { } // Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (session *Session) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session { +func (session *Session) Join(joinOperator string, tablename interface{}, condition interface{}, args ...interface{}) *Session { session.statement.Join(joinOperator, tablename, condition, args...) return session } From 71270edfcc768f6344cd4b408f7a27c071197d85 Mon Sep 17 00:00:00 2001 From: Dmitry Narizhnykh Date: Mon, 12 Dec 2022 18:35:40 +0800 Subject: [PATCH 070/116] add delimiters between COMMENT ON COLUMN... for Postgres (#2156) Fix bug: CreateTableSQL func generates invalid SQL without ";" delimiters. Reviewed-on: https://gitea.com/xorm/xorm/pulls/2156 Reviewed-by: Lunny Xiao Co-authored-by: Dmitry Narizhnykh Co-committed-by: Dmitry Narizhnykh --- dialects/postgres.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dialects/postgres.go b/dialects/postgres.go index f9de5859..5efe54f4 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -1343,14 +1343,14 @@ func (db *postgres) CreateTableSQL(ctx context.Context, queryer core.Queryer, ta commentSQL := "; " if table.Comment != "" { // support schema.table -> "schema"."table" - commentSQL += fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", quoter.Quote(tableName), table.Comment) + commentSQL += fmt.Sprintf("COMMENT ON TABLE %s IS '%s'; ", quoter.Quote(tableName), table.Comment) } for _, colName := range table.ColumnsSeq() { col := table.GetColumn(colName) if len(col.Comment) > 0 { - commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'; ", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) } } From 5fafa00043917e1253ebac95b5de29e2f08a569f Mon Sep 17 00:00:00 2001 From: tamalsaha Date: Mon, 9 Jan 2023 13:19:29 +0800 Subject: [PATCH 071/116] fix: Correctly parse jsonb column tag (#2206) Signed-off-by: Tamal Saha Co-authored-by: Tamal Saha Reviewed-on: https://gitea.com/xorm/xorm/pulls/2206 Reviewed-by: Lunny Xiao Co-authored-by: tamalsaha Co-committed-by: tamalsaha --- tags/parser_test.go | 23 +++++++++++++++++++++++ tags/tag.go | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/tags/parser_test.go b/tags/parser_test.go index 83c81a1e..434cfc07 100644 --- a/tags/parser_test.go +++ b/tags/parser_test.go @@ -557,6 +557,29 @@ func TestParseWithJSON(t *testing.T) { assert.True(t, table.Columns()[0].IsJSON) } +func TestParseWithJSONB(t *testing.T) { + parser := NewParser( + "db", + dialects.QueryDialect("postgres"), + names.GonicMapper{ + "JSONB": true, + }, + names.SnakeMapper{}, + caches.NewManager(), + ) + + type StructWithJSONB struct { + Default1 []string `db:"jsonb"` + } + + table, err := parser.Parse(reflect.ValueOf(new(StructWithJSONB))) + assert.NoError(t, err) + assert.EqualValues(t, "struct_with_jsonb", table.Name) + assert.EqualValues(t, 1, len(table.Columns())) + assert.EqualValues(t, "default1", table.Columns()[0].Name) + assert.True(t, table.Columns()[0].IsJSON) +} + func TestParseWithSQLType(t *testing.T) { parser := NewParser( "db", diff --git a/tags/tag.go b/tags/tag.go index 55f5f4cf..41d525e1 100644 --- a/tags/tag.go +++ b/tags/tag.go @@ -285,7 +285,7 @@ func CommentTagHandler(ctx *Context) error { // SQLTypeTagHandler describes SQL Type tag handler func SQLTypeTagHandler(ctx *Context) error { ctx.col.SQLType = schemas.SQLType{Name: ctx.tagUname} - if ctx.tagUname == "JSON" { + if ctx.tagUname == "JSON" || ctx.tagUname == "JSONB" { ctx.col.IsJSON = true } if len(ctx.params) == 0 { From 7dc2a188761ac198bb424b98aa1fc22892798684 Mon Sep 17 00:00:00 2001 From: datbeohbbh Date: Fri, 3 Feb 2023 17:24:16 +0800 Subject: [PATCH 072/116] Bug fix: `Rows` must be closed after used (#2214) Issue: [#2213](https://gitea.com/xorm/xorm/issues/2213#issue-132724) Co-authored-by: datbeohbbh Reviewed-on: https://gitea.com/xorm/xorm/pulls/2214 Reviewed-by: Lunny Xiao Co-authored-by: datbeohbbh Co-committed-by: datbeohbbh --- engine.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine.go b/engine.go index a42519ee..f10e30d3 100644 --- a/engine.go +++ b/engine.go @@ -815,6 +815,9 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w return err } } + // !datbeohbbh! if no error, manually close + rows.Close() + sess.Close() } return nil } From 0c9963c6379477764ab4adbab74195f92d3b89dc Mon Sep 17 00:00:00 2001 From: jamlacey Date: Sat, 4 Feb 2023 21:24:29 +0800 Subject: [PATCH 073/116] Add support for go-ora driver (#2215) Co-authored-by: James Lacey Reviewed-on: https://gitea.com/xorm/xorm/pulls/2215 Reviewed-by: Lunny Xiao Co-authored-by: jamlacey Co-committed-by: jamlacey --- README.md | 1 + dialects/dialect.go | 1 + dialects/oracle.go | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/README.md b/README.md index ccf49348..f30449a1 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Drivers for Go's sql package which currently support database/sql includes: * Oracle - [github.com/godror/godror](https://github.com/godror/godror) (experiment) - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) + - [github.com/sijms/go-ora](https://github.com/sijms/go-ora) (experiment) ## Installation diff --git a/dialects/dialect.go b/dialects/dialect.go index 555d96c6..70d599e6 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -290,6 +290,7 @@ func regDrvsNDialects() bool { "sqlite": {"sqlite3", func() Driver { return &sqlite3Driver{} }, func() Dialect { return &sqlite3{} }}, "oci8": {"oracle", func() Driver { return &oci8Driver{} }, func() Dialect { return &oracle{} }}, "godror": {"oracle", func() Driver { return &godrorDriver{} }, func() Dialect { return &oracle{} }}, + "oracle": {"oracle", func() Driver { return &oracleDriver{} }, func() Dialect { return &oracle{} }}, } for driverName, v := range providedDrvsNDialects { diff --git a/dialects/oracle.go b/dialects/oracle.go index ce91cd5d..72c26ce2 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -939,3 +939,7 @@ func (o *oci8Driver) Parse(driverName, dataSourceName string) (*URI, error) { } return db, nil } + +type oracleDriver struct { + godrorDriver +} From 52855dae32d7896bd0a12007d153168b77bb52db Mon Sep 17 00:00:00 2001 From: datbeohbbh Date: Sun, 12 Feb 2023 11:16:53 +0800 Subject: [PATCH 074/116] update go version to v1.17 in .drone.yml (#2219) issue: #2218 Co-authored-by: datbeohbbh Reviewed-on: https://gitea.com/xorm/xorm/pulls/2219 Reviewed-by: Lunny Xiao Co-authored-by: datbeohbbh Co-committed-by: datbeohbbh --- .drone.yml | 32 ++-- doc.go | 251 ++++++++++++++-------------- integrations/engine_dm_test.go | 1 + integrations/session_insert_test.go | 3 +- internal/json/gojson.go | 1 + internal/json/jsoniter.go | 1 + schemas/quote.go | 23 +-- session_update.go | 7 +- 8 files changed, 162 insertions(+), 157 deletions(-) diff --git a/.drone.yml b/.drone.yml index 210572b0..2bad4b5a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -11,7 +11,7 @@ trigger: - refs/pull/*/head steps: - name: test-vet - image: golang:1.15 + image: golang:1.17 pull: always volumes: - name: cache @@ -19,7 +19,7 @@ steps: commands: - make vet - name: test-sqlite3 - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -31,7 +31,7 @@ steps: - make test-sqlite3 - TEST_CACHE_ENABLE=true make test-sqlite3 - name: test-sqlite - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -41,7 +41,7 @@ steps: - make test-sqlite - TEST_QUOTE_POLICY=reserved make test-sqlite - name: test-mysql - image: golang:1.15 + image: golang:1.17 pull: never volumes: - name: cache @@ -58,7 +58,7 @@ steps: - TEST_CACHE_ENABLE=true make test-mysql - name: test-mysql-utf8mb4 - image: golang:1.15 + image: golang:1.17 pull: never volumes: - name: cache @@ -98,7 +98,7 @@ trigger: - refs/pull/*/head steps: - name: test-mysql8 - image: golang:1.15 + image: golang:1.17 pull: never volumes: - name: cache @@ -136,7 +136,7 @@ trigger: - refs/pull/*/head steps: - name: test-mariadb - image: golang:1.15 + image: golang:1.17 pull: never volumes: - name: cache @@ -175,7 +175,7 @@ trigger: steps: - name: test-postgres pull: never - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -190,7 +190,7 @@ steps: - name: test-postgres-schema pull: never - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -207,7 +207,7 @@ steps: - name: test-pgx pull: never - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -225,7 +225,7 @@ steps: - name: test-pgx-schema pull: never - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -267,7 +267,7 @@ trigger: steps: - name: test-mssql pull: never - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -306,7 +306,7 @@ trigger: steps: - name: test-tidb pull: never - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -339,7 +339,7 @@ trigger: steps: - name: test-cockroach pull: never - image: golang:1.15 + image: golang:1.17 volumes: - name: cache path: /go/pkg/mod @@ -375,7 +375,7 @@ services: # steps: # - name: test-dameng # pull: never -# image: golang:1.15 +# image: golang:1.17 # volumes: # - name: cache # path: /go/pkg/mod @@ -416,7 +416,7 @@ trigger: - refs/pull/*/head steps: - name: merge_coverage - image: golang:1.15 + image: golang:1.17 commands: - make coverage diff --git a/doc.go b/doc.go index a1565806..f88f5371 100644 --- a/doc.go +++ b/doc.go @@ -3,247 +3,246 @@ // license that can be found in the LICENSE file. /* - Package xorm is a simple and powerful ORM for Go. -Installation +# Installation Make sure you have installed Go 1.11+ and then: - go get xorm.io/xorm + go get xorm.io/xorm -Create Engine +# Create Engine Firstly, we should create an engine for a database - engine, err := xorm.NewEngine(driverName, dataSourceName) + engine, err := xorm.NewEngine(driverName, dataSourceName) Method NewEngine's parameters are the same as sql.Open which depend drivers' implementation. Generally, one engine for an application is enough. You can define it as a package variable. -Raw Methods +# Raw Methods XORM supports raw SQL execution: 1. query with a SQL string, the returned results is []map[string][]byte - results, err := engine.Query("select * from user") + results, err := engine.Query("select * from user") 2. query with a SQL string, the returned results is []map[string]string - results, err := engine.QueryString("select * from user") + results, err := engine.QueryString("select * from user") 3. query with a SQL string, the returned results is []map[string]interface{} - results, err := engine.QueryInterface("select * from user") + results, err := engine.QueryInterface("select * from user") 4. execute with a SQL string, the returned results - affected, err := engine.Exec("update user set .... where ...") + affected, err := engine.Exec("update user set .... where ...") -ORM Methods +# ORM Methods There are 8 major ORM methods and many helpful methods to use to operate database. 1. Insert one or multiple records to database - affected, err := engine.Insert(&struct) - // INSERT INTO struct () values () - affected, err := engine.Insert(&struct1, &struct2) - // INSERT INTO struct1 () values () - // INSERT INTO struct2 () values () - affected, err := engine.Insert(&sliceOfStruct) - // INSERT INTO struct () values (),(),() - affected, err := engine.Insert(&struct1, &sliceOfStruct2) - // INSERT INTO struct1 () values () - // INSERT INTO struct2 () values (),(),() + affected, err := engine.Insert(&struct) + // INSERT INTO struct () values () + affected, err := engine.Insert(&struct1, &struct2) + // INSERT INTO struct1 () values () + // INSERT INTO struct2 () values () + affected, err := engine.Insert(&sliceOfStruct) + // INSERT INTO struct () values (),(),() + affected, err := engine.Insert(&struct1, &sliceOfStruct2) + // INSERT INTO struct1 () values () + // INSERT INTO struct2 () values (),(),() 2. Query one record or one variable from database - has, err := engine.Get(&user) - // SELECT * FROM user LIMIT 1 + has, err := engine.Get(&user) + // SELECT * FROM user LIMIT 1 - var id int64 - has, err := engine.Table("user").Where("name = ?", name).Get(&id) - // SELECT id FROM user WHERE name = ? LIMIT 1 + var id int64 + has, err := engine.Table("user").Where("name = ?", name).Get(&id) + // SELECT id FROM user WHERE name = ? LIMIT 1 - var id int64 - var name string - has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) - // SELECT id, name FROM user LIMIT 1 + var id int64 + var name string + has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) + // SELECT id, name FROM user LIMIT 1 3. Query multiple records from database - var sliceOfStructs []Struct - err := engine.Find(&sliceOfStructs) - // SELECT * FROM user + var sliceOfStructs []Struct + err := engine.Find(&sliceOfStructs) + // SELECT * FROM user - var mapOfStructs = make(map[int64]Struct) - err := engine.Find(&mapOfStructs) - // SELECT * FROM user + var mapOfStructs = make(map[int64]Struct) + err := engine.Find(&mapOfStructs) + // SELECT * FROM user - var int64s []int64 - err := engine.Table("user").Cols("id").Find(&int64s) - // SELECT id FROM user + var int64s []int64 + err := engine.Table("user").Cols("id").Find(&int64s) + // SELECT id FROM user 4. Query multiple records and record by record handle, there two methods, one is Iterate, another is Rows - err := engine.Iterate(new(User), func(i int, bean interface{}) error { - // do something - }) - // SELECT * FROM user + err := engine.Iterate(new(User), func(i int, bean interface{}) error { + // do something + }) + // SELECT * FROM user - rows, err := engine.Rows(...) - // SELECT * FROM user - defer rows.Close() - bean := new(Struct) - for rows.Next() { - err = rows.Scan(bean) - } + rows, err := engine.Rows(...) + // SELECT * FROM user + defer rows.Close() + bean := new(Struct) + for rows.Next() { + err = rows.Scan(bean) + } or - rows, err := engine.Cols("name", "age").Rows(...) - // SELECT * FROM user - defer rows.Close() - for rows.Next() { - var name string - var age int - err = rows.Scan(&name, &age) - } + rows, err := engine.Cols("name", "age").Rows(...) + // SELECT * FROM user + defer rows.Close() + for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) + } 5. Update one or more records - affected, err := engine.ID(...).Update(&user) - // UPDATE user SET ... + affected, err := engine.ID(...).Update(&user) + // UPDATE user SET ... 6. Delete one or more records, Delete MUST has condition - affected, err := engine.Where(...).Delete(&user) - // DELETE FROM user Where ... + affected, err := engine.Where(...).Delete(&user) + // DELETE FROM user Where ... 7. Count records - counts, err := engine.Count(&user) - // SELECT count(*) AS total FROM user + counts, err := engine.Count(&user) + // SELECT count(*) AS total FROM user - counts, err := engine.SQL("select count(*) FROM user").Count() - // select count(*) FROM user + counts, err := engine.SQL("select count(*) FROM user").Count() + // select count(*) FROM user 8. Sum records - sumFloat64, err := engine.Sum(&user, "id") - // SELECT sum(id) from user + sumFloat64, err := engine.Sum(&user, "id") + // SELECT sum(id) from user - sumFloat64s, err := engine.Sums(&user, "id1", "id2") - // SELECT sum(id1), sum(id2) from user + sumFloat64s, err := engine.Sums(&user, "id1", "id2") + // SELECT sum(id1), sum(id2) from user - sumInt64s, err := engine.SumsInt(&user, "id1", "id2") - // SELECT sum(id1), sum(id2) from user + sumInt64s, err := engine.SumsInt(&user, "id1", "id2") + // SELECT sum(id1), sum(id2) from user -Conditions +# Conditions The above 8 methods could use with condition methods chainable. Notice: the above 8 methods should be the last chainable method. 1. ID, In - engine.ID(1).Get(&user) // for single primary key - // SELECT * FROM user WHERE id = 1 - engine.ID(schemas.PK{1, 2}).Get(&user) // for composite primary keys - // SELECT * FROM user WHERE id1 = 1 AND id2 = 2 - engine.In("id", 1, 2, 3).Find(&users) - // SELECT * FROM user WHERE id IN (1, 2, 3) - engine.In("id", []int{1, 2, 3}).Find(&users) - // SELECT * FROM user WHERE id IN (1, 2, 3) + engine.ID(1).Get(&user) // for single primary key + // SELECT * FROM user WHERE id = 1 + engine.ID(schemas.PK{1, 2}).Get(&user) // for composite primary keys + // SELECT * FROM user WHERE id1 = 1 AND id2 = 2 + engine.In("id", 1, 2, 3).Find(&users) + // SELECT * FROM user WHERE id IN (1, 2, 3) + engine.In("id", []int{1, 2, 3}).Find(&users) + // SELECT * FROM user WHERE id IN (1, 2, 3) 2. Where, And, Or - engine.Where().And().Or().Find() - // SELECT * FROM user WHERE (.. AND ..) OR ... + engine.Where().And().Or().Find() + // SELECT * FROM user WHERE (.. AND ..) OR ... 3. OrderBy, Asc, Desc - engine.Asc().Desc().Find() - // SELECT * FROM user ORDER BY .. ASC, .. DESC - engine.OrderBy().Find() - // SELECT * FROM user ORDER BY .. + engine.Asc().Desc().Find() + // SELECT * FROM user ORDER BY .. ASC, .. DESC + engine.OrderBy().Find() + // SELECT * FROM user ORDER BY .. 4. Limit, Top - engine.Limit().Find() - // SELECT * FROM user LIMIT .. OFFSET .. - engine.Top(5).Find() - // SELECT TOP 5 * FROM user // for mssql - // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases + engine.Limit().Find() + // SELECT * FROM user LIMIT .. OFFSET .. + engine.Top(5).Find() + // SELECT TOP 5 * FROM user // for mssql + // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases 5. SQL, let you custom SQL - var users []User - engine.SQL("select * from user").Find(&users) + var users []User + engine.SQL("select * from user").Find(&users) 6. Cols, Omit, Distinct - var users []*User - engine.Cols("col1, col2").Find(&users) - // SELECT col1, col2 FROM user - engine.Cols("col1", "col2").Where().Update(user) - // UPDATE user set col1 = ?, col2 = ? Where ... - engine.Omit("col1").Find(&users) - // SELECT col2, col3 FROM user - engine.Omit("col1").Insert(&user) - // INSERT INTO table (non-col1) VALUES () - engine.Distinct("col1").Find(&users) - // SELECT DISTINCT col1 FROM user + var users []*User + engine.Cols("col1, col2").Find(&users) + // SELECT col1, col2 FROM user + engine.Cols("col1", "col2").Where().Update(user) + // UPDATE user set col1 = ?, col2 = ? Where ... + engine.Omit("col1").Find(&users) + // SELECT col2, col3 FROM user + engine.Omit("col1").Insert(&user) + // INSERT INTO table (non-col1) VALUES () + engine.Distinct("col1").Find(&users) + // SELECT DISTINCT col1 FROM user 7. Join, GroupBy, Having - engine.GroupBy("name").Having("name='xlw'").Find(&users) - //SELECT * FROM user GROUP BY name HAVING name='xlw' - engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users) - //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id + engine.GroupBy("name").Having("name='xlw'").Find(&users) + //SELECT * FROM user GROUP BY name HAVING name='xlw' + engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users) + //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id -Builder +# Builder xorm could work with xorm.io/builder directly. 1. With Where - var cond = builder.Eq{"a":1, "b":2} - engine.Where(cond).Find(&users) + var cond = builder.Eq{"a":1, "b":2} + engine.Where(cond).Find(&users) 2. With In - var subQuery = builder.Select("name").From("group") - engine.In("group_name", subQuery).Find(&users) + var subQuery = builder.Select("name").From("group") + engine.In("group_name", subQuery).Find(&users) 3. With Join - var subQuery = builder.Select("name").From("group") - engine.Join("INNER", subQuery, "group.id = user.group_id").Find(&users) + var subQuery = builder.Select("name").From("group") + engine.Join("INNER", subQuery, "group.id = user.group_id").Find(&users) 4. With SetExprs - var subQuery = builder.Select("name").From("group") - engine.ID(1).SetExprs("name", subQuery).Update(new(User)) + var subQuery = builder.Select("name").From("group") + engine.ID(1).SetExprs("name", subQuery).Update(new(User)) 5. With SQL - var query = builder.Select("name").From("group") - results, err := engine.SQL(query).Find(&groups) + var query = builder.Select("name").From("group") + results, err := engine.SQL(query).Find(&groups) 6. With Query - var query = builder.Select("name").From("group") - results, err := engine.Query(query) - results, err := engine.QueryString(query) - results, err := engine.QueryInterface(query) + var query = builder.Select("name").From("group") + results, err := engine.Query(query) + results, err := engine.QueryString(query) + results, err := engine.QueryInterface(query) 7. With Exec - var query = builder.Insert("a, b").Into("table1").Select("b, c").From("table2") - results, err := engine.Exec(query) + var query = builder.Insert("a, b").Into("table1").Select("b, c").From("table2") + results, err := engine.Exec(query) More usage, please visit http://xorm.io/docs */ diff --git a/integrations/engine_dm_test.go b/integrations/engine_dm_test.go index 6c2f6103..3b195ef8 100644 --- a/integrations/engine_dm_test.go +++ b/integrations/engine_dm_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build dm // +build dm package integrations diff --git a/integrations/session_insert_test.go b/integrations/session_insert_test.go index 2495c1df..084deb38 100644 --- a/integrations/session_insert_test.go +++ b/integrations/session_insert_test.go @@ -744,7 +744,8 @@ func TestInsertMap(t *testing.T) { assert.EqualValues(t, "lunny", ims[3].Name) } -/*INSERT INTO `issue` (`repo_id`, `poster_id`, ... ,`name`, `content`, ... ,`index`) +/* +INSERT INTO `issue` (`repo_id`, `poster_id`, ... ,`name`, `content`, ... ,`index`) SELECT $1, $2, ..., $14, $15, ..., MAX(`index`) + 1 FROM `issue` WHERE `repo_id` = $1; */ func TestInsertWhere(t *testing.T) { diff --git a/internal/json/gojson.go b/internal/json/gojson.go index 4f1448e7..9bfa5c29 100644 --- a/internal/json/gojson.go +++ b/internal/json/gojson.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gojson // +build gojson package json diff --git a/internal/json/jsoniter.go b/internal/json/jsoniter.go index cfe7a19e..be93ac4e 100644 --- a/internal/json/jsoniter.go +++ b/internal/json/jsoniter.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build jsoniter // +build jsoniter package json diff --git a/schemas/quote.go b/schemas/quote.go index 4cab30fe..6df7bf0b 100644 --- a/schemas/quote.go +++ b/schemas/quote.go @@ -163,17 +163,18 @@ func (q Quoter) quoteWordTo(buf *strings.Builder, word string) error { } // QuoteTo quotes the table or column names. i.e. if the quotes are [ and ] -// name -> [name] -// `name` -> [name] -// [name] -> [name] -// schema.name -> [schema].[name] -// `schema`.`name` -> [schema].[name] -// `schema`.name -> [schema].[name] -// schema.`name` -> [schema].[name] -// [schema].name -> [schema].[name] -// schema.[name] -> [schema].[name] -// name AS a -> [name] AS a -// schema.name AS a -> [schema].[name] AS a +// +// name -> [name] +// `name` -> [name] +// [name] -> [name] +// schema.name -> [schema].[name] +// `schema`.`name` -> [schema].[name] +// `schema`.name -> [schema].[name] +// schema.`name` -> [schema].[name] +// [schema].name -> [schema].[name] +// schema.[name] -> [schema].[name] +// name AS a -> [name] AS a +// schema.name AS a -> [schema].[name] AS a func (q Quoter) QuoteTo(buf *strings.Builder, value string) error { var i int for i < len(value) { diff --git a/session_update.go b/session_update.go index 76f311d6..e7104710 100644 --- a/session_update.go +++ b/session_update.go @@ -145,9 +145,10 @@ func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr stri // Update records, bean's non-empty fields are updated contents, // condiBean' non-empty filds are conditions // CAUTION: -// 1.bool will defaultly be updated content nor conditions -// You should call UseBool if you have bool to use. -// 2.float32 & float64 may be not inexact as conditions +// +// 1.bool will defaultly be updated content nor conditions +// You should call UseBool if you have bool to use. +// 2.float32 & float64 may be not inexact as conditions func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) { if session.isAutoClose { defer session.Close() From 056cecc97e9ef3f5fd216944a495c41aa98f4e4a Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 20 Feb 2023 07:17:35 +0800 Subject: [PATCH 075/116] Add `Truncate` method (#2220) This PR adds a `Truncate` method which allows to delete all existing rows in a table. The current `Delete` implementation enforces conditions to prevent accidental data deletion. Co-authored-by: KN4CK3R Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2220 Reviewed-by: Lunny Xiao Co-authored-by: KN4CK3R Co-committed-by: KN4CK3R --- engine.go | 9 +++++++++ integrations/session_delete_test.go | 27 ++++++++++++++++++++++++++- interface.go | 1 + session_delete.go | 13 ++++++++++++- 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/engine.go b/engine.go index f10e30d3..fb19176e 100644 --- a/engine.go +++ b/engine.go @@ -1233,12 +1233,21 @@ func (engine *Engine) Update(bean interface{}, condiBeans ...interface{}) (int64 } // Delete records, bean's non-empty fields are conditions +// At least one condition must be set. func (engine *Engine) Delete(beans ...interface{}) (int64, error) { session := engine.NewSession() defer session.Close() return session.Delete(beans...) } +// Truncate records, bean's non-empty fields are conditions +// In contrast to Delete this method allows deletes without conditions. +func (engine *Engine) Truncate(beans ...interface{}) (int64, error) { + session := engine.NewSession() + defer session.Close() + return session.Truncate(beans...) +} + // Get retrieve one record from table, bean's non-empty fields // are conditions func (engine *Engine) Get(beans ...interface{}) (bool, error) { diff --git a/integrations/session_delete_test.go b/integrations/session_delete_test.go index b4e40edb..680c3215 100644 --- a/integrations/session_delete_test.go +++ b/integrations/session_delete_test.go @@ -208,7 +208,7 @@ func TestUnscopeDelete(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var nowUnix = time.Now().Unix() + nowUnix := time.Now().Unix() var s UnscopeDeleteStruct cnt, err = testEngine.ID(1).Delete(&s) assert.NoError(t, err) @@ -266,3 +266,28 @@ func TestDelete2(t *testing.T) { assert.NoError(t, err) assert.False(t, has) } + +func TestTruncate(t *testing.T) { + assert.NoError(t, PrepareEngine()) + + type TruncateUser struct { + Uid int64 `xorm:"id pk not null autoincr"` + } + + assert.NoError(t, testEngine.Sync(new(TruncateUser))) + + cnt, err := testEngine.Insert(&TruncateUser{}) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + _, err = testEngine.Delete(&TruncateUser{}) + assert.Error(t, err) + + _, err = testEngine.Truncate(&TruncateUser{}) + assert.NoError(t, err) + + user2 := TruncateUser{} + has, err := testEngine.ID(1).Get(&user2) + assert.NoError(t, err) + assert.False(t, has) +} diff --git a/interface.go b/interface.go index 6ad0577a..d10abe9e 100644 --- a/interface.go +++ b/interface.go @@ -31,6 +31,7 @@ type Interface interface { Decr(column string, arg ...interface{}) *Session Desc(...string) *Session Delete(...interface{}) (int64, error) + Truncate(...interface{}) (int64, error) Distinct(columns ...string) *Session DropIndexes(bean interface{}) error Exec(sqlOrArgs ...interface{}) (sql.Result, error) diff --git a/session_delete.go b/session_delete.go index 322d5a44..d36b9e52 100644 --- a/session_delete.go +++ b/session_delete.go @@ -91,7 +91,18 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri } // Delete records, bean's non-empty fields are conditions +// At least one condition must be set. func (session *Session) Delete(beans ...interface{}) (int64, error) { + return session.delete(beans, true) +} + +// Truncate records, bean's non-empty fields are conditions +// In contrast to Delete this method allows deletes without conditions. +func (session *Session) Truncate(beans ...interface{}) (int64, error) { + return session.delete(beans, false) +} + +func (session *Session) delete(beans []interface{}, mustHaveConditions bool) (int64, error) { if session.isAutoClose { defer session.Close() } @@ -127,7 +138,7 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) { } pLimitN := session.statement.LimitN - if condWriter.Len() == 0 && (pLimitN == nil || *pLimitN == 0) { + if mustHaveConditions && condWriter.Len() == 0 && (pLimitN == nil || *pLimitN == 0) { return 0, ErrNeedDeletedCond } From 914f2db9eaf24184a34499e8ba8a705202a49c65 Mon Sep 17 00:00:00 2001 From: fanshengshuai Date: Tue, 28 Feb 2023 23:42:42 +0800 Subject: [PATCH 076/116] =?UTF-8?q?mysql=E5=AD=97=E6=AE=B5=E4=B8=BAUNSIGNE?= =?UTF-8?q?D=E6=97=B6=EF=BC=8C=E4=BC=9A=E5=AF=BC=E8=87=B4=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E7=B1=BB=E5=9E=8B=E6=97=A0=E6=B3=95=E8=AF=86=E5=88=AB?= =?UTF-8?q?=EF=BC=8C=E8=BF=94=E5=9B=9ERawBytes=EF=BC=8CJSON=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E6=98=A0=E5=B0=84=E4=B8=BAString=20(#2225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 如题 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2225 Reviewed-by: Lunny Xiao Co-authored-by: fanshengshuai Co-committed-by: fanshengshuai --- dialects/mysql.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dialects/mysql.go b/dialects/mysql.go index 6ed4a1be..195e1f23 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -738,8 +738,9 @@ func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) { } func (p *mysqlDriver) GenScanResult(colType string) (interface{}, error) { + colType = strings.Replace(colType, "UNSIGNED ", "", -1) switch colType { - case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET": + case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET", "JSON": var s sql.NullString return &s, nil case "BIGINT": From d485abba57930d6eacd607405a84c6862886102f Mon Sep 17 00:00:00 2001 From: datbeohbbh Date: Mon, 6 Mar 2023 18:55:33 +0800 Subject: [PATCH 077/116] add `(*Engine) SetConnMaxIdleTime` (#2229) issue: https://gitea.com/xorm/xorm/issues/2228 Co-authored-by: datbeohbbh Reviewed-on: https://gitea.com/xorm/xorm/pulls/2229 Reviewed-by: Lunny Xiao Co-authored-by: datbeohbbh Co-committed-by: datbeohbbh --- engine.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine.go b/engine.go index fb19176e..389819e7 100644 --- a/engine.go +++ b/engine.go @@ -254,6 +254,11 @@ func (engine *Engine) SetConnMaxLifetime(d time.Duration) { engine.DB().SetConnMaxLifetime(d) } +// SetConnMaxIdleTime sets the maximum amount of time a connection may be idle. +func (engine *Engine) SetConnMaxIdleTime(d time.Duration) { + engine.DB().SetConnMaxIdleTime(d) +} + // SetMaxOpenConns is only available for go 1.2+ func (engine *Engine) SetMaxOpenConns(conns int) { engine.DB().SetMaxOpenConns(conns) From 94fcec7f65f87b4a227f2b45b6947247a6f0208d Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Thu, 6 Apr 2023 21:47:59 +0800 Subject: [PATCH 078/116] parse timestamp with milliseconds in tz format (#2246) This pr fixes a bug caused when a timestmap in TZ format with milliseconds (`2023-04-05T15:50:48.256816Z` or `2023-04-05T15:50:48.256816+08:00`) is being parsed. - The bug is happens in the function `String2Time` in `convert/time.go` - if the timestamp contains milliseconds a layout with the structure `2006-01-02 15:04:05.` is used to parse it - This layout contains an space between the date and the hour, if the timestamp returned by the db contains a `T` the parser fails and an error is returned This pr adds a check for the `T` and milliseconds in the timestamp, if both conditions are present the date is parsed using the RFC3339 layout Added test cases to check timestamps in this format Solves [#2244](https://gitea.com/xorm/xorm/issues/2244) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2246 Reviewed-by: Lunny Xiao Co-authored-by: Alberto Garcia Co-committed-by: Alberto Garcia --- convert/time.go | 7 +++++++ convert/time_test.go | 17 +++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/convert/time.go b/convert/time.go index cc2e0a10..dab6a9a2 100644 --- a/convert/time.go +++ b/convert/time.go @@ -40,6 +40,13 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t } dt = dt.In(convertedLocation) return &dt, nil + } else if len(s) >= 21 && s[10] == 'T' && s[19] == '.' { + dt, err := time.Parse(time.RFC3339, s) + if err != nil { + return nil, err + } + dt = dt.In(convertedLocation) + return &dt, nil } else if len(s) >= 21 && s[19] == '.' { var layout = "2006-01-02 15:04:05." + strings.Repeat("0", len(s)-20) dt, err := time.ParseInLocation(layout, s, originalLocation) diff --git a/convert/time_test.go b/convert/time_test.go index 5ddceb64..4b1c2279 100644 --- a/convert/time_test.go +++ b/convert/time_test.go @@ -16,10 +16,19 @@ func TestString2Time(t *testing.T) { assert.NoError(t, err) var kases = map[string]time.Time{ - "2021-08-10": time.Date(2021, 8, 10, 8, 0, 0, 0, expectedLoc), - "2021-06-06T22:58:20+08:00": time.Date(2021, 6, 6, 22, 58, 20, 0, expectedLoc), - "2021-07-11 10:44:00": time.Date(2021, 7, 11, 18, 44, 0, 0, expectedLoc), - "2021-08-10T10:33:04Z": time.Date(2021, 8, 10, 18, 33, 04, 0, expectedLoc), + "2021-08-10": time.Date(2021, 8, 10, 8, 0, 0, 0, expectedLoc), + "2021-07-11 10:44:00": time.Date(2021, 7, 11, 18, 44, 0, 0, expectedLoc), + "2021-07-11 10:44:00.999": time.Date(2021, 7, 11, 18, 44, 0, 999000000, expectedLoc), + "2021-07-11 10:44:00.999999": time.Date(2021, 7, 11, 18, 44, 0, 999999000, expectedLoc), + "2021-07-11 10:44:00.999999999": time.Date(2021, 7, 11, 18, 44, 0, 999999999, expectedLoc), + "2021-06-06T22:58:20+08:00": time.Date(2021, 6, 6, 22, 58, 20, 0, expectedLoc), + "2021-06-06T22:58:20.999+08:00": time.Date(2021, 6, 6, 22, 58, 20, 999000000, expectedLoc), + "2021-06-06T22:58:20.999999+08:00": time.Date(2021, 6, 6, 22, 58, 20, 999999000, expectedLoc), + "2021-06-06T22:58:20.999999999+08:00": time.Date(2021, 6, 6, 22, 58, 20, 999999999, expectedLoc), + "2021-08-10T10:33:04Z": time.Date(2021, 8, 10, 18, 33, 04, 0, expectedLoc), + "2021-08-10T10:33:04.999Z": time.Date(2021, 8, 10, 18, 33, 04, 999000000, expectedLoc), + "2021-08-10T10:33:04.999999Z": time.Date(2021, 8, 10, 18, 33, 04, 999999000, expectedLoc), + "2021-08-10T10:33:04.999999999Z": time.Date(2021, 8, 10, 18, 33, 04, 999999999, expectedLoc), } for layout, tm := range kases { t.Run(layout, func(t *testing.T) { From 23be940bad6a1ca680838b35b6cc9031728e1710 Mon Sep 17 00:00:00 2001 From: Abei1uo <2676567028@qq.com> Date: Fri, 12 May 2023 14:58:38 +0800 Subject: [PATCH 079/116] Fix session insert interface slice commit panic(#2259) (#2260) Fix session insert interface slice commit panic(#2259) -- TestInsertMulti2InterfaceTransaction is the test case Reviewed-on: https://gitea.com/xorm/xorm/pulls/2260 Co-authored-by: Abei1uo <2676567028@qq.com> Co-committed-by: Abei1uo <2676567028@qq.com> --- integrations/session_tx_test.go | 33 +++++++++++++++++++++++++++++++++ schemas/column.go | 2 ++ 2 files changed, 35 insertions(+) diff --git a/integrations/session_tx_test.go b/integrations/session_tx_test.go index 8d6519d0..890e755d 100644 --- a/integrations/session_tx_test.go +++ b/integrations/session_tx_test.go @@ -185,3 +185,36 @@ func TestMultipleTransaction(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 0, len(ms)) } + +func TestInsertMulti2InterfaceTransaction(t *testing.T) { + + type Multi2InterfaceTransaction struct { + ID uint64 `xorm:"id pk autoincr"` + Name string + Alias string + CreateTime time.Time `xorm:"created"` + UpdateTime time.Time `xorm:"updated"` + } + assert.NoError(t, PrepareEngine()) + assertSync(t, new(Multi2InterfaceTransaction)) + session := testEngine.NewSession() + defer session.Close() + err := session.Begin() + assert.NoError(t, err) + + users := []interface{}{ + &Multi2InterfaceTransaction{Name: "a", Alias: "A"}, + &Multi2InterfaceTransaction{Name: "b", Alias: "B"}, + &Multi2InterfaceTransaction{Name: "c", Alias: "C"}, + &Multi2InterfaceTransaction{Name: "d", Alias: "D"}, + } + cnt, err := session.Insert(&users) + + assert.NoError(t, err) + assert.EqualValues(t, len(users), cnt) + + assert.NotPanics(t, func() { + err = session.Commit() + assert.NoError(t, err) + }) +} diff --git a/schemas/column.go b/schemas/column.go index 001769cd..5a579e92 100644 --- a/schemas/column.go +++ b/schemas/column.go @@ -89,6 +89,8 @@ func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { v.Set(reflect.New(v.Type().Elem())) } v = v.Elem() + } else if v.Kind() == reflect.Interface { + v = reflect.Indirect(v.Elem()) } v = v.FieldByIndex([]int{i}) } From e6907e9a62cb107d43724866c3b3006e408d74e5 Mon Sep 17 00:00:00 2001 From: Martin Viggiano Date: Wed, 17 May 2023 09:04:00 +0800 Subject: [PATCH 080/116] oracle: Fix quotes on DropTableSQL function, add IsSequenceExist function. (#2265) - Fixed quotes on drop table SQL statement. - Implemented IsSequenceExists function for oracle dialect. Reviewed-on: https://gitea.com/xorm/xorm/pulls/2265 Reviewed-by: Lunny Xiao Co-authored-by: Martin Viggiano Co-committed-by: Martin Viggiano --- dialects/oracle.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dialects/oracle.go b/dialects/oracle.go index 72c26ce2..b0c5c38f 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -609,7 +609,7 @@ func (db *oracle) IsReserved(name string) bool { } func (db *oracle) DropTableSQL(tableName string) (string, bool) { - return fmt.Sprintf("DROP TABLE `%s`", tableName), false + return fmt.Sprintf("DROP TABLE \"%s\"", tableName), false } func (db *oracle) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { @@ -645,6 +645,10 @@ func (db *oracle) CreateTableSQL(ctx context.Context, queryer core.Queryer, tabl return sql, false, nil } +func (db *oracle) IsSequenceExist(ctx context.Context, queryer core.Queryer, seqName string) (bool, error) { + return db.HasRecords(queryer, ctx, `SELECT sequence_name FROM user_sequences WHERE sequence_name = :1`, seqName) +} + func (db *oracle) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: From 190384b4cdb9fe0d2c798ef98321aaaa442f4426 Mon Sep 17 00:00:00 2001 From: brookechen Date: Wed, 17 May 2023 15:20:40 +0800 Subject: [PATCH 081/116] =?UTF-8?q?AutoIncrement=E5=88=97=E5=B8=A6ID?= =?UTF-8?q?=E6=8F=92=E5=85=A5=E6=95=B0=E6=8D=AE=E6=97=B6=E6=B2=A1=E6=9C=89?= =?UTF-8?q?Commit=20(#2264)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 业务场景中,需要预留(1 ~ 100)的ID给系统规则使用。所以会先使用插入将AutoIncrement列的id偏移到一个特定的值(如:100),然后“带ID调用Insert插入系统规则”。 当带ID插入时,由于没有commit,会被rollback掉。 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2264 Reviewed-by: Lunny Xiao Co-authored-by: brookechen Co-committed-by: brookechen --- session_insert.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/session_insert.go b/session_insert.go index fc025613..cfa26d39 100644 --- a/session_insert.go +++ b/session_insert.go @@ -353,15 +353,15 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { if err != nil { return 0, err } - if needCommit { - if err := session.Commit(); err != nil { - return 0, err - } - } - if id == 0 { - return 0, errors.New("insert successfully but not returned id") + } + if needCommit { + if err := session.Commit(); err != nil { + return 0, err } } + if id == 0 { + return 0, errors.New("insert successfully but not returned id") + } defer handleAfterInsertProcessorFunc(bean) From 04d36cfa818d6bbe22ae4607726b74af58562619 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 25 May 2023 18:15:09 +0800 Subject: [PATCH 082/116] Use actions instead of drone (#2258) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2258 --- .drone.yml | 437 ---------------------------- .gitea/workflows/release-tag.yml | 23 ++ .gitea/workflows/test-cockroach.yml | 56 ++++ .gitea/workflows/test-mariadb.yml | 56 ++++ .gitea/workflows/test-mssql.yml | 56 ++++ .gitea/workflows/test-mysql.yml | 56 ++++ .gitea/workflows/test-mysql8.yml | 56 ++++ .gitea/workflows/test-postgres.yml | 79 +++++ .gitea/workflows/test-sqlite.yml | 49 ++++ .gitea/workflows/test-tidb.yml | 52 ++++ 10 files changed, 483 insertions(+), 437 deletions(-) delete mode 100644 .drone.yml create mode 100644 .gitea/workflows/release-tag.yml create mode 100644 .gitea/workflows/test-cockroach.yml create mode 100644 .gitea/workflows/test-mariadb.yml create mode 100644 .gitea/workflows/test-mssql.yml create mode 100644 .gitea/workflows/test-mysql.yml create mode 100644 .gitea/workflows/test-mysql8.yml create mode 100644 .gitea/workflows/test-postgres.yml create mode 100644 .gitea/workflows/test-sqlite.yml create mode 100644 .gitea/workflows/test-tidb.yml diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 2bad4b5a..00000000 --- a/.drone.yml +++ /dev/null @@ -1,437 +0,0 @@ ---- -kind: pipeline -name: test-mysql -environment: - GO111MODULE: "on" - GOPROXY: "https://goproxy.io" - CGO_ENABLED: 1 -trigger: - ref: - - refs/heads/master - - refs/pull/*/head -steps: -- name: test-vet - image: golang:1.17 - pull: always - volumes: - - name: cache - path: /go/pkg/mod - commands: - - make vet -- name: test-sqlite3 - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - depends_on: - - test-vet - commands: - - make fmt-check - - make test - - make test-sqlite3 - - TEST_CACHE_ENABLE=true make test-sqlite3 -- name: test-sqlite - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - depends_on: - - test-vet - commands: - - make test-sqlite - - TEST_QUOTE_POLICY=reserved make test-sqlite -- name: test-mysql - image: golang:1.17 - pull: never - volumes: - - name: cache - path: /go/pkg/mod - depends_on: - - test-vet - environment: - TEST_MYSQL_HOST: mysql - TEST_MYSQL_CHARSET: utf8 - TEST_MYSQL_DBNAME: xorm_test - TEST_MYSQL_USERNAME: root - TEST_MYSQL_PASSWORD: - commands: - - TEST_CACHE_ENABLE=true make test-mysql - -- name: test-mysql-utf8mb4 - image: golang:1.17 - pull: never - volumes: - - name: cache - path: /go/pkg/mod - depends_on: - - test-mysql - environment: - TEST_MYSQL_HOST: mysql - TEST_MYSQL_CHARSET: utf8mb4 - TEST_MYSQL_DBNAME: xorm_test - TEST_MYSQL_USERNAME: root - TEST_MYSQL_PASSWORD: - commands: - - make test-mysql - - TEST_QUOTE_POLICY=reserved make test-mysql-tls - -volumes: -- name: cache - host: - path: /tmp/cache - -services: -- name: mysql - image: mysql:5.7 - environment: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - MYSQL_DATABASE: xorm_test - ---- -kind: pipeline -name: test-mysql8 -depends_on: - - test-mysql -trigger: - ref: - - refs/heads/master - - refs/pull/*/head -steps: -- name: test-mysql8 - image: golang:1.17 - pull: never - volumes: - - name: cache - path: /go/pkg/mod - environment: - TEST_MYSQL_HOST: mysql8 - TEST_MYSQL_CHARSET: utf8mb4 - TEST_MYSQL_DBNAME: xorm_test - TEST_MYSQL_USERNAME: root - TEST_MYSQL_PASSWORD: - commands: - - make test-mysql - - TEST_CACHE_ENABLE=true make test-mysql - -volumes: -- name: cache - host: - path: /tmp/cache - -services: -- name: mysql8 - image: mysql:8.0 - environment: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - MYSQL_DATABASE: xorm_test - ---- -kind: pipeline -name: test-mariadb -depends_on: - - test-mysql8 -trigger: - ref: - - refs/heads/master - - refs/pull/*/head -steps: -- name: test-mariadb - image: golang:1.17 - pull: never - volumes: - - name: cache - path: /go/pkg/mod - environment: - TEST_MYSQL_HOST: mariadb - TEST_MYSQL_CHARSET: utf8mb4 - TEST_MYSQL_DBNAME: xorm_test - TEST_MYSQL_USERNAME: root - TEST_MYSQL_PASSWORD: - commands: - - make test-mysql - - TEST_QUOTE_POLICY=reserved make test-mysql - -volumes: -- name: cache - host: - path: /tmp/cache - -services: -- name: mariadb - image: mariadb:10.4 - environment: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - MYSQL_DATABASE: xorm_test - ---- -kind: pipeline -name: test-postgres -depends_on: - - test-mariadb -trigger: - ref: - - refs/heads/master - - refs/pull/*/head -steps: -- name: test-postgres - pull: never - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - environment: - TEST_PGSQL_HOST: pgsql - TEST_PGSQL_DBNAME: xorm_test - TEST_PGSQL_USERNAME: postgres - TEST_PGSQL_PASSWORD: postgres - commands: - - make test-postgres - - TEST_CACHE_ENABLE=true make test-postgres - -- name: test-postgres-schema - pull: never - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - depends_on: - - test-postgres - environment: - TEST_PGSQL_HOST: pgsql - TEST_PGSQL_SCHEMA: xorm - TEST_PGSQL_DBNAME: xorm_test - TEST_PGSQL_USERNAME: postgres - TEST_PGSQL_PASSWORD: postgres - commands: - - TEST_QUOTE_POLICY=reserved make test-postgres - -- name: test-pgx - pull: never - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - depends_on: - - test-postgres-schema - environment: - TEST_PGSQL_HOST: pgsql - TEST_PGSQL_DBNAME: xorm_test - TEST_PGSQL_USERNAME: postgres - TEST_PGSQL_PASSWORD: postgres - commands: - - make test-pgx - - TEST_CACHE_ENABLE=true make test-pgx - - TEST_QUOTE_POLICY=reserved make test-pgx - -- name: test-pgx-schema - pull: never - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - depends_on: - - test-pgx - environment: - TEST_PGSQL_HOST: pgsql - TEST_PGSQL_SCHEMA: xorm - TEST_PGSQL_DBNAME: xorm_test - TEST_PGSQL_USERNAME: postgres - TEST_PGSQL_PASSWORD: postgres - commands: - - make test-pgx - - TEST_CACHE_ENABLE=true make test-pgx - - TEST_QUOTE_POLICY=reserved make test-pgx - -volumes: -- name: cache - host: - path: /tmp/cache - -services: -- name: pgsql - image: postgres:9.5 - environment: - POSTGRES_DB: xorm_test - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - ---- -kind: pipeline -name: test-mssql -depends_on: - - test-postgres -trigger: - ref: - - refs/heads/master - - refs/pull/*/head -steps: -- name: test-mssql - pull: never - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - environment: - TEST_MSSQL_HOST: mssql - TEST_MSSQL_DBNAME: xorm_test - TEST_MSSQL_USERNAME: sa - TEST_MSSQL_PASSWORD: "yourStrong(!)Password" - commands: - - make test-mssql - - TEST_MSSQL_DEFAULT_VARCHAR=NVARCHAR TEST_MSSQL_DEFAULT_CHAR=NCHAR make test-mssql - -volumes: -- name: cache - host: - path: /tmp/cache - -services: -- name: mssql - pull: always - image: mcr.microsoft.com/mssql/server:latest - environment: - ACCEPT_EULA: Y - SA_PASSWORD: yourStrong(!)Password - MSSQL_PID: Standard - ---- -kind: pipeline -name: test-tidb -depends_on: - - test-mssql -trigger: - ref: - - refs/heads/master - - refs/pull/*/head -steps: -- name: test-tidb - pull: never - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - environment: - TEST_TIDB_HOST: "tidb:4000" - TEST_TIDB_DBNAME: xorm_test - TEST_TIDB_USERNAME: root - TEST_TIDB_PASSWORD: - commands: - - make test-tidb - -volumes: -- name: cache - host: - path: /tmp/cache - -services: -- name: tidb - image: pingcap/tidb:v3.0.3 - ---- -kind: pipeline -name: test-cockroach -depends_on: - - test-tidb -trigger: - ref: - - refs/heads/master - - refs/pull/*/head -steps: -- name: test-cockroach - pull: never - image: golang:1.17 - volumes: - - name: cache - path: /go/pkg/mod - environment: - TEST_COCKROACH_HOST: "cockroach:26257" - TEST_COCKROACH_DBNAME: xorm_test - TEST_COCKROACH_USERNAME: root - TEST_COCKROACH_PASSWORD: - commands: - - sleep 10 - - make test-cockroach - -volumes: -- name: cache - host: - path: /tmp/cache - -services: -- name: cockroach - image: cockroachdb/cockroach:v19.2.4 - commands: - - /cockroach/cockroach start --insecure - -# --- -# kind: pipeline -# name: test-dameng -# depends_on: -# - test-cockroach -# trigger: -# ref: -# - refs/heads/master -# - refs/pull/*/head -# steps: -# - name: test-dameng -# pull: never -# image: golang:1.17 -# volumes: -# - name: cache -# path: /go/pkg/mod -# environment: -# TEST_DAMENG_HOST: "dameng:5236" -# TEST_DAMENG_USERNAME: SYSDBA -# TEST_DAMENG_PASSWORD: SYSDBA -# commands: -# - sleep 30 -# - make test-dameng - -# volumes: -# - name: cache -# host: -# path: /tmp/cache - -# services: -# - name: dameng -# image: lunny/dm:v1.0 -# commands: -# - /bin/bash /startDm.sh - ---- -kind: pipeline -name: merge_coverage -depends_on: - - test-mysql - - test-mysql8 - - test-mariadb - - test-postgres - - test-mssql - - test-tidb - - test-cockroach - #- test-dameng -trigger: - ref: - - refs/heads/master - - refs/pull/*/head -steps: -- name: merge_coverage - image: golang:1.17 - commands: - - make coverage - ---- -kind: pipeline -name: release-tag -trigger: - event: - - tag -steps: -- name: release-tag-gitea - pull: always - image: plugins/gitea-release:latest - settings: - base_url: https://gitea.com - title: '${DRONE_TAG} is released' - api_key: - from_secret: gitea_token \ No newline at end of file diff --git a/.gitea/workflows/release-tag.yml b/.gitea/workflows/release-tag.yml new file mode 100644 index 00000000..10ed831e --- /dev/null +++ b/.gitea/workflows/release-tag.yml @@ -0,0 +1,23 @@ +name: release + +on: + push: + tags: + - '*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: setup go + uses: https://github.com/actions/setup-go@v4 + with: + go-version: '>=1.20.1' + - name: Use Go Action + id: use-go-action + uses: actions/release-action@main + with: + api_key: '${{secrets.RELEASE_TOKEN}}' \ No newline at end of file diff --git a/.gitea/workflows/test-cockroach.yml b/.gitea/workflows/test-cockroach.yml new file mode 100644 index 00000000..20462688 --- /dev/null +++ b/.gitea/workflows/test-cockroach.yml @@ -0,0 +1,56 @@ +name: test cockroach +on: + push: + branches: + - master + pull_request: + +env: + GOPROXY: https://goproxy.io,direct + GOPATH: /go_path + GOCACHE: /go_cache + +jobs: + test-cockroach: + name: test cockroach + runs-on: ubuntu-latest + steps: + # - name: cache go path + # id: cache-go-path + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_path + # key: go_path-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_path-${{ github.repository }}- + # go_path- + # - name: cache go cache + # id: cache-go-cache + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_cache + # key: go_cache-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_cache-${{ github.repository }}- + # go_cache- + - uses: actions/setup-go@v3 + with: + go-version: 1.20 + - uses: https://github.com/actions/checkout@v3 + - name: test cockroach + env: + TEST_COCKROACH_HOST: "cockroach:26257" + TEST_COCKROACH_DBNAME: xorm_test + TEST_COCKROACH_USERNAME: root + TEST_COCKROACH_PASSWORD: + run: sleep 20 && make test-cockroach + + services: + cockroach: + image: cockroachdb/cockroach:v19.2.4 + ports: + - 26257:26257 + cmd: + - '/cockroach/cockroach' + - 'start' + - '--insecure' \ No newline at end of file diff --git a/.gitea/workflows/test-mariadb.yml b/.gitea/workflows/test-mariadb.yml new file mode 100644 index 00000000..466f3858 --- /dev/null +++ b/.gitea/workflows/test-mariadb.yml @@ -0,0 +1,56 @@ +name: test mariadb +on: + push: + branches: + - master + pull_request: + +env: + GOPROXY: https://goproxy.io,direct + GOPATH: /go_path + GOCACHE: /go_cache + +jobs: + lint: + name: test mariadb + runs-on: ubuntu-latest + steps: + # - name: cache go path + # id: cache-go-path + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_path + # key: go_path-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_path-${{ github.repository }}- + # go_path- + # - name: cache go cache + # id: cache-go-cache + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_cache + # key: go_cache-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_cache-${{ github.repository }}- + # go_cache- + - uses: actions/setup-go@v3 + with: + go-version: 1.20 + - uses: https://github.com/actions/checkout@v3 + - name: test mariadb + env: + TEST_MYSQL_HOST: mariadb + TEST_MYSQL_CHARSET: utf8mb4 + TEST_MYSQL_DBNAME: xorm_test + TEST_MYSQL_USERNAME: root + TEST_MYSQL_PASSWORD: + run: TEST_QUOTE_POLICY=reserved make test-mysql + + services: + mariadb: + image: mariadb:10.4 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: xorm_test + ports: + - 3306:3306 \ No newline at end of file diff --git a/.gitea/workflows/test-mssql.yml b/.gitea/workflows/test-mssql.yml new file mode 100644 index 00000000..d02e6956 --- /dev/null +++ b/.gitea/workflows/test-mssql.yml @@ -0,0 +1,56 @@ +name: test mssql +on: + push: + branches: + - master + pull_request: + +env: + GOPROXY: https://goproxy.io,direct + GOPATH: /go_path + GOCACHE: /go_cache + +jobs: + test-mssql: + name: test mssql + runs-on: ubuntu-latest + steps: + # - name: cache go path + # id: cache-go-path + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_path + # key: go_path-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_path-${{ github.repository }}- + # go_path- + # - name: cache go cache + # id: cache-go-cache + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_cache + # key: go_cache-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_cache-${{ github.repository }}- + # go_cache- + - uses: actions/setup-go@v3 + with: + go-version: 1.20 + - uses: https://github.com/actions/checkout@v3 + - name: test mssql + env: + TEST_MSSQL_HOST: mssql + TEST_MSSQL_DBNAME: xorm_test + TEST_MSSQL_USERNAME: sa + TEST_MSSQL_PASSWORD: "yourStrong(!)Password" + run: TEST_MSSQL_DEFAULT_VARCHAR=NVARCHAR TEST_MSSQL_DEFAULT_CHAR=NCHAR make test-mssql + + services: + mssql: + image: mcr.microsoft.com/mssql/server:latest + env: + ACCEPT_EULA: Y + SA_PASSWORD: yourStrong(!)Password + MSSQL_PID: Standard + ports: + - 1433:1433 \ No newline at end of file diff --git a/.gitea/workflows/test-mysql.yml b/.gitea/workflows/test-mysql.yml new file mode 100644 index 00000000..03ee2725 --- /dev/null +++ b/.gitea/workflows/test-mysql.yml @@ -0,0 +1,56 @@ +name: test mysql +on: + push: + branches: + - master + pull_request: + +env: + GOPROXY: https://goproxy.io,direct + GOPATH: /go_path + GOCACHE: /go_cache + +jobs: + test-mysql: + name: test mysql + runs-on: ubuntu-latest + steps: + # - name: cache go path + # id: cache-go-path + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_path + # key: go_path-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_path-${{ github.repository }}- + # go_path- + # - name: cache go cache + # id: cache-go-cache + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_cache + # key: go_cache-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_cache-${{ github.repository }}- + # go_cache- + - uses: actions/setup-go@v3 + with: + go-version: 1.20 + - uses: https://github.com/actions/checkout@v3 + - name: test mysql utf8mb4 + env: + TEST_MYSQL_HOST: mysql + TEST_MYSQL_CHARSET: utf8mb4 + TEST_MYSQL_DBNAME: xorm_test + TEST_MYSQL_USERNAME: root + TEST_MYSQL_PASSWORD: + run: TEST_QUOTE_POLICY=reserved make test-mysql-tls + + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: xorm_test + ports: + - 3306:3306 \ No newline at end of file diff --git a/.gitea/workflows/test-mysql8.yml b/.gitea/workflows/test-mysql8.yml new file mode 100644 index 00000000..3fbd7c30 --- /dev/null +++ b/.gitea/workflows/test-mysql8.yml @@ -0,0 +1,56 @@ +name: test mysql8 +on: + push: + branches: + - master + pull_request: + +env: + GOPROXY: https://goproxy.io,direct + GOPATH: /go_path + GOCACHE: /go_cache + +jobs: + lint: + name: test mysql8 + runs-on: ubuntu-latest + steps: + # - name: cache go path + # id: cache-go-path + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_path + # key: go_path-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_path-${{ github.repository }}- + # go_path- + # - name: cache go cache + # id: cache-go-cache + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_cache + # key: go_cache-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_cache-${{ github.repository }}- + # go_cache- + - uses: actions/setup-go@v3 + with: + go-version: 1.20 + - uses: https://github.com/actions/checkout@v3 + - name: test mysql8 + env: + TEST_MYSQL_HOST: mysql8 + TEST_MYSQL_CHARSET: utf8mb4 + TEST_MYSQL_DBNAME: xorm_test + TEST_MYSQL_USERNAME: root + TEST_MYSQL_PASSWORD: + run: TEST_CACHE_ENABLE=true make test-mysql + + services: + mysql8: + image: mysql:8.0 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: xorm_test + ports: + - 3306:3306 \ No newline at end of file diff --git a/.gitea/workflows/test-postgres.yml b/.gitea/workflows/test-postgres.yml new file mode 100644 index 00000000..89aa72c3 --- /dev/null +++ b/.gitea/workflows/test-postgres.yml @@ -0,0 +1,79 @@ +name: test postgres +on: + push: + branches: + - master + pull_request: + +env: + GOPROXY: https://goproxy.io,direct + GOPATH: /go_path + GOCACHE: /go_cache + +jobs: + lint: + name: test postgres + runs-on: ubuntu-latest + steps: + # - name: cache go path + # id: cache-go-path + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_path + # key: go_path-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_path-${{ github.repository }}- + # go_path- + # - name: cache go cache + # id: cache-go-cache + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_cache + # key: go_cache-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_cache-${{ github.repository }}- + # go_cache- + - uses: actions/setup-go@v3 + with: + go-version: 1.20 + - uses: https://github.com/actions/checkout@v3 + - name: test postgres + env: + TEST_PGSQL_HOST: pgsql + TEST_PGSQL_DBNAME: xorm_test + TEST_PGSQL_USERNAME: postgres + TEST_PGSQL_PASSWORD: postgres + run: TEST_CACHE_ENABLE=true make test-postgres + - name: test postgres with schema + env: + TEST_PGSQL_HOST: pgsql + TEST_PGSQL_SCHEMA: xorm + TEST_PGSQL_DBNAME: xorm_test + TEST_PGSQL_USERNAME: postgres + TEST_PGSQL_PASSWORD: postgres + run: TEST_QUOTE_POLICY=reserved make test-postgres + - name: test pgx + env: + TEST_PGSQL_HOST: pgsql + TEST_PGSQL_DBNAME: xorm_test + TEST_PGSQL_USERNAME: postgres + TEST_PGSQL_PASSWORD: postgres + run: TEST_CACHE_ENABLE=true make test-pgx + - name: test pgx with schema + env: + TEST_PGSQL_HOST: pgsql + TEST_PGSQL_SCHEMA: xorm + TEST_PGSQL_DBNAME: xorm_test + TEST_PGSQL_USERNAME: postgres + TEST_PGSQL_PASSWORD: postgres + run: TEST_QUOTE_POLICY=reserved make test-pgx + + services: + pgsql: + image: postgres:9.5 + env: + POSTGRES_DB: xorm_test + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 \ No newline at end of file diff --git a/.gitea/workflows/test-sqlite.yml b/.gitea/workflows/test-sqlite.yml new file mode 100644 index 00000000..cca2e786 --- /dev/null +++ b/.gitea/workflows/test-sqlite.yml @@ -0,0 +1,49 @@ +name: test sqlite +on: + push: + branches: + - master + pull_request: + +env: + GOPROXY: https://goproxy.io,direct + GOPATH: /go_path + GOCACHE: /go_cache + +jobs: + test-sqlite: + name: unit test & test sqlite + runs-on: ubuntu-latest + steps: + # - name: cache go path + # id: cache-go-path + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_path + # key: go_path-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_path-${{ github.repository }}- + # go_path- + # - name: cache go cache + # id: cache-go-cache + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_cache + # key: go_cache-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_cache-${{ github.repository }}- + # go_cache- + - uses: actions/setup-go@v3 + with: + go-version: 1.20 + - uses: https://github.com/actions/checkout@v3 + - name: vet + run: make vet + - name: format check + run: make fmt-check + - name: unit test + run: make test + - name: test sqlite3 + run: make test-sqlite3 + - name: test sqlite3 with cache + run: TEST_CACHE_ENABLE=true make test-sqlite3 \ No newline at end of file diff --git a/.gitea/workflows/test-tidb.yml b/.gitea/workflows/test-tidb.yml new file mode 100644 index 00000000..fa6e27ad --- /dev/null +++ b/.gitea/workflows/test-tidb.yml @@ -0,0 +1,52 @@ +name: test tidb +on: + push: + branches: + - master + pull_request: + +env: + GOPROXY: https://goproxy.io,direct + GOPATH: /go_path + GOCACHE: /go_cache + +jobs: + test-tidb: + name: test tidb + runs-on: ubuntu-latest + steps: + # - name: cache go path + # id: cache-go-path + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_path + # key: go_path-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_path-${{ github.repository }}- + # go_path- + # - name: cache go cache + # id: cache-go-cache + # uses: https://github.com/actions/cache@v3 + # with: + # path: /go_cache + # key: go_cache-${{ github.repository }}-${{ github.ref_name }} + # restore-keys: | + # go_cache-${{ github.repository }}- + # go_cache- + - uses: actions/setup-go@v3 + with: + go-version: 1.20 + - uses: https://github.com/actions/checkout@v3 + - name: test tidb + env: + TEST_TIDB_HOST: "tidb:4000" + TEST_TIDB_DBNAME: xorm_test + TEST_TIDB_USERNAME: root + TEST_TIDB_PASSWORD: + run: make test-tidb + + services: + tidb: + image: pingcap/tidb:v3.0.3 + ports: + - 4000:4000 \ No newline at end of file From 57f7d69f1bbebc00dddf22308438c65034e4e457 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Mon, 29 May 2023 13:01:43 +0000 Subject: [PATCH 083/116] Fix test-cockroach workflow (#2269) The image has already specified the entrypoint so we don't need to call `/cockroach/cockroach` in `cmd`. ![image](/attachments/669496c1-5673-4609-abc7-f42846e4d479) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2269 Co-authored-by: Zettat123 Co-committed-by: Zettat123 --- .gitea/workflows/test-cockroach.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitea/workflows/test-cockroach.yml b/.gitea/workflows/test-cockroach.yml index 20462688..0ca18861 100644 --- a/.gitea/workflows/test-cockroach.yml +++ b/.gitea/workflows/test-cockroach.yml @@ -51,6 +51,5 @@ jobs: ports: - 26257:26257 cmd: - - '/cockroach/cockroach' - 'start' - - '--insecure' \ No newline at end of file + - '--insecure' From cb851a2f95864bf2f27b3e3222e5f38411928e79 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Wed, 31 May 2023 01:43:24 +0000 Subject: [PATCH 084/116] Filter support passing context (#2200) (#2270) ```diff // Filter is an interface to filter SQL type Filter interface { --- Do(sql string) string +++ Do(ctx context.Context, sql string) string } ``` ### Adds a `Context` parameter to the `Do` method of the Filter interface. Developers can rewrite SQL through the `Filter` `Do` method and **need to get the necessary data from the Context** to assist. For example, get user information through `Context`, so that different users can use different tables. Another example is to get the flags through `Context` to add annotations to the SQL, and use the annotations to let the subsequent `DB-Proxy` to achieve read/write separation. Reviewed-on: https://gitea.com/xorm/xorm/pulls/2270 Reviewed-by: Lunny Xiao Co-authored-by: LinkinStars Co-committed-by: LinkinStars --- dialects/filter.go | 5 +++-- session_delete.go | 2 +- session_find.go | 2 +- session_get.go | 2 +- session_raw.go | 2 +- session_update.go | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/dialects/filter.go b/dialects/filter.go index bfe2e93e..ff9a44e8 100644 --- a/dialects/filter.go +++ b/dialects/filter.go @@ -5,13 +5,14 @@ package dialects import ( + "context" "fmt" "strings" ) // Filter is an interface to filter SQL type Filter interface { - Do(sql string) string + Do(ctx context.Context, sql string) string } // SeqFilter filter SQL replace ?, ? ... to $1, $2 ... @@ -71,6 +72,6 @@ func convertQuestionMark(sql, prefix string, start int) string { } // Do implements Filter -func (s *SeqFilter) Do(sql string) string { +func (s *SeqFilter) Do(ctx context.Context, sql string) string { return convertQuestionMark(sql, s.Prefix, s.Start) } diff --git a/session_delete.go b/session_delete.go index d36b9e52..6c63d9b0 100644 --- a/session_delete.go +++ b/session_delete.go @@ -30,7 +30,7 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri } for _, filter := range session.engine.dialect.Filters() { - sqlStr = filter.Do(sqlStr) + sqlStr = filter.Do(session.ctx, sqlStr) } newsql := session.statement.ConvertIDSQL(sqlStr) diff --git a/session_find.go b/session_find.go index 2270454b..3341eafe 100644 --- a/session_find.go +++ b/session_find.go @@ -283,7 +283,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in } for _, filter := range session.engine.dialect.Filters() { - sqlStr = filter.Do(sqlStr) + sqlStr = filter.Do(session.ctx, sqlStr) } newsql := session.statement.ConvertIDSQL(sqlStr) diff --git a/session_get.go b/session_get.go index 9bb92a8b..96e362e9 100644 --- a/session_get.go +++ b/session_get.go @@ -278,7 +278,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf } for _, filter := range session.engine.dialect.Filters() { - sqlStr = filter.Do(sqlStr) + sqlStr = filter.Do(session.ctx, sqlStr) } newsql := session.statement.ConvertIDSQL(sqlStr) if newsql == "" { diff --git a/session_raw.go b/session_raw.go index add584d0..99f6be99 100644 --- a/session_raw.go +++ b/session_raw.go @@ -13,7 +13,7 @@ import ( func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) { for _, filter := range session.engine.dialect.Filters() { - *sqlStr = filter.Do(*sqlStr) + *sqlStr = filter.Do(session.ctx, *sqlStr) } session.lastSQL = *sqlStr diff --git a/session_update.go b/session_update.go index e7104710..1f80e70f 100644 --- a/session_update.go +++ b/session_update.go @@ -34,7 +34,7 @@ func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr stri return ErrCacheFailed } for _, filter := range session.engine.dialect.Filters() { - newsql = filter.Do(newsql) + newsql = filter.Do(session.ctx, newsql) } session.engine.logger.Debugf("[cache] new sql: %v, %v", oldhead, newsql) From caa8a029c60642694f3417d12d902769031d1f45 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 2 Jun 2023 14:16:30 +0000 Subject: [PATCH 085/116] some optimzation (#2272) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2272 --- dialects/dameng.go | 14 ++++++++++++-- dialects/filter.go | 7 ++++--- dialects/mysql.go | 12 +++++++++--- internal/statements/cache.go | 16 +++++++++++----- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/dialects/dameng.go b/dialects/dameng.go index 5e92ec2f..8bed7f13 100644 --- a/dialects/dameng.go +++ b/dialects/dameng.go @@ -709,7 +709,13 @@ func (db *dameng) CreateTableSQL(ctx context.Context, queryer core.Queryer, tabl return "", false, err } } - if _, err := b.WriteString(fmt.Sprintf("CONSTRAINT PK_%s PRIMARY KEY (", tableName)); err != nil { + if _, err := b.WriteString("CONSTRAINT PK_"); err != nil { + return "", false, err + } + if _, err := b.WriteString(tableName); err != nil { + return "", false, err + } + if _, err := b.WriteString(" PRIMARY KEY ("); err != nil { return "", false, err } if err := quoter.JoinWrite(&b, pkList, ","); err != nil { @@ -837,7 +843,11 @@ func addSingleQuote(name string) string { if name[0] == '\'' && name[len(name)-1] == '\'' { return name } - return fmt.Sprintf("'%s'", name) + var b strings.Builder + b.WriteRune('\'') + b.WriteString(name) + b.WriteRune('\'') + return b.String() } func (db *dameng) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) { diff --git a/dialects/filter.go b/dialects/filter.go index ff9a44e8..add67c1b 100644 --- a/dialects/filter.go +++ b/dialects/filter.go @@ -6,7 +6,7 @@ package dialects import ( "context" - "fmt" + "strconv" "strings" ) @@ -29,10 +29,11 @@ func convertQuestionMark(sql, prefix string, start int) string { var isMaybeLineComment bool var isMaybeComment bool var isMaybeCommentEnd bool - var index = start + index := start for _, c := range sql { if !beginSingleQuote && !isLineComment && !isComment && c == '?' { - buf.WriteString(fmt.Sprintf("%s%v", prefix, index)) + buf.WriteString(prefix) + buf.WriteString(strconv.Itoa(index)) index++ } else { if isMaybeLineComment { diff --git a/dialects/mysql.go b/dialects/mysql.go index 195e1f23..b941a41b 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -381,11 +381,17 @@ func (db *mysql) IsTableExist(queryer core.Queryer, ctx context.Context, tableNa func (db *mysql) AddColumnSQL(tableName string, col *schemas.Column) string { quoter := db.dialect.Quoter() s, _ := ColumnString(db, col, true) - sql := fmt.Sprintf("ALTER TABLE %v ADD %v", quoter.Quote(tableName), s) + var b strings.Builder + b.WriteString("ALTER TABLE ") + quoter.QuoteTo(&b, tableName) + b.WriteString(" ADD ") + b.WriteString(s) if len(col.Comment) > 0 { - sql += " COMMENT '" + col.Comment + "'" + b.WriteString(" COMMENT '") + b.WriteString(col.Comment) + b.WriteString("'") } - return sql + return b.String() } func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) { diff --git a/internal/statements/cache.go b/internal/statements/cache.go index 669cd018..9dd76754 100644 --- a/internal/statements/cache.go +++ b/internal/statements/cache.go @@ -6,6 +6,7 @@ package statements import ( "fmt" + "strconv" "strings" "xorm.io/xorm/internal/utils" @@ -26,14 +27,19 @@ func (statement *Statement) ConvertIDSQL(sqlStr string) string { return "" } - var top string + var b strings.Builder + b.WriteString("SELECT ") pLimitN := statement.LimitN if pLimitN != nil && statement.dialect.URI().DBType == schemas.MSSQL { - top = fmt.Sprintf("TOP %d ", *pLimitN) + b.WriteString("TOP ") + b.WriteString(strconv.Itoa(*pLimitN)) + b.WriteString(" ") } + b.WriteString(colstrs) + b.WriteString(" FROM ") + b.WriteString(sqls[1]) - newsql := fmt.Sprintf("SELECT %s%s FROM %v", top, colstrs, sqls[1]) - return newsql + return b.String() } return "" } @@ -54,7 +60,7 @@ func (statement *Statement) ConvertUpdateSQL(sqlStr string) (string, string) { return "", "" } - var whereStr = sqls[1] + whereStr := sqls[1] // TODO: for postgres only, if any other database? var paraStr string From 838a0d9bca2b3e447e29902c9b5d8df812ea1bab Mon Sep 17 00:00:00 2001 From: ccbhj Date: Mon, 12 Jun 2023 09:18:13 +0000 Subject: [PATCH 086/116] ccbhj/fix_mysql_blob_shared_bytes (#2274) Copy the sql.RawBytes when converting to []byte. Fix issue https://gitea.com/xorm/xorm/issues/2273 Co-authored-by: Bingjia Chen Reviewed-on: https://gitea.com/xorm/xorm/pulls/2274 Reviewed-by: Lunny Xiao Co-authored-by: ccbhj Co-committed-by: ccbhj --- convert/interface.go | 5 ++- integrations/session_query_test.go | 67 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/convert/interface.go b/convert/interface.go index b0f28c81..2cc8d9f4 100644 --- a/convert/interface.go +++ b/convert/interface.go @@ -24,7 +24,10 @@ func Interface2Interface(userLocation *time.Location, v interface{}) (interface{ return vv.String, nil case *sql.RawBytes: if len([]byte(*vv)) > 0 { - return []byte(*vv), nil + src := []byte(*vv) + dest := make([]byte, len(src)) + copy(dest, src) + return dest, nil } return nil, nil case *sql.NullInt32: diff --git a/integrations/session_query_test.go b/integrations/session_query_test.go index b72f7ef2..00b7d7a6 100644 --- a/integrations/session_query_test.go +++ b/integrations/session_query_test.go @@ -5,11 +5,13 @@ package integrations import ( + "bytes" "strconv" "testing" "time" "xorm.io/builder" + "xorm.io/xorm/schemas" "github.com/stretchr/testify/assert" @@ -381,3 +383,68 @@ func TestQueryStringWithLimit(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 0, len(data)) } + +func TestQueryBLOBInMySQL(t *testing.T) { + assert.NoError(t, PrepareEngine()) + + var err error + type Avatar struct { + Id int64 `xorm:"autoincr pk"` + Avatar []byte `xorm:"BLOB"` + } + + assert.NoError(t, testEngine.Sync(new(Avatar))) + testEngine.Delete(Avatar{}) + + repeatBytes := func(n int, b byte) []byte { + return bytes.Repeat([]byte{b}, n) + } + + const N = 10 + var data = []Avatar{} + for i := 0; i < N; i++ { + // allocate a []byte that is as twice big as the last one + // so that the underlying buffer will need to reallocate when querying + bs := repeatBytes(1<<(i+2), 'A'+byte(i)) + data = append(data, Avatar{ + Avatar: bs, + }) + } + _, err = testEngine.Insert(data) + assert.NoError(t, err) + defer func() { + testEngine.Delete(Avatar{}) + }() + + { + records, err := testEngine.QueryInterface("select avatar from " + testEngine.Quote(testEngine.TableName("avatar", true))) + assert.NoError(t, err) + for i, record := range records { + bs := record["avatar"].([]byte) + assert.EqualValues(t, repeatBytes(1<<(i+2), 'A'+byte(i))[:3], bs[:3]) + t.Logf("%d => %p => %02x %02x %02x", i, bs, bs[0], bs[1], bs[2]) + } + } + + { + arr := make([][]interface{}, 0) + err = testEngine.Table(testEngine.Quote(testEngine.TableName("avatar", true))).Cols("avatar").Find(&arr) + assert.NoError(t, err) + for i, record := range arr { + bs := record[0].([]byte) + assert.EqualValues(t, repeatBytes(1<<(i+2), 'A'+byte(i))[:3], bs[:3]) + t.Logf("%d => %p => %02x %02x %02x", i, bs, bs[0], bs[1], bs[2]) + } + } + + { + arr := make([]map[string]interface{}, 0) + err = testEngine.Table(testEngine.Quote(testEngine.TableName("avatar", true))).Cols("avatar").Find(&arr) + assert.NoError(t, err) + for i, record := range arr { + bs := record["avatar"].([]byte) + assert.EqualValues(t, repeatBytes(1<<(i+2), 'A'+byte(i))[:3], bs[:3]) + t.Logf("%d => %p => %02x %02x %02x", i, bs, bs[0], bs[1], bs[2]) + } + } +} From 068de8c0f8beddb82dc32e549f2e1713f27bd71f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 23 Jun 2023 08:48:37 +0000 Subject: [PATCH 087/116] Don't warn when database have extra columns which are not in struct (#2279) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2279 --- engine.go | 15 --- session_schema.go | 227 +------------------------------------- sync.go | 273 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+), 240 deletions(-) create mode 100644 sync.go diff --git a/engine.go b/engine.go index 389819e7..aa9d8050 100644 --- a/engine.go +++ b/engine.go @@ -1120,21 +1120,6 @@ func (engine *Engine) UnMapType(t reflect.Type) { engine.tagParser.ClearCacheTable(t) } -// Sync the new struct changes to database, this method will automatically add -// table, column, index, unique. but will not delete or change anything. -// If you change some field, you should change the database manually. -func (engine *Engine) Sync(beans ...interface{}) error { - session := engine.NewSession() - defer session.Close() - return session.Sync(beans...) -} - -// Sync2 synchronize structs to database tables -// Depricated -func (engine *Engine) Sync2(beans ...interface{}) error { - return engine.Sync(beans...) -} - // CreateTables create tabls according bean func (engine *Engine) CreateTables(beans ...interface{}) error { session := engine.NewSession() diff --git a/session_schema.go b/session_schema.go index e66c3b42..830ba08a 100644 --- a/session_schema.go +++ b/session_schema.go @@ -15,7 +15,6 @@ import ( "xorm.io/xorm/dialects" "xorm.io/xorm/internal/utils" - "xorm.io/xorm/schemas" ) // Ping test if database is ok @@ -169,7 +168,7 @@ func (session *Session) dropTable(beanOrTableName interface{}) error { return nil } - var seqName = utils.SeqName(tableName) + seqName := utils.SeqName(tableName) exist, err := session.engine.dialect.IsSequenceExist(session.ctx, session.getQueryer(), seqName) if err != nil { return err @@ -244,228 +243,6 @@ func (session *Session) addUnique(tableName, uqeName string) error { return err } -// Sync2 synchronize structs to database tables -// Depricated -func (session *Session) Sync2(beans ...interface{}) error { - return session.Sync(beans...) -} - -// Sync synchronize structs to database tables -func (session *Session) Sync(beans ...interface{}) error { - engine := session.engine - - if session.isAutoClose { - session.isAutoClose = false - defer session.Close() - } - - tables, err := engine.dialect.GetTables(session.getQueryer(), session.ctx) - if err != nil { - return err - } - - session.autoResetStatement = false - defer func() { - session.autoResetStatement = true - session.resetStatement() - }() - - for _, bean := range beans { - v := utils.ReflectValue(bean) - table, err := engine.tagParser.ParseWithCache(v) - if err != nil { - return err - } - var tbName string - if len(session.statement.AltTableName) > 0 { - tbName = session.statement.AltTableName - } else { - tbName = engine.TableName(bean) - } - tbNameWithSchema := engine.tbNameWithSchema(tbName) - - var oriTable *schemas.Table - for _, tb := range tables { - if strings.EqualFold(engine.tbNameWithSchema(tb.Name), engine.tbNameWithSchema(tbName)) { - oriTable = tb - break - } - } - - // this is a new table - if oriTable == nil { - err = session.StoreEngine(session.statement.StoreEngine).createTable(bean) - if err != nil { - return err - } - - err = session.createUniques(bean) - if err != nil { - return err - } - - err = session.createIndexes(bean) - if err != nil { - return err - } - continue - } - - // this will modify an old table - if err = engine.loadTableInfo(oriTable); err != nil { - return err - } - - // check columns - for _, col := range table.Columns() { - var oriCol *schemas.Column - for _, col2 := range oriTable.Columns() { - if strings.EqualFold(col.Name, col2.Name) { - oriCol = col2 - break - } - } - - // column is not exist on table - if oriCol == nil { - session.statement.RefTable = table - session.statement.SetTableName(tbNameWithSchema) - if err = session.addColumn(col.Name); err != nil { - return err - } - continue - } - - err = nil - expectedType := engine.dialect.SQLType(col) - curType := engine.dialect.SQLType(oriCol) - if expectedType != curType { - if expectedType == schemas.Text && - strings.HasPrefix(curType, schemas.Varchar) { - // currently only support mysql & postgres - if engine.dialect.URI().DBType == schemas.MYSQL || - engine.dialect.URI().DBType == schemas.POSTGRES { - engine.logger.Infof("Table %s column %s change type from %s to %s\n", - tbNameWithSchema, col.Name, curType, expectedType) - _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) - } else { - engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n", - tbNameWithSchema, col.Name, curType, expectedType) - } - } else if strings.HasPrefix(curType, schemas.Varchar) && strings.HasPrefix(expectedType, schemas.Varchar) { - if engine.dialect.URI().DBType == schemas.MYSQL { - if oriCol.Length < col.Length { - engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", - tbNameWithSchema, col.Name, oriCol.Length, col.Length) - _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) - } - } - } else { - if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') { - if !strings.EqualFold(schemas.SQLTypeName(curType), engine.dialect.Alias(schemas.SQLTypeName(expectedType))) { - engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s", - tbNameWithSchema, col.Name, curType, expectedType) - } - } - } - } else if expectedType == schemas.Varchar { - if engine.dialect.URI().DBType == schemas.MYSQL { - if oriCol.Length < col.Length { - engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", - tbNameWithSchema, col.Name, oriCol.Length, col.Length) - _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) - } - } - } else if col.Comment != oriCol.Comment { - _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) - } - - if col.Default != oriCol.Default { - switch { - case col.IsAutoIncrement: // For autoincrement column, don't check default - case (col.SQLType.Name == schemas.Bool || col.SQLType.Name == schemas.Boolean) && - ((strings.EqualFold(col.Default, "true") && oriCol.Default == "1") || - (strings.EqualFold(col.Default, "false") && oriCol.Default == "0")): - default: - engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s", - tbName, col.Name, oriCol.Default, col.Default) - } - } - if col.Nullable != oriCol.Nullable { - engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v", - tbName, col.Name, oriCol.Nullable, col.Nullable) - } - - if err != nil { - return err - } - } - - var foundIndexNames = make(map[string]bool) - var addedNames = make(map[string]*schemas.Index) - - for name, index := range table.Indexes { - var oriIndex *schemas.Index - for name2, index2 := range oriTable.Indexes { - if index.Equal(index2) { - oriIndex = index2 - foundIndexNames[name2] = true - break - } - } - - if oriIndex != nil { - if oriIndex.Type != index.Type { - sql := engine.dialect.DropIndexSQL(tbNameWithSchema, oriIndex) - _, err = session.exec(sql) - if err != nil { - return err - } - oriIndex = nil - } - } - - if oriIndex == nil { - addedNames[name] = index - } - } - - for name2, index2 := range oriTable.Indexes { - if _, ok := foundIndexNames[name2]; !ok { - sql := engine.dialect.DropIndexSQL(tbNameWithSchema, index2) - _, err = session.exec(sql) - if err != nil { - return err - } - } - } - - for name, index := range addedNames { - if index.Type == schemas.UniqueType { - session.statement.RefTable = table - session.statement.SetTableName(tbNameWithSchema) - err = session.addUnique(tbNameWithSchema, name) - } else if index.Type == schemas.IndexType { - session.statement.RefTable = table - session.statement.SetTableName(tbNameWithSchema) - err = session.addIndex(tbNameWithSchema, name) - } - if err != nil { - return err - } - } - - // check all the columns which removed from struct fields but left on database tables. - for _, colName := range oriTable.ColumnsSeq() { - if table.GetColumn(colName) == nil { - engine.logger.Warnf("Table %s has column %s but struct has not related field", engine.TableName(oriTable.Name, true), colName) - } - } - } - - return nil -} - // ImportFile SQL DDL file func (session *Session) ImportFile(ddlPath string) ([]sql.Result, error) { file, err := os.Open(ddlPath) @@ -490,7 +267,7 @@ func (session *Session) Import(r io.Reader) ([]sql.Result, error) { if atEOF && len(data) == 0 { return 0, nil, nil } - var oriInSingleQuote = inSingleQuote + oriInSingleQuote := inSingleQuote for i, b := range data { if startComment { if b == '\n' { diff --git a/sync.go b/sync.go new file mode 100644 index 00000000..2bff68d3 --- /dev/null +++ b/sync.go @@ -0,0 +1,273 @@ +// Copyright 2023 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 ( + "strings" + + "xorm.io/xorm/internal/utils" + "xorm.io/xorm/schemas" +) + +type SyncOptions struct { + WarnIfDatabaseColumnMissed bool +} + +type SyncResult struct{} + +// Sync the new struct changes to database, this method will automatically add +// table, column, index, unique. but will not delete or change anything. +// If you change some field, you should change the database manually. +func (engine *Engine) Sync(beans ...interface{}) error { + session := engine.NewSession() + defer session.Close() + return session.Sync(beans...) +} + +// SyncWithOptions sync the database schemas according options and table structs +func (engine *Engine) SyncWithOptions(opts SyncOptions, beans ...interface{}) (*SyncResult, error) { + session := engine.NewSession() + defer session.Close() + return session.SyncWithOptions(opts, beans...) +} + +// Sync2 synchronize structs to database tables +// Depricated +func (engine *Engine) Sync2(beans ...interface{}) error { + return engine.Sync(beans...) +} + +// Sync2 synchronize structs to database tables +// Depricated +func (session *Session) Sync2(beans ...interface{}) error { + return session.Sync(beans...) +} + +// Sync synchronize structs to database tables +func (session *Session) Sync(beans ...interface{}) error { + _, err := session.SyncWithOptions(SyncOptions{ + WarnIfDatabaseColumnMissed: true, + }, beans...) + return err +} + +func (session *Session) SyncWithOptions(opts SyncOptions, beans ...interface{}) (*SyncResult, error) { + engine := session.engine + + if session.isAutoClose { + session.isAutoClose = false + defer session.Close() + } + + tables, err := engine.dialect.GetTables(session.getQueryer(), session.ctx) + if err != nil { + return nil, err + } + + session.autoResetStatement = false + defer func() { + session.autoResetStatement = true + session.resetStatement() + }() + + var syncResult SyncResult + + for _, bean := range beans { + v := utils.ReflectValue(bean) + table, err := engine.tagParser.ParseWithCache(v) + if err != nil { + return nil, err + } + var tbName string + if len(session.statement.AltTableName) > 0 { + tbName = session.statement.AltTableName + } else { + tbName = engine.TableName(bean) + } + tbNameWithSchema := engine.tbNameWithSchema(tbName) + + var oriTable *schemas.Table + for _, tb := range tables { + if strings.EqualFold(engine.tbNameWithSchema(tb.Name), engine.tbNameWithSchema(tbName)) { + oriTable = tb + break + } + } + + // this is a new table + if oriTable == nil { + err = session.StoreEngine(session.statement.StoreEngine).createTable(bean) + if err != nil { + return nil, err + } + + err = session.createUniques(bean) + if err != nil { + return nil, err + } + + err = session.createIndexes(bean) + if err != nil { + return nil, err + } + continue + } + + // this will modify an old table + if err = engine.loadTableInfo(oriTable); err != nil { + return nil, err + } + + // check columns + for _, col := range table.Columns() { + var oriCol *schemas.Column + for _, col2 := range oriTable.Columns() { + if strings.EqualFold(col.Name, col2.Name) { + oriCol = col2 + break + } + } + + // column is not exist on table + if oriCol == nil { + session.statement.RefTable = table + session.statement.SetTableName(tbNameWithSchema) + if err = session.addColumn(col.Name); err != nil { + return nil, err + } + continue + } + + err = nil + expectedType := engine.dialect.SQLType(col) + curType := engine.dialect.SQLType(oriCol) + if expectedType != curType { + if expectedType == schemas.Text && + strings.HasPrefix(curType, schemas.Varchar) { + // currently only support mysql & postgres + if engine.dialect.URI().DBType == schemas.MYSQL || + engine.dialect.URI().DBType == schemas.POSTGRES { + engine.logger.Infof("Table %s column %s change type from %s to %s\n", + tbNameWithSchema, col.Name, curType, expectedType) + _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) + } else { + engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n", + tbNameWithSchema, col.Name, curType, expectedType) + } + } else if strings.HasPrefix(curType, schemas.Varchar) && strings.HasPrefix(expectedType, schemas.Varchar) { + if engine.dialect.URI().DBType == schemas.MYSQL { + if oriCol.Length < col.Length { + engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", + tbNameWithSchema, col.Name, oriCol.Length, col.Length) + _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) + } + } + } else { + if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') { + if !strings.EqualFold(schemas.SQLTypeName(curType), engine.dialect.Alias(schemas.SQLTypeName(expectedType))) { + engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s", + tbNameWithSchema, col.Name, curType, expectedType) + } + } + } + } else if expectedType == schemas.Varchar { + if engine.dialect.URI().DBType == schemas.MYSQL { + if oriCol.Length < col.Length { + engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", + tbNameWithSchema, col.Name, oriCol.Length, col.Length) + _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) + } + } + } else if col.Comment != oriCol.Comment { + _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) + } + + if col.Default != oriCol.Default { + switch { + case col.IsAutoIncrement: // For autoincrement column, don't check default + case (col.SQLType.Name == schemas.Bool || col.SQLType.Name == schemas.Boolean) && + ((strings.EqualFold(col.Default, "true") && oriCol.Default == "1") || + (strings.EqualFold(col.Default, "false") && oriCol.Default == "0")): + default: + engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s", + tbName, col.Name, oriCol.Default, col.Default) + } + } + if col.Nullable != oriCol.Nullable { + engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v", + tbName, col.Name, oriCol.Nullable, col.Nullable) + } + + if err != nil { + return nil, err + } + } + + foundIndexNames := make(map[string]bool) + addedNames := make(map[string]*schemas.Index) + + for name, index := range table.Indexes { + var oriIndex *schemas.Index + for name2, index2 := range oriTable.Indexes { + if index.Equal(index2) { + oriIndex = index2 + foundIndexNames[name2] = true + break + } + } + + if oriIndex != nil { + if oriIndex.Type != index.Type { + sql := engine.dialect.DropIndexSQL(tbNameWithSchema, oriIndex) + _, err = session.exec(sql) + if err != nil { + return nil, err + } + oriIndex = nil + } + } + + if oriIndex == nil { + addedNames[name] = index + } + } + + for name2, index2 := range oriTable.Indexes { + if _, ok := foundIndexNames[name2]; !ok { + sql := engine.dialect.DropIndexSQL(tbNameWithSchema, index2) + _, err = session.exec(sql) + if err != nil { + return nil, err + } + } + } + + for name, index := range addedNames { + if index.Type == schemas.UniqueType { + session.statement.RefTable = table + session.statement.SetTableName(tbNameWithSchema) + err = session.addUnique(tbNameWithSchema, name) + } else if index.Type == schemas.IndexType { + session.statement.RefTable = table + session.statement.SetTableName(tbNameWithSchema) + err = session.addIndex(tbNameWithSchema, name) + } + if err != nil { + return nil, err + } + } + + if opts.WarnIfDatabaseColumnMissed { + // check all the columns which removed from struct fields but left on database tables. + for _, colName := range oriTable.ColumnsSeq() { + if table.GetColumn(colName) == nil { + engine.logger.Warnf("Table %s has column %s but struct has not related field", engine.TableName(oriTable.Name, true), colName) + } + } + } + } + + return &syncResult, nil +} From 18f8e7a86c758baebc69572a924e438a290130c0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 23 Jun 2023 15:00:31 +0000 Subject: [PATCH 088/116] Default don't log warn for database extra columns when syncing (#2280) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2280 --- sync.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sync.go b/sync.go index 2bff68d3..6583e341 100644 --- a/sync.go +++ b/sync.go @@ -48,7 +48,7 @@ func (session *Session) Sync2(beans ...interface{}) error { // Sync synchronize structs to database tables func (session *Session) Sync(beans ...interface{}) error { _, err := session.SyncWithOptions(SyncOptions{ - WarnIfDatabaseColumnMissed: true, + WarnIfDatabaseColumnMissed: false, }, beans...) return err } From d29fe4993351e8c87de116963e8a066e0d71905b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 1 Jul 2023 03:40:09 +0000 Subject: [PATCH 089/116] Mysql support a new tag Collate (#2283) Fix #237 Fix #2179 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2283 --- dialects/dameng.go | 4 +- dialects/dialect.go | 21 ++-- dialects/mssql.go | 12 +- dialects/mysql.go | 26 ++-- dialects/oracle.go | 2 +- dialects/postgres.go | 2 +- ...{session_schema_test.go => schema_test.go} | 115 ++++++++++++++++- schemas/collation.go | 10 ++ schemas/column.go | 1 + schemas/table.go | 4 +- tags/parser.go | 41 ++++++ tags/tag.go | 11 ++ tags/tag_test.go | 118 ++++++++++-------- 13 files changed, 289 insertions(+), 78 deletions(-) rename integrations/{session_schema_test.go => schema_test.go} (85%) create mode 100644 schemas/collation.go diff --git a/dialects/dameng.go b/dialects/dameng.go index 8bed7f13..23d1836a 100644 --- a/dialects/dameng.go +++ b/dialects/dameng.go @@ -659,7 +659,7 @@ func (db *dameng) DropTableSQL(tableName string) (string, bool) { // ModifyColumnSQL returns a SQL to modify SQL func (db *dameng) ModifyColumnSQL(tableName string, col *schemas.Column) string { - s, _ := ColumnString(db.dialect, col, false) + s, _ := ColumnString(db.dialect, col, false, false) return fmt.Sprintf("ALTER TABLE %s MODIFY %s", db.quoter.Quote(tableName), s) } @@ -692,7 +692,7 @@ func (db *dameng) CreateTableSQL(ctx context.Context, queryer core.Queryer, tabl } } - s, _ := ColumnString(db, col, false) + s, _ := ColumnString(db, col, false, false) if _, err := b.WriteString(s); err != nil { return "", false, err } diff --git a/dialects/dialect.go b/dialects/dialect.go index 70d599e6..d1c5f200 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -135,7 +135,7 @@ func (db *Base) CreateTableSQL(ctx context.Context, queryer core.Queryer, table for i, colName := range table.ColumnsSeq() { col := table.GetColumn(colName) - s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1, false) b.WriteString(s) if i != len(table.ColumnsSeq())-1 { @@ -209,7 +209,7 @@ func (db *Base) IsColumnExist(queryer core.Queryer, ctx context.Context, tableNa // AddColumnSQL returns a SQL to add a column func (db *Base) AddColumnSQL(tableName string, col *schemas.Column) string { - s, _ := ColumnString(db.dialect, col, true) + s, _ := ColumnString(db.dialect, col, true, false) return fmt.Sprintf("ALTER TABLE %s ADD %s", db.dialect.Quoter().Quote(tableName), s) } @@ -241,7 +241,7 @@ func (db *Base) DropIndexSQL(tableName string, index *schemas.Index) string { // ModifyColumnSQL returns a SQL to modify SQL func (db *Base) ModifyColumnSQL(tableName string, col *schemas.Column) string { - s, _ := ColumnString(db.dialect, col, false) + s, _ := ColumnString(db.dialect, col, false, false) return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", db.quoter.Quote(tableName), s) } @@ -254,9 +254,7 @@ func (db *Base) ForUpdateSQL(query string) string { func (db *Base) SetParams(params map[string]string) { } -var ( - dialects = map[string]func() Dialect{} -) +var dialects = map[string]func() Dialect{} // RegisterDialect register database dialect func RegisterDialect(dbName schemas.DBType, dialectFunc func() Dialect) { @@ -307,7 +305,7 @@ func init() { } // ColumnString generate column description string according dialect -func ColumnString(dialect Dialect, col *schemas.Column, includePrimaryKey bool) (string, error) { +func ColumnString(dialect Dialect, col *schemas.Column, includePrimaryKey, supportCollation bool) (string, error) { bd := strings.Builder{} if err := dialect.Quoter().QuoteTo(&bd, col.Name); err != nil { @@ -322,6 +320,15 @@ func ColumnString(dialect Dialect, col *schemas.Column, includePrimaryKey bool) return "", err } + if supportCollation && col.Collation != "" { + if _, err := bd.WriteString(" COLLATE "); err != nil { + return "", err + } + if _, err := bd.WriteString(col.Collation); err != nil { + return "", err + } + } + if includePrimaryKey && col.IsPrimaryKey { if _, err := bd.WriteString(" PRIMARY KEY"); err != nil { return "", err diff --git a/dialects/mssql.go b/dialects/mssql.go index 1b6fe692..e517e688 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -428,7 +428,7 @@ func (db *mssql) DropTableSQL(tableName string) (string, bool) { } func (db *mssql) ModifyColumnSQL(tableName string, col *schemas.Column) string { - s, _ := ColumnString(db.dialect, col, false) + s, _ := ColumnString(db.dialect, col, false, true) return fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s", db.quoter.Quote(tableName), s) } @@ -454,7 +454,7 @@ func (db *mssql) GetColumns(queryer core.Queryer, ctx context.Context, tableName s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable, "default_is_null" = (CASE WHEN c.text is null THEN 1 ELSE 0 END), replace(replace(isnull(c.text,''),'(',''),')','') as vdefault, - ISNULL(p.is_primary_key, 0), a.is_identity as is_identity + ISNULL(p.is_primary_key, 0), a.is_identity as is_identity, a.collation_name from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id left join sys.syscomments c on a.default_object_id=c.id @@ -475,9 +475,10 @@ func (db *mssql) GetColumns(queryer core.Queryer, ctx context.Context, tableName colSeq := make([]string, 0) for rows.Next() { var name, ctype, vdefault string + var collation *string var maxLen, precision, scale int64 var nullable, isPK, defaultIsNull, isIncrement bool - err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &defaultIsNull, &vdefault, &isPK, &isIncrement) + err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &defaultIsNull, &vdefault, &isPK, &isIncrement, &collation) if err != nil { return nil, nil, err } @@ -499,6 +500,9 @@ func (db *mssql) GetColumns(queryer core.Queryer, ctx context.Context, tableName } else { col.Length = maxLen } + if collation != nil { + col.Collation = *collation + } switch ct { case "DATETIMEOFFSET": col.SQLType = schemas.SQLType{Name: schemas.TimeStampz, DefaultLength: 0, DefaultLength2: 0} @@ -646,7 +650,7 @@ func (db *mssql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table for i, colName := range table.ColumnsSeq() { col := table.GetColumn(colName) - s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1, true) b.WriteString(s) if i != len(table.ColumnsSeq())-1 { diff --git a/dialects/mysql.go b/dialects/mysql.go index b941a41b..5663d1dd 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -380,7 +380,7 @@ func (db *mysql) IsTableExist(queryer core.Queryer, ctx context.Context, tableNa func (db *mysql) AddColumnSQL(tableName string, col *schemas.Column) string { quoter := db.dialect.Quoter() - s, _ := ColumnString(db, col, true) + s, _ := ColumnString(db, col, true, true) var b strings.Builder b.WriteString("ALTER TABLE ") quoter.QuoteTo(&b, tableName) @@ -394,6 +394,12 @@ func (db *mysql) AddColumnSQL(tableName string, col *schemas.Column) string { return b.String() } +// ModifyColumnSQL returns a SQL to modify SQL +func (db *mysql) ModifyColumnSQL(tableName string, col *schemas.Column) string { + s, _ := ColumnString(db.dialect, col, false, true) + return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", db.quoter.Quote(tableName), s) +} + func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) { args := []interface{}{db.uri.DBName, tableName} alreadyQuoted := "(INSTR(VERSION(), 'maria') > 0 && " + @@ -404,7 +410,7 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName "SUBSTRING_INDEX(SUBSTRING(VERSION(), 6), '-', 1) >= 7)))))" s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," + " `COLUMN_KEY`, `EXTRA`, `COLUMN_COMMENT`, `CHARACTER_MAXIMUM_LENGTH`, " + - alreadyQuoted + " AS NEEDS_QUOTE " + + alreadyQuoted + " AS NEEDS_QUOTE, `COLLATION_NAME` " + "FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" + " ORDER BY `COLUMNS`.ORDINAL_POSITION ASC" @@ -422,8 +428,8 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName var columnName, nullableStr, colType, colKey, extra, comment string var alreadyQuoted, isUnsigned bool - var colDefault, maxLength *string - err = rows.Scan(&columnName, &nullableStr, &colDefault, &colType, &colKey, &extra, &comment, &maxLength, &alreadyQuoted) + var colDefault, maxLength, collation *string + err = rows.Scan(&columnName, &nullableStr, &colDefault, &colType, &colKey, &extra, &comment, &maxLength, &alreadyQuoted, &collation) if err != nil { return nil, nil, err } @@ -439,6 +445,9 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName } else { col.DefaultIsEmpty = true } + if collation != nil { + col.Collation = *collation + } fields := strings.Fields(colType) if len(fields) == 2 && fields[1] == "unsigned" { @@ -531,7 +540,7 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName func (db *mysql) GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) { args := []interface{}{db.uri.DBName} - s := "SELECT `TABLE_NAME`, `ENGINE`, `AUTO_INCREMENT`, `TABLE_COMMENT` from " + + s := "SELECT `TABLE_NAME`, `ENGINE`, `AUTO_INCREMENT`, `TABLE_COMMENT`, `TABLE_COLLATION` from " + "`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')" rows, err := queryer.QueryContext(ctx, s, args...) @@ -543,9 +552,9 @@ func (db *mysql) GetTables(queryer core.Queryer, ctx context.Context) ([]*schema tables := make([]*schemas.Table, 0) for rows.Next() { table := schemas.NewEmptyTable() - var name, engine string + var name, engine, collation string var autoIncr, comment *string - err = rows.Scan(&name, &engine, &autoIncr, &comment) + err = rows.Scan(&name, &engine, &autoIncr, &comment, &collation) if err != nil { return nil, err } @@ -555,6 +564,7 @@ func (db *mysql) GetTables(queryer core.Queryer, ctx context.Context) ([]*schema table.Comment = *comment } table.StoreEngine = engine + table.Collation = collation tables = append(tables, table) } if rows.Err() != nil { @@ -646,7 +656,7 @@ func (db *mysql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table for i, colName := range table.ColumnsSeq() { col := table.GetColumn(colName) - s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1, true) b.WriteString(s) if len(col.Comment) > 0 { diff --git a/dialects/oracle.go b/dialects/oracle.go index b0c5c38f..a5f8a5b2 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -628,7 +628,7 @@ func (db *oracle) CreateTableSQL(ctx context.Context, queryer core.Queryer, tabl /*if col.IsPrimaryKey && len(pkList) == 1 { sql += col.String(b.dialect) } else {*/ - s, _ := ColumnString(db, col, false) + s, _ := ColumnString(db, col, false, false) sql += s // } sql = strings.TrimSpace(sql) diff --git a/dialects/postgres.go b/dialects/postgres.go index 5efe54f4..942ab934 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -992,7 +992,7 @@ func (db *postgres) IsTableExist(queryer core.Queryer, ctx context.Context, tabl } func (db *postgres) AddColumnSQL(tableName string, col *schemas.Column) string { - s, _ := ColumnString(db.dialect, col, true) + s, _ := ColumnString(db.dialect, col, true, false) quoter := db.dialect.Quoter() addColumnSQL := "" diff --git a/integrations/session_schema_test.go b/integrations/schema_test.go similarity index 85% rename from integrations/session_schema_test.go rename to integrations/schema_test.go index 3212d027..149c6394 100644 --- a/integrations/session_schema_test.go +++ b/integrations/schema_test.go @@ -5,6 +5,7 @@ package integrations import ( + "errors" "fmt" "strings" "testing" @@ -325,14 +326,14 @@ func TestIsTableEmpty(t *testing.T) { type PictureEmpty struct { Id int64 - Url string `xorm:"unique"` //image's url + Url string `xorm:"unique"` // image's url Title string Description string Created time.Time `xorm:"created"` ILike int PageView int From_url string // nolint - Pre_url string `xorm:"unique"` //pre view image's url + Pre_url string `xorm:"unique"` // pre view image's url Uid int64 } @@ -458,7 +459,7 @@ func TestSync2_2(t *testing.T) { assert.NoError(t, PrepareEngine()) - var tableNames = make(map[string]bool) + tableNames := make(map[string]bool) for i := 0; i < 10; i++ { tableName := fmt.Sprintf("test_sync2_index_%d", i) tableNames[tableName] = true @@ -536,3 +537,111 @@ func TestModifyColum(t *testing.T) { _, err := testEngine.Exec(alterSQL) assert.NoError(t, err) } + +type TestCollateColumn struct { + Id int64 + UserId int64 `xorm:"unique(s)"` + Name string `xorm:"varchar(20) unique(s)"` + dbtype string `xorm:"-"` +} + +func (t TestCollateColumn) TableCollations() []*schemas.Collation { + if t.dbtype == string(schemas.MYSQL) { + return []*schemas.Collation{ + { + Name: "utf8mb4_general_ci", + Column: "name", + }, + } + } else if t.dbtype == string(schemas.MSSQL) { + return []*schemas.Collation{ + { + Name: "Latin1_General_CI_AS", + Column: "name", + }, + } + } + return nil +} + +func TestCollate(t *testing.T) { + assert.NoError(t, PrepareEngine()) + assertSync(t, &TestCollateColumn{ + dbtype: string(testEngine.Dialect().URI().DBType), + }) + + _, err := testEngine.Insert(&TestCollateColumn{ + UserId: 1, + Name: "test", + }) + assert.NoError(t, err) + _, err = testEngine.Insert(&TestCollateColumn{ + UserId: 1, + Name: "Test", + }) + if testEngine.Dialect().URI().DBType == schemas.MYSQL { + ver, err1 := testEngine.DBVersion() + assert.NoError(t, err1) + + tables, err1 := testEngine.DBMetas() + assert.NoError(t, err1) + for _, table := range tables { + if table.Name == "test_collate_column" { + col := table.GetColumn("name") + if col == nil { + assert.Error(t, errors.New("not found column")) + return + } + // tidb doesn't follow utf8mb4_general_ci + if col.Collation == "utf8mb4_general_ci" && ver.Edition != "TiDB" { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + break + } + } + } else if testEngine.Dialect().URI().DBType == schemas.MSSQL { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + + // Since SQLITE don't support modify column SQL, currrently just ignore + if testEngine.Dialect().URI().DBType != schemas.MYSQL && testEngine.Dialect().URI().DBType != schemas.MSSQL { + return + } + + var newCollation string + if testEngine.Dialect().URI().DBType == schemas.MYSQL { + newCollation = "utf8mb4_bin" + } else if testEngine.Dialect().URI().DBType != schemas.MSSQL { + newCollation = "Latin1_General_CS_AS" + } else { + return + } + + alterSQL := testEngine.Dialect().ModifyColumnSQL("test_collate_column", &schemas.Column{ + Name: "name", + SQLType: schemas.SQLType{ + Name: "VARCHAR", + }, + Length: 20, + Nullable: true, + DefaultIsEmpty: true, + Collation: newCollation, + }) + _, err = testEngine.Exec(alterSQL) + assert.NoError(t, err) + + _, err = testEngine.Insert(&TestCollateColumn{ + UserId: 1, + Name: "test1", + }) + assert.NoError(t, err) + _, err = testEngine.Insert(&TestCollateColumn{ + UserId: 1, + Name: "Test1", + }) + assert.NoError(t, err) +} diff --git a/schemas/collation.go b/schemas/collation.go new file mode 100644 index 00000000..acec5268 --- /dev/null +++ b/schemas/collation.go @@ -0,0 +1,10 @@ +// Copyright 2023 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 schemas + +type Collation struct { + Name string + Column string // blank means it's a table collation +} diff --git a/schemas/column.go b/schemas/column.go index 5a579e92..08d34b91 100644 --- a/schemas/column.go +++ b/schemas/column.go @@ -45,6 +45,7 @@ type Column struct { DisableTimeZone bool TimeZone *time.Location // column specified time zone Comment string + Collation string } // NewColumn creates a new column diff --git a/schemas/table.go b/schemas/table.go index 91b33e06..5c38cc70 100644 --- a/schemas/table.go +++ b/schemas/table.go @@ -27,6 +27,7 @@ type Table struct { StoreEngine string Charset string Comment string + Collation string } // NewEmptyTable creates an empty table @@ -36,7 +37,8 @@ func NewEmptyTable() *Table { // NewTable creates a new Table object func NewTable(name string, t reflect.Type) *Table { - return &Table{Name: name, Type: t, + return &Table{ + Name: name, Type: t, columnsSeq: make([]string, 0), columns: make([]*Column, 0), columnsMap: make(map[string][]*Column), diff --git a/tags/parser.go b/tags/parser.go index 028f8d0b..53ef0c10 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -31,6 +31,12 @@ type TableIndices interface { var tpTableIndices = reflect.TypeOf((*TableIndices)(nil)).Elem() +type TableCollations interface { + TableCollations() []*schemas.Collation +} + +var tpTableCollations = reflect.TypeOf((*TableCollations)(nil)).Elem() + // Parser represents a parser for xorm tag type Parser struct { identifier string @@ -356,6 +362,22 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) { } } + collations := tableCollations(v) + for _, collation := range collations { + if collation.Name == "" { + continue + } + if collation.Column == "" { + table.Collation = collation.Name + } else { + col := table.GetColumn(collation.Column) + if col == nil { + return nil, ErrUnsupportedType + } + col.Collation = collation.Name // this may override definition in struct tag + } + } + return table, nil } @@ -377,3 +399,22 @@ func tableIndices(v reflect.Value) []*schemas.Index { } return nil } + +func tableCollations(v reflect.Value) []*schemas.Collation { + if v.Type().Implements(tpTableCollations) { + return v.Interface().(TableCollations).TableCollations() + } + + if v.Kind() == reflect.Ptr { + v = v.Elem() + if v.Type().Implements(tpTableCollations) { + return v.Interface().(TableCollations).TableCollations() + } + } else if v.CanAddr() { + v1 := v.Addr() + if v1.Type().Implements(tpTableCollations) { + return v1.Interface().(TableCollations).TableCollations() + } + } + return nil +} diff --git a/tags/tag.go b/tags/tag.go index 41d525e1..024c9c18 100644 --- a/tags/tag.go +++ b/tags/tag.go @@ -123,6 +123,7 @@ var defaultTagHandlers = map[string]Handler{ "COMMENT": CommentTagHandler, "EXTENDS": ExtendsTagHandler, "UNSIGNED": UnsignedTagHandler, + "COLLATE": CollateTagHandler, } func init() { @@ -282,6 +283,16 @@ func CommentTagHandler(ctx *Context) error { return nil } +func CollateTagHandler(ctx *Context) error { + if len(ctx.params) > 0 { + ctx.col.Collation = ctx.params[0] + } else { + ctx.col.Collation = ctx.nextTag + ctx.ignoreNext = true + } + return nil +} + // SQLTypeTagHandler describes SQL Type tag handler func SQLTypeTagHandler(ctx *Context) error { ctx.col.SQLType = schemas.SQLType{Name: ctx.tagUname} diff --git a/tags/tag_test.go b/tags/tag_test.go index 3ceeefd1..6c456f2a 100644 --- a/tags/tag_test.go +++ b/tags/tag_test.go @@ -11,68 +11,84 @@ import ( ) func TestSplitTag(t *testing.T) { - var cases = []struct { + cases := []struct { tag string tags []tag }{ - {"not null default '2000-01-01 00:00:00' TIMESTAMP", []tag{ - { - name: "not", - }, - { - name: "null", - }, - { - name: "default", - }, - { - name: "'2000-01-01 00:00:00'", - }, - { - name: "TIMESTAMP", - }, - }, - }, - {"TEXT", []tag{ - { - name: "TEXT", - }, - }, - }, - {"default('2000-01-01 00:00:00')", []tag{ - { - name: "default", - params: []string{ - "'2000-01-01 00:00:00'", + { + "not null default '2000-01-01 00:00:00' TIMESTAMP", []tag{ + { + name: "not", + }, + { + name: "null", + }, + { + name: "default", + }, + { + name: "'2000-01-01 00:00:00'", + }, + { + name: "TIMESTAMP", }, }, }, - }, - {"json binary", []tag{ - { - name: "json", - }, - { - name: "binary", + { + "TEXT", []tag{ + { + name: "TEXT", + }, }, }, - }, - {"numeric(10, 2)", []tag{ - { - name: "numeric", - params: []string{"10", "2"}, + { + "default('2000-01-01 00:00:00')", []tag{ + { + name: "default", + params: []string{ + "'2000-01-01 00:00:00'", + }, + }, }, }, - }, - {"numeric(10, 2) notnull", []tag{ - { - name: "numeric", - params: []string{"10", "2"}, - }, - { - name: "notnull", + { + "json binary", []tag{ + { + name: "json", + }, + { + name: "binary", + }, }, }, + { + "numeric(10, 2)", []tag{ + { + name: "numeric", + params: []string{"10", "2"}, + }, + }, + }, + { + "numeric(10, 2) notnull", []tag{ + { + name: "numeric", + params: []string{"10", "2"}, + }, + { + name: "notnull", + }, + }, + }, + { + "collate utf8mb4_bin", []tag{ + { + name: "collate", + }, + { + name: "utf8mb4_bin", + }, + }, }, } @@ -82,7 +98,7 @@ func TestSplitTag(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, len(tags), len(kase.tags)) for i := 0; i < len(tags); i++ { - assert.Equal(t, tags[i], kase.tags[i]) + assert.Equal(t, kase.tags[i], tags[i]) } }) } From 52b01ce67fe94ddaa0411679d55c308858acb51a Mon Sep 17 00:00:00 2001 From: flyingpigge Date: Mon, 3 Jul 2023 09:15:45 +0000 Subject: [PATCH 090/116] chore: ignore unnecessary char type cast in GetColumns (#2278) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PostgreSQL有两种char类型:`character`和`"char"`: https://www.postgresql.org/docs/current/datatype-character.html. `pg_class`里的`relkind`是`"char"`类型,所以`GetColumns`这里应该转成`::"char"`或者和本PR里请求的一样去掉转换。这样对于PostgreSQL以及其他兼容PostgreSQL的数据库容错性更好,在做比较时它们通常都会被隐式转换。 ```sql postgres=# select pg_typeof(relkind), pg_typeof('a'::char), pg_typeof('a'::"char") from pg_class limit 1; pg_typeof | pg_typeof | pg_typeof -----------+-----------+----------- "char" | character | "char" (1 row) ``` Co-authored-by: August Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2278 Co-authored-by: flyingpigge Co-committed-by: flyingpigge --- dialects/postgres.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dialects/postgres.go b/dialects/postgres.go index 942ab934..28196891 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -1078,7 +1078,7 @@ FROM pg_attribute f LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey) LEFT JOIN pg_class AS g ON p.confrelid = g.oid LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name -WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.attnum;` +WHERE n.nspname= s.table_schema AND c.relkind = 'r' AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.attnum;` schema := db.getSchema() if schema != "" { From 486c344ba3750a9d2293372ad4e6f510e3371bf3 Mon Sep 17 00:00:00 2001 From: brookechen Date: Tue, 11 Jul 2023 17:10:36 +0000 Subject: [PATCH 091/116] =?UTF-8?q?In=20SQLite3,=20Sync=20doesn't=20suppor?= =?UTF-8?q?t=20Modify=20Column=EF=BC=9AError:=20near=20MODIFY:=20syntax=20?= =?UTF-8?q?error=20(#2267)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rebase Co-authored-by: brookechen Co-authored-by: brookechen Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2267 Co-authored-by: brookechen Co-committed-by: brookechen --- dialects/mysql.go | 3 +++ integrations/engine_test.go | 42 +++++++++++++++++++++++++++++++++++++ sync.go | 5 ++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/dialects/mysql.go b/dialects/mysql.go index 5663d1dd..82505707 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -397,6 +397,9 @@ func (db *mysql) AddColumnSQL(tableName string, col *schemas.Column) string { // ModifyColumnSQL returns a SQL to modify SQL func (db *mysql) ModifyColumnSQL(tableName string, col *schemas.Column) string { s, _ := ColumnString(db.dialect, col, false, true) + if col.Comment != "" { + s += fmt.Sprintf(" COMMENT '%s'", col.Comment) + } return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", db.quoter.Quote(tableName), s) } diff --git a/integrations/engine_test.go b/integrations/engine_test.go index 730a424e..86ed7344 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -289,6 +289,48 @@ func TestGetColumnsComment(t *testing.T) { assert.Zero(t, noComment) } +type TestCommentUpdate struct { + HasComment int `xorm:"bigint comment('this is a comment before update')"` +} + +func (m *TestCommentUpdate) TableName() string { + return "test_comment_struct" +} + +type TestCommentUpdate2 struct { + HasComment int `xorm:"bigint comment('this is a comment after update')"` +} + +func (m *TestCommentUpdate2) TableName() string { + return "test_comment_struct" +} + +func TestColumnCommentUpdate(t *testing.T) { + comment := "this is a comment after update" + assertSync(t, new(TestCommentUpdate)) + assert.NoError(t, testEngine.Sync2(new(TestCommentUpdate2))) // modify table column comment + + switch testEngine.Dialect().URI().DBType { + case schemas.POSTGRES, schemas.MYSQL: // only postgres / mysql dialect implement the feature of modify comment in postgres.ModifyColumnSQL + default: + t.Skip() + return + } + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + tableName := "test_comment_struct" + var hasComment string + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn(testEngine.GetColumnMapper().Obj2Table("HasComment")) + assert.NotNil(t, col) + hasComment = col.Comment + break + } + } + assert.Equal(t, comment, hasComment) +} + func TestGetColumnsLength(t *testing.T) { var max_length int64 switch testEngine.Dialect().URI().DBType { diff --git a/sync.go b/sync.go index 6583e341..11e75404 100644 --- a/sync.go +++ b/sync.go @@ -181,7 +181,10 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...interface{}) } } } else if col.Comment != oriCol.Comment { - _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) + if engine.dialect.URI().DBType == schemas.POSTGRES || + engine.dialect.URI().DBType == schemas.MYSQL { + _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) + } } if col.Default != oriCol.Default { From 79a8bc804b75beca6a31d0f1a3ffea8a85540447 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 12 Jul 2023 02:01:56 +0000 Subject: [PATCH 092/116] Fix join problem (#2291) Fix #2284 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2291 --- integrations/session_find_test.go | 41 ++++++++ internal/statements/join.go | 157 ++++++++++++++++-------------- internal/statements/query.go | 22 ++--- internal/statements/select.go | 4 +- internal/statements/statement.go | 23 +++-- rows.go | 10 +- session.go | 2 +- session_find.go | 2 +- 8 files changed, 161 insertions(+), 100 deletions(-) diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index 5c2a4c68..65df5aee 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -12,6 +12,7 @@ import ( "xorm.io/xorm" "xorm.io/xorm/internal/utils" "xorm.io/xorm/names" + "xorm.io/xorm/schemas" "github.com/stretchr/testify/assert" ) @@ -1196,3 +1197,43 @@ func TestUpdateFindDate(t *testing.T) { assert.EqualValues(t, 1, len(tufs)) assert.EqualValues(t, tuf.Tm.Format("2006-01-02"), tufs[0].Tm.Format("2006-01-02")) } + +func TestBuilderDialect(t *testing.T) { + assert.NoError(t, PrepareEngine()) + + type TestBuilderDialect struct { + Id int64 + Name string `xorm:"index"` + Age2 int + } + + type TestBuilderDialectFoo struct { + Id int64 + DialectId int64 `xorm:"index"` + Age int + } + + assertSync(t, new(TestBuilderDialect), new(TestBuilderDialectFoo)) + + session := testEngine.NewSession() + defer session.Close() + + var dialect string + switch testEngine.Dialect().URI().DBType { + case schemas.MYSQL: + dialect = builder.MYSQL + case schemas.MSSQL: + dialect = builder.MSSQL + case schemas.POSTGRES: + dialect = builder.POSTGRES + case schemas.SQLITE: + dialect = builder.SQLITE + } + + tbName := testEngine.TableName(new(TestBuilderDialectFoo), dialect == builder.POSTGRES) + + inner := builder.Dialect(dialect).Select("*").From(tbName).Where(builder.Eq{"age": 20}) + result := make([]*TestBuilderDialect, 0, 10) + err := testEngine.Table("test_builder_dialect").Where(builder.Eq{"age2": 2}).Join("INNER", inner, "test_builder_dialect_foo.dialect_id = test_builder_dialect.id").Find(&result) + assert.NoError(t, err) +} diff --git a/internal/statements/join.go b/internal/statements/join.go index adf349e7..61a1b4de 100644 --- a/internal/statements/join.go +++ b/internal/statements/join.go @@ -15,82 +15,97 @@ import ( ) // Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (statement *Statement) Join(joinOP string, tablename interface{}, condition interface{}, args ...interface{}) *Statement { - var buf strings.Builder - if len(statement.JoinStr) > 0 { - fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) - } else { - fmt.Fprintf(&buf, "%v JOIN ", joinOP) - } - - condStr := "" - condArgs := []interface{}{} - switch condTp := condition.(type) { - case string: - condStr = condTp - case builder.Cond: - var err error - condStr, condArgs, err = builder.ToSQL(condTp) - if err != nil { - statement.LastError = err - return statement - } - default: - statement.LastError = fmt.Errorf("unsupported join condition type: %v", condTp) - return statement - } - - switch tp := tablename.(type) { - case builder.Builder: - subSQL, subQueryArgs, err := tp.ToSQL() - if err != nil { - statement.LastError = err - return statement - } - - fields := strings.Split(tp.TableName(), ".") - aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) - aliasName = schemas.CommonQuoter.Trim(aliasName) - - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condStr)) - statement.joinArgs = append(append(statement.joinArgs, subQueryArgs...), condArgs...) - case *builder.Builder: - subSQL, subQueryArgs, err := tp.ToSQL() - if err != nil { - statement.LastError = err - return statement - } - - fields := strings.Split(tp.TableName(), ".") - aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) - aliasName = schemas.CommonQuoter.Trim(aliasName) - - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condStr)) - statement.joinArgs = append(append(statement.joinArgs, subQueryArgs...), condArgs...) - default: - tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true) - if !utils.IsSubQuery(tbName) { - var buf strings.Builder - _ = statement.dialect.Quoter().QuoteTo(&buf, tbName) - tbName = buf.String() - } else { - tbName = statement.ReplaceQuote(tbName) - } - fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condStr)) - statement.joinArgs = append(statement.joinArgs, condArgs...) - } - - statement.JoinStr = buf.String() - statement.joinArgs = append(statement.joinArgs, args...) +func (statement *Statement) Join(joinOP string, joinTable interface{}, condition interface{}, args ...interface{}) *Statement { + statement.joins = append(statement.joins, join{ + op: joinOP, + table: joinTable, + condition: condition, + args: args, + }) return statement } -func (statement *Statement) writeJoin(w builder.Writer) error { - if statement.JoinStr != "" { - if _, err := fmt.Fprint(w, " ", statement.JoinStr); err != nil { +func (statement *Statement) writeJoins(w *builder.BytesWriter) error { + for _, join := range statement.joins { + if err := statement.writeJoin(w, join); err != nil { return err } - w.Append(statement.joinArgs...) } return nil } + +func (statement *Statement) writeJoin(buf *builder.BytesWriter, join join) error { + // write join operator + if _, err := fmt.Fprintf(buf, " %v JOIN", join.op); err != nil { + return err + } + + // write join table or subquery + switch tp := join.table.(type) { + case builder.Builder: + if _, err := fmt.Fprintf(buf, " ("); err != nil { + return err + } + if err := tp.WriteTo(statement.QuoteReplacer(buf)); err != nil { + return err + } + + fields := strings.Split(tp.TableName(), ".") + aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) + aliasName = schemas.CommonQuoter.Trim(aliasName) + + if _, err := fmt.Fprintf(buf, ") %s", statement.quote(aliasName)); err != nil { + return err + } + case *builder.Builder: + if _, err := fmt.Fprintf(buf, " ("); err != nil { + return err + } + if err := tp.WriteTo(statement.QuoteReplacer(buf)); err != nil { + return err + } + + fields := strings.Split(tp.TableName(), ".") + aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) + aliasName = schemas.CommonQuoter.Trim(aliasName) + + if _, err := fmt.Fprintf(buf, ") %s", statement.quote(aliasName)); err != nil { + return err + } + default: + tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), join.table, true) + if !utils.IsSubQuery(tbName) { + var sb strings.Builder + if err := statement.dialect.Quoter().QuoteTo(&sb, tbName); err != nil { + return err + } + tbName = sb.String() + } else { + tbName = statement.ReplaceQuote(tbName) + } + if _, err := fmt.Fprint(buf, " ", tbName); err != nil { + return err + } + } + + // write on condition + if _, err := fmt.Fprint(buf, " ON "); err != nil { + return err + } + + switch condTp := join.condition.(type) { + case string: + if _, err := fmt.Fprint(buf, statement.ReplaceQuote(condTp)); err != nil { + return err + } + case builder.Cond: + if err := condTp.WriteTo(statement.QuoteReplacer(buf)); err != nil { + return err + } + default: + return fmt.Errorf("unsupported join condition type: %v", condTp) + } + buf.Append(join.args...) + + return nil +} diff --git a/internal/statements/query.go b/internal/statements/query.go index f72c8602..ca106208 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -33,7 +33,7 @@ func (statement *Statement) GenQuerySQL(sqlOrArgs ...interface{}) (string, []int if len(statement.SelectStr) > 0 { columnStr = statement.SelectStr } else { - if statement.JoinStr == "" { + if len(statement.joins) == 0 { if columnStr == "" { if statement.GroupByStr != "" { columnStr = statement.quoteColumnStr(statement.GroupByStr) @@ -108,7 +108,7 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{}, columnStr = statement.SelectStr } else { // TODO: always generate column names, not use * even if join - if len(statement.JoinStr) == 0 { + if len(statement.joins) == 0 { if len(columnStr) == 0 { if len(statement.GroupByStr) > 0 { columnStr = statement.quoteColumnStr(statement.GroupByStr) @@ -188,7 +188,7 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa return sqlStr, condArgs, nil } -func (statement *Statement) writeFrom(w builder.Writer) error { +func (statement *Statement) writeFrom(w *builder.BytesWriter) error { if _, err := fmt.Fprint(w, " FROM "); err != nil { return err } @@ -198,7 +198,7 @@ func (statement *Statement) writeFrom(w builder.Writer) error { if err := statement.writeAlias(w); err != nil { return err } - return statement.writeJoin(w) + return statement.writeJoins(w) } func (statement *Statement) writeLimitOffset(w builder.Writer) error { @@ -263,7 +263,7 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB } else { column = statement.RefTable.PKColumns()[0].Name } - if statement.needTableName() { + if statement.NeedTableName() { if len(statement.TableAlias) > 0 { column = fmt.Sprintf("%s.%s", statement.TableAlias, column) } else { @@ -291,7 +291,7 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB return "", nil, err } } - if err := statement.WriteGroupBy(mssqlCondi); err != nil { + if err := statement.writeGroupBy(mssqlCondi); err != nil { return "", nil, err } if _, err := fmt.Fprint(mssqlCondi, "))"); err != nil { @@ -331,7 +331,7 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB } } - if err := statement.WriteGroupBy(buf); err != nil { + if err := statement.writeGroupBy(buf); err != nil { return "", nil, err } if err := statement.writeHaving(buf); err != nil { @@ -402,7 +402,7 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac if _, err := fmt.Fprintf(buf, "SELECT TOP 1 * FROM %s", tableName); err != nil { return "", nil, err } - if err := statement.writeJoin(buf); err != nil { + if err := statement.writeJoins(buf); err != nil { return "", nil, err } if statement.Conds().IsValid() { @@ -417,7 +417,7 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac if _, err := fmt.Fprintf(buf, "SELECT * FROM %s", tableName); err != nil { return "", nil, err } - if err := statement.writeJoin(buf); err != nil { + if err := statement.writeJoins(buf); err != nil { return "", nil, err } if _, err := fmt.Fprintf(buf, " WHERE "); err != nil { @@ -438,7 +438,7 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac if _, err := fmt.Fprintf(buf, "SELECT 1 FROM %s", tableName); err != nil { return "", nil, err } - if err := statement.writeJoin(buf); err != nil { + if err := statement.writeJoins(buf); err != nil { return "", nil, err } if statement.Conds().IsValid() { @@ -471,7 +471,7 @@ func (statement *Statement) GenFindSQL(autoCond builder.Cond) (string, []interfa if len(statement.SelectStr) > 0 { columnStr = statement.SelectStr } else { - if statement.JoinStr == "" { + if len(statement.joins) == 0 { if columnStr == "" { if statement.GroupByStr != "" { columnStr = statement.quoteColumnStr(statement.GroupByStr) diff --git a/internal/statements/select.go b/internal/statements/select.go index 2bd2e94d..59161d76 100644 --- a/internal/statements/select.go +++ b/internal/statements/select.go @@ -102,7 +102,7 @@ func (statement *Statement) genColumnStr() string { buf.WriteString(", ") } - if statement.JoinStr != "" { + if len(statement.joins) > 0 { if statement.TableAlias != "" { buf.WriteString(statement.TableAlias) } else { @@ -119,7 +119,7 @@ func (statement *Statement) genColumnStr() string { } func (statement *Statement) colName(col *schemas.Column, tableName string) string { - if statement.needTableName() { + if statement.NeedTableName() { nm := tableName if len(statement.TableAlias) > 0 { nm = statement.TableAlias diff --git a/internal/statements/statement.go b/internal/statements/statement.go index a8fe34fa..ae38ca27 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -34,6 +34,13 @@ var ( ErrTableNotFound = errors.New("Table not found") ) +type join struct { + op string + table interface{} + condition interface{} + args []interface{} +} + // Statement save all the sql info for executing SQL type Statement struct { RefTable *schemas.Table @@ -45,8 +52,7 @@ type Statement struct { idParam schemas.PK orderStr string orderArgs []interface{} - JoinStr string - joinArgs []interface{} + joins []join GroupByStr string HavingStr string SelectStr string @@ -123,8 +129,7 @@ func (statement *Statement) Reset() { statement.LimitN = nil statement.ResetOrderBy() statement.UseCascade = true - statement.JoinStr = "" - statement.joinArgs = make([]interface{}, 0) + statement.joins = nil statement.GroupByStr = "" statement.HavingStr = "" statement.ColumnMap = columnMap{} @@ -205,8 +210,8 @@ func (statement *Statement) SetRefBean(bean interface{}) error { return nil } -func (statement *Statement) needTableName() bool { - return len(statement.JoinStr) > 0 +func (statement *Statement) NeedTableName() bool { + return len(statement.joins) > 0 } // Incr Generate "Update ... Set column = column + arg" statement @@ -290,7 +295,7 @@ func (statement *Statement) GroupBy(keys string) *Statement { return statement } -func (statement *Statement) WriteGroupBy(w builder.Writer) error { +func (statement *Statement) writeGroupBy(w builder.Writer) error { if statement.GroupByStr == "" { return nil } @@ -605,7 +610,7 @@ func (statement *Statement) BuildConds(table *schemas.Table, bean interface{}, i // MergeConds merge conditions from bean and id func (statement *Statement) MergeConds(bean interface{}) error { if !statement.NoAutoCondition && statement.RefTable != nil { - addedTableName := (len(statement.JoinStr) > 0) + addedTableName := (len(statement.joins) > 0) autoCond, err := statement.BuildConds(statement.RefTable, bean, true, true, false, true, addedTableName) if err != nil { return err @@ -673,7 +678,7 @@ func (statement *Statement) joinColumns(cols []*schemas.Column, includeTableName // CondDeleted returns the conditions whether a record is soft deleted. func (statement *Statement) CondDeleted(col *schemas.Column) builder.Cond { colName := statement.quote(col.Name) - if statement.JoinStr != "" { + if len(statement.joins) > 0 { var prefix string if statement.TableAlias != "" { prefix = statement.TableAlias diff --git a/rows.go b/rows.go index 4801c300..ef3e42b6 100644 --- a/rows.go +++ b/rows.go @@ -46,8 +46,8 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { if rows.session.statement.RawSQL == "" { var autoCond builder.Cond - var addedTableName = (len(session.statement.JoinStr) > 0) - var table = rows.session.statement.RefTable + addedTableName := session.statement.NeedTableName() + table := rows.session.statement.RefTable if !session.statement.NoAutoCondition { var err error @@ -103,12 +103,12 @@ func (rows *Rows) Scan(beans ...interface{}) error { return rows.Err() } - var bean = beans[0] - var tp = reflect.TypeOf(bean) + bean := beans[0] + tp := reflect.TypeOf(bean) if tp.Kind() == reflect.Ptr { tp = tp.Elem() } - var beanKind = tp.Kind() + beanKind := tp.Kind() if len(beans) == 1 { if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { diff --git a/session.go b/session.go index e1a16e5b..af6e4921 100644 --- a/session.go +++ b/session.go @@ -354,7 +354,7 @@ func (session *Session) DB() *core.DB { func (session *Session) canCache() bool { if session.statement.RefTable == nil || - session.statement.JoinStr != "" || + session.statement.NeedTableName() || session.statement.RawSQL != "" || !session.statement.UseCache || session.statement.IsForUpdate || diff --git a/session_find.go b/session_find.go index 3341eafe..d9444aee 100644 --- a/session_find.go +++ b/session_find.go @@ -114,7 +114,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) var ( table = session.statement.RefTable - addedTableName = (len(session.statement.JoinStr) > 0) + addedTableName = session.statement.NeedTableName() autoCond builder.Cond ) if tp == tpStruct { From f1f5e7cd1abc07e924e4395bd4eafe461f740b2a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 12 Jul 2023 07:53:25 +0000 Subject: [PATCH 093/116] Some refactor (#2292) replace #2285 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2292 --- dialects/dialect.go | 7 - dialects/mssql.go | 4 - dialects/sqlite3.go | 10 +- integrations/session_count_test.go | 6 +- internal/statements/query.go | 405 +++++++++++++++-------------- internal/statements/update.go | 10 +- 6 files changed, 228 insertions(+), 214 deletions(-) diff --git a/dialects/dialect.go b/dialects/dialect.go index d1c5f200..8e512c4f 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -85,8 +85,6 @@ type Dialect interface { AddColumnSQL(tableName string, col *schemas.Column) string ModifyColumnSQL(tableName string, col *schemas.Column) string - ForUpdateSQL(query string) string - Filters() []Filter SetParams(params map[string]string) } @@ -245,11 +243,6 @@ func (db *Base) ModifyColumnSQL(tableName string, col *schemas.Column) string { return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", db.quoter.Quote(tableName), s) } -// ForUpdateSQL returns for updateSQL -func (db *Base) ForUpdateSQL(query string) string { - return query + " FOR UPDATE" -} - // SetParams set params func (db *Base) SetParams(params map[string]string) { } diff --git a/dialects/mssql.go b/dialects/mssql.go index e517e688..dcac9c3f 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -669,10 +669,6 @@ func (db *mssql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table return b.String(), true, nil } -func (db *mssql) ForUpdateSQL(query string) string { - return query -} - func (db *mssql) Filters() []Filter { return []Filter{} } diff --git a/dialects/sqlite3.go b/dialects/sqlite3.go index 4ff9a39e..62519b6b 100644 --- a/dialects/sqlite3.go +++ b/dialects/sqlite3.go @@ -193,11 +193,11 @@ func (db *sqlite3) Features() *DialectFeatures { func (db *sqlite3) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: - var q = sqlite3Quoter + q := sqlite3Quoter q.IsReserved = schemas.AlwaysNoReserve db.quoter = q case QuotePolicyReserved: - var q = sqlite3Quoter + q := sqlite3Quoter q.IsReserved = db.IsReserved db.quoter = q case QuotePolicyAlways: @@ -291,10 +291,6 @@ func (db *sqlite3) DropIndexSQL(tableName string, index *schemas.Index) string { return fmt.Sprintf("DROP INDEX %v", db.Quoter().Quote(idxName)) } -func (db *sqlite3) ForUpdateSQL(query string) string { - return query -} - func (db *sqlite3) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName, colName string) (bool, error) { query := "SELECT * FROM " + tableName + " LIMIT 0" rows, err := queryer.QueryContext(ctx, query) @@ -320,7 +316,7 @@ func (db *sqlite3) IsColumnExist(queryer core.Queryer, ctx context.Context, tabl // splitColStr splits a sqlite col strings as fields func splitColStr(colStr string) []string { colStr = strings.TrimSpace(colStr) - var results = make([]string, 0, 10) + results := make([]string, 0, 10) var lastIdx int var hasC, hasQuote bool for i, c := range colStr { diff --git a/integrations/session_count_test.go b/integrations/session_count_test.go index 13d84edb..079602c3 100644 --- a/integrations/session_count_test.go +++ b/integrations/session_count_test.go @@ -89,7 +89,7 @@ func TestCountWithOthers(t *testing.T) { }) assert.NoError(t, err) - total, err := testEngine.OrderBy("`id` desc").Limit(1).Count(new(CountWithOthers)) + total, err := testEngine.OrderBy("count(`id`) desc").Limit(1).Count(new(CountWithOthers)) assert.NoError(t, err) assert.EqualValues(t, 2, total) } @@ -118,11 +118,11 @@ func TestWithTableName(t *testing.T) { }) assert.NoError(t, err) - total, err := testEngine.OrderBy("`id` desc").Count(new(CountWithTableName)) + total, err := testEngine.OrderBy("count(`id`) desc").Count(new(CountWithTableName)) assert.NoError(t, err) assert.EqualValues(t, 2, total) - total, err = testEngine.OrderBy("`id` desc").Count(CountWithTableName{}) + total, err = testEngine.OrderBy("count(`id`) desc").Count(CountWithTableName{}) assert.NoError(t, err) assert.EqualValues(t, 2, total) } diff --git a/internal/statements/query.go b/internal/statements/query.go index ca106208..cea8be6d 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -7,6 +7,7 @@ package statements import ( "errors" "fmt" + "io" "reflect" "strings" @@ -29,37 +30,15 @@ func (statement *Statement) GenQuerySQL(sqlOrArgs ...interface{}) (string, []int return "", nil, ErrTableNotFound } - columnStr := statement.ColumnStr() - if len(statement.SelectStr) > 0 { - columnStr = statement.SelectStr - } else { - if len(statement.joins) == 0 { - if columnStr == "" { - if statement.GroupByStr != "" { - columnStr = statement.quoteColumnStr(statement.GroupByStr) - } else { - columnStr = statement.genColumnStr() - } - } - } else { - if columnStr == "" { - if statement.GroupByStr != "" { - columnStr = statement.quoteColumnStr(statement.GroupByStr) - } else { - columnStr = "*" - } - } - } - if columnStr == "" { - columnStr = "*" - } - } - if err := statement.ProcessIDParam(); err != nil { return "", nil, err } - return statement.genSelectSQL(columnStr, true, true) + buf := builder.NewWriter() + if err := statement.writeSelect(buf, statement.genSelectColumnStr(), true); err != nil { + return "", nil, err + } + return buf.String(), buf.Args(), nil } // GenSumSQL generates sum SQL @@ -81,13 +60,16 @@ func (statement *Statement) GenSumSQL(bean interface{}, columns ...string) (stri } sumStrs = append(sumStrs, fmt.Sprintf("COALESCE(sum(%s),0)", colName)) } - sumSelect := strings.Join(sumStrs, ", ") if err := statement.MergeConds(bean); err != nil { return "", nil, err } - return statement.genSelectSQL(sumSelect, true, true) + buf := builder.NewWriter() + if err := statement.writeSelect(buf, strings.Join(sumStrs, ", "), true); err != nil { + return "", nil, err + } + return buf.String(), buf.Args(), nil } // GenGetSQL generates Get SQL @@ -139,7 +121,11 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{}, } } - return statement.genSelectSQL(columnStr, true, true) + buf := builder.NewWriter() + if err := statement.writeSelect(buf, columnStr, true); err != nil { + return "", nil, err + } + return buf.String(), buf.Args(), nil } // GenCountSQL generates the SQL for counting @@ -148,8 +134,6 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa return statement.GenRawSQL(), statement.RawParams, nil } - var condArgs []interface{} - var err error if len(beans) > 0 { if err := statement.SetRefBean(beans[0]); err != nil { return "", nil, err @@ -176,16 +160,24 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa subQuerySelect = selectSQL } - sqlStr, condArgs, err := statement.genSelectSQL(subQuerySelect, false, false) - if err != nil { + buf := builder.NewWriter() + if statement.GroupByStr != "" { + if _, err := fmt.Fprintf(buf, "SELECT %s FROM (", selectSQL); err != nil { + return "", nil, err + } + } + + if err := statement.writeSelect(buf, subQuerySelect, false); err != nil { return "", nil, err } if statement.GroupByStr != "" { - sqlStr = fmt.Sprintf("SELECT %s FROM (%s) sub", selectSQL, sqlStr) + if _, err := fmt.Fprintf(buf, ") sub"); err != nil { + return "", nil, err + } } - return sqlStr, condArgs, nil + return buf.String(), buf.Args(), nil } func (statement *Statement) writeFrom(w *builder.BytesWriter) error { @@ -218,153 +210,183 @@ func (statement *Statement) writeLimitOffset(w builder.Writer) error { return nil } -func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderBy bool) (string, []interface{}, error) { - var ( - distinct string - dialect = statement.dialect - top, whereStr string - mssqlCondi = builder.NewWriter() - ) +func (statement *Statement) writeTop(w builder.Writer) error { + if statement.dialect.URI().DBType != schemas.MSSQL { + return nil + } + if statement.LimitN == nil { + return nil + } + _, err := fmt.Fprintf(w, " TOP %d", *statement.LimitN) + return err +} - if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { - distinct = "DISTINCT " +func (statement *Statement) writeDistinct(w builder.Writer) error { + if statement.IsDistinct && !strings.HasPrefix(statement.SelectStr, "count(") { + _, err := fmt.Fprint(w, " DISTINCT") + return err + } + return nil +} + +func (statement *Statement) writeSelectColumns(w *builder.BytesWriter, columnStr string) error { + if _, err := fmt.Fprintf(w, "SELECT "); err != nil { + return err + } + if err := statement.writeDistinct(w); err != nil { + return err + } + if err := statement.writeTop(w); err != nil { + return err + } + _, err := fmt.Fprint(w, " ", columnStr) + return err +} + +func (statement *Statement) writeWhere(w *builder.BytesWriter) error { + if !statement.cond.IsValid() { + return statement.writeMssqlPaginationCond(w) + } + if _, err := fmt.Fprint(w, " WHERE "); err != nil { + return err + } + if err := statement.cond.WriteTo(statement.QuoteReplacer(w)); err != nil { + return err + } + return statement.writeMssqlPaginationCond(w) +} + +func (statement *Statement) writeForUpdate(w io.Writer) error { + if !statement.IsForUpdate { + return nil } - condWriter := builder.NewWriter() - if err := statement.cond.WriteTo(statement.QuoteReplacer(condWriter)); err != nil { - return "", nil, err + if statement.dialect.URI().DBType != schemas.MYSQL { + return errors.New("only support mysql for update") + } + _, err := fmt.Fprint(w, " FOR UPDATE") + return err +} + +func (statement *Statement) writeMssqlPaginationCond(w *builder.BytesWriter) error { + if statement.dialect.URI().DBType != schemas.MSSQL || statement.Start <= 0 { + return nil } - if condWriter.Len() > 0 { - whereStr = " WHERE " + if statement.RefTable == nil { + return errors.New("unsupported query limit without reference table") } - pLimitN := statement.LimitN - if dialect.URI().DBType == schemas.MSSQL { - if pLimitN != nil { - LimitNValue := *pLimitN - top = fmt.Sprintf("TOP %d ", LimitNValue) + var column string + if len(statement.RefTable.PKColumns()) == 0 { + for _, index := range statement.RefTable.Indexes { + if len(index.Cols) == 1 { + column = index.Cols[0] + break + } } - if statement.Start > 0 { - if statement.RefTable == nil { - return "", nil, errors.New("Unsupported query limit without reference table") - } - var column string - if len(statement.RefTable.PKColumns()) == 0 { - for _, index := range statement.RefTable.Indexes { - if len(index.Cols) == 1 { - column = index.Cols[0] - break - } - } - if len(column) == 0 { - column = statement.RefTable.ColumnsSeq()[0] - } - } else { - column = statement.RefTable.PKColumns()[0].Name - } - if statement.NeedTableName() { - if len(statement.TableAlias) > 0 { - column = fmt.Sprintf("%s.%s", statement.TableAlias, column) - } else { - column = fmt.Sprintf("%s.%s", statement.TableName(), column) - } - } - - if _, err := fmt.Fprintf(mssqlCondi, "(%s NOT IN (SELECT TOP %d %s", - column, statement.Start, column); err != nil { - return "", nil, err - } - if err := statement.writeFrom(mssqlCondi); err != nil { - return "", nil, err - } - if whereStr != "" { - if _, err := fmt.Fprint(mssqlCondi, whereStr); err != nil { - return "", nil, err - } - if err := utils.WriteBuilder(mssqlCondi, statement.QuoteReplacer(condWriter)); err != nil { - return "", nil, err - } - } - if needOrderBy { - if err := statement.WriteOrderBy(mssqlCondi); err != nil { - return "", nil, err - } - } - if err := statement.writeGroupBy(mssqlCondi); err != nil { - return "", nil, err - } - if _, err := fmt.Fprint(mssqlCondi, "))"); err != nil { - return "", nil, err - } + if len(column) == 0 { + column = statement.RefTable.ColumnsSeq()[0] + } + } else { + column = statement.RefTable.PKColumns()[0].Name + } + if statement.NeedTableName() { + if len(statement.TableAlias) > 0 { + column = fmt.Sprintf("%s.%s", statement.TableAlias, column) + } else { + column = fmt.Sprintf("%s.%s", statement.TableName(), column) } } - buf := builder.NewWriter() - if _, err := fmt.Fprintf(buf, "SELECT %v%v%v", distinct, top, columnStr); err != nil { - return "", nil, err + subWriter := builder.NewWriter() + if _, err := fmt.Fprintf(subWriter, "(%s NOT IN (SELECT TOP %d %s", + column, statement.Start, column); err != nil { + return err + } + if err := statement.writeFrom(subWriter); err != nil { + return err + } + if statement.cond.IsValid() { + if _, err := fmt.Fprint(subWriter, " WHERE "); err != nil { + return err + } + if err := statement.cond.WriteTo(statement.QuoteReplacer(subWriter)); err != nil { + return err + } + } + if err := statement.WriteOrderBy(subWriter); err != nil { + return err + } + if err := statement.writeGroupBy(subWriter); err != nil { + return err + } + if _, err := fmt.Fprint(subWriter, "))"); err != nil { + return err + } + + if statement.cond.IsValid() { + if _, err := fmt.Fprint(w, " AND "); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(w, " WHERE "); err != nil { + return err + } + } + + return utils.WriteBuilder(w, subWriter) +} + +func (statement *Statement) writeOracleLimit(w *builder.BytesWriter, columnStr string) error { + if statement.LimitN == nil { + return nil + } + + oldString := w.String() + w.Reset() + rawColStr := columnStr + if rawColStr == "*" { + rawColStr = "at.*" + } + _, err := fmt.Fprintf(w, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", + columnStr, rawColStr, oldString, statement.Start+*statement.LimitN, statement.Start) + return err +} + +func (statement *Statement) writeSelect(buf *builder.BytesWriter, columnStr string, needLimit bool) error { + if err := statement.writeSelectColumns(buf, columnStr); err != nil { + return err } if err := statement.writeFrom(buf); err != nil { - return "", nil, err + return err } - if whereStr != "" { - if _, err := fmt.Fprint(buf, whereStr); err != nil { - return "", nil, err - } - if err := utils.WriteBuilder(buf, statement.QuoteReplacer(condWriter)); err != nil { - return "", nil, err - } + if err := statement.writeWhere(buf); err != nil { + return err } - if mssqlCondi.Len() > 0 { - if len(whereStr) > 0 { - if _, err := fmt.Fprint(buf, " AND "); err != nil { - return "", nil, err - } - } else { - if _, err := fmt.Fprint(buf, " WHERE "); err != nil { - return "", nil, err - } - } - - if err := utils.WriteBuilder(buf, mssqlCondi); err != nil { - return "", nil, err - } - } - if err := statement.writeGroupBy(buf); err != nil { - return "", nil, err + return err } if err := statement.writeHaving(buf); err != nil { - return "", nil, err + return err } - if needOrderBy { - if err := statement.WriteOrderBy(buf); err != nil { - return "", nil, err - } - } - if needLimit { - if dialect.URI().DBType != schemas.MSSQL && dialect.URI().DBType != schemas.ORACLE { - if err := statement.writeLimitOffset(buf); err != nil { - return "", nil, err - } - } else if dialect.URI().DBType == schemas.ORACLE { - if pLimitN != nil { - oldString := buf.String() - buf.Reset() - rawColStr := columnStr - if rawColStr == "*" { - rawColStr = "at.*" - } - fmt.Fprintf(buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", - columnStr, rawColStr, oldString, statement.Start+*pLimitN, statement.Start) - } - } - } - if statement.IsForUpdate { - return dialect.ForUpdateSQL(buf.String()), buf.Args(), nil + if err := statement.WriteOrderBy(buf); err != nil { + return err } - return buf.String(), buf.Args(), nil + dialect := statement.dialect + if needLimit { + if dialect.URI().DBType == schemas.ORACLE { + if err := statement.writeOracleLimit(buf, columnStr); err != nil { + return err + } + } else if dialect.URI().DBType != schemas.MSSQL { + if err := statement.writeLimitOffset(buf); err != nil { + return err + } + } + } + return statement.writeForUpdate(buf) } // GenExistSQL generates Exist SQL @@ -457,6 +479,33 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac return buf.String(), buf.Args(), nil } +func (statement *Statement) genSelectColumnStr() string { + // manually select columns + if len(statement.SelectStr) > 0 { + return statement.SelectStr + } + + columnStr := statement.ColumnStr() + if columnStr != "" { + return columnStr + } + + // autodetect columns + if statement.GroupByStr != "" { + return statement.quoteColumnStr(statement.GroupByStr) + } + + if len(statement.joins) != 0 { + return "*" + } + + columnStr = statement.genColumnStr() + if columnStr == "" { + columnStr = "*" + } + return columnStr +} + // GenFindSQL generates Find SQL func (statement *Statement) GenFindSQL(autoCond builder.Cond) (string, []interface{}, error) { if statement.RawSQL != "" { @@ -467,33 +516,11 @@ func (statement *Statement) GenFindSQL(autoCond builder.Cond) (string, []interfa return "", nil, ErrTableNotFound } - columnStr := statement.ColumnStr() - if len(statement.SelectStr) > 0 { - columnStr = statement.SelectStr - } else { - if len(statement.joins) == 0 { - if columnStr == "" { - if statement.GroupByStr != "" { - columnStr = statement.quoteColumnStr(statement.GroupByStr) - } else { - columnStr = statement.genColumnStr() - } - } - } else { - if columnStr == "" { - if statement.GroupByStr != "" { - columnStr = statement.quoteColumnStr(statement.GroupByStr) - } else { - columnStr = "*" - } - } - } - if columnStr == "" { - columnStr = "*" - } - } - statement.cond = statement.cond.And(autoCond) - return statement.genSelectSQL(columnStr, true, true) + buf := builder.NewWriter() + if err := statement.writeSelect(buf, statement.genSelectColumnStr(), true); err != nil { + return "", nil, err + } + return buf.String(), buf.Args(), nil } diff --git a/internal/statements/update.go b/internal/statements/update.go index 40159e0c..4dc54780 100644 --- a/internal/statements/update.go +++ b/internal/statements/update.go @@ -19,7 +19,8 @@ import ( ) func (statement *Statement) ifAddColUpdate(col *schemas.Column, includeVersion, includeUpdated, includeNil, - includeAutoIncr, update bool) (bool, error) { + includeAutoIncr, update bool, +) (bool, error) { columnMap := statement.ColumnMap omitColumnMap := statement.OmitColumnMap unscoped := statement.unscoped @@ -64,15 +65,16 @@ func (statement *Statement) ifAddColUpdate(col *schemas.Column, includeVersion, // BuildUpdates auto generating update columnes and values according a struct func (statement *Statement) BuildUpdates(tableValue reflect.Value, includeVersion, includeUpdated, includeNil, - includeAutoIncr, update bool) ([]string, []interface{}, error) { + includeAutoIncr, update bool, +) ([]string, []interface{}, error) { table := statement.RefTable allUseBool := statement.allUseBool useAllCols := statement.useAllCols mustColumnMap := statement.MustColumnMap nullableMap := statement.NullableMap - var colNames = make([]string, 0) - var args = make([]interface{}, 0) + colNames := make([]string, 0) + args := make([]interface{}, 0) for _, col := range table.Columns() { ok, err := statement.ifAddColUpdate(col, includeVersion, includeUpdated, includeNil, From 55d9407390986c43316bf3e484c6a878b8cf2667 Mon Sep 17 00:00:00 2001 From: martinvigg Date: Wed, 12 Jul 2023 08:52:23 +0000 Subject: [PATCH 094/116] mysql: add CHAIN, RANK to reserved word list (#2282) Reference: https://dev.mysql.com/doc/refman/8.0/en/keywords.html Co-authored-by: Martin Viggiano Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2282 Co-authored-by: martinvigg Co-committed-by: martinvigg --- dialects/mysql.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dialects/mysql.go b/dialects/mysql.go index 82505707..6b92752b 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -38,6 +38,7 @@ var ( "CALL": true, "CASCADE": true, "CASE": true, + "CHAIN": true, "CHANGE": true, "CHAR": true, "CHARACTER": true, @@ -128,6 +129,7 @@ var ( "OUT": true, "OUTER": true, "OUTFILE": true, "PRECISION": true, "PRIMARY": true, "PROCEDURE": true, "PURGE": true, "RAID0": true, "RANGE": true, + "RANK": true, "READ": true, "READS": true, "REAL": true, "REFERENCES": true, "REGEXP": true, "RELEASE": true, "RENAME": true, "REPEAT": true, "REPLACE": true, From 73eee961cceebc99bbcc38788c54978e0ea56265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B4=AA=E5=9D=A4=E5=AE=89?= Date: Wed, 12 Jul 2023 09:53:19 +0000 Subject: [PATCH 095/116] Performance Optimization: reduce slice2Bean calling times of strings.ToLower and map creation times (#2255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 优化性能:减少func (session *Session) slice2Bean方法中的strings.ToLower调用次数以及减少map创建和访问次数(见工单 https://gitea.com/xorm/xorm/issues/2243) 2. 新增SetDefaultJSONHandler方法,用于用户自行设置DefaultJSONHandler(见工单 https://gitea.com/xorm/xorm/issues/2129) 优化前: 加载耗时=16.079s,总记录数=4028940 加载耗时=15.775s,总记录数=4028940 加载耗时=15.946s,总记录数=4028940 优化后: 加载耗时=10.863s,总记录数=4028940 加载耗时=11.257s,总记录数=4028940 加载耗时=11.155s,总记录数=4028940 Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2255 Co-authored-by: 洪坤安 Co-committed-by: 洪坤安 --- rows.go | 4 ++- session.go | 38 +++++++++++------------------ session_find.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++- session_get.go | 8 +++--- 4 files changed, 86 insertions(+), 29 deletions(-) diff --git a/rows.go b/rows.go index ef3e42b6..a42eedb9 100644 --- a/rows.go +++ b/rows.go @@ -129,7 +129,9 @@ func (rows *Rows) Scan(beans ...interface{}) error { return err } - if err := rows.session.scan(rows.rows, rows.session.statement.RefTable, beanKind, beans, types, fields); err != nil { + columnsSchema := ParseColumnsSchema(fields, types, rows.session.statement.RefTable) + + if err := rows.session.scan(rows.rows, rows.session.statement.RefTable, beanKind, beans, columnsSchema, types, fields); err != nil { return err } diff --git a/session.go b/session.go index af6e4921..14d0781e 100644 --- a/session.go +++ b/session.go @@ -16,8 +16,6 @@ import ( "io" "reflect" "strconv" - "strings" - "xorm.io/xorm/contexts" "xorm.io/xorm/convert" "xorm.io/xorm/core" @@ -395,10 +393,10 @@ func (session *Session) doPrepareTx(sqlStr string) (stmt *core.Stmt, err error) return } -func getField(dataStruct *reflect.Value, table *schemas.Table, colName string, idx int) (*schemas.Column, *reflect.Value, error) { - col := table.GetColumnIdx(colName, idx) +func getField(dataStruct *reflect.Value, table *schemas.Table, field *QueryedField) (*schemas.Column, *reflect.Value, error) { + col := field.ColumnSchema if col == nil { - return nil, nil, ErrFieldIsNotExist{colName, table.Name} + return nil, nil, ErrFieldIsNotExist{field.FieldName, table.Name} } fieldValue, err := col.ValueOfV(dataStruct) @@ -406,10 +404,10 @@ func getField(dataStruct *reflect.Value, table *schemas.Table, colName string, i return nil, nil, err } if fieldValue == nil { - return nil, nil, ErrFieldIsNotValid{colName, table.Name} + return nil, nil, ErrFieldIsNotValid{field.FieldName, table.Name} } if !fieldValue.IsValid() || !fieldValue.CanSet() { - return nil, nil, ErrFieldIsNotValid{colName, table.Name} + return nil, nil, ErrFieldIsNotValid{field.FieldName, table.Name} } return col, fieldValue, nil @@ -418,7 +416,7 @@ func getField(dataStruct *reflect.Value, table *schemas.Table, colName string, i // Cell cell is a result of one column field type Cell *interface{} -func (session *Session) rows2Beans(rows *core.Rows, fields []string, types []*sql.ColumnType, +func (session *Session) rows2Beans(rows *core.Rows, columnsSchema *ColumnsSchema, fields []string, types []*sql.ColumnType, table *schemas.Table, newElemFunc func([]string) reflect.Value, sliceValueSetFunc func(*reflect.Value, schemas.PK) error, ) error { @@ -432,7 +430,7 @@ func (session *Session) rows2Beans(rows *core.Rows, fields []string, types []*sq if err != nil { return err } - pk, err := session.slice2Bean(scanResults, fields, bean, &dataStruct, table) + pk, err := session.slice2Bean(scanResults, columnsSchema, fields, bean, &dataStruct, table) if err != nil { return err } @@ -705,28 +703,16 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec return convert.AssignValue(fieldValue.Addr(), scanResult) } -func (session *Session) slice2Bean(scanResults []interface{}, fields []string, bean interface{}, dataStruct *reflect.Value, table *schemas.Table) (schemas.PK, error) { +func (session *Session) slice2Bean(scanResults []interface{}, columnsSchema *ColumnsSchema, fields []string, bean interface{}, dataStruct *reflect.Value, table *schemas.Table) (schemas.PK, error) { defer func() { executeAfterSet(bean, fields, scanResults) }() buildAfterProcessors(session, bean) - tempMap := make(map[string]int) var pk schemas.PK - for i, colName := range fields { - var idx int - lKey := strings.ToLower(colName) - var ok bool - - if idx, ok = tempMap[lKey]; !ok { - idx = 0 - } else { - idx++ - } - tempMap[lKey] = idx - - col, fieldValue, err := getField(dataStruct, table, colName, idx) + for i, field := range columnsSchema.Fields { + col, fieldValue, err := getField(dataStruct, table, field) if _, ok := err.(ErrFieldIsNotExist); ok { continue } else if err != nil { @@ -800,3 +786,7 @@ func (session *Session) NoVersionCheck() *Session { session.statement.CheckVersion = false return session } + +func SetDefaultJSONHandler(jsonHandler json.Interface) { + json.DefaultJSONHandler = jsonHandler +} diff --git a/session_find.go b/session_find.go index d9444aee..1026910c 100644 --- a/session_find.go +++ b/session_find.go @@ -5,8 +5,10 @@ package xorm import ( + "database/sql" "errors" "reflect" + "strings" "xorm.io/builder" "xorm.io/xorm/caches" @@ -161,6 +163,64 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) return session.noCacheFind(table, sliceValue, sqlStr, args...) } +type QueryedField struct { + FieldName string + LowerFieldName string + ColumnType *sql.ColumnType + TempIndex int + ColumnSchema *schemas.Column +} + +type ColumnsSchema struct { + Fields []*QueryedField + FieldNames []string + Types []*sql.ColumnType +} + +func (columnsSchema *ColumnsSchema) ParseTableSchema(table *schemas.Table) { + for _, field := range columnsSchema.Fields { + field.ColumnSchema = table.GetColumnIdx(field.FieldName, field.TempIndex) + } +} + +func ParseColumnsSchema(fieldNames []string, types []*sql.ColumnType, table *schemas.Table) *ColumnsSchema { + var columnsSchema ColumnsSchema + + fields := make([]*QueryedField, 0, len(fieldNames)) + + for i, fieldName := range fieldNames { + field := &QueryedField{ + FieldName: fieldName, + LowerFieldName: strings.ToLower(fieldName), + ColumnType: types[i], + } + fields = append(fields, field) + } + + columnsSchema.Fields = fields + + tempMap := make(map[string]int) + for _, field := range fields { + var idx int + var ok bool + + if idx, ok = tempMap[field.LowerFieldName]; !ok { + idx = 0 + } else { + idx++ + } + + tempMap[field.LowerFieldName] = idx + field.TempIndex = idx + } + + if table != nil { + columnsSchema.ParseTableSchema(table) + } + + return &columnsSchema +} + func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect.Value, sqlStr string, args ...interface{}) error { elemType := containerValue.Type().Elem() var isPointer bool @@ -238,7 +298,10 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect if err != nil { return err } - err = session.rows2Beans(rows, fields, types, tb, newElemFunc, containerValueSetFunc) + + columnsSchema := ParseColumnsSchema(fields, types, tb) + + err = session.rows2Beans(rows, columnsSchema, fields, types, tb, newElemFunc, containerValueSetFunc) rows.Close() if err != nil { return err diff --git a/session_get.go b/session_get.go index 96e362e9..0d590330 100644 --- a/session_get.go +++ b/session_get.go @@ -164,7 +164,9 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, return true, err } - if err := session.scan(rows, table, beanKind, beans, types, fields); err != nil { + columnsSchema := ParseColumnsSchema(fields, types, table) + + if err := session.scan(rows, table, beanKind, beans, columnsSchema, types, fields); err != nil { return true, err } rows.Close() @@ -172,7 +174,7 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, return true, session.executeProcessors() } -func (session *Session) scan(rows *core.Rows, table *schemas.Table, firstBeanKind reflect.Kind, beans []interface{}, types []*sql.ColumnType, fields []string) error { +func (session *Session) scan(rows *core.Rows, table *schemas.Table, firstBeanKind reflect.Kind, beans []interface{}, columnsSchema *ColumnsSchema, types []*sql.ColumnType, fields []string) error { if len(beans) == 1 { bean := beans[0] switch firstBeanKind { @@ -186,7 +188,7 @@ func (session *Session) scan(rows *core.Rows, table *schemas.Table, firstBeanKin } dataStruct := utils.ReflectValue(bean) - _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table) + _, err = session.slice2Bean(scanResults, columnsSchema, fields, bean, &dataStruct, table) return err case reflect.Slice: return session.getSlice(rows, types, fields, bean) From 3b53a5f8479ae8fbbab4dd6642d61a26131917b9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 12 Jul 2023 11:06:39 +0000 Subject: [PATCH 096/116] upgrade dependencies (#2223) Fix #2209 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2223 --- README.md | 2 +- README_CN.md | 3 +- go.mod | 16 +- go.sum | 569 ++++++++++++--------------------------------------- 4 files changed, 138 insertions(+), 452 deletions(-) diff --git a/README.md b/README.md index f30449a1..1348f4f8 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Drivers for Go's sql package which currently support database/sql includes: * [SQLite](https://sqlite.org) - [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - - [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) (windows unsupported) + - [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) * MsSql - [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) diff --git a/README_CN.md b/README_CN.md index a5aaae66..aa466894 100644 --- a/README_CN.md +++ b/README_CN.md @@ -44,7 +44,7 @@ v1.0.0 相对于 v0.8.2 有以下不兼容的变更: * [SQLite](https://sqlite.org) - [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - - [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) (Windows试验性支持) + - [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) * MsSql - [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) @@ -52,6 +52,7 @@ v1.0.0 相对于 v0.8.2 有以下不兼容的变更: * Oracle - [github.com/godror/godror](https://github.com/godror/godror) (试验性支持) - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持) + - [github.com/sijms/go-ora](https://github.com/sijms/go-ora) (试验性支持) ## 安装 diff --git a/go.mod b/go.mod index 7bde41ae..5f2f18e0 100644 --- a/go.mod +++ b/go.mod @@ -4,18 +4,18 @@ go 1.13 require ( gitee.com/travelliu/dm v1.8.11192 - github.com/denisenkom/go-mssqldb v0.10.0 - github.com/go-sql-driver/mysql v1.6.0 + github.com/denisenkom/go-mssqldb v0.12.3 + github.com/go-sql-driver/mysql v1.7.0 github.com/goccy/go-json v0.8.1 github.com/golang/snappy v0.0.4 // indirect - github.com/jackc/pgx/v4 v4.12.0 + github.com/jackc/pgx/v4 v4.18.0 github.com/json-iterator/go v1.1.12 - github.com/lib/pq v1.10.2 - github.com/mattn/go-sqlite3 v1.14.9 - github.com/shopspring/decimal v1.2.0 - github.com/stretchr/testify v1.7.0 + github.com/lib/pq v1.10.7 + github.com/mattn/go-sqlite3 v1.14.16 + github.com/shopspring/decimal v1.3.1 + github.com/stretchr/testify v1.8.1 github.com/syndtr/goleveldb v1.0.0 github.com/ziutek/mymysql v1.5.4 - modernc.org/sqlite v1.14.2 + modernc.org/sqlite v1.20.4 xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 ) diff --git a/go.sum b/go.sum index 8bdc9798..9f197fae 100644 --- a/go.sum +++ b/go.sum @@ -1,151 +1,62 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= gitee.com/travelliu/dm v1.8.11192 h1:aqJT0xhodZjRutIfEXxKYv0CxqmHUHzsbz6SFaRL6OY= gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8= -github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw= +github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI= github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -154,18 +65,17 @@ github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgO github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g= -github.com/jackc/pgconn v1.9.0 h1:gqibKSTJup/ahCsNKyMZAniPuZEfIqfXFc8FOWVYR+Q= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= +github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd h1:eDErF6V/JPJON/B7s68BxwHgfmyOntHJQ8IOaz0x4R8= github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= @@ -174,52 +84,36 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= +github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= -github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= -github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE= -github.com/jackc/pgtype v1.8.0 h1:iFVCcVhYlw0PulYCVoguRGm0SE9guIcPcccnLzHj8bA= -github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= -github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= -github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc= -github.com/jackc/pgx/v4 v4.12.0 h1:xiP3TdnkwyslWNp77yE5XAPfxAsU9RMFDe0c1SwN8h4= -github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.0 h1:Ltaa1ePvc7msFGALnCrqKJVEByu/qYh5jJBYcDtAno4= +github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -229,151 +123,68 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -385,279 +196,153 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U= -modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= -modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= -modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= -modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= -modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= -modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= -modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= -modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= -modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= -modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= -modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= -modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= -modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= -modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= -modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= -modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= -modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= -modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= -modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= -modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= -modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= -modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= -modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= -modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= -modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= -modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= -modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= -modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= -modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= -modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4= -modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= -modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= -modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= -modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= -modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8= -modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= -modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA= -modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= +modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= +modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= +modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= +modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= +modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0= +modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= +modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= +modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= -modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= -modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= -modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= -modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= -modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= -modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= -modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= -modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= -modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= -modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= -modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= -modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= -modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= -modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= -modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= -modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= -modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= -modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= -modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= -modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= -modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= -modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= -modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= -modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= -modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= -modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= -modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= -modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= -modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= -modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= -modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= -modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= -modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE= -modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= -modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= -modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= -modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= +modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= +modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= +modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= +modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= +modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= +modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= +modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= +modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE= -modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8= -modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/tcl v1.8.13 h1:V0sTNBw0Re86PvXZxuCub3oO9WrSTqALgrwNZNvLFGw= -modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY= -modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.2.19 h1:BGyRFWhDVn5LFS5OcX4Yd/MlpRTOc7hOPTdcIpCiUao= -modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE= +modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= +modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34= +modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE= +modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= +modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= +modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= From 722f1cc14189224d7d1188b9607c32b93df74081 Mon Sep 17 00:00:00 2001 From: takumin Date: Wed, 12 Jul 2023 13:01:34 +0000 Subject: [PATCH 097/116] Generate coverage html (#2170) Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2170 Co-authored-by: takumin Co-committed-by: takumin --- .gitignore | 3 ++- Makefile | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a183a295..4cd4252b 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,5 @@ test.db.sql test.db integrations/*.sql integrations/test_sqlite* -cover.out \ No newline at end of file +cover.out +cover.html diff --git a/Makefile b/Makefile index b43c4a4c..ade90e6d 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ build: go-check $(GO_SOURCES) .PHONY: clean clean: $(GO) clean -i ./... - rm -rf *.sql *.log test.db *coverage.out coverage.all integrations/*.sql + rm -rf *.sql *.log test.db cover.out cover.html *coverage.out coverage.all integrations/*.sql .PHONY: coverage coverage: @@ -132,7 +132,8 @@ golangci-lint-check: .PHONY: test test: go-check - $(GO) test $(PACKAGES) + $(GO) test -cover -coverprofile=cover.out $(PACKAGES) + $(GO) tool cover -html=cover.out -o cover.html .PNONY: test-cockroach test-cockroach: go-check From f33221df7481861aef8d13b53ec27769b556d86c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 12 Jul 2023 13:22:32 +0000 Subject: [PATCH 098/116] Fix question mark replacement on postgres (#2202) Fix #2112 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2202 --- dialects/filter.go | 77 ++++++++++++++++++++++++++++++++++++++--- dialects/filter_test.go | 23 ++++++++---- dialects/oracle.go | 2 +- dialects/postgres.go | 2 +- 4 files changed, 91 insertions(+), 13 deletions(-) diff --git a/dialects/filter.go b/dialects/filter.go index add67c1b..ab7c65b6 100644 --- a/dialects/filter.go +++ b/dialects/filter.go @@ -6,6 +6,7 @@ package dialects import ( "context" + "fmt" "strconv" "strings" ) @@ -15,13 +16,79 @@ type Filter interface { Do(ctx context.Context, sql string) string } -// SeqFilter filter SQL replace ?, ? ... to $1, $2 ... -type SeqFilter struct { +// postgresSeqFilter filter SQL replace ?, ? ... to $1, $2 ... +type postgresSeqFilter struct { Prefix string Start int } -func convertQuestionMark(sql, prefix string, start int) string { +func postgresSeqFilterConvertQuestionMark(sql, prefix string, start int) string { + var buf strings.Builder + var beginSingleQuote bool + var isLineComment bool + var isComment bool + var isMaybeLineComment bool + var isMaybeComment bool + var isMaybeCommentEnd bool + var isMaybeJsonbQuestion bool + index := start + for i, c := range sql { + if !beginSingleQuote && !isLineComment && !isComment && !isMaybeJsonbQuestion && c == '?' { + buf.WriteString(fmt.Sprintf("%s%v", prefix, index)) + index++ + } else { + if isMaybeJsonbQuestion && c == '?' { + isMaybeJsonbQuestion = false + } else if isMaybeLineComment { + if c == '-' { + isLineComment = true + } + isMaybeLineComment = false + } else if isMaybeComment { + if c == '*' { + isComment = true + } + isMaybeComment = false + } else if isMaybeCommentEnd { + if c == '/' { + isComment = false + } + isMaybeCommentEnd = false + } else if isLineComment { + if c == '\n' { + isLineComment = false + } + } else if isComment { + if c == '*' { + isMaybeCommentEnd = true + } + } else if !beginSingleQuote && c == '-' { + isMaybeLineComment = true + } else if !beginSingleQuote && c == '/' { + isMaybeComment = true + } else if !beginSingleQuote && c == ' ' && i >= 7 && strings.TrimSpace(sql[i-7:i]) == "::jsonb" { + isMaybeJsonbQuestion = true + } else if c == '\'' { + beginSingleQuote = !beginSingleQuote + } + buf.WriteRune(c) + } + } + return buf.String() +} + +// Do implements Filter +func (s *postgresSeqFilter) Do(ctx context.Context, sql string) string { + return postgresSeqFilterConvertQuestionMark(sql, s.Prefix, s.Start) +} + +// oracleSeqFilter filter SQL replace ?, ? ... to :1, :2 ... +type oracleSeqFilter struct { + Prefix string + Start int +} + +func oracleSeqFilterConvertQuestionMark(sql, prefix string, start int) string { var buf strings.Builder var beginSingleQuote bool var isLineComment bool @@ -73,6 +140,6 @@ func convertQuestionMark(sql, prefix string, start int) string { } // Do implements Filter -func (s *SeqFilter) Do(ctx context.Context, sql string) string { - return convertQuestionMark(sql, s.Prefix, s.Start) +func (s *oracleSeqFilter) Do(ctx context.Context, sql string) string { + return oracleSeqFilterConvertQuestionMark(sql, s.Prefix, s.Start) } diff --git a/dialects/filter_test.go b/dialects/filter_test.go index 15050656..e0da11df 100644 --- a/dialects/filter_test.go +++ b/dialects/filter_test.go @@ -7,7 +7,7 @@ import ( ) func TestSeqFilter(t *testing.T) { - var kases = map[string]string{ + kases := map[string]string{ "SELECT * FROM TABLE1 WHERE a=? AND b=?": "SELECT * FROM TABLE1 WHERE a=$1 AND b=$2", "SELECT 1, '???', '2006-01-02 15:04:05' FROM TABLE1 WHERE a=? AND b=?": "SELECT 1, '???', '2006-01-02 15:04:05' FROM TABLE1 WHERE a=$1 AND b=$2", "select '1''?' from issue": "select '1''?' from issue", @@ -16,12 +16,12 @@ func TestSeqFilter(t *testing.T) { "select '1\\''?',? from issue": "select '1\\''?',$1 from issue", } for sql, result := range kases { - assert.EqualValues(t, result, convertQuestionMark(sql, "$", 1)) + assert.EqualValues(t, result, postgresSeqFilterConvertQuestionMark(sql, "$", 1)) } } func TestSeqFilterLineComment(t *testing.T) { - var kases = map[string]string{ + kases := map[string]string{ `SELECT * FROM TABLE1 WHERE foo='bar' @@ -49,12 +49,12 @@ func TestSeqFilterLineComment(t *testing.T) { AND b=$2`, } for sql, result := range kases { - assert.EqualValues(t, result, convertQuestionMark(sql, "$", 1)) + assert.EqualValues(t, result, postgresSeqFilterConvertQuestionMark(sql, "$", 1)) } } func TestSeqFilterComment(t *testing.T) { - var kases = map[string]string{ + kases := map[string]string{ `SELECT * FROM TABLE1 WHERE a=? /* it's a comment */ @@ -73,6 +73,17 @@ func TestSeqFilterComment(t *testing.T) { AND b=$2`, } for sql, result := range kases { - assert.EqualValues(t, result, convertQuestionMark(sql, "$", 1)) + assert.EqualValues(t, result, postgresSeqFilterConvertQuestionMark(sql, "$", 1)) + } +} + +func TestSeqFilterPostgresQuestions(t *testing.T) { + kases := map[string]string{ + `SELECT '{"a":1, "b":2}'::jsonb ? 'b' + FROM table1 WHERE c = ?`: `SELECT '{"a":1, "b":2}'::jsonb ? 'b' + FROM table1 WHERE c = $1`, + } + for sql, result := range kases { + assert.EqualValues(t, result, postgresSeqFilterConvertQuestionMark(sql, "$", 1)) } } diff --git a/dialects/oracle.go b/dialects/oracle.go index a5f8a5b2..fbda9dda 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -863,7 +863,7 @@ func (db *oracle) GetIndexes(queryer core.Queryer, ctx context.Context, tableNam func (db *oracle) Filters() []Filter { return []Filter{ - &SeqFilter{Prefix: ":", Start: 1}, + &oracleSeqFilter{Prefix: ":", Start: 1}, } } diff --git a/dialects/postgres.go b/dialects/postgres.go index 28196891..f1f6a2f2 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -1358,7 +1358,7 @@ func (db *postgres) CreateTableSQL(ctx context.Context, queryer core.Queryer, ta } func (db *postgres) Filters() []Filter { - return []Filter{&SeqFilter{Prefix: "$", Start: 1}} + return []Filter{&postgresSeqFilter{Prefix: "$", Start: 1}} } type pqDriver struct { From 0b18440d738b453aa3198fcae26cd38645a26ca7 Mon Sep 17 00:00:00 2001 From: CyJaySong Date: Fri, 14 Jul 2023 07:34:02 +0000 Subject: [PATCH 099/116] fix time parse layout (#2296) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2296 Co-authored-by: CyJaySong Co-committed-by: CyJaySong --- convert/time.go | 2 +- integrations/time_test.go | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/convert/time.go b/convert/time.go index dab6a9a2..dc36912b 100644 --- a/convert/time.go +++ b/convert/time.go @@ -41,7 +41,7 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t dt = dt.In(convertedLocation) return &dt, nil } else if len(s) >= 21 && s[10] == 'T' && s[19] == '.' { - dt, err := time.Parse(time.RFC3339, s) + dt, err := time.Parse(time.RFC3339Nano, s) if err != nil { return nil, err } diff --git a/integrations/time_test.go b/integrations/time_test.go index a8447eea..5a17417a 100644 --- a/integrations/time_test.go +++ b/integrations/time_test.go @@ -6,9 +6,11 @@ package integrations import ( "fmt" + "strconv" "strings" "testing" "time" + "xorm.io/xorm/convert" "xorm.io/xorm/internal/utils" @@ -619,3 +621,59 @@ func TestTimestamp(t *testing.T) { assert.EqualValues(t, formatTime(d3.InsertTime, 6), formatTime(d4.InsertTime, 6)) }*/ } + +func TestString2Time(t *testing.T) { + loc, err := time.LoadLocation("Asia/Shanghai") + assert.NoError(t, err) + var timeTmp1 = time.Date(2023, 7, 14, 11, 30, 0, 0, loc) + var timeTmp2 = time.Date(2023, 7, 14, 0, 0, 0, 0, loc) + var time1StampStr = strconv.FormatInt(timeTmp1.Unix(), 10) + var timeStr = "0000-00-00 00:00:00" + dt, err := convert.String2Time(timeStr, time.Local, time.Local) + assert.NoError(t, err) + assert.True(t, dt.Nanosecond() == 0) + + timeStr = "0001-01-01 00:00:00" + dt, err = convert.String2Time(timeStr, time.Local, time.Local) + assert.NoError(t, err) + assert.True(t, dt.Nanosecond() == 0) + + timeStr = "2023-07-14 11:30:00" + dt, err = convert.String2Time(timeStr, loc, time.UTC) + assert.NoError(t, err) + assert.True(t, timeTmp1.In(time.UTC).Equal(*dt)) + + timeStr = "2023-07-14T11:30:00Z" + dt, err = convert.String2Time(timeStr, loc, time.UTC) + assert.NoError(t, err) + assert.True(t, timeTmp1.In(time.UTC).Equal(*dt)) + + timeStr = "2023-07-14T11:30:00+08:00" + dt, err = convert.String2Time(timeStr, loc, time.UTC) + assert.NoError(t, err) + assert.True(t, timeTmp1.In(time.UTC).Equal(*dt)) + + timeStr = "2023-07-14T11:30:00.00000000+08:00" + dt, err = convert.String2Time(timeStr, loc, time.UTC) + assert.NoError(t, err) + assert.True(t, timeTmp1.In(time.UTC).Equal(*dt)) + + timeStr = "0000-00-00" + dt, err = convert.String2Time(timeStr, loc, time.UTC) + assert.NoError(t, err) + assert.True(t, dt.Nanosecond() == 0) + + timeStr = "0001-01-01" + dt, err = convert.String2Time(timeStr, loc, time.UTC) + assert.NoError(t, err) + assert.True(t, dt.Nanosecond() == 0) + + timeStr = "2023-07-14" + dt, err = convert.String2Time(timeStr, loc, time.UTC) + assert.NoError(t, err) + assert.True(t, timeTmp2.In(time.UTC).Equal(*dt)) + + dt, err = convert.String2Time(time1StampStr, loc, time.UTC) + assert.NoError(t, err) + assert.True(t, timeTmp1.In(time.UTC).Equal(*dt)) +} From 9b71cb49cca4311cc84bcf2997b4d752bc8bfcea Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Fri, 14 Jul 2023 07:35:35 +0000 Subject: [PATCH 100/116] The loadTableInfo function supports passing the context. (#2297) ## `loadTableInfo` add context parameter ### Main change ```diff +++ func (engine *Engine) loadTableInfo(ctx context.Context, table *schemas.Table) error --- func (engine *Engine) loadTableInfo(table *schemas.Table) error ``` ### Situation After #2200, I built custom dialect to control the SQL. I find that everything else is fine, except when the `SYNC` method executes with an exception. The reason is that the `loadTableInfo` method calls the `GetIndexes` and `GetColumns` methods with the dialect during execution. **The context passed to these two methods are all `engine.defaultContext` not the current session's context.** So, I think the `loadTableInfo` method should add the context parameter to ensure that the correct context is used during execution. ### Review Note 1. dialect's `GetColumns` and `GetIndexes` methods are **only** used here, if the context here is incorrect, then the context parameter is invalid. 2. `loadTableInfo` method is only used in `SYNC` and `DBMetas` methods, `SYNC` should pass the session's context, while `DBMetas` has no problem passing `engine.defaultContext`. All in all, I think this change should not affect other function. Reviewed-on: https://gitea.com/xorm/xorm/pulls/2297 Co-authored-by: LinkinStars Co-committed-by: LinkinStars --- engine.go | 8 ++++---- sync.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/engine.go b/engine.go index aa9d8050..0cbfdede 100644 --- a/engine.go +++ b/engine.go @@ -360,15 +360,15 @@ func (engine *Engine) NoAutoCondition(no ...bool) *Session { return session.NoAutoCondition(no...) } -func (engine *Engine) loadTableInfo(table *schemas.Table) error { - colSeq, cols, err := engine.dialect.GetColumns(engine.db, engine.defaultContext, table.Name) +func (engine *Engine) loadTableInfo(ctx context.Context, table *schemas.Table) error { + colSeq, cols, err := engine.dialect.GetColumns(engine.db, ctx, table.Name) if err != nil { return err } for _, name := range colSeq { table.AddColumn(cols[name]) } - indexes, err := engine.dialect.GetIndexes(engine.db, engine.defaultContext, table.Name) + indexes, err := engine.dialect.GetIndexes(engine.db, ctx, table.Name) if err != nil { return err } @@ -404,7 +404,7 @@ func (engine *Engine) DBMetas() ([]*schemas.Table, error) { } for _, table := range tables { - if err = engine.loadTableInfo(table); err != nil { + if err = engine.loadTableInfo(engine.defaultContext, table); err != nil { return nil, err } } diff --git a/sync.go b/sync.go index 11e75404..635a8ba9 100644 --- a/sync.go +++ b/sync.go @@ -116,7 +116,7 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...interface{}) } // this will modify an old table - if err = engine.loadTableInfo(oriTable); err != nil { + if err = engine.loadTableInfo(session.ctx, oriTable); err != nil { return nil, err } From 2df56f033a20f60c12d58b89bca88d9137948865 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 20 Jul 2023 14:45:31 +0000 Subject: [PATCH 101/116] Some refactors (#2293) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2293 --- internal/statements/insert.go | 10 +- internal/statements/query.go | 39 ++--- internal/statements/statement.go | 6 +- internal/statements/update.go | 84 ++++++++++ session_update.go | 278 +++++-------------------------- 5 files changed, 149 insertions(+), 268 deletions(-) diff --git a/internal/statements/insert.go b/internal/statements/insert.go index 91a33319..187b94a3 100644 --- a/internal/statements/insert.go +++ b/internal/statements/insert.go @@ -43,8 +43,8 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } - var hasInsertColumns = len(colNames) > 0 - var needSeq = len(table.AutoIncrement) > 0 && (statement.dialect.URI().DBType == schemas.ORACLE || statement.dialect.URI().DBType == schemas.DAMENG) + hasInsertColumns := len(colNames) > 0 + needSeq := len(table.AutoIncrement) > 0 && (statement.dialect.URI().DBType == schemas.ORACLE || statement.dialect.URI().DBType == schemas.DAMENG) if needSeq { for _, col := range colNames { if strings.EqualFold(col, table.AutoIncrement) { @@ -124,11 +124,7 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } - if _, err := buf.WriteString(" WHERE "); err != nil { - return "", nil, err - } - - if err := statement.Conds().WriteTo(buf); err != nil { + if err := statement.writeWhere(buf); err != nil { return "", nil, err } } else { diff --git a/internal/statements/query.go b/internal/statements/query.go index cea8be6d..2e38f0fe 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -244,6 +244,16 @@ func (statement *Statement) writeSelectColumns(w *builder.BytesWriter, columnStr } func (statement *Statement) writeWhere(w *builder.BytesWriter) error { + if !statement.cond.IsValid() { + return nil + } + if _, err := fmt.Fprint(w, " WHERE "); err != nil { + return err + } + return statement.cond.WriteTo(statement.QuoteReplacer(w)) +} + +func (statement *Statement) writeWhereWithMssqlPagination(w *builder.BytesWriter) error { if !statement.cond.IsValid() { return statement.writeMssqlPaginationCond(w) } @@ -307,13 +317,8 @@ func (statement *Statement) writeMssqlPaginationCond(w *builder.BytesWriter) err if err := statement.writeFrom(subWriter); err != nil { return err } - if statement.cond.IsValid() { - if _, err := fmt.Fprint(subWriter, " WHERE "); err != nil { - return err - } - if err := statement.cond.WriteTo(statement.QuoteReplacer(subWriter)); err != nil { - return err - } + if err := statement.writeWhere(subWriter); err != nil { + return err } if err := statement.WriteOrderBy(subWriter); err != nil { return err @@ -361,7 +366,7 @@ func (statement *Statement) writeSelect(buf *builder.BytesWriter, columnStr stri if err := statement.writeFrom(buf); err != nil { return err } - if err := statement.writeWhere(buf); err != nil { + if err := statement.writeWhereWithMssqlPagination(buf); err != nil { return err } if err := statement.writeGroupBy(buf); err != nil { @@ -427,13 +432,8 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac if err := statement.writeJoins(buf); err != nil { return "", nil, err } - if statement.Conds().IsValid() { - if _, err := fmt.Fprintf(buf, " WHERE "); err != nil { - return "", nil, err - } - if err := statement.Conds().WriteTo(statement.QuoteReplacer(buf)); err != nil { - return "", nil, err - } + if err := statement.writeWhere(buf); err != nil { + return "", nil, err } } else if statement.dialect.URI().DBType == schemas.ORACLE { if _, err := fmt.Fprintf(buf, "SELECT * FROM %s", tableName); err != nil { @@ -463,13 +463,8 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac if err := statement.writeJoins(buf); err != nil { return "", nil, err } - if statement.Conds().IsValid() { - if _, err := fmt.Fprintf(buf, " WHERE "); err != nil { - return "", nil, err - } - if err := statement.Conds().WriteTo(statement.QuoteReplacer(buf)); err != nil { - return "", nil, err - } + if err := statement.writeWhere(buf); err != nil { + return "", nil, err } if _, err := fmt.Fprintf(buf, " LIMIT 1"); err != nil { return "", nil, err diff --git a/internal/statements/statement.go b/internal/statements/statement.go index ae38ca27..afc38a2e 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -299,13 +299,13 @@ func (statement *Statement) writeGroupBy(w builder.Writer) error { if statement.GroupByStr == "" { return nil } - _, err := fmt.Fprintf(w, " GROUP BY %s", statement.GroupByStr) + _, err := fmt.Fprint(w, " GROUP BY ", statement.GroupByStr) return err } // Having generate "Having conditions" statement func (statement *Statement) Having(conditions string) *Statement { - statement.HavingStr = fmt.Sprintf("HAVING %v", statement.ReplaceQuote(conditions)) + statement.HavingStr = conditions return statement } @@ -313,7 +313,7 @@ func (statement *Statement) writeHaving(w builder.Writer) error { if statement.HavingStr == "" { return nil } - _, err := fmt.Fprint(w, " ", statement.HavingStr) + _, err := fmt.Fprint(w, " HAVING ", statement.ReplaceQuote(statement.HavingStr)) return err } diff --git a/internal/statements/update.go b/internal/statements/update.go index 4dc54780..16ab5676 100644 --- a/internal/statements/update.go +++ b/internal/statements/update.go @@ -9,8 +9,10 @@ import ( "errors" "fmt" "reflect" + "strings" "time" + "xorm.io/builder" "xorm.io/xorm/convert" "xorm.io/xorm/dialects" "xorm.io/xorm/internal/json" @@ -308,3 +310,85 @@ func (statement *Statement) BuildUpdates(tableValue reflect.Value, return colNames, args, nil } + +func (statement *Statement) WriteUpdate(updateWriter *builder.BytesWriter, cond builder.Cond, colNames []string) error { + whereWriter := builder.NewWriter() + if cond.IsValid() { + fmt.Fprint(whereWriter, "WHERE ") + } + if err := cond.WriteTo(statement.QuoteReplacer(whereWriter)); err != nil { + return err + } + if err := statement.WriteOrderBy(whereWriter); err != nil { + return err + } + + table := statement.RefTable + tableName := statement.TableName() + // TODO: Oracle support needed + var top string + if statement.LimitN != nil { + limitValue := *statement.LimitN + switch statement.dialect.URI().DBType { + case schemas.MYSQL: + fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) + case schemas.SQLITE: + fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) + + cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)", + statement.quote(tableName), whereWriter.String()), whereWriter.Args()...)) + + whereWriter = builder.NewWriter() + fmt.Fprint(whereWriter, "WHERE ") + if err := cond.WriteTo(statement.QuoteReplacer(whereWriter)); err != nil { + return err + } + case schemas.POSTGRES: + fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) + + cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)", + statement.quote(tableName), whereWriter.String()), whereWriter.Args()...)) + + whereWriter = builder.NewWriter() + fmt.Fprint(whereWriter, "WHERE ") + if err := cond.WriteTo(statement.QuoteReplacer(whereWriter)); err != nil { + return err + } + case schemas.MSSQL: + if statement.HasOrderBy() && table != nil && len(table.PrimaryKeys) == 1 { + cond = builder.Expr(fmt.Sprintf("%s IN (SELECT TOP (%d) %s FROM %v%v)", + table.PrimaryKeys[0], limitValue, table.PrimaryKeys[0], + statement.quote(tableName), whereWriter.String()), whereWriter.Args()...) + + whereWriter = builder.NewWriter() + fmt.Fprint(whereWriter, "WHERE ") + if err := cond.WriteTo(whereWriter); err != nil { + return err + } + } else { + top = fmt.Sprintf("TOP (%d) ", limitValue) + } + } + } + + tableAlias := statement.quote(tableName) + var fromSQL string + if statement.TableAlias != "" { + switch statement.dialect.URI().DBType { + case schemas.MSSQL: + fromSQL = fmt.Sprintf("FROM %s %s ", tableAlias, statement.TableAlias) + tableAlias = statement.TableAlias + default: + tableAlias = fmt.Sprintf("%s AS %s", tableAlias, statement.TableAlias) + } + } + + if _, err := fmt.Fprintf(updateWriter, "UPDATE %v%v SET %v %v", + top, + tableAlias, + strings.Join(colNames, ", "), + fromSQL); err != nil { + return err + } + return utils.WriteBuilder(updateWriter, whereWriter) +} diff --git a/session_update.go b/session_update.go index 1f80e70f..9a6964f1 100644 --- a/session_update.go +++ b/session_update.go @@ -6,13 +6,9 @@ package xorm import ( "errors" - "fmt" "reflect" - "strconv" - "strings" "xorm.io/builder" - "xorm.io/xorm/caches" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -22,124 +18,39 @@ var ( ErrNoColumnsTobeUpdated = errors.New("no columns found to be updated") ) -//revive:disable -func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr string, args ...interface{}) error { - if table == nil || - session.tx != nil { - return ErrCacheFailed +func (session *Session) genAutoCond(condiBean interface{}) (builder.Cond, error) { + if session.statement.NoAutoCondition { + return builder.NewCond(), nil } - oldhead, newsql := session.statement.ConvertUpdateSQL(sqlStr) - if newsql == "" { - return ErrCacheFailed - } - for _, filter := range session.engine.dialect.Filters() { - newsql = filter.Do(session.ctx, newsql) - } - session.engine.logger.Debugf("[cache] new sql: %v, %v", oldhead, newsql) - - var nStart int - if len(args) > 0 { - if strings.Contains(sqlStr, "?") { - nStart = strings.Count(oldhead, "?") - } else { - // only for pq, TODO: if any other databse? - nStart = strings.Count(oldhead, "$") + if c, ok := condiBean.(map[string]interface{}); ok { + eq := make(builder.Eq) + for k, v := range c { + eq[session.engine.Quote(k)] = v } + + if session.statement.RefTable != nil { + if col := session.statement.RefTable.DeletedColumn(); col != nil && !session.statement.GetUnscoped() { // tag "deleted" is enabled + return eq.And(session.statement.CondDeleted(col)), nil + } + } + return eq, nil } - cacher := session.engine.GetCacher(tableName) - session.engine.logger.Debugf("[cache] get cache sql: %v, %v", newsql, args[nStart:]) - ids, err := caches.GetCacheSql(cacher, tableName, newsql, args[nStart:]) + ct := reflect.TypeOf(condiBean) + k := ct.Kind() + if k == reflect.Ptr { + k = ct.Elem().Kind() + } + if k != reflect.Struct { + return nil, ErrConditionType + } + + condTable, err := session.engine.TableInfo(condiBean) if err != nil { - rows, err := session.NoCache().queryRows(newsql, args[nStart:]...) - if err != nil { - return err - } - defer rows.Close() - - ids = make([]schemas.PK, 0) - for rows.Next() { - res := make([]string, len(table.PrimaryKeys)) - err = rows.ScanSlice(&res) - if err != nil { - return err - } - var pk schemas.PK = make([]interface{}, len(table.PrimaryKeys)) - for i, col := range table.PKColumns() { - if col.SQLType.IsNumeric() { - n, err := strconv.ParseInt(res[i], 10, 64) - if err != nil { - return err - } - pk[i] = n - } else if col.SQLType.IsText() { - pk[i] = res[i] - } else { - return errors.New("not supported") - } - } - - ids = append(ids, pk) - } - if rows.Err() != nil { - return rows.Err() - } - session.engine.logger.Debugf("[cache] find updated id: %v", ids) - } /*else { - session.engine.LogDebug("[xorm:cacheUpdate] del cached sql:", tableName, newsql, args) - cacher.DelIds(tableName, genSqlKey(newsql, args)) - }*/ - - for _, id := range ids { - sid, err := id.ToString() - if err != nil { - return err - } - if bean := cacher.GetBean(tableName, sid); bean != nil { - sqls := utils.SplitNNoCase(sqlStr, "where", 2) - if len(sqls) == 0 || len(sqls) > 2 { - return ErrCacheFailed - } - - sqls = utils.SplitNNoCase(sqls[0], "set", 2) - if len(sqls) != 2 { - return ErrCacheFailed - } - kvs := strings.Split(strings.TrimSpace(sqls[1]), ",") - - for idx, kv := range kvs { - sps := strings.SplitN(kv, "=", 2) - sps2 := strings.Split(sps[0], ".") - colName := sps2[len(sps2)-1] - colName = session.engine.dialect.Quoter().Trim(colName) - colName = schemas.CommonQuoter.Trim(colName) - - if col := table.GetColumn(colName); col != nil { - fieldValue, err := col.ValueOf(bean) - if err != nil { - session.engine.logger.Errorf("%v", err) - } else { - session.engine.logger.Debugf("[cache] set bean field: %v, %v, %v", bean, colName, fieldValue.Interface()) - if col.IsVersion && session.statement.CheckVersion { - session.incrVersionFieldValue(fieldValue) - } else { - fieldValue.Set(reflect.ValueOf(args[idx])) - } - } - } else { - session.engine.logger.Errorf("[cache] ERROR: column %v is not table %v's", - colName, table.Name) - } - } - - session.engine.logger.Debugf("[cache] update cache: %v, %v, %v", tableName, id, bean) - cacher.PutBean(tableName, sid, bean) - } + return nil, err } - session.engine.logger.Debugf("[cache] clear cached table sql: %v", tableName) - cacher.ClearIds(tableName) - return nil + return session.statement.BuildConds(condTable, condiBean, true, true, false, true, false) } // Update records, bean's non-empty fields are updated contents, @@ -277,53 +188,23 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } var autoCond builder.Cond - if !session.statement.NoAutoCondition { - condBeanIsStruct := false - if len(condiBean) > 0 { - if c, ok := condiBean[0].(map[string]interface{}); ok { - eq := make(builder.Eq) - for k, v := range c { - eq[session.engine.Quote(k)] = v - } - autoCond = builder.Eq(eq) - } else { - ct := reflect.TypeOf(condiBean[0]) - k := ct.Kind() - if k == reflect.Ptr { - k = ct.Elem().Kind() - } - if k == reflect.Struct { - condTable, err := session.engine.TableInfo(condiBean[0]) - if err != nil { - return 0, err - } - - autoCond, err = session.statement.BuildConds(condTable, condiBean[0], true, true, false, true, false) - if err != nil { - return 0, err - } - condBeanIsStruct = true - } else { - return 0, ErrConditionType - } - } + if len(condiBean) > 0 { + autoCond, err = session.genAutoCond(condiBean[0]) + if err != nil { + return 0, err } + } else if table != nil { + if col := table.DeletedColumn(); col != nil && !session.statement.GetUnscoped() { // tag "deleted" is enabled + autoCond1 := session.statement.CondDeleted(col) - if !condBeanIsStruct && table != nil { - if col := table.DeletedColumn(); col != nil && !session.statement.GetUnscoped() { // tag "deleted" is enabled - autoCond1 := session.statement.CondDeleted(col) - - if autoCond == nil { - autoCond = autoCond1 - } else { - autoCond = autoCond.And(autoCond1) - } + if autoCond == nil { + autoCond = autoCond1 + } else { + autoCond = autoCond.And(autoCond1) } } } - st := session.statement - var ( cond = session.statement.Conds().And(autoCond) doIncVer = isStruct && (table != nil && table.Version != "" && session.statement.CheckVersion) @@ -345,88 +226,14 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return 0, ErrNoColumnsTobeUpdated } - whereWriter := builder.NewWriter() - if cond.IsValid() { - fmt.Fprint(whereWriter, "WHERE ") - } - if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil { - return 0, err - } - if err := st.WriteOrderBy(whereWriter); err != nil { - return 0, err - } - - tableName := session.statement.TableName() - // TODO: Oracle support needed - var top string - if st.LimitN != nil { - limitValue := *st.LimitN - switch session.engine.dialect.URI().DBType { - case schemas.MYSQL: - fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) - case schemas.SQLITE: - fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) - - cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)", - session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...)) - - whereWriter = builder.NewWriter() - fmt.Fprint(whereWriter, "WHERE ") - if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil { - return 0, err - } - case schemas.POSTGRES: - fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) - - cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)", - session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...)) - - whereWriter = builder.NewWriter() - fmt.Fprint(whereWriter, "WHERE ") - if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil { - return 0, err - } - case schemas.MSSQL: - if st.HasOrderBy() && table != nil && len(table.PrimaryKeys) == 1 { - cond = builder.Expr(fmt.Sprintf("%s IN (SELECT TOP (%d) %s FROM %v%v)", - table.PrimaryKeys[0], limitValue, table.PrimaryKeys[0], - session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...) - - whereWriter = builder.NewWriter() - fmt.Fprint(whereWriter, "WHERE ") - if err := cond.WriteTo(whereWriter); err != nil { - return 0, err - } - } else { - top = fmt.Sprintf("TOP (%d) ", limitValue) - } - } - } - - tableAlias := session.engine.Quote(tableName) - var fromSQL string - if session.statement.TableAlias != "" { - switch session.engine.dialect.URI().DBType { - case schemas.MSSQL: - fromSQL = fmt.Sprintf("FROM %s %s ", tableAlias, session.statement.TableAlias) - tableAlias = session.statement.TableAlias - default: - tableAlias = fmt.Sprintf("%s AS %s", tableAlias, session.statement.TableAlias) - } - } - updateWriter := builder.NewWriter() - if _, err := fmt.Fprintf(updateWriter, "UPDATE %v%v SET %v %v", - top, - tableAlias, - strings.Join(colNames, ", "), - fromSQL); err != nil { - return 0, err - } - if err := utils.WriteBuilder(updateWriter, whereWriter); err != nil { + if err := session.statement.WriteUpdate(updateWriter, cond, colNames); err != nil { return 0, err } + tableName := session.statement.TableName() // table name must been get before exec because statement will be reset + useCache := session.statement.UseCache + res, err := session.exec(updateWriter.String(), append(args, updateWriter.Args()...)...) if err != nil { return 0, err @@ -436,8 +243,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } - if cacher := session.engine.GetCacher(tableName); cacher != nil && session.statement.UseCache { - // session.cacheUpdate(table, tableName, sqlStr, args...) + if cacher := session.engine.GetCacher(tableName); cacher != nil && useCache { session.engine.logger.Debugf("[cache] clear table: %v", tableName) cacher.ClearIds(tableName) cacher.ClearBeans(tableName) From dabcb4c0ee5b0ccf7925ce89bfa229daaf452b9a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 20 Jul 2023 14:48:29 +0000 Subject: [PATCH 102/116] Return error if count returned no row (#2298) Fix #2139 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2298 --- session_stats.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/session_stats.go b/session_stats.go index 5d0da5e9..be98e467 100644 --- a/session_stats.go +++ b/session_stats.go @@ -24,7 +24,7 @@ func (session *Session) Count(bean ...interface{}) (int64, error) { var total int64 err = session.queryRow(sqlStr, args...).Scan(&total) - if err == sql.ErrNoRows || err == nil { + if err == nil { return total, nil } @@ -70,12 +70,12 @@ func (session *Session) SumInt(bean interface{}, columnName string) (res int64, // Sums call sum some columns. bean's non-empty fields are conditions. func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) { - var res = make([]float64, len(columnNames)) + res := make([]float64, len(columnNames)) return res, session.sum(&res, bean, columnNames...) } // SumsInt sum specify columns and return as []int64 instead of []float64 func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { - var res = make([]int64, len(columnNames)) + res := make([]int64, len(columnNames)) return res, session.sum(&res, bean, columnNames...) } From 96ed5584e3a5fec478cc294cd6485a82e4375ef3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 21 Jul 2023 16:25:58 +0000 Subject: [PATCH 103/116] move sql geneartion for update to statement (#2300) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2300 --- internal/statements/delete.go | 122 ++++++++++++++++++++++++++++++++ internal/statements/order_by.go | 2 +- session_delete.go | 93 ++++-------------------- 3 files changed, 136 insertions(+), 81 deletions(-) create mode 100644 internal/statements/delete.go diff --git a/internal/statements/delete.go b/internal/statements/delete.go new file mode 100644 index 00000000..fccebbed --- /dev/null +++ b/internal/statements/delete.go @@ -0,0 +1,122 @@ +// Copyright 2023 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 statements + +import ( + "errors" + "fmt" + "time" + + "xorm.io/builder" + "xorm.io/xorm/internal/utils" + "xorm.io/xorm/schemas" +) + +func (statement *Statement) writeDeleteOrder(w builder.Writer) error { + if err := statement.WriteOrderBy(w); err != nil { + return err + } + + if statement.LimitN != nil && *statement.LimitN > 0 { + limitNValue := *statement.LimitN + if _, err := fmt.Fprintf(w, " LIMIT %d", limitNValue); err != nil { + return err + } + } + + return nil +} + +// ErrNotImplemented not implemented +var ErrNotImplemented = errors.New("Not implemented") + +func (statement *Statement) writeOrderCond(orderCondWriter *builder.BytesWriter, tableName string) error { + orderSQLWriter := builder.NewWriter() + if err := statement.writeDeleteOrder(orderSQLWriter); err != nil { + return err + } + + if orderSQLWriter.Len() == 0 { + return nil + } + + switch statement.dialect.URI().DBType { + case schemas.POSTGRES: + if statement.cond.IsValid() { + if _, err := fmt.Fprint(orderCondWriter, " AND "); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(orderCondWriter, " WHERE "); err != nil { + return err + } + } + if _, err := fmt.Fprintf(orderCondWriter, "ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQLWriter.String()); err != nil { + return err + } + orderCondWriter.Append(orderSQLWriter.Args()...) + return nil + case schemas.SQLITE: + if statement.cond.IsValid() { + if _, err := fmt.Fprint(orderCondWriter, " AND "); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(orderCondWriter, " WHERE "); err != nil { + return err + } + } + if _, err := fmt.Fprintf(orderCondWriter, "rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQLWriter.String()); err != nil { + return err + } + orderCondWriter.Append(orderSQLWriter.Args()...) + return nil + // TODO: how to handle delete limit on mssql? + case schemas.MSSQL: + return ErrNotImplemented + default: + return utils.WriteBuilder(orderCondWriter, orderSQLWriter) + } +} + +func (statement *Statement) WriteDelete(realSQLWriter, deleteSQLWriter *builder.BytesWriter, nowTime func(*schemas.Column) (interface{}, time.Time, error)) error { + tableNameNoQuote := statement.TableName() + tableName := statement.dialect.Quoter().Quote(tableNameNoQuote) + table := statement.RefTable + if _, err := fmt.Fprint(deleteSQLWriter, "DELETE FROM ", tableName); err != nil { + return err + } + if err := statement.writeWhere(deleteSQLWriter); err != nil { + return err + } + + orderCondWriter := builder.NewWriter() + if err := statement.writeOrderCond(orderCondWriter, tableName); err != nil { + return err + } + + if statement.GetUnscoped() || table == nil || table.DeletedColumn() == nil { // tag "deleted" is disabled + return utils.WriteBuilder(realSQLWriter, deleteSQLWriter, orderCondWriter) + } + + deletedColumn := table.DeletedColumn() + if _, err := fmt.Fprintf(realSQLWriter, "UPDATE %v SET %v = ?", + statement.dialect.Quoter().Quote(statement.TableName()), + statement.dialect.Quoter().Quote(deletedColumn.Name)); err != nil { + return err + } + + val, _, err := nowTime(deletedColumn) + if err != nil { + return err + } + realSQLWriter.Append(val) + + if err := statement.writeWhere(realSQLWriter); err != nil { + return err + } + + return utils.WriteBuilder(realSQLWriter, orderCondWriter) +} diff --git a/internal/statements/order_by.go b/internal/statements/order_by.go index 08a8263b..b7eeeb87 100644 --- a/internal/statements/order_by.go +++ b/internal/statements/order_by.go @@ -24,7 +24,7 @@ func (statement *Statement) ResetOrderBy() { // WriteOrderBy write order by to writer func (statement *Statement) WriteOrderBy(w builder.Writer) error { if len(statement.orderStr) > 0 { - if _, err := fmt.Fprintf(w, " ORDER BY %s", statement.orderStr); err != nil { + if _, err := fmt.Fprint(w, " ORDER BY ", statement.orderStr); err != nil { return err } w.Append(statement.orderArgs...) diff --git a/session_delete.go b/session_delete.go index 6c63d9b0..7336040f 100644 --- a/session_delete.go +++ b/session_delete.go @@ -6,22 +6,15 @@ package xorm import ( "errors" - "fmt" "strconv" "xorm.io/builder" "xorm.io/xorm/caches" - "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) -var ( - // ErrNeedDeletedCond delete needs less one condition error - ErrNeedDeletedCond = errors.New("Delete action needs at least one condition") - - // ErrNotImplemented not implemented - ErrNotImplemented = errors.New("Not implemented") -) +// ErrNeedDeletedCond delete needs less one condition error +var ErrNeedDeletedCond = errors.New("Delete action needs at least one condition") func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr string, args ...interface{}) error { if table == nil || @@ -112,9 +105,8 @@ func (session *Session) delete(beans []interface{}, mustHaveConditions bool) (in } var ( - condWriter = builder.NewWriter() - err error - bean interface{} + err error + bean interface{} ) if len(beans) > 0 { bean = beans[0] @@ -133,90 +125,27 @@ func (session *Session) delete(beans []interface{}, mustHaveConditions bool) (in } } - if err = session.statement.Conds().WriteTo(session.statement.QuoteReplacer(condWriter)); err != nil { - return 0, err - } - pLimitN := session.statement.LimitN - if mustHaveConditions && condWriter.Len() == 0 && (pLimitN == nil || *pLimitN == 0) { + if mustHaveConditions && !session.statement.Conds().IsValid() && (pLimitN == nil || *pLimitN == 0) { return 0, ErrNeedDeletedCond } tableNameNoQuote := session.statement.TableName() - tableName := session.engine.Quote(tableNameNoQuote) table := session.statement.RefTable - deleteSQLWriter := builder.NewWriter() - fmt.Fprintf(deleteSQLWriter, "DELETE FROM %v", tableName) - if condWriter.Len() > 0 { - fmt.Fprintf(deleteSQLWriter, " WHERE %v", condWriter.String()) - deleteSQLWriter.Append(condWriter.Args()...) - } - orderSQLWriter := builder.NewWriter() - if err := session.statement.WriteOrderBy(orderSQLWriter); err != nil { + realSQLWriter := builder.NewWriter() + deleteSQLWriter := builder.NewWriter() + if err := session.statement.WriteDelete(realSQLWriter, deleteSQLWriter, session.engine.nowTime); err != nil { return 0, err } - if pLimitN != nil && *pLimitN > 0 { - limitNValue := *pLimitN - if _, err := fmt.Fprintf(orderSQLWriter, " LIMIT %d", limitNValue); err != nil { - return 0, err - } - } - - orderCondWriter := builder.NewWriter() - if orderSQLWriter.Len() > 0 { - switch session.engine.dialect.URI().DBType { - case schemas.POSTGRES: - if condWriter.Len() > 0 { - fmt.Fprintf(orderCondWriter, " AND ") - } else { - fmt.Fprintf(orderCondWriter, " WHERE ") - } - fmt.Fprintf(orderCondWriter, "ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQLWriter.String()) - orderCondWriter.Append(orderSQLWriter.Args()...) - case schemas.SQLITE: - if condWriter.Len() > 0 { - fmt.Fprintf(orderCondWriter, " AND ") - } else { - fmt.Fprintf(orderCondWriter, " WHERE ") - } - fmt.Fprintf(orderCondWriter, "rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQLWriter.String()) - // TODO: how to handle delete limit on mssql? - case schemas.MSSQL: - return 0, ErrNotImplemented - default: - fmt.Fprint(orderCondWriter, orderSQLWriter.String()) - orderCondWriter.Append(orderSQLWriter.Args()...) - } - } - - realSQLWriter := builder.NewWriter() - argsForCache := make([]interface{}, 0, len(deleteSQLWriter.Args())*2) - copy(argsForCache, deleteSQLWriter.Args()) - argsForCache = append(deleteSQLWriter.Args(), argsForCache...) if session.statement.GetUnscoped() || table == nil || table.DeletedColumn() == nil { // tag "deleted" is disabled - if err := utils.WriteBuilder(realSQLWriter, deleteSQLWriter, orderCondWriter); err != nil { - return 0, err - } } else { deletedColumn := table.DeletedColumn() - if _, err := fmt.Fprintf(realSQLWriter, "UPDATE %v SET %v = ? WHERE %v", - session.engine.Quote(session.statement.TableName()), - session.engine.Quote(deletedColumn.Name), - condWriter.String()); err != nil { - return 0, err - } - val, t, err := session.engine.nowTime(deletedColumn) + _, t, err := session.engine.nowTime(deletedColumn) if err != nil { return 0, err } - realSQLWriter.Append(val) - realSQLWriter.Append(condWriter.Args()...) - - if err := utils.WriteBuilder(realSQLWriter, orderCondWriter); err != nil { - return 0, err - } colName := deletedColumn.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { @@ -225,6 +154,10 @@ func (session *Session) delete(beans []interface{}, mustHaveConditions bool) (in }) } + argsForCache := make([]interface{}, 0, len(deleteSQLWriter.Args())*2) + copy(argsForCache, deleteSQLWriter.Args()) + argsForCache = append(deleteSQLWriter.Args(), argsForCache...) + if cacher := session.engine.GetCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache { _ = session.cacheDelete(table, tableNameNoQuote, deleteSQLWriter.String(), argsForCache...) } From 9988dac44d7c3c9a5658567a105ff890b36bd3d1 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 22 Jul 2023 11:52:38 +0000 Subject: [PATCH 104/116] improve write order by (#2301) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2301 --- internal/statements/delete.go | 4 +- internal/statements/order_by.go | 102 ++++++++++++++++--------------- internal/statements/query.go | 4 +- internal/statements/statement.go | 9 ++- internal/statements/update.go | 2 +- 5 files changed, 63 insertions(+), 58 deletions(-) diff --git a/internal/statements/delete.go b/internal/statements/delete.go index fccebbed..a77cf862 100644 --- a/internal/statements/delete.go +++ b/internal/statements/delete.go @@ -14,8 +14,8 @@ import ( "xorm.io/xorm/schemas" ) -func (statement *Statement) writeDeleteOrder(w builder.Writer) error { - if err := statement.WriteOrderBy(w); err != nil { +func (statement *Statement) writeDeleteOrder(w *builder.BytesWriter) error { + if err := statement.writeOrderBys(w); err != nil { return err } diff --git a/internal/statements/order_by.go b/internal/statements/order_by.go index b7eeeb87..595c0430 100644 --- a/internal/statements/order_by.go +++ b/internal/statements/order_by.go @@ -6,85 +6,91 @@ package statements import ( "fmt" - "strings" "xorm.io/builder" ) +type orderBy struct { + orderStr interface{} + orderArgs []interface{} + direction string // ASC, DESC or "", "" means raw orderStr +} + func (statement *Statement) HasOrderBy() bool { - return statement.orderStr != "" + return len(statement.orderBy) > 0 } // ResetOrderBy reset ordery conditions func (statement *Statement) ResetOrderBy() { - statement.orderStr = "" - statement.orderArgs = nil + statement.orderBy = []orderBy{} +} + +func (statement *Statement) writeOrderBy(w *builder.BytesWriter, orderBy orderBy) error { + switch t := orderBy.orderStr.(type) { + case (*builder.Expression): + if _, err := fmt.Fprint(w.Builder, statement.dialect.Quoter().Replace(t.Content())); err != nil { + return err + } + w.Append(t.Args()...) + return nil + case string: + if orderBy.direction == "" { + if _, err := fmt.Fprint(w.Builder, statement.dialect.Quoter().Replace(t)); err != nil { + return err + } + w.Append(orderBy.orderArgs...) + return nil + } + if err := statement.dialect.Quoter().QuoteTo(w.Builder, t); err != nil { + return err + } + _, err := fmt.Fprint(w, " ", orderBy.direction) + return err + default: + return ErrUnSupportedSQLType + } } // WriteOrderBy write order by to writer -func (statement *Statement) WriteOrderBy(w builder.Writer) error { - if len(statement.orderStr) > 0 { - if _, err := fmt.Fprint(w, " ORDER BY ", statement.orderStr); err != nil { +func (statement *Statement) writeOrderBys(w *builder.BytesWriter) error { + if len(statement.orderBy) == 0 { + return nil + } + + if _, err := fmt.Fprint(w, " ORDER BY "); err != nil { + return err + } + for i, ob := range statement.orderBy { + if err := statement.writeOrderBy(w, ob); err != nil { return err } - w.Append(statement.orderArgs...) + if i < len(statement.orderBy)-1 { + if _, err := fmt.Fprint(w, ", "); err != nil { + return err + } + } } return nil } // OrderBy generate "Order By order" statement func (statement *Statement) OrderBy(order interface{}, args ...interface{}) *Statement { - if len(statement.orderStr) > 0 { - statement.orderStr += ", " - } - var rawOrder string - switch t := order.(type) { - case (*builder.Expression): - rawOrder = t.Content() - args = t.Args() - case string: - rawOrder = t - default: - statement.LastError = ErrUnSupportedSQLType - return statement - } - statement.orderStr += statement.ReplaceQuote(rawOrder) - if len(args) > 0 { - statement.orderArgs = append(statement.orderArgs, args...) - } + statement.orderBy = append(statement.orderBy, orderBy{order, args, ""}) return statement } // Desc generate `ORDER BY xx DESC` func (statement *Statement) Desc(colNames ...string) *Statement { - var buf strings.Builder - if len(statement.orderStr) > 0 { - fmt.Fprint(&buf, statement.orderStr, ", ") + for _, colName := range colNames { + statement.orderBy = append(statement.orderBy, orderBy{colName, nil, "DESC"}) } - for i, col := range colNames { - if i > 0 { - fmt.Fprint(&buf, ", ") - } - _ = statement.dialect.Quoter().QuoteTo(&buf, col) - fmt.Fprint(&buf, " DESC") - } - statement.orderStr = buf.String() return statement } // Asc provide asc order by query condition, the input parameters are columns. func (statement *Statement) Asc(colNames ...string) *Statement { - var buf strings.Builder - if len(statement.orderStr) > 0 { - fmt.Fprint(&buf, statement.orderStr, ", ") + for _, colName := range colNames { + statement.orderBy = append(statement.orderBy, orderBy{colName, nil, "ASC"}) } - for i, col := range colNames { - if i > 0 { - fmt.Fprint(&buf, ", ") - } - _ = statement.dialect.Quoter().QuoteTo(&buf, col) - fmt.Fprint(&buf, " ASC") - } - statement.orderStr = buf.String() return statement } diff --git a/internal/statements/query.go b/internal/statements/query.go index 2e38f0fe..63e079e7 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -320,7 +320,7 @@ func (statement *Statement) writeMssqlPaginationCond(w *builder.BytesWriter) err if err := statement.writeWhere(subWriter); err != nil { return err } - if err := statement.WriteOrderBy(subWriter); err != nil { + if err := statement.writeOrderBys(subWriter); err != nil { return err } if err := statement.writeGroupBy(subWriter); err != nil { @@ -375,7 +375,7 @@ func (statement *Statement) writeSelect(buf *builder.BytesWriter, columnStr stri if err := statement.writeHaving(buf); err != nil { return err } - if err := statement.WriteOrderBy(buf); err != nil { + if err := statement.writeOrderBys(buf); err != nil { return err } diff --git a/internal/statements/statement.go b/internal/statements/statement.go index afc38a2e..017f40a5 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -50,8 +50,7 @@ type Statement struct { Start int LimitN *int idParam schemas.PK - orderStr string - orderArgs []interface{} + orderBy []orderBy joins []join GroupByStr string HavingStr string @@ -163,15 +162,15 @@ func (statement *Statement) Reset() { // SQL adds raw sql statement func (statement *Statement) SQL(query interface{}, args ...interface{}) *Statement { - switch query.(type) { + switch t := query.(type) { case (*builder.Builder): var err error - statement.RawSQL, statement.RawParams, err = query.(*builder.Builder).ToSQL() + statement.RawSQL, statement.RawParams, err = t.ToSQL() if err != nil { statement.LastError = err } case string: - statement.RawSQL = query.(string) + statement.RawSQL = t statement.RawParams = args default: statement.LastError = ErrUnSupportedSQLType diff --git a/internal/statements/update.go b/internal/statements/update.go index 16ab5676..f0914b0b 100644 --- a/internal/statements/update.go +++ b/internal/statements/update.go @@ -319,7 +319,7 @@ func (statement *Statement) WriteUpdate(updateWriter *builder.BytesWriter, cond if err := cond.WriteTo(statement.QuoteReplacer(whereWriter)); err != nil { return err } - if err := statement.WriteOrderBy(whereWriter); err != nil { + if err := statement.writeOrderBys(whereWriter); err != nil { return err } From 6c29ab378ed5f3daab40c59256a1f105669b080e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 22 Jul 2023 15:24:19 +0000 Subject: [PATCH 105/116] refactor write insert sql (#2302) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2302 --- internal/statements/insert.go | 96 +++++++++++++++++++++++++++++++++++ internal/statements/join.go | 2 +- internal/statements/query.go | 2 +- session_insert.go | 51 ++++++++----------- 4 files changed, 118 insertions(+), 33 deletions(-) diff --git a/internal/statements/insert.go b/internal/statements/insert.go index 187b94a3..9370c984 100644 --- a/internal/statements/insert.go +++ b/internal/statements/insert.go @@ -293,3 +293,99 @@ func (statement *Statement) GenInsertMultipleMapSQL(columns []string, argss [][] return buf.String(), buf.Args(), nil } + +func (statement *Statement) writeColumns(w *builder.BytesWriter, slice []string) error { + for i, s := range slice { + if i > 0 { + if _, err := fmt.Fprint(w, ","); err != nil { + return err + } + } + if err := statement.dialect.Quoter().QuoteTo(w.Builder, s); err != nil { + return err + } + } + return nil +} + +func (statement *Statement) writeQuestions(w *builder.BytesWriter, length int) error { + for i := 0; i < length; i++ { + if i > 0 { + if _, err := fmt.Fprint(w, ","); err != nil { + return err + } + } + if _, err := fmt.Fprint(w, "?"); err != nil { + return err + } + } + return nil +} + +func (statement *Statement) oracleWriteInsertMultiple(w *builder.BytesWriter, tableName string, colNames []string, colMultiPlaces []string) error { + if _, err := fmt.Fprint(w, "INSERT ALL"); err != nil { + return err + } + + for _, cols := range colMultiPlaces { + if _, err := fmt.Fprint(w, " INTO "); err != nil { + return err + } + if err := statement.dialect.Quoter().QuoteTo(w.Builder, tableName); err != nil { + return err + } + if _, err := fmt.Fprint(w, " ("); err != nil { + return err + } + if err := statement.writeColumns(w, colNames); err != nil { + return err + } + if _, err := fmt.Fprint(w, ") VALUES ("); err != nil { + return err + } + if _, err := fmt.Fprintf(w, cols, ")"); err != nil { + return err + } + } + + if _, err := fmt.Fprint(w, " SELECT 1 FROM DUAL"); err != nil { + return err + } + return nil +} + +func (statement *Statement) WriteInsertMultiple(w *builder.BytesWriter, tableName string, colNames []string, colMultiPlaces []string) error { + if statement.dialect.URI().DBType == schemas.ORACLE { + return statement.oracleWriteInsertMultiple(w, tableName, colNames, colMultiPlaces) + } + return statement.plainWriteInsertMultiple(w, tableName, colNames, colMultiPlaces) +} + +func (statement *Statement) plainWriteInsertMultiple(w *builder.BytesWriter, tableName string, colNames []string, colMultiPlaces []string) error { + if _, err := fmt.Fprint(w, "INSERT INTO "); err != nil { + return err + } + if err := statement.dialect.Quoter().QuoteTo(w.Builder, tableName); err != nil { + return err + } + if _, err := fmt.Fprint(w, " ("); err != nil { + return err + } + if err := statement.writeColumns(w, colNames); err != nil { + return err + } + if _, err := fmt.Fprint(w, ") VALUES ("); err != nil { + return err + } + for i, cols := range colMultiPlaces { + if _, err := fmt.Fprint(w, cols, ")"); err != nil { + return err + } + if i < len(colMultiPlaces)-1 { + if _, err := fmt.Fprint(w, ",("); err != nil { + return err + } + } + } + return nil +} diff --git a/internal/statements/join.go b/internal/statements/join.go index 61a1b4de..740b31de 100644 --- a/internal/statements/join.go +++ b/internal/statements/join.go @@ -36,7 +36,7 @@ func (statement *Statement) writeJoins(w *builder.BytesWriter) error { func (statement *Statement) writeJoin(buf *builder.BytesWriter, join join) error { // write join operator - if _, err := fmt.Fprintf(buf, " %v JOIN", join.op); err != nil { + if _, err := fmt.Fprint(buf, " ", join.op, " JOIN"); err != nil { return err } diff --git a/internal/statements/query.go b/internal/statements/query.go index 63e079e7..211ba268 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -230,7 +230,7 @@ func (statement *Statement) writeDistinct(w builder.Writer) error { } func (statement *Statement) writeSelectColumns(w *builder.BytesWriter, columnStr string) error { - if _, err := fmt.Fprintf(w, "SELECT "); err != nil { + if _, err := fmt.Fprintf(w, "SELECT"); err != nil { return err } if err := statement.writeDistinct(w); err != nil { diff --git a/session_insert.go b/session_insert.go index cfa26d39..7003e0f7 100644 --- a/session_insert.go +++ b/session_insert.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "xorm.io/builder" "xorm.io/xorm/convert" "xorm.io/xorm/dialects" "xorm.io/xorm/internal/utils" @@ -156,14 +157,14 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e } args = append(args, val) - var colName = col.Name + colName := col.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { col := table.GetColumn(colName) setColumnTime(bean, col, t) }) } else if col.IsVersion && session.statement.CheckVersion { args = append(args, 1) - var colName = col.Name + colName := col.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { col := table.GetColumn(colName) setColumnInt(bean, col, 1) @@ -186,24 +187,12 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e } cleanupProcessorsClosures(&session.beforeClosures) - quoter := session.engine.dialect.Quoter() - var sql string - colStr := quoter.Join(colNames, ",") - if session.engine.dialect.URI().DBType == schemas.ORACLE { - temp := fmt.Sprintf(") INTO %s (%v) VALUES (", - quoter.Quote(tableName), - colStr) - sql = fmt.Sprintf("INSERT ALL INTO %s (%v) VALUES (%v) SELECT 1 FROM DUAL", - quoter.Quote(tableName), - colStr, - strings.Join(colMultiPlaces, temp)) - } else { - sql = fmt.Sprintf("INSERT INTO %s (%v) VALUES (%v)", - quoter.Quote(tableName), - colStr, - strings.Join(colMultiPlaces, "),(")) + w := builder.NewWriter() + if err := session.statement.WriteInsertMultiple(w, tableName, colNames, colMultiPlaces); err != nil { + return 0, err } - res, err := session.exec(sql, args...) + + res, err := session.exec(w.String(), args...) if err != nil { return 0, err } @@ -276,7 +265,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { processor.BeforeInsert() } - var tableName = session.statement.TableName() + tableName := session.statement.TableName() table := session.statement.RefTable colNames, args, err := session.genInsertColumns(bean) @@ -517,7 +506,7 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac } args = append(args, val) - var colName = col.Name + colName := col.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { col := table.GetColumn(colName) setColumnTime(bean, col, t) @@ -547,7 +536,7 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err return 0, ErrTableNotFound } - var columns = make([]string, 0, len(m)) + columns := make([]string, 0, len(m)) exprs := session.statement.ExprColumns for k := range m { if !exprs.IsColExist(k) { @@ -556,7 +545,7 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err } sort.Strings(columns) - var args = make([]interface{}, 0, len(m)) + args := make([]interface{}, 0, len(m)) for _, colName := range columns { args = append(args, m[colName]) } @@ -574,7 +563,7 @@ func (session *Session) insertMultipleMapInterface(maps []map[string]interface{} return 0, ErrTableNotFound } - var columns = make([]string, 0, len(maps[0])) + columns := make([]string, 0, len(maps[0])) exprs := session.statement.ExprColumns for k := range maps[0] { if !exprs.IsColExist(k) { @@ -583,9 +572,9 @@ func (session *Session) insertMultipleMapInterface(maps []map[string]interface{} } sort.Strings(columns) - var argss = make([][]interface{}, 0, len(maps)) + argss := make([][]interface{}, 0, len(maps)) for _, m := range maps { - var args = make([]interface{}, 0, len(m)) + args := make([]interface{}, 0, len(m)) for _, colName := range columns { args = append(args, m[colName]) } @@ -605,7 +594,7 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { return 0, ErrTableNotFound } - var columns = make([]string, 0, len(m)) + columns := make([]string, 0, len(m)) exprs := session.statement.ExprColumns for k := range m { if !exprs.IsColExist(k) { @@ -615,7 +604,7 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { sort.Strings(columns) - var args = make([]interface{}, 0, len(m)) + args := make([]interface{}, 0, len(m)) for _, colName := range columns { args = append(args, m[colName]) } @@ -633,7 +622,7 @@ func (session *Session) insertMultipleMapString(maps []map[string]string) (int64 return 0, ErrTableNotFound } - var columns = make([]string, 0, len(maps[0])) + columns := make([]string, 0, len(maps[0])) exprs := session.statement.ExprColumns for k := range maps[0] { if !exprs.IsColExist(k) { @@ -642,9 +631,9 @@ func (session *Session) insertMultipleMapString(maps []map[string]string) (int64 } sort.Strings(columns) - var argss = make([][]interface{}, 0, len(maps)) + argss := make([][]interface{}, 0, len(maps)) for _, m := range maps { - var args = make([]interface{}, 0, len(m)) + args := make([]interface{}, 0, len(m)) for _, colName := range columns { args = append(args, m[colName]) } From c0d09c0def61d6e7bea1c4a9b36f71c1d54ef47f Mon Sep 17 00:00:00 2001 From: datbeohbbh Date: Sun, 23 Jul 2023 01:30:49 +0000 Subject: [PATCH 106/116] update go version in `go.mod` (#2224) Hi! I would like to request an update to the `go.mod` file. Co-authored-by: datbeohbbh Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2224 Co-authored-by: datbeohbbh Co-committed-by: datbeohbbh --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5f2f18e0..b48b3bbc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module xorm.io/xorm -go 1.13 +go 1.16 require ( gitee.com/travelliu/dm v1.8.11192 From 3626de1459be9e93d30f35735872492068886bba Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 23 Jul 2023 01:31:59 +0000 Subject: [PATCH 107/116] mod tidy (#2303) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2303 --- go.sum | 3 --- 1 file changed, 3 deletions(-) diff --git a/go.sum b/go.sum index 9f197fae..5c780ec6 100644 --- a/go.sum +++ b/go.sum @@ -24,7 +24,6 @@ github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDror github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -39,7 +38,6 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -231,7 +229,6 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= From 24a672be3cafdf150701988ffd9e47ac9cff801d Mon Sep 17 00:00:00 2001 From: arturwwl Date: Sun, 23 Jul 2023 02:34:10 +0000 Subject: [PATCH 108/116] convert - String2Time accept HH:mm:ss format (#2074) resolves #2073 Co-authored-by: arturwwl <> Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2074 Co-authored-by: arturwwl Co-committed-by: arturwwl --- convert/time.go | 16 +++++++++++++++- convert/time_test.go | 13 +++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/convert/time.go b/convert/time.go index dc36912b..c923e955 100644 --- a/convert/time.go +++ b/convert/time.go @@ -15,6 +15,7 @@ import ( ) // String2Time converts a string to time with original location +// be aware for time strings (HH:mm:ss) returns zero year (LMT) for converted location func String2Time(s string, originalLocation *time.Location, convertedLocation *time.Location) (*time.Time, error) { if len(s) == 19 { if s == utils.ZeroTime0 || s == utils.ZeroTime1 { @@ -32,6 +33,7 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t return nil, err } dt = dt.In(convertedLocation) + dt.IsZero() return &dt, nil } else if len(s) == 25 && s[10] == 'T' && s[19] == '+' && s[22] == ':' { dt, err := time.Parse(time.RFC3339, s) @@ -48,7 +50,7 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t dt = dt.In(convertedLocation) return &dt, nil } else if len(s) >= 21 && s[19] == '.' { - var layout = "2006-01-02 15:04:05." + strings.Repeat("0", len(s)-20) + layout := "2006-01-02 15:04:05." + strings.Repeat("0", len(s)-20) dt, err := time.ParseInLocation(layout, s, originalLocation) if err != nil { return nil, err @@ -65,6 +67,18 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t } dt = dt.In(convertedLocation) return &dt, nil + } else if len(s) == 8 && s[2] == ':' && s[5] == ':' { + currentDate := time.Now() + dt, err := time.ParseInLocation("15:04:05", s, originalLocation) + if err != nil { + return nil, err + } + // add current date for correct time locations + dt = dt.AddDate(currentDate.Year(), int(currentDate.Month()), currentDate.Day()) + dt = dt.In(convertedLocation) + // back to zero year + dt = dt.AddDate(-currentDate.Year(), int(-currentDate.Month()), -currentDate.Day()) + return &dt, nil } else { i, err := strconv.ParseInt(s, 10, 64) if err == nil { diff --git a/convert/time_test.go b/convert/time_test.go index 4b1c2279..d7a9d5ad 100644 --- a/convert/time_test.go +++ b/convert/time_test.go @@ -15,7 +15,7 @@ func TestString2Time(t *testing.T) { expectedLoc, err := time.LoadLocation("Asia/Shanghai") assert.NoError(t, err) - var kases = map[string]time.Time{ + cases := map[string]time.Time{ "2021-08-10": time.Date(2021, 8, 10, 8, 0, 0, 0, expectedLoc), "2021-07-11 10:44:00": time.Date(2021, 7, 11, 18, 44, 0, 0, expectedLoc), "2021-07-11 10:44:00.999": time.Date(2021, 7, 11, 18, 44, 0, 999000000, expectedLoc), @@ -25,12 +25,13 @@ func TestString2Time(t *testing.T) { "2021-06-06T22:58:20.999+08:00": time.Date(2021, 6, 6, 22, 58, 20, 999000000, expectedLoc), "2021-06-06T22:58:20.999999+08:00": time.Date(2021, 6, 6, 22, 58, 20, 999999000, expectedLoc), "2021-06-06T22:58:20.999999999+08:00": time.Date(2021, 6, 6, 22, 58, 20, 999999999, expectedLoc), - "2021-08-10T10:33:04Z": time.Date(2021, 8, 10, 18, 33, 04, 0, expectedLoc), - "2021-08-10T10:33:04.999Z": time.Date(2021, 8, 10, 18, 33, 04, 999000000, expectedLoc), - "2021-08-10T10:33:04.999999Z": time.Date(2021, 8, 10, 18, 33, 04, 999999000, expectedLoc), - "2021-08-10T10:33:04.999999999Z": time.Date(2021, 8, 10, 18, 33, 04, 999999999, expectedLoc), + "2021-08-10T10:33:04Z": time.Date(2021, 8, 10, 18, 33, 0o4, 0, expectedLoc), + "2021-08-10T10:33:04.999Z": time.Date(2021, 8, 10, 18, 33, 0o4, 999000000, expectedLoc), + "2021-08-10T10:33:04.999999Z": time.Date(2021, 8, 10, 18, 33, 0o4, 999999000, expectedLoc), + "2021-08-10T10:33:04.999999999Z": time.Date(2021, 8, 10, 18, 33, 0o4, 999999999, expectedLoc), + "10:22:33": time.Date(0, 1, 1, 18, 22, 33, 0, expectedLoc), } - for layout, tm := range kases { + for layout, tm := range cases { t.Run(layout, func(t *testing.T) { target, err := String2Time(layout, time.UTC, expectedLoc) assert.NoError(t, err) From a13564976c480324a73f85323579f385b12b776f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 24 Jul 2023 07:57:05 +0000 Subject: [PATCH 109/116] refactor write update sql (#2304) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2304 --- integrations/session_update_test.go | 57 ++++++-- internal/statements/query.go | 11 +- internal/statements/update.go | 215 ++++++++++++++++++++-------- session_update.go | 4 +- 4 files changed, 208 insertions(+), 79 deletions(-) diff --git a/integrations/session_update_test.go b/integrations/session_update_test.go index 45338cad..2a8f8187 100644 --- a/integrations/session_update_test.go +++ b/integrations/session_update_test.go @@ -28,7 +28,7 @@ func TestUpdateMap(t *testing.T) { } assert.NoError(t, testEngine.Sync(new(UpdateTable))) - var tb = UpdateTable{ + tb := UpdateTable{ Name: "test", Age: 35, } @@ -79,7 +79,7 @@ func TestUpdateLimit(t *testing.T) { } assert.NoError(t, testEngine.Sync(new(UpdateTable2))) - var tb = UpdateTable2{ + tb := UpdateTable2{ Name: "test1", Age: 35, } @@ -400,7 +400,7 @@ func TestUpdate1(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var s = "test" + s := "test" col1 := &UpdateAllCols{Ptr: &s} err = testEngine.Sync(col1) @@ -864,7 +864,7 @@ func TestCreatedUpdated2(t *testing.T) { assertSync(t, new(CreatedUpdatedStruct)) - var s = CreatedUpdatedStruct{ + s := CreatedUpdatedStruct{ Name: "test", } cnt, err := testEngine.Insert(&s) @@ -874,7 +874,7 @@ func TestCreatedUpdated2(t *testing.T) { time.Sleep(time.Second) - var s1 = CreatedUpdatedStruct{ + s1 := CreatedUpdatedStruct{ Name: "test1", CreateAt: s.CreateAt, UpdateAt: s.UpdateAt, @@ -907,7 +907,7 @@ func TestDeletedUpdate(t *testing.T) { assertSync(t, new(DeletedUpdatedStruct)) - var s = DeletedUpdatedStruct{ + s := DeletedUpdatedStruct{ Name: "test", } cnt, err := testEngine.Insert(&s) @@ -956,7 +956,7 @@ func TestUpdateMapCondition(t *testing.T) { assertSync(t, new(UpdateMapCondition)) - var c = UpdateMapCondition{ + c := UpdateMapCondition{ String: "string", } _, err := testEngine.Insert(&c) @@ -990,7 +990,7 @@ func TestUpdateMapContent(t *testing.T) { assertSync(t, new(UpdateMapContent)) - var c = UpdateMapContent{ + c := UpdateMapContent{ Name: "lunny", IsMan: true, Gender: 1, @@ -1126,7 +1126,7 @@ func TestUpdateDeleted(t *testing.T) { assertSync(t, new(UpdateDeletedStruct)) - var s = UpdateDeletedStruct{ + s := UpdateDeletedStruct{ Name: "test", } cnt, err := testEngine.Insert(&s) @@ -1232,7 +1232,7 @@ func TestUpdateExprs2(t *testing.T) { assertSync(t, new(UpdateExprsRelease)) - var uer = UpdateExprsRelease{ + uer := UpdateExprsRelease{ RepoId: 1, IsTag: false, IsDraft: false, @@ -1407,7 +1407,7 @@ func TestNilFromDB(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestTable1)) - var tt0 = TestTable1{ + tt0 := TestTable1{ Field1: &TestFieldType1{ cb: []byte("string"), }, @@ -1437,7 +1437,7 @@ func TestNilFromDB(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var tt = TestTable1{ + tt := TestTable1{ UpdateTime: time.Now(), Field1: &TestFieldType1{ cb: nil, @@ -1453,7 +1453,7 @@ func TestNilFromDB(t *testing.T) { assert.True(t, has) assert.Nil(t, tt2.Field1) - var tt3 = TestTable1{ + tt3 := TestTable1{ UpdateTime: time.Now(), Field1: &TestFieldType1{ cb: []byte{}, @@ -1470,3 +1470,34 @@ func TestNilFromDB(t *testing.T) { assert.NotNil(t, tt4.Field1) assert.NotNil(t, tt4.Field1.cb) } + +/* +func TestUpdateWithJoin(t *testing.T) { + type TestUpdateWithJoin struct { + Id int64 + ExtId int64 + Name string + } + + type TestUpdateWithJoin2 struct { + Id int64 + Name string + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(TestUpdateWithJoin), new(TestUpdateWithJoin2)) + + b := TestUpdateWithJoin2{Name: "test"} + _, err := testEngine.Insert(&b) + assert.NoError(t, err) + + _, err = testEngine.Insert(&TestUpdateWithJoin{ExtId: b.Id, Name: "test"}) + assert.NoError(t, err) + + _, err = testEngine.Table("test_update_with_join"). + Join("INNER", "test_update_with_join2", "test_update_with_join.ext_id = test_update_with_join2.id"). + Where("test_update_with_join2.name = ?", "test"). + Update(&TestUpdateWithJoin{Name: "test2"}) + assert.NoError(t, err) +} +*/ diff --git a/internal/statements/query.go b/internal/statements/query.go index 211ba268..0f4c40b1 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -243,14 +243,19 @@ func (statement *Statement) writeSelectColumns(w *builder.BytesWriter, columnStr return err } -func (statement *Statement) writeWhere(w *builder.BytesWriter) error { - if !statement.cond.IsValid() { +func (statement *Statement) writeWhereCond(w *builder.BytesWriter, cond builder.Cond) error { + if !cond.IsValid() { return nil } + if _, err := fmt.Fprint(w, " WHERE "); err != nil { return err } - return statement.cond.WriteTo(statement.QuoteReplacer(w)) + return cond.WriteTo(statement.QuoteReplacer(w)) +} + +func (statement *Statement) writeWhere(w *builder.BytesWriter) error { + return statement.writeWhereCond(w, statement.cond) } func (statement *Statement) writeWhereWithMssqlPagination(w *builder.BytesWriter) error { diff --git a/internal/statements/update.go b/internal/statements/update.go index f0914b0b..1eb431a8 100644 --- a/internal/statements/update.go +++ b/internal/statements/update.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "reflect" - "strings" "time" "xorm.io/builder" @@ -311,84 +310,178 @@ func (statement *Statement) BuildUpdates(tableValue reflect.Value, return colNames, args, nil } -func (statement *Statement) WriteUpdate(updateWriter *builder.BytesWriter, cond builder.Cond, colNames []string) error { - whereWriter := builder.NewWriter() - if cond.IsValid() { - fmt.Fprint(whereWriter, "WHERE ") +func (statement *Statement) writeUpdateTop(updateWriter *builder.BytesWriter) error { + if statement.dialect.URI().DBType != schemas.MSSQL || statement.LimitN == nil { + return nil } - if err := cond.WriteTo(statement.QuoteReplacer(whereWriter)); err != nil { + + table := statement.RefTable + if statement.HasOrderBy() && table != nil && len(table.PrimaryKeys) == 1 { + return nil + } + + _, err := fmt.Fprintf(updateWriter, " TOP (%d)", *statement.LimitN) + return err +} + +func (statement *Statement) writeUpdateTableName(updateWriter *builder.BytesWriter) error { + tableName := statement.quote(statement.TableName()) + if statement.TableAlias == "" { + _, err := fmt.Fprint(updateWriter, " ", tableName) return err } - if err := statement.writeOrderBys(whereWriter); err != nil { + + switch statement.dialect.URI().DBType { + case schemas.MSSQL: + _, err := fmt.Fprint(updateWriter, " ", statement.TableAlias) return err + default: + _, err := fmt.Fprint(updateWriter, " ", tableName, " AS ", statement.TableAlias) + return err + } +} + +func (statement *Statement) writeUpdateFrom(updateWriter *builder.BytesWriter) error { + if statement.dialect.URI().DBType != schemas.MSSQL || statement.TableAlias == "" { + return nil + } + + _, err := fmt.Fprint(updateWriter, " FROM ", statement.quote(statement.TableName()), " ", statement.TableAlias) + return err +} + +func (statement *Statement) writeUpdateLimit(updateWriter *builder.BytesWriter, cond builder.Cond) error { + if statement.LimitN == nil { + return nil } table := statement.RefTable tableName := statement.TableName() - // TODO: Oracle support needed - var top string - if statement.LimitN != nil { - limitValue := *statement.LimitN - switch statement.dialect.URI().DBType { - case schemas.MYSQL: - fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) - case schemas.SQLITE: - fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) - cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)", - statement.quote(tableName), whereWriter.String()), whereWriter.Args()...)) - - whereWriter = builder.NewWriter() - fmt.Fprint(whereWriter, "WHERE ") - if err := cond.WriteTo(statement.QuoteReplacer(whereWriter)); err != nil { + limitValue := *statement.LimitN + switch statement.dialect.URI().DBType { + case schemas.MYSQL: + _, err := fmt.Fprintf(updateWriter, " LIMIT %d", limitValue) + return err + case schemas.SQLITE: + if cond.IsValid() { + if _, err := fmt.Fprint(updateWriter, " AND "); err != nil { return err } - case schemas.POSTGRES: - fmt.Fprintf(whereWriter, " LIMIT %d", limitValue) - - cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)", - statement.quote(tableName), whereWriter.String()), whereWriter.Args()...)) - - whereWriter = builder.NewWriter() - fmt.Fprint(whereWriter, "WHERE ") - if err := cond.WriteTo(statement.QuoteReplacer(whereWriter)); err != nil { + } else { + if _, err := fmt.Fprint(updateWriter, " WHERE "); err != nil { return err } - case schemas.MSSQL: - if statement.HasOrderBy() && table != nil && len(table.PrimaryKeys) == 1 { - cond = builder.Expr(fmt.Sprintf("%s IN (SELECT TOP (%d) %s FROM %v%v)", - table.PrimaryKeys[0], limitValue, table.PrimaryKeys[0], - statement.quote(tableName), whereWriter.String()), whereWriter.Args()...) - - whereWriter = builder.NewWriter() - fmt.Fprint(whereWriter, "WHERE ") - if err := cond.WriteTo(whereWriter); err != nil { - return err - } - } else { - top = fmt.Sprintf("TOP (%d) ", limitValue) + } + if _, err := fmt.Fprint(updateWriter, "rowid IN (SELECT rowid FROM ", statement.quote(tableName)); err != nil { + return err + } + if err := statement.writeWhereCond(updateWriter, cond); err != nil { + return err + } + if err := statement.writeOrderBys(updateWriter); err != nil { + return err + } + _, err := fmt.Fprintf(updateWriter, " LIMIT %d)", limitValue) + return err + case schemas.POSTGRES: + if cond.IsValid() { + if _, err := fmt.Fprint(updateWriter, " AND "); err != nil { + return err + } + } else { + if _, err := fmt.Fprint(updateWriter, " WHERE "); err != nil { + return err } } - } - - tableAlias := statement.quote(tableName) - var fromSQL string - if statement.TableAlias != "" { - switch statement.dialect.URI().DBType { - case schemas.MSSQL: - fromSQL = fmt.Sprintf("FROM %s %s ", tableAlias, statement.TableAlias) - tableAlias = statement.TableAlias - default: - tableAlias = fmt.Sprintf("%s AS %s", tableAlias, statement.TableAlias) + if _, err := fmt.Fprint(updateWriter, "CTID IN (SELECT CTID FROM ", statement.quote(tableName)); err != nil { + return err } + if err := statement.writeWhereCond(updateWriter, cond); err != nil { + return err + } + if err := statement.writeOrderBys(updateWriter); err != nil { + return err + } + _, err := fmt.Fprintf(updateWriter, " LIMIT %d)", limitValue) + return err + case schemas.MSSQL: + if statement.HasOrderBy() && table != nil && len(table.PrimaryKeys) == 1 { + if _, err := fmt.Fprintf(updateWriter, " WHERE %s IN (SELECT TOP (%d) %s FROM %v", + table.PrimaryKeys[0], limitValue, table.PrimaryKeys[0], + statement.quote(tableName)); err != nil { + return err + } + if err := statement.writeWhereCond(updateWriter, cond); err != nil { + return err + } + if err := statement.writeOrderBys(updateWriter); err != nil { + return err + } + _, err := fmt.Fprint(updateWriter, ")") + return err + } + return nil + default: // TODO: Oracle support needed + return fmt.Errorf("not implemented") } +} - if _, err := fmt.Fprintf(updateWriter, "UPDATE %v%v SET %v %v", - top, - tableAlias, - strings.Join(colNames, ", "), - fromSQL); err != nil { +func (statement *Statement) WriteUpdate(updateWriter *builder.BytesWriter, cond builder.Cond, colNames []string, args []interface{}) error { + if _, err := fmt.Fprintf(updateWriter, "UPDATE"); err != nil { return err } - return utils.WriteBuilder(updateWriter, whereWriter) + + if err := statement.writeUpdateTop(updateWriter); err != nil { + return err + } + + if err := statement.writeUpdateTableName(updateWriter); err != nil { + return err + } + + // write set + if _, err := fmt.Fprint(updateWriter, " SET "); err != nil { + return err + } + for i, colName := range colNames { + if i > 0 { + if _, err := fmt.Fprint(updateWriter, ", "); err != nil { + return err + } + } + if _, err := fmt.Fprint(updateWriter, colName); err != nil { + return err + } + } + updateWriter.Append(args...) + + // write from + if err := statement.writeUpdateFrom(updateWriter); err != nil { + return err + } + + if statement.dialect.URI().DBType == schemas.MSSQL { + table := statement.RefTable + if statement.HasOrderBy() && table != nil && len(table.PrimaryKeys) == 1 { + } else { + // write where + if err := statement.writeWhereCond(updateWriter, cond); err != nil { + return err + } + } + } else { + // write where + if err := statement.writeWhereCond(updateWriter, cond); err != nil { + return err + } + } + + if statement.dialect.URI().DBType == schemas.MYSQL { + if err := statement.writeOrderBys(updateWriter); err != nil { + return err + } + } + + return statement.writeUpdateLimit(updateWriter, cond) } diff --git a/session_update.go b/session_update.go index 9a6964f1..97c55eb4 100644 --- a/session_update.go +++ b/session_update.go @@ -227,14 +227,14 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } updateWriter := builder.NewWriter() - if err := session.statement.WriteUpdate(updateWriter, cond, colNames); err != nil { + if err := session.statement.WriteUpdate(updateWriter, cond, colNames, args); err != nil { return 0, err } tableName := session.statement.TableName() // table name must been get before exec because statement will be reset useCache := session.statement.UseCache - res, err := session.exec(updateWriter.String(), append(args, updateWriter.Args()...)...) + res, err := session.exec(updateWriter.String(), updateWriter.Args()...) if err != nil { return 0, err } else if doIncVer { From 4109ce1e237d49594956e7f6a80946814d2d9da4 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 25 Jul 2023 08:37:12 +0000 Subject: [PATCH 110/116] Fix a serious bug when using rows and reuse the session (#2309) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2309 --- integrations/session_query_test.go | 73 +++++++++++++++++++++++++----- rows.go | 2 + 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/integrations/session_query_test.go b/integrations/session_query_test.go index 00b7d7a6..ff62f25d 100644 --- a/integrations/session_query_test.go +++ b/integrations/session_query_test.go @@ -30,7 +30,7 @@ func TestQueryString(t *testing.T) { assert.NoError(t, testEngine.Sync(new(GetVar2))) - var data = GetVar2{ + data := GetVar2{ Msg: "hi", Age: 28, Money: 1.5, @@ -58,7 +58,7 @@ func TestQueryString2(t *testing.T) { assert.NoError(t, testEngine.Sync(new(GetVar3))) - var data = GetVar3{ + data := GetVar3{ Msg: false, } _, err := testEngine.Insert(data) @@ -95,7 +95,7 @@ func TestQueryInterface(t *testing.T) { assert.NoError(t, testEngine.Sync(new(GetVarInterface))) - var data = GetVarInterface{ + data := GetVarInterface{ Msg: "hi", Age: 28, Money: 1.5, @@ -128,7 +128,7 @@ func TestQueryNoParams(t *testing.T) { assert.NoError(t, testEngine.Sync(new(QueryNoParams))) - var q = QueryNoParams{ + q := QueryNoParams{ Msg: "message", Age: 20, Money: 3000, @@ -172,7 +172,7 @@ func TestQueryStringNoParam(t *testing.T) { assert.NoError(t, testEngine.Sync(new(GetVar4))) - var data = GetVar4{ + data := GetVar4{ Msg: false, } _, err := testEngine.Insert(data) @@ -209,7 +209,7 @@ func TestQuerySliceStringNoParam(t *testing.T) { assert.NoError(t, testEngine.Sync(new(GetVar6))) - var data = GetVar6{ + data := GetVar6{ Msg: false, } _, err := testEngine.Insert(data) @@ -246,7 +246,7 @@ func TestQueryInterfaceNoParam(t *testing.T) { assert.NoError(t, testEngine.Sync(new(GetVar5))) - var data = GetVar5{ + data := GetVar5{ Msg: false, } _, err := testEngine.Insert(data) @@ -280,7 +280,7 @@ func TestQueryWithBuilder(t *testing.T) { assert.NoError(t, testEngine.Sync(new(QueryWithBuilder))) - var q = QueryWithBuilder{ + q := QueryWithBuilder{ Msg: "message", Age: 20, Money: 3000, @@ -329,14 +329,14 @@ func TestJoinWithSubQuery(t *testing.T) { assert.NoError(t, testEngine.Sync(new(JoinWithSubQuery1), new(JoinWithSubQueryDepart))) - var depart = JoinWithSubQueryDepart{ + depart := JoinWithSubQueryDepart{ Name: "depart1", } cnt, err := testEngine.Insert(&depart) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var q = JoinWithSubQuery1{ + q := JoinWithSubQuery1{ Msg: "message", DepartId: depart.Id, Money: 3000, @@ -401,7 +401,7 @@ func TestQueryBLOBInMySQL(t *testing.T) { } const N = 10 - var data = []Avatar{} + data := []Avatar{} for i := 0; i < N; i++ { // allocate a []byte that is as twice big as the last one // so that the underlying buffer will need to reallocate when querying @@ -448,3 +448,54 @@ func TestQueryBLOBInMySQL(t *testing.T) { } } } + +func TestRowsReset(t *testing.T) { + assert.NoError(t, PrepareEngine()) + + type RowsReset1 struct { + Id int64 + Name string + } + + type RowsReset2 struct { + Id int64 + Name string + } + + assert.NoError(t, testEngine.Sync(new(RowsReset1), new(RowsReset2))) + + data := []RowsReset1{ + {0, "1"}, + {0, "2"}, + {0, "3"}, + } + _, err := testEngine.Insert(data) + assert.NoError(t, err) + + data2 := []RowsReset2{ + {0, "4"}, + {0, "5"}, + {0, "6"}, + } + _, err = testEngine.Insert(data2) + assert.NoError(t, err) + + sess := testEngine.NewSession() + defer sess.Close() + + rows, err := sess.Rows(new(RowsReset1)) + assert.NoError(t, err) + for rows.Next() { + var data1 RowsReset1 + assert.NoError(t, rows.Scan(&data1)) + } + rows.Close() + + var rrs []RowsReset2 + assert.NoError(t, sess.Find(&rrs)) + + assert.Len(t, rrs, 3) + assert.EqualValues(t, "4", rrs[0].Name) + assert.EqualValues(t, "5", rrs[1].Name) + assert.EqualValues(t, "6", rrs[2].Name) +} diff --git a/rows.go b/rows.go index a42eedb9..c539410e 100644 --- a/rows.go +++ b/rows.go @@ -144,6 +144,8 @@ func (rows *Rows) Close() error { defer rows.session.Close() } + defer rows.session.resetStatement() + if rows.rows != nil { return rows.rows.Close() } From 9aab1f689cde85322856b9410194d7c26f4cd217 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 25 Jul 2023 09:27:25 +0000 Subject: [PATCH 111/116] Count will ignore order by as before (#2307) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2307 --- integrations/session_count_test.go | 5 +++++ internal/statements/query.go | 31 ++++++++++++++++-------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/integrations/session_count_test.go b/integrations/session_count_test.go index 079602c3..c6e64e76 100644 --- a/integrations/session_count_test.go +++ b/integrations/session_count_test.go @@ -125,6 +125,11 @@ func TestWithTableName(t *testing.T) { total, err = testEngine.OrderBy("count(`id`) desc").Count(CountWithTableName{}) assert.NoError(t, err) assert.EqualValues(t, 2, total) + + // the orderby will be ignored by count because some databases will return errors if the orderby columns not in group by + total, err = testEngine.OrderBy("`name`").Count(CountWithTableName{}) + assert.NoError(t, err) + assert.EqualValues(t, 2, total) } func TestCountWithSelectCols(t *testing.T) { diff --git a/internal/statements/query.go b/internal/statements/query.go index 0f4c40b1..216a2028 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -35,7 +35,7 @@ func (statement *Statement) GenQuerySQL(sqlOrArgs ...interface{}) (string, []int } buf := builder.NewWriter() - if err := statement.writeSelect(buf, statement.genSelectColumnStr(), true); err != nil { + if err := statement.writeSelect(buf, statement.genSelectColumnStr(), true, true); err != nil { return "", nil, err } return buf.String(), buf.Args(), nil @@ -66,7 +66,7 @@ func (statement *Statement) GenSumSQL(bean interface{}, columns ...string) (stri } buf := builder.NewWriter() - if err := statement.writeSelect(buf, strings.Join(sumStrs, ", "), true); err != nil { + if err := statement.writeSelect(buf, strings.Join(sumStrs, ", "), true, true); err != nil { return "", nil, err } return buf.String(), buf.Args(), nil @@ -122,7 +122,7 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{}, } buf := builder.NewWriter() - if err := statement.writeSelect(buf, columnStr, true); err != nil { + if err := statement.writeSelect(buf, columnStr, true, true); err != nil { return "", nil, err } return buf.String(), buf.Args(), nil @@ -153,12 +153,6 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa selectSQL = "count(*)" } } - var subQuerySelect string - if statement.GroupByStr != "" { - subQuerySelect = statement.GroupByStr - } else { - subQuerySelect = selectSQL - } buf := builder.NewWriter() if statement.GroupByStr != "" { @@ -167,7 +161,14 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa } } - if err := statement.writeSelect(buf, subQuerySelect, false); err != nil { + var subQuerySelect string + if statement.GroupByStr != "" { + subQuerySelect = statement.GroupByStr + } else { + subQuerySelect = selectSQL + } + + if err := statement.writeSelect(buf, subQuerySelect, false, false); err != nil { return "", nil, err } @@ -364,7 +365,7 @@ func (statement *Statement) writeOracleLimit(w *builder.BytesWriter, columnStr s return err } -func (statement *Statement) writeSelect(buf *builder.BytesWriter, columnStr string, needLimit bool) error { +func (statement *Statement) writeSelect(buf *builder.BytesWriter, columnStr string, needLimit, needOrderBy bool) error { if err := statement.writeSelectColumns(buf, columnStr); err != nil { return err } @@ -380,8 +381,10 @@ func (statement *Statement) writeSelect(buf *builder.BytesWriter, columnStr stri if err := statement.writeHaving(buf); err != nil { return err } - if err := statement.writeOrderBys(buf); err != nil { - return err + if needOrderBy { + if err := statement.writeOrderBys(buf); err != nil { + return err + } } dialect := statement.dialect @@ -519,7 +522,7 @@ func (statement *Statement) GenFindSQL(autoCond builder.Cond) (string, []interfa statement.cond = statement.cond.And(autoCond) buf := builder.NewWriter() - if err := statement.writeSelect(buf, statement.genSelectColumnStr(), true); err != nil { + if err := statement.writeSelect(buf, statement.genSelectColumnStr(), true, true); err != nil { return "", nil, err } return buf.String(), buf.Args(), nil From cb4f310151f208a64ceb0472ad17b875f85e1d5f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 25 Jul 2023 10:49:55 +0000 Subject: [PATCH 112/116] Refactor write update (#2310) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2310 --- internal/statements/update.go | 172 +++++++++++++++++++++++++++++++--- session_update.go | 53 ++--------- 2 files changed, 167 insertions(+), 58 deletions(-) diff --git a/internal/statements/update.go b/internal/statements/update.go index 1eb431a8..5d71f34d 100644 --- a/internal/statements/update.go +++ b/internal/statements/update.go @@ -427,7 +427,158 @@ func (statement *Statement) writeUpdateLimit(updateWriter *builder.BytesWriter, } } -func (statement *Statement) WriteUpdate(updateWriter *builder.BytesWriter, cond builder.Cond, colNames []string, args []interface{}) error { +func (statement *Statement) GenConditionsFromMap(m interface{}) ([]builder.Cond, error) { + switch t := m.(type) { + case map[string]interface{}: + conds := []builder.Cond{} + for k, v := range t { + conds = append(conds, builder.Eq{k: v}) + } + return conds, nil + case map[string]string: + conds := []builder.Cond{} + for k, v := range t { + conds = append(conds, builder.Eq{k: v}) + } + return conds, nil + default: + return nil, fmt.Errorf("unsupported condition map type %v", t) + } +} + +func (statement *Statement) writeVersionIncrSet(w builder.Writer, v reflect.Value, hasPreviousSet bool) error { + if v.Type().Kind() != reflect.Struct { + return nil + } + + table := statement.RefTable + if !(statement.RefTable != nil && table.Version != "" && statement.CheckVersion) { + return nil + } + + verValue, err := table.VersionColumn().ValueOfV(&v) + if err != nil { + return err + } + + if verValue == nil { + return nil + } + + if hasPreviousSet { + if _, err := fmt.Fprint(w, ", "); err != nil { + return err + } + } + + if _, err := fmt.Fprint(w, statement.quote(table.Version), " = ", statement.quote(table.Version), " + 1"); err != nil { + return err + } + return nil +} + +func (statement *Statement) writeIncrSets(w builder.Writer, hasPreviousSet bool) error { + for i, expr := range statement.IncrColumns { + if i > 0 || hasPreviousSet { + if _, err := fmt.Fprint(w, ", "); err != nil { + return err + } + } + if _, err := fmt.Fprint(w, statement.quote(expr.ColName), " = ", statement.quote(expr.ColName), " + ?"); err != nil { + return err + } + w.Append(expr.Arg) + } + return nil +} + +func (statement *Statement) writeDecrSets(w builder.Writer, hasPreviousSet bool) error { + // for update action to like "column = column - ?" + for i, expr := range statement.DecrColumns { + if i > 0 || hasPreviousSet { + if _, err := fmt.Fprint(w, ", "); err != nil { + return err + } + } + if _, err := fmt.Fprint(w, statement.quote(expr.ColName), " = ", statement.quote(expr.ColName), " - ?"); err != nil { + return err + } + w.Append(expr.Arg) + } + return nil +} + +func (statement *Statement) writeExprSets(w *builder.BytesWriter, hasPreviousSet bool) error { + // for update action to like "column = expression" + for i, expr := range statement.ExprColumns { + if i > 0 || hasPreviousSet { + if _, err := fmt.Fprint(w, ", "); err != nil { + return err + } + } + switch tp := expr.Arg.(type) { + case string: + if len(tp) == 0 { + tp = "''" + } + if _, err := fmt.Fprint(w, statement.quote(expr.ColName), " = ", tp); err != nil { + return err + } + case *builder.Builder: + if _, err := fmt.Fprint(w, statement.quote(expr.ColName), " = ("); err != nil { + return err + } + if err := tp.WriteTo(statement.QuoteReplacer(w)); err != nil { + return err + } + if _, err := fmt.Fprint(w, ")"); err != nil { + return err + } + default: + if _, err := fmt.Fprint(w, statement.quote(expr.ColName), " = ?"); err != nil { + return err + } + w.Append(expr.Arg) + } + } + return nil +} + +func (statement *Statement) writeUpdateSets(w *builder.BytesWriter, v reflect.Value, colNames []string, args []interface{}) error { + previousLen := w.Len() + for i, colName := range colNames { + if i > 0 { + if _, err := fmt.Fprint(w, ", "); err != nil { + return err + } + } + if _, err := fmt.Fprint(w, colName); err != nil { + return err + } + } + w.Append(args...) + + if err := statement.writeIncrSets(w, w.Len() > previousLen); err != nil { + return err + } + + if err := statement.writeDecrSets(w, w.Len() > previousLen); err != nil { + return err + } + + if err := statement.writeExprSets(w, w.Len() > previousLen); err != nil { + return err + } + + if err := statement.writeVersionIncrSet(w, v, w.Len() > previousLen); err != nil { + return err + } + return nil +} + +var ErrNoColumnsTobeUpdated = errors.New("no columns found to be updated") + +func (statement *Statement) WriteUpdate(updateWriter *builder.BytesWriter, cond builder.Cond, v reflect.Value, colNames []string, args []interface{}) error { if _, err := fmt.Fprintf(updateWriter, "UPDATE"); err != nil { return err } @@ -444,17 +595,16 @@ func (statement *Statement) WriteUpdate(updateWriter *builder.BytesWriter, cond if _, err := fmt.Fprint(updateWriter, " SET "); err != nil { return err } - for i, colName := range colNames { - if i > 0 { - if _, err := fmt.Fprint(updateWriter, ", "); err != nil { - return err - } - } - if _, err := fmt.Fprint(updateWriter, colName); err != nil { - return err - } + previousLen := updateWriter.Len() + + if err := statement.writeUpdateSets(updateWriter, v, colNames, args); err != nil { + return err + } + + // if no columns to be updated, return error + if previousLen == updateWriter.Len() { + return ErrNoColumnsTobeUpdated } - updateWriter.Append(args...) // write from if err := statement.writeUpdateFrom(updateWriter); err != nil { diff --git a/session_update.go b/session_update.go index 97c55eb4..b3640ad2 100644 --- a/session_update.go +++ b/session_update.go @@ -5,17 +5,17 @@ package xorm import ( - "errors" "reflect" "xorm.io/builder" + "xorm.io/xorm/internal/statements" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) // enumerated all errors var ( - ErrNoColumnsTobeUpdated = errors.New("no columns found to be updated") + ErrNoColumnsTobeUpdated = statements.ErrNoColumnsTobeUpdated ) func (session *Session) genAutoCond(condiBean interface{}) (builder.Cond, error) { @@ -74,9 +74,6 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 v := utils.ReflectValue(bean) t := v.Type() - var colNames []string - var args []interface{} - // handle before update processors for _, closure := range session.beforeClosures { closure(bean) @@ -87,6 +84,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } // -- + var colNames []string + var args []interface{} var err error isMap := t.Kind() == reflect.Map isStruct := t.Kind() == reflect.Struct @@ -148,41 +147,6 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } - // for update action to like "column = column + ?" - incColumns := session.statement.IncrColumns - for _, expr := range incColumns { - colNames = append(colNames, session.engine.Quote(expr.ColName)+" = "+session.engine.Quote(expr.ColName)+" + ?") - args = append(args, expr.Arg) - } - // for update action to like "column = column - ?" - decColumns := session.statement.DecrColumns - for _, expr := range decColumns { - colNames = append(colNames, session.engine.Quote(expr.ColName)+" = "+session.engine.Quote(expr.ColName)+" - ?") - args = append(args, expr.Arg) - } - // for update action to like "column = expression" - exprColumns := session.statement.ExprColumns - for _, expr := range exprColumns { - switch tp := expr.Arg.(type) { - case string: - if len(tp) == 0 { - tp = "''" - } - colNames = append(colNames, session.engine.Quote(expr.ColName)+"="+tp) - case *builder.Builder: - subQuery, subArgs, err := builder.ToSQL(tp) - if err != nil { - return 0, err - } - subQuery = session.statement.ReplaceQuote(subQuery) - colNames = append(colNames, session.engine.Quote(expr.ColName)+"=("+subQuery+")") - args = append(args, subArgs...) - default: - colNames = append(colNames, session.engine.Quote(expr.ColName)+"=?") - args = append(args, expr.Arg) - } - } - if err = session.statement.ProcessIDParam(); err != nil { return 0, err } @@ -211,23 +175,18 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 verValue *reflect.Value ) if doIncVer { - verValue, err = table.VersionColumn().ValueOf(bean) + verValue, err = table.VersionColumn().ValueOfV(&v) if err != nil { return 0, err } if verValue != nil { cond = cond.And(builder.Eq{session.engine.Quote(table.Version): verValue.Interface()}) - colNames = append(colNames, session.engine.Quote(table.Version)+" = "+session.engine.Quote(table.Version)+" + 1") } } - if len(colNames) == 0 { - return 0, ErrNoColumnsTobeUpdated - } - updateWriter := builder.NewWriter() - if err := session.statement.WriteUpdate(updateWriter, cond, colNames, args); err != nil { + if err := session.statement.WriteUpdate(updateWriter, cond, v, colNames, args); err != nil { return 0, err } From 59b727260d358b16318196b428c35b69cdbad143 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 25 Jul 2023 14:02:38 +0000 Subject: [PATCH 113/116] Check orderby validate (#2313) Reviewed-on: https://gitea.com/xorm/xorm/pulls/2313 --- internal/statements/order_by.go | 52 +++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/internal/statements/order_by.go b/internal/statements/order_by.go index 595c0430..54a3c6e0 100644 --- a/internal/statements/order_by.go +++ b/internal/statements/order_by.go @@ -5,6 +5,7 @@ package statements import ( + "errors" "fmt" "xorm.io/builder" @@ -16,6 +17,26 @@ type orderBy struct { direction string // ASC, DESC or "", "" means raw orderStr } +func (ob orderBy) CheckValid() error { + if ob.orderStr == nil { + return fmt.Errorf("order by string is nil") + } + switch t := ob.orderStr.(type) { + case string: + if t == "" { + return fmt.Errorf("order by string is empty") + } + return nil + case *builder.Expression: + if t.Content() == "" { + return fmt.Errorf("order by string is empty") + } + return nil + default: + return fmt.Errorf("order by string is not string or builder.Expression") + } +} + func (statement *Statement) HasOrderBy() bool { return len(statement.orderBy) > 0 } @@ -25,6 +46,8 @@ func (statement *Statement) ResetOrderBy() { statement.orderBy = []orderBy{} } +var ErrNoColumnName = errors.New("no column name") + func (statement *Statement) writeOrderBy(w *builder.BytesWriter, orderBy orderBy) error { switch t := orderBy.orderStr.(type) { case (*builder.Expression): @@ -75,22 +98,45 @@ func (statement *Statement) writeOrderBys(w *builder.BytesWriter) error { // OrderBy generate "Order By order" statement func (statement *Statement) OrderBy(order interface{}, args ...interface{}) *Statement { - statement.orderBy = append(statement.orderBy, orderBy{order, args, ""}) + ob := orderBy{order, args, ""} + if err := ob.CheckValid(); err != nil { + statement.LastError = err + return statement + } + statement.orderBy = append(statement.orderBy, ob) return statement } // Desc generate `ORDER BY xx DESC` func (statement *Statement) Desc(colNames ...string) *Statement { + if len(colNames) == 0 { + statement.LastError = ErrNoColumnName + return statement + } for _, colName := range colNames { - statement.orderBy = append(statement.orderBy, orderBy{colName, nil, "DESC"}) + ob := orderBy{colName, nil, "DESC"} + statement.orderBy = append(statement.orderBy, ob) + if err := ob.CheckValid(); err != nil { + statement.LastError = err + return statement + } } return statement } // Asc provide asc order by query condition, the input parameters are columns. func (statement *Statement) Asc(colNames ...string) *Statement { + if len(colNames) == 0 { + statement.LastError = ErrNoColumnName + return statement + } for _, colName := range colNames { - statement.orderBy = append(statement.orderBy, orderBy{colName, nil, "ASC"}) + ob := orderBy{colName, nil, "ASC"} + statement.orderBy = append(statement.orderBy, ob) + if err := ob.CheckValid(); err != nil { + statement.LastError = err + return statement + } } return statement } From 1572367155aa41f1d0af2959edb75eed783c9a8f Mon Sep 17 00:00:00 2001 From: takumin Date: Wed, 26 Jul 2023 00:57:40 +0000 Subject: [PATCH 114/116] Add dialects/time_test.go (#2169) Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/xorm/xorm/pulls/2169 Co-authored-by: takumin Co-committed-by: takumin --- dialects/time_test.go | 190 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 dialects/time_test.go diff --git a/dialects/time_test.go b/dialects/time_test.go new file mode 100644 index 00000000..670207c6 --- /dev/null +++ b/dialects/time_test.go @@ -0,0 +1,190 @@ +// Copyright 2019 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 dialects + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "xorm.io/xorm/schemas" +) + +type dialect struct { + Dialect + dbType schemas.DBType +} + +func (d dialect) URI() *URI { + return &URI{ + DBType: d.dbType, + } +} + +func TestFormatColumnTime(t *testing.T) { + date := time.Date(2020, 10, 23, 10, 14, 15, 123456, time.Local) + tests := []struct { + name string + dialect Dialect + location *time.Location + column *schemas.Column + time time.Time + wantRes interface{} + wantErr error + }{ + { + name: "nullable", + dialect: nil, + location: nil, + column: &schemas.Column{Nullable: true}, + time: time.Time{}, + wantRes: nil, + wantErr: nil, + }, + { + name: "invalid sqltype", + dialect: nil, + location: nil, + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.Bit}}, + time: time.Time{}, + wantRes: 0, + wantErr: nil, + }, + { + name: "return default", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.Bit}}, + time: date, + wantRes: date, + wantErr: nil, + }, + { + name: "return default (set timezone)", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.Bit}, TimeZone: time.UTC}, + time: date, + wantRes: date.In(time.UTC), + wantErr: nil, + }, + { + name: "format date", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.Date}}, + time: date, + wantRes: date.Format("2006-01-02"), + wantErr: nil, + }, + { + name: "format time", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.Time}}, + time: date, + wantRes: date.Format("15:04:05"), + wantErr: nil, + }, + { + name: "format time (set length)", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.Time}, Length: 64}, + time: date, + wantRes: date.Format("15:04:05.999999999"), + wantErr: nil, + }, + { + name: "format datetime", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.DateTime}}, + time: date, + wantRes: date.Format("2006-01-02 15:04:05"), + wantErr: nil, + }, + { + name: "format datetime (set length)", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.DateTime}, Length: 64}, + time: date, + wantRes: date.Format("2006-01-02 15:04:05.999999999"), + wantErr: nil, + }, + { + name: "format timestamp", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.TimeStamp}}, + time: date, + wantRes: date.Format("2006-01-02 15:04:05"), + wantErr: nil, + }, + { + name: "format timestamp (set length)", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.TimeStamp}, Length: 64}, + time: date, + wantRes: date.Format("2006-01-02 15:04:05.999999999"), + wantErr: nil, + }, + { + name: "format varchar", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.Varchar}}, + time: date, + wantRes: date.Format("2006-01-02 15:04:05"), + wantErr: nil, + }, + { + name: "format timestampz", + dialect: dialect{}, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.TimeStampz}}, + time: date, + wantRes: date.Format(time.RFC3339Nano), + wantErr: nil, + }, + { + name: "format timestampz (mssql)", + dialect: dialect{dbType: schemas.MSSQL}, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.TimeStampz}}, + time: date, + wantRes: date.Format("2006-01-02T15:04:05.9999999Z07:00"), + wantErr: nil, + }, + { + name: "format int", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.Int}}, + time: date, + wantRes: date.Unix(), + wantErr: nil, + }, + { + name: "format bigint", + dialect: nil, + location: date.Location(), + column: &schemas.Column{SQLType: schemas.SQLType{Name: schemas.BigInt}}, + time: date, + wantRes: date.Unix(), + wantErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := FormatColumnTime(tt.dialect, tt.location, tt.column, tt.time) + assert.Equal(t, tt.wantErr, err) + assert.Equal(t, tt.wantRes, got) + }) + } +} From 12ec0388532e12292e109aafe0c4ef52fc8f2d60 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Jul 2023 03:03:01 +0000 Subject: [PATCH 115/116] Add test for get customized types (#2194) Fix #2097 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2194 --- convert/int.go | 58 ++++++++++++++++++++++++++++---- integrations/session_get_test.go | 8 +++++ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/convert/int.go b/convert/int.go index af8d4f75..03994773 100644 --- a/convert/int.go +++ b/convert/int.go @@ -35,6 +35,56 @@ func AsInt64(src interface{}) (int64, error) { return int64(v), nil case uint64: return int64(v), nil + case *int: + if v == nil { + return 0, nil + } + return int64(*v), nil + case *int16: + if v == nil { + return 0, nil + } + return int64(*v), nil + case *int32: + if v == nil { + return 0, nil + } + return int64(*v), nil + case *int8: + if v == nil { + return 0, nil + } + return int64(*v), nil + case *int64: + if v == nil { + return 0, nil + } + return *v, nil + case *uint: + if v == nil { + return 0, nil + } + return int64(*v), nil + case *uint8: + if v == nil { + return 0, nil + } + return int64(*v), nil + case *uint16: + if v == nil { + return 0, nil + } + return int64(*v), nil + case *uint32: + if v == nil { + return 0, nil + } + return int64(*v), nil + case *uint64: + if v == nil { + return 0, nil + } + return int64(*v), nil case []byte: return strconv.ParseInt(string(v), 10, 64) case string: @@ -110,9 +160,7 @@ func AsUint64(src interface{}) (uint64, error) { return 0, fmt.Errorf("unsupported value %T as uint64", src) } -var ( - _ sql.Scanner = &NullUint64{} -) +var _ sql.Scanner = &NullUint64{} // NullUint64 represents an uint64 that may be null. // NullUint64 implements the Scanner interface so @@ -142,9 +190,7 @@ func (n NullUint64) Value() (driver.Value, error) { return n.Uint64, nil } -var ( - _ sql.Scanner = &NullUint32{} -) +var _ sql.Scanner = &NullUint32{} // NullUint32 represents an uint32 that may be null. // NullUint32 implements the Scanner interface so diff --git a/integrations/session_get_test.go b/integrations/session_get_test.go index 841ec709..d3403814 100644 --- a/integrations/session_get_test.go +++ b/integrations/session_get_test.go @@ -1012,4 +1012,12 @@ func TestGetBytesVars(t *testing.T) { assert.True(t, has) assert.EqualValues(t, []byte("bytes1-1"), gbv.Bytes1) assert.EqualValues(t, []byte("bytes2-2"), gbv.Bytes2) + + type MyID int64 + var myID MyID + + has, err = testEngine.Table("get_bytes_vars").Select("id").Desc("id").Get(&myID) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, gbv.Id, myID) } From 47003ff937b0abfaa11a54554653235e231d93ea Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Jul 2023 06:58:55 +0000 Subject: [PATCH 116/116] Delete Limit with start will return error because it's not supported by database (#1895) Fix #1894 Reviewed-on: https://gitea.com/xorm/xorm/pulls/1895 --- .gitea/workflows/test-cockroach.yml | 1 + integrations/session_delete_test.go | 58 +++++++++++++++++++++++++++++ internal/statements/delete.go | 3 ++ 3 files changed, 62 insertions(+) diff --git a/.gitea/workflows/test-cockroach.yml b/.gitea/workflows/test-cockroach.yml index 0ca18861..ba966dc9 100644 --- a/.gitea/workflows/test-cockroach.yml +++ b/.gitea/workflows/test-cockroach.yml @@ -43,6 +43,7 @@ jobs: TEST_COCKROACH_DBNAME: xorm_test TEST_COCKROACH_USERNAME: root TEST_COCKROACH_PASSWORD: + IGNORE_TEST_DELETE_LIMIT: true run: sleep 20 && make test-cockroach services: diff --git a/integrations/session_delete_test.go b/integrations/session_delete_test.go index 680c3215..1ed3e706 100644 --- a/integrations/session_delete_test.go +++ b/integrations/session_delete_test.go @@ -5,6 +5,7 @@ package integrations import ( + "os" "testing" "time" @@ -70,6 +71,63 @@ func TestDelete(t *testing.T) { assert.False(t, has) } +func TestDeleteLimit(t *testing.T) { + assert.NoError(t, PrepareEngine()) + + if testEngine.Dialect().URI().DBType == schemas.MSSQL || os.Getenv("IGNORE_TEST_DELETE_LIMIT") == "true" { + t.Skip() + return + } + + type UserinfoDeleteLimit struct { + Uid int64 `xorm:"id pk not null autoincr"` + IsMan bool + } + + assert.NoError(t, testEngine.Sync2(new(UserinfoDeleteLimit))) + + session := testEngine.NewSession() + defer session.Close() + + var err error + if testEngine.Dialect().URI().DBType == schemas.MSSQL { + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT userinfo_delete_limit ON") + assert.NoError(t, err) + } + + user := UserinfoDeleteLimit{Uid: 1, IsMan: true} + cnt, err := session.Insert(&user) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + user2 := UserinfoDeleteLimit{Uid: 2} + cnt, err = session.Insert(&user2) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + if testEngine.Dialect().URI().DBType == schemas.MSSQL { + err = session.Commit() + assert.NoError(t, err) + } + + cnt, err = testEngine.Limit(1, 1).Delete(&UserinfoDeleteLimit{}) + assert.Error(t, err) + assert.EqualValues(t, 0, cnt) + + cnt, err = testEngine.Limit(1).Desc("id").Delete(&UserinfoDeleteLimit{}) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var users []UserinfoDeleteLimit + err = testEngine.Find(&users) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(users)) + assert.EqualValues(t, 1, users[0].Uid) + assert.EqualValues(t, true, users[0].IsMan) +} + func TestDeleted(t *testing.T) { assert.NoError(t, PrepareEngine()) diff --git a/internal/statements/delete.go b/internal/statements/delete.go index a77cf862..6e859399 100644 --- a/internal/statements/delete.go +++ b/internal/statements/delete.go @@ -20,6 +20,9 @@ func (statement *Statement) writeDeleteOrder(w *builder.BytesWriter) error { } if statement.LimitN != nil && *statement.LimitN > 0 { + if statement.Start > 0 { + return fmt.Errorf("Delete with Limit start is unsupported") + } limitNValue := *statement.LimitN if _, err := fmt.Fprintf(w, " LIMIT %d", limitNValue); err != nil { return err