diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index a687c4dbb..4682f7dab 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -332,7 +332,7 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa } type formatWhereHolderInput struct { - ModelWhereHolder + WhereHolder OmitNil bool OmitEmpty bool Schema string diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 6d72f9b21..4769aaa4d 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -17,39 +17,39 @@ import ( // Model is core struct implementing the DAO for ORM. type Model struct { - db DB // Underlying DB interface. - tx *TX // Underlying TX interface. - rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. - schema string // Custom database schema. - linkType int // Mark for operation on master or slave. - tablesInit string // Table names when model initialization. - tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud". - fields string // Operation fields, multiple fields joined using char ','. - fieldsEx string // Excluded operation fields, multiple fields joined using char ','. - withArray []interface{} // Arguments for With feature. - withAll bool // Enable model association operations on all objects that have "with" tag in the struct. - extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver. - whereHolder []ModelWhereHolder // Condition strings for where operation. - groupBy string // Used for "group by" statement. - orderBy string // Used for "order by" statement. - having []interface{} // Used for "having..." statement. - start int // Used for "select ... start, limit ..." statement. - limit int // Used for "select ... start, limit ..." statement. - option int // Option for extra operation features. - offset int // Offset statement for some databases grammar. - data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc. - batch int // Batch number for batch Insert/Replace/Save operations. - filter bool // Filter data and where key-value pairs according to the fields of the table. - distinct string // Force the query to only return distinct results. - lockInfo string // Lock for update or in shared lock. - cacheEnabled bool // Enable sql result cache feature, which is mainly for indicating cache duration(especially 0) usage. - cacheOption CacheOption // Cache option for query statement. - hookHandler HookHandler // Hook functions for model hook feature. - shardingHandler ShardingHandler // Custom sharding handler for sharding feature. - unscoped bool // Disables soft deleting features when select/delete operations. - safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model. - onDuplicate interface{} // onDuplicate is used for ON "DUPLICATE KEY UPDATE" statement. - onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement. + db DB // Underlying DB interface. + tx *TX // Underlying TX interface. + rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. + schema string // Custom database schema. + linkType int // Mark for operation on master or slave. + tablesInit string // Table names when model initialization. + tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud". + fields string // Operation fields, multiple fields joined using char ','. + fieldsEx string // Excluded operation fields, multiple fields joined using char ','. + withArray []interface{} // Arguments for With feature. + withAll bool // Enable model association operations on all objects that have "with" tag in the struct. + extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver. + whereBuilder *WhereBuilder // Condition builder for where operation. + groupBy string // Used for "group by" statement. + orderBy string // Used for "order by" statement. + having []interface{} // Used for "having..." statement. + start int // Used for "select ... start, limit ..." statement. + limit int // Used for "select ... start, limit ..." statement. + option int // Option for extra operation features. + offset int // Offset statement for some databases grammar. + data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc. + batch int // Batch number for batch Insert/Replace/Save operations. + filter bool // Filter data and where key-value pairs according to the fields of the table. + distinct string // Force the query to only return distinct results. + lockInfo string // Lock for update or in shared lock. + cacheEnabled bool // Enable sql result cache feature, which is mainly for indicating cache duration(especially 0) usage. + cacheOption CacheOption // Cache option for query statement. + hookHandler HookHandler // Hook functions for model hook feature. + shardingHandler ShardingHandler // Custom sharding handler for sharding feature. + unscoped bool // Disables soft deleting features when select/delete operations. + safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model. + onDuplicate interface{} // onDuplicate is used for ON "DUPLICATE KEY UPDATE" statement. + onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement. } // ModelHandler is a function that handles given Model and returns a new Model that is custom modified. @@ -59,15 +59,6 @@ type ModelHandler func(m *Model) *Model // It returns true if it wants to continue chunking, or else it returns false to stop chunking. type ChunkHandler func(result Result, err error) bool -// ModelWhereHolder is the holder for where condition preparing. -type ModelWhereHolder struct { - Type string // Type of this holder. - Operator int // Operator for this holder. - Where interface{} // Where parameter, which can commonly be type of string/map/struct. - Args []interface{} // Arguments for where parameter. - Prefix string // Field prefix, eg: "user.", "order.". -} - const ( linkTypeMaster = 1 linkTypeSlave = 2 @@ -102,16 +93,16 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { if len(tableNameQueryOrStruct) > 1 { conditionStr := gconv.String(tableNameQueryOrStruct[0]) if gstr.Contains(conditionStr, "?") { - whereHolder := ModelWhereHolder{ + whereHolder := WhereHolder{ Where: conditionStr, Args: tableNameQueryOrStruct[1:], } tableStr, extraArgs = formatWhereHolder(ctx, c.db, formatWhereHolderInput{ - ModelWhereHolder: whereHolder, - OmitNil: false, - OmitEmpty: false, - Schema: "", - Table: "", + WhereHolder: whereHolder, + OmitNil: false, + OmitEmpty: false, + Schema: "", + Table: "", }) } } @@ -144,6 +135,7 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { filter: true, extraArgs: extraArgs, } + m.whereBuilder = m.Builder() if defaultModelSafe { m.safe = true } @@ -269,15 +261,14 @@ func (m *Model) Clone() *Model { } // Basic attributes copy. *newModel = *m + // WhereBuilder copy, note the attribute pointer. + newModel.whereBuilder = m.whereBuilder.Clone() + newModel.whereBuilder.model = newModel // Shallow copy slice attributes. if n := len(m.extraArgs); n > 0 { newModel.extraArgs = make([]interface{}, n) copy(newModel.extraArgs, m.extraArgs) } - if n := len(m.whereHolder); n > 0 { - newModel.whereHolder = make([]ModelWhereHolder, n) - copy(newModel.whereHolder, m.whereHolder) - } if n := len(m.withArray); n > 0 { newModel.withArray = make([]interface{}, n) copy(newModel.withArray, m.withArray) diff --git a/database/gdb/gdb_model_builder.go b/database/gdb/gdb_model_builder.go new file mode 100644 index 000000000..0a1ccd6ff --- /dev/null +++ b/database/gdb/gdb_model_builder.go @@ -0,0 +1,143 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +import ( + "fmt" + + "github.com/gogf/gf/v2/text/gstr" +) + +type WhereBuilder struct { + model *Model // Bound to parent model. + whereHolder []WhereHolder // Condition strings for where operation. +} + +// WhereHolder is the holder for where condition preparing. +type WhereHolder struct { + Type string // Type of this holder. + Operator int // Operator for this holder. + Where interface{} // Where parameter, which can commonly be type of string/map/struct. + Args []interface{} // Arguments for where parameter. + Prefix string // Field prefix, eg: "user.", "order.". +} + +func (m *Model) Builder() *WhereBuilder { + b := &WhereBuilder{ + model: m, + whereHolder: make([]WhereHolder, 0), + } + return b +} + +// getBuilder creates and returns a cloned WhereBuilder of current WhereBuilder if `safe` is true, +// or else it returns the current WhereBuilder. +func (b *WhereBuilder) getBuilder() *WhereBuilder { + if !b.model.safe { + return b + } else { + return b.Clone() + } +} + +func (b *WhereBuilder) Clone() *WhereBuilder { + newBuilder := b.model.Builder() + newBuilder.whereHolder = make([]WhereHolder, len(b.whereHolder)) + copy(newBuilder.whereHolder, b.whereHolder) + return newBuilder +} + +func (b *WhereBuilder) Build() (conditionWhere string, conditionArgs []interface{}) { + var ( + ctx = b.model.GetCtx() + autoPrefix = b.model.getAutoPrefix() + tableForMappingAndFiltering = b.model.tables + ) + if len(b.whereHolder) > 0 { + for _, holder := range b.whereHolder { + if holder.Prefix == "" { + holder.Prefix = autoPrefix + } + switch holder.Operator { + case whereHolderOperatorWhere: + if conditionWhere == "" { + newWhere, newArgs := formatWhereHolder(ctx, b.model.db, formatWhereHolderInput{ + WhereHolder: holder, + OmitNil: b.model.option&optionOmitNilWhere > 0, + OmitEmpty: b.model.option&optionOmitEmptyWhere > 0, + Schema: b.model.schema, + Table: tableForMappingAndFiltering, + }) + if len(newWhere) > 0 { + conditionWhere = newWhere + conditionArgs = newArgs + } + continue + } + fallthrough + + case whereHolderOperatorAnd: + newWhere, newArgs := formatWhereHolder(ctx, b.model.db, formatWhereHolderInput{ + WhereHolder: holder, + OmitNil: b.model.option&optionOmitNilWhere > 0, + OmitEmpty: b.model.option&optionOmitEmptyWhere > 0, + Schema: b.model.schema, + Table: tableForMappingAndFiltering, + }) + if len(newWhere) > 0 { + if len(conditionWhere) == 0 { + conditionWhere = newWhere + } else if conditionWhere[0] == '(' { + conditionWhere = fmt.Sprintf(`%s AND (%s)`, conditionWhere, newWhere) + } else { + conditionWhere = fmt.Sprintf(`(%s) AND (%s)`, conditionWhere, newWhere) + } + conditionArgs = append(conditionArgs, newArgs...) + } + + case whereHolderOperatorOr: + newWhere, newArgs := formatWhereHolder(ctx, b.model.db, formatWhereHolderInput{ + WhereHolder: holder, + OmitNil: b.model.option&optionOmitNilWhere > 0, + OmitEmpty: b.model.option&optionOmitEmptyWhere > 0, + Schema: b.model.schema, + Table: tableForMappingAndFiltering, + }) + if len(newWhere) > 0 { + if len(conditionWhere) == 0 { + conditionWhere = newWhere + } else if conditionWhere[0] == '(' { + conditionWhere = fmt.Sprintf(`%s OR (%s)`, conditionWhere, newWhere) + } else { + conditionWhere = fmt.Sprintf(`(%s) OR (%s)`, conditionWhere, newWhere) + } + conditionArgs = append(conditionArgs, newArgs...) + } + } + } + } + // Soft deletion. + softDeletingCondition := b.model.getConditionForSoftDeleting() + if b.model.rawSql != "" && conditionWhere != "" { + if gstr.ContainsI(b.model.rawSql, " WHERE ") { + conditionWhere = " AND " + conditionWhere + } else { + conditionWhere = " WHERE " + conditionWhere + } + } else if !b.model.unscoped && softDeletingCondition != "" { + if conditionWhere == "" { + conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) + } else { + conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition) + } + } else { + if conditionWhere != "" { + conditionWhere = " WHERE " + conditionWhere + } + } + return +} diff --git a/database/gdb/gdb_model_builder_where.go b/database/gdb/gdb_model_builder_where.go new file mode 100644 index 000000000..5803f8d82 --- /dev/null +++ b/database/gdb/gdb_model_builder_where.go @@ -0,0 +1,159 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +import ( + "fmt" + + "github.com/gogf/gf/v2/text/gstr" +) + +// doWhereType sets the condition statement for the model. The parameter `where` can be type of +// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, +// multiple conditions will be joined into where statement using "AND". +func (b *WhereBuilder) doWhereType(t string, where interface{}, args ...interface{}) *WhereBuilder { + builder := b.getBuilder() + if builder.whereHolder == nil { + builder.whereHolder = make([]WhereHolder, 0) + } + if t == "" { + if len(args) == 0 { + t = whereHolderTypeNoArgs + } else { + t = whereHolderTypeDefault + } + } + builder.whereHolder = append(builder.whereHolder, WhereHolder{ + Type: t, + Operator: whereHolderOperatorWhere, + Where: where, + Args: args, + }) + return builder +} + +// doWherefType builds condition string using fmt.Sprintf and arguments. +// Note that if the number of `args` is more than the placeholder in `format`, +// the extra `args` will be used as the where condition arguments of the Model. +func (b *WhereBuilder) doWherefType(t string, format string, args ...interface{}) *WhereBuilder { + var ( + placeHolderCount = gstr.Count(format, "?") + conditionStr = fmt.Sprintf(format, args[:len(args)-placeHolderCount]...) + ) + return b.doWhereType(t, conditionStr, args[len(args)-placeHolderCount:]...) +} + +// Where sets the condition statement for the builder. The parameter `where` can be type of +// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, +// multiple conditions will be joined into where statement using "AND". +// Eg: +// Where("uid=10000") +// Where("uid", 10000) +// Where("money>? AND name like ?", 99999, "vip_%") +// Where("uid", 1).Where("name", "john") +// Where("status IN (?)", g.Slice{1,2,3}) +// Where("age IN(?,?)", 18, 50) +// Where(User{ Id : 1, UserName : "john"}). +func (b *WhereBuilder) Where(where interface{}, args ...interface{}) *WhereBuilder { + return b.doWhereType(``, where, args...) +} + +// Wheref builds condition string using fmt.Sprintf and arguments. +// Note that if the number of `args` is more than the placeholder in `format`, +// the extra `args` will be used as the where condition arguments of the Model. +// Eg: +// Wheref(`amount WHERE `amount`<100 and status='paid' +// Wheref(`amount<%d and status=%s`, 100, "paid") => WHERE `amount`<100 and status='paid' +func (b *WhereBuilder) Wheref(format string, args ...interface{}) *WhereBuilder { + return b.doWherefType(``, format, args...) +} + +// WherePri does the same logic as Model.Where except that if the parameter `where` +// is a single condition like int/string/float/slice, it treats the condition as the primary +// key value. That is, if primary key is "id" and given `where` parameter as "123", the +// WherePri function treats the condition as "id=123", but Model.Where treats the condition +// as string "123". +func (b *WhereBuilder) WherePri(where interface{}, args ...interface{}) *WhereBuilder { + if len(args) > 0 { + return b.Where(where, args...) + } + newWhere := GetPrimaryKeyCondition(b.model.getPrimaryKey(), where) + return b.Where(newWhere[0], newWhere[1:]...) +} + +// WhereLT builds `column < value` statement. +func (b *WhereBuilder) WhereLT(column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s < ?`, b.model.QuoteWord(column), value) +} + +// WhereLTE builds `column <= value` statement. +func (b *WhereBuilder) WhereLTE(column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s <= ?`, b.model.QuoteWord(column), value) +} + +// WhereGT builds `column > value` statement. +func (b *WhereBuilder) WhereGT(column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s > ?`, b.model.QuoteWord(column), value) +} + +// WhereGTE builds `column >= value` statement. +func (b *WhereBuilder) WhereGTE(column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s >= ?`, b.model.QuoteWord(column), value) +} + +// WhereBetween builds `column BETWEEN min AND max` statement. +func (b *WhereBuilder) WhereBetween(column string, min, max interface{}) *WhereBuilder { + return b.Wheref(`%s BETWEEN ? AND ?`, b.model.QuoteWord(column), min, max) +} + +// WhereLike builds `column LIKE like` statement. +func (b *WhereBuilder) WhereLike(column string, like string) *WhereBuilder { + return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), like) +} + +// WhereIn builds `column IN (in)` statement. +func (b *WhereBuilder) WhereIn(column string, in interface{}) *WhereBuilder { + return b.doWherefType(whereHolderTypeIn, `%s IN (?)`, b.model.QuoteWord(column), in) +} + +// WhereNull builds `columns[0] IS NULL AND columns[1] IS NULL ...` statement. +func (b *WhereBuilder) WhereNull(columns ...string) *WhereBuilder { + builder := b + for _, column := range columns { + builder = builder.Wheref(`%s IS NULL`, b.model.QuoteWord(column)) + } + return builder +} + +// WhereNotBetween builds `column NOT BETWEEN min AND max` statement. +func (b *WhereBuilder) WhereNotBetween(column string, min, max interface{}) *WhereBuilder { + return b.Wheref(`%s NOT BETWEEN ? AND ?`, b.model.QuoteWord(column), min, max) +} + +// WhereNotLike builds `column NOT LIKE like` statement. +func (b *WhereBuilder) WhereNotLike(column string, like interface{}) *WhereBuilder { + return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), like) +} + +// WhereNot builds `column != value` statement. +func (b *WhereBuilder) WhereNot(column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s != ?`, b.model.QuoteWord(column), value) +} + +// WhereNotIn builds `column NOT IN (in)` statement. +func (b *WhereBuilder) WhereNotIn(column string, in interface{}) *WhereBuilder { + return b.doWherefType(whereHolderTypeIn, `%s NOT IN (?)`, b.model.QuoteWord(column), in) +} + +// WhereNotNull builds `columns[0] IS NOT NULL AND columns[1] IS NOT NULL ...` statement. +func (b *WhereBuilder) WhereNotNull(columns ...string) *WhereBuilder { + builder := b + for _, column := range columns { + builder = builder.Wheref(`%s IS NOT NULL`, b.model.QuoteWord(column)) + } + return builder +} diff --git a/database/gdb/gdb_model_builder_where_prefix.go b/database/gdb/gdb_model_builder_where_prefix.go new file mode 100644 index 000000000..7cc189901 --- /dev/null +++ b/database/gdb/gdb_model_builder_where_prefix.go @@ -0,0 +1,99 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +// WherePrefix performs as Where, but it adds prefix to each field in where statement. +// Eg: +// WherePrefix("order", "status", "paid") => WHERE `order`.`status`='paid' +// WherePrefix("order", struct{Status:"paid", "channel":"bank"}) => WHERE `order`.`status`='paid' AND `order`.`channel`='bank' +func (b *WhereBuilder) WherePrefix(prefix string, where interface{}, args ...interface{}) *WhereBuilder { + builder := b.getBuilder() + if builder.whereHolder == nil { + builder.whereHolder = make([]WhereHolder, 0) + } + builder.whereHolder = append(builder.whereHolder, WhereHolder{ + Type: whereHolderTypeDefault, + Operator: whereHolderOperatorWhere, + Where: where, + Args: args, + Prefix: prefix, + }) + return builder +} + +// WherePrefixLT builds `prefix.column < value` statement. +func (b *WhereBuilder) WherePrefixLT(prefix string, column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s.%s < ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WherePrefixLTE builds `prefix.column <= value` statement. +func (b *WhereBuilder) WherePrefixLTE(prefix string, column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s.%s <= ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WherePrefixGT builds `prefix.column > value` statement. +func (b *WhereBuilder) WherePrefixGT(prefix string, column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s.%s > ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WherePrefixGTE builds `prefix.column >= value` statement. +func (b *WhereBuilder) WherePrefixGTE(prefix string, column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s.%s >= ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WherePrefixBetween builds `prefix.column BETWEEN min AND max` statement. +func (b *WhereBuilder) WherePrefixBetween(prefix string, column string, min, max interface{}) *WhereBuilder { + return b.Wheref(`%s.%s BETWEEN ? AND ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), min, max) +} + +// WherePrefixLike builds `prefix.column LIKE like` statement. +func (b *WhereBuilder) WherePrefixLike(prefix string, column string, like interface{}) *WhereBuilder { + return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) +} + +// WherePrefixIn builds `prefix.column IN (in)` statement. +func (b *WhereBuilder) WherePrefixIn(prefix string, column string, in interface{}) *WhereBuilder { + return b.doWherefType(whereHolderTypeIn, `%s.%s IN (?)`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), in) +} + +// WherePrefixNull builds `prefix.columns[0] IS NULL AND prefix.columns[1] IS NULL ...` statement. +func (b *WhereBuilder) WherePrefixNull(prefix string, columns ...string) *WhereBuilder { + builder := b + for _, column := range columns { + builder = builder.Wheref(`%s.%s IS NULL`, b.model.QuoteWord(prefix), b.model.QuoteWord(column)) + } + return builder +} + +// WherePrefixNotBetween builds `prefix.column NOT BETWEEN min AND max` statement. +func (b *WhereBuilder) WherePrefixNotBetween(prefix string, column string, min, max interface{}) *WhereBuilder { + return b.Wheref(`%s.%s NOT BETWEEN ? AND ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), min, max) +} + +// WherePrefixNotLike builds `prefix.column NOT LIKE like` statement. +func (b *WhereBuilder) WherePrefixNotLike(prefix string, column string, like interface{}) *WhereBuilder { + return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) +} + +// WherePrefixNot builds `prefix.column != value` statement. +func (b *WhereBuilder) WherePrefixNot(prefix string, column string, value interface{}) *WhereBuilder { + return b.Wheref(`%s.%s != ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WherePrefixNotIn builds `prefix.column NOT IN (in)` statement. +func (b *WhereBuilder) WherePrefixNotIn(prefix string, column string, in interface{}) *WhereBuilder { + return b.doWherefType(whereHolderTypeIn, `%s.%s NOT IN (?)`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), in) +} + +// WherePrefixNotNull builds `prefix.columns[0] IS NOT NULL AND prefix.columns[1] IS NOT NULL ...` statement. +func (b *WhereBuilder) WherePrefixNotNull(prefix string, columns ...string) *WhereBuilder { + builder := b + for _, column := range columns { + builder = builder.Wheref(`%s.%s IS NOT NULL`, b.model.QuoteWord(prefix), b.model.QuoteWord(column)) + } + return builder +} diff --git a/database/gdb/gdb_model_builder_whereor.go b/database/gdb/gdb_model_builder_whereor.go new file mode 100644 index 000000000..993c4def3 --- /dev/null +++ b/database/gdb/gdb_model_builder_whereor.go @@ -0,0 +1,118 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +import ( + "fmt" + + "github.com/gogf/gf/v2/text/gstr" +) + +// WhereOr adds "OR" condition to the where statement. +func (b *WhereBuilder) doWhereOrType(t string, where interface{}, args ...interface{}) *WhereBuilder { + builder := b.getBuilder() + if builder.whereHolder == nil { + builder.whereHolder = make([]WhereHolder, 0) + } + builder.whereHolder = append(builder.whereHolder, WhereHolder{ + Type: t, + Operator: whereHolderOperatorOr, + Where: where, + Args: args, + }) + return builder +} + +// WhereOrf builds `OR` condition string using fmt.Sprintf and arguments. +func (b *WhereBuilder) doWhereOrfType(t string, format string, args ...interface{}) *WhereBuilder { + var ( + placeHolderCount = gstr.Count(format, "?") + conditionStr = fmt.Sprintf(format, args[:len(args)-placeHolderCount]...) + ) + return b.doWhereOrType(t, conditionStr, args[len(args)-placeHolderCount:]...) +} + +// WhereOr adds "OR" condition to the where statement. +func (b *WhereBuilder) WhereOr(where interface{}, args ...interface{}) *WhereBuilder { + return b.doWhereOrType(``, where, args...) +} + +// WhereOrf builds `OR` condition string using fmt.Sprintf and arguments. +// Eg: +// WhereOrf(`amount WHERE xxx OR `amount`<100 and status='paid' +// WhereOrf(`amount<%d and status=%s`, 100, "paid") => WHERE xxx OR `amount`<100 and status='paid' +func (b *WhereBuilder) WhereOrf(format string, args ...interface{}) *WhereBuilder { + return b.doWhereOrfType(``, format, args...) +} + +// WhereOrLT builds `column < value` statement in `OR` conditions.. +func (b *WhereBuilder) WhereOrLT(column string, value interface{}) *WhereBuilder { + return b.WhereOrf(`%s < ?`, column, value) +} + +// WhereOrLTE builds `column <= value` statement in `OR` conditions.. +func (b *WhereBuilder) WhereOrLTE(column string, value interface{}) *WhereBuilder { + return b.WhereOrf(`%s <= ?`, column, value) +} + +// WhereOrGT builds `column > value` statement in `OR` conditions.. +func (b *WhereBuilder) WhereOrGT(column string, value interface{}) *WhereBuilder { + return b.WhereOrf(`%s > ?`, column, value) +} + +// WhereOrGTE builds `column >= value` statement in `OR` conditions.. +func (b *WhereBuilder) WhereOrGTE(column string, value interface{}) *WhereBuilder { + return b.WhereOrf(`%s >= ?`, column, value) +} + +// WhereOrBetween builds `column BETWEEN min AND max` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrBetween(column string, min, max interface{}) *WhereBuilder { + return b.WhereOrf(`%s BETWEEN ? AND ?`, b.model.QuoteWord(column), min, max) +} + +// WhereOrLike builds `column LIKE like` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrLike(column string, like interface{}) *WhereBuilder { + return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), like) +} + +// WhereOrIn builds `column IN (in)` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrIn(column string, in interface{}) *WhereBuilder { + return b.doWhereOrfType(whereHolderTypeIn, `%s IN (?)`, b.model.QuoteWord(column), in) +} + +// WhereOrNull builds `columns[0] IS NULL OR columns[1] IS NULL ...` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrNull(columns ...string) *WhereBuilder { + var builder *WhereBuilder + for _, column := range columns { + builder = b.WhereOrf(`%s IS NULL`, b.model.QuoteWord(column)) + } + return builder +} + +// WhereOrNotBetween builds `column NOT BETWEEN min AND max` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrNotBetween(column string, min, max interface{}) *WhereBuilder { + return b.WhereOrf(`%s NOT BETWEEN ? AND ?`, b.model.QuoteWord(column), min, max) +} + +// WhereOrNotLike builds `column NOT LIKE like` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrNotLike(column string, like interface{}) *WhereBuilder { + return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), like) +} + +// WhereOrNotIn builds `column NOT IN (in)` statement. +func (b *WhereBuilder) WhereOrNotIn(column string, in interface{}) *WhereBuilder { + return b.doWhereOrfType(whereHolderTypeIn, `%s NOT IN (?)`, b.model.QuoteWord(column), in) +} + +// WhereOrNotNull builds `columns[0] IS NOT NULL OR columns[1] IS NOT NULL ...` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrNotNull(columns ...string) *WhereBuilder { + builder := b + for _, column := range columns { + builder = builder.WhereOrf(`%s IS NOT NULL`, b.model.QuoteWord(column)) + } + return builder +} diff --git a/database/gdb/gdb_model_builder_whereor_prefix.go b/database/gdb/gdb_model_builder_whereor_prefix.go new file mode 100644 index 000000000..c720b7c3f --- /dev/null +++ b/database/gdb/gdb_model_builder_whereor_prefix.go @@ -0,0 +1,91 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +// WhereOrPrefix performs as WhereOr, but it adds prefix to each field in where statement. +// Eg: +// WhereOrPrefix("order", "status", "paid") => WHERE xxx OR (`order`.`status`='paid') +// WhereOrPrefix("order", struct{Status:"paid", "channel":"bank"}) => WHERE xxx OR (`order`.`status`='paid' AND `order`.`channel`='bank') +func (b *WhereBuilder) WhereOrPrefix(prefix string, where interface{}, args ...interface{}) *WhereBuilder { + builder := b.getBuilder() + builder.whereHolder = append(builder.whereHolder, WhereHolder{ + Type: whereHolderTypeDefault, + Operator: whereHolderOperatorOr, + Where: where, + Args: args, + Prefix: prefix, + }) + return builder +} + +// WhereOrPrefixLT builds `prefix.column < value` statement in `OR` conditions.. +func (b *WhereBuilder) WhereOrPrefixLT(prefix string, column string, value interface{}) *WhereBuilder { + return b.WhereOrf(`%s.%s < ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WhereOrPrefixLTE builds `prefix.column <= value` statement in `OR` conditions.. +func (b *WhereBuilder) WhereOrPrefixLTE(prefix string, column string, value interface{}) *WhereBuilder { + return b.WhereOrf(`%s.%s <= ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WhereOrPrefixGT builds `prefix.column > value` statement in `OR` conditions.. +func (b *WhereBuilder) WhereOrPrefixGT(prefix string, column string, value interface{}) *WhereBuilder { + return b.WhereOrf(`%s.%s > ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WhereOrPrefixGTE builds `prefix.column >= value` statement in `OR` conditions.. +func (b *WhereBuilder) WhereOrPrefixGTE(prefix string, column string, value interface{}) *WhereBuilder { + return b.WhereOrf(`%s.%s >= ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) +} + +// WhereOrPrefixBetween builds `prefix.column BETWEEN min AND max` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrPrefixBetween(prefix string, column string, min, max interface{}) *WhereBuilder { + return b.WhereOrf(`%s.%s BETWEEN ? AND ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), min, max) +} + +// WhereOrPrefixLike builds `prefix.column LIKE like` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrPrefixLike(prefix string, column string, like interface{}) *WhereBuilder { + return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) +} + +// WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrPrefixIn(prefix string, column string, in interface{}) *WhereBuilder { + return b.doWhereOrfType(whereHolderTypeIn, `%s.%s IN (?)`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), in) +} + +// WhereOrPrefixNull builds `prefix.columns[0] IS NULL OR prefix.columns[1] IS NULL ...` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrPrefixNull(prefix string, columns ...string) *WhereBuilder { + builder := b + for _, column := range columns { + builder = builder.WhereOrf(`%s.%s IS NULL`, b.model.QuoteWord(prefix), b.model.QuoteWord(column)) + } + return builder +} + +// WhereOrPrefixNotBetween builds `prefix.column NOT BETWEEN min AND max` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrPrefixNotBetween(prefix string, column string, min, max interface{}) *WhereBuilder { + return b.WhereOrf(`%s.%s NOT BETWEEN ? AND ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), min, max) +} + +// WhereOrPrefixNotLike builds `prefix.column NOT LIKE like` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrPrefixNotLike(prefix string, column string, like interface{}) *WhereBuilder { + return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) +} + +// WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement. +func (b *WhereBuilder) WhereOrPrefixNotIn(prefix string, column string, in interface{}) *WhereBuilder { + return b.doWhereOrfType(whereHolderTypeIn, `%s.%s NOT IN (?)`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), in) +} + +// WhereOrPrefixNotNull builds `prefix.columns[0] IS NOT NULL OR prefix.columns[1] IS NOT NULL ...` statement in `OR` conditions. +func (b *WhereBuilder) WhereOrPrefixNotNull(prefix string, columns ...string) *WhereBuilder { + builder := b + for _, column := range columns { + builder = builder.WhereOrf(`%s.%s IS NOT NULL`, b.model.QuoteWord(prefix), b.model.QuoteWord(column)) + } + return builder +} diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 27b1ac185..b7f782f4a 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -594,122 +594,41 @@ func (m *Model) getFormattedSqlAndArgs(ctx context.Context, queryType int, limit } } -// formatCondition formats where arguments of the model and returns a new condition sql and its arguments. -// Note that this function does not change any attribute value of the `m`. -// -// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set. -func (m *Model) formatCondition(ctx context.Context, limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { +func (m *Model) getAutoPrefix() string { autoPrefix := "" if gstr.Contains(m.tables, " JOIN ") { autoPrefix = m.db.GetCore().QuoteWord( m.db.GetCore().guessPrimaryTableName(m.tablesInit), ) } - var ( - tableForMappingAndFiltering = m.tables - ) - if len(m.whereHolder) > 0 { - for _, holder := range m.whereHolder { - tableForMappingAndFiltering = m.tables - if holder.Prefix == "" { - holder.Prefix = autoPrefix - } - - switch holder.Operator { - case whereHolderOperatorWhere: - if conditionWhere == "" { - newWhere, newArgs := formatWhereHolder(ctx, m.db, formatWhereHolderInput{ - ModelWhereHolder: holder, - OmitNil: m.option&optionOmitNilWhere > 0, - OmitEmpty: m.option&optionOmitEmptyWhere > 0, - Schema: m.schema, - Table: tableForMappingAndFiltering, - }) - if len(newWhere) > 0 { - conditionWhere = newWhere - conditionArgs = newArgs - } - continue - } - fallthrough - - case whereHolderOperatorAnd: - newWhere, newArgs := formatWhereHolder(ctx, m.db, formatWhereHolderInput{ - ModelWhereHolder: holder, - OmitNil: m.option&optionOmitNilWhere > 0, - OmitEmpty: m.option&optionOmitEmptyWhere > 0, - Schema: m.schema, - Table: tableForMappingAndFiltering, - }) - if len(newWhere) > 0 { - if len(conditionWhere) == 0 { - conditionWhere = newWhere - } else if conditionWhere[0] == '(' { - conditionWhere = fmt.Sprintf(`%s AND (%s)`, conditionWhere, newWhere) - } else { - conditionWhere = fmt.Sprintf(`(%s) AND (%s)`, conditionWhere, newWhere) - } - conditionArgs = append(conditionArgs, newArgs...) - } - - case whereHolderOperatorOr: - newWhere, newArgs := formatWhereHolder(ctx, m.db, formatWhereHolderInput{ - ModelWhereHolder: holder, - OmitNil: m.option&optionOmitNilWhere > 0, - OmitEmpty: m.option&optionOmitEmptyWhere > 0, - Schema: m.schema, - Table: tableForMappingAndFiltering, - }) - if len(newWhere) > 0 { - if len(conditionWhere) == 0 { - conditionWhere = newWhere - } else if conditionWhere[0] == '(' { - conditionWhere = fmt.Sprintf(`%s OR (%s)`, conditionWhere, newWhere) - } else { - conditionWhere = fmt.Sprintf(`(%s) OR (%s)`, conditionWhere, newWhere) - } - conditionArgs = append(conditionArgs, newArgs...) - } - } - } - } - // Soft deletion. - softDeletingCondition := m.getConditionForSoftDeleting() - if m.rawSql != "" && conditionWhere != "" { - if gstr.ContainsI(m.rawSql, " WHERE ") { - conditionWhere = " AND " + conditionWhere - } else { - conditionWhere = " WHERE " + conditionWhere - } - } else if !m.unscoped && softDeletingCondition != "" { - if conditionWhere == "" { - conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) - } else { - conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition) - } - } else { - if conditionWhere != "" { - conditionWhere = " WHERE " + conditionWhere - } - } + return autoPrefix +} +// formatCondition formats where arguments of the model and returns a new condition sql and its arguments. +// Note that this function does not change any attribute value of the `m`. +// +// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set. +func (m *Model) formatCondition(ctx context.Context, limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { + var autoPrefix = m.getAutoPrefix() // GROUP BY. if m.groupBy != "" { conditionExtra += " GROUP BY " + m.groupBy } + // WHERE + conditionWhere, conditionArgs = m.whereBuilder.Build() // HAVING. if len(m.having) > 0 { - havingHolder := ModelWhereHolder{ + havingHolder := WhereHolder{ Where: m.having[0], Args: gconv.Interfaces(m.having[1]), Prefix: autoPrefix, } havingStr, havingArgs := formatWhereHolder(ctx, m.db, formatWhereHolderInput{ - ModelWhereHolder: havingHolder, - OmitNil: m.option&optionOmitNilWhere > 0, - OmitEmpty: m.option&optionOmitEmptyWhere > 0, - Schema: m.schema, - Table: m.tables, + WhereHolder: havingHolder, + OmitNil: m.option&optionOmitNilWhere > 0, + OmitEmpty: m.option&optionOmitEmptyWhere > 0, + Schema: m.schema, + Table: m.tables, }) if len(havingStr) > 0 { conditionExtra += " HAVING " + havingStr diff --git a/database/gdb/gdb_model_where.go b/database/gdb/gdb_model_where.go index 0b168eb64..09566db91 100644 --- a/database/gdb/gdb_model_where.go +++ b/database/gdb/gdb_model_where.go @@ -2,158 +2,88 @@ // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. +// You can obtain one at https://githum.com/gogf/gf. package gdb -import ( - "fmt" - - "github.com/gogf/gf/v2/text/gstr" -) - -// doWhereType sets the condition statement for the model. The parameter `where` can be type of -// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, -// multiple conditions will be joined into where statement using "AND". -func (m *Model) doWhereType(t string, where interface{}, args ...interface{}) *Model { +func (m *Model) callWhereBuilder(builder *WhereBuilder) *Model { model := m.getModel() - if model.whereHolder == nil { - model.whereHolder = make([]ModelWhereHolder, 0) - } - if t == "" { - if len(args) == 0 { - t = whereHolderTypeNoArgs - } else { - t = whereHolderTypeDefault - } - } - model.whereHolder = append(model.whereHolder, ModelWhereHolder{ - Type: t, - Operator: whereHolderOperatorWhere, - Where: where, - Args: args, - }) + model.whereBuilder = builder return model } -// doWherefType builds condition string using fmt.Sprintf and arguments. -// Note that if the number of `args` is more than the placeholder in `format`, -// the extra `args` will be used as the where condition arguments of the Model. -func (m *Model) doWherefType(t string, format string, args ...interface{}) *Model { - var ( - placeHolderCount = gstr.Count(format, "?") - conditionStr = fmt.Sprintf(format, args[:len(args)-placeHolderCount]...) - ) - return m.doWhereType(t, conditionStr, args[len(args)-placeHolderCount:]...) -} - -// Where sets the condition statement for the model. The parameter `where` can be type of -// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, -// multiple conditions will be joined into where statement using "AND". -// Eg: -// Where("uid=10000") -// Where("uid", 10000) -// Where("money>? AND name like ?", 99999, "vip_%") -// Where("uid", 1).Where("name", "john") -// Where("status IN (?)", g.Slice{1,2,3}) -// Where("age IN(?,?)", 18, 50) -// Where(User{ Id : 1, UserName : "john"}). func (m *Model) Where(where interface{}, args ...interface{}) *Model { - return m.doWhereType(``, where, args...) + return m.callWhereBuilder(m.whereBuilder.Where(where, args...)) } -// Wheref builds condition string using fmt.Sprintf and arguments. -// Note that if the number of `args` is more than the placeholder in `format`, -// the extra `args` will be used as the where condition arguments of the Model. -// Eg: -// Wheref(`amount WHERE `amount`<100 and status='paid' -// Wheref(`amount<%d and status=%s`, 100, "paid") => WHERE `amount`<100 and status='paid' func (m *Model) Wheref(format string, args ...interface{}) *Model { - return m.doWherefType(``, format, args...) + return m.callWhereBuilder(m.whereBuilder.Wheref(format, args...)) } -// WherePri does the same logic as Model.Where except that if the parameter `where` -// is a single condition like int/string/float/slice, it treats the condition as the primary -// key value. That is, if primary key is "id" and given `where` parameter as "123", the -// WherePri function treats the condition as "id=123", but Model.Where treats the condition -// as string "123". func (m *Model) WherePri(where interface{}, args ...interface{}) *Model { - if len(args) > 0 { - return m.Where(where, args...) - } - newWhere := GetPrimaryKeyCondition(m.getPrimaryKey(), where) - return m.Where(newWhere[0], newWhere[1:]...) + return m.callWhereBuilder(m.whereBuilder.WherePri(where, args...)) } -// WhereLT builds `column < value` statement. func (m *Model) WhereLT(column string, value interface{}) *Model { - return m.Wheref(`%s < ?`, m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereLT(column, value)) } // WhereLTE builds `column <= value` statement. func (m *Model) WhereLTE(column string, value interface{}) *Model { - return m.Wheref(`%s <= ?`, m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereLTE(column, value)) } // WhereGT builds `column > value` statement. func (m *Model) WhereGT(column string, value interface{}) *Model { - return m.Wheref(`%s > ?`, m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereGT(column, value)) } // WhereGTE builds `column >= value` statement. func (m *Model) WhereGTE(column string, value interface{}) *Model { - return m.Wheref(`%s >= ?`, m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereGTE(column, value)) } // WhereBetween builds `column BETWEEN min AND max` statement. func (m *Model) WhereBetween(column string, min, max interface{}) *Model { - return m.Wheref(`%s BETWEEN ? AND ?`, m.QuoteWord(column), min, max) + return m.callWhereBuilder(m.whereBuilder.WhereBetween(column, min, max)) } // WhereLike builds `column LIKE like` statement. func (m *Model) WhereLike(column string, like string) *Model { - return m.Wheref(`%s LIKE ?`, m.QuoteWord(column), like) + return m.callWhereBuilder(m.whereBuilder.WhereLike(column, like)) } // WhereIn builds `column IN (in)` statement. func (m *Model) WhereIn(column string, in interface{}) *Model { - return m.doWherefType(whereHolderTypeIn, `%s IN (?)`, m.QuoteWord(column), in) + return m.callWhereBuilder(m.whereBuilder.WhereIn(column, in)) } // WhereNull builds `columns[0] IS NULL AND columns[1] IS NULL ...` statement. func (m *Model) WhereNull(columns ...string) *Model { - model := m - for _, column := range columns { - model = m.Wheref(`%s IS NULL`, m.QuoteWord(column)) - } - return model + return m.callWhereBuilder(m.whereBuilder.WhereNull(columns...)) } // WhereNotBetween builds `column NOT BETWEEN min AND max` statement. func (m *Model) WhereNotBetween(column string, min, max interface{}) *Model { - return m.Wheref(`%s NOT BETWEEN ? AND ?`, m.QuoteWord(column), min, max) + return m.callWhereBuilder(m.whereBuilder.WhereNotBetween(column, min, max)) } // WhereNotLike builds `column NOT LIKE like` statement. func (m *Model) WhereNotLike(column string, like interface{}) *Model { - return m.Wheref(`%s NOT LIKE ?`, m.QuoteWord(column), like) + return m.callWhereBuilder(m.whereBuilder.WhereNotLike(column, like)) } // WhereNot builds `column != value` statement. func (m *Model) WhereNot(column string, value interface{}) *Model { - return m.Wheref(`%s != ?`, m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereNot(column, value)) } // WhereNotIn builds `column NOT IN (in)` statement. func (m *Model) WhereNotIn(column string, in interface{}) *Model { - return m.doWherefType(whereHolderTypeIn, `%s NOT IN (?)`, m.QuoteWord(column), in) + return m.callWhereBuilder(m.whereBuilder.WhereNotIn(column, in)) } // WhereNotNull builds `columns[0] IS NOT NULL AND columns[1] IS NOT NULL ...` statement. func (m *Model) WhereNotNull(columns ...string) *Model { - model := m - for _, column := range columns { - model = m.Wheref(`%s IS NOT NULL`, m.QuoteWord(column)) - } - return model + return m.callWhereBuilder(m.whereBuilder.WhereNotNull(columns...)) } diff --git a/database/gdb/gdb_model_where_prefix.go b/database/gdb/gdb_model_where_prefix.go index 382a0b1a7..1ec8a466a 100644 --- a/database/gdb/gdb_model_where_prefix.go +++ b/database/gdb/gdb_model_where_prefix.go @@ -7,93 +7,71 @@ package gdb // WherePrefix performs as Where, but it adds prefix to each field in where statement. -// Eg: -// WherePrefix("order", "status", "paid") => WHERE `order`.`status`='paid' -// WherePrefix("order", struct{Status:"paid", "channel":"bank"}) => WHERE `order`.`status`='paid' AND `order`.`channel`='bank' func (m *Model) WherePrefix(prefix string, where interface{}, args ...interface{}) *Model { - model := m.getModel() - if model.whereHolder == nil { - model.whereHolder = make([]ModelWhereHolder, 0) - } - model.whereHolder = append(model.whereHolder, ModelWhereHolder{ - Type: whereHolderTypeDefault, - Operator: whereHolderOperatorWhere, - Where: where, - Args: args, - Prefix: prefix, - }) - return model + return m.callWhereBuilder(m.whereBuilder.WherePrefix(prefix, where, args...)) } // WherePrefixLT builds `prefix.column < value` statement. func (m *Model) WherePrefixLT(prefix string, column string, value interface{}) *Model { - return m.Wheref(`%s.%s < ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WherePrefixLT(prefix, column, value)) } // WherePrefixLTE builds `prefix.column <= value` statement. func (m *Model) WherePrefixLTE(prefix string, column string, value interface{}) *Model { - return m.Wheref(`%s.%s <= ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WherePrefixLTE(prefix, column, value)) } // WherePrefixGT builds `prefix.column > value` statement. func (m *Model) WherePrefixGT(prefix string, column string, value interface{}) *Model { - return m.Wheref(`%s.%s > ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WherePrefixGT(prefix, column, value)) } // WherePrefixGTE builds `prefix.column >= value` statement. func (m *Model) WherePrefixGTE(prefix string, column string, value interface{}) *Model { - return m.Wheref(`%s.%s >= ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WherePrefixGTE(prefix, column, value)) } // WherePrefixBetween builds `prefix.column BETWEEN min AND max` statement. func (m *Model) WherePrefixBetween(prefix string, column string, min, max interface{}) *Model { - return m.Wheref(`%s.%s BETWEEN ? AND ?`, m.QuoteWord(prefix), m.QuoteWord(column), min, max) + return m.callWhereBuilder(m.whereBuilder.WherePrefixBetween(prefix, column, min, max)) } // WherePrefixLike builds `prefix.column LIKE like` statement. func (m *Model) WherePrefixLike(prefix string, column string, like interface{}) *Model { - return m.Wheref(`%s.%s LIKE ?`, m.QuoteWord(prefix), m.QuoteWord(column), like) + return m.callWhereBuilder(m.whereBuilder.WherePrefixLike(prefix, column, like)) } // WherePrefixIn builds `prefix.column IN (in)` statement. func (m *Model) WherePrefixIn(prefix string, column string, in interface{}) *Model { - return m.doWherefType(whereHolderTypeIn, `%s.%s IN (?)`, m.QuoteWord(prefix), m.QuoteWord(column), in) + return m.callWhereBuilder(m.whereBuilder.WherePrefixIn(prefix, column, in)) } // WherePrefixNull builds `prefix.columns[0] IS NULL AND prefix.columns[1] IS NULL ...` statement. func (m *Model) WherePrefixNull(prefix string, columns ...string) *Model { - model := m - for _, column := range columns { - model = m.Wheref(`%s.%s IS NULL`, m.QuoteWord(prefix), m.QuoteWord(column)) - } - return model + return m.callWhereBuilder(m.whereBuilder.WherePrefixNull(prefix, columns...)) } // WherePrefixNotBetween builds `prefix.column NOT BETWEEN min AND max` statement. func (m *Model) WherePrefixNotBetween(prefix string, column string, min, max interface{}) *Model { - return m.Wheref(`%s.%s NOT BETWEEN ? AND ?`, m.QuoteWord(prefix), m.QuoteWord(column), min, max) + return m.callWhereBuilder(m.whereBuilder.WherePrefixNotBetween(prefix, column, min, max)) } // WherePrefixNotLike builds `prefix.column NOT LIKE like` statement. func (m *Model) WherePrefixNotLike(prefix string, column string, like interface{}) *Model { - return m.Wheref(`%s.%s NOT LIKE ?`, m.QuoteWord(prefix), m.QuoteWord(column), like) + return m.callWhereBuilder(m.whereBuilder.WherePrefixNotLike(prefix, column, like)) } // WherePrefixNot builds `prefix.column != value` statement. func (m *Model) WherePrefixNot(prefix string, column string, value interface{}) *Model { - return m.Wheref(`%s.%s != ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WherePrefixNot(prefix, column, value)) } // WherePrefixNotIn builds `prefix.column NOT IN (in)` statement. func (m *Model) WherePrefixNotIn(prefix string, column string, in interface{}) *Model { - return m.doWherefType(whereHolderTypeIn, `%s.%s NOT IN (?)`, m.QuoteWord(prefix), m.QuoteWord(column), in) + return m.callWhereBuilder(m.whereBuilder.WherePrefixNot(prefix, column, in)) } // WherePrefixNotNull builds `prefix.columns[0] IS NOT NULL AND prefix.columns[1] IS NOT NULL ...` statement. func (m *Model) WherePrefixNotNull(prefix string, columns ...string) *Model { - model := m - for _, column := range columns { - model = m.Wheref(`%s.%s IS NOT NULL`, m.QuoteWord(prefix), m.QuoteWord(column)) - } - return model + return m.callWhereBuilder(m.whereBuilder.WherePrefixNotNull(prefix, columns...)) } diff --git a/database/gdb/gdb_model_whereor.go b/database/gdb/gdb_model_whereor.go index 3d288459e..9dcf14ae6 100644 --- a/database/gdb/gdb_model_whereor.go +++ b/database/gdb/gdb_model_whereor.go @@ -6,113 +6,72 @@ package gdb -import ( - "fmt" - - "github.com/gogf/gf/v2/text/gstr" -) - -// WhereOr adds "OR" condition to the where statement. -func (m *Model) doWhereOrType(t string, where interface{}, args ...interface{}) *Model { - model := m.getModel() - if model.whereHolder == nil { - model.whereHolder = make([]ModelWhereHolder, 0) - } - model.whereHolder = append(model.whereHolder, ModelWhereHolder{ - Type: t, - Operator: whereHolderOperatorOr, - Where: where, - Args: args, - }) - return model -} - -// WhereOrf builds `OR` condition string using fmt.Sprintf and arguments. -func (m *Model) doWhereOrfType(t string, format string, args ...interface{}) *Model { - var ( - placeHolderCount = gstr.Count(format, "?") - conditionStr = fmt.Sprintf(format, args[:len(args)-placeHolderCount]...) - ) - return m.doWhereOrType(t, conditionStr, args[len(args)-placeHolderCount:]...) -} - // WhereOr adds "OR" condition to the where statement. func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model { - return m.doWhereOrType(``, where, args...) + return m.callWhereBuilder(m.whereBuilder.WhereOr(where, args...)) } // WhereOrf builds `OR` condition string using fmt.Sprintf and arguments. -// Eg: -// WhereOrf(`amount WHERE xxx OR `amount`<100 and status='paid' -// WhereOrf(`amount<%d and status=%s`, 100, "paid") => WHERE xxx OR `amount`<100 and status='paid' func (m *Model) WhereOrf(format string, args ...interface{}) *Model { - return m.doWhereOrfType(``, format, args...) + return m.callWhereBuilder(m.whereBuilder.WhereOrf(format, args...)) } // WhereOrLT builds `column < value` statement in `OR` conditions.. func (m *Model) WhereOrLT(column string, value interface{}) *Model { - return m.WhereOrf(`%s < ?`, column, value) + return m.callWhereBuilder(m.whereBuilder.WhereOrLT(column, value)) } // WhereOrLTE builds `column <= value` statement in `OR` conditions.. func (m *Model) WhereOrLTE(column string, value interface{}) *Model { - return m.WhereOrf(`%s <= ?`, column, value) + return m.callWhereBuilder(m.whereBuilder.WhereOrLTE(column, value)) } // WhereOrGT builds `column > value` statement in `OR` conditions.. func (m *Model) WhereOrGT(column string, value interface{}) *Model { - return m.WhereOrf(`%s > ?`, column, value) + return m.callWhereBuilder(m.whereBuilder.WhereOrGT(column, value)) } // WhereOrGTE builds `column >= value` statement in `OR` conditions.. func (m *Model) WhereOrGTE(column string, value interface{}) *Model { - return m.WhereOrf(`%s >= ?`, column, value) + return m.callWhereBuilder(m.whereBuilder.WhereOrGTE(column, value)) } // WhereOrBetween builds `column BETWEEN min AND max` statement in `OR` conditions. func (m *Model) WhereOrBetween(column string, min, max interface{}) *Model { - return m.WhereOrf(`%s BETWEEN ? AND ?`, m.QuoteWord(column), min, max) + return m.callWhereBuilder(m.whereBuilder.WhereOrBetween(column, min, max)) } // WhereOrLike builds `column LIKE like` statement in `OR` conditions. func (m *Model) WhereOrLike(column string, like interface{}) *Model { - return m.WhereOrf(`%s LIKE ?`, m.QuoteWord(column), like) + return m.callWhereBuilder(m.whereBuilder.WhereOrLike(column, like)) } // WhereOrIn builds `column IN (in)` statement in `OR` conditions. func (m *Model) WhereOrIn(column string, in interface{}) *Model { - return m.doWhereOrfType(whereHolderTypeIn, `%s IN (?)`, m.QuoteWord(column), in) + return m.callWhereBuilder(m.whereBuilder.WhereOrIn(column, in)) } // WhereOrNull builds `columns[0] IS NULL OR columns[1] IS NULL ...` statement in `OR` conditions. func (m *Model) WhereOrNull(columns ...string) *Model { - model := m - for _, column := range columns { - model = m.WhereOrf(`%s IS NULL`, m.QuoteWord(column)) - } - return model + return m.callWhereBuilder(m.whereBuilder.WhereOrNull(columns...)) } // WhereOrNotBetween builds `column NOT BETWEEN min AND max` statement in `OR` conditions. func (m *Model) WhereOrNotBetween(column string, min, max interface{}) *Model { - return m.WhereOrf(`%s NOT BETWEEN ? AND ?`, m.QuoteWord(column), min, max) + return m.callWhereBuilder(m.whereBuilder.WhereOrNotBetween(column, min, max)) } // WhereOrNotLike builds `column NOT LIKE like` statement in `OR` conditions. func (m *Model) WhereOrNotLike(column string, like interface{}) *Model { - return m.WhereOrf(`%s NOT LIKE ?`, m.QuoteWord(column), like) + return m.callWhereBuilder(m.whereBuilder.WhereOrNotLike(column, like)) } // WhereOrNotIn builds `column NOT IN (in)` statement. func (m *Model) WhereOrNotIn(column string, in interface{}) *Model { - return m.doWhereOrfType(whereHolderTypeIn, `%s NOT IN (?)`, m.QuoteWord(column), in) + return m.callWhereBuilder(m.whereBuilder.WhereOrNotIn(column, in)) } // WhereOrNotNull builds `columns[0] IS NOT NULL OR columns[1] IS NOT NULL ...` statement in `OR` conditions. func (m *Model) WhereOrNotNull(columns ...string) *Model { - model := m - for _, column := range columns { - model = m.WhereOrf(`%s IS NOT NULL`, m.QuoteWord(column)) - } - return model + return m.callWhereBuilder(m.whereBuilder.WhereOrNotNull(columns...)) } diff --git a/database/gdb/gdb_model_whereor_prefix.go b/database/gdb/gdb_model_whereor_prefix.go index 7230895cb..ebf5ad575 100644 --- a/database/gdb/gdb_model_whereor_prefix.go +++ b/database/gdb/gdb_model_whereor_prefix.go @@ -7,88 +7,66 @@ package gdb // WhereOrPrefix performs as WhereOr, but it adds prefix to each field in where statement. -// Eg: -// WhereOrPrefix("order", "status", "paid") => WHERE xxx OR (`order`.`status`='paid') -// WhereOrPrefix("order", struct{Status:"paid", "channel":"bank"}) => WHERE xxx OR (`order`.`status`='paid' AND `order`.`channel`='bank') func (m *Model) WhereOrPrefix(prefix string, where interface{}, args ...interface{}) *Model { - model := m.getModel() - if model.whereHolder == nil { - model.whereHolder = make([]ModelWhereHolder, 0) - } - model.whereHolder = append(model.whereHolder, ModelWhereHolder{ - Type: whereHolderTypeDefault, - Operator: whereHolderOperatorOr, - Where: where, - Args: args, - Prefix: prefix, - }) - return model + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefix(prefix, where, args...)) } // WhereOrPrefixLT builds `prefix.column < value` statement in `OR` conditions.. func (m *Model) WhereOrPrefixLT(prefix string, column string, value interface{}) *Model { - return m.WhereOrf(`%s.%s < ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixLT(prefix, column, value)) } // WhereOrPrefixLTE builds `prefix.column <= value` statement in `OR` conditions.. func (m *Model) WhereOrPrefixLTE(prefix string, column string, value interface{}) *Model { - return m.WhereOrf(`%s.%s <= ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixLTE(prefix, column, value)) } // WhereOrPrefixGT builds `prefix.column > value` statement in `OR` conditions.. func (m *Model) WhereOrPrefixGT(prefix string, column string, value interface{}) *Model { - return m.WhereOrf(`%s.%s > ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixGT(prefix, column, value)) } // WhereOrPrefixGTE builds `prefix.column >= value` statement in `OR` conditions.. func (m *Model) WhereOrPrefixGTE(prefix string, column string, value interface{}) *Model { - return m.WhereOrf(`%s.%s >= ?`, m.QuoteWord(prefix), m.QuoteWord(column), value) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixGTE(prefix, column, value)) } // WhereOrPrefixBetween builds `prefix.column BETWEEN min AND max` statement in `OR` conditions. func (m *Model) WhereOrPrefixBetween(prefix string, column string, min, max interface{}) *Model { - return m.WhereOrf(`%s.%s BETWEEN ? AND ?`, m.QuoteWord(prefix), m.QuoteWord(column), min, max) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixBetween(prefix, column, min, max)) } // WhereOrPrefixLike builds `prefix.column LIKE like` statement in `OR` conditions. func (m *Model) WhereOrPrefixLike(prefix string, column string, like interface{}) *Model { - return m.WhereOrf(`%s.%s LIKE ?`, m.QuoteWord(prefix), m.QuoteWord(column), like) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixLike(prefix, column, like)) } // WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions. func (m *Model) WhereOrPrefixIn(prefix string, column string, in interface{}) *Model { - return m.doWhereOrfType(whereHolderTypeIn, `%s.%s IN (?)`, m.QuoteWord(prefix), m.QuoteWord(column), in) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixIn(prefix, column, in)) } // WhereOrPrefixNull builds `prefix.columns[0] IS NULL OR prefix.columns[1] IS NULL ...` statement in `OR` conditions. func (m *Model) WhereOrPrefixNull(prefix string, columns ...string) *Model { - model := m - for _, column := range columns { - model = m.WhereOrf(`%s.%s IS NULL`, m.QuoteWord(prefix), m.QuoteWord(column)) - } - return model + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNull(prefix, columns...)) } // WhereOrPrefixNotBetween builds `prefix.column NOT BETWEEN min AND max` statement in `OR` conditions. func (m *Model) WhereOrPrefixNotBetween(prefix string, column string, min, max interface{}) *Model { - return m.WhereOrf(`%s.%s NOT BETWEEN ? AND ?`, m.QuoteWord(prefix), m.QuoteWord(column), min, max) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotBetween(prefix, column, min, max)) } // WhereOrPrefixNotLike builds `prefix.column NOT LIKE like` statement in `OR` conditions. func (m *Model) WhereOrPrefixNotLike(prefix string, column string, like interface{}) *Model { - return m.WhereOrf(`%s.%s NOT LIKE ?`, m.QuoteWord(prefix), m.QuoteWord(column), like) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotLike(prefix, column, like)) } // WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement. func (m *Model) WhereOrPrefixNotIn(prefix string, column string, in interface{}) *Model { - return m.doWhereOrfType(whereHolderTypeIn, `%s.%s NOT IN (?)`, m.QuoteWord(prefix), m.QuoteWord(column), in) + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotIn(prefix, column, in)) } // WhereOrPrefixNotNull builds `prefix.columns[0] IS NOT NULL OR prefix.columns[1] IS NOT NULL ...` statement in `OR` conditions. func (m *Model) WhereOrPrefixNotNull(prefix string, columns ...string) *Model { - model := m - for _, column := range columns { - model = m.WhereOrf(`%s.%s IS NOT NULL`, m.QuoteWord(prefix), m.QuoteWord(column)) - } - return model + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotNull(prefix, columns...)) }