123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996 |
- // 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 statements
-
- import (
- "database/sql/driver"
- "errors"
- "fmt"
- "reflect"
- "strings"
- "time"
-
- "xorm.io/builder"
- "xorm.io/xorm/contexts"
- "xorm.io/xorm/convert"
- "xorm.io/xorm/dialects"
- "xorm.io/xorm/internal/json"
- "xorm.io/xorm/internal/utils"
- "xorm.io/xorm/schemas"
- "xorm.io/xorm/tags"
- )
-
- var (
- // ErrConditionType condition type unsupported
- ErrConditionType = errors.New("Unsupported condition type")
- // ErrUnSupportedSQLType parameter of SQL is not supported
- ErrUnSupportedSQLType = errors.New("Unsupported sql type")
- // ErrUnSupportedType unsupported error
- ErrUnSupportedType = errors.New("Unsupported type error")
- // ErrTableNotFound table not found error
- ErrTableNotFound = errors.New("Table not found")
- )
-
- // Statement save all the sql info for executing SQL
- type Statement struct {
- RefTable *schemas.Table
- dialect dialects.Dialect
- defaultTimeZone *time.Location
- tagParser *tags.Parser
- Start int
- LimitN *int
- idParam schemas.PK
- OrderStr string
- JoinStr string
- joinArgs []interface{}
- GroupByStr string
- HavingStr string
- SelectStr string
- useAllCols bool
- AltTableName string
- tableName string
- RawSQL string
- RawParams []interface{}
- UseCascade bool
- UseAutoJoin bool
- StoreEngine string
- Charset string
- UseCache bool
- UseAutoTime bool
- NoAutoCondition bool
- IsDistinct bool
- IsForUpdate bool
- TableAlias string
- allUseBool bool
- CheckVersion bool
- unscoped bool
- ColumnMap columnMap
- OmitColumnMap columnMap
- MustColumnMap map[string]bool
- NullableMap map[string]bool
- IncrColumns exprParams
- DecrColumns exprParams
- ExprColumns exprParams
- cond builder.Cond
- BufferSize int
- Context contexts.ContextCache
- LastError error
- }
-
- // NewStatement creates a new statement
- func NewStatement(dialect dialects.Dialect, tagParser *tags.Parser, defaultTimeZone *time.Location) *Statement {
- statement := &Statement{
- dialect: dialect,
- tagParser: tagParser,
- defaultTimeZone: defaultTimeZone,
- }
- statement.Reset()
- return statement
- }
-
- func (statement *Statement) SetTableName(tableName string) {
- statement.tableName = tableName
- }
-
- func (statement *Statement) omitStr() string {
- return statement.dialect.Quoter().Join(statement.OmitColumnMap, " ,")
- }
-
- // GenRawSQL generates correct raw sql
- func (statement *Statement) GenRawSQL() string {
- return statement.ReplaceQuote(statement.RawSQL)
- }
-
- func (statement *Statement) GenCondSQL(condOrBuilder interface{}) (string, []interface{}, error) {
- condSQL, condArgs, err := builder.ToSQL(condOrBuilder)
- if err != nil {
- return "", nil, err
- }
- return statement.ReplaceQuote(condSQL), condArgs, nil
- }
-
- func (statement *Statement) ReplaceQuote(sql string) string {
- if sql == "" || statement.dialect.URI().DBType == schemas.MYSQL ||
- statement.dialect.URI().DBType == schemas.SQLITE {
- return sql
- }
- return statement.dialect.Quoter().Replace(sql)
- }
-
- func (statement *Statement) SetContextCache(ctxCache contexts.ContextCache) {
- statement.Context = ctxCache
- }
-
- // Init reset all the statement's fields
- func (statement *Statement) Reset() {
- statement.RefTable = nil
- statement.Start = 0
- statement.LimitN = nil
- statement.OrderStr = ""
- statement.UseCascade = true
- statement.JoinStr = ""
- statement.joinArgs = make([]interface{}, 0)
- statement.GroupByStr = ""
- statement.HavingStr = ""
- statement.ColumnMap = columnMap{}
- statement.OmitColumnMap = columnMap{}
- statement.AltTableName = ""
- statement.tableName = ""
- statement.idParam = nil
- statement.RawSQL = ""
- statement.RawParams = 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.IncrColumns = exprParams{}
- statement.DecrColumns = exprParams{}
- statement.ExprColumns = exprParams{}
- statement.cond = builder.NewCond()
- statement.BufferSize = 0
- statement.Context = nil
- statement.LastError = nil
- }
-
- // NoAutoCondition if you do not want convert bean's field as query condition, then use this function
- func (statement *Statement) SetNoAutoCondition(no ...bool) *Statement {
- statement.NoAutoCondition = true
- if len(no) > 0 {
- statement.NoAutoCondition = no[0]
- }
- return statement
- }
-
- // Alias set the table alias
- func (statement *Statement) Alias(alias string) *Statement {
- statement.TableAlias = alias
- return statement
- }
-
- // SQL adds raw sql statement
- func (statement *Statement) SQL(query interface{}, args ...interface{}) *Statement {
- switch query.(type) {
- case (*builder.Builder):
- var err error
- statement.RawSQL, statement.RawParams, err = query.(*builder.Builder).ToSQL()
- if err != nil {
- statement.LastError = err
- }
- case string:
- statement.RawSQL = query.(string)
- statement.RawParams = args
- default:
- statement.LastError = ErrUnSupportedSQLType
- }
-
- return statement
- }
-
- // Where add Where statement
- func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement {
- return statement.And(query, args...)
- }
-
- func (statement *Statement) quote(s string) string {
- return statement.dialect.Quoter().Quote(s)
- }
-
- // And add Where & and statement
- func (statement *Statement) And(query interface{}, args ...interface{}) *Statement {
- switch query.(type) {
- case string:
- cond := builder.Expr(query.(string), args...)
- statement.cond = statement.cond.And(cond)
- case map[string]interface{}:
- queryMap := query.(map[string]interface{})
- newMap := make(map[string]interface{})
- for k, v := range queryMap {
- newMap[statement.quote(k)] = v
- }
- statement.cond = statement.cond.And(builder.Eq(newMap))
- case builder.Cond:
- cond := query.(builder.Cond)
- statement.cond = statement.cond.And(cond)
- for _, v := range args {
- if vv, ok := v.(builder.Cond); ok {
- statement.cond = statement.cond.And(vv)
- }
- }
- default:
- statement.LastError = ErrConditionType
- }
-
- return statement
- }
-
- // Or add Where & Or statement
- func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement {
- switch query.(type) {
- case string:
- cond := builder.Expr(query.(string), args...)
- statement.cond = statement.cond.Or(cond)
- case map[string]interface{}:
- cond := builder.Eq(query.(map[string]interface{}))
- statement.cond = statement.cond.Or(cond)
- case builder.Cond:
- cond := query.(builder.Cond)
- statement.cond = statement.cond.Or(cond)
- for _, v := range args {
- if vv, ok := v.(builder.Cond); ok {
- statement.cond = statement.cond.Or(vv)
- }
- }
- default:
- // TODO: not support condition type
- }
- return statement
- }
-
- // In generate "Where column IN (?) " statement
- func (statement *Statement) In(column string, args ...interface{}) *Statement {
- in := builder.In(statement.quote(column), args...)
- statement.cond = statement.cond.And(in)
- return statement
- }
-
- // NotIn generate "Where column NOT IN (?) " statement
- func (statement *Statement) NotIn(column string, args ...interface{}) *Statement {
- notIn := builder.NotIn(statement.quote(column), args...)
- statement.cond = statement.cond.And(notIn)
- return statement
- }
-
- func (statement *Statement) SetRefValue(v reflect.Value) error {
- var err error
- statement.RefTable, err = statement.tagParser.ParseWithCache(reflect.Indirect(v))
- if err != nil {
- return err
- }
- statement.tableName = dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), v, true)
- return nil
- }
-
- func rValue(bean interface{}) reflect.Value {
- return reflect.Indirect(reflect.ValueOf(bean))
- }
-
- func (statement *Statement) SetRefBean(bean interface{}) error {
- var err error
- statement.RefTable, err = statement.tagParser.ParseWithCache(rValue(bean))
- if err != nil {
- return err
- }
- statement.tableName = dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), bean, true)
- return nil
- }
-
- func (statement *Statement) needTableName() bool {
- return len(statement.JoinStr) > 0
- }
-
- func (statement *Statement) colName(col *schemas.Column, tableName string) string {
- if statement.needTableName() {
- var nm = tableName
- if len(statement.TableAlias) > 0 {
- nm = statement.TableAlias
- }
- return statement.quote(nm) + "." + statement.quote(col.Name)
- }
- return statement.quote(col.Name)
- }
-
- // TableName return current tableName
- func (statement *Statement) TableName() string {
- if statement.AltTableName != "" {
- return statement.AltTableName
- }
-
- return statement.tableName
- }
-
- // Incr Generate "Update ... Set column = column + arg" statement
- func (statement *Statement) Incr(column string, arg ...interface{}) *Statement {
- if len(arg) > 0 {
- statement.IncrColumns.addParam(column, arg[0])
- } else {
- statement.IncrColumns.addParam(column, 1)
- }
- return statement
- }
-
- // Decr Generate "Update ... Set column = column - arg" statement
- func (statement *Statement) Decr(column string, arg ...interface{}) *Statement {
- if len(arg) > 0 {
- statement.DecrColumns.addParam(column, arg[0])
- } else {
- statement.DecrColumns.addParam(column, 1)
- }
- return statement
- }
-
- // SetExpr Generate "Update ... Set column = {expression}" statement
- func (statement *Statement) SetExpr(column string, expression interface{}) *Statement {
- if e, ok := expression.(string); ok {
- statement.ExprColumns.addParam(column, statement.dialect.Quoter().Replace(e))
- } else {
- statement.ExprColumns.addParam(column, expression)
- }
- return statement
- }
-
- // Distinct generates "DISTINCT col1, col2 " statement
- func (statement *Statement) Distinct(columns ...string) *Statement {
- statement.IsDistinct = true
- statement.Cols(columns...)
- return statement
- }
-
- // ForUpdate generates "SELECT ... FOR UPDATE" statement
- func (statement *Statement) ForUpdate() *Statement {
- statement.IsForUpdate = true
- return statement
- }
-
- // Select replace select
- func (statement *Statement) Select(str string) *Statement {
- statement.SelectStr = statement.ReplaceQuote(str)
- return statement
- }
-
- func col2NewCols(columns ...string) []string {
- newColumns := make([]string, 0, len(columns))
- for _, col := range columns {
- col = strings.Replace(col, "`", "", -1)
- col = strings.Replace(col, `"`, "", -1)
- ccols := strings.Split(col, ",")
- for _, c := range ccols {
- newColumns = append(newColumns, strings.TrimSpace(c))
- }
- }
- return newColumns
- }
-
- // Cols generate "col1, col2" statement
- func (statement *Statement) Cols(columns ...string) *Statement {
- cols := col2NewCols(columns...)
- for _, nc := range cols {
- statement.ColumnMap.Add(nc)
- }
- return statement
- }
-
- func (statement *Statement) ColumnStr() string {
- return statement.dialect.Quoter().Join(statement.ColumnMap, ", ")
- }
-
- // AllCols update use only: update all columns
- func (statement *Statement) AllCols() *Statement {
- statement.useAllCols = true
- return statement
- }
-
- // MustCols update use only: must update columns
- func (statement *Statement) MustCols(columns ...string) *Statement {
- newColumns := col2NewCols(columns...)
- for _, nc := range newColumns {
- statement.MustColumnMap[strings.ToLower(nc)] = true
- }
- return statement
- }
-
- // UseBool indicates that use bool fields as update contents and query contiditions
- func (statement *Statement) UseBool(columns ...string) *Statement {
- if len(columns) > 0 {
- statement.MustCols(columns...)
- } else {
- statement.allUseBool = true
- }
- return statement
- }
-
- // Omit do not use the columns
- func (statement *Statement) Omit(columns ...string) {
- newColumns := col2NewCols(columns...)
- for _, nc := range newColumns {
- statement.OmitColumnMap = append(statement.OmitColumnMap, nc)
- }
- }
-
- // Nullable 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
- }
- }
-
- // Top generate LIMIT limit statement
- func (statement *Statement) Top(limit int) *Statement {
- statement.Limit(limit)
- return statement
- }
-
- // Limit generate LIMIT start, limit statement
- func (statement *Statement) Limit(limit int, start ...int) *Statement {
- statement.LimitN = &limit
- if len(start) > 0 {
- statement.Start = start[0]
- }
- return statement
- }
-
- // OrderBy generate "Order By order" statement
- func (statement *Statement) OrderBy(order string) *Statement {
- if len(statement.OrderStr) > 0 {
- statement.OrderStr += ", "
- }
- statement.OrderStr += statement.ReplaceQuote(order)
- return statement
- }
-
- // Desc generate `ORDER BY xx DESC`
- func (statement *Statement) Desc(colNames ...string) *Statement {
- var buf strings.Builder
- if len(statement.OrderStr) > 0 {
- fmt.Fprint(&buf, statement.OrderStr, ", ")
- }
- for i, col := range colNames {
- if i > 0 {
- fmt.Fprint(&buf, ", ")
- }
- statement.dialect.Quoter().QuoteTo(&buf, col)
- fmt.Fprint(&buf, " DESC")
- }
- statement.OrderStr = buf.String()
- return statement
- }
-
- // Asc provide asc order by query condition, the input parameters are columns.
- func (statement *Statement) Asc(colNames ...string) *Statement {
- var buf strings.Builder
- if len(statement.OrderStr) > 0 {
- fmt.Fprint(&buf, statement.OrderStr, ", ")
- }
- for i, col := range colNames {
- if i > 0 {
- fmt.Fprint(&buf, ", ")
- }
- statement.dialect.Quoter().QuoteTo(&buf, col)
- fmt.Fprint(&buf, " ASC")
- }
- statement.OrderStr = buf.String()
- return statement
- }
-
- func (statement *Statement) Conds() builder.Cond {
- return statement.cond
- }
-
- // Table tempororily set table name, the parameter could be a string or a pointer of struct
- func (statement *Statement) SetTable(tableNameOrBean interface{}) error {
- v := rValue(tableNameOrBean)
- t := v.Type()
- if t.Kind() == reflect.Struct {
- var err error
- statement.RefTable, err = statement.tagParser.ParseWithCache(v)
- if err != nil {
- return err
- }
- }
-
- statement.AltTableName = dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tableNameOrBean, true)
- return nil
- }
-
- // Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
- func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement {
- var buf strings.Builder
- if len(statement.JoinStr) > 0 {
- fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP)
- } else {
- fmt.Fprintf(&buf, "%v JOIN ", joinOP)
- }
-
- switch tp := tablename.(type) {
- case builder.Builder:
- subSQL, subQueryArgs, err := tp.ToSQL()
- if err != nil {
- statement.LastError = err
- return statement
- }
-
- fields := strings.Split(tp.TableName(), ".")
- aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1])
- aliasName = schemas.CommonQuoter.Trim(aliasName)
-
- fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), aliasName, statement.ReplaceQuote(condition))
- statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
- case *builder.Builder:
- subSQL, subQueryArgs, err := tp.ToSQL()
- if err != nil {
- statement.LastError = err
- return statement
- }
-
- fields := strings.Split(tp.TableName(), ".")
- aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1])
- aliasName = schemas.CommonQuoter.Trim(aliasName)
-
- fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), aliasName, statement.ReplaceQuote(condition))
- statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
- default:
- tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true)
- if !utils.IsSubQuery(tbName) {
- var buf strings.Builder
- statement.dialect.Quoter().QuoteTo(&buf, tbName)
- tbName = buf.String()
- }
- fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condition))
- }
-
- statement.JoinStr = buf.String()
- statement.joinArgs = append(statement.joinArgs, args...)
- return statement
- }
-
- // tbName get some table's table name
- func (statement *Statement) tbNameNoSchema(table *schemas.Table) string {
- if len(statement.AltTableName) > 0 {
- return statement.AltTableName
- }
-
- return table.Name
- }
-
- // GroupBy generate "Group By keys" statement
- func (statement *Statement) GroupBy(keys string) *Statement {
- statement.GroupByStr = statement.ReplaceQuote(keys)
- return statement
- }
-
- // Having generate "Having conditions" statement
- func (statement *Statement) Having(conditions string) *Statement {
- statement.HavingStr = fmt.Sprintf("HAVING %v", statement.ReplaceQuote(conditions))
- return statement
- }
-
- // Unscoped always disable struct tag "deleted"
- func (statement *Statement) SetUnscoped() *Statement {
- statement.unscoped = true
- return statement
- }
-
- func (statement *Statement) GetUnscoped() bool {
- return statement.unscoped
- }
-
- func (statement *Statement) genColumnStr() string {
- if statement.RefTable == nil {
- return ""
- }
-
- var buf strings.Builder
- columns := statement.RefTable.Columns()
-
- for _, col := range columns {
- if statement.OmitColumnMap.Contain(col.Name) {
- continue
- }
-
- if len(statement.ColumnMap) > 0 && !statement.ColumnMap.Contain(col.Name) {
- continue
- }
-
- if col.MapType == schemas.ONLYTODB {
- continue
- }
-
- if buf.Len() != 0 {
- buf.WriteString(", ")
- }
-
- if statement.JoinStr != "" {
- if statement.TableAlias != "" {
- buf.WriteString(statement.TableAlias)
- } else {
- buf.WriteString(statement.TableName())
- }
-
- buf.WriteString(".")
- }
-
- statement.dialect.Quoter().QuoteTo(&buf, col.Name)
- }
-
- return buf.String()
- }
-
- func (statement *Statement) GenCreateTableSQL() []string {
- statement.RefTable.StoreEngine = statement.StoreEngine
- statement.RefTable.Charset = statement.Charset
- s, _ := statement.dialect.CreateTableSQL(statement.RefTable, statement.TableName())
- return s
- }
-
- func (statement *Statement) GenIndexSQL() []string {
- var sqls []string
- tbName := statement.TableName()
- for _, index := range statement.RefTable.Indexes {
- if index.Type == schemas.IndexType {
- sql := statement.dialect.CreateIndexSQL(tbName, index)
- sqls = append(sqls, sql)
- }
- }
- return sqls
- }
-
- func uniqueName(tableName, uqeName string) string {
- return fmt.Sprintf("UQE_%v_%v", tableName, uqeName)
- }
-
- func (statement *Statement) GenUniqueSQL() []string {
- var sqls []string
- tbName := statement.TableName()
- for _, index := range statement.RefTable.Indexes {
- if index.Type == schemas.UniqueType {
- sql := statement.dialect.CreateIndexSQL(tbName, index)
- sqls = append(sqls, sql)
- }
- }
- return sqls
- }
-
- func (statement *Statement) GenDelIndexSQL() []string {
- var sqls []string
- tbName := statement.TableName()
- idx := strings.Index(tbName, ".")
- if idx > -1 {
- tbName = tbName[idx+1:]
- }
- for _, index := range statement.RefTable.Indexes {
- sqls = append(sqls, statement.dialect.DropIndexSQL(tbName, index))
- }
- return sqls
- }
-
- func (statement *Statement) buildConds2(table *schemas.Table, bean interface{},
- includeVersion bool, includeUpdated bool, includeNil bool,
- includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool,
- mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) {
- var conds []builder.Cond
- for _, col := range table.Columns() {
- if !includeVersion && col.IsVersion {
- continue
- }
- if !includeUpdated && col.IsUpdated {
- continue
- }
- if !includeAutoIncr && col.IsAutoIncrement {
- continue
- }
-
- if statement.dialect.URI().DBType == schemas.MSSQL && (col.SQLType.Name == schemas.Text ||
- col.SQLType.IsBlob() || col.SQLType.Name == schemas.TimeStampz) {
- continue
- }
- if col.SQLType.IsJson() {
- continue
- }
-
- var colName string
- if addedTableName {
- var nm = tableName
- if len(aliasName) > 0 {
- nm = aliasName
- }
- colName = statement.quote(nm) + "." + statement.quote(col.Name)
- } else {
- colName = statement.quote(col.Name)
- }
-
- fieldValuePtr, err := col.ValueOf(bean)
- if err != nil {
- if !strings.Contains(err.Error(), "is not valid") {
- //engine.logger.Warn(err)
- }
- continue
- }
-
- if col.IsDeleted && !unscoped { // tag "deleted" is enabled
- conds = append(conds, statement.CondDeleted(col))
- }
-
- fieldValue := *fieldValuePtr
- if fieldValue.Interface() == nil {
- continue
- }
-
- fieldType := reflect.TypeOf(fieldValue.Interface())
- requiredField := useAllCols
-
- if b, ok := getFlagForColumn(mustColumnMap, col); ok {
- if b {
- requiredField = true
- } else {
- continue
- }
- }
-
- if fieldType.Kind() == reflect.Ptr {
- if fieldValue.IsNil() {
- if includeNil {
- conds = append(conds, builder.Eq{colName: nil})
- }
- continue
- } else if !fieldValue.IsValid() {
- continue
- } else {
- // dereference ptr type to instance type
- fieldValue = fieldValue.Elem()
- fieldType = reflect.TypeOf(fieldValue.Interface())
- requiredField = true
- }
- }
-
- var val interface{}
- 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
- }
- t := int64(fieldValue.Uint())
- val = reflect.ValueOf(&t).Interface()
- case reflect.Struct:
- if fieldType.ConvertibleTo(schemas.TimeType) {
- t := fieldValue.Convert(schemas.TimeType).Interface().(time.Time)
- if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
- continue
- }
- val = dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t)
- } else if _, ok := reflect.New(fieldType).Interface().(convert.Conversion); ok {
- continue
- } else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
- val, _ = valNul.Value()
- if val == nil && !requiredField {
- continue
- }
- } else {
- if col.SQLType.IsJson() {
- if col.SQLType.IsText() {
- bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
- if err != nil {
- return nil, err
- }
- val = string(bytes)
- } else if col.SQLType.IsBlob() {
- var bytes []byte
- var err error
- bytes, err = json.DefaultJSONHandler.Marshal(fieldValue.Interface())
- if err != nil {
- return nil, err
- }
- val = bytes
- }
- } else {
- table, err := statement.tagParser.ParseWithCache(fieldValue)
- if err != nil {
- val = fieldValue.Interface()
- } else {
- 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() && !utils.IsZero(pkField.Interface()) {
- val = pkField.Interface()
- } else {
- continue
- }
- } else {
- //TODO: how to handler?
- return nil, fmt.Errorf("not supported %v as %v", fieldValue.Interface(), table.PrimaryKeys)
- }
- }
- }
- }
- case reflect.Array:
- continue
- case 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.DefaultJSONHandler.Marshal(fieldValue.Interface())
- if err != nil {
- return nil, err
- }
- 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.DefaultJSONHandler.Marshal(fieldValue.Interface())
- if err != nil {
- return nil, err
- }
- val = bytes
- }
- } else {
- continue
- }
- default:
- val = fieldValue.Interface()
- }
-
- conds = append(conds, builder.Eq{colName: val})
- }
-
- return builder.And(conds...), nil
- }
-
- func (statement *Statement) BuildConds(table *schemas.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, addedTableName bool) (builder.Cond, error) {
- return statement.buildConds2(table, bean, includeVersion, includeUpdated, includeNil, includeAutoIncr, statement.allUseBool, statement.useAllCols,
- statement.unscoped, statement.MustColumnMap, statement.TableName(), statement.TableAlias, addedTableName)
- }
-
- func (statement *Statement) mergeConds(bean interface{}) error {
- if !statement.NoAutoCondition && statement.RefTable != nil {
- var addedTableName = (len(statement.JoinStr) > 0)
- autoCond, err := statement.BuildConds(statement.RefTable, bean, true, true, false, true, addedTableName)
- if err != nil {
- return err
- }
- statement.cond = statement.cond.And(autoCond)
- }
-
- if err := statement.ProcessIDParam(); err != nil {
- return err
- }
- return nil
- }
-
- func (statement *Statement) GenConds(bean interface{}) (string, []interface{}, error) {
- if err := statement.mergeConds(bean); err != nil {
- return "", nil, err
- }
-
- return statement.GenCondSQL(statement.cond)
- }
-
- func (statement *Statement) quoteColumnStr(columnStr string) string {
- columns := strings.Split(columnStr, ",")
- return statement.dialect.Quoter().Join(columns, ",")
- }
-
- func (statement *Statement) ConvertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) {
- sql, args, err := convertSQLOrArgs(sqlOrArgs...)
- if err != nil {
- return "", nil, err
- }
- return statement.ReplaceQuote(sql), args, nil
- }
-
- func convertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) {
- switch sqlOrArgs[0].(type) {
- case string:
- return sqlOrArgs[0].(string), sqlOrArgs[1:], nil
- case *builder.Builder:
- return sqlOrArgs[0].(*builder.Builder).ToSQL()
- case builder.Builder:
- bd := sqlOrArgs[0].(builder.Builder)
- return bd.ToSQL()
- }
-
- return "", nil, ErrUnSupportedType
- }
-
- func (statement *Statement) joinColumns(cols []*schemas.Column, includeTableName bool) string {
- var colnames = make([]string, len(cols))
- for i, col := range cols {
- if includeTableName {
- colnames[i] = statement.quote(statement.TableName()) +
- "." + statement.quote(col.Name)
- } else {
- colnames[i] = statement.quote(col.Name)
- }
- }
- return strings.Join(colnames, ", ")
- }
-
- // CondDeleted returns the conditions whether a record is soft deleted.
- func (statement *Statement) CondDeleted(col *schemas.Column) builder.Cond {
- var colName = col.Name
- if statement.JoinStr != "" {
- var prefix string
- if statement.TableAlias != "" {
- prefix = statement.TableAlias
- } else {
- prefix = statement.TableName()
- }
- colName = statement.quote(prefix) + "." + statement.quote(col.Name)
- }
- var cond = builder.NewCond()
- if col.SQLType.IsNumeric() {
- cond = builder.Eq{colName: 0}
- } else {
- // FIXME: mssql: The conversion of a nvarchar data type to a datetime data type resulted in an out-of-range value.
- if statement.dialect.URI().DBType != schemas.MSSQL {
- cond = builder.Eq{colName: utils.ZeroTime1}
- }
- }
-
- if col.Nullable {
- cond = cond.Or(builder.IsNull{colName})
- }
-
- return cond
- }
|