Explorar o código

Add context support

* add context support

* fix config

* fix circle config

* golang test to go1.10

* fix bench tests

* Add some missing MS SQL Server types: NChar, SmallDateTime, Money, SmallMoney
xormplus %!s(int64=6) %!d(string=hai) anos
pai
achega
da7907271e
Modificáronse 21 ficheiros con 484 adicións e 239 borrados
  1. 4 0
      cache.go
  2. 9 2
      column.go
  3. 4 0
      converstion.go
  4. 51 229
      db.go
  5. 24 5
      db_test.go
  6. 4 0
      dialect.go
  7. 4 0
      driver.go
  8. 4 0
      error.go
  9. 4 0
      filter.go
  10. 4 0
      ilogger.go
  11. 4 0
      index.go
  12. 4 0
      mapper.go
  13. 4 0
      mapper_test.go
  14. 4 0
      pk.go
  15. 4 0
      pk_test.go
  16. 4 0
      rows.go
  17. 11 0
      scan.go
  18. 165 0
      stmt.go
  19. 4 0
      table.go
  20. 153 0
      tx.go
  21. 15 3
      type.go

+ 4 - 0
cache.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 9 - 2
column.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (
@@ -41,6 +45,7 @@ type Column struct {
 	Comment         string
 }
 
+// NewColumn creates a new column
 func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column {
 	return &Column{
 		Name:            name,
@@ -66,7 +71,7 @@ func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable
 	}
 }
 
-// generate column description string according dialect
+// String generate column description string according dialect
 func (col *Column) String(d Dialect) string {
 	sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "
 
@@ -94,6 +99,7 @@ func (col *Column) String(d Dialect) string {
 	return sql
 }
 
+// StringNoPk generate column description string according dialect without primary keys
 func (col *Column) StringNoPk(d Dialect) string {
 	sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "
 
@@ -114,12 +120,13 @@ func (col *Column) StringNoPk(d Dialect) string {
 	return sql
 }
 
-// return col's filed of struct's value
+// ValueOf returns column's filed of struct's value
 func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) {
 	dataStruct := reflect.Indirect(reflect.ValueOf(bean))
 	return col.ValueOfV(&dataStruct)
 }
 
+// ValueOfV returns column's filed of struct's value accept reflevt value
 func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) {
 	var fieldValue reflect.Value
 	fieldPath := strings.Split(col.FieldName, ".")

+ 4 - 0
converstion.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 // Conversion is an interface. A type implements Conversion will according

+ 51 - 229
db.go

@@ -1,9 +1,13 @@
+// Copyright 2019 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 core
 
 import (
+	"context"
 	"database/sql"
 	"database/sql/driver"
-	"errors"
 	"fmt"
 	"reflect"
 	"regexp"
@@ -68,6 +72,7 @@ type cacheStruct struct {
 	idx   int
 }
 
+// DB is a wrap of sql.DB with extra contents
 type DB struct {
 	*sql.DB
 	Mapper            IMapper
@@ -75,6 +80,7 @@ type DB struct {
 	reflectCacheMutex sync.RWMutex
 }
 
+// Open opens a database
 func Open(driverName, dataSourceName string) (*DB, error) {
 	db, err := sql.Open(driverName, dataSourceName)
 	if err != nil {
@@ -87,6 +93,7 @@ func Open(driverName, dataSourceName string) (*DB, error) {
 	}, nil
 }
 
+// FromDB creates a DB from a sql.DB
 func FromDB(db *sql.DB) *DB {
 	return &DB{
 		DB:           db,
@@ -108,8 +115,9 @@ func (db *DB) reflectNew(typ reflect.Type) reflect.Value {
 	return cs.value.Index(cs.idx).Addr()
 }
 
-func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
-	rows, err := db.DB.Query(query, args...)
+// QueryContext overwrites sql.DB.QueryContext
+func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
+	rows, err := db.DB.QueryContext(ctx, query, args...)
 	if err != nil {
 		if rows != nil {
 			rows.Close()
@@ -119,161 +127,69 @@ func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
 	return &Rows{rows, db}, nil
 }
 
-func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) {
-	query, args, err := MapToSlice(query, mp)
-	if err != nil {
-		return nil, err
-	}
-	return db.Query(query, args...)
+// Query overwrites sql.DB.Query
+func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
+	return db.QueryContext(context.Background(), query, args...)
 }
 
-func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) {
-	query, args, err := StructToSlice(query, st)
+func (db *DB) QueryMapContext(ctx context.Context, query string, mp interface{}) (*Rows, error) {
+	query, args, err := MapToSlice(query, mp)
 	if err != nil {
 		return nil, err
 	}
-	return db.Query(query, args...)
+	return db.QueryContext(ctx, query, args...)
 }
 
-func (db *DB) QueryRow(query string, args ...interface{}) *Row {
-	rows, err := db.Query(query, args...)
-	if err != nil {
-		return &Row{nil, err}
-	}
-	return &Row{rows, nil}
-}
-
-func (db *DB) QueryRowMap(query string, mp interface{}) *Row {
-	query, args, err := MapToSlice(query, mp)
-	if err != nil {
-		return &Row{nil, err}
-	}
-	return db.QueryRow(query, args...)
+func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) {
+	return db.QueryMapContext(context.Background(), query, mp)
 }
 
-func (db *DB) QueryRowStruct(query string, st interface{}) *Row {
+func (db *DB) QueryStructContext(ctx context.Context, query string, st interface{}) (*Rows, error) {
 	query, args, err := StructToSlice(query, st)
-	if err != nil {
-		return &Row{nil, err}
-	}
-	return db.QueryRow(query, args...)
-}
-
-type Stmt struct {
-	*sql.Stmt
-	db    *DB
-	names map[string]int
-}
-
-func (db *DB) Prepare(query string) (*Stmt, error) {
-	names := make(map[string]int)
-	var i int
-	query = re.ReplaceAllStringFunc(query, func(src string) string {
-		names[src[1:]] = i
-		i += 1
-		return "?"
-	})
-
-	stmt, err := db.DB.Prepare(query)
 	if err != nil {
 		return nil, err
 	}
-	return &Stmt{stmt, db, names}, nil
-}
-
-func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) {
-	vv := reflect.ValueOf(mp)
-	if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
-		return nil, errors.New("mp should be a map's pointer")
-	}
-
-	args := make([]interface{}, len(s.names))
-	for k, i := range s.names {
-		args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
-	}
-	return s.Stmt.Exec(args...)
+	return db.QueryContext(ctx, query, args...)
 }
 
-func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) {
-	vv := reflect.ValueOf(st)
-	if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
-		return nil, errors.New("mp should be a map's pointer")
-	}
-
-	args := make([]interface{}, len(s.names))
-	for k, i := range s.names {
-		args[i] = vv.Elem().FieldByName(k).Interface()
-	}
-	return s.Stmt.Exec(args...)
+func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) {
+	return db.QueryStructContext(context.Background(), query, st)
 }
 
-func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
-	rows, err := s.Stmt.Query(args...)
+func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row {
+	rows, err := db.QueryContext(ctx, query, args...)
 	if err != nil {
-		return nil, err
+		return &Row{nil, err}
 	}
-	return &Rows{rows, s.db}, nil
+	return &Row{rows, nil}
 }
 
-func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) {
-	vv := reflect.ValueOf(mp)
-	if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
-		return nil, errors.New("mp should be a map's pointer")
-	}
-
-	args := make([]interface{}, len(s.names))
-	for k, i := range s.names {
-		args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
-	}
-
-	return s.Query(args...)
+func (db *DB) QueryRow(query string, args ...interface{}) *Row {
+	return db.QueryRowContext(context.Background(), query, args...)
 }
 
-func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) {
-	vv := reflect.ValueOf(st)
-	if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
-		return nil, errors.New("mp should be a map's pointer")
-	}
-
-	args := make([]interface{}, len(s.names))
-	for k, i := range s.names {
-		args[i] = vv.Elem().FieldByName(k).Interface()
+func (db *DB) QueryRowMapContext(ctx context.Context, query string, mp interface{}) *Row {
+	query, args, err := MapToSlice(query, mp)
+	if err != nil {
+		return &Row{nil, err}
 	}
-
-	return s.Query(args...)
+	return db.QueryRowContext(ctx, query, args...)
 }
 
-func (s *Stmt) QueryRow(args ...interface{}) *Row {
-	rows, err := s.Query(args...)
-	return &Row{rows, err}
+func (db *DB) QueryRowMap(query string, mp interface{}) *Row {
+	return db.QueryRowMapContext(context.Background(), query, mp)
 }
 
-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")}
-	}
-
-	args := make([]interface{}, len(s.names))
-	for k, i := range s.names {
-		args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
+func (db *DB) QueryRowStructContext(ctx context.Context, query string, st interface{}) *Row {
+	query, args, err := StructToSlice(query, st)
+	if err != nil {
+		return &Row{nil, err}
 	}
-
-	return s.QueryRow(args...)
+	return db.QueryRowContext(ctx, query, args...)
 }
 
-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")}
-	}
-
-	args := make([]interface{}, len(s.names))
-	for k, i := range s.names {
-		args[i] = vv.Elem().FieldByName(k).Interface()
-	}
-
-	return s.QueryRow(args...)
+func (db *DB) QueryRowStruct(query string, st interface{}) *Row {
+	return db.QueryRowStructContext(context.Background(), query, st)
 }
 
 var (
@@ -282,120 +198,26 @@ var (
 
 // insert into (name) values (?)
 // insert into (name) values (?name)
-func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) {
-	query, args, err := MapToSlice(query, mp)
-	if err != nil {
-		return nil, err
-	}
-	return db.DB.Exec(query, args...)
-}
-
-func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) {
-	query, args, err := StructToSlice(query, st)
-	if err != nil {
-		return nil, err
-	}
-	return db.DB.Exec(query, args...)
-}
-
-type EmptyScanner struct {
-}
-
-func (EmptyScanner) Scan(src interface{}) error {
-	return nil
-}
-
-type Tx struct {
-	*sql.Tx
-	db *DB
-}
-
-func (db *DB) Begin() (*Tx, error) {
-	tx, err := db.DB.Begin()
-	if err != nil {
-		return nil, err
-	}
-	return &Tx{tx, db}, nil
-}
-
-func (tx *Tx) Prepare(query string) (*Stmt, error) {
-	names := make(map[string]int)
-	var i int
-	query = re.ReplaceAllStringFunc(query, func(src string) string {
-		names[src[1:]] = i
-		i += 1
-		return "?"
-	})
-
-	stmt, err := tx.Tx.Prepare(query)
-	if err != nil {
-		return nil, err
-	}
-	return &Stmt{stmt, tx.db, names}, nil
-}
-
-func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
-	// TODO:
-	return stmt
-}
-
-func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) {
+func (db *DB) ExecMapContext(ctx context.Context, query string, mp interface{}) (sql.Result, error) {
 	query, args, err := MapToSlice(query, mp)
 	if err != nil {
 		return nil, err
 	}
-	return tx.Tx.Exec(query, args...)
-}
-
-func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) {
-	query, args, err := StructToSlice(query, st)
-	if err != nil {
-		return nil, err
-	}
-	return tx.Tx.Exec(query, args...)
-}
-
-func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
-	rows, err := tx.Tx.Query(query, args...)
-	if err != nil {
-		return nil, err
-	}
-	return &Rows{rows, tx.db}, nil
+	return db.DB.ExecContext(ctx, query, args...)
 }
 
