dependency(): updated go lib go-xorm

pull/3991/head
Torkel Ödegaard 9 years ago
parent ddbdb54c04
commit 9dac382fbf
  1. 9
      Godeps/Godeps.json
  2. 27
      Godeps/_workspace/src/github.com/go-xorm/core/LICENSE
  3. 19
      Godeps/_workspace/src/github.com/go-xorm/core/cache.go
  4. 56
      Godeps/_workspace/src/github.com/go-xorm/core/column.go
  5. 125
      Godeps/_workspace/src/github.com/go-xorm/core/db.go
  6. 8
      Godeps/_workspace/src/github.com/go-xorm/core/db_test.go
  7. 63
      Godeps/_workspace/src/github.com/go-xorm/core/dialect.go
  8. 2
      Godeps/_workspace/src/github.com/go-xorm/core/error.go
  9. 52
      Godeps/_workspace/src/github.com/go-xorm/core/scan.go
  10. 10
      Godeps/_workspace/src/github.com/go-xorm/core/type.go
  11. 2
      Godeps/_workspace/src/github.com/go-xorm/xorm/LICENSE
  12. 186
      Godeps/_workspace/src/github.com/go-xorm/xorm/README.md
  13. 180
      Godeps/_workspace/src/github.com/go-xorm/xorm/README_CN.md
  14. 2
      Godeps/_workspace/src/github.com/go-xorm/xorm/VERSION
  15. 2
      Godeps/_workspace/src/github.com/go-xorm/xorm/doc.go
  16. 137
      Godeps/_workspace/src/github.com/go-xorm/xorm/engine.go
  17. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/error.go
  18. BIN
      Godeps/_workspace/src/github.com/go-xorm/xorm/examples/goroutine.db-journal
  19. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/examples/goroutine.go
  20. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/goracle_driver.go
  21. 73
      Godeps/_workspace/src/github.com/go-xorm/xorm/helpers.go
  22. 22
      Godeps/_workspace/src/github.com/go-xorm/xorm/helpers_test.go
  23. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/logger.go
  24. 5
      Godeps/_workspace/src/github.com/go-xorm/xorm/lru_cacher.go
  25. 5
      Godeps/_workspace/src/github.com/go-xorm/xorm/memory_store.go
  26. 19
      Godeps/_workspace/src/github.com/go-xorm/xorm/mssql_dialect.go
  27. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/mymysql_driver.go
  28. 6
      Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_dialect.go
  29. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_driver.go
  30. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/oci8_driver.go
  31. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/odbc_driver.go
  32. 12
      Godeps/_workspace/src/github.com/go-xorm/xorm/oracle_dialect.go
  33. 13
      Godeps/_workspace/src/github.com/go-xorm/xorm/postgres_dialect.go
  34. 8
      Godeps/_workspace/src/github.com/go-xorm/xorm/pq_driver.go
  35. 8
      Godeps/_workspace/src/github.com/go-xorm/xorm/processors.go
  36. 6
      Godeps/_workspace/src/github.com/go-xorm/xorm/rows.go
  37. 959
      Godeps/_workspace/src/github.com/go-xorm/xorm/session.go
  38. 30
      Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_dialect.go
  39. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_driver.go
  40. 627
      Godeps/_workspace/src/github.com/go-xorm/xorm/statement.go
  41. 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/syslogger.go
  42. 8
      Godeps/_workspace/src/github.com/go-xorm/xorm/xorm.go

9
Godeps/Godeps.json generated vendored

