Merge branch 'master' of github.com:go-xorm/xorm

This commit is contained in:
Nash Tsai 2014-05-05 17:20:33 +08:00
commit c2ec7bc287
10 changed files with 374 additions and 73 deletions

View File

@ -18,7 +18,7 @@ Xorm is a simple and powerful ORM for Go.
* Query Cache speed up * Query Cache speed up
* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md) * Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/cmd/blob/master/README.md)
* Simple cascade loading support * Simple cascade loading support
@ -123,13 +123,10 @@ Or
Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm)
# Contributors # Contributing
If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md) If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
* [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai)
# LICENSE # LICENSE
BSD License BSD License

View File

@ -124,13 +124,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
请加入QQ群280360085 进行讨论。 请加入QQ群280360085 进行讨论。
# 贡献 ## 贡献
如果您也想为Xorm贡献您的力量请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md) 如果您也想为Xorm贡献您的力量请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
* [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai)
## LICENSE ## LICENSE
BSD License BSD License

View File

@ -58,7 +58,7 @@ engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close() defer engine.Close()
``` ```
Generally, you can only create one engine. Engine supports run on go rutines. You can create many engines for different databases.Generally, you just need create only one engine. Engine supports run on go routines.
xorm supports four drivers now: xorm supports four drivers now:
@ -385,7 +385,7 @@ engine.Cols("age", "name").Update(&user)
* Omit(...string) * Omit(...string)
和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用 和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
```Go ```Go
engine.Cols("age").Update(&user) engine.Omit("age").Update(&user)
// UPDATE user SET name = ? AND department = ? // UPDATE user SET name = ? AND department = ?
``` ```

View File

@ -62,7 +62,7 @@ engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close() defer engine.Close()
``` ```
一般如果只针对一个数据库进行操作只需要创建一个Engine即可。Engine支持在多GoRutine下使用。 你可以创建一个或多个engine, 不过一般如果操作一个数据库只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
xorm当前支持五种驱动四个数据库如下 xorm当前支持五种驱动四个数据库如下
@ -419,7 +419,7 @@ engine.Cols("age", "name").Update(&user)
* Omit(...string) * Omit(...string)
和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用 和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
```Go ```Go
engine.Cols("age").Update(&user) engine.Omit("age").Update(&user)
// UPDATE user SET name = ? AND department = ? // UPDATE user SET name = ? AND department = ?
``` ```

View File