-func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) {
-	query, args, err := MapToSlice(query, mp)
-	if err != nil {
-		return nil, err
-	}
-	return tx.Query(query, args...)
+func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) {
+	return db.ExecMapContext(context.Background(), query, mp)
 }
 
-func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) {
+func (db *DB) ExecStructContext(ctx context.Context, query string, st interface{}) (sql.Result, error) {
 	query, args, err := StructToSlice(query, st)
 	if err != nil {
 		return nil, err
 	}
-	return tx.Query(query, args...)
-}
-
-func (tx *Tx) QueryRow(query string, args ...interface{}) *Row {
-	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}
-	}
-	return tx.QueryRow(query, args...)
+	return db.DB.ExecContext(ctx, query, args...)
 }
 
-func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row {
-	query, args, err := StructToSlice(query, st)
-	if err != nil {
-		return &Row{nil, err}
-	}
-	return tx.QueryRow(query, args...)
+func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) {
+	return db.ExecStructContext(context.Background(), query, st)
 }

+ 24 - 5
db_test.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (
@@ -229,8 +233,15 @@ func BenchmarkSliceInterfaceQuery(b *testing.B) {
 				b.Error(err)
 			}
 			b.Log(slice)
-			if *slice[1].(*string) != "xlw" {
-				b.Error(errors.New("name should be xlw"))
+			switch slice[1].(type) {
+			case *string:
+				if *slice[1].(*string) != "xlw" {
+					b.Error(errors.New("name should be xlw"))
+				}
+			case []byte:
+				if string(slice[1].([]byte)) != "xlw" {
+					b.Error(errors.New("name should be xlw"))
+				}
 			}
 		}
 
@@ -376,9 +387,17 @@ func BenchmarkMapInterfaceQuery(b *testing.B) {
 			if err != nil {
 				b.Error(err)
 			}
-			if m["name"].(string) != "xlw" {
-				b.Log(m)
-				b.Error(errors.New("name should be xlw"))
+			switch m["name"].(type) {
+			case string:
+				if m["name"].(string) != "xlw" {
+					b.Log(m)
+					b.Error(errors.New("name should be xlw"))
+				}
+			case []byte:
+				if string(m["name"].([]byte)) != "xlw" {
+					b.Log(m)
+					b.Error(errors.New("name should be xlw"))
+				}
 			}
 		}
 

+ 4 - 0
dialect.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 4 - 0
driver.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 type Driver interface {

+ 4 - 0
error.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import "errors"

+ 4 - 0
filter.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 4 - 0
ilogger.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 type LogLevel int

+ 4 - 0
index.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 4 - 0
mapper.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 4 - 0
mapper_test.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 4 - 0
pk.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 4 - 0
pk_test.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 4 - 0
rows.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 11 - 0
scan.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (
@@ -53,3 +57,10 @@ func convertTime(dest *NullTime, src interface{}) error {
 	}
 	return nil
 }
+
+type EmptyScanner struct {
+}
+
+func (EmptyScanner) Scan(src interface{}) error {
+	return nil
+}

+ 165 - 0
stmt.go

@@ -0,0 +1,165 @@
+// Copyright 2019 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 core
+
+import (
+	"context"
+	"database/sql"
+	"errors"
+	"reflect"
+)
+
+type Stmt struct {
+	*sql.Stmt
+	db    *DB
+	names map[string]int
+}
+
+func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
+	names := make(map[string]int)
+	var i int
+	query = re.ReplaceAllStringFunc(query, func(src string) string {
+		names[src[1:]] = i
+		i += 1
+		return "?"
+	})
+
+	stmt, err := db.DB.PrepareContext(ctx, query)
+	if err != nil {
+		return nil, err
+	}
+	return &Stmt{stmt, db, names}, nil
+}
+
+func (db *DB) Prepare(query string) (*Stmt, error) {
+	return db.PrepareContext(context.Background(), query)
+}
+
+func (s *Stmt) ExecMapContext(ctx context.Context, mp interface{}) (sql.Result, error) {
+	vv := reflect.ValueOf(mp)
+	if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
+		return nil, errors.New("mp should be a map's pointer")
+	}
+
+	args := make([]interface{}, len(s.names))
+	for k, i := range s.names {
+		args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
+	}
+	return s.Stmt.ExecContext(ctx, args...)
+}
+
+func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) {
+	return s.ExecMapContext(context.Background(), mp)
+}
+
+func (s *Stmt) ExecStructContext(ctx context.Context, st interface{}) (sql.Result, error) {
+	vv := reflect.ValueOf(st)
+	if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
+		return nil, errors.New("mp should be a map's pointer")
+	}
+
+	args := make([]interface{}, len(s.names))
+	for k, i := range s.names {
+		args[i] = vv.Elem().FieldByName(k).Interface()
+	}
+	return s.Stmt.ExecContext(ctx, args...)
+}
+
+func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) {
+	return s.ExecStructContext(context.Background(), st)
+}
+
+func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*Rows, error) {
+	rows, err := s.Stmt.QueryContext(ctx, args...)
+	if err != nil {
+		return nil, err
+	}
+	return &Rows{rows, s.db}, nil
+}
+
+func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
+	return s.QueryContext(context.Background(), args...)
+}
+
+func (s *Stmt) QueryMapContext(ctx context.Context, mp interface{}) (*Rows, error) {
+	vv := reflect.ValueOf(mp)
+	if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
+		return nil, errors.New("mp should be a map's pointer")
+	}
+
+	args := make([]interface{}, len(s.names))
+	for k, i := range s.names {
+		args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
+	}
+
+	return s.QueryContext(ctx, args...)
+}
+
+func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) {
+	return s.QueryMapContext(context.Background(), mp)
+}
+
+func (s *Stmt) QueryStructContext(ctx context.Context, st interface{}) (*Rows, error) {
+	vv := reflect.ValueOf(st)
+	if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
+		return nil, errors.New("mp should be a map's pointer")
+	}
+
+	args := make([]interface{}, len(s.names))
+	for k, i := range s.names {
+		args[i] = vv.Elem().FieldByName(k).Interface()
+	}
+
+	return s.Query(args...)
+}
+
+func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) {
+	return s.QueryStructContext(context.Background(), st)
+}
+
+func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row {
+	rows, err := s.QueryContext(ctx, args...)
+	return &Row{rows, err}
+}
+
+func (s *Stmt) QueryRow(args ...interface{}) *Row {
+	return s.QueryRowContext(context.Background(), args...)
+}
+
+func (s *Stmt) QueryRowMapContext(ctx context.Context, 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")}
+	}
+
+	args := make([]interface{}, len(s.names))
+	for k, i := range s.names {
+		args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
+	}
+
+	return s.QueryRowContext(ctx, args...)
+}
+
+func (s *Stmt) QueryRowMap(mp interface{}) *Row {
+	return s.QueryRowMapContext(context.Background(), mp)
+}
+
+func (s *Stmt) QueryRowStructContext(ctx context.Context, 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")}
+	}
+
+	args := make([]interface{}, len(s.names))
+	for k, i := range s.names {
+		args[i] = vv.Elem().FieldByName(k).Interface()
+	}
+
+	return s.QueryRowContext(ctx, args...)
+}
+
+func (s *Stmt) QueryRowStruct(st interface{}) *Row {
+	return s.QueryRowStructContext(context.Background(), st)
+}

