From a3e2e73b6597ec6b2a35d50fb3a6afc02a542146 Mon Sep 17 00:00:00 2001 From: TossPig Date: Wed, 2 Sep 2015 04:16:03 +0800 Subject: [PATCH 01/18] PG connection URIs http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING --- pq_driver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pq_driver.go b/pq_driver.go index d86c97bb..f665bed5 100644 --- a/pq_driver.go +++ b/pq_driver.go @@ -41,7 +41,7 @@ func parseURL(connstr string) (string, error) { return "", err } - if u.Scheme != "postgres" { + if u.Scheme != "postgresql" { return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) } @@ -103,7 +103,7 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { db := &core.Uri{DbType: core.POSTGRES} o := make(values) var err error - if strings.HasPrefix(dataSourceName, "postgres://") { + if strings.HasPrefix(dataSourceName, "postgresql://") { dataSourceName, err = parseURL(dataSourceName) if err != nil { return nil, err From b9ba2ed347f6de23f68bc4065e9a8b60a6424eb6 Mon Sep 17 00:00:00 2001 From: evalphobia Date: Tue, 17 Nov 2015 16:19:23 +0900 Subject: [PATCH 02/18] Modified time.IsZero() to use interface --- helpers.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/helpers.go b/helpers.go index 7e8978f0..c83391ff 100644 --- a/helpers.go +++ b/helpers.go @@ -15,6 +15,10 @@ import ( "github.com/go-xorm/core" ) +type zeroable interface { + IsZero() bool +} + func isZero(k interface{}) bool { switch k.(type) { case int: @@ -45,8 +49,8 @@ func isZero(k interface{}) bool { return k.(bool) == false case string: return k.(string) == "" - case time.Time: - return k.(time.Time).IsZero() + case zeroable: + return k.(zeroable).IsZero() } return false } From 4d5da3bb4d344fc46b0ebcbf13f652a2429a0649 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Nov 2015 14:25:06 +0800 Subject: [PATCH 03/18] bug fixed for dump --- VERSION | 2 +- engine.go | 4 ++-- xorm.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 78f657d6..77c58d3b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1029 +xorm v0.4.4.1124 diff --git a/engine.go b/engine.go index bf2926f6..35b26618 100644 --- a/engine.go +++ b/engine.go @@ -378,12 +378,12 @@ func (engine *Engine) DumpAll(w io.Writer) error { } for _, table := range tables { - _, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+"\n\n") + _, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n\n") if err != nil { return err } for _, index := range table.Indexes { - _, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+"\n\n") + _, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+";\n\n") if err != nil { return err } diff --git a/xorm.go b/xorm.go index a03e6cc1..a97c62f7 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1029" + Version string = "0.4.4.1124" ) func regDrvsNDialects() bool { From 0cae9529c11ebf288edbe98c329c4e559afdcb87 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Nov 2015 15:13:53 +0800 Subject: [PATCH 04/18] beautify for dump sql --- engine.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/engine.go b/engine.go index 35b26618..676101a3 100644 --- a/engine.go +++ b/engine.go @@ -377,13 +377,25 @@ func (engine *Engine) DumpAll(w io.Writer) error { return err } - for _, table := range tables { - _, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n\n") + _, err = io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s*/\n\n", + Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05"))) + if err != nil { + return err + } + + for i, table := range tables { + if i > 0 { + _, err = io.WriteString(w, "\n") + if err != nil { + return err + } + } + _, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n") if err != nil { return err } for _, index := range table.Indexes { - _, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+";\n\n") + _, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+";\n") if err != nil { return err } @@ -443,7 +455,7 @@ func (engine *Engine) DumpAll(w io.Writer) error { } } } - _, err = io.WriteString(w, temp[2:]+");\n\n") + _, err = io.WriteString(w, temp[2:]+");\n") if err != nil { return err } From b36f8ed87eeddf8c1b4dbf1b3edcf6c6f905fa45 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 26 Nov 2015 15:52:25 +0800 Subject: [PATCH 05/18] bug fixed for #249 --- VERSION | 2 +- helpers.go | 21 +++++++++++++++++++++ session.go | 50 +++++--------------------------------------------- xorm.go | 2 +- 4 files changed, 28 insertions(+), 47 deletions(-) diff --git a/VERSION b/VERSION index 77c58d3b..8b43e1ae 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1124 +xorm v0.4.4.1126 diff --git a/helpers.go b/helpers.go index c83391ff..1ac6ad62 100644 --- a/helpers.go +++ b/helpers.go @@ -55,6 +55,27 @@ func isZero(k interface{}) bool { return false } +func int64ToInt(id int64, k reflect.Kind) interface{} { + var v interface{} = id + switch k { + 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: + v = uint64(id) + case reflect.Uint: + v = uint(id) + } + return v +} + func isPKZero(pk core.PK) bool { for _, k := range pk { if isZero(k) { diff --git a/session.go b/session.go index a0e17eb9..0dfab59a 100644 --- a/session.go +++ b/session.go @@ -2171,7 +2171,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error return 0, err } fieldValue := *ptrFieldValue - if col.IsAutoIncrement && fieldValue.Int() == 0 { + if col.IsAutoIncrement && isZero(fieldValue.Interface()) { continue } if col.MapType == core.ONLYFROMDB { @@ -2219,7 +2219,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error } fieldValue := *ptrFieldValue - if col.IsAutoIncrement && fieldValue.Int() == 0 { + if col.IsAutoIncrement && isZero(fieldValue.Interface()) { continue } if col.MapType == core.ONLYFROMDB { @@ -3219,19 +3219,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { 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) - } + v := int64ToInt(id, aiValue.Type().Kind()) aiValue.Set(reflect.ValueOf(v)) return 1, nil @@ -3278,19 +3266,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { 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) - } + v := int64ToInt(id, aiValue.Type().Kind()) aiValue.Set(reflect.ValueOf(v)) return 1, nil @@ -3334,23 +3310,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { return res.RowsAffected() } - 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: - v = uint64(id) - case reflect.Uint: - v = uint(id) - } + v := int64ToInt(id, aiValue.Type().Kind()) aiValue.Set(reflect.ValueOf(v)) return res.RowsAffected() diff --git a/xorm.go b/xorm.go index a97c62f7..88d23b81 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1124" + Version string = "0.4.4.1126" ) func regDrvsNDialects() bool { From 1aa62881bb4301b039391afc326744fdeac23497 Mon Sep 17 00:00:00 2001 From: TossPig Date: Thu, 26 Nov 2015 16:47:40 +0800 Subject: [PATCH 06/18] CONNSTRING MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 同时兼容 postgresql和postgres --- pq_driver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pq_driver.go b/pq_driver.go index f665bed5..a4e26975 100644 --- a/pq_driver.go +++ b/pq_driver.go @@ -41,7 +41,7 @@ func parseURL(connstr string) (string, error) { return "", err } - if u.Scheme != "postgresql" { + if u.Scheme != "postgresql" && u.Scheme != "postgres" { return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) } @@ -103,7 +103,7 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { db := &core.Uri{DbType: core.POSTGRES} o := make(values) var err error - if strings.HasPrefix(dataSourceName, "postgresql://") { + if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") { dataSourceName, err = parseURL(dataSourceName) if err != nil { return nil, err From 1c77c0aa097b52b61b928ac372b382e4ff06603c Mon Sep 17 00:00:00 2001 From: TossPig Date: Thu, 26 Nov 2015 16:49:12 +0800 Subject: [PATCH 07/18] fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复一个判断错误 --- pq_driver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pq_driver.go b/pq_driver.go index a4e26975..98fd62d8 100644 --- a/pq_driver.go +++ b/pq_driver.go @@ -41,7 +41,7 @@ func parseURL(connstr string) (string, error) { return "", err } - if u.Scheme != "postgresql" && u.Scheme != "postgres" { + if u.Scheme != "postgresql" || u.Scheme != "postgres" { return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) } From 4153efc97ee62f7f6a7ec78ae487a7b96f8594b9 Mon Sep 17 00:00:00 2001 From: TossPig Date: Thu, 26 Nov 2015 19:04:38 +0800 Subject: [PATCH 08/18] Update pq_driver.go fix --- pq_driver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pq_driver.go b/pq_driver.go index 98fd62d8..a4e26975 100644 --- a/pq_driver.go +++ b/pq_driver.go @@ -41,7 +41,7 @@ func parseURL(connstr string) (string, error) { return "", err } - if u.Scheme != "postgresql" || u.Scheme != "postgres" { + if u.Scheme != "postgresql" && u.Scheme != "postgres" { return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) } From aac04b323fd2465c07cce3e2feb913e18c736d7a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 2 Dec 2015 14:54:58 +0800 Subject: [PATCH 09/18] bug fixed for update empty slice, map --- statement.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/statement.go b/statement.go index 627c0bd8..2a22a2c4 100644 --- a/statement.go +++ b/statement.go @@ -182,7 +182,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { return statement } -// Auto generating conditions according a struct +// Auto generating update columnes and values according a struct func buildUpdates(engine *Engine, table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, useAllCols bool, @@ -211,10 +211,6 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, continue } - if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text { - continue - } - fieldValuePtr, err := col.ValueOf(bean) if err != nil { engine.LogError(err) @@ -338,7 +334,6 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, 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 { @@ -364,11 +359,13 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, } } case reflect.Array, reflect.Slice, reflect.Map: - if fieldValue == reflect.Zero(fieldType) { - continue - } - if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { - continue + if !requiredField { + if fieldValue == reflect.Zero(fieldType) { + continue + } + if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { + continue + } } if col.SQLType.IsText() { From a8fba4d3d9f91edaf3d7cd51bbcb893e5f6ba7e8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 3 Dec 2015 16:11:27 +0800 Subject: [PATCH 10/18] resolved #312 --- VERSION | 2 +- session.go | 17 +++++++++++------ xorm.go | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index 8b43e1ae..b8e352cf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1126 +xorm v0.4.4.1203 diff --git a/session.go b/session.go index 0dfab59a..07d3af1e 100644 --- a/session.go +++ b/session.go @@ -3537,7 +3537,9 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 // -- var err error - if t.Kind() == reflect.Struct { + var isMap = t.Kind() == reflect.Map + var isStruct = t.Kind() == reflect.Struct + if isStruct { table = session.Engine.TableInfo(bean) session.Statement.RefTable = table @@ -3552,7 +3554,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return 0, err } } - } else if t.Kind() == reflect.Map { + } else if isMap { if session.Statement.RefTable == nil { return 0, ErrTableNotFound } @@ -3576,10 +3578,12 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 args = append(args, val) var colName = col.Name - session.afterClosures = append(session.afterClosures, func(bean interface{}) { - col := table.GetColumn(colName) - setColumnTime(bean, col, t) - }) + if isStruct { + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } } //for update action to like "column = column + ?" @@ -3723,6 +3727,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } else { afterClosures := make([]func(interface{}), lenAfterClosures) copy(afterClosures, session.afterClosures) + // FIXME: if bean is a map type, it will panic because map cannot be as map key session.afterUpdateBeans[bean] = &afterClosures } diff --git a/xorm.go b/xorm.go index 88d23b81..71bfbfd1 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1126" + Version string = "0.4.4.1203" ) func regDrvsNDialects() bool { From d5819023e79a62eb3623a854e762b35e860764a2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 25 Dec 2015 10:35:48 +0800 Subject: [PATCH 11/18] added varchar size increment auto sync for mysql in Sync2 --- session.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/session.go b/session.go index 07d3af1e..9d00b7e7 100644 --- a/session.go +++ b/session.go @@ -4011,10 +4011,26 @@ func (s *Session) Sync2(beans ...interface{}) error { engine.LogWarnf("Table %s column %s db type is %s, struct type is %s\n", table.Name, col.Name, curType, expectedType) } + } else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) { + if engine.dialect.DBType() == core.MYSQL { + if oriCol.Length < col.Length { + engine.LogInfof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", + table.Name, col.Name, oriCol.Length, col.Length) + _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col)) + } + } } else { engine.LogWarnf("Table %s column %s db type is %s, struct type is %s", table.Name, col.Name, curType, expectedType) } + } else if expectedType == core.Varchar { + if engine.dialect.DBType() == core.MYSQL { + if oriCol.Length < col.Length { + engine.LogInfof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", + table.Name, col.Name, oriCol.Length, col.Length) + _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col)) + } + } } if col.Default != oriCol.Default { engine.LogWarnf("Table %s Column %s db default is %s, struct default is %s", From b85dbfe3ff815c1e583ea91d9f38115b055832a5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 29 Dec 2015 18:16:27 +0800 Subject: [PATCH 12/18] resolved #326 --- VERSION | 2 +- session.go | 78 ++++++++++++++++++++++++++++-------------------------- xorm.go | 2 +- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/VERSION b/VERSION index b8e352cf..348ae384 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1203 +xorm v0.4.4.1225 diff --git a/session.go b/session.go index 9d00b7e7..12e43844 100644 --- a/session.go +++ b/session.go @@ -44,6 +44,7 @@ type Session struct { beforeClosures []func(interface{}) afterClosures []func(interface{}) + prepareStmt bool stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) cascadeDeep int @@ -60,6 +61,7 @@ func (session *Session) Init() { session.IsCommitedOrRollbacked = false session.IsAutoClose = false session.AutoResetStatement = true + session.prepareStmt = false // !nashtsai! is lazy init better? session.afterInsertBeans = make(map[interface{}]*[]func(interface{}), 0) @@ -97,6 +99,12 @@ func (session *Session) resetStatement() { } } +// Prepare +func (session *Session) Prepare() *Session { + session.prepareStmt = true + return session +} + // Method 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(querystring string, args ...interface{}) *Session { @@ -459,17 +467,20 @@ func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]b //Execute sql func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Result, error) { - stmt, err := session.doPrepare(sqlStr) - if err != nil { - return nil, err - } - //defer stmt.Close() + if session.prepareStmt { + stmt, err := session.doPrepare(sqlStr) + if err != nil { + return nil, err + } - res, err := stmt.Exec(args...) - if err != nil { - return nil, err + res, err := stmt.Exec(args...) + if err != nil { + return nil, err + } + return res, nil } - return res, nil + + return session.DB().Exec(sqlStr, args...) } func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) { @@ -1041,12 +1052,16 @@ func (session *Session) Get(bean interface{}) (bool, error) { var err error session.queryPreprocess(&sqlStr, args...) if session.IsAutoCommit { - stmt, errPrepare := session.doPrepare(sqlStr) - if errPrepare != nil { - return false, errPrepare + if session.prepareStmt { + stmt, errPrepare := session.doPrepare(sqlStr) + if errPrepare != nil { + return false, errPrepare + } + // defer stmt.Close() // !nashtsai! don't close due to stmt is cached and bounded to this session + rawRows, err = stmt.Query(args...) + } else { + rawRows, err = session.DB().Query(sqlStr, args...) } - // defer stmt.Close() // !nashtsai! don't close due to stmt is cached and bounded to this session - rawRows, err = stmt.Query(args...) } else { rawRows, err = session.Tx.Query(sqlStr, args...) } @@ -1286,11 +1301,15 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) session.queryPreprocess(&sqlStr, args...) if session.IsAutoCommit { - stmt, err = session.doPrepare(sqlStr) - if err != nil { - return err + if session.prepareStmt { + stmt, err = session.doPrepare(sqlStr) + if err != nil { + return err + } + rawRows, err = stmt.Query(args...) + } else { + rawRows, err = session.DB().Query(sqlStr, args...) } - rawRows, err = stmt.Query(args...) } else { rawRows, err = session.Tx.Query(sqlStr, args...) } @@ -1391,20 +1410,6 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) return nil } -// func (session *Session) queryRows(rawStmt **sql.Stmt, rawRows **sql.Rows, sqlStr string, args ...interface{}) error { -// var err error -// if session.IsAutoCommit { -// *rawStmt, err = session.doPrepare(sqlStr) -// if err != nil { -// return err -// } -// *rawRows, err = (*rawStmt).Query(args...) -// } else { -// *rawRows, err = session.Tx.Query(sqlStr, args...) -// } -// return err -// } - // Test if database is ok func (session *Session) Ping() error { defer session.resetStatement() @@ -1608,7 +1613,6 @@ type Cell *interface{} func (session *Session) rows2Beans(rows *core.Rows, fields []string, fieldsCount int, table *core.Table, newElemFunc func() reflect.Value, sliceValueSetFunc func(*reflect.Value)) error { - for rows.Next() { var newValue reflect.Value = newElemFunc() bean := newValue.Interface() @@ -1618,7 +1622,6 @@ func (session *Session) rows2Beans(rows *core.Rows, fields []string, fieldsCount return err } sliceValueSetFunc(&newValue) - } return nil } @@ -1854,8 +1857,10 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount pk[0] = uint8(vv.Uint()) case reflect.String: pk[0] = vv.String() + case reflect.Slice: + pk[0], _ = strconv.ParseInt(string(rawValue.Interface().([]byte)), 10, 64) default: - panic("unsupported primary key type cascade") + panic(fmt.Sprintf("unsupported primary key type: %v, %v", rawValueType, fieldValue)) } if !isPKZero(pk) { @@ -3027,13 +3032,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 col.SQLType.IsText() { @@ -3051,7 +3054,6 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val } return bytes, nil } - return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) case reflect.Complex64, reflect.Complex128: bytes, err := json.Marshal(fieldValue.Interface()) diff --git a/xorm.go b/xorm.go index 71bfbfd1..d612d0b1 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1203" + Version string = "0.4.4.1229" ) func regDrvsNDialects() bool { From 5ca36c5476a7eb32582320a95835cb6a9ae4d671 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 30 Dec 2015 16:17:33 +0800 Subject: [PATCH 13/18] resolved #326 --- session.go | 70 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/session.go b/session.go index 12e43844..bf88b83b 100644 --- a/session.go +++ b/session.go @@ -1795,8 +1795,22 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount rawValueType == core.Int32Type { hasAssigned = true t := time.Unix(vv.Int(), 0).In(session.Engine.TZLocation) - vv = reflect.ValueOf(t) - fieldValue.Set(vv) + //vv = reflect.ValueOf(t) + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } else { + if d, ok := vv.Interface().([]uint8); ok { + hasAssigned = true + t, err := session.byte2Time(col, d) + fmt.Println(string(d), t, err) + if err != nil { + session.Engine.LogError("byte2Time error:", err.Error()) + hasAssigned = false + } else { + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } + } else { + panic(fmt.Sprintf("rawValueType is %v, value is %v", rawValueType, vv.Interface())) + } } } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { // !! 增加支持sql.Scanner接口的结构,如sql.NullString @@ -2031,7 +2045,7 @@ func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSl session.queryPreprocess(&sqlStr, paramStr...) if session.IsAutoCommit { - return session.innerQuery(session.DB(), sqlStr, paramStr...) + return session.innerQuery(sqlStr, paramStr...) } return session.txQuery(session.Tx, sqlStr, paramStr...) } @@ -2046,22 +2060,33 @@ func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{ return rows2maps(rows) } -func (session *Session) innerQuery(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { - stmt, rows, err := session.Engine.LogSQLQueryTime(sqlStr, params, func() (*core.Stmt, *core.Rows, error) { - stmt, err := db.Prepare(sqlStr) - if err != nil { - return stmt, nil, err +func (session *Session) innerQuery(sqlStr string, params ...interface{}) ([]map[string][]byte, error) { + var callback func() (*core.Stmt, *core.Rows, error) + if session.prepareStmt { + callback = func() (*core.Stmt, *core.Rows, error) { + stmt, err := session.doPrepare(sqlStr) + if err != nil { + return nil, nil, err + } + rows, err := stmt.Query(params...) + if err != nil { + return nil, nil, err + } + return stmt, rows, nil } - rows, err := stmt.Query(params...) - - return stmt, rows, err - }) + } else { + callback = func() (*core.Stmt, *core.Rows, error) { + rows, err := session.DB().Query(sqlStr, params...) + if err != nil { + return nil, nil, err + } + return nil, rows, err + } + } + _, rows, err := session.Engine.LogSQLQueryTime(sqlStr, params, callback) if rows != nil { defer rows.Close() } - if stmt != nil { - defer stmt.Close() - } if err != nil { return nil, err } @@ -2347,17 +2372,18 @@ func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.T // time stamp sd, err := strconv.ParseInt(sdata, 10, 64) if err == nil { - x = time.Unix(0, sd) + x = time.Unix(sd, 0) // !nashtsai! HACK mymysql driver is casuing Local location being change to CHAT and cause wrong time conversion - x = x.In(time.UTC) - x = time.Date(x.Year(), x.Month(), x.Day(), x.Hour(), - x.Minute(), x.Second(), x.Nanosecond(), session.Engine.TZLocation) + //fmt.Println(x.In(session.Engine.TZLocation), "===") + x = x.In(session.Engine.TZLocation) + //fmt.Println(x, "=====") + /*x = time.Date(x.Year(), x.Month(), x.Day(), x.Hour(), + x.Minute(), x.Second(), x.Nanosecond(), session.Engine.TZLocation)*/ session.Engine.LogDebugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) } else { session.Engine.LogDebugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) } - } else if len(sdata) > 19 { - + } else if len(sdata) > 19 && strings.Contains(sdata, "-") { x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation) session.Engine.LogDebugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) if err != nil { @@ -2369,7 +2395,7 @@ func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.T session.Engine.LogDebugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) } - } else if len(sdata) == 19 { + } else if len(sdata) == 19 && strings.Contains(sdata, "-") { x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation) session.Engine.LogDebugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) } else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' { From 00ee02e2bb870f6162b57f7f392d38ec56f58491 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 30 Dec 2015 16:49:46 +0800 Subject: [PATCH 14/18] udpated readme --- README.md | 146 ++++++++++++++++++++++++++++++++++++++++++++++++- README_CN.md | 151 +++++++++++++++++++++++++++++++++++++++++++++++++-- VERSION | 2 +- 3 files changed, 291 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b89f7666..7d237d60 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Xorm is a simple and powerful ORM for Go. * Struct <-> Table Mapping Support * Chainable APIs - + * Transaction Support * Both ORM and raw SQL operation Support @@ -72,7 +72,7 @@ Drivers for Go's sql package which currently support database/sql includes: # Installation -If you have [gopm](https://github.com/gpmgo/gopm) installed, +If you have [gopm](https://github.com/gpmgo/gopm) installed, gopm get github.com/go-xorm/xorm @@ -88,6 +88,148 @@ Or * [GoWalker](http://gowalker.org/github.com/go-xorm/xorm) +# Quick Start + +## Create Engine + +```Go +engine, err := xorm.NewEngine(driverName, dataSourceName) +``` + +## Define a struct and Sync2 table struct to database + +```Go +type User struct { + Id int64 + Name string + Salt string + Age int + Passwd string `xorm:"varchar(200)"` + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` +} + +err := engine.Sync2(new(User)) +``` + +## Query a SQL string, the returned results is []map[string][]byte + +```Go +results, err := engine.Query("select * from user") +``` + +## Execute a SQL string, the returned results + +```Go +affected, err := engine.Exec("update user set age = ? where name = ?", age, name) +``` + +## Insert one or multipe records to database + +```Go +affected, err := engine.Insert(&user) +// INSERT INTO struct () values () +affected, err := engine.Insert(&user1, &user2) +// INSERT INTO struct1 () values () +// INSERT INTO struct2 () values () +affected, err := engine.Insert(&users) +// INSERT INTO struct () values (),(),() +affected, err := engine.Insert(&user1, &users) +// INSERT INTO struct1 () values () +// INSERT INTO struct2 () values (),(),() +``` + +## Query one record from database + +```Go +has, err := engine.Get(&user) +// SELECT * FROM user LIMIT 1 +has, err := engine.Where("name = ?", name).Desc("id").Get(&user) +// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 +``` + +## Query multiple records from database, also you can use join and extends + +```Go + var users []User + err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users) + // SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10 + + type Detail struct { + Id int64 + UserId int64 `xorm:"index"` + } + + type UserDetail struct { + User `xorm:"extends"` + Detail `xorm:"extends"` + } + + var users []UserDetail + err := engine.Table("user").Select("user.*, detail.*") + Join("INNER", "detail", "detail.user_id = user.id"). + Where("user.name = ?", name).Limit(10, 0). + Find(&users) + // SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 +``` + +## Query multiple records and record by record handle, there two methods Iterate and Rows + +```Go +err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error { + user := bean.(*User) + return nil +}) +// SELECT * FROM user + +rows, err := engine.Rows(&User{Name:name}) +// SELECT * FROM user +defer rows.Close() +bean := new(Struct) +for rows.Next() { + err = rows.Scan(bean) +} +``` + +## Update one or more records, default will update non-empty and non-zero fields except to use Cols, AllCols and etc. + +```Go +affected, err := engine.Id(1).Update(&user) +// UPDATE user SET ... Where id = ? + +affected, err := engine.Update(&user, &User{Name:name}) +// UPDATE user SET ... Where name = ? + +var ids = []int64{1, 2, 3} +affected, err := engine.In(ids).Update(&user) +// UPDATE user SET ... Where id IN (?, ?, ?) + +// force update indicated columns by Cols +affected, err := engine.Id(1).Cols("age").Update(&User{Name:name, Age: 12}) +// UPDATE user SET age = ?, updated=? Where id = ? + +// force NOT update indicated columns by Omit +affected, err := engine.Id(1).Omit("name").Update(&User{Name:name, Age: 12}) +// UPDATE user SET age = ?, updated=? Where id = ? + +affected, err := engine.Id(1).AllCols().Update(&user) +// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ? +``` + +## Delete one or more records, Delete MUST has conditon + +```Go +affected, err := engine.Where(...).Delete(&user) +// DELETE FROM user Where ... +``` + +## Count records + +```Go +counts, err := engine.Count(&user) +// SELECT count(*) AS total FROM user +``` + # Cases * [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader) diff --git a/README_CN.md b/README_CN.md index d3954382..df2b75b8 100644 --- a/README_CN.md +++ b/README_CN.md @@ -20,7 +20,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件 -* 支持级联加载Struct +* 支持级联加载Struct * 支持缓存 @@ -58,7 +58,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * sql.NullString支持 * ForUpdate 支持 * bug修正 - + * **v0.4.3** * Json 字段类型支持 * oracle实验性支持 @@ -68,10 +68,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 ## 安装 -推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: +推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: gopm get github.com/go-xorm/xorm - + 或者您也可以使用go工具进行安装: go get github.com/go-xorm/xorm @@ -84,8 +84,149 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm) +# 快速开始 -## 案例 +## 第一步创建引擎,driverName, dataSourceName和database/sql接口相同 + +```Go +engine, err := xorm.NewEngine(driverName, dataSourceName) +``` + +## 定义一个和表同步的结构体,并且自动同步结构体到数据库 + +```Go +type User struct { + Id int64 + Name string + Salt string + Age int + Passwd string `xorm:"varchar(200)"` + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` +} + +err := engine.Sync2(new(User)) +``` + +## 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte + +```Go +results, err := engine.Query("select * from user") +``` + +## 执行一个SQL语句 + +```Go +affected, err := engine.Exec("update user set age = ? where name = ?", age, name) +``` + +## 插入一条或者多条记录 + +```Go +affected, err := engine.Insert(&user) +// INSERT INTO struct () values () +affected, err := engine.Insert(&user1, &user2) +// INSERT INTO struct1 () values () +// INSERT INTO struct2 () values () +affected, err := engine.Insert(&users) +// INSERT INTO struct () values (),(),() +affected, err := engine.Insert(&user1, &users) +// INSERT INTO struct1 () values () +// INSERT INTO struct2 () values (),(),() +``` + +## 查询单条记录 + +```Go +has, err := engine.Get(&user) +// SELECT * FROM user LIMIT 1 +has, err := engine.Where("name = ?", name).Desc("id").Get(&user) +// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 +``` + +## 查询多条记录,当然可以使用Join和extends来组合使用 + +```Go + var users []User + err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users) + // SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10 + + type Detail struct { + Id int64 + UserId int64 `xorm:"index"` + } + + type UserDetail struct { + User `xorm:"extends"` + Detail `xorm:"extends"` + } + + var users []UserDetail + err := engine.Table("user").Select("user.*, detail.*") + Join("INNER", "detail", "detail.user_id = user.id"). + Where("user.name = ?", name).Limit(10, 0). + Find(&users) + // SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 +``` + +## 根据条件遍历数据库,可以有两种方式: Iterate and Rows + +```Go +err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error { + user := bean.(*User) + return nil +}) +// SELECT * FROM user + +rows, err := engine.Rows(&User{Name:name}) +// SELECT * FROM user +defer rows.Close() +bean := new(Struct) +for rows.Next() { + err = rows.Scan(bean) +} +``` + +## 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段 + +```Go +affected, err := engine.Id(1).Update(&user) +// UPDATE user SET ... Where id = ? + +affected, err := engine.Update(&user, &User{Name:name}) +// UPDATE user SET ... Where name = ? + +var ids = []int64{1, 2, 3} +affected, err := engine.In(ids).Update(&user) +// UPDATE user SET ... Where id IN (?, ?, ?) + +// force update indicated columns by Cols +affected, err := engine.Id(1).Cols("age").Update(&User{Name:name, Age: 12}) +// UPDATE user SET age = ?, updated=? Where id = ? + +// force NOT update indicated columns by Omit +affected, err := engine.Id(1).Omit("name").Update(&User{Name:name, Age: 12}) +// UPDATE user SET age = ?, updated=? Where id = ? + +affected, err := engine.Id(1).AllCols().Update(&user) +// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ? +``` + +## 删除记录,需要注意,删除必须至少有一个条件,否则会报错。要清空数据库可以用EmptyTable + +```Go +affected, err := engine.Where(...).Delete(&user) +// DELETE FROM user Where ... +``` + +## 获取记录条数 + +```Go +counts, err := engine.Count(&user) +// SELECT count(*) AS total FROM user +``` + +# 案例 * [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader) diff --git a/VERSION b/VERSION index 348ae384..041712a5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1225 +xorm v0.4.4.1230 From d3155a494da964e2a447a474d6603a0a0b7ce98b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 30 Dec 2015 16:58:58 +0800 Subject: [PATCH 15/18] updated readme --- README.md | 56 ++++++++++++++++++++++++++-------------------------- README_CN.md | 56 ++++++++++++++++++++++++++-------------------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 7d237d60..4b4685ea 100644 --- a/README.md +++ b/README.md @@ -90,13 +90,13 @@ Or # Quick Start -## Create Engine +* Create Engine ```Go engine, err := xorm.NewEngine(driverName, dataSourceName) ``` -## Define a struct and Sync2 table struct to database +* Define a struct and Sync2 table struct to database ```Go type User struct { @@ -112,19 +112,19 @@ type User struct { err := engine.Sync2(new(User)) ``` -## Query a SQL string, the returned results is []map[string][]byte +* Query a SQL string, the returned results is []map[string][]byte ```Go results, err := engine.Query("select * from user") ``` -## Execute a SQL string, the returned results +* Execute a SQL string, the returned results ```Go affected, err := engine.Exec("update user set age = ? where name = ?", age, name) ``` -## Insert one or multipe records to database +* Insert one or multipe records to database ```Go affected, err := engine.Insert(&user) @@ -139,7 +139,7 @@ affected, err := engine.Insert(&user1, &users) // INSERT INTO struct2 () values (),(),() ``` -## Query one record from database +* Query one record from database ```Go has, err := engine.Get(&user) @@ -148,32 +148,32 @@ has, err := engine.Where("name = ?", name).Desc("id").Get(&user) // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 ``` -## Query multiple records from database, also you can use join and extends +* Query multiple records from database, also you can use join and extends ```Go - var users []User - err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users) - // SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10 +var users []User +err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users) +// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10 - type Detail struct { - Id int64 - UserId int64 `xorm:"index"` - } +type Detail struct { + Id int64 + UserId int64 `xorm:"index"` +} - type UserDetail struct { - User `xorm:"extends"` - Detail `xorm:"extends"` - } +type UserDetail struct { + User `xorm:"extends"` + Detail `xorm:"extends"` +} - var users []UserDetail - err := engine.Table("user").Select("user.*, detail.*") - Join("INNER", "detail", "detail.user_id = user.id"). - Where("user.name = ?", name).Limit(10, 0). - Find(&users) - // SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 +var users []UserDetail +err := engine.Table("user").Select("user.*, detail.*") + Join("INNER", "detail", "detail.user_id = user.id"). + Where("user.name = ?", name).Limit(10, 0). + Find(&users) +// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 ``` -## Query multiple records and record by record handle, there two methods Iterate and Rows +* Query multiple records and record by record handle, there two methods Iterate and Rows ```Go err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error { @@ -191,7 +191,7 @@ for rows.Next() { } ``` -## Update one or more records, default will update non-empty and non-zero fields except to use Cols, AllCols and etc. +* Update one or more records, default will update non-empty and non-zero fields except to use Cols, AllCols and etc. ```Go affected, err := engine.Id(1).Update(&user) @@ -216,14 +216,14 @@ affected, err := engine.Id(1).AllCols().Update(&user) // UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ? ``` -## Delete one or more records, Delete MUST has conditon +* Delete one or more records, Delete MUST has conditon ```Go affected, err := engine.Where(...).Delete(&user) // DELETE FROM user Where ... ``` -## Count records +* Count records ```Go counts, err := engine.Count(&user) diff --git a/README_CN.md b/README_CN.md index df2b75b8..4466bc70 100644 --- a/README_CN.md +++ b/README_CN.md @@ -86,13 +86,13 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 # 快速开始 -## 第一步创建引擎,driverName, dataSourceName和database/sql接口相同 +* 第一步创建引擎,driverName, dataSourceName和database/sql接口相同 ```Go engine, err := xorm.NewEngine(driverName, dataSourceName) ``` -## 定义一个和表同步的结构体,并且自动同步结构体到数据库 +* 定义一个和表同步的结构体,并且自动同步结构体到数据库 ```Go type User struct { @@ -108,19 +108,19 @@ type User struct { err := engine.Sync2(new(User)) ``` -## 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte +* 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte ```Go results, err := engine.Query("select * from user") ``` -## 执行一个SQL语句 +* 执行一个SQL语句 ```Go affected, err := engine.Exec("update user set age = ? where name = ?", age, name) ``` -## 插入一条或者多条记录 +* 插入一条或者多条记录 ```Go affected, err := engine.Insert(&user) @@ -135,7 +135,7 @@ affected, err := engine.Insert(&user1, &users) // INSERT INTO struct2 () values (),(),() ``` -## 查询单条记录 +* 查询单条记录 ```Go has, err := engine.Get(&user) @@ -144,32 +144,32 @@ has, err := engine.Where("name = ?", name).Desc("id").Get(&user) // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 ``` -## 查询多条记录,当然可以使用Join和extends来组合使用 +* 查询多条记录,当然可以使用Join和extends来组合使用 ```Go - var users []User - err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users) - // SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10 +var users []User +err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users) +// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10 - type Detail struct { - Id int64 - UserId int64 `xorm:"index"` - } +type Detail struct { + Id int64 + UserId int64 `xorm:"index"` +} - type UserDetail struct { - User `xorm:"extends"` - Detail `xorm:"extends"` - } +type UserDetail struct { + User `xorm:"extends"` + Detail `xorm:"extends"` +} - var users []UserDetail - err := engine.Table("user").Select("user.*, detail.*") - Join("INNER", "detail", "detail.user_id = user.id"). - Where("user.name = ?", name).Limit(10, 0). - Find(&users) - // SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 +var users []UserDetail +err := engine.Table("user").Select("user.*, detail.*") + Join("INNER", "detail", "detail.user_id = user.id"). + Where("user.name = ?", name).Limit(10, 0). + Find(&users) +// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 ``` -## 根据条件遍历数据库,可以有两种方式: Iterate and Rows +* 根据条件遍历数据库,可以有两种方式: Iterate and Rows ```Go err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error { @@ -187,7 +187,7 @@ for rows.Next() { } ``` -## 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段 +* 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段 ```Go affected, err := engine.Id(1).Update(&user) @@ -212,14 +212,14 @@ affected, err := engine.Id(1).AllCols().Update(&user) // UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ? ``` -## 删除记录,需要注意,删除必须至少有一个条件,否则会报错。要清空数据库可以用EmptyTable +* 删除记录,需要注意,删除必须至少有一个条件,否则会报错。要清空数据库可以用EmptyTable ```Go affected, err := engine.Where(...).Delete(&user) // DELETE FROM user Where ... ``` -## 获取记录条数 +* 获取记录条数 ```Go counts, err := engine.Count(&user) From 069f551119c35282755422db42d40ebe0eb0ef5f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 30 Dec 2015 17:09:37 +0800 Subject: [PATCH 16/18] remove trace code --- session.go | 2 +- xorm.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/session.go b/session.go index bf88b83b..75ef01fe 100644 --- a/session.go +++ b/session.go @@ -1801,7 +1801,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount if d, ok := vv.Interface().([]uint8); ok { hasAssigned = true t, err := session.byte2Time(col, d) - fmt.Println(string(d), t, err) + //fmt.Println(string(d), t, err) if err != nil { session.Engine.LogError("byte2Time error:", err.Error()) hasAssigned = false diff --git a/xorm.go b/xorm.go index d612d0b1..9d42ab28 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1229" + Version string = "0.4.4.1230" ) func regDrvsNDialects() bool { From 8700152b6cbbf6a221e7ca59ef55e21ef73cf580 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 2 Jan 2016 22:58:49 +0800 Subject: [PATCH 17/18] resolved #67 --- VERSION | 2 +- engine.go | 2 +- helpers.go | 20 ++++++++++++++++++++ helpers_test.go | 22 ++++++++++++++++++++++ xorm.go | 2 +- 5 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 helpers_test.go diff --git a/VERSION b/VERSION index 041712a5..2efec6a8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.4.1230 +xorm v0.4.5.0102 diff --git a/engine.go b/engine.go index 676101a3..5188d27e 100644 --- a/engine.go +++ b/engine.go @@ -759,7 +759,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { if ormTagStr != "" { col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)} - tags := strings.Split(ormTagStr, " ") + tags := splitTag(ormTagStr) if len(tags) > 0 { if tags[0] == "-" { diff --git a/helpers.go b/helpers.go index 1ac6ad62..62cc4383 100644 --- a/helpers.go +++ b/helpers.go @@ -15,6 +15,26 @@ import ( "github.com/go-xorm/core" ) +func splitTag(tag string) (tags []string) { + tag = strings.TrimSpace(tag) + var hasQuote = false + var lastIdx = 0 + for i, t := range tag { + if t == '\'' { + hasQuote = !hasQuote + } else if t == ' ' { + if lastIdx < i && !hasQuote { + tags = append(tags, strings.TrimSpace(tag[lastIdx:i])) + lastIdx = i + 1 + } + } + } + if lastIdx < len(tag) { + tags = append(tags, strings.TrimSpace(tag[lastIdx:len(tag)])) + } + return +} + type zeroable interface { IsZero() bool } diff --git a/helpers_test.go b/helpers_test.go new file mode 100644 index 00000000..7d17383d --- /dev/null +++ b/helpers_test.go @@ -0,0 +1,22 @@ +package xorm + +import "testing" + +func TestSplitTag(t *testing.T) { + var cases = []struct { + tag string + tags []string + }{ + {"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}}, + {"TEXT", []string{"TEXT"}}, + {"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}}, + {"json binary", []string{"json", "binary"}}, + } + + for _, kase := range cases { + tags := splitTag(kase.tag) + if !sliceEq(tags, kase.tags) { + t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags) + } + } +} diff --git a/xorm.go b/xorm.go index 9d42ab28..4a13edc6 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( ) const ( - Version string = "0.4.4.1230" + Version string = "0.4.5.0102" ) func regDrvsNDialects() bool { From fad61020e856d1dfe16f44e006d288713feb38fb Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 2 Jan 2016 23:55:01 +0800 Subject: [PATCH 18/18] resolved #250, #317 --- engine.go | 6 +++ session.go | 21 +++++++--- statement.go | 113 +++++++++++++++++++++++++++++---------------------- 3 files changed, 85 insertions(+), 55 deletions(-) diff --git a/engine.go b/engine.go index 5188d27e..a035a63d 100644 --- a/engine.go +++ b/engine.go @@ -320,6 +320,12 @@ func (engine *Engine) NoAutoTime() *Session { return session.NoAutoTime() } +func (engine *Engine) NoAutoCondition(no ...bool) *Session { + session := engine.NewSession() + session.IsAutoClose = true + return session.NoAutoCondition(no...) +} + // Retrieve all tables, columns, indexes' informations from database. func (engine *Engine) DBMetas() ([]*core.Table, error) { tables, err := engine.dialect.GetTables() diff --git a/session.go b/session.go index 75ef01fe..51073fa3 100644 --- a/session.go +++ b/session.go @@ -258,6 +258,11 @@ func (session *Session) NoAutoTime() *Session { return session } +func (session *Session) NoAutoCondition(no ...bool) *Session { + session.Statement.NoAutoCondition(no...) + return session +} + // Method Limit provide limit and offset query condition func (session *Session) Limit(limit int, start ...int) *Session { session.Statement.Limit(limit, start...) @@ -1219,7 +1224,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } var addedTableName = (len(session.Statement.JoinStr) > 0) - if len(condiBean) > 0 { + if !session.Statement.noAutoCondition && len(condiBean) > 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, @@ -3635,7 +3640,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 var condiColNames []string var condiArgs []interface{} - if len(condiBean) > 0 { + if !session.Statement.noAutoCondition && len(condiBean) > 0 { condiColNames, condiArgs = buildConditions(session.Engine, session.Statement.RefTable, condiBean[0], true, true, false, true, session.Statement.allUseBool, session.Statement.useAllCols, session.Statement.unscoped, session.Statement.mustColumnMap, session.Statement.TableName(), false) @@ -3857,11 +3862,15 @@ func (session *Session) Delete(bean interface{}) (int64, error) { table := session.Engine.TableInfo(bean) 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.TableName(), false) + var colNames []string + var args []interface{} + if !session.Statement.noAutoCondition { + 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.TableName(), false) + } var condition = "" var andStr = session.Engine.dialect.AndStr() diff --git a/statement.go b/statement.go index 2a22a2c4..a8d82f24 100644 --- a/statement.go +++ b/statement.go @@ -39,45 +39,46 @@ type exprParam struct { // statement save all the sql info for executing SQL type Statement struct { - RefTable *core.Table - Engine *Engine - Start int - LimitN int - WhereStr string - IdParam *core.PK - Params []interface{} - OrderStr string - JoinStr string - GroupByStr string - HavingStr string - ColumnStr string - selectStr string - columnMap map[string]bool - useAllCols bool - OmitStr string - ConditionStr string - AltTableName string - RawSQL string - RawParams []interface{} - UseCascade bool - UseAutoJoin bool - StoreEngine string - Charset string - BeanArgs []interface{} - UseCache bool - UseAutoTime bool - IsDistinct bool - IsForUpdate bool - TableAlias string - allUseBool bool - checkVersion bool - unscoped bool - mustColumnMap map[string]bool - nullableMap map[string]bool - inColumns map[string]*inParam - incrColumns map[string]incrParam - decrColumns map[string]decrParam - exprColumns map[string]exprParam + RefTable *core.Table + Engine *Engine + Start int + LimitN int + WhereStr string + IdParam *core.PK + Params []interface{} + OrderStr string + JoinStr string + GroupByStr string + HavingStr string + ColumnStr string + selectStr string + columnMap map[string]bool + useAllCols bool + OmitStr string + ConditionStr string + AltTableName string + RawSQL string + RawParams []interface{} + UseCascade bool + UseAutoJoin bool + StoreEngine string + Charset string + BeanArgs []interface{} + UseCache bool + UseAutoTime bool + noAutoCondition bool + IsDistinct bool + IsForUpdate bool + TableAlias string + allUseBool bool + checkVersion bool + unscoped bool + mustColumnMap map[string]bool + nullableMap map[string]bool + inColumns map[string]*inParam + incrColumns map[string]incrParam + decrColumns map[string]decrParam + exprColumns map[string]exprParam } // init @@ -103,6 +104,7 @@ func (statement *Statement) Init() { statement.BeanArgs = make([]interface{}, 0) statement.UseCache = true statement.UseAutoTime = true + statement.noAutoCondition = false statement.IsDistinct = false statement.IsForUpdate = false statement.TableAlias = "" @@ -119,6 +121,15 @@ func (statement *Statement) Init() { statement.exprColumns = make(map[string]exprParam) } +// NoAutoCondition +func (statement *Statement) NoAutoCondition(no ...bool) *Statement { + statement.noAutoCondition = true + if len(no) > 0 { + statement.noAutoCondition = no[0] + } + return statement +} + // add the raw sql statement func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement { statement.RawSQL = querystring @@ -1111,12 +1122,14 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) var addedTableName = (len(statement.JoinStr) > 0) - colNames, args := buildConditions(statement.Engine, table, bean, true, true, - false, true, statement.allUseBool, statement.useAllCols, - statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName) + if !statement.noAutoCondition { + colNames, args := buildConditions(statement.Engine, table, bean, true, true, + false, true, statement.allUseBool, statement.useAllCols, + statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName) - statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ") - statement.BeanArgs = args + statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ") + statement.BeanArgs = args + } var columnStr string = statement.ColumnStr if len(statement.selectStr) > 0 { @@ -1172,12 +1185,14 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{} var addedTableName = (len(statement.JoinStr) > 0) - colNames, args := buildConditions(statement.Engine, table, bean, true, true, false, - true, statement.allUseBool, statement.useAllCols, - statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName) + if !statement.noAutoCondition { + colNames, args := buildConditions(statement.Engine, table, bean, true, true, false, + true, statement.allUseBool, statement.useAllCols, + statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName) - statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ") - statement.BeanArgs = args + statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ") + statement.BeanArgs = args + } // count(index fieldname) > count(0) > count(*) var id string = "*"