diff --git a/README.md b/README.md
index b89f7666..4b4685ea 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..4466bc70 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 78f657d6..2efec6a8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-xorm v0.4.4.1029
+xorm v0.4.5.0102
diff --git a/engine.go b/engine.go
index fb4bfa9a..f865d6fc 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()
@@ -377,13 +383,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 +461,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
}
@@ -747,7 +765,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 b5e320bc..c1f4df72 100644
--- a/helpers.go
+++ b/helpers.go
@@ -15,6 +15,30 @@ 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
+}
+
func isZero(k interface{}) bool {
switch k.(type) {
case int:
@@ -45,12 +69,33 @@ 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
}
+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/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/pq_driver.go b/pq_driver.go
index d86c97bb..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 != "postgres" {
+ 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, "postgres://") {
+ if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
dataSourceName, err = parseURL(dataSourceName)
if err != nil {
return nil, err
diff --git a/session.go b/session.go
index a8824e28..51073fa3 100644
--- a/session.go
+++ b/session.go
@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
package xorm
import (
@@ -40,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
@@ -50,12 +55,13 @@ type Session struct {
// Method Init reset the session as the init status.
func (session *Session) Init() {
- session.Statement = Statement{Engine: session.Engine}
session.Statement.Init()
+ session.Statement.Engine = session.Engine
session.IsAutoCommit = true
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)
@@ -75,11 +81,15 @@ func (session *Session) Close() {
}
if session.db != nil {
- //session.Engine.Pool.ReleaseDB(session.Engine, session.Db)
- session.db = nil
+ // When Close be called, if session is a transaction and do not call
+ // Commit or Rollback, then call Rollback.
+ if session.Tx != nil && !session.IsCommitedOrRollbacked {
+ session.Rollback()
+ }
session.Tx = nil
session.stmtCache = nil
session.Init()
+ session.db = nil
}
}
@@ -89,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 {
@@ -172,6 +188,12 @@ func (session *Session) SetExpr(column string, expression string) *Session {
return session
}
+// Method Cols provides some columns to special
+func (session *Session) Select(str string) *Session {
+ session.Statement.Select(str)
+ return session
+}
+
// Method Cols provides some columns to special
func (session *Session) Cols(columns ...string) *Session {
session.Statement.Cols(columns...)
@@ -223,6 +245,12 @@ func (session *Session) Omit(columns ...string) *Session {
return session
}
+// Set null when column is zero-value and nullable for update
+func (session *Session) Nullable(columns ...string) *Session {
+ session.Statement.Nullable(columns...)
+ return session
+}
+
// Method NoAutoTime means do not automatically give created field and updated field
// the current time on the current session temporarily
func (session *Session) NoAutoTime() *Session {
@@ -230,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...)
@@ -439,17 +472,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) {
@@ -633,8 +669,6 @@ func (statement *Statement) convertIdSql(sqlStr string) string {
return ""
}
-<<<<<<< HEAD
-=======
func (session *Session) canCache() bool {
if session.Statement.RefTable == nil ||
session.Statement.JoinStr != "" ||
@@ -646,12 +680,9 @@ func (session *Session) canCache() bool {
return true
}
->>>>>>> refs/remotes/go-xorm/master
func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) {
// if has no reftable, then don't use cache currently
- if session.Statement.RefTable == nil ||
- session.Statement.JoinStr != "" ||
- session.Statement.RawSQL != "" {
+ if !session.canCache() {
return false, ErrCacheFailed
}
@@ -749,11 +780,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) {
-<<<<<<< HEAD
- if session.Statement.RefTable == nil ||
-=======
if !session.canCache() ||
->>>>>>> refs/remotes/go-xorm/master
indexNoCase(sqlStr, "having") != -1 ||
indexNoCase(sqlStr, "group by") != -1 {
return ErrCacheFailed
@@ -897,7 +924,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
}
temps[ididxes[sid]] = bean
- session.Engine.LogDebug("[cacheFind] cache bean:", tableName, id, bean)
+ session.Engine.LogDebug("[cacheFind] cache bean:", tableName, id, bean, temps)
cacher.PutBean(tableName, sid, bean)
}
}
@@ -905,7 +932,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
for j := 0; j < len(temps); j++ {
bean := temps[j]
if bean == nil {
- session.Engine.LogWarn("[cacheFind] cache no hit:", tableName, ides[j])
+ session.Engine.LogWarn("[cacheFind] cache no hit:", tableName, ids[j], temps)
// return errors.New("cache error") // !nashtsai! no need to return error, but continue instead
continue
}
@@ -1030,12 +1057,16 @@ func (session *Session) Get(bean interface{}) (bool, error) {
var err error
session.queryPreprocess(&sqlStr, args...)
if session.IsAutoCommit {
- stmt, err := session.doPrepare(sqlStr)
- if err != nil {
- return false, err
+ 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...)
}
@@ -1157,161 +1188,6 @@ func Atot(s string, tp reflect.Type) (interface{}, error) {
return result, nil
}
-func (session *Session) FindMap(rowsSlicePtr interface{}, condiBean ...interface{}) error {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
- if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map {
- return errors.New("needs a pointer to a slice or a map")
- }
-
- sliceElementType := sliceValue.Type().Elem()
- //fmt.Println("sliceValue.Kind()", sliceValue.Kind(), sliceElementType)
-
- //fmt.Println("sliceValue.Kind()")
- /*
- if 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)
- session.Statement.ConditionStr = strings.Join(colNames, " AND ")
- session.Statement.BeanArgs = args
- } else {
- // !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
- 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))
- }
- }*/
- //fmt.Println("sliceValue.Kind()")
- var sqlStr string
- var args []interface{}
- if session.Statement.RawSQL == "" {
- var columnStr string = session.Statement.ColumnStr
- if session.Statement.JoinStr == "" {
- if columnStr == "" {
- if session.Statement.GroupByStr != "" {
- columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
- } else {
- columnStr = session.Statement.genColumnStr()
- }
- }
- } else {
- if columnStr == "" {
- if session.Statement.GroupByStr != "" {
- columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
- } else {
- columnStr = "*"
- }
- }
- }
-
- session.Statement.attachInSql()
-
- sqlStr = session.Statement.genSelectSql(columnStr)
- args = append(session.Statement.Params, session.Statement.BeanArgs...)
- // for mssql and use limit
- qs := strings.Count(sqlStr, "?")
- if len(args)*2 == qs {
- args = append(args, args...)
- }
- } else {
- sqlStr = session.Statement.RawSQL
- args = session.Statement.RawParams
- }
- //fmt.Println("sliceValue.Kind()")
- // var err error
- /*
- if session.Statement.JoinStr == "" {
- if cacher := session.Engine.getCacher2(table); cacher != nil &&
- session.Statement.UseCache &&
- !session.Statement.IsDistinct &&
- !session.Statement.unscoped {
- err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...)
- if err != ErrCacheFailed {
- return err
- }
- err = nil // !nashtsai! reset err to nil for ErrCacheFailed
- session.Engine.LogWarn("Cache Find Failed")
- }
- }
- */
-
- //fmt.Println("sliceValue.Kind()", sliceValue.Kind())
- if sliceValue.Kind() != reflect.Map {
- fmt.Println("sliceValue.Type()", sliceValue.Type(), reflect.TypeOf(make(map[string]interface{})).Name())
-
- if sliceElementType == reflect.TypeOf(make(map[string]interface{})) {
- //fmt.Println("sliceValue.Type()OK")
- resultsSlice, err := session.queryX(sqlStr, args...)
- if err != nil {
- fmt.Println("sliceValue.Type()err", err.Error())
- return err
- }
-
- for _, results := range resultsSlice {
- //fmt.Println("sliceValue.Type()OK", results)
- lRec := make(map[string]interface{})
- for key, val := range results {
- lRec[key] = val
- /*
- lValPr := reflect.Indirect(reflect.ValueOf(val))
- lVal := reflect.ValueOf(lValPr.Interface())
- fmt.Println(key, lVal.Type())
- switch reflect.TypeOf(val).Kind() {
- case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- //str = strconv.FormatInt(lVal.Int(), 10)
- lRec[key] = lVal.Int()
- fmt.Println(key, lVal.Int())
- case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- //str = strconv.FormatUint(lVal.Uint(), 10)
- lRec[key] = lVal.Uint()
- fmt.Println(key, lVal.Uint())
- case reflect.Float32, reflect.Float64:
- //str = strconv.FormatFloat(lVal.Float(), 'f', -1, 64)
- lRec[key] = lVal.Float()
- fmt.Println(key, lVal.Float())
- case reflect.Slice:
-
- fmt.Println(key, lVal)
- /*if lVal.Elem().Kind() == reflect.Uint8 {
- // result[key] = string(reflect.ValueOf(val).Interface().([]byte))
- // fmt.Println(key, aa.Kind())
- break
- }*/
- /*
- case reflect.String:
- //str = lVal.String()
- lRec[key] = lVal.String()
- fmt.Println(key, lVal.String())
- //时间类型
- case reflect.Struct:
- if val, ok := lVal.Interface().(time.Time); ok {
- lRec[key] = val //lVal.Interface().(time.Time)
- fmt.Println(key, val, lVal.Interface().(time.Time))
- }
-
- default:
- fmt.Println(key, lVal.Kind())
- }
- */
- }
- sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(lRec))))
- }
-
- } else {
- fmt.Println("sliceValue.Index(0).Type() == reflect.TypeOf(make(map[string]interface{}))")
- }
- } else {
- fmt.Println("sliceValue.Kind() != reflect.Map")
- }
- return nil
-}
-
// Find retrieve records from table, condiBeans's non-empty fields
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct
@@ -1327,7 +1203,6 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
}
sliceElementType := sliceValue.Type().Elem()
-
var table *core.Table
if session.Statement.RefTable == nil {
if sliceElementType.Kind() == reflect.Ptr {
@@ -1335,7 +1210,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
pv := reflect.New(sliceElementType.Elem())
table = session.Engine.autoMapType(pv.Elem())
} else {
-
+ return errors.New("slice type")
}
} else if sliceElementType.Kind() == reflect.Struct {
pv := reflect.New(sliceElementType)
@@ -1347,21 +1222,13 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
} else {
table = session.Statement.RefTable
}
-<<<<<<< HEAD
- fmt.Println("sliceValue.Kind()")
- if 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)
-=======
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,
session.Statement.TableName(), addedTableName)
->>>>>>> refs/remotes/go-xorm/master
session.Statement.ConditionStr = strings.Join(colNames, " AND ")
session.Statement.BeanArgs = args
} else {
@@ -1376,25 +1243,29 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
colName, colName)
}
}
- fmt.Println("sliceValue.Kind()")
+
var sqlStr string
var args []interface{}
if session.Statement.RawSQL == "" {
var columnStr string = session.Statement.ColumnStr
- if session.Statement.JoinStr == "" {
- if columnStr == "" {
- if session.Statement.GroupByStr != "" {
- columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
- } else {
- columnStr = session.Statement.genColumnStr()
- }
- }
+ if len(session.Statement.selectStr) > 0 {
+ columnStr = session.Statement.selectStr
} else {
- if columnStr == "" {
- if session.Statement.GroupByStr != "" {
- columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
- } else {
- columnStr = "*"
+ if session.Statement.JoinStr == "" {
+ if columnStr == "" {
+ if session.Statement.GroupByStr != "" {
+ columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
+ } else {
+ columnStr = session.Statement.genColumnStr()
+ }
+ }
+ } else {
+ if columnStr == "" {
+ if session.Statement.GroupByStr != "" {
+ columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
+ } else {
+ columnStr = "*"
+ }
}
}
}
@@ -1412,7 +1283,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
sqlStr = session.Statement.RawSQL
args = session.Statement.RawParams
}
- fmt.Println("sliceValue.Kind()")
+
var err error
if session.Statement.JoinStr == "" {
if cacher := session.Engine.getCacher2(table); cacher != nil &&
@@ -1428,20 +1299,22 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
}
}
- fmt.Println("sliceValue.Kind()", sliceValue.Kind())
if sliceValue.Kind() != reflect.Map {
-
var rawRows *core.Rows
var stmt *core.Stmt
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...)
}
@@ -1490,7 +1363,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...)
if err != nil {
@@ -1506,7 +1378,6 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
} else {
newValue = reflect.New(sliceElementType)
}
-
err := session.scanMapIntoStruct(newValue.Interface(), results)
if err != nil {
return err
@@ -1540,25 +1411,10 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.Indirect(reflect.ValueOf(newValue.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()
@@ -1762,7 +1618,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()
@@ -1772,7 +1627,6 @@ func (session *Session) rows2Beans(rows *core.Rows, fields []string, fieldsCount
return err
}
sliceValueSetFunc(&newValue)
-
}
return nil
}
@@ -1946,8 +1800,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
@@ -2008,8 +1876,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) {
@@ -2180,7 +2050,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...)
}
@@ -2195,64 +2065,33 @@ func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{
return rows2maps(rows)
}
-func (session *Session) queryX(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string]string, err error) {
-
- session.queryPreprocess(&sqlStr, paramStr...)
-
- if session.IsAutoCommit {
- return session.innerQuery2(session.DB(), sqlStr, paramStr...)
- }
- return session.txQuery2(session.Tx, sqlStr, paramStr...)
-}
-
-func (session *Session) txQuery2(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) {
- rows, err := tx.Query(sqlStr, params...)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
-
- return rows2Strings(rows)
-}
-
-func (session *Session) innerQuery2(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, 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
- }
- return rows2Strings(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
- }
- rows, err := stmt.Query(params...)
-
- return stmt, rows, err
- })
- if rows != nil {
- defer rows.Close()
- }
- if stmt != nil {
- defer stmt.Close()
- }
if err != nil {
return nil, err
}
@@ -2367,7 +2206,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 {
@@ -2415,7 +2254,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 {
@@ -2538,17 +2377,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 {
@@ -2560,7 +2400,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] == '-' {
@@ -2664,7 +2504,6 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
} else {
x = 0
}
- //fmt.Println("######", x, data)
} else if strings.HasPrefix(sdata, "0x") {
x, err = strconv.ParseInt(sdata, 16, 64)
} else if strings.HasPrefix(sdata, "0") {
@@ -2920,7 +2759,6 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
} else {
x = 0
}
- //fmt.Println("######", x, data)
} else if strings.HasPrefix(sdata, "0x") {
x, err = strconv.ParseInt(sdata, 16, 64)
} else if strings.HasPrefix(sdata, "0") {
@@ -2946,7 +2784,6 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
} else {
x = 0
}
- //fmt.Println("######", x, data)
} else if strings.HasPrefix(sdata, "0x") {
x1, err = strconv.ParseInt(sdata, 16, 64)
x = int(x1)
@@ -2975,7 +2812,6 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
} else {
x = 0
}
- //fmt.Println("######", x, data)
} else if strings.HasPrefix(sdata, "0x") {
x1, err = strconv.ParseInt(sdata, 16, 64)
x = int32(x1)
@@ -3004,7 +2840,6 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
} else {
x = 0
}
- //fmt.Println("######", x, data)
} else if strings.HasPrefix(sdata, "0x") {
x1, err = strconv.ParseInt(sdata, 16, 64)
x = int8(x1)
@@ -3033,7 +2868,6 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
} else {
x = 0
}
- //fmt.Println("######", x, data)
} else if strings.HasPrefix(sdata, "0x") {
x1, err = strconv.ParseInt(sdata, 16, 64)
x = int16(x1)
@@ -3211,20 +3045,15 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
case reflect.String:
return fieldValue.String(), nil
case reflect.Struct:
- if fieldType == core.TimeType {
- switch fieldValue.Interface().(type) {
- case time.Time:
- t := fieldValue.Interface().(time.Time)
- if session.Engine.dialect.DBType() == core.MSSQL {
- if t.IsZero() {
- return nil, nil
- }
+ if fieldType.ConvertibleTo(core.TimeType) {
+ t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+ if session.Engine.dialect.DBType() == core.MSSQL {
+ if t.IsZero() {
+ return nil, nil
}
- tf := session.Engine.FormatTime(col.SQLType.Name, t)
- return tf, nil
- default:
- return fieldValue.Interface(), nil
}
+ tf := session.Engine.FormatTime(col.SQLType.Name, t)
+ return tf, nil
}
if !col.SQLType.IsJson() {
@@ -3234,18 +3063,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
}
-<<<<<<< HEAD
- } else {
- return 0, fmt.Errorf("Unsupported type %v\n", fieldValue.Type())
-=======
return 0, fmt.Errorf("no primary key for col %v", col.Name)
- //}
->>>>>>> refs/remotes/go-xorm/master
}
if col.SQLType.IsText() {
@@ -3263,7 +3085,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())
@@ -3431,19 +3252,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
@@ -3490,19 +3299,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
@@ -3546,23 +3343,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()
@@ -3639,7 +3420,8 @@ func (session *Session) cacheInsert(tables ...string) error {
}
func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error {
- if session.Statement.RefTable == nil || len(session.Statement.RefTable.PrimaryKeys) != 1 {
+ if session.Statement.RefTable == nil ||
+ session.Tx != nil {
return ErrCacheFailed
}
@@ -3788,26 +3570,24 @@ 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
if session.Statement.ColumnStr == "" {
colNames, args = buildUpdates(session.Engine, table, bean, false, false,
false, false, session.Statement.allUseBool, session.Statement.useAllCols,
-<<<<<<< HEAD
- session.Statement.mustColumnMap, session.Statement.columnMap, true)
-=======
session.Statement.mustColumnMap, session.Statement.nullableMap,
session.Statement.columnMap, true, session.Statement.unscoped)
->>>>>>> refs/remotes/go-xorm/master
} else {
colNames, args, err = genCols(table, session, bean, true, true)
if err != nil {
return 0, err
}
}
- } else if t.Kind() == reflect.Map {
+ } else if isMap {
if session.Statement.RefTable == nil {
return 0, ErrTableNotFound
}
@@ -3831,10 +3611,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 + ?"
@@ -3858,10 +3640,10 @@ 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.unscoped, session.Statement.mustColumnMap, session.Statement.TableName(), false)
}
var condition = ""
@@ -3978,6 +3760,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
}
@@ -3994,7 +3777,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
}
func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
- if session.Statement.RefTable == nil || len(session.Statement.RefTable.PrimaryKeys) != 1 {
+ if session.Statement.RefTable == nil ||
+ session.Tx != nil {
return ErrCacheFailed
}
@@ -4019,15 +3803,25 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
if len(resultsSlice) > 0 {
for _, data := range resultsSlice {
var id int64
- if v, ok := data[session.Statement.RefTable.PrimaryKeys[0]]; !ok {
- return errors.New("no id")
- } else {
- id, err = strconv.ParseInt(string(v), 10, 64)
- if err != nil {
- return err
+ var pk core.PK = make([]interface{}, 0)
+ for _, col := range session.Statement.RefTable.PKColumns() {
+ if v, ok := data[col.Name]; !ok {
+ return errors.New("no id")
+ } else {
+ if col.SQLType.IsText() {
+ pk = append(pk, string(v))
+ } else if col.SQLType.IsNumeric() {
+ id, err = strconv.ParseInt(string(v), 10, 64)
+ if err != nil {
+ return err
+ }
+ pk = append(pk, id)
+ } else {
+ return errors.New("not supported primary key type")
+ }
}
}
- ids = append(ids, core.PK{id})
+ ids = append(ids, pk)
}
}
} /*else {
@@ -4068,15 +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,
-<<<<<<< HEAD
- session.Statement.unscoped, session.Statement.mustColumnMap)
-=======
- session.Statement.unscoped, session.Statement.mustColumnMap,
- session.Statement.TableName(), false)
->>>>>>> refs/remotes/go-xorm/master
+ 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()
@@ -4254,10 +4048,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",
diff --git a/statement.go b/statement.go
index fdd49ff0..ddade508 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
@@ -182,7 +193,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 +222,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 +345,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 +370,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() {
@@ -1122,12 +1130,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 {
@@ -1183,12 +1193,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 = "*"
diff --git a/xorm.go b/xorm.go
index a03e6cc1..4a13edc6 100644
--- a/xorm.go
+++ b/xorm.go
@@ -17,7 +17,7 @@ import (
)
const (
- Version string = "0.4.4.1029"
+ Version string = "0.4.5.0102"
)
func regDrvsNDialects() bool {