Преглед на файлове

dependency(): updated go lib go-xorm

Torkel Ödegaard преди 10 години
родител
ревизия
9dac382fbf
променени са 42 файла, в които са добавени 1635 реда и са изтрити 719 реда
  1. 5 4
      Godeps/Godeps.json
  2. 27 0
      Godeps/_workspace/src/github.com/go-xorm/core/LICENSE
  3. 12 7
      Godeps/_workspace/src/github.com/go-xorm/core/cache.go
  4. 21 35
      Godeps/_workspace/src/github.com/go-xorm/core/column.go
  5. 106 19
      Godeps/_workspace/src/github.com/go-xorm/core/db.go
  6. 4 4
      Godeps/_workspace/src/github.com/go-xorm/core/db_test.go
  7. 37 26
      Godeps/_workspace/src/github.com/go-xorm/core/dialect.go
  8. 1 1
      Godeps/_workspace/src/github.com/go-xorm/core/error.go
  9. 52 0
      Godeps/_workspace/src/github.com/go-xorm/core/scan.go
  10. 8 2
      Godeps/_workspace/src/github.com/go-xorm/core/type.go
  11. 1 1
      Godeps/_workspace/src/github.com/go-xorm/xorm/LICENSE
  12. 169 17
      Godeps/_workspace/src/github.com/go-xorm/xorm/README.md
  13. 169 11
      Godeps/_workspace/src/github.com/go-xorm/xorm/README_CN.md
  14. 1 1
      Godeps/_workspace/src/github.com/go-xorm/xorm/VERSION
  15. 1 1
      Godeps/_workspace/src/github.com/go-xorm/xorm/doc.go
  16. 94 43
      Godeps/_workspace/src/github.com/go-xorm/xorm/engine.go
  17. 4 0
      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. 2 2
      Godeps/_workspace/src/github.com/go-xorm/xorm/examples/goroutine.go
  20. 4 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/goracle_driver.go
  21. 71 2
      Godeps/_workspace/src/github.com/go-xorm/xorm/helpers.go
  22. 22 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/helpers_test.go
  23. 4 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/logger.go
  24. 4 1
      Godeps/_workspace/src/github.com/go-xorm/xorm/lru_cacher.go
  25. 4 1
      Godeps/_workspace/src/github.com/go-xorm/xorm/memory_store.go
  26. 16 3
      Godeps/_workspace/src/github.com/go-xorm/xorm/mssql_dialect.go
  27. 4 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/mymysql_driver.go
  28. 6 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_dialect.go
  29. 4 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_driver.go
  30. 4 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/oci8_driver.go
  31. 4 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/odbc_driver.go
  32. 9 3
      Godeps/_workspace/src/github.com/go-xorm/xorm/oracle_dialect.go
  33. 9 4
      Godeps/_workspace/src/github.com/go-xorm/xorm/postgres_dialect.go
  34. 6 2
      Godeps/_workspace/src/github.com/go-xorm/xorm/pq_driver.go
  35. 8 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/processors.go
  36. 5 1
      Godeps/_workspace/src/github.com/go-xorm/xorm/rows.go
  37. 380 212
      Godeps/_workspace/src/github.com/go-xorm/xorm/session.go
  38. 25 5
      Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_dialect.go
  39. 4 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_driver.go
  40. 318 309
      Godeps/_workspace/src/github.com/go-xorm/xorm/statement.go
  41. 4 0
      Godeps/_workspace/src/github.com/go-xorm/xorm/syslogger.go
  42. 6 2
      Godeps/_workspace/src/github.com/go-xorm/xorm/xorm.go

+ 5 - 4
Godeps/Godeps.json

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

+ 27 - 0
Godeps/_workspace/src/github.com/go-xorm/core/LICENSE

@@ -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.

+ 12 - 7
Godeps/_workspace/src/github.com/go-xorm/core/cache.go

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

+ 21 - 35
Godeps/_workspace/src/github.com/go-xorm/core/column.go

@@ -1,10 +1,10 @@
 package core
 
 import (
-	"errors"
 	"fmt"
 	"reflect"
 	"strings"
+	"time"
 )
 
 const (
@@ -35,6 +35,8 @@ type Column struct {
 	DefaultIsEmpty  bool
 	EnumOptions     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 {
@@ -122,50 +124,34 @@ func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) {
 	}
 
 	if dataStruct.Type().Kind() == reflect.Map {
-		var keyValue reflect.Value
-
-		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)
-		}
-
+		keyValue := reflect.ValueOf(col.fieldPath[len(col.fieldPath)-1])
 		fieldValue = dataStruct.MapIndex(keyValue)
 		return &fieldValue, nil
+	} else if dataStruct.Type().Kind() == reflect.Interface {
+		structValue := reflect.ValueOf(dataStruct.Interface())
+		dataStruct = &structValue
 	}
 
