Merge branch 'feature/wherebuilder' into develop

This commit is contained in:
John Guo
2022-05-07 17:49:30 +08:00
22 changed files with 877 additions and 771 deletions

View File

@ -6,11 +6,14 @@ on:
branches:
- master
- develop
- feature/**
- fix/**
pull_request:
branches:
- master
- develop
- feature/**
- fix/**
env:
GF_DEBUG: 0

View File

@ -216,10 +216,10 @@ func (d *Driver) DoFilter(
ctx context.Context, link gdb.Link, originSql string, args []interface{},
) (newSql string, newArgs []interface{}, err error) {
// It replaces STD SQL to Clickhouse SQL grammar.
// MySQL eg: UPDATE visits SET xxx
// Clickhouse eg: ALTER TABLE visits UPDATE xxx
// MySQL eg: DELETE FROM VISIT
// Clickhouse eg: ALTER TABLE VISIT DELETE WHERE filter_expr
// MySQL eg: UPDATE `table` SET xxx
// Clickhouse eg: ALTER TABLE `table` UPDATE xxx
// MySQL eg: DELETE FROM `table`
// Clickhouse eg: ALTER TABLE `table` DELETE WHERE filter_expr
result, err := gregex.MatchString("(?i)^UPDATE|DELETE", originSql)
if err != nil {
return "", nil, err

View File

@ -104,7 +104,7 @@ func (d *Driver) FilteredLink() string {
// GetChars returns the security char for this type of database.
func (d *Driver) GetChars() (charLeft string, charRight string) {
return "\"", "\""
return `"`, `"`
}
// DoFilter deals with the sql string before commits it to underlying sql driver.

View File

@ -108,7 +108,7 @@ func (d *Driver) FilteredLink() string {
// GetChars returns the security char for this type of database.
func (d *Driver) GetChars() (charLeft string, charRight string) {
return "\"", "\""
return `"`, `"`
}
// DoFilter deals with the sql string before commits it to underlying sql driver.

View File

@ -106,7 +106,7 @@ func (d *Driver) FilteredLink() string {
// GetChars returns the security char for this type of database.
func (d *Driver) GetChars() (charLeft string, charRight string) {
return "\"", "\""
return `"`, `"`
}
// DoFilter deals with the sql string before commits it to underlying sql driver.

View File

@ -1,245 +0,0 @@
// 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 (
"context"
"strings"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gctx"
"github.com/longbridgeapp/sqlparser"
)
// ShardingInput is input parameters for custom sharding handler.
type ShardingInput struct {
Table string // Current operation table name.
Schema string // Current operation schema, usually empty string which means uses default schema from configuration.
OperationData map[string]Value // Accurate readonly key-value data pairs from INSERT/UPDATE statement.
ConditionData map[string]Value // Accurate readonly key-value condition pairs from SELECT/UPDATE/DELETE statement.
}
// ShardingOutput is output parameters for custom sharding handler.
type ShardingOutput struct {
Table string // New table name for current operation. Use empty string for no changes of table name.
Schema string // New schema name for current operation. Use empty string for using default schema from configuration.
}
// ShardingHandler is a custom function for custom sharding table and schema for DB operation.
type ShardingHandler func(ctx context.Context, in ShardingInput) (out *ShardingOutput, err error)
const (
ctxKeyForShardingHandler gctx.StrKey = "ShardingHandler"
)
// Sharding creates and returns a new model with sharding handler.
func (m *Model) Sharding(handler ShardingHandler) *Model {
var (
ctx = m.GetCtx()
model = m.getModel()
)
model.shardingHandler = handler
// Inject sharding handler into context.
model = model.Ctx(model.injectShardingInputCaller(ctx))
return model
}
// injectShardingInputCaller injects custom sharding handler into context.
func (m *Model) injectShardingInputCaller(ctx context.Context) context.Context {
if m.shardingHandler == nil {
return ctx
}
if ctx.Value(ctxKeyForShardingHandler) != nil {
return ctx
}
return context.WithValue(ctx, ctxKeyForShardingHandler, m.shardingHandler)
}
type callShardingHandlerFromCtxInput struct {
Sql string
FormattedSql string
}
type callShardingHandlerFromCtxOutput struct {
Sql string
Table string
Schema string
ParsedSqlOutput *parseFormattedSqlOutput
}
func (c *Core) callShardingHandlerFromCtx(
ctx context.Context, in callShardingHandlerFromCtxInput,
) (out *callShardingHandlerFromCtxOutput, err error) {
var (
newSql = in.Sql
ctxValue interface{}
shardingHandler ShardingHandler
ok bool
)
// If no sharding handler, it does nothing.
if ctxValue = ctx.Value(ctxKeyForShardingHandler); ctxValue == nil {
return nil, nil
}
if shardingHandler, ok = ctxValue.(ShardingHandler); !ok {
return nil, nil
}
parsedOut, err := c.parseFormattedSql(in.FormattedSql)
if err != nil {
return nil, err
}
var shardingIn = ShardingInput{
Table: parsedOut.Table,
Schema: c.db.GetSchema(),
OperationData: parsedOut.OperationData,
ConditionData: parsedOut.ConditionData,
}
shardingOut, err := shardingHandler(ctx, shardingIn)
if err != nil {
return nil, gerror.Wrap(err, `calling sharding handler failed`)
}
if shardingOut.Table != shardingIn.Table || shardingOut.Schema != shardingIn.Schema {
if shardingOut.Table != shardingIn.Table {
newSql, err = c.formatSqlWithNewTable(in.Sql, shardingOut.Table)
if err != nil {
return nil, err
}
}
out = &callShardingHandlerFromCtxOutput{
Sql: newSql,
Table: shardingOut.Table,
Schema: shardingOut.Schema,
ParsedSqlOutput: parsedOut,
}
return out, nil
}
return nil, nil
}
// formatSqlWithNewTable modifies given `sql` and returns a sql with new table name `table`.
func (c *Core) formatSqlWithNewTable(sql, table string) (newSql string, err error) {
parsedStmt, err := sqlparser.NewParser(strings.NewReader(sql)).ParseStatement()
if err != nil {
return "", gerror.Wrapf(err, `parse failed for SQL: %s`, sql)
}
newTable := &sqlparser.TableName{Name: &sqlparser.Ident{Name: table}}
switch stmt := parsedStmt.(type) {
case *sqlparser.SelectStatement:
stmt.FromItems = newTable
return stmt.String(), nil
case *sqlparser.InsertStatement:
stmt.TableName = newTable
return stmt.String(), nil
case *sqlparser.UpdateStatement:
stmt.TableName = newTable
return stmt.String(), nil
case *sqlparser.DeleteStatement:
stmt.TableName = newTable
return stmt.String(), nil
default:
return "", gerror.Wrapf(err, `unsupported SQL: %s`, sql)
}
}
type parseFormattedSqlOutput struct {
Table string
OperationData map[string]Value
ConditionData map[string]Value
ParsedStmt sqlparser.Statement
SelectedFields []string
}
func (c *Core) parseFormattedSql(formattedSql string) (*parseFormattedSqlOutput, error) {
var (
condition sqlparser.Expr
err error
out = &parseFormattedSqlOutput{
SelectedFields: make([]string, 0),
OperationData: make(map[string]Value),
ConditionData: make(map[string]Value),
}
)
out.ParsedStmt, err = sqlparser.NewParser(strings.NewReader(formattedSql)).ParseStatement()
if err != nil {
return nil, gerror.Wrapf(err, `parse failed for SQL: %s`, formattedSql)
}
switch stmt := out.ParsedStmt.(type) {
case *sqlparser.SelectStatement:
if stmt.FromItems != nil {
table, ok := stmt.FromItems.(*sqlparser.TableName)
if !ok {
return nil, gerror.Newf(
`invalid table name "%s" in SQL: %s`,
stmt.FromItems.String(), formattedSql,
)
}
out.Table = table.TableName()
}
condition = stmt.Condition
if stmt.Columns != nil {
for _, column := range *stmt.Columns {
if column.Alias != nil {
out.SelectedFields = append(out.SelectedFields, column.Alias.Name)
} else if column.Expr != nil {
out.SelectedFields = append(out.SelectedFields, column.Expr.String())
}
}
}
case *sqlparser.InsertStatement:
out.Table = stmt.TableName.TableName()
if len(stmt.Expressions) > 0 && len(stmt.ColumnNames) > 0 {
names := make([]string, len(stmt.ColumnNames))
for i, ident := range stmt.ColumnNames {
names[i] = ident.Name
}
// It just uses the first item.
for i, expr := range stmt.Expressions[0].Exprs {
c.injectDataByExpr(out.OperationData, names[i], expr)
}
}
case *sqlparser.UpdateStatement:
out.Table = stmt.TableName.TableName()
condition = stmt.Condition
if len(stmt.Assignments) > 0 {
for _, assignment := range stmt.Assignments {
if len(assignment.Columns) > 0 {
c.injectDataByExpr(out.OperationData, assignment.Columns[0].Name, assignment.Expr)
}
}
}
case *sqlparser.DeleteStatement:
out.Table = stmt.TableName.TableName()
condition = stmt.Condition
default:
return nil, gerror.Wrapf(err, `unsupported SQL: %s`, formattedSql)
}
err = sqlparser.Walk(sqlparser.VisitFunc(func(node sqlparser.Node) error {
if n, ok := node.(*sqlparser.BinaryExpr); ok {
if x, ok := n.X.(*sqlparser.Ident); ok {
if n.Op == sqlparser.EQ {
c.injectDataByExpr(out.ConditionData, x.Name, n.Y)
}
}
}
return nil
}), condition)
return out, err
}
func (c *Core) injectDataByExpr(data map[string]Value, name string, expr sqlparser.Expr) {
switch exprImp := expr.(type) {
case *sqlparser.StringLit:
data[name] = gvar.New(exprImp.Value)
case *sqlparser.NumberLit:
data[name] = gvar.New(exprImp.Value)
default:
data[name] = gvar.New(exprImp.String())
}
}

View File

@ -137,31 +137,6 @@ type sqlParsingHandlerOutput struct {
DoCommitInput
}
func (c *Core) sqlParsingHandler(ctx context.Context, in sqlParsingHandlerInput) (out *sqlParsingHandlerOutput, err error) {
var shardingOut *callShardingHandlerFromCtxOutput
// Sharding handling.
shardingOut, err = c.callShardingHandlerFromCtx(ctx, callShardingHandlerFromCtxInput{
Sql: in.Sql,
FormattedSql: in.FormattedSql,
})
if err != nil {
return
}
if shardingOut != nil {
if shardingOut.Sql != "" {
in.Sql = shardingOut.Sql
}
// If schema changes, it here creates and uses a new DB link operation object.
if shardingOut.Schema != c.db.GetSchema() {
in.Link, err = c.db.GetCore().GetLink(ctx, in.Link.IsOnMaster(), shardingOut.Schema)
}
}
out = &sqlParsingHandlerOutput{
DoCommitInput: in.DoCommitInput,
}
return
}
// DoCommit commits current sql and arguments to underlying sql driver.
func (c *Core) DoCommit(ctx context.Context, in DoCommitInput) (out DoCommitOutput, err error) {
// Inject internal data into ctx, especially for transaction creating.
@ -180,18 +155,6 @@ func (c *Core) DoCommit(ctx context.Context, in DoCommitInput) (out DoCommitOutp
timestampMilli1 = gtime.TimestampMilli()
)
// SQL parser handler.
sqlParsingHandlerOut, err := c.sqlParsingHandler(ctx, sqlParsingHandlerInput{
DoCommitInput: in,
FormattedSql: formattedSql,
})
if err != nil {
return
}
if sqlParsingHandlerOut != nil {
in = sqlParsingHandlerOut.DoCommitInput
}
// Trace span start.
tr := otel.GetTracerProvider().Tracer(traceInstrumentName, trace.WithInstrumentationVersion(gf.VERSION))
ctx, span := tr.Start(ctx, in.Type, trace.WithSpanKind(trace.SpanKindInternal))

View File

@ -13,7 +13,6 @@ import (
"net/url"
_ "github.com/go-sql-driver/mysql"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"

View File

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

View File

@ -17,41 +17,43 @@ 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.
*modelWhereBuilder
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.
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.
}
type modelWhereBuilder = WhereBuilder
// ModelHandler is a function that handles given Model and returns a new Model that is custom modified.
type ModelHandler func(m *Model) *Model
@ -59,15 +61,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 +95,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 +137,8 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
filter: true,
extraArgs: extraArgs,
}
m.whereBuilder = m.Builder()
m.whereBuilder.safe = &m.safe
if defaultModelSafe {
m.safe = true
}
@ -258,8 +253,8 @@ func (m *Model) Schema(schema string) *Model {
return model
}
// Clone creates and returns a new model which is a clone of current model.
// Note that it uses deep-copy for the clone.
// Clone creates and returns a new model which is a Clone of current model.
// Note that it uses deep-copy for the Clone.
func (m *Model) Clone() *Model {
newModel := (*Model)(nil)
if m.tx != nil {
@ -269,15 +264,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)

View File

@ -0,0 +1,139 @@
// 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"
)
// WhereBuilder holds multiple where conditions in a group.
type WhereBuilder struct {
safe *bool // If nil, it uses the safe attribute of its model.
model *Model // A WhereBuilder should be bound to certain 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.".
}
// Builder creates and returns a WhereBuilder.
func (m *Model) Builder() *WhereBuilder {
// The WhereBuilder is safe in default when it is created using Builder().
var isSafe = true
b := &WhereBuilder{
safe: &isSafe,
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 {
var isSafe bool
if b.safe != nil {
isSafe = *b.safe
} else {
isSafe = b.model.safe
}
if !isSafe {
return b
} else {
return b.Clone()
}
}
// Clone clones and returns a WhereBuilder that is a copy of current one.
func (b *WhereBuilder) Clone() *WhereBuilder {
newBuilder := b.model.Builder()
newBuilder.safe = b.safe
newBuilder.whereHolder = make([]WhereHolder, len(b.whereHolder))
copy(newBuilder.whereHolder, b.whereHolder)
return newBuilder
}
// Build builds current WhereBuilder and returns the condition string and parameters.
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, 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...)
}
}
}
}
return
}
// convertWhereBuilder converts parameter `where` to condition string and parameters if `where` is also a WhereBuilder.
func (b *WhereBuilder) convertWhereBuilder(where interface{}, args []interface{}) (newWhere interface{}, newArgs []interface{}) {
var builder *WhereBuilder
switch v := where.(type) {
case WhereBuilder:
builder = &v
case *WhereBuilder:
builder = v
}
if builder != nil {
conditionWhere, conditionArgs := builder.Build()
if len(b.whereHolder) == 0 {
conditionWhere = "(" + conditionWhere + ")"
}
return conditionWhere, conditionArgs
}
return where, args
}

View File

@ -0,0 +1,161 @@
// 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(whereType string, where interface{}, args ...interface{}) *WhereBuilder {
where, args = b.convertWhereBuilder(where, args)
builder := b.getBuilder()
if builder.whereHolder == nil {
builder.whereHolder = make([]WhereHolder, 0)
}
if whereType == "" {
if len(args) == 0 {
whereType = whereHolderTypeNoArgs
} else {
whereType = whereHolderTypeDefault
}
}
builder.whereHolder = append(builder.whereHolder, WhereHolder{
Type: whereType,
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<? and status=%s`, "paid", 100) => 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
}

View File

@ -0,0 +1,101 @@
// 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 {
where, args = b.convertWhereBuilder(where, args)
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
}

View File

@ -0,0 +1,120 @@
// 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 {
where, args = b.convertWhereBuilder(where, args)
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<? and status=%s`, "paid", 100) => 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
}

View File

@ -0,0 +1,93 @@
// 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 {
where, args = b.convertWhereBuilder(where, args)
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
}

View File

@ -594,86 +594,28 @@ 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
}
return 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...)
}
}
}
// 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
}
// Soft deletion.
// WHERE
conditionWhere, conditionArgs = m.whereBuilder.Build()
softDeletingCondition := m.getConditionForSoftDeleting()
if m.rawSql != "" && conditionWhere != "" {
if gstr.ContainsI(m.rawSql, " WHERE ") {
@ -692,24 +634,19 @@ func (m *Model) formatCondition(ctx context.Context, limit1 bool, isCountStateme
conditionWhere = " WHERE " + conditionWhere
}
}
// GROUP BY.
if m.groupBy != "" {
conditionExtra += " GROUP BY " + m.groupBy
}
// 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

View File

@ -2,74 +2,32 @@
//
// 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 {
// callWhereBuilder creates and returns a new Model, and sets its WhereBuilder if current Model is safe.
// It sets the WhereBuilder and returns current Model directly if it is not safe.
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
// 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"}).
// See WhereBuilder.Where.
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<? and status=%s`, "paid", 100) => WHERE `amount`<100 and status='paid'
// Wheref(`amount<%d and status=%s`, 100, "paid") => WHERE `amount`<100 and status='paid'
// See WhereBuilder.Wheref.
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`
@ -77,83 +35,85 @@ func (m *Model) Wheref(format string, args ...interface{}) *Model {
// 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".
// See WhereBuilder.WherePri.
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.
// See WhereBuilder.WhereLT.
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.
// See WhereBuilder.WhereLTE.
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.
// See WhereBuilder.WhereGT.
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.
// See WhereBuilder.WhereGTE.
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.
// See WhereBuilder.WhereBetween.
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.
// See WhereBuilder.WhereLike.
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.
// See WhereBuilder.WhereIn.
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.
// See WhereBuilder.WhereNull.
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.
// See WhereBuilder.WhereNotBetween.
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.
// See WhereBuilder.WhereNotLike.
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.
// See WhereBuilder.WhereNot.
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.
// See WhereBuilder.WhereNotIn.
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.
// See WhereBuilder.WhereNotNull.
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...))
}

View File

@ -7,93 +7,85 @@
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'
// See WhereBuilder.WherePrefix.
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.
// See WhereBuilder.WherePrefixLT.
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.
// See WhereBuilder.WherePrefixLTE.
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.
// See WhereBuilder.WherePrefixGT.
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.
// See WhereBuilder.WherePrefixGTE.
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.
// See WhereBuilder.WherePrefixBetween.
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.
// See WhereBuilder.WherePrefixLike.
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.
// See WhereBuilder.WherePrefixIn.
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.
// See WhereBuilder.WherePrefixNull.
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.
// See WhereBuilder.WherePrefixNotBetween.
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.
// See WhereBuilder.WherePrefixNotLike.
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.
// See WhereBuilder.WherePrefixNot.
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.
// See WhereBuilder.WherePrefixNotIn.
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.
// See WhereBuilder.WherePrefixNotNull.
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...))
}

View File

@ -6,113 +6,86 @@
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.
// See WhereBuilder.WhereOr.
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<? and status=%s`, "paid", 100) => WHERE xxx OR `amount`<100 and status='paid'
// WhereOrf(`amount<%d and status=%s`, 100, "paid") => WHERE xxx OR `amount`<100 and status='paid'
// See WhereBuilder.WhereOrf.
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..
// See WhereBuilder.WhereOrLT.
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..
// See WhereBuilder.WhereOrLTE.
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..
// See WhereBuilder.WhereOrGT.
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..
// See WhereBuilder.WhereOrGTE.
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.
// See WhereBuilder.WhereOrBetween.
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.
// See WhereBuilder.WhereOrLike.
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.
// See WhereBuilder.WhereOrIn.
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.
// See WhereBuilder.WhereOrNull.
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.
// See WhereBuilder.WhereOrNotBetween.
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.
// See WhereBuilder.WhereOrNotLike.
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.
// See WhereBuilder.WhereOrNotIn.
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.
// See WhereBuilder.WhereOrNotNull.
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...))
}

View File

@ -7,88 +7,79 @@
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')
// See WhereBuilder.WhereOrPrefix.
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..
// See WhereBuilder.WhereOrPrefixLT.
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..
// See WhereBuilder.WhereOrPrefixLTE.
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..
// See WhereBuilder.WhereOrPrefixGT.
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..
// See WhereBuilder.WhereOrPrefixGTE.
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.
// See WhereBuilder.WhereOrPrefixBetween.
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.
// See WhereBuilder.WhereOrPrefixLike.
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.
// See WhereBuilder.WhereOrPrefixIn.
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.
// See WhereBuilder.WhereOrPrefixNull.
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.
// See WhereBuilder.WhereOrPrefixNotBetween.
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.
// See WhereBuilder.WhereOrPrefixNotLike.
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.
// See WhereBuilder.WhereOrPrefixNotIn.
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.
// See WhereBuilder.WhereOrPrefixNotNull.
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...))
}

View File

@ -0,0 +1,63 @@
// 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_test
import (
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_Model_Builder(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
all, err := m.Where(
b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}),
).All()
t.AssertNil(err)
t.Assert(len(all), 6)
})
// Where And
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
all, err := m.Where(
b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}),
).Where(
b.Where("id", g.Slice{2, 3}).WhereOr("id", g.Slice{5, 6}),
).Where(
b.Where("id", g.Slice{3}).Where("id", g.Slice{1, 2, 3}),
).All()
t.AssertNil(err)
t.Assert(len(all), 1)
})
db.SetDebug(true)
// Where Or
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
all, err := m.WhereOr(
b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}),
).WhereOr(
b.Where("id", g.Slice{2, 3}).WhereOr("id", g.Slice{5, 6}),
).WhereOr(
b.Where("id", g.Slice{3}).Where("id", g.Slice{1, 2, 3}),
).All()
t.AssertNil(err)
t.Assert(len(all), 6)
})
}

View File

@ -1,138 +0,0 @@
// 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_test
import (
"context"
"testing"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_Model_Sharding(t *testing.T) {
table1 := createTable()
table2 := createTable()
defer dropTable(table1)
defer dropTable(table2)
gtest.C(t, func(t *gtest.T) {
_, err1 := db.Model(table1).Data(g.Map{
"id": 1,
}).Insert()
t.AssertNil(err1)
_, err2 := db.Model(table2).Data(g.Map{
"id": 2,
}).Insert()
t.AssertNil(err2)
})
// no sharding.
gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table1).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"].String(), 1)
})
// with sharding handler.
gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table1).Sharding(func(ctx context.Context, in gdb.ShardingInput) (out *gdb.ShardingOutput, err error) {
out = &gdb.ShardingOutput{
Table: table2,
}
return
}).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"].String(), 2)
})
// with sharding handler and no existence table name.
gtest.C(t, func(t *gtest.T) {
all, err := db.Model("none").Sharding(func(ctx context.Context, in gdb.ShardingInput) (out *gdb.ShardingOutput, err error) {
out = &gdb.ShardingOutput{
Table: table2,
}
return
}).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"].String(), 2)
})
// with sharding handler and no existence table name and tables fields retrieving.
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
Password string
NickName string
}
var users []User
err := db.Model("none").Sharding(func(ctx context.Context, in gdb.ShardingInput) (out *gdb.ShardingOutput, err error) {
out = &gdb.ShardingOutput{
Table: table2,
}
return
}).Scan(&users)
t.AssertNil(err)
t.Assert(len(users), 1)
t.Assert(users[0].Id, 2)
})
}
func Test_Model_Sharding_Schema(t *testing.T) {
var (
db1 = db
db2 = db.Schema(TestSchema2)
table1 = createTableWithDb(db1)
table2 = createTableWithDb(db2)
)
defer dropTableWithDb(db1, table1)
defer dropTableWithDb(db2, table2)
gtest.C(t, func(t *gtest.T) {
_, err1 := db1.Model(table1).Data(g.Map{
"id": 1,
}).Insert()
t.AssertNil(err1)
_, err2 := db2.Model(table2).Data(g.Map{
"id": 2,
}).Insert()
t.AssertNil(err2)
})
// no sharding.
gtest.C(t, func(t *gtest.T) {
all, err := db1.Model(table1).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"].String(), 1)
})
gtest.C(t, func(t *gtest.T) {
_, err := db1.Model(table2).All()
// Table not exist error.
t.AssertNE(err, nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db2.Model(table2).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"].String(), 2)
})
// with sharding handler and no existence table name and schema change.
gtest.C(t, func(t *gtest.T) {
all, err := db1.Model("none").Sharding(func(ctx context.Context, in gdb.ShardingInput) (out *gdb.ShardingOutput, err error) {
out = &gdb.ShardingOutput{
Table: table2,
Schema: TestSchema2,
}
return
}).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"].String(), 2)
})
}