diff --git a/engine.go b/engine.go index b60234e0..1ab2eed2 100644 --- a/engine.go +++ b/engine.go @@ -191,11 +191,6 @@ func (engine *Engine) SupportInsertMany() bool { return engine.dialect.SupportInsertMany() } -func (engine *Engine) quoteColumns(columnStr string) string { - columns := strings.Split(columnStr, ",") - return engine.dialect.Quoter().Join(columns, ",") -} - // Quote Use QuoteStr quote the string sql func (engine *Engine) Quote(value string) string { value = strings.TrimSpace(value) @@ -222,18 +217,6 @@ func (engine *Engine) QuoteTo(buf *strings.Builder, value string) { engine.dialect.Quoter().QuoteTo(buf, value) } -/* -func (engine *Engine) quote(sql string) string { - return engine.dialect.Quote(sql) -}*/ - -// SqlType will be deprecated, please use SQLType instead -// -// Deprecated: use SQLType instead -func (engine *Engine) SqlType(c *schemas.Column) string { - return engine.SQLType(c) -} - // SQLType A simple wrapper to dialect's core.SqlType method func (engine *Engine) SQLType(c *schemas.Column) string { return engine.dialect.SQLType(c) @@ -335,14 +318,6 @@ func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) { } } -// Sql provides raw sql input parameter. When you have a complex SQL statement -// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. -// -// Deprecated: use SQL instead. -func (engine *Engine) Sql(querystring string, args ...interface{}) *Session { - return engine.SQL(querystring, args...) -} - // SQL method let's you manually write raw SQL and operate // For example: // @@ -597,13 +572,6 @@ func (engine *Engine) Where(query interface{}, args ...interface{}) *Session { return session.Where(query, args...) } -// Id will be deprecated, please use ID instead -func (engine *Engine) Id(id interface{}) *Session { - session := engine.NewSession() - session.isAutoClose = true - return session.Id(id) -} - // ID method provoide a condition as (id) = ? func (engine *Engine) ID(id interface{}) *Session { session := engine.NewSession() @@ -1092,23 +1060,9 @@ func (engine *Engine) IsTableExist(beanOrTableName interface{}) (bool, error) { return session.IsTableExist(beanOrTableName) } -// IdOf get id from one struct -// -// Deprecated: use IDOf instead. -func (engine *Engine) IdOf(bean interface{}) schemas.PK { - return engine.IDOf(bean) -} - // IDOf get id from one struct func (engine *Engine) IDOf(bean interface{}) schemas.PK { - return engine.IdOfV(reflect.ValueOf(bean)) -} - -// IdOfV get id from one value of struct -// -// Deprecated: use IDOfV instead. -func (engine *Engine) IdOfV(rv reflect.Value) schemas.PK { - return engine.IDOfV(rv) + return engine.IDOfV(reflect.ValueOf(bean)) } // IDOfV get id from one value of struct diff --git a/engine_cond.go b/engine_cond.go index 480e7ded..949d705d 100644 --- a/engine_cond.go +++ b/engine_cond.go @@ -12,6 +12,7 @@ import ( "time" "xorm.io/builder" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -169,7 +170,7 @@ func (engine *Engine) buildConds(table *schemas.Table, bean interface{}, pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) // fix non-int pk issues //if pkField.Int() != 0 { - if pkField.IsValid() && !isZero(pkField.Interface()) { + if pkField.IsValid() && !utils.IsZero(pkField.Interface()) { val = pkField.Interface() } else { continue diff --git a/helpers.go b/helpers.go index b7c583f7..75393ae3 100644 --- a/helpers.go +++ b/helpers.go @@ -11,8 +11,6 @@ import ( "sort" "strconv" "strings" - - "xorm.io/xorm/schemas" ) // str2PK convert string value to primary key value according to tp @@ -95,95 +93,6 @@ func str2PK(s string, tp reflect.Type) (interface{}, error) { return v.Interface(), nil } -type zeroable interface { - IsZero() bool -} - -func isZero(k interface{}) bool { - switch k.(type) { - case int: - return k.(int) == 0 - case int8: - return k.(int8) == 0 - case int16: - return k.(int16) == 0 - case int32: - return k.(int32) == 0 - case int64: - return k.(int64) == 0 - case uint: - return k.(uint) == 0 - case uint8: - return k.(uint8) == 0 - case uint16: - return k.(uint16) == 0 - case uint32: - return k.(uint32) == 0 - case uint64: - return k.(uint64) == 0 - case float32: - return k.(float32) == 0 - case float64: - return k.(float64) == 0 - case bool: - return k.(bool) == false - case string: - return k.(string) == "" - case zeroable: - return k.(zeroable).IsZero() - } - return false -} - -func isZeroValue(v reflect.Value) bool { - if isZero(v.Interface()) { - return true - } - switch v.Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return v.IsNil() - } - return false -} - -func isStructZero(v reflect.Value) bool { - if !v.IsValid() { - return true - } - - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - switch field.Kind() { - case reflect.Ptr: - field = field.Elem() - fallthrough - case reflect.Struct: - if !isStructZero(field) { - return false - } - default: - if field.CanInterface() && !isZero(field.Interface()) { - return false - } - } - } - return true -} - -func isArrayValueZero(v reflect.Value) bool { - if !v.IsValid() || v.Len() == 0 { - return true - } - - for i := 0; i < v.Len(); i++ { - if !isZero(v.Index(i).Interface()) { - return false - } - } - - return true -} - func int64ToIntValue(id int64, tp reflect.Type) reflect.Value { var v interface{} kind := tp.Kind() @@ -229,15 +138,6 @@ func int64ToInt(id int64, tp reflect.Type) interface{} { return int64ToIntValue(id, tp).Interface() } -func isPKZero(pk schemas.PK) bool { - for _, k := range pk { - if isZero(k) { - return true - } - } - return false -} - func indexNoCase(s, sep string) int { return strings.Index(strings.ToLower(s), strings.ToLower(sep)) } @@ -314,10 +214,3 @@ func eraseAny(value string, strToErase ...string) string { return replacer.Replace(value) } - -func quoteColumns(cols []string, quoteFunc func(string) string, sep string) string { - for i := range cols { - cols[i] = quoteFunc(cols[i]) - } - return strings.Join(cols, sep+" ") -} diff --git a/helpers_test.go b/helpers_test.go index caf7b9f0..fc9ece27 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -16,12 +16,3 @@ func TestEraseAny(t *testing.T) { assert.EqualValues(t, "SELECT * FROM table.[table_name]", eraseAny(raw, "`")) assert.EqualValues(t, "SELECT * FROM table.table_name", eraseAny(raw, "`", "[", "]")) } - -func TestQuoteColumns(t *testing.T) { - cols := []string{"f1", "f2", "f3"} - quoteFunc := func(value string) string { - return "[" + value + "]" - } - - assert.EqualValues(t, "[f1], [f2], [f3]", quoteColumns(cols, quoteFunc, ",")) -} diff --git a/internal/utils/zero.go b/internal/utils/zero.go new file mode 100644 index 00000000..8fd1870c --- /dev/null +++ b/internal/utils/zero.go @@ -0,0 +1,98 @@ +// Copyright 2020 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" +) + +type Zeroable interface { + IsZero() bool +} + +func IsZero(k interface{}) bool { + switch k.(type) { + case int: + return k.(int) == 0 + case int8: + return k.(int8) == 0 + case int16: + return k.(int16) == 0 + case int32: + return k.(int32) == 0 + case int64: + return k.(int64) == 0 + case uint: + return k.(uint) == 0 + case uint8: + return k.(uint8) == 0 + case uint16: + return k.(uint16) == 0 + case uint32: + return k.(uint32) == 0 + case uint64: + return k.(uint64) == 0 + case float32: + return k.(float32) == 0 + case float64: + return k.(float64) == 0 + case bool: + return k.(bool) == false + case string: + return k.(string) == "" + case Zeroable: + return k.(Zeroable).IsZero() + } + return false +} + +func IsValueZero(v reflect.Value) bool { + if IsZero(v.Interface()) { + return true + } + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + } + return false +} + +func IsStructZero(v reflect.Value) bool { + if !v.IsValid() { + return true + } + + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + switch field.Kind() { + case reflect.Ptr: + field = field.Elem() + fallthrough + case reflect.Struct: + if !IsStructZero(field) { + return false + } + default: + if field.CanInterface() && !IsZero(field.Interface()) { + return false + } + } + } + return true +} + +func IsArrayZero(v reflect.Value) bool { + if !v.IsValid() || v.Len() == 0 { + return true + } + + for i := 0; i < v.Len(); i++ { + if !IsZero(v.Index(i).Interface()) { + return false + } + } + + return true +} diff --git a/schemas/pk.go b/schemas/pk.go index 3fd3d28b..03916b44 100644 --- a/schemas/pk.go +++ b/schemas/pk.go @@ -7,6 +7,8 @@ package schemas import ( "bytes" "encoding/gob" + + "xorm.io/xorm/internal/utils" ) type PK []interface{} @@ -16,6 +18,15 @@ func NewPK(pks ...interface{}) *PK { return &p } +func (p *PK) IsZero() bool { + for _, k := range *p { + if utils.IsZero(k) { + return true + } + } + return false +} + func (p *PK) ToString() (string, error) { buf := new(bytes.Buffer) enc := gob.NewEncoder(buf) diff --git a/schemas/quote.go b/schemas/quote.go index e3571e34..5dac6d27 100644 --- a/schemas/quote.go +++ b/schemas/quote.go @@ -5,7 +5,6 @@ package schemas import ( - "fmt" "strings" ) @@ -76,7 +75,7 @@ func (q Quoter) Trim(s string) string { return s } -func TrimSpaceJoin(a []string, sep string) string { +func (q Quoter) Join(a []string, sep string) string { switch len(a) { case 0: return "" @@ -90,19 +89,21 @@ func TrimSpaceJoin(a []string, sep string) string { var b strings.Builder b.Grow(n) - b.WriteString(strings.TrimSpace(a[0])) - for _, s := range a[1:] { - b.WriteString(sep) + for i, s := range a { + if i > 0 { + b.WriteString(sep) + } + if q[0] != "" { + b.WriteString(q[0]) + } b.WriteString(strings.TrimSpace(s)) + if q[1] != "" { + b.WriteString(q[1]) + } } return b.String() } -func (q Quoter) Join(s []string, splitter string) string { - //return fmt.Sprintf("%s%s%s", q[0], TrimSpaceJoin(s, fmt.Sprintf("%s%s%s", q[1], splitter, q[0])), q[1]) - return q.Quote(TrimSpaceJoin(s, fmt.Sprintf("%s%s%s", q[1], splitter, q[0]))) -} - func (q Quoter) QuoteTo(buf *strings.Builder, value string) { if q.IsEmpty() { buf.WriteString(value) diff --git a/schemas/quote_test.go b/schemas/quote_test.go index af773c8b..5eea05d3 100644 --- a/schemas/quote_test.go +++ b/schemas/quote_test.go @@ -45,3 +45,13 @@ func TestQuoteTo(t *testing.T) { quoter.QuoteTo(buf, "noquote") assert.EqualValues(t, "noquote", buf.String()) } + +func TestJoin(t *testing.T) { + cols := []string{"f1", "f2", "f3"} + quoter := Quoter{"[", "]"} + + assert.EqualValues(t, "[f1], [f2], [f3]", quoter.Join(cols, ", ")) + + quoter = Quoter{"", ""} + assert.EqualValues(t, "f1, f2, f3", quoter.Join(cols, ", ")) +} diff --git a/session.go b/session.go index 57e4055b..3e31d3d7 100644 --- a/session.go +++ b/session.go @@ -703,7 +703,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b return nil, err } - if !isPKZero(pk) { + if !pk.IsZero() { // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne // property to be fetched lazily diff --git a/session_cond.go b/session_cond.go index b16bdea8..72e3abc3 100644 --- a/session_cond.go +++ b/session_cond.go @@ -6,14 +6,6 @@ package xorm import "xorm.io/builder" -// Sql provides raw sql input parameter. When you have a complex SQL statement -// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. -// -// Deprecated: use SQL instead. -func (session *Session) Sql(query string, args ...interface{}) *Session { - return session.SQL(query, args...) -} - // SQL provides raw sql input parameter. When you have a complex SQL statement // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. func (session *Session) SQL(query interface{}, args ...interface{}) *Session { @@ -39,13 +31,6 @@ func (session *Session) Or(query interface{}, args ...interface{}) *Session { return session } -// Id provides converting id as a query condition -// -// Deprecated: use ID instead -func (session *Session) Id(id interface{}) *Session { - return session.ID(id) -} - // ID provides converting id as a query condition func (session *Session) ID(id interface{}) *Session { session.statement.ID(id) diff --git a/session_convert.go b/session_convert.go index 24c51011..020507ed 100644 --- a/session_convert.go +++ b/session_convert.go @@ -224,7 +224,7 @@ func (session *Session) bytes2Value(col *schemas.Column, fieldValue *reflect.Val return err } - if !isPKZero(pk) { + if !pk.IsZero() { // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne // property to be fetched lazily @@ -506,7 +506,7 @@ func (session *Session) bytes2Value(col *schemas.Column, fieldValue *reflect.Val return err } - if !isPKZero(pk) { + if !pk.IsZero() { // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne // property to be fetched lazily diff --git a/session_find.go b/session_find.go index 7ae54a5f..566e83dd 100644 --- a/session_find.go +++ b/session_find.go @@ -142,7 +142,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) if session.statement.JoinStr == "" { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.engine.quoteColumns(session.statement.GroupByStr) + columnStr = session.statement.quoteColumnStr(session.statement.GroupByStr) } else { columnStr = session.statement.genColumnStr() } @@ -150,7 +150,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } else { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.engine.quoteColumns(session.statement.GroupByStr) + columnStr = session.statement.quoteColumnStr(session.statement.GroupByStr) } else { columnStr = "*" } @@ -417,7 +417,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in } else { session.engine.logger.Debug("[cacheFind] cache hit bean:", tableName, id, bean) - pk := session.engine.IdOf(bean) + pk := session.engine.IDOf(bean) xid, err := pk.ToString() if err != nil { return err diff --git a/session_insert.go b/session_insert.go index 4a026b78..b788e629 100644 --- a/session_insert.go +++ b/session_insert.go @@ -13,6 +13,7 @@ import ( "strings" "xorm.io/builder" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -153,7 +154,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error return 0, err } fieldValue := *ptrFieldValue - if col.IsAutoIncrement && isZero(fieldValue.Interface()) { + if col.IsAutoIncrement && utils.IsZero(fieldValue.Interface()) { continue } if col.MapType == schemas.ONLYFROMDB { @@ -204,7 +205,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error } fieldValue := *ptrFieldValue - if col.IsAutoIncrement && isZero(fieldValue.Interface()) { + if col.IsAutoIncrement && utils.IsZero(fieldValue.Interface()) { continue } if col.MapType == schemas.ONLYFROMDB { @@ -250,19 +251,21 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error } cleanupProcessorsClosures(&session.beforeClosures) + quoter := session.engine.dialect.Quoter() var sql string + colStr := quoter.Join(colNames, ",") if session.engine.dialect.DBType() == schemas.ORACLE { temp := fmt.Sprintf(") INTO %s (%v) VALUES (", - session.engine.Quote(tableName), - quoteColumns(colNames, session.engine.Quote, ",")) + quoter.Quote(tableName), + colStr) sql = fmt.Sprintf("INSERT ALL INTO %s (%v) VALUES (%v) SELECT 1 FROM DUAL", - session.engine.Quote(tableName), - quoteColumns(colNames, session.engine.Quote, ","), + quoter.Quote(tableName), + colStr, strings.Join(colMultiPlaces, temp)) } else { sql = fmt.Sprintf("INSERT INTO %s (%v) VALUES (%v)", - session.engine.Quote(tableName), - quoteColumns(colNames, session.engine.Quote, ","), + quoter.Quote(tableName), + colStr, strings.Join(colMultiPlaces, "),(")) } res, err := session.exec(sql, args...) @@ -679,7 +682,7 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac // !evalphobia! set fieldValue as nil when column is nullable and zero-value if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok { - if col.Nullable && isZeroValue(fieldValue) { + if col.Nullable && utils.IsValueZero(fieldValue) { var nilValue *int fieldValue = reflect.ValueOf(nilValue) } diff --git a/session_query.go b/session_query.go index 0623c90c..afed4bcb 100644 --- a/session_query.go +++ b/session_query.go @@ -36,7 +36,7 @@ func (session *Session) genQuerySQL(sqlOrArgs ...interface{}) (string, []interfa if session.statement.JoinStr == "" { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.engine.quoteColumns(session.statement.GroupByStr) + columnStr = session.statement.quoteColumnStr(session.statement.GroupByStr) } else { columnStr = session.statement.genColumnStr() } @@ -44,7 +44,7 @@ func (session *Session) genQuerySQL(sqlOrArgs ...interface{}) (string, []interfa } else { if columnStr == "" { if session.statement.GroupByStr != "" { - columnStr = session.engine.quoteColumns(session.statement.GroupByStr) + columnStr = session.statement.quoteColumnStr(session.statement.GroupByStr) } else { columnStr = "*" } diff --git a/session_update.go b/session_update.go index 7316c3b2..427d452d 100644 --- a/session_update.go +++ b/session_update.go @@ -13,6 +13,7 @@ import ( "xorm.io/builder" "xorm.io/xorm/caches" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -518,7 +519,7 @@ func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interfac // !evalphobia! set fieldValue as nil when column is nullable and zero-value if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok { - if col.Nullable && isZeroValue(fieldValue) { + if col.Nullable && utils.IsValueZero(fieldValue) { var nilValue *int fieldValue = reflect.ValueOf(nilValue) } diff --git a/statement.go b/statement.go index 26c5bd1b..9dc5bf52 100644 --- a/statement.go +++ b/statement.go @@ -13,6 +13,7 @@ import ( "xorm.io/builder" "xorm.io/xorm/dialects" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -304,7 +305,7 @@ func (statement *Statement) buildUpdates(bean interface{}, // !evalphobia! set fieldValue as nil when column is nullable and zero-value if b, ok := getFlagForColumn(nullableMap, col); ok { - if b && col.Nullable && isZero(fieldValue.Interface()) { + if b && col.Nullable && utils.IsZero(fieldValue.Interface()) { var nilValue *int fieldValue = reflect.ValueOf(nilValue) fieldType = reflect.TypeOf(fieldValue.Interface()) @@ -404,7 +405,7 @@ func (statement *Statement) buildUpdates(bean interface{}, if len(table.PrimaryKeys) == 1 { pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) // fix non-int pk issues - if pkField.IsValid() && (!requiredField && !isZero(pkField.Interface())) { + if pkField.IsValid() && (!requiredField && !utils.IsZero(pkField.Interface())) { val = pkField.Interface() } else { continue @@ -418,7 +419,7 @@ func (statement *Statement) buildUpdates(bean interface{}, } } else { // Blank struct could not be as update data - if requiredField || !isStructZero(fieldValue) { + if requiredField || !utils.IsStructZero(fieldValue) { bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface())) @@ -439,7 +440,7 @@ func (statement *Statement) buildUpdates(bean interface{}, continue } if fieldType.Kind() == reflect.Array { - if isArrayValueZero(fieldValue) { + if utils.IsArrayZero(fieldValue) { continue } } else if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { @@ -939,6 +940,11 @@ func (statement *Statement) genConds(bean interface{}) (string, []interface{}, e return builder.ToSQL(statement.cond) } +func (statement *Statement) quoteColumnStr(columnStr string) string { + columns := strings.Split(columnStr, ",") + return statement.Engine.dialect.Quoter().Join(columns, ",") +} + func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, error) { v := rValue(bean) isStruct := v.Kind() == reflect.Struct @@ -954,7 +960,7 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, if len(statement.JoinStr) == 0 { if len(columnStr) == 0 { if len(statement.GroupByStr) > 0 { - columnStr = statement.Engine.quoteColumns(statement.GroupByStr) + columnStr = statement.quoteColumnStr(statement.GroupByStr) } else { columnStr = statement.genColumnStr() } @@ -962,7 +968,7 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, } else { if len(columnStr) == 0 { if len(statement.GroupByStr) > 0 { - columnStr = statement.Engine.quoteColumns(statement.GroupByStr) + columnStr = statement.quoteColumnStr(statement.GroupByStr) } } }