-	if len(col.fieldPath) == 1 {
-		fieldValue = dataStruct.FieldByName(col.FieldName)
-	} else if len(col.fieldPath) == 2 {
-		parentField := dataStruct.FieldByName(col.fieldPath[0])
-		if parentField.IsValid() {
-			if parentField.Kind() == reflect.Struct {
-				fieldValue = parentField.FieldByName(col.fieldPath[1])
-			} else if parentField.Kind() == reflect.Ptr {
-				if parentField.IsNil() {
-					parentField.Set(reflect.New(parentField.Type().Elem()))
-					fieldValue = parentField.Elem().FieldByName(col.fieldPath[1])
-				} 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)
-					}
-				}
+	level := len(col.fieldPath)
+	fieldValue = dataStruct.FieldByName(col.fieldPath[0])
+	for i := 0; i < level-1; i++ {
+		if !fieldValue.IsValid() {
+			break
+		}
+		if fieldValue.Kind() == reflect.Struct {
+			fieldValue = fieldValue.FieldByName(col.fieldPath[i+1])
+		} else if fieldValue.Kind() == reflect.Ptr {
+			if fieldValue.IsNil() {
+				fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
 			}
+			fieldValue = fieldValue.Elem().FieldByName(col.fieldPath[i+1])
 		} else {
-			// so we can use a different struct as conditions
-			fieldValue = dataStruct.FieldByName(col.fieldPath[1])
+			return nil, fmt.Errorf("field  %v is not valid", col.FieldName)
 		}
-	} else {
-		return nil, fmt.Errorf("Unsupported mutliderive %v", col.FieldName)
 	}
 
 	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

+ 106 - 19
Godeps/_workspace/src/github.com/go-xorm/core/db.go

@@ -2,6 +2,7 @@ package core
 
 import (
 	"database/sql"
+	"database/sql/driver"
 	"errors"
 	"reflect"
 	"regexp"
@@ -29,10 +30,24 @@ func StructToSlice(query string, st interface{}) (string, []interface{}, error)
 	}
 
 	args := make([]interface{}, 0)
+	var err error
 	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 "?"
 	})
+	if err != nil {
+		return "", []interface{}{}, err
+	}
 	return query, args, nil
 }
 