+ 4 - 0
table.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (

+ 153 - 0
tx.go

@@ -0,0 +1,153 @@
+// Copyright 2019 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 core
+
+import (
+	"context"
+	"database/sql"
+)
+
+type Tx struct {
+	*sql.Tx
+	db *DB
+}
+
+func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
+	tx, err := db.DB.BeginTx(ctx, opts)
+	if err != nil {
+		return nil, err
+	}
+	return &Tx{tx, db}, nil
+}
+
+func (db *DB) Begin() (*Tx, error) {
+	tx, err := db.DB.Begin()
+	if err != nil {
+		return nil, err
+	}
+	return &Tx{tx, db}, nil
+}
+
+func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
+	names := make(map[string]int)
+	var i int
+	query = re.ReplaceAllStringFunc(query, func(src string) string {
+		names[src[1:]] = i
+		i += 1
+		return "?"
+	})
+
+	stmt, err := tx.Tx.PrepareContext(ctx, query)
+	if err != nil {
+		return nil, err
+	}
+	return &Stmt{stmt, tx.db, names}, nil
+}
+
+func (tx *Tx) Prepare(query string) (*Stmt, error) {
+	return tx.PrepareContext(context.Background(), query)
+}
+
+func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
+	stmt.Stmt = tx.Tx.StmtContext(ctx, stmt.Stmt)
+	return stmt
+}
+
+func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
+	return tx.StmtContext(context.Background(), stmt)
+}
+
+func (tx *Tx) ExecMapContext(ctx context.Context, query string, mp interface{}) (sql.Result, error) {
+	query, args, err := MapToSlice(query, mp)
+	if err != nil {
+		return nil, err
+	}
+	return tx.Tx.ExecContext(ctx, query, args...)
+}
+
+func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) {
+	return tx.ExecMapContext(context.Background(), query, mp)
+}
+
+func (tx *Tx) ExecStructContext(ctx context.Context, query string, st interface{}) (sql.Result, error) {
+	query, args, err := StructToSlice(query, st)
+	if err != nil {
+		return nil, err
+	}
+	return tx.Tx.ExecContext(ctx, query, args...)
+}
+
+func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) {
+	return tx.ExecStructContext(context.Background(), query, st)
+}
+
+func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
+	rows, err := tx.Tx.QueryContext(ctx, query, args...)
+	if err != nil {
+		return nil, err
+	}
+	return &Rows{rows, tx.db}, nil
+}
+
+func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
+	return tx.QueryContext(context.Background(), query, args...)
+}
+
+func (tx *Tx) QueryMapContext(ctx context.Context, query string, mp interface{}) (*Rows, error) {
+	query, args, err := MapToSlice(query, mp)
+	if err != nil {
+		return nil, err
+	}
+	return tx.QueryContext(ctx, query, args...)
+}
+
+func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) {
+	return tx.QueryMapContext(context.Background(), query, mp)
+}
+
+func (tx *Tx) QueryStructContext(ctx context.Context, query string, st interface{}) (*Rows, error) {
+	query, args, err := StructToSlice(query, st)
+	if err != nil {
+		return nil, err
+	}
+	return tx.QueryContext(ctx, query, args...)
+}
+
+func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) {
+	return tx.QueryStructContext(context.Background(), query, st)
+}
+
+func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row {
+	rows, err := tx.QueryContext(ctx, query, args...)
+	return &Row{rows, err}
+}
+
+func (tx *Tx) QueryRow(query string, args ...interface{}) *Row {
+	return tx.QueryRowContext(context.Background(), query, args...)
+}
+
+func (tx *Tx) QueryRowMapContext(ctx context.Context, query string, mp interface{}) *Row {
+	query, args, err := MapToSlice(query, mp)
+	if err != nil {
+		return &Row{nil, err}
+	}
+	return tx.QueryRowContext(ctx, query, args...)
+}
+
+func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row {
+	return tx.QueryRowMapContext(context.Background(), query, mp)
+}
+
+func (tx *Tx) QueryRowStructContext(ctx context.Context, query string, st interface{}) *Row {
+	query, args, err := StructToSlice(query, st)
+	if err != nil {
+		return &Row{nil, err}
+	}
+	return tx.QueryRowContext(ctx, query, args...)
+}
+
+func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row {
+	return tx.QueryRowStructContext(context.Background(), query, st)
+}