@ -6,6 +6,7 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"reflect" "reflect"
"strconv" "strconv"
@ -34,8 +35,7 @@ type Engine struct {
ShowErr bool ShowErr bool
ShowDebug bool ShowDebug bool
ShowWarn bool ShowWarn bool
//Pool IConnectPool
//Filters []core.Filter
Logger ILogger // io.Writer Logger ILogger // io.Writer
TZLocation *time.Location TZLocation *time.Location
} }
@ -266,6 +266,77 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) {
return tables, nil return tables, nil
} }
func (engine *Engine) DumpAllToFile(fp string) error {
f, err := os.Create(fp)
if err != nil {
return err
}
defer f.Close()
return engine.DumpAll(f)
}
func (engine *Engine) DumpAll(w io.Writer) error {
tables, err := engine.DBMetas()
if err != nil {
return err
}
for _, table := range tables {
_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", "", "")+"\n\n")
if err != nil {
return err
}
for _, index := range table.Indexes {
_, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+"\n\n")
if err != nil {
return err
}
}
rows, err := engine.DB().Query("SELECT * FROM " + engine.Quote(table.Name))
if err != nil {
return err
}
cols, err := rows.Columns()
if err != nil {
return err
}
if len(cols) == 0 {
continue
}
for rows.Next() {
dest := make([]interface{}, len(cols))
err = rows.ScanSlice(&dest)
if err != nil {
return err
}
_, err = io.WriteString(w, "INSERT INTO "+engine.Quote(table.Name)+" ("+engine.Quote(strings.Join(cols, engine.Quote(", ")))+") VALUES (")
if err != nil {
return err
}
var temp string
for _, d := range dest {
if d == nil {
temp += ", NULL"
} else if reflect.TypeOf(d).Kind() == reflect.String {
temp += ", '" + strings.Replace(d.(string), "'", "''", -1) + "'"
} else if reflect.TypeOf(d).Kind() == reflect.Slice {
temp += fmt.Sprintf(", %s", engine.dialect.FormatBytes(d.([]byte)))
} else {
temp += fmt.Sprintf(", %v", d)
}
}
_, err = io.WriteString(w, temp[2:]+");\n\n")
if err != nil {
return err
}
}
}
return nil
}
// use cascade or not // use cascade or not
func (engine *Engine) Cascade(trueOrFalse ...bool) *Session { func (engine *Engine) Cascade(trueOrFalse ...bool) *Session {
session := engine.NewSession() session := engine.NewSession()
@ -456,15 +527,6 @@ func (engine *Engine) autoMap(bean interface{}) *core.Table {
return engine.autoMapType(v) return engine.autoMapType(v)
} }
/*func (engine *Engine) mapType(t reflect.Type) *core.Table {
return mappingTable(t, engine.TableMapper, engine.ColumnMapper, engine.dialect, engine.TagIdentifier)
}*/
/*
func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapper, dialect core.Dialect, tagId string) *core.Table {
table := core.NewEmptyTable()
table.Name = tableMapper.Obj2Table(t.Name())
*/
func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) { func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
if index, ok := table.Indexes[indexName]; ok { if index, ok := table.Indexes[indexName]; ok {
index.AddColumn(col.Name) index.AddColumn(col.Name)
@ -524,18 +586,20 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
if tags[0] == "-" { if tags[0] == "-" {
continue continue
} }
if (strings.ToUpper(tags[0]) == "EXTENDS") && if strings.ToUpper(tags[0]) == "EXTENDS" {
(fieldType.Kind() == reflect.Struct) { fieldValue = reflect.Indirect(fieldValue)
if fieldValue.Kind() == reflect.Struct {
//parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId) //parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
parentTable := engine.mapType(fieldValue) parentTable := engine.mapType(fieldValue)
for _, col := range parentTable.Columns() { for _, col := range parentTable.Columns() {
col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName) col.FieldName = fmt.Sprintf("%v.%v", fieldValue.Type().Name(), col.FieldName)
table.AddColumn(col) table.AddColumn(col)
} }
continue continue
} }
//TODO: warning
}
indexNames := make(map[string]int) indexNames := make(map[string]int)
var isIndex, isUnique bool var isIndex, isUnique bool
@ -701,7 +765,7 @@ func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
rows, err := session.Count(bean) rows, err := session.Count(bean)
return rows > 0, err return rows == 0, err
} }
// If a table is exist // If a table is exist

34
examples/tables.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"fmt"
"os"
"github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("need db path")
return
}
orm, err := xorm.NewEngine("sqlite3", os.Args[1])
if err != nil {
fmt.Println(err)
return
}
defer orm.Close()
orm.ShowSQL = true
tables, err := orm.DBMetas()
if err != nil {
fmt.Println(err)
return
}
for _, table := range tables {
fmt.Println(table.Name)
}
}

View File

@ -15,6 +15,10 @@ type BeforeDeleteProcessor interface {
BeforeDelete() BeforeDelete()
} }
type BeforeSetProcessor interface {
BeforeSet(string, Cell)
}
// !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations // !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations
//// Executed before an object is validated //// Executed before an object is validated
//type BeforeValidateProcessor interface { //type BeforeValidateProcessor interface {

View File