@@ -43,12 +58,25 @@ type DB struct {
 
 func Open(driverName, dataSourceName string) (*DB, error) {
 	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) {
 	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) {
@@ -68,28 +96,87 @@ func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) {
 }
 
 type Row struct {
-	*sql.Row
+	rows *Rows
 	// One of these two will be non-nil:
-	err    error // deferred error for easy chaining
-	Mapper IMapper
+	err error // deferred error for easy chaining
+}
+
+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 {
 	if row.err != nil {
 		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 {
-	row := db.DB.QueryRow(query, args...)
-	return &Row{row, nil, db.Mapper}
+	rows, err := db.Query(query, args...)
+	return &Row{rows, err}
 }
 
 func (db *DB) QueryRowMap(query string, mp interface{}) *Row {
 	query, args, err := MapToSlice(query, mp)
 	if err != nil {
-		return &Row{nil, err, db.Mapper}
+		return &Row{nil, err}
 	}
 	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 {
 	query, args, err := StructToSlice(query, st)
 	if err != nil {
-		return &Row{nil, err, db.Mapper}
+		return &Row{nil, err}
 	}
 	return db.QueryRow(query, args...)
 }
@@ -187,14 +274,14 @@ func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) {
 }
 
 func (s *Stmt) QueryRow(args ...interface{}) *Row {
-	row := s.Stmt.QueryRow(args...)
-	return &Row{row, nil, s.Mapper}
+	rows, err := s.Query(args...)
+	return &Row{rows, err}
 }
 
 func (s *Stmt) QueryRowMap(mp interface{}) *Row {
 	vv := reflect.ValueOf(mp)
 	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))
@@ -208,7 +295,7 @@ func (s *Stmt) QueryRowMap(mp interface{}) *Row {
 func (s *Stmt) QueryRowStruct(st interface{}) *Row {
 	vv := reflect.ValueOf(st)
 	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))
@@ -540,14 +627,14 @@ func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) {
 }
 
 func (tx *Tx) QueryRow(query string, args ...interface{}) *Row {
-	row := tx.Tx.QueryRow(query, args...)
-	return &Row{row, nil, tx.Mapper}
+	rows, err := tx.Query(query, args...)
+	return &Row{rows, err}
 }
 
 func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row {
 	query, args, err := MapToSlice(query, mp)
 	if err != nil {
-		return &Row{nil, err, tx.Mapper}
+		return &Row{nil, err}
 	}
 	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 {
 	query, args, err := StructToSlice(query, st)
 	if err != nil {
-		return &Row{nil, err, tx.Mapper}
+		return &Row{nil, err}
 	}
 	return tx.QueryRow(query, args...)
 }

+ 4 - 4
Godeps/_workspace/src/github.com/go-xorm/core/db_test.go

@@ -24,7 +24,7 @@ type User struct {
 	Age      float32
 	Alias    string
 	NickName string
-	Created  time.Time
+	Created  NullTime
 }
 
 func init() {
@@ -85,7 +85,7 @@ func BenchmarkOriQuery(b *testing.B) {
 			var Id int64
 			var Name, Title, Alias, NickName string
 			var Age float32
-			var Created time.Time
+			var Created NullTime
 			err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName, &Created)
 			if err != nil {
 				b.Error(err)
@@ -600,7 +600,7 @@ func TestExecStruct(t *testing.T) {
 		Age:      1.2,
 		Alias:    "lunny",
 		NickName: "lunny xiao",
-		Created:  time.Now(),
+		Created:  NullTime(time.Now()),
 	}
 
 	_, 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,
 		Alias:    "lunny",
 		NickName: "lunny xiao",
-		Created:  time.Now(),
+		Created:  NullTime(time.Now()),
 	}
 
 	for i := 0; i < b.N; i++ {

+ 37 - 26
Godeps/_workspace/src/github.com/go-xorm/core/dialect.go

@@ -54,7 +54,7 @@ type Dialect interface {
 	IndexCheckSql(tableName, idxName 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
 	DropTableSql(tableName string) string
@@ -63,6 +63,8 @@ type Dialect interface {
 
 	ModifyColumnSql(tableName string, col *Column) string
 
+	ForUpdateSql(query string) string
+
 	//CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error
 	//MustDropTable(tableName string) error
 
@@ -164,10 +166,10 @@ func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) {
 	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 = 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
 	}
 
-	sql += b.dialect.Quote(tableName) + " ("
-
-	pkList := table.PrimaryKeys
+	sql += b.dialect.Quote(tableName)
+	sql += " ("
+
+	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() {
-		col := table.GetColumn(colName)
-		if col.IsPrimaryKey && len(pkList) == 1 {
-			sql += col.String(b.dialect)
-		} else {
-			sql += col.StringNoPk(b.dialect)
+		if len(pkList) > 1 {
+			sql += "PRIMARY KEY ( "
+			sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
+			sql += " ), "
 		}
-		sql = strings.TrimSpace(sql)
-		sql += ", "
-	}
 
-	if len(pkList) > 1 {
-		sql += "PRIMARY KEY ( "
-		sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
-		sql += " ), "
+		sql = sql[:len(sql)-2]
 	}
+	sql += ")"
 
-	sql = sql[:len(sql)-2] + ")"
 	if b.dialect.SupportEngine() && storeEngine != "" {
 		sql += " ENGINE=" + storeEngine
 	}
@@ -262,21 +269,25 @@ func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset stri
 			sql += " DEFAULT CHARSET " + charset
 		}
 	}
-	sql += ";"
+
 	return sql
 }
 
+func (b *Base) ForUpdateSql(query string) string {
+	return query + " FOR UPDATE"
+}
+
 var (
-	dialects = map[DbType]Dialect{}
+	dialects = map[DbType]func() Dialect{}
 )
 
-func RegisterDialect(dbName DbType, dialect Dialect) {
-	if dialect == nil {
+func RegisterDialect(dbName DbType, dialectFunc func() Dialect) {
+	if dialectFunc == 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 {
-	return dialects[dbName]
+	return dialects[dbName]()
 }

+ 1 - 1
Godeps/_workspace/src/github.com/go-xorm/core/error.go

@@ -4,7 +4,7 @@ import "errors"
 
 var (
 	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")
 	//ErrIgnore = errors.New("Ignore")
 )

+ 52 - 0
Godeps/_workspace/src/github.com/go-xorm/core/scan.go

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

+ 8 - 2
Godeps/_workspace/src/github.com/go-xorm/core/type.go

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

+ 1 - 1
Godeps/_workspace/src/github.com/go-xorm/xorm/LICENSE

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

+ 169 - 17
Godeps/_workspace/src/github.com/go-xorm/xorm/README.md

@@ -2,6 +2,8 @@
 
 Xorm is a simple and powerful ORM for Go.
 
+[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
 [![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest)  [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
 
 # Features
@@ -9,7 +11,7 @@ Xorm is a simple and powerful ORM for Go.
 * Struct <-> Table Mapping Support
 
 * Chainable APIs
- 
+
 * Transaction Support
 
 * Both ORM and raw SQL operation Support
@@ -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)
 
-* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
-
 * Postgres: [github.com/lib/pq](https://github.com/lib/pq)
 
+* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
+
+* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
+
 * MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
 
 * MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
 
+* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
 
+* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (experiment)
 
 # Changelog
 
-* **v0.4.1**
-	Features:
-	* Add deleted xorm tag for soft delete and add unscoped
+* **v0.4.4**
+    * ql database expriment support
+    * tidb database expriment support
+    * sql.NullString and etc. field support
+    * select ForUpdate support
+    * many bugs fixed
 
-* **v0.4.0 RC1** 
-	Changes:
-	* moved xorm cmd to [github.com/go-xorm/cmd](github.com/go-xorm/cmd)
-	* refactored general DB operation a core lib at [github.com/go-xorm/core](https://github.com/go-xorm/core)
-	* moved tests to github.com/go-xorm/tests [github.com/go-xorm/tests](github.com/go-xorm/tests)
+* **v0.4.3**
+    * Json column type support
+    * oracle expirement support
+    * bug fixed
 
-	Improvements:
-	* Prepared statement cache
-	* Add Incr API
-	* Specify Timezone Location
+* **v0.4.2**
+	* Transaction will auto rollback if not Rollback or Commit be called.
+    * Gonic Mapper support
+    * 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
 
-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
 
@@ -80,8 +88,152 @@ Or
 
 * [GoWalker](http://gowalker.org/github.com/go-xorm/xorm)
 
+# Quick Start
+
+* Create Engine
+
+```Go
+engine, err := xorm.NewEngine(driverName, dataSourceName)
+```
+
+* Define a struct and Sync2 table struct to database
+
+```Go
+type User struct {
+    Id int64
+    Name string
+    Salt string
+    Age int
+    Passwd string `xorm:"varchar(200)"`
+    Created time.Time `xorm:"created"`
+    Updated time.Time `xorm:"updated"`
+}
+
+err := engine.Sync2(new(User))
+```
+
+* Query a SQL string, the returned results is []map[string][]byte
+
+```Go
+results, err := engine.Query("select * from user")
+```
+
+* Execute a SQL string, the returned results
+
+```Go
+affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
+```
+
+* Insert one or multipe records to database
+
+```Go
+affected, err := engine.Insert(&user)
+// INSERT INTO struct () values ()
+affected, err := engine.Insert(&user1, &user2)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values ()
+affected, err := engine.Insert(&users)
+// INSERT INTO struct () values (),(),()
+affected, err := engine.Insert(&user1, &users)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values (),(),()
+```
+
+* Query one record from database
+
+```Go
+has, err := engine.Get(&user)
+// SELECT * FROM user LIMIT 1
+has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
+// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
+```
+
+* Query multiple records from database, also you can use join and extends
+
+```Go
+var users []User
+err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
+// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10
+
+type Detail struct {
+    Id int64
+    UserId int64 `xorm:"index"`
+}
+
+type UserDetail struct {
+    User `xorm:"extends"`
+    Detail `xorm:"extends"`
+}
+
+var users []UserDetail
+err := engine.Table("user").Select("user.*, detail.*")
+    Join("INNER", "detail", "detail.user_id = user.id").
+    Where("user.name = ?", name).Limit(10, 0).
+    Find(&users)
+// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10
+```
+
+* Query multiple records and record by record handle, there two methods Iterate and Rows
+
+```Go
+err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
+    user := bean.(*User)
+    return nil
+})
+// SELECT * FROM user
+
+rows, err := engine.Rows(&User{Name:name})
+// SELECT * FROM user
+defer rows.Close()
+bean := new(Struct)
+for rows.Next() {
+    err = rows.Scan(bean)
+}
+```
+
+* Update one or more records, default will update non-empty and non-zero fields except to use Cols, AllCols and etc.
+
+```Go
+affected, err := engine.Id(1).Update(&user)
+// UPDATE user SET ... Where id = ?
+
+affected, err := engine.Update(&user, &User{Name:name})
+// UPDATE user SET ... Where name = ?
+
+var ids = []int64{1, 2, 3}
+affected, err := engine.In(ids).Update(&user)
+// UPDATE user SET ... Where id IN (?, ?, ?)
+
+// force update indicated columns by Cols
+affected, err := engine.Id(1).Cols("age").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+// force NOT update indicated columns by Omit
+affected, err := engine.Id(1).Omit("name").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+affected, err := engine.Id(1).AllCols().Update(&user)
+// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
+```
+
+* Delete one or more records, Delete MUST has conditon
+
+```Go
+affected, err := engine.Where(...).Delete(&user)
+// DELETE FROM user Where ...
+```
+
+* Count records
+
+```Go
+counts, err := engine.Count(&user)
+// SELECT count(*) AS total FROM user
+```
+
 # Cases
 
+* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
+
 * [Wego](http://github.com/go-tango/wego)
 
 * [Docker.cn](https://docker.cn/)

+ 169 - 11
Godeps/_workspace/src/github.com/go-xorm/xorm/README_CN.md

@@ -4,6 +4,8 @@
 
 xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
 
+[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
 [![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest)  [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm)
 
 ## 特性
@@ -18,7 +20,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
 
 * 支持使用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)
 
-* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
-
 * Postgres: [github.com/lib/pq](https://github.com/lib/pq)
 
+* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
+
+* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
+
 * MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
 
 * MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
 
+* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持)
+
+* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (试验性支持)
+
 ## 更新日志
 
-* **v0.4.2**
-	新特性:
-	* deleted标记
-	* bug fixed
+* **v0.4.4**
+    * Tidb 数据库支持
+    * QL 试验性支持
+    * sql.NullString支持
+    * ForUpdate 支持
+    * bug修正
+
+* **v0.4.3**
+    * Json 字段类型支持
+    * oracle实验性支持
+    * bug修正
 
 [更多更新日志...](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
-	
+
 或者您也可以使用go工具进行安装:
 
 	go get github.com/go-xorm/xorm
@@ -69,8 +84,151 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
 
 * [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm)
 
-
-## 案例
+# 快速开始
+
+* 第一步创建引擎,driverName, dataSourceName和database/sql接口相同
+
+```Go
+engine, err := xorm.NewEngine(driverName, dataSourceName)
+```
+
+* 定义一个和表同步的结构体,并且自动同步结构体到数据库
+
+```Go
+type User struct {
+    Id int64
+    Name string
+    Salt string
+    Age int
+    Passwd string `xorm:"varchar(200)"`
+    Created time.Time `xorm:"created"`
+    Updated time.Time `xorm:"updated"`
+}
+
+err := engine.Sync2(new(User))
+```
+
+* 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte
+
+```Go
+results, err := engine.Query("select * from user")
+```
+
+* 执行一个SQL语句
+
+```Go
+affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
+```
+
+* 插入一条或者多条记录
+
+```Go
+affected, err := engine.Insert(&user)
+// INSERT INTO struct () values ()
+affected, err := engine.Insert(&user1, &user2)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values ()
+affected, err := engine.Insert(&users)
+// INSERT INTO struct () values (),(),()
+affected, err := engine.Insert(&user1, &users)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values (),(),()
+```
+
+* 查询单条记录
+
+```Go
+has, err := engine.Get(&user)
+// SELECT * FROM user LIMIT 1
+has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
+// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
+```
+
+* 查询多条记录,当然可以使用Join和extends来组合使用
+
+```Go
+var users []User
+err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
+// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10
+
+type Detail struct {
+    Id int64
+    UserId int64 `xorm:"index"`
+}
+
+type UserDetail struct {
+    User `xorm:"extends"`
+    Detail `xorm:"extends"`
+}
+
+var users []UserDetail
+err := engine.Table("user").Select("user.*, detail.*")
+    Join("INNER", "detail", "detail.user_id = user.id").
+    Where("user.name = ?", name).Limit(10, 0).
+    Find(&users)
+// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10
+```
+
+* 根据条件遍历数据库,可以有两种方式: Iterate and Rows
+
+```Go
+err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
+    user := bean.(*User)
+    return nil
+})
+// SELECT * FROM user
+
+rows, err := engine.Rows(&User{Name:name})
+// SELECT * FROM user
+defer rows.Close()
+bean := new(Struct)
+for rows.Next() {
+    err = rows.Scan(bean)
+}
+```
+
+* 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段
+
+```Go
+affected, err := engine.Id(1).Update(&user)
+// UPDATE user SET ... Where id = ?
+
+affected, err := engine.Update(&user, &User{Name:name})
+// UPDATE user SET ... Where name = ?
+
+var ids = []int64{1, 2, 3}
+affected, err := engine.In(ids).Update(&user)
+// UPDATE user SET ... Where id IN (?, ?, ?)
+
+// force update indicated columns by Cols
+affected, err := engine.Id(1).Cols("age").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+// force NOT update indicated columns by Omit
+affected, err := engine.Id(1).Omit("name").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+affected, err := engine.Id(1).AllCols().Update(&user)
+// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
+```
+
+* 删除记录,需要注意,删除必须至少有一个条件,否则会报错。要清空数据库可以用EmptyTable
+
+```Go
+affected, err := engine.Where(...).Delete(&user)
+// DELETE FROM user Where ...
+```
+
+* 获取记录条数
+
+```Go
+counts, err := engine.Count(&user)
+// SELECT count(*) AS total FROM user
+```
+
+# 案例
+
+* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
 
 * [Wego](http://github.com/go-tango/wego)
 

+ 1 - 1
Godeps/_workspace/src/github.com/go-xorm/xorm/VERSION

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

+ 1 - 1
Godeps/_workspace/src/github.com/go-xorm/xorm/doc.go

@@ -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
 // license that can be found in the LICENSE file.
 

+ 94 - 43
Godeps/_workspace/src/github.com/go-xorm/xorm/engine.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -316,6 +320,12 @@ func (engine *Engine) NoAutoTime() *Session {
 	return session.NoAutoTime()
 }
 
+func (engine *Engine) NoAutoCondition(no ...bool) *Session {
+	session := engine.NewSession()
+	session.IsAutoClose = true
+	return session.NoAutoCondition(no...)
+}
+
 // Retrieve all tables, columns, indexes' informations from database.
 func (engine *Engine) DBMetas() ([]*core.Table, error) {
 	tables, err := engine.dialect.GetTables()
@@ -373,13 +383,25 @@ func (engine *Engine) DumpAll(w io.Writer) error {
 		return err
 	}
 
-	for _, table := range tables {
-		_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+"\n\n")
+	_, err = io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s*/\n\n",
+		Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05")))
+	if err != nil {
+		return err
+	}
+
+	for i, table := range tables {
+		if i > 0 {
+			_, err = io.WriteString(w, "\n")
+			if err != nil {
+				return err
+			}
+		}
+		_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n")
 		if err != nil {
 			return err
 		}
 		for _, index := range table.Indexes {
-			_, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+"\n\n")
+			_, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+";\n")
 			if err != nil {
 				return err
 			}
@@ -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 {
 				return err
 			}
@@ -506,6 +528,12 @@ func (engine *Engine) Distinct(columns ...string) *Session {
 	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
 func (engine *Engine) Cols(columns ...string) *Session {
 	session := engine.NewSession()
@@ -543,6 +571,13 @@ func (engine *Engine) Omit(columns ...string) *Session {
 	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 (?, ?)"
 func (engine *Engine) In(column string, args ...interface{}) *Session {
 	session := engine.NewSession()
@@ -642,20 +677,20 @@ func (engine *Engine) Having(conditions string) *Session {
 
 func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
 	t := v.Type()
-	engine.mutex.RLock()
+	engine.mutex.Lock()
 	table, ok := engine.Tables[t]
-	engine.mutex.RUnlock()
 	if !ok {
 		table = engine.mapType(v)
-		engine.mutex.Lock()
 		engine.Tables[t] = table
-		if v.CanAddr() {
-			engine.GobRegister(v.Addr().Interface())
-		} else {
-			engine.GobRegister(v.Interface())
+		if engine.Cacher != nil {
+			if v.CanAddr() {
+				engine.GobRegister(v.Addr().Interface())
+			} else {
+				engine.GobRegister(v.Interface())
+			}
 		}
-		engine.mutex.Unlock()
 	}
+	engine.mutex.Unlock()
 	return table
 }
 
@@ -691,33 +726,24 @@ func (engine *Engine) newTable() *core.Table {
 	return table
 }
 
+type TableName interface {
+	TableName() string
+}
+
 func (engine *Engine) mapType(v reflect.Value) *core.Table {
 	t := v.Type()
 	table := engine.newTable()
-	method := v.MethodByName("TableName")
-	if !method.IsValid() {
-		if v.CanAddr() {
-			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 == "" {
+	if tb, ok := v.Interface().(TableName); ok {
+		table.Name = tb.TableName()
+	} else {
 		table.Name = engine.TableMapper.Obj2Table(t.Name())
 	}
+
 	table.Type = t
 
 	var idFieldColName string
 	var err error
-
-	hasCacheTag := false
-	hasNoCacheTag := false
+	var hasCacheTag, hasNoCacheTag bool
 
 	for i := 0; i < t.NumField(); i++ {
 		tag := t.Field(i).Tag
@@ -730,7 +756,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
 		if ormTagStr != "" {
 			col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false,
 				IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)}
-			tags := strings.Split(ormTagStr, " ")
+			tags := splitTag(ormTagStr)
 
 			if len(tags) > 0 {
 				if tags[0] == "-" {
@@ -804,6 +830,16 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
 					case k == "VERSION":
 						col.IsVersion = true
 						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":
 						col.IsUpdated = true
 					case k == "DELETED":
@@ -1112,7 +1148,7 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 				session := engine.NewSession()
 				session.Statement.RefTable = table
 				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 {
 					return err
 				}
@@ -1387,7 +1423,6 @@ var (
 )
 
 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()
 		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{}) {
+	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 {
 		return t
 	}
+	if tz != nil {
+		t = engine.TZTime(t)
+	}
 	switch sqlTypeName {
 	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]
 	case core.Date:
-		v = engine.TZTime(t).Format("2006-01-02")
+		v = t.Format("2006-01-02")
 	case core.DateTime, core.TimeStamp:
 		if engine.dialect.DBType() == "ql" {
-			v = engine.TZTime(t)
+			v = t
 		} 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 {
-			v = engine.TZTime(t).Format("2006-01-02 15:04:05")
+			v = t.Format("2006-01-02 15:04:05")
 		}
 	case core.TimeStampz:
 		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" {
-			v = engine.TZTime(t)
+			v = t
 		} else {
-			v = engine.TZTime(t).Format(time.RFC3339Nano)
+			v = t.Format(time.RFC3339Nano)
 		}
 	case core.BigInt, core.Int:
-		v = engine.TZTime(t).Unix()
+		v = t.Unix()
 	default:
-		v = engine.TZTime(t)
+		v = t
 	}
 	return
 }

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/error.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (

BIN
Godeps/_workspace/src/github.com/go-xorm/xorm/examples/goroutine.db-journal


+ 2 - 2
Godeps/_workspace/src/github.com/go-xorm/xorm/examples/goroutine.go

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

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/goracle_driver.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (

+ 71 - 2
Godeps/_workspace/src/github.com/go-xorm/xorm/helpers.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -11,6 +15,30 @@ import (
 	"github.com/go-xorm/core"
 )
 
+func splitTag(tag string) (tags []string) {
+	tag = strings.TrimSpace(tag)
+	var hasQuote = false
+	var lastIdx = 0
+	for i, t := range tag {
+		if t == '\'' {
+			hasQuote = !hasQuote
+		} else if t == ' ' {
+			if lastIdx < i && !hasQuote {
+				tags = append(tags, strings.TrimSpace(tag[lastIdx:i]))
+				lastIdx = i + 1
+			}
+		}
+	}
+	if lastIdx < len(tag) {
+		tags = append(tags, strings.TrimSpace(tag[lastIdx:len(tag)]))
+	}
+	return
+}
+
+type zeroable interface {
+	IsZero() bool
+}
+
 func isZero(k interface{}) bool {
 	switch k.(type) {
 	case int:
@@ -33,12 +61,41 @@ func isZero(k interface{}) bool {
 		return k.(uint32) == 0
 	case uint64:
 		return k.(uint64) == 0
+	case float32:
+		return k.(float32) == 0
+	case float64:
+		return k.(float64) == 0
+	case bool:
+		return k.(bool) == false
 	case string:
 		return k.(string) == ""
+	case zeroable:
+		return k.(zeroable).IsZero()
 	}
 	return false
 }
 
+func int64ToInt(id int64, k reflect.Kind) interface{} {
+	var v interface{} = id
+	switch k {
+	case reflect.Int16:
+		v = int16(id)
+	case reflect.Int32:
+		v = int32(id)
+	case reflect.Int:
+		v = int(id)
+	case reflect.Uint16:
+		v = uint16(id)
+	case reflect.Uint32:
+		v = uint32(id)
+	case reflect.Uint64:
+		v = uint64(id)
+	case reflect.Uint:
+		v = uint(id)
+	}
+	return v
+}
+
 func isPKZero(pk core.PK) bool {
 	for _, k := range pk {
 		if isZero(k) {
@@ -48,6 +105,10 @@ func isPKZero(pk core.PK) bool {
 	return false
 }
 
+func equalNoCase(s1, s2 string) bool {
+	return strings.ToLower(s1) == strings.ToLower(s2)
+}
+
 func indexNoCase(s, sep string) int {
 	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:
-		if aa == core.TimeType {
-			str = rawValue.Interface().(time.Time).Format(time.RFC3339Nano)
+		if aa.ConvertibleTo(core.TimeType) {
+			str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano)
 		} else {
 			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 {
 			val, t := session.Engine.NowTime2(col.SQLType.Name)
 			args = append(args, val)

+ 22 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/helpers_test.go

@@ -0,0 +1,22 @@
+package xorm
+
+import "testing"
+
+func TestSplitTag(t *testing.T) {
+	var cases = []struct {
+		tag  string
+		tags []string
+	}{
+		{"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}},
+		{"TEXT", []string{"TEXT"}},
+		{"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}},
+		{"json  binary", []string{"json", "binary"}},
+	}
+
+	for _, kase := range cases {
+		tags := splitTag(kase.tag)
+		if !sliceEq(tags, kase.tags) {
+			t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags)
+		}
+	}
+}

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/logger.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (

+ 4 - 1
Godeps/_workspace/src/github.com/go-xorm/xorm/lru_cacher.go

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

+ 4 - 1
Godeps/_workspace/src/github.com/go-xorm/xorm/memroy_store.go → Godeps/_workspace/src/github.com/go-xorm/xorm/memory_store.go

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

+ 16 - 3
Godeps/_workspace/src/github.com/go-xorm/xorm/mssql_dialect.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -216,6 +220,11 @@ func (db *mssql) SqlType(c *core.Column) string {
 	switch t := c.SQLType.Name; t {
 	case core.Bool:
 		res = core.TinyInt
+		if c.Default == "true" {
+			c.Default = "1"
+		} else if c.Default == "false" {
+			c.Default = "0"
+		}
 	case core.Serial:
 		c.IsAutoIncrement = true
 		c.IsPrimaryKey = true
@@ -238,7 +247,7 @@ func (db *mssql) SqlType(c *core.Column) string {
 		c.Length = 7
 	case core.MediumInt:
 		res = core.Int
-	case core.MediumText, core.TinyText, core.LongText:
+	case core.MediumText, core.TinyText, core.LongText, core.Json:
 		res = core.Text
 	case core.Double:
 		res = core.Real
@@ -311,10 +320,10 @@ func (db *mssql) IndexCheckSql(tableName, idxName string) (string, []interface{}
 	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" = ?`
 
-	return db.HasRecords(query, tableName, col.Name)
+	return db.HasRecords(query, tableName, colName)
 }
 
 func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
@@ -500,6 +509,10 @@ func (db *mssql) CreateTableSql(table *core.Table, tableName, storeEngine, chars
 	return sql
 }
 
+func (db *mssql) ForUpdateSql(query string) string {
+	return query
+}
+
 func (db *mssql) Filters() []core.Filter {
 	return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
 }

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/mymysql_driver.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (

+ 6 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_dialect.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -221,6 +225,8 @@ func (db *mysql) SqlType(c *core.Column) string {
 	case core.Uuid:
 		res = core.Varchar
 		c.Length = 40
+	case core.Json:
+		res = core.Text
 	default:
 		res = t
 	}

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_driver.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/oci8_driver.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/odbc_driver.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (

+ 9 - 3
Godeps/_workspace/src/github.com/go-xorm/xorm/oracle_dialect.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -518,7 +522,7 @@ func (db *oracle) SqlType(c *core.Column) string {
 		res = "TIMESTAMP WITH TIME ZONE"
 	case core.Float, core.Double, core.Numeric, core.Decimal:
 		res = "NUMBER"
-	case core.Text, core.MediumText, core.LongText:
+	case core.Text, core.MediumText, core.LongText, core.Json:
 		res = "CLOB"
 	case core.Char, core.Varchar, core.TinyText:
 		res = "VARCHAR2"
@@ -661,8 +665,8 @@ func (db *oracle) MustDropTable(tableName string) error {
 		" AND column_name = ?", args
 }*/
 
-func (db *oracle) IsColumnExist(tableName string, col *core.Column) (bool, error) {
-	args := []interface{}{tableName, col.Name}
+func (db *oracle) IsColumnExist(tableName, colName string) (bool, error) {
+	args := []interface{}{tableName, colName}
 	query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = :1" +
 		" AND column_name = :2"
 	rows, err := db.DB().Query(query, args...)
@@ -740,6 +744,8 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum
 		switch dt {
 		case "VARCHAR2":
 			col.SQLType = core.SQLType{core.Varchar, len1, len2}
+		case "NVARCHAR2":
+			col.SQLType = core.SQLType{core.NVarchar, len1, len2}
 		case "TIMESTAMP WITH TIME ZONE":
 			col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
 		case "NUMBER":

+ 9 - 4
Godeps/_workspace/src/github.com/go-xorm/xorm/postgres_dialect.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -892,8 +896,8 @@ func (db *postgres) DropIndexSql(tableName string, index *core.Index) string {
 	return fmt.Sprintf("DROP INDEX %v", quote(idxName))
 }
 
-func (db *postgres) IsColumnExist(tableName string, col *core.Column) (bool, error) {
-	args := []interface{}{tableName, col.Name}
+func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) {
+	args := []interface{}{tableName, colName}
 	query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" +
 		" AND column_name = $2"
 	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) {
-	args := []interface{}{tableName}
+	pgSchema := "public"
+	args := []interface{}{tableName,pgSchema}
 	s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix ,
     CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey,
     CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey
@@ -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_class AS g ON p.confrelid = g.oid
     LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name
-WHERE c.relkind = 'r'::char AND c.relname = $1 AND f.attnum > 0 ORDER BY f.attnum;`
+WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;`
 
 	rows, err := db.DB().Query(s, args...)
 	if db.Logger != nil {

+ 6 - 2
Godeps/_workspace/src/github.com/go-xorm/xorm/pq_driver.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -37,7 +41,7 @@ func parseURL(connstr string) (string, error) {
 		return "", err
 	}
 
-	if u.Scheme != "postgres" {
+	if u.Scheme != "postgresql" && u.Scheme != "postgres" {
 		return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
 	}
 
@@ -99,7 +103,7 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
 	db := &core.Uri{DbType: core.POSTGRES}
 	o := make(values)
 	var err error
-	if strings.HasPrefix(dataSourceName, "postgres://") {
+	if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
 		dataSourceName, err = parseURL(dataSourceName)
 		if err != nil {
 			return nil, err

+ 8 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/processors.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 // Executed before an object is initially persisted to the database
@@ -19,6 +23,10 @@ type BeforeSetProcessor interface {
 	BeforeSet(string, Cell)
 }
 
+type AfterSetProcessor interface {
+	AfterSet(string, Cell)
+}
+
 // !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations
 //// Executed before an object is validated
 //type BeforeValidateProcessor interface {

+ 5 - 1
Godeps/_workspace/src/github.com/go-xorm/xorm/rows.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -41,7 +45,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
 		sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable)
 	}
 
-	rows.session.Engine.logSQL(sqlStr, args)
+	rows.session.saveLastSQL(sqlStr, args)
 	var err error
 	rows.stmt, err = rows.session.DB().Prepare(sqlStr)
 	if err != nil {

Файловите разлики са ограничени, защото са твърде много
+ 380 - 212
Godeps/_workspace/src/github.com/go-xorm/xorm/session.go


+ 25 - 5
Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_dialect.go

@@ -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
 
 import (
 	"database/sql"
 	"errors"
 	"fmt"
+	"regexp"
 	"strings"
 
 	"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 {
 	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:
 		return core.DateTime
 	case core.TimeStampz:
 		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
-	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
 	case core.Float, core.Double, 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))
 }
 
+func (db *sqlite3) ForUpdateSql(query string) string {
+	return query
+}
+
 /*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
 	args := []interface{}{tableName}
 	sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
 	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}
-	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...)
 	if db.Logger != nil {
 		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, "(")
 	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)
 	colSeq := make([]string, 0)
 	for _, colStr := range colCreates {
+		reg = regexp.MustCompile(`,\s`)
+		colStr = reg.ReplaceAllString(colStr, ",")
 		fields := strings.Fields(strings.TrimSpace(colStr))
 		col := new(core.Column)
 		col.Indexes = make(map[string]bool)

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_driver.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (

+ 318 - 309
Godeps/_workspace/src/github.com/go-xorm/xorm/statement.go

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

+ 4 - 0
Godeps/_workspace/src/github.com/go-xorm/xorm/syslogger.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 // +build !windows,!nacl,!plan9
 
 package xorm

+ 6 - 2
Godeps/_workspace/src/github.com/go-xorm/xorm/xorm.go

@@ -1,3 +1,7 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
 package xorm
 
 import (
@@ -13,7 +17,7 @@ import (
 )
 
 const (
-	Version string = "0.4.2.0225"
+	Version string = "0.4.5.0204"
 )
 
 func regDrvsNDialects() bool {
@@ -35,7 +39,7 @@ func regDrvsNDialects() bool {
 	for driverName, v := range providedDrvsNDialects {
 		if driver := core.QueryDriver(driverName); driver == nil {
 			core.RegisterDriver(driverName, v.getDriver())
-			core.RegisterDialect(v.dbType, v.getDialect())
+			core.RegisterDialect(v.dbType, v.getDialect)
 		}
 	}
 	return true

Някои файлове не бяха показани, защото твърде много файлове са промени