+ 15 - 3
type.go

@@ -1,3 +1,7 @@
+// Copyright 2019 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 core
 
 import (
@@ -71,6 +75,7 @@ var (
 
 	Char             = "CHAR"
 	Varchar          = "VARCHAR"
+	NChar            = "NCHAR"
 	NVarchar         = "NVARCHAR"
 	TinyText         = "TINYTEXT"
 	Text             = "TEXT"
@@ -84,12 +89,15 @@ var (
 
 	Date       = "DATE"
 	DateTime   = "DATETIME"
+	SmallDateTime   = "SMALLDATETIME"
 	Time       = "TIME"
 	TimeStamp  = "TIMESTAMP"
 	TimeStampz = "TIMESTAMPZ"
 
 	Decimal = "DECIMAL"
 	Numeric = "NUMERIC"
+	Money   = "MONEY"
+	SmallMoney = "SMALLMONEY"
 
 	Real   = "REAL"
 	Float  = "FLOAT"
@@ -127,6 +135,7 @@ var (
 		Jsonb: TEXT_TYPE,
 
 		Char:       TEXT_TYPE,
+		NChar:      TEXT_TYPE,
 		Varchar:    TEXT_TYPE,
 		NVarchar:   TEXT_TYPE,
 		TinyText:   TEXT_TYPE,
@@ -143,12 +152,15 @@ var (
 		Time:       TIME_TYPE,
 		TimeStamp:  TIME_TYPE,
 		TimeStampz: TIME_TYPE,
+		SmallDateTime:   TIME_TYPE,
 
 		Decimal: NUMERIC_TYPE,
 		Numeric: NUMERIC_TYPE,
 		Real:    NUMERIC_TYPE,
 		Float:   NUMERIC_TYPE,
 		Double:  NUMERIC_TYPE,
+		Money:   NUMERIC_TYPE,
+		SmallMoney: NUMERIC_TYPE,
 
 		Binary:    BLOB_TYPE,
 		VarBinary: BLOB_TYPE,
@@ -295,15 +307,15 @@ func SQLType2Type(st SQLType) reflect.Type {
 		return reflect.TypeOf(float32(1))
 	case Double:
 		return reflect.TypeOf(float64(1))
-	case Char, Varchar, NVarchar, TinyText, Text, NText, MediumText, LongText, Enum, Set, Uuid, Clob, SysName:
+	case Char, NChar, Varchar, NVarchar, TinyText, Text, NText, MediumText, LongText, Enum, Set, Uuid, Clob, SysName:
 		return reflect.TypeOf("")
 	case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary, UniqueIdentifier:
 		return reflect.TypeOf([]byte{})
 	case Bool:
 		return reflect.TypeOf(true)
-	case DateTime, Date, Time, TimeStamp, TimeStampz:
+	case DateTime, Date, Time, TimeStamp, TimeStampz, SmallDateTime:
 		return reflect.TypeOf(c_TIME_DEFAULT)
-	case Decimal, Numeric:
+	case Decimal, Numeric, Money, SmallMoney:
 		return reflect.TypeOf("")
 	default:
 		return reflect.TypeOf("")