@ -1,6 +1,6 @@
{ {
"ImportPath": "github.com/grafana/grafana", "ImportPath": "github.com/grafana/grafana",
"GoVersion": "go1.5", "GoVersion": "go1.5.1",
"Packages": [ "Packages": [
"./pkg/..." "./pkg/..."
], ],
@ -106,12 +106,13 @@
}, },
{ {
"ImportPath": "github.com/go-xorm/core", "ImportPath": "github.com/go-xorm/core",
"Rev": "be6e7ac47dc57bd0ada25322fa526944f66ccaa6" "Comment": "v0.4.4-7-g9e608f7",
"Rev": "9e608f7330b9d16fe2818cfe731128b3f156cb9a"
}, },
{ {
"ImportPath": "github.com/go-xorm/xorm", "ImportPath": "github.com/go-xorm/xorm",
"Comment": "v0.4.2-58-ge2889e5", "Comment": "v0.4.4-44-gf561133",
"Rev": "e2889e5517600b82905f1d2ba8b70deb71823ffe" "Rev": "f56113384f2c63dfe4cd8e768e349f1c35122b58"
}, },
{ {
"ImportPath": "github.com/gosimple/slug", "ImportPath": "github.com/gosimple/slug",

@ -0,0 +1,27 @@
Copyright (c) 2013 - 2015 Lunny Xiao <xiaolunwen@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -1,10 +1,11 @@
package core package core
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"time" "time"
"bytes"
"encoding/gob"
) )
const ( const (
@ -47,16 +48,20 @@ type Cacher interface {
} }
func encodeIds(ids []PK) (string, error) { func encodeIds(ids []PK) (string, error) {
b, err := json.Marshal(ids) buf := new(bytes.Buffer)
if err != nil { enc := gob.NewEncoder(buf)
return "", err err := enc.Encode(ids)
}
return string(b), nil return buf.String(), err
} }
func decodeIds(s string) ([]PK, error) { func decodeIds(s string) ([]PK, error) {
pks := make([]PK, 0) pks := make([]PK, 0)
err := json.Unmarshal([]byte(s), &pks)
dec := gob.NewDecoder(bytes.NewBufferString(s))
err := dec.Decode(&pks)
return pks, err return pks, err
} }

@ -1,10 +1,10 @@
package core package core
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
"time"
) )
const ( const (
@ -35,6 +35,8 @@ type Column struct {
DefaultIsEmpty bool DefaultIsEmpty bool
EnumOptions map[string]int EnumOptions map[string]int
SetOptions map[string]int SetOptions map[string]int
DisableTimeZone bool
TimeZone *time.Location // column specified time zone
} }
func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column { func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column {
@ -122,50 +124,34 @@ func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) {
} }
if dataStruct.Type().Kind() == reflect.Map { if dataStruct.Type().Kind() == reflect.Map {
var keyValue reflect.Value keyValue := reflect.ValueOf(col.fieldPath[len(col.fieldPath)-1])
if len(col.fieldPath) == 1 {
keyValue = reflect.ValueOf(col.FieldName)
} else if len(col.fieldPath) == 2 {
keyValue = reflect.ValueOf(col.fieldPath[1])
} else {
return nil, fmt.Errorf("Unsupported mutliderive %v", col.FieldName)
}
fieldValue = dataStruct.MapIndex(keyValue) fieldValue = dataStruct.MapIndex(keyValue)
return &fieldValue, nil return &fieldValue, nil
} else if dataStruct.Type().Kind() == reflect.Interface {
structValue := reflect.ValueOf(dataStruct.Interface())
dataStruct = &structValue
} }
if len(col.fieldPath) == 1 { level := len(col.fieldPath)
fieldValue = dataStruct.FieldByName(col.FieldName) fieldValue = dataStruct.FieldByName(col.fieldPath[0])
} else if len(col.fieldPath) == 2 { for i := 0; i < level-1; i++ {
parentField := dataStruct.FieldByName(col.fieldPath[0]) if !fieldValue.IsValid() {
if parentField.IsValid() { break
if parentField.Kind() == reflect.Struct { }
fieldValue = parentField.FieldByName(col.fieldPath[1]) if fieldValue.Kind() == reflect.Struct {
} else if parentField.Kind() == reflect.Ptr { fieldValue = fieldValue.FieldByName(col.fieldPath[i+1])
if parentField.IsNil() { } else if fieldValue.Kind() == reflect.Ptr {
parentField.Set(reflect.New(parentField.Type().Elem())) if fieldValue.IsNil() {
fieldValue = parentField.Elem().FieldByName(col.fieldPath[1]) fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
} else {
parentField = parentField.Elem()
if parentField.IsValid() {
fieldValue = parentField.FieldByName(col.fieldPath[1])
} else {
return nil, fmt.Errorf("field %v is not valid", col.FieldName)
}
}
} }
fieldValue = fieldValue.Elem().FieldByName(col.fieldPath[i+1])
} else { } else {
// so we can use a different struct as conditions return nil, fmt.Errorf("field %v is not valid", col.FieldName)
fieldValue = dataStruct.FieldByName(col.fieldPath[1])
} }
} else {
return nil, fmt.Errorf("Unsupported mutliderive %v", col.FieldName)
} }
if !fieldValue.IsValid() { if !fieldValue.IsValid() {
return nil, errors.New("no find field matched") return nil, fmt.Errorf("field %v is not valid", col.FieldName)
} }
return &fieldValue, nil return &fieldValue, nil

@ -2,6 +2,7 @@ package core
import ( import (
"database/sql" "database/sql"
"database/sql/driver"
"errors" "errors"
"reflect" "reflect"
"regexp" "regexp"
@ -29,10 +30,24 @@ func StructToSlice(query string, st interface{}) (string, []interface{}, error)
} }
args := make([]interface{}, 0) args := make([]interface{}, 0)
var err error
query = re.ReplaceAllStringFunc(query, func(src string) string { query = re.ReplaceAllStringFunc(query, func(src string) string {
args = append(args, vv.Elem().FieldByName(src[1:]).Interface()) fv := vv.Elem().FieldByName(src[1:]).Interface()
if v, ok := fv.(driver.Valuer); ok {
var value driver.Value
value, err = v.Value()
if err != nil {
return "?"
}
args = append(args, value)
} else {
args = append(args, fv)
}
return "?" return "?"
}) })
if err != nil {
return "", []interface{}{}, err
}
return query, args, nil return query, args, nil
} }
@ -43,12 +58,25 @@ type DB struct {
func Open(driverName, dataSourceName string) (*DB, error) { func Open(driverName, dataSourceName string) (*DB, error) {
db, err := sql.Open(driverName, dataSourceName) db, err := sql.Open(driverName, dataSourceName)
return &DB{db, NewCacheMapper(&SnakeMapper{})}, err if err != nil {
return nil, err
}
return &DB{db, NewCacheMapper(&SnakeMapper{})}, nil
}
func FromDB(db *sql.DB) *DB {
return &DB{db, NewCacheMapper(&SnakeMapper{})}
} }
func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
rows, err := db.DB.Query(query, args...) rows, err := db.DB.Query(query, args...)
return &Rows{rows, db.Mapper}, err if err != nil {
if rows != nil {
rows.Close()
}
return nil, err
}
return &Rows{rows, db.Mapper}, nil
} }
func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) { func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) {
@ -68,28 +96,87 @@ func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) {
} }
type Row struct { type Row struct {
*sql.Row rows *Rows
// One of these two will be non-nil: // One of these two will be non-nil:
err error // deferred error for easy chaining err error // deferred error for easy chaining
Mapper IMapper }
func (row *Row) Columns() ([]string, error) {
if row.err != nil {
return nil, row.err
}
return row.rows.Columns()
} }
func (row *Row) Scan(dest ...interface{}) error { func (row *Row) Scan(dest ...interface{}) error {
if row.err != nil { if row.err != nil {
return row.err return row.err
} }
return row.Row.Scan(dest...) defer row.rows.Close()
for _, dp := range dest {
if _, ok := dp.(*sql.RawBytes); ok {
return errors.New("sql: RawBytes isn't allowed on Row.Scan")
}
}
if !row.rows.Next() {
if err := row.rows.Err(); err != nil {
return err
}
return sql.ErrNoRows
}
err := row.rows.Scan(dest...)
if err != nil {
return err
}
// Make sure the query can be processed to completion with no errors.
if err := row.rows.Close(); err != nil {
return err
}
return nil
}
func (row *Row) ScanStructByName(dest interface{}) error {
if row.err != nil {
return row.err
}
return row.rows.ScanStructByName(dest)
}
func (row *Row) ScanStructByIndex(dest interface{}) error {
if row.err != nil {
return row.err
}
return row.rows.ScanStructByIndex(dest)
}
// scan data to a slice's pointer, slice's length should equal to columns' number
func (row *Row) ScanSlice(dest interface{}) error {
if row.err != nil {
return row.err
}
return row.rows.ScanSlice(dest)
}
// scan data to a map's pointer
func (row *Row) ScanMap(dest interface{}) error {
if row.err != nil {
return row.err
}
return row.rows.ScanMap(dest)
} }
func (db *DB) QueryRow(query string, args ...interface{}) *Row { func (db *DB) QueryRow(query string, args ...interface{}) *Row {
row := db.DB.QueryRow(query, args...) rows, err := db.Query(query, args...)
return &Row{row, nil, db.Mapper} return &Row{rows, err}
} }
func (db *DB) QueryRowMap(query string, mp interface{}) *Row { func (db *DB) QueryRowMap(query string, mp interface{}) *Row {
query, args, err := MapToSlice(query, mp) query, args, err := MapToSlice(query, mp)
if err != nil { if err != nil {
return &Row{nil, err, db.Mapper} return &Row{nil, err}
} }
return db.QueryRow(query, args...) return db.QueryRow(query, args...)
} }
@ -97,7 +184,7 @@ func (db *DB) QueryRowMap(query string, mp interface{}) *Row {
func (db *DB) QueryRowStruct(query string, st interface{}) *Row { func (db *DB) QueryRowStruct(query string, st interface{}) *Row {
query, args, err := StructToSlice(query, st) query, args, err := StructToSlice(query, st)
if err != nil { if err != nil {
return &Row{nil, err, db.Mapper} return &Row{nil, err}
} }
return db.QueryRow(query, args...) return db.QueryRow(query, args...)
} }
@ -187,14 +274,14 @@ func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) {
} }
func (s *Stmt) QueryRow(args ...interface{}) *Row { func (s *Stmt) QueryRow(args ...interface{}) *Row {
row := s.Stmt.QueryRow(args...) rows, err := s.Query(args...)
return &Row{row, nil, s.Mapper} return &Row{rows, err}
} }
func (s *Stmt) QueryRowMap(mp interface{}) *Row { func (s *Stmt) QueryRowMap(mp interface{}) *Row {
vv := reflect.ValueOf(mp) vv := reflect.ValueOf(mp)
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
return &Row{nil, errors.New("mp should be a map's pointer"), s.Mapper} return &Row{nil, errors.New("mp should be a map's pointer")}
} }
args := make([]interface{}, len(s.names)) args := make([]interface{}, len(s.names))
@ -208,7 +295,7 @@ func (s *Stmt) QueryRowMap(mp interface{}) *Row {
func (s *Stmt) QueryRowStruct(st interface{}) *Row { func (s *Stmt) QueryRowStruct(st interface{}) *Row {
vv := reflect.ValueOf(st) vv := reflect.ValueOf(st)
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
return &Row{nil, errors.New("st should be a struct's pointer"), s.Mapper} return &Row{nil, errors.New("st should be a struct's pointer")}
} }
args := make([]interface{}, len(s.names)) args := make([]interface{}, len(s.names))
@ -540,14 +627,14 @@ func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) {
} }
func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { func (tx *Tx) QueryRow(query string, args ...interface{}) *Row {
row := tx.Tx.QueryRow(query, args...) rows, err := tx.Query(query, args...)
return &Row{row, nil, tx.Mapper} return &Row{rows, err}
} }
func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row { func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row {
query, args, err := MapToSlice(query, mp) query, args, err := MapToSlice(query, mp)
if err != nil { if err != nil {
return &Row{nil, err, tx.Mapper} return &Row{nil, err}
} }
return tx.QueryRow(query, args...) return tx.QueryRow(query, args...)
} }
@ -555,7 +642,7 @@ func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row {
func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row { func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row {
query, args, err := StructToSlice(query, st) query, args, err := StructToSlice(query, st)
if err != nil { if err != nil {
return &Row{nil, err, tx.Mapper} return &Row{nil, err}
} }
return tx.QueryRow(query, args...) return tx.QueryRow(query, args...)
} }

@ -24,7 +24,7 @@ type User struct {
Age float32 Age float32
Alias string Alias string
NickName string NickName string
Created time.Time Created NullTime
} }
func init() { func init() {
@ -85,7 +85,7 @@ func BenchmarkOriQuery(b *testing.B) {
var Id int64 var Id int64
var Name, Title, Alias, NickName string var Name, Title, Alias, NickName string
var Age float32 var Age float32
var Created time.Time var Created NullTime
err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName, &Created) err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName, &Created)
if err != nil { if err != nil {
b.Error(err) b.Error(err)
@ -600,7 +600,7 @@ func TestExecStruct(t *testing.T) {
Age: 1.2, Age: 1.2,
Alias: "lunny", Alias: "lunny",
NickName: "lunny xiao", NickName: "lunny xiao",
Created: time.Now(), Created: NullTime(time.Now()),
} }
_, err = db.ExecStruct("insert into user (`name`, title, age, alias, nick_name,created) "+ _, err = db.ExecStruct("insert into user (`name`, title, age, alias, nick_name,created) "+
@ -645,7 +645,7 @@ func BenchmarkExecStruct(b *testing.B) {
Age: 1.2, Age: 1.2,
Alias: "lunny", Alias: "lunny",
NickName: "lunny xiao", NickName: "lunny xiao",
Created: time.Now(), Created: NullTime(time.Now()),
} }
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

@ -54,7 +54,7 @@ type Dialect interface {
IndexCheckSql(tableName, idxName string) (string, []interface{}) IndexCheckSql(tableName, idxName string) (string, []interface{})
TableCheckSql(tableName string) (string, []interface{}) TableCheckSql(tableName string) (string, []interface{})
IsColumnExist(tableName string, col *Column) (bool, error) IsColumnExist(tableName string, colName string) (bool, error)
CreateTableSql(table *Table, tableName, storeEngine, charset string) string CreateTableSql(table *Table, tableName, storeEngine, charset string) string
DropTableSql(tableName string) string DropTableSql(tableName string) string
@ -63,6 +63,8 @@ type Dialect interface {
ModifyColumnSql(tableName string, col *Column) string ModifyColumnSql(tableName string, col *Column) string
ForUpdateSql(query string) string
//CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error //CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error
//MustDropTable(tableName string) error //MustDropTable(tableName string) error
@ -164,10 +166,10 @@ func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) {
return false, nil return false, nil
} }
func (db *Base) IsColumnExist(tableName string, col *Column) (bool, error) { func (db *Base) IsColumnExist(tableName, colName string) (bool, error) {
query := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?" query := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
query = strings.Replace(query, "`", db.dialect.QuoteStr(), -1) query = strings.Replace(query, "`", db.dialect.QuoteStr(), -1)
return db.HasRecords(query, db.DbName, tableName, col.Name) return db.HasRecords(query, db.DbName, tableName, colName)
} }
/* /*
@ -229,28 +231,33 @@ func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset stri
tableName = table.Name tableName = table.Name
} }
sql += b.dialect.Quote(tableName) + " (" sql += b.dialect.Quote(tableName)
sql += " ("
pkList := table.PrimaryKeys
if len(table.ColumnsSeq()) > 0 {
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
if col.IsPrimaryKey && len(pkList) == 1 {
sql += col.String(b.dialect)
} else {
sql += col.StringNoPk(b.dialect)
}
sql = strings.TrimSpace(sql)
sql += ", "
}
for _, colName := range table.ColumnsSeq() { if len(pkList) > 1 {
col := table.GetColumn(colName) sql += "PRIMARY KEY ( "
if col.IsPrimaryKey && len(pkList) == 1 { sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
sql += col.String(b.dialect) sql += " ), "
} else {
sql += col.StringNoPk(b.dialect)
} }
sql = strings.TrimSpace(sql)
sql += ", "
}
if len(pkList) > 1 { sql = sql[:len(sql)-2]
sql += "PRIMARY KEY ( "
sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
sql += " ), "
} }
sql += ")"
sql = sql[:len(sql)-2] + ")"
if b.dialect.SupportEngine() && storeEngine != "" { if b.dialect.SupportEngine() && storeEngine != "" {
sql += " ENGINE=" + storeEngine sql += " ENGINE=" + storeEngine
} }
@ -262,21 +269,25 @@ func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset stri
sql += " DEFAULT CHARSET " + charset sql += " DEFAULT CHARSET " + charset
} }
} }
sql += ";"
return sql return sql
} }
func (b *Base) ForUpdateSql(query string) string {
return query + " FOR UPDATE"
}
var ( var (
dialects = map[DbType]Dialect{} dialects = map[DbType]func() Dialect{}
) )
func RegisterDialect(dbName DbType, dialect Dialect) { func RegisterDialect(dbName DbType, dialectFunc func() Dialect) {
if dialect == nil { if dialectFunc == nil {
panic("core: Register dialect is nil") panic("core: Register dialect is nil")
} }
dialects[dbName] = dialect // !nashtsai! allow override dialect dialects[dbName] = dialectFunc // !nashtsai! allow override dialect
} }
func QueryDialect(dbName DbType) Dialect { func QueryDialect(dbName DbType) Dialect {
return dialects[dbName] return dialects[dbName]()
} }

@ -4,7 +4,7 @@ import "errors"
var ( var (
ErrNoMapPointer = errors.New("mp should be a map's pointer") ErrNoMapPointer = errors.New("mp should be a map's pointer")
ErrNoStructPointer = errors.New("mp should be a map's pointer") ErrNoStructPointer = errors.New("mp should be a struct's pointer")
//ErrNotExist = errors.New("Not exist") //ErrNotExist = errors.New("Not exist")
//ErrIgnore = errors.New("Ignore") //ErrIgnore = errors.New("Ignore")
) )

@ -0,0 +1,52 @@
package core
import (
"database/sql/driver"
"fmt"
"time"
)
type NullTime time.Time
var (
_ driver.Valuer = NullTime{}
)
func (ns *NullTime) Scan(value interface{}) error {
if value == nil {
return nil
}
return convertTime(ns, value)
}
// Value implements the driver Valuer interface.
func (ns NullTime) Value() (driver.Value, error) {
if (time.Time)(ns).IsZero() {
return nil, nil
}
return (time.Time)(ns).Format("2006-01-02 15:04:05"), nil
}
func convertTime(dest *NullTime, src interface{}) error {
// Common cases, without reflect.
switch s := src.(type) {
case string:
t, err := time.Parse("2006-01-02 15:04:05", s)
if err != nil {
return err
}
*dest = NullTime(t)
return nil
case []uint8:
t, err := time.Parse("2006-01-02 15:04:05", string(s))
if err != nil {
return err
}
*dest = NullTime(t)
return nil
case nil:
default:
return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest)
}
return nil
}

@ -53,6 +53,10 @@ func (s *SQLType) IsNumeric() bool {
return s.IsType(NUMERIC_TYPE) return s.IsType(NUMERIC_TYPE)
} }
func (s *SQLType) IsJson() bool {
return s.Name == Json
}
var ( var (
Bit = "BIT" Bit = "BIT"
TinyInt = "TINYINT" TinyInt = "TINYINT"
@ -101,6 +105,8 @@ var (
Serial = "SERIAL" Serial = "SERIAL"
BigSerial = "BIGSERIAL" BigSerial = "BIGSERIAL"
Json = "JSON"
SqlTypes = map[string]int{ SqlTypes = map[string]int{
Bit: NUMERIC_TYPE, Bit: NUMERIC_TYPE,
TinyInt: NUMERIC_TYPE, TinyInt: NUMERIC_TYPE,
@ -112,6 +118,7 @@ var (
Enum: TEXT_TYPE, Enum: TEXT_TYPE,
Set: TEXT_TYPE, Set: TEXT_TYPE,
Json: TEXT_TYPE,
Char: TEXT_TYPE, Char: TEXT_TYPE,
Varchar: TEXT_TYPE, Varchar: TEXT_TYPE,
@ -229,7 +236,6 @@ var (
) )
func Type2SQLType(t reflect.Type) (st SQLType) { func Type2SQLType(t reflect.Type) (st SQLType) {
switch k := t.Kind(); k { switch k := t.Kind(); k {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
st = SQLType{Int, 0, 0} st = SQLType{Int, 0, 0}
@ -252,7 +258,7 @@ func Type2SQLType(t reflect.Type) (st SQLType) {
case reflect.String: case reflect.String:
st = SQLType{Varchar, 255, 0} st = SQLType{Varchar, 255, 0}
case reflect.Struct: case reflect.Struct:
if t.ConvertibleTo(reflect.TypeOf(c_TIME_DEFAULT)) { if t.ConvertibleTo(TimeType) {
st = SQLType{DateTime, 0, 0} st = SQLType{DateTime, 0, 0}
} else { } else {
// TODO need to handle association struct // TODO need to handle association struct

@ -1,4 +1,4 @@
Copyright (c) 2013 - 2014 Copyright (c) 2013 - 2015 The Xorm Authors
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

@ -2,6 +2,8 @@
Xorm is a simple and powerful ORM for Go. Xorm is a simple and powerful ORM for Go.
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge") [![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
# Features # Features
@ -9,7 +11,7 @@ Xorm is a simple and powerful ORM for Go.
* Struct <-> Table Mapping Support * Struct <-> Table Mapping Support
* Chainable APIs * Chainable APIs
* Transaction Support * Transaction Support
* Both ORM and raw SQL operation Support * Both ORM and raw SQL operation Support
@ -33,38 +35,44 @@ Drivers for Go's sql package which currently support database/sql includes:
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* Postgres: [github.com/lib/pq](https://github.com/lib/pq) * Postgres: [github.com/lib/pq](https://github.com/lib/pq)
* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) * MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) * MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (experiment)
# Changelog # Changelog
* **v0.4.1** * **v0.4.4**
Features: * ql database expriment support
* Add deleted xorm tag for soft delete and add unscoped * tidb database expriment support
* sql.NullString and etc. field support
* select ForUpdate support
* many bugs fixed
* **v0.4.0 RC1** * **v0.4.3**
Changes: * Json column type support
* moved xorm cmd to [github.com/go-xorm/cmd](github.com/go-xorm/cmd) * oracle expirement support
* refactored general DB operation a core lib at [github.com/go-xorm/core](https://github.com/go-xorm/core) * bug fixed
* moved tests to github.com/go-xorm/tests [github.com/go-xorm/tests](github.com/go-xorm/tests)
Improvements: * **v0.4.2**
* Prepared statement cache * Transaction will auto rollback if not Rollback or Commit be called.
* Add Incr API * Gonic Mapper support
* Specify Timezone Location * bug fixed
[More changelogs ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-15) [More changelogs ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16)
# Installation # 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 gopm get github.com/go-xorm/xorm
@ -80,8 +88,152 @@ Or
* [GoWalker](http://gowalker.org/github.com/go-xorm/xorm) * [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 # Cases
* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
* [Wego](http://github.com/go-tango/wego) * [Wego](http://github.com/go-tango/wego)
* [Docker.cn](https://docker.cn/) * [Docker.cn](https://docker.cn/)

@ -4,6 +4,8 @@
xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。 xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) [![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm)
## 特性 ## 特性
@ -18,7 +20,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件 * 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件
* 支持级联加载Struct * 支持级联加载Struct
* 支持缓存 * 支持缓存
@ -34,29 +36,42 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* Postgres: [github.com/lib/pq](https://github.com/lib/pq) * Postgres: [github.com/lib/pq](https://github.com/lib/pq)
* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) * MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) * MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持)
* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (试验性支持)
## 更新日志 ## 更新日志
* **v0.4.2** * **v0.4.4**
新特性: * Tidb 数据库支持
* deleted标记 * QL 试验性支持
* bug fixed * sql.NullString支持
* ForUpdate 支持
* bug修正
* **v0.4.3**
* Json 字段类型支持
* oracle实验性支持
* bug修正
[更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16) [更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16)
## 安装 ## 安装
推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: 推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
gopm get github.com/go-xorm/xorm gopm get github.com/go-xorm/xorm
或者您也可以使用go工具进行安装: 或者您也可以使用go工具进行安装:
go get github.com/go-xorm/xorm go get github.com/go-xorm/xorm
@ -69,8 +84,151 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm) * [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)
* [Wego](http://github.com/go-tango/wego) * [Wego](http://github.com/go-tango/wego)

@ -1 +1 @@
xorm v0.4.2.0225 xorm v0.4.5.0204

@ -1,4 +1,4 @@
// Copyright 2013 - 2014 The XORM Authors. All rights reserved. // Copyright 2013 - 2015 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD // Use of this source code is governed by a BSD
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

@ -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 package xorm
import ( import (
@ -316,6 +320,12 @@ func (engine *Engine) NoAutoTime() *Session {
return session.NoAutoTime() 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. // Retrieve all tables, columns, indexes' informations from database.
func (engine *Engine) DBMetas() ([]*core.Table, error) { func (engine *Engine) DBMetas() ([]*core.Table, error) {
tables, err := engine.dialect.GetTables() tables, err := engine.dialect.GetTables()
@ -373,13 +383,25 @@ func (engine *Engine) DumpAll(w io.Writer) error {
return err return err
} }
for _, table := range tables { _, err = io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s*/\n\n",
_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+"\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 { if err != nil {
return err return err
} }
for _, index := range table.Indexes { 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 { if err != nil {
return err return err
} }
@ -439,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 { if err != nil {
return err return err
} }
@ -506,6 +528,12 @@ func (engine *Engine) Distinct(columns ...string) *Session {
return session.Distinct(columns...) return session.Distinct(columns...)
} }
func (engine *Engine) Select(str string) *Session {
session := engine.NewSession()
session.IsAutoClose = true
return session.Select(str)
}
// only use the paramters as select or update columns // only use the paramters as select or update columns
func (engine *Engine) Cols(columns ...string) *Session { func (engine *Engine) Cols(columns ...string) *Session {
session := engine.NewSession() session := engine.NewSession()
@ -543,6 +571,13 @@ func (engine *Engine) Omit(columns ...string) *Session {
return session.Omit(columns...) return session.Omit(columns...)
} }
// Set null when column is zero-value and nullable for update
func (engine *Engine) Nullable(columns ...string) *Session {
session := engine.NewSession()
session.IsAutoClose = true
return session.Nullable(columns...)
}
// This method will generate "column IN (?, ?)" // This method will generate "column IN (?, ?)"
func (engine *Engine) In(column string, args ...interface{}) *Session { func (engine *Engine) In(column string, args ...interface{}) *Session {
session := engine.NewSession() session := engine.NewSession()
@ -642,20 +677,20 @@ func (engine *Engine) Having(conditions string) *Session {
func (engine *Engine) autoMapType(v reflect.Value) *core.Table { func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
t := v.Type() t := v.Type()
engine.mutex.RLock() engine.mutex.Lock()
table, ok := engine.Tables[t] table, ok := engine.Tables[t]
engine.mutex.RUnlock()
if !ok { if !ok {
table = engine.mapType(v) table = engine.mapType(v)
engine.mutex.Lock()
engine.Tables[t] = table engine.Tables[t] = table
if v.CanAddr() { if engine.Cacher != nil {
engine.GobRegister(v.Addr().Interface()) if v.CanAddr() {
} else { engine.GobRegister(v.Addr().Interface())
engine.GobRegister(v.Interface()) } else {
engine.GobRegister(v.Interface())
}
} }
engine.mutex.Unlock()
} }
engine.mutex.Unlock()
return table return table
} }
@ -691,33 +726,24 @@ func (engine *Engine) newTable() *core.Table {
return table return table
} }
type TableName interface {
TableName() string
}
func (engine *Engine) mapType(v reflect.Value) *core.Table { func (engine *Engine) mapType(v reflect.Value) *core.Table {
t := v.Type() t := v.Type()
table := engine.newTable() table := engine.newTable()
method := v.MethodByName("TableName") if tb, ok := v.Interface().(TableName); ok {
if !method.IsValid() { table.Name = tb.TableName()
if v.CanAddr() { } else {
method = v.Addr().MethodByName("TableName")
}
}
if method.IsValid() {
params := []reflect.Value{}
results := method.Call(params)
if len(results) == 1 {
table.Name = results[0].Interface().(string)
}
}
if table.Name == "" {
table.Name = engine.TableMapper.Obj2Table(t.Name()) table.Name = engine.TableMapper.Obj2Table(t.Name())
} }
table.Type = t table.Type = t
var idFieldColName string var idFieldColName string
var err error var err error
var hasCacheTag, hasNoCacheTag bool
hasCacheTag := false
hasNoCacheTag := false
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
tag := t.Field(i).Tag tag := t.Field(i).Tag
@ -730,7 +756,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
if ormTagStr != "" { if ormTagStr != "" {
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false,
IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)} IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)}
tags := strings.Split(ormTagStr, " ") tags := splitTag(ormTagStr)
if len(tags) > 0 { if len(tags) > 0 {
if tags[0] == "-" { if tags[0] == "-" {
@ -804,6 +830,16 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
case k == "VERSION": case k == "VERSION":
col.IsVersion = true col.IsVersion = true
col.Default = "1" col.Default = "1"
case k == "UTC":
col.TimeZone = time.UTC
case k == "LOCAL":
col.TimeZone = time.Local
case strings.HasPrefix(k, "LOCALE(") && strings.HasSuffix(k, ")"):
location := k[len("INDEX")+1 : len(k)-1]
col.TimeZone, err = time.LoadLocation(location)
if err != nil {
engine.LogError(err)
}
case k == "UPDATED": case k == "UPDATED":
col.IsUpdated = true col.IsUpdated = true
case k == "DELETED": case k == "DELETED":
@ -1112,7 +1148,7 @@ func (engine *Engine) Sync(beans ...interface{}) error {
session := engine.NewSession() session := engine.NewSession()
session.Statement.RefTable = table session.Statement.RefTable = table
defer session.Close() defer session.Close()
isExist, err := session.Engine.dialect.IsColumnExist(table.Name, col) isExist, err := session.Engine.dialect.IsColumnExist(table.Name, col.Name)
if err != nil { if err != nil {
return err return err
} }
@ -1387,7 +1423,6 @@ var (
) )
func (engine *Engine) TZTime(t time.Time) time.Time { func (engine *Engine) TZTime(t time.Time) time.Time {
if NULL_TIME != t { // if time is not initialized it's not suitable for Time.In() if NULL_TIME != t { // if time is not initialized it's not suitable for Time.In()
return t.In(engine.TZLocation) return t.In(engine.TZLocation)
} }
@ -1405,35 +1440,51 @@ func (engine *Engine) NowTime2(sqlTypeName string) (interface{}, time.Time) {
} }
func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{}) { func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{}) {
return engine.formatTime(engine.TZLocation, sqlTypeName, t)
}
func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{}) {
if col.DisableTimeZone {
return engine.formatTime(nil, col.SQLType.Name, t)
} else if col.TimeZone != nil {
return engine.formatTime(col.TimeZone, col.SQLType.Name, t)
}
return engine.formatTime(engine.TZLocation, col.SQLType.Name, t)
}
func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.Time) (v interface{}) {
if engine.dialect.DBType() == core.ORACLE { if engine.dialect.DBType() == core.ORACLE {
return t return t
} }
if tz != nil {
t = engine.TZTime(t)
}
switch sqlTypeName { switch sqlTypeName {
case core.Time: case core.Time:
s := engine.TZTime(t).Format("2006-01-02 15:04:05") //time.RFC3339 s := t.Format("2006-01-02 15:04:05") //time.RFC3339
v = s[11:19] v = s[11:19]
case core.Date: case core.Date:
v = engine.TZTime(t).Format("2006-01-02") v = t.Format("2006-01-02")
case core.DateTime, core.TimeStamp: case core.DateTime, core.TimeStamp:
if engine.dialect.DBType() == "ql" { if engine.dialect.DBType() == "ql" {
v = engine.TZTime(t) v = t
} else if engine.dialect.DBType() == "sqlite3" { } else if engine.dialect.DBType() == "sqlite3" {
v = engine.TZTime(t).UTC().Format("2006-01-02 15:04:05") v = t.UTC().Format("2006-01-02 15:04:05")
} else { } else {
v = engine.TZTime(t).Format("2006-01-02 15:04:05") v = t.Format("2006-01-02 15:04:05")
} }
case core.TimeStampz: case core.TimeStampz:
if engine.dialect.DBType() == core.MSSQL { if engine.dialect.DBType() == core.MSSQL {
v = engine.TZTime(t).Format("2006-01-02T15:04:05.9999999Z07:00") v = t.Format("2006-01-02T15:04:05.9999999Z07:00")
} else if engine.DriverName() == "mssql" { } else if engine.DriverName() == "mssql" {
v = engine.TZTime(t) v = t
} else { } else {
v = engine.TZTime(t).Format(time.RFC3339Nano) v = t.Format(time.RFC3339Nano)
} }
case core.BigInt, core.Int: case core.BigInt, core.Int:
v = engine.TZTime(t).Unix() v = t.Unix()
default: default:
v = engine.TZTime(t) v = t
} }
return return
} }

@ -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 package xorm
import ( import (

@ -33,7 +33,7 @@ func test(engine *xorm.Engine) {
return return
} }
size := 500 size := 100
queue := make(chan int, size) queue := make(chan int, size)
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
@ -83,7 +83,7 @@ func test(engine *xorm.Engine) {
} }
func main() { func main() {
runtime.GOMAXPROCS(1) runtime.GOMAXPROCS(2)
fmt.Println("-----start sqlite go routines-----") fmt.Println("-----start sqlite go routines-----")
engine, err := sqliteEngine() engine, err := sqliteEngine()
if err != nil { if err != nil {

@ -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 package xorm
import ( import (

@ -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 package xorm
import ( import (
@ -11,6 +15,30 @@ import (
"github.com/go-xorm/core" "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 { func isZero(k interface{}) bool {
switch k.(type) { switch k.(type) {
case int: case int:
@ -33,12 +61,41 @@ func isZero(k interface{}) bool {
return k.(uint32) == 0 return k.(uint32) == 0
case uint64: case uint64:
return k.(uint64) == 0 return k.(uint64) == 0
case float32:
return k.(float32) == 0
case float64:
return k.(float64) == 0
case bool:
return k.(bool) == false
case string: case string:
return k.(string) == "" return k.(string) == ""
case zeroable:
return k.(zeroable).IsZero()
} }
return false 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 { func isPKZero(pk core.PK) bool {
for _, k := range pk { for _, k := range pk {
if isZero(k) { if isZero(k) {
@ -48,6 +105,10 @@ func isPKZero(pk core.PK) bool {
return false return false
} }
func equalNoCase(s1, s2 string) bool {
return strings.ToLower(s1) == strings.ToLower(s2)
}
func indexNoCase(s, sep string) int { func indexNoCase(s, sep string) int {
return strings.Index(strings.ToLower(s), strings.ToLower(sep)) return strings.Index(strings.ToLower(s), strings.ToLower(sep))
} }
@ -129,8 +190,8 @@ func reflect2value(rawValue *reflect.Value) (str string, err error) {
} }
//时间类型 //时间类型
case reflect.Struct: case reflect.Struct:
if aa == core.TimeType { if aa.ConvertibleTo(core.TimeType) {
str = rawValue.Interface().(time.Time).Format(time.RFC3339Nano) str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano)
} else { } else {
err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
} }
@ -352,6 +413,14 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
} }
} }
// !evalphobia! set fieldValue as nil when column is nullable and zero-value
if _, ok := session.Statement.nullableMap[lColName]; ok {
if col.Nullable && isZero(fieldValue.Interface()) {
var nilValue *int
fieldValue = reflect.ValueOf(nilValue)
}
}
if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime {
val, t := session.Engine.NowTime2(col.SQLType.Name) val, t := session.Engine.NowTime2(col.SQLType.Name)
args = append(args, val) args = append(args, val)

@ -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)
}
}
}

@ -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 package xorm
import ( import (

@ -1,4 +1,7 @@
//LRUCacher implements Cacher according to LRU algorithm // 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 package xorm
import ( import (

@ -1,4 +1,7 @@
// MemoryStore implements CacheStore provide local machine // 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 package xorm
import ( import (

@ -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 package xorm
import ( import (
@ -216,6 +220,11 @@ func (db *mssql) SqlType(c *core.Column) string {
switch t := c.SQLType.Name; t { switch t := c.SQLType.Name; t {
case core.Bool: case core.Bool:
res = core.TinyInt res = core.TinyInt
if c.Default == "true" {
c.Default = "1"
} else if c.Default == "false" {
c.Default = "0"
}
case core.Serial: case core.Serial:
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.IsPrimaryKey = true c.IsPrimaryKey = true
@ -238,7 +247,7 @@ func (db *mssql) SqlType(c *core.Column) string {
c.Length = 7 c.Length = 7
case core.MediumInt: case core.MediumInt:
res = core.Int res = core.Int
case core.MediumText, core.TinyText, core.LongText: case core.MediumText, core.TinyText, core.LongText, core.Json:
res = core.Text res = core.Text
case core.Double: case core.Double:
res = core.Real res = core.Real
@ -311,10 +320,10 @@ func (db *mssql) IndexCheckSql(tableName, idxName string) (string, []interface{}
return sql, args return sql, args
}*/ }*/
func (db *mssql) IsColumnExist(tableName string, col *core.Column) (bool, error) { func (db *mssql) IsColumnExist(tableName, colName string) (bool, error) {
query := `SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = ? AND "COLUMN_NAME" = ?` query := `SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = ? AND "COLUMN_NAME" = ?`
return db.HasRecords(query, tableName, col.Name) return db.HasRecords(query, tableName, colName)
} }
func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) { func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
@ -500,6 +509,10 @@ func (db *mssql) CreateTableSql(table *core.Table, tableName, storeEngine, chars
return sql return sql
} }
func (db *mssql) ForUpdateSql(query string) string {
return query
}
func (db *mssql) Filters() []core.Filter { func (db *mssql) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}} return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
} }

@ -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 package xorm
import ( import (

@ -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 package xorm
import ( import (
@ -221,6 +225,8 @@ func (db *mysql) SqlType(c *core.Column) string {
case core.Uuid: case core.Uuid:
res = core.Varchar res = core.Varchar
c.Length = 40 c.Length = 40
case core.Json:
res = core.Text
default: default:
res = t res = t
} }

@ -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 package xorm
import ( import (

@ -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 package xorm
import ( import (

@ -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 package xorm
import ( import (

@ -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 package xorm
import ( import (
@ -518,7 +522,7 @@ func (db *oracle) SqlType(c *core.Column) string {
res = "TIMESTAMP WITH TIME ZONE" res = "TIMESTAMP WITH TIME ZONE"
case core.Float, core.Double, core.Numeric, core.Decimal: case core.Float, core.Double, core.Numeric, core.Decimal:
res = "NUMBER" res = "NUMBER"
case core.Text, core.MediumText, core.LongText: case core.Text, core.MediumText, core.LongText, core.Json:
res = "CLOB" res = "CLOB"
case core.Char, core.Varchar, core.TinyText: case core.Char, core.Varchar, core.TinyText:
res = "VARCHAR2" res = "VARCHAR2"
@ -661,8 +665,8 @@ func (db *oracle) MustDropTable(tableName string) error {
" AND column_name = ?", args " AND column_name = ?", args
}*/ }*/
func (db *oracle) IsColumnExist(tableName string, col *core.Column) (bool, error) { func (db *oracle) IsColumnExist(tableName, colName string) (bool, error) {
args := []interface{}{tableName, col.Name} args := []interface{}{tableName, colName}
query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = :1" + query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = :1" +
" AND column_name = :2" " AND column_name = :2"
rows, err := db.DB().Query(query, args...) rows, err := db.DB().Query(query, args...)
@ -740,6 +744,8 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum
switch dt { switch dt {
case "VARCHAR2": case "VARCHAR2":
col.SQLType = core.SQLType{core.Varchar, len1, len2} col.SQLType = core.SQLType{core.Varchar, len1, len2}
case "NVARCHAR2":
col.SQLType = core.SQLType{core.NVarchar, len1, len2}
case "TIMESTAMP WITH TIME ZONE": case "TIMESTAMP WITH TIME ZONE":
col.SQLType = core.SQLType{core.TimeStampz, 0, 0} col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
case "NUMBER": case "NUMBER":

@ -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 package xorm
import ( import (
@ -892,8 +896,8 @@ func (db *postgres) DropIndexSql(tableName string, index *core.Index) string {
return fmt.Sprintf("DROP INDEX %v", quote(idxName)) return fmt.Sprintf("DROP INDEX %v", quote(idxName))
} }
func (db *postgres) IsColumnExist(tableName string, col *core.Column) (bool, error) { func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) {
args := []interface{}{tableName, col.Name} args := []interface{}{tableName, colName}
query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" + query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" +
" AND column_name = $2" " AND column_name = $2"
rows, err := db.DB().Query(query, args...) rows, err := db.DB().Query(query, args...)
@ -909,7 +913,8 @@ func (db *postgres) IsColumnExist(tableName string, col *core.Column) (bool, err
} }
func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
args := []interface{}{tableName} pgSchema := "public"
args := []interface{}{tableName,pgSchema}
s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix , s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix ,
CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey, CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey,
CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey
@ -920,7 +925,7 @@ FROM pg_attribute f
LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey) LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)
LEFT JOIN pg_class AS g ON p.confrelid = g.oid LEFT JOIN pg_class AS g ON p.confrelid = g.oid
LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name
WHERE c.relkind = 'r'::char AND c.relname = $1 AND f.attnum > 0 ORDER BY f.attnum;` WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;`
rows, err := db.DB().Query(s, args...) rows, err := db.DB().Query(s, args...)
if db.Logger != nil { if db.Logger != nil {

@ -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 package xorm
import ( import (
@ -37,7 +41,7 @@ func parseURL(connstr string) (string, error) {
return "", err return "", err
} }
if u.Scheme != "postgres" { if u.Scheme != "postgresql" && u.Scheme != "postgres" {
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
} }
@ -99,7 +103,7 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
db := &core.Uri{DbType: core.POSTGRES} db := &core.Uri{DbType: core.POSTGRES}
o := make(values) o := make(values)
var err error var err error
if strings.HasPrefix(dataSourceName, "postgres://") { if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
dataSourceName, err = parseURL(dataSourceName) dataSourceName, err = parseURL(dataSourceName)
if err != nil { if err != nil {
return nil, err return nil, err

@ -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 package xorm
// Executed before an object is initially persisted to the database // Executed before an object is initially persisted to the database
@ -19,6 +23,10 @@ type BeforeSetProcessor interface {
BeforeSet(string, Cell) BeforeSet(string, Cell)
} }
type AfterSetProcessor interface {
AfterSet(string, Cell)
}
// !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations // !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations
//// Executed before an object is validated //// Executed before an object is validated
//type BeforeValidateProcessor interface { //type BeforeValidateProcessor interface {

@ -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 package xorm
import ( import (
@ -41,7 +45,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable) sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable)
} }
rows.session.Engine.logSQL(sqlStr, args) rows.session.saveLastSQL(sqlStr, args)
var err error var err error
rows.stmt, err = rows.session.DB().Prepare(sqlStr) rows.stmt, err = rows.session.DB().Prepare(sqlStr)
if err != nil { if err != nil {

File diff suppressed because it is too large Load Diff

@ -1,9 +1,14 @@
// 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 package xorm
import ( import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"regexp"
"strings" "strings"
"github.com/go-xorm/core" "github.com/go-xorm/core"
@ -152,13 +157,21 @@ func (db *sqlite3) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName st
func (db *sqlite3) SqlType(c *core.Column) string { func (db *sqlite3) SqlType(c *core.Column) string {
switch t := c.SQLType.Name; t { switch t := c.SQLType.Name; t {
case core.Bool:
if c.Default == "true" {
c.Default = "1"
} else if c.Default == "false" {
c.Default = "0"
}
return core.Integer
case core.Date, core.DateTime, core.TimeStamp, core.Time: case core.Date, core.DateTime, core.TimeStamp, core.Time:
return core.DateTime return core.DateTime
case core.TimeStampz: case core.TimeStampz:
return core.Text return core.Text
case core.Char, core.Varchar, core.NVarchar, core.TinyText, core.Text, core.MediumText, core.LongText: case core.Char, core.Varchar, core.NVarchar, core.TinyText,
core.Text, core.MediumText, core.LongText, core.Json:
return core.Text return core.Text
case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool: case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt:
return core.Integer return core.Integer
case core.Float, core.Double, core.Real: case core.Float, core.Double, core.Real:
return core.Real return core.Real
@ -238,15 +251,19 @@ func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string {
return fmt.Sprintf("DROP INDEX %v", quote(idxName)) return fmt.Sprintf("DROP INDEX %v", quote(idxName))
} }
func (db *sqlite3) ForUpdateSql(query string) string {
return query
}
/*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) { /*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
args := []interface{}{tableName} args := []interface{}{tableName}
sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))" sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
return sql, args return sql, args
}*/ }*/
func (db *sqlite3) IsColumnExist(tableName string, col *core.Column) (bool, error) { func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) {
args := []interface{}{tableName} args := []interface{}{tableName}
query := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + col.Name + "`%') or (sql like '%[" + col.Name + "]%'))" query := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
rows, err := db.DB().Query(query, args...) rows, err := db.DB().Query(query, args...)
if db.Logger != nil { if db.Logger != nil {
db.Logger.Info("[sql]", query, args) db.Logger.Info("[sql]", query, args)
@ -290,10 +307,13 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu
nStart := strings.Index(name, "(") nStart := strings.Index(name, "(")
nEnd := strings.LastIndex(name, ")") nEnd := strings.LastIndex(name, ")")
colCreates := strings.Split(name[nStart+1:nEnd], ",") reg := regexp.MustCompile(`[^\(,\)]*(\([^\(]*\))?`)
colCreates := reg.FindAllString(name[nStart+1:nEnd], -1)
cols := make(map[string]*core.Column) cols := make(map[string]*core.Column)
colSeq := make([]string, 0) colSeq := make([]string, 0)
for _, colStr := range colCreates { for _, colStr := range colCreates {
reg = regexp.MustCompile(`,\s`)
colStr = reg.ReplaceAllString(colStr, ",")
fields := strings.Fields(strings.TrimSpace(colStr)) fields := strings.Fields(strings.TrimSpace(colStr))
col := new(core.Column) col := new(core.Column)
col.Indexes = make(map[string]bool) col.Indexes = make(map[string]bool)

@ -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 package xorm
import ( import (

@ -1,6 +1,12 @@
// 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 package xorm
import ( import (
"bytes"
"database/sql/driver"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -33,42 +39,46 @@ type exprParam struct {
// statement save all the sql info for executing SQL // statement save all the sql info for executing SQL
type Statement struct { type Statement struct {
RefTable *core.Table RefTable *core.Table
Engine *Engine Engine *Engine
Start int Start int
LimitN int LimitN int
WhereStr string WhereStr string
IdParam *core.PK IdParam *core.PK
Params []interface{} Params []interface{}
OrderStr string OrderStr string
JoinStr string JoinStr string
GroupByStr string GroupByStr string
HavingStr string HavingStr string
ColumnStr string ColumnStr string
columnMap map[string]bool selectStr string
useAllCols bool columnMap map[string]bool
OmitStr string useAllCols bool
ConditionStr string OmitStr string
AltTableName string ConditionStr string
RawSQL string AltTableName string
RawParams []interface{} RawSQL string
UseCascade bool RawParams []interface{}
UseAutoJoin bool UseCascade bool
StoreEngine string UseAutoJoin bool
Charset string StoreEngine string
BeanArgs []interface{} Charset string
UseCache bool BeanArgs []interface{}
UseAutoTime bool UseCache bool
IsDistinct bool UseAutoTime bool
TableAlias string noAutoCondition bool
allUseBool bool IsDistinct bool
checkVersion bool IsForUpdate bool
unscoped bool TableAlias string
mustColumnMap map[string]bool allUseBool bool
inColumns map[string]*inParam checkVersion bool
incrColumns map[string]incrParam unscoped bool
decrColumns map[string]decrParam mustColumnMap map[string]bool
exprColumns map[string]exprParam nullableMap map[string]bool
inColumns map[string]*inParam
incrColumns map[string]incrParam
decrColumns map[string]decrParam
exprColumns map[string]exprParam
} }
// init // init
@ -94,11 +104,15 @@ func (statement *Statement) Init() {
statement.BeanArgs = make([]interface{}, 0) statement.BeanArgs = make([]interface{}, 0)
statement.UseCache = true statement.UseCache = true
statement.UseAutoTime = true statement.UseAutoTime = true
statement.noAutoCondition = false
statement.IsDistinct = false statement.IsDistinct = false
statement.IsForUpdate = false
statement.TableAlias = "" statement.TableAlias = ""
statement.selectStr = ""
statement.allUseBool = false statement.allUseBool = false
statement.useAllCols = false statement.useAllCols = false
statement.mustColumnMap = make(map[string]bool) statement.mustColumnMap = make(map[string]bool)
statement.nullableMap = make(map[string]bool)
statement.checkVersion = true statement.checkVersion = true
statement.unscoped = false statement.unscoped = false
statement.inColumns = make(map[string]*inParam) statement.inColumns = make(map[string]*inParam)
@ -107,20 +121,29 @@ func (statement *Statement) Init() {
statement.exprColumns = make(map[string]exprParam) statement.exprColumns = make(map[string]exprParam)
} }
// add the raw sql statement // NoAutoCondition if you do not want convert bean's field as query condition, then use this function
func (statement *Statement) NoAutoCondition(no ...bool) *Statement {
statement.noAutoCondition = true
if len(no) > 0 {
statement.noAutoCondition = no[0]
}
return statement
}
// Sql add the raw sql statement
func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement { func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement {
statement.RawSQL = querystring statement.RawSQL = querystring
statement.RawParams = args statement.RawParams = args
return statement return statement
} }
// set the table alias // Alias set the table alias
func (statement *Statement) Alias(alias string) *Statement { func (statement *Statement) Alias(alias string) *Statement {
statement.TableAlias = alias statement.TableAlias = alias
return statement return statement
} }
// add Where statment // Where add Where statment
func (statement *Statement) Where(querystring string, args ...interface{}) *Statement { func (statement *Statement) Where(querystring string, args ...interface{}) *Statement {
if !strings.Contains(querystring, statement.Engine.dialect.EqStr()) { if !strings.Contains(querystring, statement.Engine.dialect.EqStr()) {
querystring = strings.Replace(querystring, "=", statement.Engine.dialect.EqStr(), -1) querystring = strings.Replace(querystring, "=", statement.Engine.dialect.EqStr(), -1)
@ -130,11 +153,13 @@ func (statement *Statement) Where(querystring string, args ...interface{}) *Stat
return statement return statement
} }
// add Where & and statment // And add Where & and statment
func (statement *Statement) And(querystring string, args ...interface{}) *Statement { func (statement *Statement) And(querystring string, args ...interface{}) *Statement {
if statement.WhereStr != "" { if len(statement.WhereStr) > 0 {
statement.WhereStr = fmt.Sprintf("(%v) %s (%v)", statement.WhereStr, var buf bytes.Buffer
fmt.Fprintf(&buf, "(%v) %s (%v)", statement.WhereStr,
statement.Engine.dialect.AndStr(), querystring) statement.Engine.dialect.AndStr(), querystring)
statement.WhereStr = buf.String()
} else { } else {
statement.WhereStr = querystring statement.WhereStr = querystring
} }
@ -142,11 +167,13 @@ func (statement *Statement) And(querystring string, args ...interface{}) *Statem
return statement return statement
} }
// add Where & Or statment // Or add Where & Or statment
func (statement *Statement) Or(querystring string, args ...interface{}) *Statement { func (statement *Statement) Or(querystring string, args ...interface{}) *Statement {
if statement.WhereStr != "" { if len(statement.WhereStr) > 0 {
statement.WhereStr = fmt.Sprintf("(%v) %s (%v)", statement.WhereStr, var buf bytes.Buffer
fmt.Fprintf(&buf, "(%v) %s (%v)", statement.WhereStr,
statement.Engine.dialect.OrStr(), querystring) statement.Engine.dialect.OrStr(), querystring)
statement.WhereStr = buf.String()
} else { } else {
statement.WhereStr = querystring statement.WhereStr = querystring
} }
@ -154,7 +181,7 @@ func (statement *Statement) Or(querystring string, args ...interface{}) *Stateme
return statement return statement
} }
// tempororily set table name // Table tempororily set table name, the parameter could be a string or a pointer of struct
func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
v := rValue(tableNameOrBean) v := rValue(tableNameOrBean)
t := v.Type() t := v.Type()
@ -166,127 +193,12 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
return statement return statement
} }
/*func (statement *Statement) genFields(bean interface{}) map[string]interface{} { // Auto generating update columnes and values according a struct
results := make(map[string]interface{})
table := statement.Engine.TableInfo(bean)
for _, col := range table.Columns {
fieldValue := col.ValueOf(bean)
fieldType := reflect.TypeOf(fieldValue.Interface())
var val interface{}
switch fieldType.Kind() {
case reflect.Bool:
if allUseBool {
val = fieldValue.Interface()
} else if _, ok := boolColumnMap[col.Name]; ok {
val = fieldValue.Interface()
} else {
// if a bool in a struct, it will not be as a condition because it default is false,
// please use Where() instead
continue
}
case reflect.String:
if fieldValue.String() == "" {
continue
}
// for MyString, should convert to string or panic
if fieldType.String() != reflect.String.String() {
val = fieldValue.String()
} else {
val = fieldValue.Interface()
}
case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
if fieldValue.Int() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Float32, reflect.Float64:
if fieldValue.Float() == 0.0 {
continue
}
val = fieldValue.Interface()
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
if fieldValue.Uint() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Struct:
if fieldType == reflect.TypeOf(time.Now()) {
t := fieldValue.Interface().(time.Time)
if t.IsZero() || !fieldValue.IsValid() {
continue
}
var str string
if col.SQLType.Name == Time {
s := t.UTC().Format("2006-01-02 15:04:05")
val = s[11:19]
} else if col.SQLType.Name == Date {
str = t.Format("2006-01-02")
val = str
} else {
val = t
}
} else {
engine.autoMapType(fieldValue.Type())
if table, ok := engine.Tables[fieldValue.Type()]; ok {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumn().FieldName)
if pkField.Int() != 0 {
val = pkField.Interface()
} else {
continue
}
} else {
val = fieldValue.Interface()
}
}
case reflect.Array, reflect.Slice, reflect.Map:
if fieldValue == reflect.Zero(fieldType) {
continue
}
if fieldValue.IsNil() || !fieldValue.IsValid() {
continue
}
if col.SQLType.IsText() {
bytes, err := json.Marshal(fieldValue.Interface())
if err != nil {
engine.LogError(err)
continue
}
val = string(bytes)
} else if col.SQLType.IsBlob() {
var bytes []byte
var err error
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
fieldType.Elem().Kind() == reflect.Uint8 {
if fieldValue.Len() > 0 {
val = fieldValue.Bytes()
} else {
continue
}
} else {
bytes, err = json.Marshal(fieldValue.Interface())
if err != nil {
engine.LogError(err)
continue
}
val = bytes
}
} else {
continue
}
default:
val = fieldValue.Interface()
}
results[col.Name] = val
}
return results
}*/
// Auto generating conditions according a struct
func buildUpdates(engine *Engine, table *core.Table, bean interface{}, func buildUpdates(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool, includeVersion bool, includeUpdated bool, includeNil bool,
includeAutoIncr bool, allUseBool bool, useAllCols bool, includeAutoIncr bool, allUseBool bool, useAllCols bool,
mustColumnMap map[string]bool, columnMap map[string]bool, update bool) ([]string, []interface{}) { mustColumnMap map[string]bool, nullableMap map[string]bool,
columnMap map[string]bool, update, unscoped bool) ([]string, []interface{}) {
colNames := make([]string, 0) colNames := make([]string, 0)
var args = make([]interface{}, 0) var args = make([]interface{}, 0)
@ -303,17 +215,13 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
if !includeAutoIncr && col.IsAutoIncrement { if !includeAutoIncr && col.IsAutoIncrement {
continue continue
} }
if col.IsDeleted { if col.IsDeleted && !unscoped {
continue continue
} }
if use, ok := columnMap[col.Name]; ok && !use { if use, ok := columnMap[col.Name]; ok && !use {
continue continue
} }
if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text {
continue
}
fieldValuePtr, err := col.ValueOf(bean) fieldValuePtr, err := col.ValueOf(bean)
if err != nil { if err != nil {
engine.LogError(err) engine.LogError(err)
@ -325,7 +233,9 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
requiredField := useAllCols requiredField := useAllCols
includeNil := useAllCols includeNil := useAllCols
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok { lColName := strings.ToLower(col.Name)
if b, ok := mustColumnMap[lColName]; ok {
if b { if b {
requiredField = true requiredField = true
} else { } else {
@ -333,6 +243,16 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
} }
} }
// !evalphobia! set fieldValue as nil when column is nullable and zero-value
if b, ok := nullableMap[lColName]; ok {
if b && col.Nullable && isZero(fieldValue.Interface()) {
var nilValue *int
fieldValue = reflect.ValueOf(nilValue)
fieldType = reflect.TypeOf(fieldValue.Interface())
includeNil = true
}
}
var val interface{} var val interface{}
if fieldValue.CanAddr() { if fieldValue.CanAddr() {
@ -410,38 +330,53 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
t := int64(fieldValue.Uint()) t := int64(fieldValue.Uint())
val = reflect.ValueOf(&t).Interface() val = reflect.ValueOf(&t).Interface()
case reflect.Struct: case reflect.Struct:
if fieldType == reflect.TypeOf(time.Now()) { if fieldType.ConvertibleTo(core.TimeType) {
t := fieldValue.Interface().(time.Time) t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) { if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
continue continue
} }
val = engine.FormatTime(col.SQLType.Name, t) val = engine.FormatTime(col.SQLType.Name, t)
} else if nulType, ok := fieldValue.Interface().(driver.Valuer); ok {
val, _ = nulType.Value()
} else { } else {
engine.autoMapType(fieldValue) if !col.SQLType.IsJson() {
if table, ok := engine.Tables[fieldValue.Type()]; ok { engine.autoMapType(fieldValue)
if len(table.PrimaryKeys) == 1 { if table, ok := engine.Tables[fieldValue.Type()]; ok {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) if len(table.PrimaryKeys) == 1 {
// fix non-int pk issues pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
//if pkField.Int() != 0 { // fix non-int pk issues
if pkField.IsValid() && !isZero(pkField.Interface()) { if pkField.IsValid() && !isZero(pkField.Interface()) {
val = pkField.Interface() val = pkField.Interface()
} else {
continue
}
} else { } else {
continue //TODO: how to handler?
panic("not supported")
} }
} else { } else {
//TODO: how to handler? val = fieldValue.Interface()
panic("not supported")
} }
} else { } else {
val = fieldValue.Interface() bytes, err := json.Marshal(fieldValue.Interface())
if err != nil {
panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface()))
}
if col.SQLType.IsText() {
val = string(bytes)
} else if col.SQLType.IsBlob() {
val = bytes
}
} }
} }
case reflect.Array, reflect.Slice, reflect.Map: case reflect.Array, reflect.Slice, reflect.Map:
if fieldValue == reflect.Zero(fieldType) { if !requiredField {
continue if fieldValue == reflect.Zero(fieldType) {
} continue
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { }
continue if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
continue
}
} }
if col.SQLType.IsText() { if col.SQLType.IsText() {
@ -492,8 +427,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
func buildConditions(engine *Engine, table *core.Table, bean interface{}, func buildConditions(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool, includeVersion bool, includeUpdated bool, includeNil bool,
includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool, includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool,
mustColumnMap map[string]bool) ([]string, []interface{}) { mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) ([]string, []interface{}) {
colNames := make([]string, 0) colNames := make([]string, 0)
var args = make([]interface{}, 0) var args = make([]interface{}, 0)
for _, col := range table.Columns() { for _, col := range table.Columns() {
@ -510,6 +444,21 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text { if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text {
continue continue
} }
if col.SQLType.IsJson() {
continue
}
var colName string
if addedTableName {
var nm = tableName
if len(aliasName) > 0 {
nm = aliasName
}
colName = engine.Quote(nm) + "." + engine.Quote(col.Name)
} else {
colName = engine.Quote(col.Name)
}
fieldValuePtr, err := col.ValueOf(bean) fieldValuePtr, err := col.ValueOf(bean)
if err != nil { if err != nil {
engine.LogError(err) engine.LogError(err)
@ -517,7 +466,8 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
} }
if col.IsDeleted && !unscoped { // tag "deleted" is enabled if col.IsDeleted && !unscoped { // tag "deleted" is enabled
colNames = append(colNames, fmt.Sprintf("(%v IS NULL or %v = '0001-01-01 00:00:00')", engine.Quote(col.Name), engine.Quote(col.Name))) colNames = append(colNames, fmt.Sprintf("%v IS NULL or %v = '0001-01-01 00:00:00'",
colName, colName))
} }
fieldValue := *fieldValuePtr fieldValue := *fieldValuePtr
@ -539,7 +489,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
if fieldValue.IsNil() { if fieldValue.IsNil() {
if includeNil { if includeNil {
args = append(args, nil) args = append(args, nil)
colNames = append(colNames, fmt.Sprintf("%v %s ?", engine.Quote(col.Name), engine.dialect.EqStr())) colNames = append(colNames, fmt.Sprintf("%v %s ?", colName, engine.dialect.EqStr()))
} }
continue continue
} else if !fieldValue.IsValid() { } else if !fieldValue.IsValid() {
@ -597,24 +547,49 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
val = engine.FormatTime(col.SQLType.Name, t) val = engine.FormatTime(col.SQLType.Name, t)
} else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok { } else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok {
continue continue
} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
val, _ = valNul.Value()
if val == nil {
continue
}
} else { } else {
engine.autoMapType(fieldValue) if col.SQLType.IsJson() {
if table, ok := engine.Tables[fieldValue.Type()]; ok { if col.SQLType.IsText() {
if len(table.PrimaryKeys) == 1 { bytes, err := json.Marshal(fieldValue.Interface())
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) if err != nil {
// fix non-int pk issues engine.LogError(err)
//if pkField.Int() != 0 {
if pkField.IsValid() && !isZero(pkField.Interface()) {
val = pkField.Interface()
} else {
continue continue
} }
} else { val = string(bytes)
//TODO: how to handler? } else if col.SQLType.IsBlob() {
panic("not supported") var bytes []byte
var err error
bytes, err = json.Marshal(fieldValue.Interface())
if err != nil {
engine.LogError(err)
continue
}
val = bytes
} }
} else { } else {
val = fieldValue.Interface() engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok {
if len(table.PrimaryKeys) == 1 {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
// fix non-int pk issues
//if pkField.Int() != 0 {
if pkField.IsValid() && !isZero(pkField.Interface()) {
val = pkField.Interface()
} else {
continue
}
} else {
//TODO: how to handler?
panic(fmt.Sprintln("not supported", fieldValue.Interface(), "as", table.PrimaryKeys))
}
} else {
val = fieldValue.Interface()
}
} }
} }
case reflect.Array, reflect.Slice, reflect.Map: case reflect.Array, reflect.Slice, reflect.Map:
@ -662,7 +637,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
if col.IsPrimaryKey && engine.dialect.DBType() == "ql" { if col.IsPrimaryKey && engine.dialect.DBType() == "ql" {
condi = "id() == ?" condi = "id() == ?"
} else { } else {
condi = fmt.Sprintf("%v %s ?", engine.Quote(col.Name), engine.dialect.EqStr()) condi = fmt.Sprintf("%v %s ?", colName, engine.dialect.EqStr())
} }
colNames = append(colNames, condi) colNames = append(colNames, condi)
} }
@ -709,7 +684,7 @@ func (statement *Statement) Id(id interface{}) *Statement {
return statement return statement
} }
// Generate "Update ... Set column = column + arg" statment // Incr Generate "Update ... Set column = column + arg" statment
func (statement *Statement) Incr(column string, arg ...interface{}) *Statement { func (statement *Statement) Incr(column string, arg ...interface{}) *Statement {
k := strings.ToLower(column) k := strings.ToLower(column)
if len(arg) > 0 { if len(arg) > 0 {
@ -720,7 +695,7 @@ func (statement *Statement) Incr(column string, arg ...interface{}) *Statement {
return statement return statement
} }
// Generate "Update ... Set column = column - arg" statment // Decr Generate "Update ... Set column = column - arg" statment
func (statement *Statement) Decr(column string, arg ...interface{}) *Statement { func (statement *Statement) Decr(column string, arg ...interface{}) *Statement {
k := strings.ToLower(column) k := strings.ToLower(column)
if len(arg) > 0 { if len(arg) > 0 {
@ -731,7 +706,7 @@ func (statement *Statement) Decr(column string, arg ...interface{}) *Statement {
return statement return statement
} }
// Generate "Update ... Set column = {expression}" statment // SetExpr Generate "Update ... Set column = {expression}" statment
func (statement *Statement) SetExpr(column string, expression string) *Statement { func (statement *Statement) SetExpr(column string, expression string) *Statement {
k := strings.ToLower(column) k := strings.ToLower(column)
statement.exprColumns[k] = exprParam{column, expression} statement.exprColumns[k] = exprParam{column, expression}
@ -755,9 +730,14 @@ func (statement *Statement) getExpr() map[string]exprParam {
// Generate "Where column IN (?) " statment // Generate "Where column IN (?) " statment
func (statement *Statement) In(column string, args ...interface{}) *Statement { func (statement *Statement) In(column string, args ...interface{}) *Statement {
length := len(args)
if length == 0 {
return statement
}
k := strings.ToLower(column) k := strings.ToLower(column)
var newargs []interface{} var newargs []interface{}
if len(args) == 1 && if length == 1 &&
reflect.TypeOf(args[0]).Kind() == reflect.Slice { reflect.TypeOf(args[0]).Kind() == reflect.Slice {
newargs = make([]interface{}, 0) newargs = make([]interface{}, 0)
v := reflect.ValueOf(args[0]) v := reflect.ValueOf(args[0])
@ -781,12 +761,17 @@ func (statement *Statement) genInSql() (string, []interface{}) {
return "", []interface{}{} return "", []interface{}{}
} }
inStrs := make([]string, 0, len(statement.inColumns)) inStrs := make([]string, len(statement.inColumns), len(statement.inColumns))
args := make([]interface{}, 0) args := make([]interface{}, 0)
var buf bytes.Buffer
var i int
for _, params := range statement.inColumns { for _, params := range statement.inColumns {
inStrs = append(inStrs, fmt.Sprintf("(%v IN (%v))", buf.Reset()
fmt.Fprintf(&buf, "(%v IN (%v))",
statement.Engine.autoQuote(params.colName), statement.Engine.autoQuote(params.colName),
strings.Join(makeArray("?", len(params.args)), ","))) strings.Join(makeArray("?", len(params.args)), ","))
inStrs[i] = buf.String()
i++
args = append(args, params.args...) args = append(args, params.args...)
} }
@ -799,7 +784,7 @@ func (statement *Statement) genInSql() (string, []interface{}) {
func (statement *Statement) attachInSql() { func (statement *Statement) attachInSql() {
inSql, inArgs := statement.genInSql() inSql, inArgs := statement.genInSql()
if len(inSql) > 0 { if len(inSql) > 0 {
if statement.ConditionStr != "" { if len(statement.ConditionStr) > 0 {
statement.ConditionStr += " " + statement.Engine.dialect.AndStr() + " " statement.ConditionStr += " " + statement.Engine.dialect.AndStr() + " "
} }
statement.ConditionStr += inSql statement.ConditionStr += inSql
@ -858,6 +843,18 @@ func (statement *Statement) Distinct(columns ...string) *Statement {
return statement return statement
} }
// Generate "SELECT ... FOR UPDATE" statment
func (statement *Statement) ForUpdate() *Statement {
statement.IsForUpdate = true
return statement
}
// replace select
func (s *Statement) Select(str string) *Statement {
s.selectStr = str
return s
}
// Generate "col1, col2" statement // Generate "col1, col2" statement
func (statement *Statement) Cols(columns ...string) *Statement { func (statement *Statement) Cols(columns ...string) *Statement {
newColumns := col2NewCols(columns...) newColumns := col2NewCols(columns...)
@ -868,6 +865,7 @@ func (statement *Statement) Cols(columns ...string) *Statement {
if strings.Contains(statement.ColumnStr, ".") { if strings.Contains(statement.ColumnStr, ".") {
statement.ColumnStr = strings.Replace(statement.ColumnStr, ".", statement.Engine.Quote("."), -1) statement.ColumnStr = strings.Replace(statement.ColumnStr, ".", statement.Engine.Quote("."), -1)
} }
statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.Quote("*"), "*", -1)
return statement return statement
} }
@ -886,15 +884,6 @@ func (statement *Statement) MustCols(columns ...string) *Statement {
return statement return statement
} }
// Update use only: not update columns
/*func (statement *Statement) NotCols(columns ...string) *Statement {
newColumns := col2NewCols(columns...)
for _, nc := range newColumns {
statement.mustColumnMap[strings.ToLower(nc)] = false
}
return statement
}*/
// indicates that use bool fields as update contents and query contiditions // indicates that use bool fields as update contents and query contiditions
func (statement *Statement) UseBool(columns ...string) *Statement { func (statement *Statement) UseBool(columns ...string) *Statement {
if len(columns) > 0 { if len(columns) > 0 {
@ -914,6 +903,14 @@ func (statement *Statement) Omit(columns ...string) {
statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", ")))
} }
// Update use only: update columns to null when value is nullable and zero-value
func (statement *Statement) Nullable(columns ...string) {
newColumns := col2NewCols(columns...)
for _, nc := range newColumns {
statement.nullableMap[strings.ToLower(nc)] = true
}
}
// Generate LIMIT limit statement // Generate LIMIT limit statement
func (statement *Statement) Top(limit int) *Statement { func (statement *Statement) Top(limit int) *Statement {
statement.Limit(limit) statement.Limit(limit)
@ -931,7 +928,7 @@ func (statement *Statement) Limit(limit int, start ...int) *Statement {
// Generate "Order By order" statement // Generate "Order By order" statement
func (statement *Statement) OrderBy(order string) *Statement { func (statement *Statement) OrderBy(order string) *Statement {
if statement.OrderStr != "" { if len(statement.OrderStr) > 0 {
statement.OrderStr += ", " statement.OrderStr += ", "
} }
statement.OrderStr += order statement.OrderStr += order
@ -939,44 +936,51 @@ func (statement *Statement) OrderBy(order string) *Statement {
} }
func (statement *Statement) Desc(colNames ...string) *Statement { func (statement *Statement) Desc(colNames ...string) *Statement {
if statement.OrderStr != "" { var buf bytes.Buffer
statement.OrderStr += ", " fmt.Fprintf(&buf, statement.OrderStr)
if len(statement.OrderStr) > 0 {
fmt.Fprint(&buf, ", ")
} }
newColNames := statement.col2NewColsWithQuote(colNames...) newColNames := statement.col2NewColsWithQuote(colNames...)
sqlStr := strings.Join(newColNames, " DESC, ") fmt.Fprintf(&buf, "%v DESC", strings.Join(newColNames, " DESC, "))
statement.OrderStr += sqlStr + " DESC" statement.OrderStr = buf.String()
return statement return statement
} }
// Method Asc provide asc order by query condition, the input parameters are columns. // Method Asc provide asc order by query condition, the input parameters are columns.
func (statement *Statement) Asc(colNames ...string) *Statement { func (statement *Statement) Asc(colNames ...string) *Statement {
if statement.OrderStr != "" { var buf bytes.Buffer
statement.OrderStr += ", " fmt.Fprintf(&buf, statement.OrderStr)
if len(statement.OrderStr) > 0 {
fmt.Fprint(&buf, ", ")
} }
newColNames := statement.col2NewColsWithQuote(colNames...) newColNames := statement.col2NewColsWithQuote(colNames...)
sqlStr := strings.Join(newColNames, " ASC, ") fmt.Fprintf(&buf, "%v ASC", strings.Join(newColNames, " ASC, "))
statement.OrderStr += sqlStr + " ASC" statement.OrderStr = buf.String()
return statement return statement
} }
//The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN //The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
func (statement *Statement) Join(join_operator string, tablename interface{}, condition string) *Statement { func (statement *Statement) Join(join_operator string, tablename interface{}, condition string) *Statement {
var joinTable string var buf bytes.Buffer
if len(statement.JoinStr) > 0 {
fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, join_operator)
} else {
fmt.Fprintf(&buf, "%v JOIN ", join_operator)
}
switch tablename.(type) { switch tablename.(type) {
case []string: case []string:
t := tablename.([]string) t := tablename.([]string)
l := len(t) if len(t) > 1 {
if l > 1 { fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(t[0]), statement.Engine.Quote(t[1]))
table := t[0] } else if len(t) == 1 {
joinTable = statement.Engine.Quote(table) + " AS " + statement.Engine.Quote(t[1]) fmt.Fprintf(&buf, statement.Engine.Quote(t[0]))
} else if l == 1 {
table := t[0]
joinTable = statement.Engine.Quote(table)
} }
case []interface{}: case []interface{}:
t := tablename.([]interface{}) t := tablename.([]interface{})
l := len(t) l := len(t)
table := "" var table string
if l > 0 { if l > 0 {
f := t[0] f := t[0]
v := rValue(f) v := rValue(f)
@ -989,21 +993,17 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co
} }
} }
if l > 1 { if l > 1 {
joinTable = statement.Engine.Quote(table) + " AS " + statement.Engine.Quote(fmt.Sprintf("%v", t[1])) fmt.Fprintf(&buf, "%v AS %v", statement.Engine.Quote(table),
statement.Engine.Quote(fmt.Sprintf("%v", t[1])))
} else if l == 1 { } else if l == 1 {
joinTable = statement.Engine.Quote(table) fmt.Fprintf(&buf, statement.Engine.Quote(table))
} }
default: default:
t := fmt.Sprintf("%v", tablename) fmt.Fprintf(&buf, statement.Engine.Quote(fmt.Sprintf("%v", tablename)))
joinTable = statement.Engine.Quote(t)
}
if statement.JoinStr != "" {
statement.JoinStr = statement.JoinStr + fmt.Sprintf(" %v JOIN %v ON %v", join_operator,
joinTable, condition)
} else {
statement.JoinStr = fmt.Sprintf("%v JOIN %v ON %v", join_operator,
joinTable, condition)
} }
fmt.Fprintf(&buf, " ON %v", condition)
statement.JoinStr = buf.String()
return statement return statement
} }
@ -1120,11 +1120,6 @@ func (s *Statement) genDelIndexSQL() []string {
return sqls return sqls
} }
/*
func (s *Statement) genDropSQL() string {
return s.Engine.dialect.MustDropTa(s.TableName()) + ";"
}*/
func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) { func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) {
var table *core.Table var table *core.Table
if statement.RefTable == nil { if statement.RefTable == nil {
@ -1134,28 +1129,34 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{})
table = statement.RefTable table = statement.RefTable
} }
colNames, args := buildConditions(statement.Engine, table, bean, true, true, var addedTableName = (len(statement.JoinStr) > 0)
false, true, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap)
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ") if !statement.noAutoCondition {
statement.BeanArgs = args colNames, args := statement.buildConditions(table, bean, true, true, false, true, addedTableName)
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ")
statement.BeanArgs = args
}
var columnStr string = statement.ColumnStr var columnStr string = statement.ColumnStr
if len(statement.JoinStr) == 0 { if len(statement.selectStr) > 0 {
if len(columnStr) == 0 { columnStr = statement.selectStr
if statement.GroupByStr != "" {
columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1))
} else {
columnStr = statement.genColumnStr()
}
}
} else { } else {
if len(columnStr) == 0 { if len(statement.JoinStr) == 0 {
if statement.GroupByStr != "" { if len(columnStr) == 0 {
columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) if statement.GroupByStr != "" {
} else { columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1))
columnStr = "*" } else {
columnStr = statement.genColumnStr()
}
}
} else {
if len(columnStr) == 0 {
if statement.GroupByStr != "" {
columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1))
} else {
columnStr = "*"
}
} }
} }
} }
@ -1185,16 +1186,23 @@ func (s *Statement) genAddUniqueStr(uqeName string, cols []string) (string, []in
return sql, []interface{}{} return sql, []interface{}{}
}*/ }*/
func (statement *Statement) buildConditions(table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, addedTableName bool) ([]string, []interface{}) {
return buildConditions(statement.Engine, table, bean, includeVersion, includeUpdated, includeNil, includeAutoIncr, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap, statement.TableName(), statement.TableAlias, addedTableName)
}
func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}) { func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}) {
table := statement.Engine.TableInfo(bean) table := statement.Engine.TableInfo(bean)
statement.RefTable = table statement.RefTable = table
colNames, args := buildConditions(statement.Engine, table, bean, true, true, false, var addedTableName = (len(statement.JoinStr) > 0)
true, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap)
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ") if !statement.noAutoCondition {
statement.BeanArgs = args colNames, args := statement.buildConditions(table, bean, true, true, false, true, addedTableName)
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ")
statement.BeanArgs = args
}
// count(index fieldname) > count(0) > count(*) // count(index fieldname) > count(0) > count(*)
var id string = "*" var id string = "*"
@ -1206,47 +1214,46 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
} }
func (statement *Statement) genSelectSql(columnStr string) (a string) { func (statement *Statement) genSelectSql(columnStr string) (a string) {
/*if statement.GroupByStr != "" {
if columnStr == "" {
columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1))
}
//statement.GroupByStr = columnStr
}*/
var distinct string var distinct string
if statement.IsDistinct { if statement.IsDistinct {
distinct = "DISTINCT " distinct = "DISTINCT "
} }
var dialect = statement.Engine.Dialect()
var quote = statement.Engine.Quote
var top string var top string
var mssqlCondi string var mssqlCondi string
/*var orderBy string
if statement.OrderStr != "" {
orderBy = fmt.Sprintf(" ORDER BY %v", statement.OrderStr)
}*/
statement.processIdParam() statement.processIdParam()
var whereStr string
if statement.WhereStr != "" { var buf bytes.Buffer
whereStr = fmt.Sprintf(" WHERE %v", statement.WhereStr) if len(statement.WhereStr) > 0 {
if len(statement.ConditionStr) > 0 {
fmt.Fprintf(&buf, " WHERE (%v)", statement.WhereStr)
} else {
fmt.Fprintf(&buf, " WHERE %v", statement.WhereStr)
}
if statement.ConditionStr != "" { if statement.ConditionStr != "" {
whereStr = fmt.Sprintf("%v %s %v", whereStr, statement.Engine.Dialect().AndStr(), fmt.Fprintf(&buf, " %s (%v)", dialect.AndStr(), statement.ConditionStr)
statement.ConditionStr)
} }
} else if statement.ConditionStr != "" { } else if len(statement.ConditionStr) > 0 {
whereStr = fmt.Sprintf(" WHERE %v", statement.ConditionStr) fmt.Fprintf(&buf, " WHERE %v", statement.ConditionStr)
} }
var fromStr string = " FROM " + statement.Engine.Quote(statement.TableName()) var whereStr = buf.String()
var fromStr string = " FROM " + quote(statement.TableName())
if statement.TableAlias != "" { if statement.TableAlias != "" {
if statement.Engine.dialect.DBType() == core.ORACLE { if dialect.DBType() == core.ORACLE {
fromStr += " " + statement.Engine.Quote(statement.TableAlias) fromStr += " " + quote(statement.TableAlias)
} else { } else {
fromStr += " AS " + statement.Engine.Quote(statement.TableAlias) fromStr += " AS " + quote(statement.TableAlias)
} }
} }
if statement.JoinStr != "" { if statement.JoinStr != "" {
fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr) fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr)
} }
if statement.Engine.dialect.DBType() == core.MSSQL { if dialect.DBType() == core.MSSQL {
if statement.LimitN > 0 { if statement.LimitN > 0 {
top = fmt.Sprintf(" TOP %d ", statement.LimitN) top = fmt.Sprintf(" TOP %d ", statement.LimitN)
} }
@ -1277,10 +1284,9 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) {
} }
// !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern // !nashtsai! REVIEW Sprintf is considered slowest mean of string concatnation, better to work with builder pattern
a = fmt.Sprintf("SELECT %v%v%v%v%v", top, distinct, columnStr, a = fmt.Sprintf("SELECT %v%v%v%v%v", top, distinct, columnStr, fromStr, whereStr)
fromStr, whereStr) if len(mssqlCondi) > 0 {
if mssqlCondi != "" { if len(whereStr) > 0 {
if whereStr != "" {
a += " AND " + mssqlCondi a += " AND " + mssqlCondi
} else { } else {
a += " WHERE " + mssqlCondi a += " WHERE " + mssqlCondi
@ -1296,17 +1302,20 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) {
if statement.OrderStr != "" { if statement.OrderStr != "" {
a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr) a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr)
} }
if statement.Engine.dialect.DBType() != core.MSSQL && statement.Engine.dialect.DBType() != core.ORACLE { if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE {
if statement.Start > 0 { if statement.Start > 0 {
a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start) a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start)
} else if statement.LimitN > 0 { } else if statement.LimitN > 0 {
a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN) a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN)
} }
} else if statement.Engine.dialect.DBType() == core.ORACLE { } else if dialect.DBType() == core.ORACLE {
if statement.Start != 0 || statement.LimitN != 0 { if statement.Start != 0 || statement.LimitN != 0 {
a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start) a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start)
} }
} }
if statement.IsForUpdate {
a = dialect.ForUpdateSql(a)
}
return return
} }

@ -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.
// +build !windows,!nacl,!plan9 // +build !windows,!nacl,!plan9
package xorm package xorm

@ -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 package xorm
import ( import (
@ -13,7 +17,7 @@ import (
) )
const ( const (
Version string = "0.4.2.0225" Version string = "0.4.5.0204"
) )
func regDrvsNDialects() bool { func regDrvsNDialects() bool {
@ -35,7 +39,7 @@ func regDrvsNDialects() bool {
for driverName, v := range providedDrvsNDialects { for driverName, v := range providedDrvsNDialects {
if driver := core.QueryDriver(driverName); driver == nil { if driver := core.QueryDriver(driverName); driver == nil {
core.RegisterDriver(driverName, v.getDriver()) core.RegisterDriver(driverName, v.getDriver())
core.RegisterDialect(v.dbType, v.getDialect()) core.RegisterDialect(v.dbType, v.getDialect)
} }
} }
return true return true

Loading…
Cancel
Save