From 6b8d0973c7efad78170c7ee425431a11a7d5a554 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 26 Jun 2015 21:21:12 +0800 Subject: [PATCH 01/34] bug fixed for #261 --- xorm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xorm.go b/xorm.go index 036d420f..f5f8f2a7 100644 --- a/xorm.go +++ b/xorm.go @@ -39,7 +39,7 @@ func regDrvsNDialects() bool { for driverName, v := range providedDrvsNDialects { if driver := core.QueryDriver(driverName); driver == nil { core.RegisterDriver(driverName, v.getDriver()) - core.RegisterDialect(v.dbType, v.getDialect()) + core.RegisterDialect(v.dbType, v.getDialect) } } return true From 2ee7de9fa5577a77ca28cef30af678aea50e91ba Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 27 Jun 2015 12:36:38 +0800 Subject: [PATCH 02/34] bug fixed for sync2 --- session.go | 33 +++++++++++++++++++-------------- xorm.go | 2 +- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/session.go b/session.go index a1984805..79dbe3ae 100644 --- a/session.go +++ b/session.go @@ -3889,6 +3889,7 @@ func (s *Session) Sync2(beans ...interface{}) error { } var foundIndexNames = make(map[string]bool) + var addedNames = make(map[string]*core.Index) for name, index := range table.Indexes { var oriIndex *core.Index @@ -3912,20 +3913,7 @@ func (s *Session) Sync2(beans ...interface{}) error { } if oriIndex == nil { - if index.Type == core.UniqueType { - session := engine.NewSession() - session.Statement.RefTable = table - defer session.Close() - err = session.addUnique(table.Name, name) - } else if index.Type == core.IndexType { - session := engine.NewSession() - session.Statement.RefTable = table - defer session.Close() - err = session.addIndex(table.Name, name) - } - if err != nil { - return err - } + addedNames[name] = index } } @@ -3938,6 +3926,23 @@ func (s *Session) Sync2(beans ...interface{}) error { } } } + + for name, index := range addedNames { + if index.Type == core.UniqueType { + session := engine.NewSession() + session.Statement.RefTable = table + defer session.Close() + err = session.addUnique(table.Name, name) + } else if index.Type == core.IndexType { + session := engine.NewSession() + session.Statement.RefTable = table + defer session.Close() + err = session.addIndex(table.Name, name) + } + if err != nil { + return err + } + } } } diff --git a/xorm.go b/xorm.go index f5f8f2a7..8e630b9a 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.3.0526" + Version string = "0.4.3.0627" ) func regDrvsNDialects() bool { From ffa0712280455c6897af2b0cdba3f5da90a486b0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 8 Jul 2015 16:53:35 +0800 Subject: [PATCH 03/34] resolved for go-gitea/gitea#105 --- examples/goroutine.db-journal | Bin 2576 -> 0 bytes mssql_dialect.go | 5 +++++ sqlite3_dialect.go | 9 ++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) delete mode 100644 examples/goroutine.db-journal diff --git a/examples/goroutine.db-journal b/examples/goroutine.db-journal deleted file mode 100644 index 95fc238e2941a7bdc3c6c855336d5289be8828d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2576 zcmeI!J5K^Z6b0bn99-AODvBt+@r`d(NGvTWEw`jGp|BtrBPAuBlyp*3Qc_Ykbsk+iI!eJoV0w9_wr7r Date: Mon, 20 Jul 2015 09:25:07 +0800 Subject: [PATCH 04/34] add support sql.NullString sql.NullInt64 ... --- session.go | 226 +++++++++++++++++++++++++++++------------------------ 1 file changed, 123 insertions(+), 103 deletions(-) diff --git a/session.go b/session.go index 79dbe3ae..a1be49e5 100644 --- a/session.go +++ b/session.go @@ -6,6 +6,7 @@ package xorm import ( "database/sql" + "database/sql/driver" "encoding/json" "errors" "fmt" @@ -637,7 +638,7 @@ func (session *Session) canCache() bool { if session.Statement.RefTable == nil || session.Statement.JoinStr != "" || session.Statement.RawSQL != "" || - session.Tx != nil || + session.Tx != nil || len(session.Statement.selectStr) > 0 { return false } @@ -744,7 +745,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf } func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) { - if !session.canCache() || + if !session.canCache() || indexNoCase(sqlStr, "having") != -1 || indexNoCase(sqlStr, "group by") != -1 { return ErrCacheFailed @@ -1187,7 +1188,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) var addedTableName = (len(session.Statement.JoinStr) > 0) colNames, args := buildConditions(session.Engine, table, condiBean[0], true, true, false, true, session.Statement.allUseBool, session.Statement.useAllCols, - session.Statement.unscoped, session.Statement.mustColumnMap, + session.Statement.unscoped, session.Statement.mustColumnMap, session.Statement.TableName(), addedTableName) session.Statement.ConditionStr = strings.Join(colNames, " AND ") session.Statement.BeanArgs = args @@ -1314,7 +1315,6 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } table := session.Engine.autoMapType(dataStruct) - return session.rows2Beans(rawRows, fields, fieldsCount, table, newElemFunc, sliceValueSetFunc) } else { resultsSlice, err := session.query(sqlStr, args...) @@ -1755,6 +1755,14 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount vv = reflect.ValueOf(t) fieldValue.Set(vv) } + } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { + // !! 增加支持sql.Scanner接口的结构,如sql.NullString + hasAssigned = true + if err := nulVal.Scan(vv.Interface()); err != nil { + fmt.Println("sql.Sanner error:", err.Error()) + session.Engine.LogError("sql.Sanner error:", err.Error()) + hasAssigned = false + } } else if session.Statement.UseCascade { table := session.Engine.autoMapType(*fieldValue) if table != nil { @@ -1762,6 +1770,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount panic("unsupported composited primary key cascade") } var pk = make(core.PK, len(table.PrimaryKeys)) + switch rawValueType.Kind() { case reflect.Int64: pk[0] = vv.Int() @@ -2416,108 +2425,115 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, fieldValue.SetUint(x) //Currently only support Time type case reflect.Struct: - if fieldType.ConvertibleTo(core.TimeType) { - x, err := session.byte2Time(col, data) - if err != nil { - return err + // !! 增加支持sql.Scanner接口的结构,如sql.NullString + if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { + if err := nulVal.Scan(data); err != nil { + return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error()) } - v = x - fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) - } else if session.Statement.UseCascade { - table := session.Engine.autoMapType(*fieldValue) - if table != nil { - if len(table.PrimaryKeys) > 1 { - panic("unsupported composited primary key cascade") + } else { + if fieldType.ConvertibleTo(core.TimeType) { + x, err := session.byte2Time(col, data) + if err != nil { + return err } - var pk = make(core.PK, len(table.PrimaryKeys)) - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) - switch rawValueType.Kind() { - case reflect.Int64: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) + v = x + fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) + } else if session.Statement.UseCascade { + table := session.Engine.autoMapType(*fieldValue) + if table != nil { + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") } - pk[0] = x - case reflect.Int: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) + var pk = make(core.PK, len(table.PrimaryKeys)) + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + switch rawValueType.Kind() { + case reflect.Int64: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = x + case reflect.Int: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int(x) + case reflect.Int32: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int32(x) + case reflect.Int16: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int16(x) + case reflect.Int8: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int8(x) + case reflect.Uint64: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = x + case reflect.Uint: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint(x) + case reflect.Uint32: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint32(x) + case reflect.Uint16: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint16(x) + case reflect.Uint8: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint8(x) + case reflect.String: + pk[0] = string(data) + default: + panic("unsupported primary key type cascade") } - pk[0] = int(x) - case reflect.Int32: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = int32(x) - case reflect.Int16: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = int16(x) - case reflect.Int8: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = int8(x) - case reflect.Uint64: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = x - case reflect.Uint: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = uint(x) - case reflect.Uint32: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = uint32(x) - case reflect.Uint16: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = uint16(x) - case reflect.Uint8: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = uint8(x) - case reflect.String: - pk[0] = string(data) - default: - panic("unsupported primary key type cascade") - } - if !isPKZero(pk) { - // !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 - structInter := reflect.New(fieldValue.Type()) - newsession := session.Engine.NewSession() - defer newsession.Close() - has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) - if err != nil { - return err - } - if has { - v = structInter.Elem().Interface() - fieldValue.Set(reflect.ValueOf(v)) - } else { - return errors.New("cascade obj is not exist!") + if !isPKZero(pk) { + // !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 + structInter := reflect.New(fieldValue.Type()) + newsession := session.Engine.NewSession() + defer newsession.Close() + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) + if err != nil { + return err + } + if has { + v = structInter.Elem().Interface() + fieldValue.Set(reflect.ValueOf(v)) + } else { + return errors.New("cascade obj is not exist!") + } } + } else { + return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) } - } else { - return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) } } case reflect.Ptr: @@ -2931,6 +2947,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val tf := session.Engine.FormatTime(col.SQLType.Name, t) return tf, nil } + if fieldTable, ok := session.Engine.Tables[fieldValue.Type()]; ok { if len(fieldTable.PrimaryKeys) == 1 { pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) @@ -2939,6 +2956,11 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val return 0, fmt.Errorf("no primary key for col %v", col.Name) } } else { + // !! 增加支持driver.Valuer接口的结构,如sql.NullString + if v, ok := fieldValue.Interface().(driver.Valuer); ok { + return v.Value() + } + return 0, fmt.Errorf("Unsupported type %v", fieldValue.Type()) } case reflect.Complex64, reflect.Complex128: @@ -2998,12 +3020,10 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { processor.BeforeInsert() } // -- - colNames, args, err := genCols(table, session, bean, false, false) if err != nil { return 0, err } - // insert expr columns, override if exists exprColumns := session.Statement.getExpr() exprColVals := make([]string, 0, len(exprColumns)) @@ -3414,7 +3434,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 if session.Statement.ColumnStr == "" { colNames, args = buildUpdates(session.Engine, table, bean, false, false, false, false, session.Statement.allUseBool, session.Statement.useAllCols, - session.Statement.mustColumnMap, session.Statement.nullableMap, + session.Statement.mustColumnMap, session.Statement.nullableMap, session.Statement.columnMap, true) } else { colNames, args, err = genCols(table, session, bean, true, true) @@ -3696,7 +3716,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) { session.Statement.RefTable = table colNames, args := buildConditions(session.Engine, table, bean, true, true, false, true, session.Statement.allUseBool, session.Statement.useAllCols, - session.Statement.unscoped, session.Statement.mustColumnMap, + session.Statement.unscoped, session.Statement.mustColumnMap, session.Statement.TableName(), false) var condition = "" From 916367d81e4383436220bc89496a20556298bc9e Mon Sep 17 00:00:00 2001 From: haolei Date: Mon, 20 Jul 2015 11:23:29 +0800 Subject: [PATCH 05/34] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=94=AF=E6=8C=81sql.N?= =?UTF-8?q?ullString...Iterate=E6=96=B9=E6=B3=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- statement.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/statement.go b/statement.go index 0a8de1ee..d5f6d072 100644 --- a/statement.go +++ b/statement.go @@ -5,6 +5,7 @@ package xorm import ( + "database/sql/driver" "encoding/json" "errors" "fmt" @@ -49,7 +50,7 @@ type Statement struct { GroupByStr string HavingStr string ColumnStr string - selectStr string + selectStr string columnMap map[string]bool useAllCols bool OmitStr string @@ -416,7 +417,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, var colName string if addedTableName { - colName = engine.Quote(tableName)+"."+engine.Quote(col.Name) + colName = engine.Quote(tableName) + "." + engine.Quote(col.Name) } else { colName = engine.Quote(col.Name) } @@ -428,7 +429,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, } if col.IsDeleted && !unscoped { // tag "deleted" is enabled - colNames = append(colNames, fmt.Sprintf("(%v IS NULL or %v = '0001-01-01 00:00:00')", + colNames = append(colNames, fmt.Sprintf("(%v IS NULL or %v = '0001-01-01 00:00:00')", colName, colName)) } @@ -509,6 +510,11 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, val = engine.FormatTime(col.SQLType.Name, t) } else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok { continue + } else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok { + val, _ = valNul.Value() + if val == nil { + continue + } } else { engine.autoMapType(fieldValue) if table, ok := engine.Tables[fieldValue.Type()]; ok { From 3189b41ec3dc91e263199ddca7a6f0e63da90f5d Mon Sep 17 00:00:00 2001 From: haolei Date: Mon, 20 Jul 2015 14:13:13 +0800 Subject: [PATCH 06/34] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=94=AF=E6=8C=81sql.N?= =?UTF-8?q?ullString=E5=AD=97=E6=AE=B5=E6=97=B6=EF=BC=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?Iterater=E6=96=B9=E6=B3=95=E6=97=B6=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- statement.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/statement.go b/statement.go index d5f6d072..56c0471f 100644 --- a/statement.go +++ b/statement.go @@ -321,6 +321,10 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, continue } val = engine.FormatTime(col.SQLType.Name, t) + } else if nulVal, ok := fieldValue.Interface().(driver.Valuer); ok { + if val, _ = nulVal.Value(); val == nil { + continue + } } else { engine.autoMapType(fieldValue) if table, ok := engine.Tables[fieldValue.Type()]; ok { From 88a0888d6d897ffd0e033275839b6f5b10624198 Mon Sep 17 00:00:00 2001 From: haolei Date: Sun, 26 Jul 2015 00:01:06 +0800 Subject: [PATCH 07/34] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=94=AF=E6=8C=81sql.N?= =?UTF-8?q?ullString...=E6=97=B6AllCols()=E6=97=B6=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A9=BA=E5=AD=97=E6=AE=B5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- statement.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/statement.go b/statement.go index 56c0471f..6919b6c5 100644 --- a/statement.go +++ b/statement.go @@ -220,6 +220,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, requiredField := useAllCols includeNil := useAllCols lColName := strings.ToLower(col.Name) + if b, ok := mustColumnMap[lColName]; ok { if b { requiredField = true @@ -321,10 +322,8 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, continue } val = engine.FormatTime(col.SQLType.Name, t) - } else if nulVal, ok := fieldValue.Interface().(driver.Valuer); ok { - if val, _ = nulVal.Value(); val == nil { - continue - } + } else if nulType, ok := fieldValue.Interface().(driver.Valuer); ok { + val, _ = nulType.Value() } else { engine.autoMapType(fieldValue) if table, ok := engine.Tables[fieldValue.Type()]; ok { From b6a31aa9a3c6660afa963489cb7782a5ac01a940 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 28 Jul 2015 10:03:32 +0800 Subject: [PATCH 08/34] bug fixed #260 --- session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.go b/session.go index a1be49e5..b50c46cf 100644 --- a/session.go +++ b/session.go @@ -625,7 +625,7 @@ func (statement *Statement) convertIdSql(sqlStr string) string { } colstrs := statement.JoinColumns(cols) - sqls := splitNNoCase(sqlStr, "from", 2) + sqls := splitNNoCase(sqlStr, " from ", 2) if len(sqls) != 2 { return "" } From ebd11048f3d3205228f85720d1c082dd93da8264 Mon Sep 17 00:00:00 2001 From: evalphobia Date: Tue, 28 Jul 2015 13:03:20 +0900 Subject: [PATCH 09/34] Added feature for storing lastSQL query on session --- rows.go | 2 +- session.go | 32 +++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/rows.go b/rows.go index 2d3bbb85..fb18454d 100644 --- a/rows.go +++ b/rows.go @@ -45,7 +45,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable) } - rows.session.Engine.logSQL(sqlStr, args) + rows.session.saveLastSQL(sqlStr, args) var err error rows.stmt, err = rows.session.DB().Prepare(sqlStr) if err != nil { diff --git a/session.go b/session.go index b50c46cf..04ea196c 100644 --- a/session.go +++ b/session.go @@ -46,6 +46,10 @@ type Session struct { stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) cascadeDeep int + + // !evalphobia! stored the last executed query on this session + lastSQL string + lastSQLArgs []interface{} } // Method Init reset the session as the init status. @@ -63,6 +67,9 @@ func (session *Session) Init() { session.afterDeleteBeans = make(map[interface{}]*[]func(interface{}), 0) session.beforeClosures = make([]func(interface{}), 0) session.afterClosures = make([]func(interface{}), 0) + + session.lastSQL = "" + session.lastSQLArgs = []interface{}{} } // Method Close release the connection from pool @@ -325,8 +332,7 @@ func (session *Session) Begin() error { session.IsAutoCommit = false session.IsCommitedOrRollbacked = false session.Tx = tx - - session.Engine.logSQL("BEGIN TRANSACTION") + session.saveLastSQL("BEGIN TRANSACTION") } return nil } @@ -334,7 +340,7 @@ func (session *Session) Begin() error { // When using transaction, you can rollback if any error func (session *Session) Rollback() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { - session.Engine.logSQL(session.Engine.dialect.RollBackStr()) + session.saveLastSQL(session.Engine.dialect.RollBackStr()) session.IsCommitedOrRollbacked = true return session.Tx.Rollback() } @@ -344,7 +350,7 @@ func (session *Session) Rollback() error { // When using transaction, Commit will commit all operations. func (session *Session) Commit() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { - session.Engine.logSQL("COMMIT") + session.saveLastSQL("COMMIT") session.IsCommitedOrRollbacked = true var err error if err = session.Tx.Commit(); err == nil { @@ -465,7 +471,7 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } - session.Engine.logSQL(sqlStr, args...) + session.saveLastSQL(sqlStr, args...) return session.Engine.LogSQLExecutionTime(sqlStr, args, func() (sql.Result, error) { if session.IsAutoCommit { @@ -1457,7 +1463,7 @@ func (session *Session) isTableEmpty(tableName string) (bool, error) { var total int64 sql := fmt.Sprintf("select count(*) from %s", session.Engine.Quote(tableName)) err := session.DB().QueryRow(sql).Scan(&total) - session.Engine.logSQL(sql) + session.saveLastSQL(sql) if err != nil { return true, err } @@ -1958,7 +1964,7 @@ func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) *sqlStr = filter.Do(*sqlStr, session.Engine.dialect, session.Statement.RefTable) } - session.Engine.logSQL(*sqlStr, paramStr...) + session.saveLastSQL(*sqlStr, paramStr...) } func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { @@ -3821,6 +3827,18 @@ func (session *Session) Delete(bean interface{}) (int64, error) { return res.RowsAffected() } +// saveLastSQL stores executed query information +func (session *Session) saveLastSQL(sql string, args ...interface{}) { + session.lastSQL = sql + session.lastSQLArgs = args + session.Engine.logSQL(sql, args...) +} + +// LastSQL returns last query information +func (session *Session) LastSQL() (string, []interface{}) { + return session.lastSQL, session.lastSQLArgs +} + func (s *Session) Sync2(beans ...interface{}) error { engine := s.Engine From f9e450f3488d71533ac3acd3f8f5cf441c61dea9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 6 Aug 2015 14:24:17 +0800 Subject: [PATCH 10/34] add AfterSet processor support & resolved #276 --- VERSION | 2 +- processors.go | 4 +++ session.go | 78 ++++++++++++++++++++++++++++++++++++++++++--------- statement.go | 49 +++++++++++++++++++++++--------- xorm.go | 2 +- 5 files changed, 106 insertions(+), 29 deletions(-) diff --git a/VERSION b/VERSION index af3e71ec..62af1c7f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.3.0520 +xorm v0.4.3.0806 diff --git a/processors.go b/processors.go index 18071c1f..8f95ae3b 100644 --- a/processors.go +++ b/processors.go @@ -23,6 +23,10 @@ type BeforeSetProcessor interface { BeforeSet(string, Cell) } +type AfterSetProcessor interface { + AfterSet(string, Cell) +} + // !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations //// Executed before an object is validated //type BeforeValidateProcessor interface { diff --git a/session.go b/session.go index b50c46cf..af146496 100644 --- a/session.go +++ b/session.go @@ -1626,6 +1626,14 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount } } + defer func() { + if b, hasAfterSet := bean.(AfterSetProcessor); hasAfterSet { + for ii, key := range fields { + b.AfterSet(key, Cell(scanResults[ii].(*interface{}))) + } + } + }() + var tempMap = make(map[string]int) for ii, key := range fields { var idx int @@ -1675,7 +1683,6 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount hasAssigned := false switch fieldType.Kind() { - case reflect.Complex64, reflect.Complex128: if rawValueType.Kind() == reflect.String { hasAssigned = true @@ -1686,6 +1693,15 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount return err } fieldValue.Set(x.Elem()) + } else if rawValueType.Kind() == reflect.Slice { + hasAssigned = true + x := reflect.New(fieldType) + err := json.Unmarshal(vv.Bytes(), x.Interface()) + if err != nil { + session.Engine.LogError(err) + return err + } + fieldValue.Set(x.Elem()) } case reflect.Slice, reflect.Array: switch rawValueType.Kind() { @@ -1730,6 +1746,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount fieldValue.SetUint(uint64(vv.Int())) } case reflect.Struct: + col := table.GetColumn(key) if fieldType.ConvertibleTo(core.TimeType) { if rawValueType == core.TimeType { hasAssigned = true @@ -1763,11 +1780,31 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount session.Engine.LogError("sql.Sanner error:", err.Error()) hasAssigned = false } + } else if col.SQLType.IsJson() { + if rawValueType.Kind() == reflect.String { + hasAssigned = true + x := reflect.New(fieldType) + err := json.Unmarshal([]byte(vv.String()), x.Interface()) + if err != nil { + session.Engine.LogError(err) + return err + } + fieldValue.Set(x.Elem()) + } else if rawValueType.Kind() == reflect.Slice { + hasAssigned = true + x := reflect.New(fieldType) + err := json.Unmarshal(vv.Bytes(), x.Interface()) + if err != nil { + session.Engine.LogError(err) + return err + } + fieldValue.Set(x.Elem()) + } } else if session.Statement.UseCascade { table := session.Engine.autoMapType(*fieldValue) if table != nil { - if len(table.PrimaryKeys) > 1 { - panic("unsupported composited primary key cascade") + if len(table.PrimaryKeys) != 1 { + panic("unsupported non or composited primary key cascade") } var pk = make(core.PK, len(table.PrimaryKeys)) @@ -2952,17 +2989,31 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val if len(fieldTable.PrimaryKeys) == 1 { pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) return pkField.Interface(), nil - } else { - return 0, fmt.Errorf("no primary key for col %v", col.Name) } - } else { - // !! 增加支持driver.Valuer接口的结构,如sql.NullString - if v, ok := fieldValue.Interface().(driver.Valuer); ok { - return v.Value() - } - - return 0, fmt.Errorf("Unsupported type %v", fieldValue.Type()) + return 0, fmt.Errorf("no primary key for col %v", col.Name) } + // !! 增加支持driver.Valuer接口的结构,如sql.NullString + if v, ok := fieldValue.Interface().(driver.Valuer); ok { + return v.Value() + } + + if col.SQLType.IsText() { + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + session.Engine.LogError(err) + return 0, err + } + return string(bytes), nil + } else if col.SQLType.IsBlob() { + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + session.Engine.LogError(err) + return 0, err + } + return bytes, nil + } + + return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) case reflect.Complex64, reflect.Complex128: bytes, err := json.Marshal(fieldValue.Interface()) if err != nil { @@ -2996,9 +3047,8 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val } } return bytes, nil - } else { - return nil, ErrUnSupportedType } + return nil, ErrUnSupportedType case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: return int64(fieldValue.Uint()), nil default: diff --git a/statement.go b/statement.go index 6919b6c5..c3e93eed 100644 --- a/statement.go +++ b/statement.go @@ -417,6 +417,9 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text { continue } + if col.SQLType.IsJson() { + continue + } var colName string if addedTableName { @@ -519,23 +522,43 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, continue } } else { - engine.autoMapType(fieldValue) - if table, ok := engine.Tables[fieldValue.Type()]; ok { - 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.IsValid() && !isZero(pkField.Interface()) { - val = pkField.Interface() - } else { + if col.SQLType.IsJson() { + if col.SQLType.IsText() { + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + engine.LogError(err) continue } - } else { - //TODO: how to handler? - panic(fmt.Sprintln("not supported", fieldValue.Interface(), "as", table.PrimaryKeys)) + val = string(bytes) + } else if col.SQLType.IsBlob() { + var bytes []byte + var err error + bytes, err = json.Marshal(fieldValue.Interface()) + if err != nil { + engine.LogError(err) + continue + } + val = bytes } } else { - val = fieldValue.Interface() + engine.autoMapType(fieldValue) + if table, ok := engine.Tables[fieldValue.Type()]; ok { + 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.IsValid() && !isZero(pkField.Interface()) { + val = pkField.Interface() + } else { + continue + } + } else { + //TODO: how to handler? + panic(fmt.Sprintln("not supported", fieldValue.Interface(), "as", table.PrimaryKeys)) + } + } else { + val = fieldValue.Interface() + } } } case reflect.Array, reflect.Slice, reflect.Map: diff --git a/xorm.go b/xorm.go index 8e630b9a..341160c5 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.3.0627" + Version string = "0.4.3.0806" ) func regDrvsNDialects() bool { From 29fd03b318909228491b68b6af86b752c44c3f5f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 7 Aug 2015 09:55:42 +0800 Subject: [PATCH 11/34] hack for ql support --- session.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/session.go b/session.go index c1c48bc4..714efc03 100644 --- a/session.go +++ b/session.go @@ -48,8 +48,8 @@ type Session struct { cascadeDeep int // !evalphobia! stored the last executed query on this session - lastSQL string - lastSQLArgs []interface{} + lastSQL string + lastSQLArgs []interface{} } // Method Init reset the session as the init status. @@ -614,11 +614,15 @@ func (session *Session) DropTable(beanOrTableName interface{}) error { return nil } -func (statement *Statement) JoinColumns(cols []*core.Column) string { +func (statement *Statement) JoinColumns(cols []*core.Column, includeTableName bool) string { var colnames = make([]string, len(cols)) for i, col := range cols { - colnames[i] = statement.Engine.Quote(statement.TableName()) + - "." + statement.Engine.Quote(col.Name) + if includeTableName { + colnames[i] = statement.Engine.Quote(statement.TableName()) + + "." + statement.Engine.Quote(col.Name) + } else { + colnames[i] = statement.Engine.Quote(col.Name) + } } return strings.Join(colnames, ", ") } @@ -630,11 +634,14 @@ func (statement *Statement) convertIdSql(sqlStr string) string { return "" } - colstrs := statement.JoinColumns(cols) + colstrs := statement.JoinColumns(cols, false) sqls := splitNNoCase(sqlStr, " from ", 2) if len(sqls) != 2 { return "" } + if statement.Engine.dialect.DBType() == "ql" { + return fmt.Sprintf("SELECT id() FROM %v", sqls[1]) + } return fmt.Sprintf("SELECT %s FROM %v", colstrs, sqls[1]) } return "" @@ -3280,7 +3287,7 @@ func (statement *Statement) convertUpdateSql(sqlStr string) (string, string) { return "", "" } - colstrs := statement.JoinColumns(statement.RefTable.PKColumns()) + colstrs := statement.JoinColumns(statement.RefTable.PKColumns(), true) sqls := splitNNoCase(sqlStr, "where", 2) if len(sqls) != 2 { if len(sqls) == 1 { From b3ec16b09ce2be7ac94962a5532ef266016a4c55 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 12 Aug 2015 22:02:27 +0800 Subject: [PATCH 12/34] bug fixed #280 --- VERSION | 2 +- examples/goroutine.go | 4 ++-- statement.go | 1 + xorm.go | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 62af1c7f..bff78cca 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.3.0806 +xorm v0.4.3.0812 diff --git a/examples/goroutine.go b/examples/goroutine.go index b18fe4f8..f99c9fbe 100644 --- a/examples/goroutine.go +++ b/examples/goroutine.go @@ -33,7 +33,7 @@ func test(engine *xorm.Engine) { return } - size := 500 + size := 100 queue := make(chan int, size) for i := 0; i < size; i++ { @@ -83,7 +83,7 @@ func test(engine *xorm.Engine) { } func main() { - runtime.GOMAXPROCS(1) + runtime.GOMAXPROCS(2) fmt.Println("-----start sqlite go routines-----") engine, err := sqliteEngine() if err != nil { diff --git a/statement.go b/statement.go index c3e93eed..f4d0bcf5 100644 --- a/statement.go +++ b/statement.go @@ -818,6 +818,7 @@ func (statement *Statement) Cols(columns ...string) *Statement { if strings.Contains(statement.ColumnStr, ".") { statement.ColumnStr = strings.Replace(statement.ColumnStr, ".", statement.Engine.Quote("."), -1) } + statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.Quote("*"), "*", -1) return statement } diff --git a/xorm.go b/xorm.go index 341160c5..df4f3c18 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.3.0806" + Version string = "0.4.3.0812" ) func regDrvsNDialects() bool { From 97e7703766cb821b4903ddfc0468b21d5b26a717 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 12 Aug 2015 23:11:45 +0800 Subject: [PATCH 13/34] bug fixed & comment trace line --- session.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/session.go b/session.go index 714efc03..3e26bba6 100644 --- a/session.go +++ b/session.go @@ -1767,7 +1767,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount t := vv.Convert(core.TimeType).Interface().(time.Time) z, _ := t.Zone() if len(z) == 0 || t.Year() == 0 { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location - session.Engine.LogDebug("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) + session.Engine.LogDebugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.Local) } @@ -1789,7 +1789,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount // !! 增加支持sql.Scanner接口的结构,如sql.NullString hasAssigned = true if err := nulVal.Scan(vv.Interface()); err != nil { - fmt.Println("sql.Sanner error:", err.Error()) + //fmt.Println("sql.Sanner error:", err.Error()) session.Engine.LogError("sql.Sanner error:", err.Error()) hasAssigned = false } From a01186e3cdc3f3e28019d53d62fe782108fc1b45 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 24 Aug 2015 16:43:58 +0800 Subject: [PATCH 14/34] bug fixed for #283 --- VERSION | 2 +- session.go | 22 +++++++++++++--------- xorm.go | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index bff78cca..915f9513 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.3.0812 +xorm v0.4.3.0824 diff --git a/session.go b/session.go index 3e26bba6..a8f53117 100644 --- a/session.go +++ b/session.go @@ -2998,16 +2998,20 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val return tf, nil } - if fieldTable, ok := session.Engine.Tables[fieldValue.Type()]; ok { - if len(fieldTable.PrimaryKeys) == 1 { - pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) - return pkField.Interface(), nil + if !col.SQLType.IsJson() { + // !! 增加支持driver.Valuer接口的结构,如sql.NullString + if v, ok := fieldValue.Interface().(driver.Valuer); ok { + return v.Value() } - return 0, fmt.Errorf("no primary key for col %v", col.Name) - } - // !! 增加支持driver.Valuer接口的结构,如sql.NullString - if v, ok := fieldValue.Interface().(driver.Valuer); ok { - return v.Value() + + fieldTable := session.Engine.autoMapType(fieldValue) + //if fieldTable, ok := session.Engine.Tables[fieldValue.Type()]; ok { + if len(fieldTable.PrimaryKeys) == 1 { + pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) + return pkField.Interface(), nil + } + return 0, fmt.Errorf("no primary key for col %v", col.Name) + //} } if col.SQLType.IsText() { diff --git a/xorm.go b/xorm.go index df4f3c18..4f4d8938 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.3.0812" + Version string = "0.4.3.0824" ) func regDrvsNDialects() bool { From b6ffc57f8060e0f46f85ea3737599e40eab90d24 Mon Sep 17 00:00:00 2001 From: TossPig Date: Tue, 25 Aug 2015 22:37:16 +0800 Subject: [PATCH 15/34] =?UTF-8?q?=E4=BF=AE=E6=94=B9GetColumns=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 防止数据库中,存在多个schema同表名,不同的字段属性的污染 --- postgres_dialect.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/postgres_dialect.go b/postgres_dialect.go index 67ceecd0..972a214f 100644 --- a/postgres_dialect.go +++ b/postgres_dialect.go @@ -913,7 +913,8 @@ func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { } func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { - args := []interface{}{tableName} + pgSchema := "public" + args := []interface{}{tableName,pgSchema} s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix , CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey, CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey @@ -924,7 +925,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 c.relkind = 'r'::char AND c.relname = $1 AND f.attnum > 0 ORDER BY f.attnum;` +WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;` rows, err := db.DB().Query(s, args...) if db.Logger != nil { From 964ff43d18400e60492f776b2fbd57f879c79154 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Aug 2015 09:39:56 +0800 Subject: [PATCH 16/34] added gitter chatroom --- README.md | 2 ++ README_CN.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 25fbc7b2..ddea8f92 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Xorm is a simple and powerful ORM for Go. +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + [![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge") # Features diff --git a/README_CN.md b/README_CN.md index fb08040b..8ab58ea5 100644 --- a/README_CN.md +++ b/README_CN.md @@ -4,6 +4,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。 +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + [![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) ## 特性 From adadb47c60fcb8c6ed4029620eb152f47ec92d0e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Aug 2015 14:28:59 +0800 Subject: [PATCH 17/34] bug fixed for oracle insert --- session.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/session.go b/session.go index a8f53117..f0edb0ba 100644 --- a/session.go +++ b/session.go @@ -3212,6 +3212,64 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { aiValue.Set(reflect.ValueOf(v)) return res.RowsAffected() + } else if session.Engine.DriverName() == core.ORACLE { + //assert table.AutoIncrement != "" + res, err := session.query("select seq_atable.currval from dual", args...) + + if err != nil { + return 0, err + } else { + handleAfterInsertProcessorFunc(bean) + } + + if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { + session.cacheInsert(session.Statement.TableName()) + } + + if table.Version != "" && session.Statement.checkVersion { + verValue, err := table.VersionColumn().ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + } else if verValue.IsValid() && verValue.CanSet() { + verValue.SetInt(1) + } + } + + if len(res) < 1 { + return 0, errors.New("insert no error but not returned id") + } + + idByte := res[0][table.AutoIncrement] + id, err := strconv.ParseInt(string(idByte), 10, 64) + if err != nil { + return 1, err + } + + aiValue, err := table.AutoIncrColumn().ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + } + + if aiValue == nil || !aiValue.IsValid() /*|| aiValue. != 0*/ || !aiValue.CanSet() { + return 1, nil + } + + var v interface{} = id + switch aiValue.Type().Kind() { + case reflect.Int32: + v = int32(id) + case reflect.Int: + v = int(id) + case reflect.Uint32: + v = uint32(id) + case reflect.Uint64: + v = uint64(id) + case reflect.Uint: + v = uint(id) + } + aiValue.Set(reflect.ValueOf(v)) + + return 1, nil } else { //assert table.AutoIncrement != "" sqlStr = sqlStr + " RETURNING " + session.Engine.Quote(table.AutoIncrement) From e89f74daa0fd551ba07b96b2dfb02d5bdf59e43f Mon Sep 17 00:00:00 2001 From: evalphobia Date: Fri, 28 Aug 2015 16:54:19 +0900 Subject: [PATCH 18/34] Added feature for SELECT ... FOR UPDATE --- session.go | 6 ++++++ statement.go | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/session.go b/session.go index f0edb0ba..9b17958e 100644 --- a/session.go +++ b/session.go @@ -225,6 +225,12 @@ func (session *Session) Distinct(columns ...string) *Session { return session } +// Set Read/Write locking for UPDATE +func (session *Session) ForUpdate() *Session { + session.Statement.IsForUpdate = true + return session +} + // Only not use the paramters as select or update columns func (session *Session) Omit(columns ...string) *Session { session.Statement.Omit(columns...) diff --git a/statement.go b/statement.go index f4d0bcf5..063a6193 100644 --- a/statement.go +++ b/statement.go @@ -66,6 +66,7 @@ type Statement struct { UseCache bool UseAutoTime bool IsDistinct bool + IsForUpdate bool TableAlias string allUseBool bool checkVersion bool @@ -102,6 +103,7 @@ func (statement *Statement) Init() { statement.UseCache = true statement.UseAutoTime = true statement.IsDistinct = false + statement.IsForUpdate = false statement.TableAlias = "" statement.selectStr = "" statement.allUseBool = false @@ -802,6 +804,12 @@ func (statement *Statement) Distinct(columns ...string) *Statement { return statement } +// Generate "SELECT ... FOR UPDATE" statment +func (statement *Statement) ForUpdate() *Statement { + statement.IsForUpdate = true + return statement +} + // replace select func (s *Statement) Select(str string) *Statement { s.selectStr = str @@ -1274,6 +1282,9 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start) } } + if statement.IsForUpdate { + a += " FOR UPDATE" + } return } From dd30b1fa61fc1923e197bf44aad84ff1b93bb4c3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 28 Aug 2015 17:01:41 +0800 Subject: [PATCH 19/34] oracle insert primary key set back support --- session.go | 117 ++++++++++++++++++++++++++--------------------------- 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/session.go b/session.go index f0edb0ba..f10a1423 100644 --- a/session.go +++ b/session.go @@ -3155,64 +3155,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { // for postgres, many of them didn't implement lastInsertId, so we should // implemented it ourself. - - if session.Engine.DriverName() != core.POSTGRES || table.AutoIncrement == "" { - res, err := session.exec(sqlStr, args...) - if err != nil { - return 0, err - } else { - handleAfterInsertProcessorFunc(bean) - } - - if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { - session.cacheInsert(session.Statement.TableName()) - } - - if table.Version != "" && session.Statement.checkVersion { - verValue, err := table.VersionColumn().ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } else if verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(1) - } - } - - if table.AutoIncrement == "" { - return res.RowsAffected() - } - - var id int64 = 0 - id, err = res.LastInsertId() - if err != nil || id <= 0 { - return res.RowsAffected() - } - - aiValue, err := table.AutoIncrColumn().ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } - - if aiValue == nil || !aiValue.IsValid() /*|| aiValue.Int() != 0*/ || !aiValue.CanSet() { - return res.RowsAffected() - } - - var v interface{} = id - switch aiValue.Type().Kind() { - case reflect.Int32: - v = int32(id) - case reflect.Int: - v = int(id) - case reflect.Uint32: - v = uint32(id) - case reflect.Uint64: - v = uint64(id) - case reflect.Uint: - v = uint(id) - } - aiValue.Set(reflect.ValueOf(v)) - - return res.RowsAffected() - } else if session.Engine.DriverName() == core.ORACLE { + if session.Engine.DriverName() == core.ORACLE && len(table.AutoIncrement) > 0 { //assert table.AutoIncrement != "" res, err := session.query("select seq_atable.currval from dual", args...) @@ -3270,7 +3213,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { aiValue.Set(reflect.ValueOf(v)) return 1, nil - } else { + } else if session.Engine.DriverName() == core.POSTGRES && len(table.AutoIncrement) > 0 { //assert table.AutoIncrement != "" sqlStr = sqlStr + " RETURNING " + session.Engine.Quote(table.AutoIncrement) res, err := session.query(sqlStr, args...) @@ -3329,6 +3272,62 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { aiValue.Set(reflect.ValueOf(v)) return 1, nil + } else { + res, err := session.exec(sqlStr, args...) + if err != nil { + return 0, err + } else { + handleAfterInsertProcessorFunc(bean) + } + + if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { + session.cacheInsert(session.Statement.TableName()) + } + + if table.Version != "" && session.Statement.checkVersion { + verValue, err := table.VersionColumn().ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + } else if verValue.IsValid() && verValue.CanSet() { + verValue.SetInt(1) + } + } + + if table.AutoIncrement == "" { + return res.RowsAffected() + } + + var id int64 = 0 + id, err = res.LastInsertId() + if err != nil || id <= 0 { + return res.RowsAffected() + } + + aiValue, err := table.AutoIncrColumn().ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + } + + if aiValue == nil || !aiValue.IsValid() /*|| aiValue.Int() != 0*/ || !aiValue.CanSet() { + return res.RowsAffected() + } + + var v interface{} = id + switch aiValue.Type().Kind() { + case reflect.Int32: + v = int32(id) + case reflect.Int: + v = int(id) + case reflect.Uint32: + v = uint32(id) + case reflect.Uint64: + v = uint64(id) + case reflect.Uint: + v = uint(id) + } + aiValue.Set(reflect.ValueOf(v)) + + return res.RowsAffected() } } From 0a5845a61cf37b5863f405fc330435e44538c983 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 30 Aug 2015 09:48:16 +0800 Subject: [PATCH 20/34] bug fixed for innerInsert --- session.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/session.go b/session.go index f10a1423..0a365aba 100644 --- a/session.go +++ b/session.go @@ -3155,7 +3155,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { // for postgres, many of them didn't implement lastInsertId, so we should // implemented it ourself. - if session.Engine.DriverName() == core.ORACLE && len(table.AutoIncrement) > 0 { + if session.Engine.dialect.DBType() == core.ORACLE && len(table.AutoIncrement) > 0 { //assert table.AutoIncrement != "" res, err := session.query("select seq_atable.currval from dual", args...) @@ -3213,7 +3213,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { aiValue.Set(reflect.ValueOf(v)) return 1, nil - } else if session.Engine.DriverName() == core.POSTGRES && len(table.AutoIncrement) > 0 { + } else if session.Engine.dialect.DBType() == core.POSTGRES && len(table.AutoIncrement) > 0 { //assert table.AutoIncrement != "" sqlStr = sqlStr + " RETURNING " + session.Engine.Quote(table.AutoIncrement) res, err := session.query(sqlStr, args...) From 693501fd607555f0d45740f017a547cf450e3e2a Mon Sep 17 00:00:00 2001 From: evalphobia Date: Sun, 30 Aug 2015 20:07:18 +0900 Subject: [PATCH 21/34] Fixed FOR UPDATE for each dialects #290 --- mssql_dialect.go | 4 ++++ sqlite3_dialect.go | 4 ++++ statement.go | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mssql_dialect.go b/mssql_dialect.go index 0eef76d4..41989b91 100644 --- a/mssql_dialect.go +++ b/mssql_dialect.go @@ -509,6 +509,10 @@ func (db *mssql) CreateTableSql(table *core.Table, tableName, storeEngine, chars return sql } +func (db *mssql) ForUpdateSql(query string) string { + return query +} + func (db *mssql) Filters() []core.Filter { return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}} } diff --git a/sqlite3_dialect.go b/sqlite3_dialect.go index 94e7d6b3..80873dbd 100644 --- a/sqlite3_dialect.go +++ b/sqlite3_dialect.go @@ -250,6 +250,10 @@ func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string { return fmt.Sprintf("DROP INDEX %v", quote(idxName)) } +func (db *sqlite3) ForUpdateSql(query string) string { + return query +} + /*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) { args := []interface{}{tableName} sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))" diff --git a/statement.go b/statement.go index 063a6193..61d42dfe 100644 --- a/statement.go +++ b/statement.go @@ -1283,7 +1283,7 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { } } if statement.IsForUpdate { - a += " FOR UPDATE" + a = statement.Engine.dialect.ForUpdateSql(a) } return From 65f413ecf31cc472e03f82f03fb0ae4f9c4d2709 Mon Sep 17 00:00:00 2001 From: evalphobia Date: Sun, 30 Aug 2015 20:23:46 +0900 Subject: [PATCH 22/34] refactored genSelectSql --- statement.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/statement.go b/statement.go index 61d42dfe..6064c542 100644 --- a/statement.go +++ b/statement.go @@ -1192,6 +1192,7 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { distinct = "DISTINCT " } + var dialect core.Dialect = statement.Engine.Dialect() var top string var mssqlCondi string /*var orderBy string @@ -1203,7 +1204,7 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { if statement.WhereStr != "" { whereStr = fmt.Sprintf(" WHERE %v", statement.WhereStr) if statement.ConditionStr != "" { - whereStr = fmt.Sprintf("%v %s %v", whereStr, statement.Engine.Dialect().AndStr(), + whereStr = fmt.Sprintf("%v %s %v", whereStr, dialect.AndStr(), statement.ConditionStr) } } else if statement.ConditionStr != "" { @@ -1211,7 +1212,7 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { } var fromStr string = " FROM " + statement.Engine.Quote(statement.TableName()) if statement.TableAlias != "" { - if statement.Engine.dialect.DBType() == core.ORACLE { + if dialect.DBType() == core.ORACLE { fromStr += " " + statement.Engine.Quote(statement.TableAlias) } else { fromStr += " AS " + statement.Engine.Quote(statement.TableAlias) @@ -1221,7 +1222,7 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr) } - if statement.Engine.dialect.DBType() == core.MSSQL { + if dialect.DBType() == core.MSSQL { if statement.LimitN > 0 { top = fmt.Sprintf(" TOP %d ", statement.LimitN) } @@ -1271,19 +1272,19 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { if statement.OrderStr != "" { a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr) } - if statement.Engine.dialect.DBType() != core.MSSQL && statement.Engine.dialect.DBType() != core.ORACLE { + if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE { if statement.Start > 0 { a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start) } else if statement.LimitN > 0 { a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN) } - } else if statement.Engine.dialect.DBType() == core.ORACLE { + } else if dialect.DBType() == core.ORACLE { if statement.Start != 0 || statement.LimitN != 0 { a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start) } } if statement.IsForUpdate { - a = statement.Engine.dialect.ForUpdateSql(a) + a = dialect.ForUpdateSql(a) } return From ecad0c3ad63a882e0a86a67c9cb204d787fe2d73 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 31 Aug 2015 17:10:16 +0800 Subject: [PATCH 23/34] typo error --- memroy_store.go => memory_store.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename memroy_store.go => memory_store.go (100%) diff --git a/memroy_store.go b/memory_store.go similarity index 100% rename from memroy_store.go rename to memory_store.go From 495927eb9c4543c9aff0ad586e3e3225c57a5d71 Mon Sep 17 00:00:00 2001 From: Alexandre Viau Date: Sun, 6 Sep 2015 23:14:46 -0400 Subject: [PATCH 24/34] copyright: added owner --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 9c1d175d..84d2ae53 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013 - 2015 +Copyright (c) 2013 - 2015 The Xorm Authors All rights reserved. Redistribution and use in source and binary forms, with or without From 803f6db50c3ea88c459f024888e06f35d6130346 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 24 Sep 2015 22:49:14 +0800 Subject: [PATCH 25/34] improved readme --- README.md | 15 +++++++++++++-- README_CN.md | 20 +++++++++++++------- VERSION | 2 +- xorm.go | 2 +- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ddea8f92..b89f7666 100644 --- a/README.md +++ b/README.md @@ -35,18 +35,29 @@ Drivers for Go's sql package which currently support database/sql includes: * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) -* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - * Postgres: [github.com/lib/pq](https://github.com/lib/pq) +* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb) + +* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) + * MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) * MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) * Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) +* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (experiment) + # Changelog +* **v0.4.4** + * ql database expriment support + * tidb database expriment support + * sql.NullString and etc. field support + * select ForUpdate support + * many bugs fixed + * **v0.4.3** * Json column type support * oracle expirement support diff --git a/README_CN.md b/README_CN.md index 8ab58ea5..d3954382 100644 --- a/README_CN.md +++ b/README_CN.md @@ -36,28 +36,34 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) -* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - * Postgres: [github.com/lib/pq](https://github.com/lib/pq) +* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb) + +* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) + * MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) * MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) * Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持) +* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (试验性支持) + ## 更新日志 +* **v0.4.4** + * Tidb 数据库支持 + * QL 试验性支持 + * sql.NullString支持 + * ForUpdate 支持 + * bug修正 + * **v0.4.3** * Json 字段类型支持 * oracle实验性支持 * bug修正 -* **v0.4.2** - * 事物如未Rollback或Commit,在关闭时会自动Rollback - * Gonic 映射支持 - * bug修正 - [更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16) ## 安装 diff --git a/VERSION b/VERSION index 915f9513..3341336d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.3.0824 +xorm v0.4.4.0924 diff --git a/xorm.go b/xorm.go index 4f4d8938..791c1cbd 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.3.0824" + Version string = "0.4.4.0824" ) func regDrvsNDialects() bool { From 300379e01605e2be08dd53ecea7ca7d347a21a1d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 6 Oct 2015 22:56:28 +0800 Subject: [PATCH 26/34] bug fixed #297 --- VERSION | 2 +- session.go | 2 +- statement.go | 4 ++-- xorm.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 3341336d..23a53651 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.0924 +xorm v0.4.4.1006 diff --git a/session.go b/session.go index 419a5876..5f391bdc 100644 --- a/session.go +++ b/session.go @@ -3565,7 +3565,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 colNames, args = buildUpdates(session.Engine, table, bean, false, false, false, false, session.Statement.allUseBool, session.Statement.useAllCols, session.Statement.mustColumnMap, session.Statement.nullableMap, - session.Statement.columnMap, true) + session.Statement.columnMap, true, session.Statement.unscoped) } else { colNames, args, err = genCols(table, session, bean, true, true) if err != nil { diff --git a/statement.go b/statement.go index 6064c542..df6d9104 100644 --- a/statement.go +++ b/statement.go @@ -182,7 +182,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, useAllCols bool, mustColumnMap map[string]bool, nullableMap map[string]bool, - columnMap map[string]bool, update bool) ([]string, []interface{}) { + columnMap map[string]bool, update, unscoped bool) ([]string, []interface{}) { colNames := make([]string, 0) var args = make([]interface{}, 0) @@ -199,7 +199,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if !includeAutoIncr && col.IsAutoIncrement { continue } - if col.IsDeleted { + if col.IsDeleted && !unscoped { continue } if use, ok := columnMap[col.Name]; ok && !use { diff --git a/xorm.go b/xorm.go index 791c1cbd..3ea2feb2 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.0824" + Version string = "0.4.4.1006" ) func regDrvsNDialects() bool { From 6d9fe27a6df483be326b176f34f9148f9c3d7420 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 10 Oct 2015 10:32:08 +0800 Subject: [PATCH 27/34] disable gob register if cache is not enabled --- engine.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/engine.go b/engine.go index 4081ec14..bf2926f6 100644 --- a/engine.go +++ b/engine.go @@ -664,10 +664,12 @@ func (engine *Engine) autoMapType(v reflect.Value) *core.Table { if !ok { table = engine.mapType(v) engine.Tables[t] = table - if v.CanAddr() { - engine.GobRegister(v.Addr().Interface()) - } else { - engine.GobRegister(v.Interface()) + if engine.Cacher != nil { + if v.CanAddr() { + engine.GobRegister(v.Addr().Interface()) + } else { + engine.GobRegister(v.Interface()) + } } } engine.mutex.Unlock() From 45786683626b6944df52a4ff0d5d533555b6ed6a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 26 Oct 2015 14:51:04 +0800 Subject: [PATCH 28/34] bug fixed for extends insert --- VERSION | 2 +- session.go | 30 +++++++++++++++++++++--------- xorm.go | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index 23a53651..23fab9b3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1006 +xorm v0.4.4.1026 diff --git a/session.go b/session.go index 5f391bdc..1c67faca 100644 --- a/session.go +++ b/session.go @@ -2144,7 +2144,9 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error cols := make([]*core.Column, 0) for i := 0; i < size; i++ { - elemValue := sliceValue.Index(i).Interface() + v := sliceValue.Index(i) + vv := reflect.Indirect(v) + elemValue := v.Interface() colPlaces := make([]string, 0) // handle BeforeInsertProcessor @@ -2160,7 +2162,11 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error if i == 0 { for _, col := range table.Columns() { - fieldValue := reflect.Indirect(reflect.ValueOf(elemValue)).FieldByName(col.FieldName) + ptrFieldValue, err := col.ValueOfV(&vv) + if err != nil { + return 0, err + } + fieldValue := *ptrFieldValue if col.IsAutoIncrement && fieldValue.Int() == 0 { continue } @@ -2203,7 +2209,12 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error } } else { for _, col := range cols { - fieldValue := reflect.Indirect(reflect.ValueOf(elemValue)).FieldByName(col.FieldName) + ptrFieldValue, err := col.ValueOfV(&vv) + if err != nil { + return 0, err + } + fieldValue := *ptrFieldValue + if col.IsAutoIncrement && fieldValue.Int() == 0 { continue } @@ -2267,7 +2278,8 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error lenAfterClosures := len(session.afterClosures) for i := 0; i < size; i++ { - elemValue := sliceValue.Index(i).Interface() + elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface() + // handle AfterInsertProcessor if session.IsAutoCommit { // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi?? @@ -3012,11 +3024,11 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val fieldTable := session.Engine.autoMapType(fieldValue) //if fieldTable, ok := session.Engine.Tables[fieldValue.Type()]; ok { - if len(fieldTable.PrimaryKeys) == 1 { - pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) - return pkField.Interface(), nil - } - return 0, fmt.Errorf("no primary key for col %v", col.Name) + if len(fieldTable.PrimaryKeys) == 1 { + pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) + return pkField.Interface(), nil + } + return 0, fmt.Errorf("no primary key for col %v", col.Name) //} } diff --git a/xorm.go b/xorm.go index 3ea2feb2..e791565f 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1006" + Version string = "0.4.4.1026" ) func regDrvsNDialects() bool { From 1da71e850a838ab4b1320e7cde0dac6316cb3777 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 26 Oct 2015 16:06:38 +0800 Subject: [PATCH 29/34] resolved #309 --- session.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/session.go b/session.go index 1c67faca..e2b1d407 100644 --- a/session.go +++ b/session.go @@ -3332,10 +3332,14 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { var v interface{} = id switch aiValue.Type().Kind() { + case reflect.Int16: + v = int16(id) case reflect.Int32: v = int32(id) case reflect.Int: v = int(id) + case reflect.Uint16: + v = uint16(id) case reflect.Uint32: v = uint32(id) case reflect.Uint64: From 1d3b0d699e66105665a09bd4fb60c9e3f169833c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 26 Oct 2015 16:34:04 +0800 Subject: [PATCH 30/34] bug fixed: resolved #301 --- statement.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/statement.go b/statement.go index df6d9104..b05f2b04 100644 --- a/statement.go +++ b/statement.go @@ -1181,12 +1181,6 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{} } func (statement *Statement) genSelectSql(columnStr string) (a string) { - /*if statement.GroupByStr != "" { - if columnStr == "" { - columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) - } - //statement.GroupByStr = columnStr - }*/ var distinct string if statement.IsDistinct { distinct = "DISTINCT " @@ -1195,16 +1189,17 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { var dialect core.Dialect = statement.Engine.Dialect() var top string var mssqlCondi string - /*var orderBy string - if statement.OrderStr != "" { - orderBy = fmt.Sprintf(" ORDER BY %v", statement.OrderStr) - }*/ + statement.processIdParam() var whereStr string if statement.WhereStr != "" { - whereStr = fmt.Sprintf(" WHERE %v", statement.WhereStr) if statement.ConditionStr != "" { - whereStr = fmt.Sprintf("%v %s %v", whereStr, dialect.AndStr(), + whereStr = fmt.Sprintf(" WHERE (%v)", statement.WhereStr) + } else { + whereStr = fmt.Sprintf(" WHERE %v", statement.WhereStr) + } + if statement.ConditionStr != "" { + whereStr = fmt.Sprintf("%v %s (%v)", whereStr, dialect.AndStr(), statement.ConditionStr) } } else if statement.ConditionStr != "" { @@ -1284,7 +1279,7 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { } } if statement.IsForUpdate { - a = dialect.ForUpdateSql(a) + a = dialect.ForUpdateSql(a) } return From d776f5da3e5f3e356578d8cb36ca326820a1f59d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 26 Oct 2015 17:01:46 +0800 Subject: [PATCH 31/34] bug fixed: resolved #288 --- session.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/session.go b/session.go index e2b1d407..a0e17eb9 100644 --- a/session.go +++ b/session.go @@ -1203,8 +1203,8 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) table = session.Statement.RefTable } + var addedTableName = (len(session.Statement.JoinStr) > 0) if len(condiBean) > 0 { - var addedTableName = (len(session.Statement.JoinStr) > 0) colNames, args := buildConditions(session.Engine, table, condiBean[0], true, true, false, true, session.Statement.allUseBool, session.Statement.useAllCols, session.Statement.unscoped, session.Statement.mustColumnMap, @@ -1215,8 +1215,12 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) // !oinume! Add " IS NULL" to WHERE whatever condiBean is given. // See https://github.com/go-xorm/xorm/issues/179 if col := table.DeletedColumn(); col != nil && !session.Statement.unscoped { // tag "deleted" is enabled + var colName string = session.Engine.Quote(col.Name) + if addedTableName { + colName = session.Engine.Quote(session.Statement.TableName()) + "." + colName + } session.Statement.ConditionStr = fmt.Sprintf("(%v IS NULL or %v = '0001-01-01 00:00:00') ", - session.Engine.Quote(col.Name), session.Engine.Quote(col.Name)) + colName, colName) } } From 81d7f834b16c5b30dcbfc05452245f3038f50431 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 27 Oct 2015 14:45:48 +0800 Subject: [PATCH 32/34] small optimizations for statement string generation --- VERSION | 2 +- statement.go | 128 +++++++++++++++++++++++++-------------------------- xorm.go | 2 +- 3 files changed, 66 insertions(+), 66 deletions(-) diff --git a/VERSION b/VERSION index 23fab9b3..847eb09b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1026 +xorm v0.4.4.1027 diff --git a/statement.go b/statement.go index b05f2b04..1e6c067f 100644 --- a/statement.go +++ b/statement.go @@ -5,6 +5,7 @@ package xorm import ( + "bytes" "database/sql/driver" "encoding/json" "errors" @@ -143,9 +144,11 @@ func (statement *Statement) Where(querystring string, args ...interface{}) *Stat // add Where & and statment func (statement *Statement) And(querystring string, args ...interface{}) *Statement { - if statement.WhereStr != "" { - statement.WhereStr = fmt.Sprintf("(%v) %s (%v)", statement.WhereStr, + if len(statement.WhereStr) > 0 { + var buf bytes.Buffer + fmt.Fprintf(&buf, "(%v) %s (%v)", statement.WhereStr, statement.Engine.dialect.AndStr(), querystring) + statement.WhereStr = buf.String() } else { statement.WhereStr = querystring } @@ -155,9 +158,11 @@ func (statement *Statement) And(querystring string, args ...interface{}) *Statem // add Where & Or statment func (statement *Statement) Or(querystring string, args ...interface{}) *Statement { - if statement.WhereStr != "" { - statement.WhereStr = fmt.Sprintf("(%v) %s (%v)", statement.WhereStr, + if len(statement.WhereStr) > 0 { + var buf bytes.Buffer + fmt.Fprintf(&buf, "(%v) %s (%v)", statement.WhereStr, statement.Engine.dialect.OrStr(), querystring) + statement.WhereStr = buf.String() } else { statement.WhereStr = querystring } @@ -727,12 +732,17 @@ func (statement *Statement) genInSql() (string, []interface{}) { return "", []interface{}{} } - inStrs := make([]string, 0, len(statement.inColumns)) + inStrs := make([]string, len(statement.inColumns), len(statement.inColumns)) args := make([]interface{}, 0) + var buf bytes.Buffer + var i int for _, params := range statement.inColumns { - inStrs = append(inStrs, fmt.Sprintf("(%v IN (%v))", + buf.Reset() + fmt.Fprintf(&buf, "(%v IN (%v))", statement.Engine.autoQuote(params.colName), - strings.Join(makeArray("?", len(params.args)), ","))) + strings.Join(makeArray("?", len(params.args)), ",")) + inStrs[i] = buf.String() + i++ args = append(args, params.args...) } @@ -745,7 +755,7 @@ func (statement *Statement) genInSql() (string, []interface{}) { func (statement *Statement) attachInSql() { inSql, inArgs := statement.genInSql() if len(inSql) > 0 { - if statement.ConditionStr != "" { + if len(statement.ConditionStr) > 0 { statement.ConditionStr += " " + statement.Engine.dialect.AndStr() + " " } statement.ConditionStr += inSql @@ -845,15 +855,6 @@ func (statement *Statement) MustCols(columns ...string) *Statement { return statement } -// Update use only: not update columns -/*func (statement *Statement) NotCols(columns ...string) *Statement { - newColumns := col2NewCols(columns...) - for _, nc := range newColumns { - statement.mustColumnMap[strings.ToLower(nc)] = false - } - return statement -}*/ - // indicates that use bool fields as update contents and query contiditions func (statement *Statement) UseBool(columns ...string) *Statement { if len(columns) > 0 { @@ -898,7 +899,7 @@ func (statement *Statement) Limit(limit int, start ...int) *Statement { // Generate "Order By order" statement func (statement *Statement) OrderBy(order string) *Statement { - if statement.OrderStr != "" { + if len(statement.OrderStr) > 0 { statement.OrderStr += ", " } statement.OrderStr += order @@ -906,44 +907,51 @@ func (statement *Statement) OrderBy(order string) *Statement { } func (statement *Statement) Desc(colNames ...string) *Statement { - if statement.OrderStr != "" { - statement.OrderStr += ", " + var buf bytes.Buffer + fmt.Fprintf(&buf, statement.OrderStr) + if len(statement.OrderStr) > 0 { + fmt.Fprint(&buf, ", ") } newColNames := statement.col2NewColsWithQuote(colNames...) - sqlStr := strings.Join(newColNames, " DESC, ") - statement.OrderStr += sqlStr + " DESC" + fmt.Fprintf(&buf, "%q DESC", strings.Join(newColNames, " DESC, ")) + statement.OrderStr = buf.String() return statement } // Method Asc provide asc order by query condition, the input parameters are columns. func (statement *Statement) Asc(colNames ...string) *Statement { - if statement.OrderStr != "" { - statement.OrderStr += ", " + var buf bytes.Buffer + fmt.Fprintf(&buf, statement.OrderStr) + if len(statement.OrderStr) > 0 { + fmt.Fprint(&buf, ", ") } newColNames := statement.col2NewColsWithQuote(colNames...) - sqlStr := strings.Join(newColNames, " ASC, ") - statement.OrderStr += sqlStr + " ASC" + fmt.Fprintf(&buf, "%q ASC", strings.Join(newColNames, " ASC, ")) + statement.OrderStr = buf.String() return statement } //The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN func (statement *Statement) Join(join_operator string, tablename interface{}, condition string) *Statement { - var joinTable string + var buf bytes.Buffer + if len(statement.JoinStr) > 0 { + fmt.Fprintf(&buf, "%q %v JOIN ", statement.JoinStr, join_operator) + } else { + fmt.Fprintf(&buf, "%v JOIN ", join_operator) + } + switch tablename.(type) { case []string: t := tablename.([]string) - l := len(t) - if l > 1 { - table := t[0] - joinTable = statement.Engine.Quote(table) + " AS " + statement.Engine.Quote(t[1]) - } else if l == 1 { - table := t[0] - joinTable = statement.Engine.Quote(table) + if len(t) > 1 { + fmt.Fprintf(&buf, "%q AS %q", statement.Engine.Quote(t[0]), statement.Engine.Quote(t[1])) + } else if len(t) == 1 { + fmt.Fprintf(&buf, statement.Engine.Quote(t[0])) } case []interface{}: t := tablename.([]interface{}) l := len(t) - table := "" + var table string if l > 0 { f := t[0] v := rValue(f) @@ -956,21 +964,17 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co } } if l > 1 { - joinTable = statement.Engine.Quote(table) + " AS " + statement.Engine.Quote(fmt.Sprintf("%v", t[1])) + fmt.Fprintf(&buf, "%q AS %q", statement.Engine.Quote(table), + statement.Engine.Quote(fmt.Sprintf("%v", t[1]))) } else if l == 1 { - joinTable = statement.Engine.Quote(table) + fmt.Fprintf(&buf, statement.Engine.Quote(table)) } default: - t := fmt.Sprintf("%v", tablename) - joinTable = statement.Engine.Quote(t) - } - if statement.JoinStr != "" { - statement.JoinStr = statement.JoinStr + fmt.Sprintf(" %v JOIN %v ON %v", join_operator, - joinTable, condition) - } else { - statement.JoinStr = fmt.Sprintf("%v JOIN %v ON %v", join_operator, - joinTable, condition) + fmt.Fprintf(&buf, statement.Engine.Quote(fmt.Sprintf("%v", tablename))) } + + fmt.Fprintf(&buf, " ON %v", condition) + statement.JoinStr = buf.String() return statement } @@ -1087,11 +1091,6 @@ func (s *Statement) genDelIndexSQL() []string { return sqls } -/* -func (s *Statement) genDropSQL() string { - return s.Engine.dialect.MustDropTa(s.TableName()) + ";" -}*/ - func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) { var table *core.Table if statement.RefTable == nil { @@ -1191,20 +1190,22 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { var mssqlCondi string statement.processIdParam() - var whereStr string - if statement.WhereStr != "" { - if statement.ConditionStr != "" { - whereStr = fmt.Sprintf(" WHERE (%v)", statement.WhereStr) + + var buf bytes.Buffer + if len(statement.WhereStr) > 0 { + if len(statement.ConditionStr) > 0 { + fmt.Fprintf(&buf, " WHERE (%v)", statement.WhereStr) } else { - whereStr = fmt.Sprintf(" WHERE %v", statement.WhereStr) + fmt.Fprintf(&buf, " WHERE %v", statement.WhereStr) } if statement.ConditionStr != "" { - whereStr = fmt.Sprintf("%v %s (%v)", whereStr, dialect.AndStr(), - statement.ConditionStr) + fmt.Fprintf(&buf, " %s (%v)", dialect.AndStr(), statement.ConditionStr) } - } else if statement.ConditionStr != "" { - whereStr = fmt.Sprintf(" WHERE %v", statement.ConditionStr) + } else if len(statement.ConditionStr) > 0 { + fmt.Fprintf(&buf, " WHERE %v", statement.ConditionStr) } + var whereStr = buf.String() + var fromStr string = " FROM " + statement.Engine.Quote(statement.TableName()) if statement.TableAlias != "" { if dialect.DBType() == core.ORACLE { @@ -1248,10 +1249,9 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { } // !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern - a = fmt.Sprintf("SELECT %v%v%v%v%v", top, distinct, columnStr, - fromStr, whereStr) - if mssqlCondi != "" { - if whereStr != "" { + a = fmt.Sprintf("SELECT %v%v%v%v%v", top, distinct, columnStr, fromStr, whereStr) + if len(mssqlCondi) > 0 { + if len(whereStr) > 0 { a += " AND " + mssqlCondi } else { a += " WHERE " + mssqlCondi diff --git a/xorm.go b/xorm.go index e791565f..c372e2a9 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1026" + Version string = "0.4.4.1027" ) func regDrvsNDialects() bool { From a0e63df4070097e74c7968a87d084845664f920f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 27 Oct 2015 15:41:13 +0800 Subject: [PATCH 33/34] bug fixed --- statement.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/statement.go b/statement.go index 1e6c067f..47d09320 100644 --- a/statement.go +++ b/statement.go @@ -913,7 +913,7 @@ func (statement *Statement) Desc(colNames ...string) *Statement { fmt.Fprint(&buf, ", ") } newColNames := statement.col2NewColsWithQuote(colNames...) - fmt.Fprintf(&buf, "%q DESC", strings.Join(newColNames, " DESC, ")) + fmt.Fprintf(&buf, "%v DESC", strings.Join(newColNames, " DESC, ")) statement.OrderStr = buf.String() return statement } @@ -926,7 +926,7 @@ func (statement *Statement) Asc(colNames ...string) *Statement { fmt.Fprint(&buf, ", ") } newColNames := statement.col2NewColsWithQuote(colNames...) - fmt.Fprintf(&buf, "%q ASC", strings.Join(newColNames, " ASC, ")) + fmt.Fprintf(&buf, "%v ASC", strings.Join(newColNames, " ASC, ")) statement.OrderStr = buf.String() return statement } @@ -935,7 +935,7 @@ func (statement *Statement) Asc(colNames ...string) *Statement { func (statement *Statement) Join(join_operator string, tablename interface{}, condition string) *Statement { var buf bytes.Buffer if len(statement.JoinStr) > 0 { - fmt.Fprintf(&buf, "%q %v JOIN ", statement.JoinStr, join_operator) + fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, join_operator) } else { fmt.Fprintf(&buf, "%v JOIN ", join_operator) } @@ -944,7 +944,7 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co case []string: t := tablename.([]string) if len(t) > 1 { - fmt.Fprintf(&buf, "%q AS %q", statement.Engine.Quote(t[0]), statement.Engine.Quote(t[1])) + fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(t[0]), statement.Engine.Quote(t[1])) } else if len(t) == 1 { fmt.Fprintf(&buf, statement.Engine.Quote(t[0])) } @@ -964,7 +964,7 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co } } if l > 1 { - fmt.Fprintf(&buf, "%q AS %q", statement.Engine.Quote(table), + fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(table), statement.Engine.Quote(fmt.Sprintf("%v", t[1]))) } else if l == 1 { fmt.Fprintf(&buf, statement.Engine.Quote(table)) From 8bf440560e310e6093e9c4cb40ddf2a8216725e4 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 29 Oct 2015 13:30:22 +0800 Subject: [PATCH 34/34] bug fixed for update which includes json struct fields --- VERSION | 2 +- statement.go | 36 ++++++++++++++++++++++++------------ xorm.go | 2 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/VERSION b/VERSION index 847eb09b..78f657d6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1027 +xorm v0.4.4.1029 diff --git a/statement.go b/statement.go index 47d09320..627c0bd8 100644 --- a/statement.go +++ b/statement.go @@ -332,23 +332,35 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, } else if nulType, ok := fieldValue.Interface().(driver.Valuer); ok { val, _ = nulType.Value() } else { - engine.autoMapType(fieldValue) - if table, ok := engine.Tables[fieldValue.Type()]; ok { - 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.IsValid() && !isZero(pkField.Interface()) { - val = pkField.Interface() + if !col.SQLType.IsJson() { + engine.autoMapType(fieldValue) + if table, ok := engine.Tables[fieldValue.Type()]; ok { + 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.IsValid() && !isZero(pkField.Interface()) { + val = pkField.Interface() + } else { + continue + } } else { - continue + //TODO: how to handler? + panic("not supported") } } else { - //TODO: how to handler? - panic("not supported") + val = fieldValue.Interface() } } else { - val = fieldValue.Interface() + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface())) + } + if col.SQLType.IsText() { + val = string(bytes) + } else if col.SQLType.IsBlob() { + val = bytes + } } } case reflect.Array, reflect.Slice, reflect.Map: diff --git a/xorm.go b/xorm.go index c372e2a9..a03e6cc1 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1027" + Version string = "0.4.4.1029" ) func regDrvsNDialects() bool {