@ -883,7 +883,6 @@ func (session *Session) Rows(bean interface{}) (*Rows, error) {
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct // are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct // map[int64]*Struct
func (session *Session) Iterate(bean interface{}, fun IterFunc) error { func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
rows, err := session.Rows(bean) rows, err := session.Rows(bean)
if err != nil { if err != nil {
return err return err
@ -982,24 +981,6 @@ func (session *Session) Get(bean interface{}) (bool, error) {
} else { } else {
return false, nil return false, nil
} }
// resultsSlice, err := session.query(sqlStr, args...)
// if err != nil {
// return false, err
// }
// if len(resultsSlice) < 1 {
// return false, nil
// }
// err = session.scanMapIntoStruct(bean, resultsSlice[0])
// if err != nil {
// return true, err
// }
// if len(resultsSlice) == 1 {
// return true, nil
// } else {
// return true, errors.New("More than one record")
// }
} }
// Count counts the records. bean's non-empty fields // Count counts the records. bean's non-empty fields
@ -1441,6 +1422,8 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c
return fieldValue return fieldValue
} }
type Cell *interface{}
func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error { func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
dataStruct := rValue(bean) dataStruct := rValue(bean)
if dataStruct.Kind() != reflect.Struct { if dataStruct.Kind() != reflect.Struct {
@ -1449,18 +1432,24 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
table := session.Engine.autoMapType(dataStruct) table := session.Engine.autoMapType(dataStruct)
scanResultContainers := make([]interface{}, len(fields)) scanResults := make([]interface{}, len(fields))
for i := 0; i < len(fields); i++ { for i := 0; i < len(fields); i++ {
var scanResultContainer interface{} var cell interface{}
scanResultContainers[i] = &scanResultContainer scanResults[i] = &cell
} }
if err := rows.Scan(scanResultContainers...); err != nil { if err := rows.Scan(scanResults...); err != nil {
return err return err
} }
b, hasBeforeSet := bean.(BeforeSetProcessor)
for ii, key := range fields { for ii, key := range fields {
if hasBeforeSet {
b.BeforeSet(fields[ii], Cell(scanResults[ii].(*interface{})))
}
if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil { if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil {
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii]))
//if row is null then ignore //if row is null then ignore
if rawValue.Interface() == nil { if rawValue.Interface() == nil {
@ -1468,6 +1457,7 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
continue continue
} }
if fieldValue.CanAddr() {
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
if data, err := value2Bytes(&rawValue); err == nil { if data, err := value2Bytes(&rawValue); err == nil {
structConvert.FromDB(data) structConvert.FromDB(data)
@ -1476,6 +1466,16 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
} }
continue continue
} }
}
if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
if data, err := value2Bytes(&rawValue); err == nil {
structConvert.FromDB(data)
} else {
session.Engine.LogError(err)
}
continue
}
rawValueType := reflect.TypeOf(rawValue.Interface()) rawValueType := reflect.TypeOf(rawValue.Interface())
vv := reflect.ValueOf(rawValue.Interface()) vv := reflect.ValueOf(rawValue.Interface())
@ -2451,6 +2451,16 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
} }
} }
} }
if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok {
data, err := fieldConvert.ToDB()
if err != nil {
return 0, err
} else {
return string(data), nil
}
}
fieldType := fieldValue.Type() fieldType := fieldValue.Type()
k := fieldType.Kind() k := fieldType.Kind()
if k == reflect.Ptr { if k == reflect.Ptr {
@ -2470,11 +2480,6 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
switch k { switch k {
case reflect.Bool: case reflect.Bool:
return fieldValue.Bool(), nil return fieldValue.Bool(), nil
/*if fieldValue.Bool() {
return 1, nil
} else {
return 0, nil
}*/
case reflect.String: case reflect.String:
return fieldValue.String(), nil return fieldValue.String(), nil
case reflect.Struct: case reflect.Struct:
@ -2947,7 +2952,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
session.Statement.RefTable = table session.Statement.RefTable = table
if session.Statement.ColumnStr == "" { if session.Statement.ColumnStr == "" {
colNames, args = buildConditions(session.Engine, table, bean, false, false, colNames, args = buildUpdates(session.Engine, table, bean, false, false,
false, false, session.Statement.allUseBool, session.Statement.useAllCols, false, false, session.Statement.allUseBool, session.Statement.useAllCols,
session.Statement.mustColumnMap) session.Statement.mustColumnMap)
} else { } else {
@ -3314,7 +3319,7 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
} }
if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime {
args = append(args, time.Now()) args = append(args, session.Engine.NowTime(col.SQLType.Name))
} else if col.IsVersion && session.Statement.checkVersion { } else if col.IsVersion && session.Statement.checkVersion {
args = append(args, 1) args = append(args, 1)
} else { } else {

View File

@ -1,6 +1,7 @@
package xorm package xorm
import ( import (
"fmt"
"strings" "strings"
"github.com/go-xorm/core" "github.com/go-xorm/core"
@ -44,6 +45,10 @@ func (db *sqlite3) SqlType(c *core.Column) string {
} }
} }
func (db *sqlite3) FormatBytes(bs []byte) string {
return fmt.Sprintf("X'%x'", bs)
}
func (db *sqlite3) SupportInsertMany() bool { func (db *sqlite3) SupportInsertMany() bool {
return true return true
} }

View File

@ -253,6 +253,197 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
return results return results
}*/ }*/
// Auto generating conditions 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) ([]string, []interface{}) {
colNames := make([]string, 0)
var args = make([]interface{}, 0)
for _, col := range table.Columns() {
if !includeVersion && col.IsVersion {
continue
}
if col.IsCreated {
continue
}
if !includeUpdated && col.IsUpdated {
continue
}
if !includeAutoIncr && col.IsAutoIncrement {
continue
}
//
//fmt.Println(engine.dialect.DBType(), Text)
if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text {
continue
}
fieldValuePtr, err := col.ValueOf(bean)
if err != nil {
engine.LogError(err)
continue
}
fieldValue := *fieldValuePtr
fieldType := reflect.TypeOf(fieldValue.Interface())
requiredField := useAllCols
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
if b {
requiredField = true
} else {
continue
}
}
var val interface{}
if fieldValue.CanAddr() {
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
data, err := structConvert.ToDB()
if err != nil {
engine.LogError(err)
} else {
val = data
}
continue
}
}
if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
data, err := structConvert.ToDB()
if err != nil {
engine.LogError(err)
} else {
val = data
}
continue
}
if fieldType.Kind() == reflect.Ptr {
if fieldValue.IsNil() {
if includeNil {
args = append(args, nil)
colNames = append(colNames, fmt.Sprintf("%v=?", engine.Quote(col.Name)))
}
continue
} else if !fieldValue.IsValid() {
continue
} else {
// dereference ptr type to instance type
fieldValue = fieldValue.Elem()
fieldType = reflect.TypeOf(fieldValue.Interface())
requiredField = true
}
}
switch fieldType.Kind() {
case reflect.Bool:
if allUseBool || requiredField {
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 !requiredField && 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 !requiredField && fieldValue.Int() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Float32, reflect.Float64:
if !requiredField && fieldValue.Float() == 0.0 {
continue
}
val = fieldValue.Interface()
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
if !requiredField && fieldValue.Uint() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Struct:
if fieldType == reflect.TypeOf(time.Now()) {
t := fieldValue.Interface().(time.Time)
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
continue
}
val = engine.FormatTime(col.SQLType.Name, t)
//fmt.Println("-------", t, val, col.Name)
} 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)
if pkField.Int() != 0 {
val = pkField.Interface()
} else {
continue
}
} else {
//TODO: how to handler?
}
} else {
val = fieldValue.Interface()
}
}
case reflect.Array, reflect.Slice, reflect.Map:
if fieldValue == reflect.Zero(fieldType) {
continue
}
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
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()
}
args = append(args, val)
colNames = append(colNames, fmt.Sprintf("%v=?", engine.Quote(col.Name)))
}
return colNames, args
}
// Auto generating conditions according a struct // Auto generating conditions according a struct
func buildConditions(engine *Engine, table *core.Table, bean interface{}, func buildConditions(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool, includeVersion bool, includeUpdated bool, includeNil bool,
@ -283,8 +474,11 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
} }
fieldValue := *fieldValuePtr fieldValue := *fieldValuePtr
fieldType := reflect.TypeOf(fieldValue.Interface()) if fieldValue.Interface() == nil {
continue
}
fieldType := reflect.TypeOf(fieldValue.Interface())
requiredField := useAllCols requiredField := useAllCols
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok { if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
if b { if b {
@ -353,7 +547,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
continue continue
} }
val = engine.FormatTime(col.SQLType.Name, t) val = engine.FormatTime(col.SQLType.Name, t)
fmt.Println("-------", t, val, col.Name) //fmt.Println("-------", t, val, col.Name)
} else { } else {
engine.autoMapType(fieldValue) engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok { if table, ok := engine.Tables[fieldValue.Type()]; ok {
@ -762,11 +956,12 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
statement.ConditionStr = strings.Join(colNames, " AND ") statement.ConditionStr = strings.Join(colNames, " AND ")
statement.BeanArgs = args statement.BeanArgs = args
// count(index fieldname) > count(0) > count(*) // count(index fieldname) > count(0) > count(*)
var id string = "0" // for compitable on kinds of database, just use *
/*var id string = "0"
if len(table.PrimaryKeys) == 1 { if len(table.PrimaryKeys) == 1 {
id = statement.Engine.Quote(table.PrimaryKeys[0]) id = statement.Engine.Quote(statement.TableName()) + "." + statement.Engine.Quote(table.PrimaryKeys[0])
} }*/
return statement.genSelectSql(fmt.Sprintf("COUNT(%v) AS %v", id, statement.Engine.Quote("total"))), append(statement.Params, statement.BeanArgs...) return statement.genSelectSql(fmt.Sprintf("COUNT(*) AS %v", statement.Engine.Quote("total"))), append(statement.Params, statement.BeanArgs...)
} }
func (statement *Statement) genSelectSql(columnStr string) (a string) { func (statement *Statement) genSelectSql(columnStr string) (a string) {