From f2ef7454c2865a61119ed12dec47f08012b4e3a6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 17 Nov 2021 21:29:46 +0800 Subject: [PATCH] improve prefix feature for package gdb --- database/gdb/gdb_func.go | 26 ++- database/gdb/gdb_model.go | 2 +- database/gdb/gdb_model_condition.go | 82 +++----- database/gdb/gdb_model_condition_prefix.go | 184 ++++++++++++++++++ database/gdb/gdb_model_fields.go | 10 +- database/gdb/gdb_model_select.go | 8 +- database/gdb/gdb_model_utility.go | 9 + ...=> gdb_z_mysql_model_where_prefix_test.go} | 28 +++ 8 files changed, 272 insertions(+), 77 deletions(-) create mode 100644 database/gdb/gdb_model_condition_prefix.go rename database/gdb/{gdb_z_mysql_model_where_test.go => gdb_z_mysql_model_where_prefix_test.go} (70%) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 50edd7e36..8c69f2762 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -376,7 +376,7 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa return handleArguments(sql, args) } -type formatWhereInput struct { +type formatWhereHolderInput struct { Where interface{} Args []interface{} OmitNil bool @@ -386,8 +386,8 @@ type formatWhereInput struct { Prefix string // Field prefix, eg: "user.", "order.". } -// formatWhere formats where statement and its arguments for `Where` and `Having` statements. -func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interface{}) { +// formatWhereHolder formats where statement and its arguments for `Where` and `Having` statements. +func formatWhereHolder(db DB, in formatWhereHolderInput) (newWhere string, newArgs []interface{}) { var ( buffer = bytes.NewBuffer(nil) reflectInfo = utils.OriginValueAndKind(in.Where) @@ -454,8 +454,8 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa var ( reflectType = reflectInfo.OriginValue.Type() structField reflect.StructField + data = DataToMapDeep(in.Where) ) - data := DataToMapDeep(in.Where) if in.Table != "" { data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true) } @@ -484,9 +484,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa default: // Usually a string. - whereStr := gconv.String(in.Where) - // Is `whereStr` a field name which composed as a key-value condition? // Eg: // Where("id", 1) @@ -514,9 +512,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa // Regular string and parameter place holder handling. // Eg: // Where("id in(?) and name=?", g.Slice{1,2,3}, "john") - i := 0 - for { if i >= len(in.Args) { break @@ -608,13 +604,13 @@ func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, new } type formatWhereKeyValueInput struct { - Db DB - Buffer *bytes.Buffer - Args []interface{} - Key string - Value interface{} - OmitEmpty bool - Prefix string // Field prefix, eg: "user.", "order.". + Db DB // Db is the underlying DB object for current operation. + Buffer *bytes.Buffer // Buffer is the sql statement string without Args for current operation. + Args []interface{} // Args is the full arguments of current operation. + Key string // The field name, eg: "id", "name", etc. + Value interface{} // The field value, can be any types. + OmitEmpty bool // Ignores current condition key if `value` is empty. + Prefix string // Field prefix, eg: "user", "order", etc. } // formatWhereKeyValue handles each key-value pair of the parameter map. diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 723773a1f..172176029 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -95,7 +95,7 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { if len(tableNameQueryOrStruct) > 1 { conditionStr := gconv.String(tableNameQueryOrStruct[0]) if gstr.Contains(conditionStr, "?") { - tableStr, extraArgs = formatWhere(c.db, formatWhereInput{ + tableStr, extraArgs = formatWhereHolder(c.db, formatWhereHolderInput{ Where: conditionStr, Args: tableNameQueryOrStruct[1:], OmitNil: false, diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 724a412f7..ac34d470e 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -37,21 +37,6 @@ func (m *Model) Where(where interface{}, args ...interface{}) *Model { return model } -// WherePrefix performs as Where, but it adds prefix to each field in where statement. -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{ - Operator: whereHolderOperatorWhere, - Where: where, - Args: args, - Prefix: prefix, - }) - return model -} - // Having sets the having statement for the model. // The parameters of this function usage are as the same as function Where. // See Where. @@ -79,6 +64,9 @@ func (m *Model) WherePri(where interface{}, args ...interface{}) *Model { // 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 { var ( placeHolderCount = gstr.Count(format, "?") @@ -109,53 +97,53 @@ func (m *Model) WhereGTE(column string, value interface{}) *Model { // 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.db.GetCore().QuoteWord(column), min, max) + return m.Wheref(`%s BETWEEN ? AND ?`, m.QuoteWord(column), min, max) } // WhereLike builds `column LIKE like` statement. func (m *Model) WhereLike(column string, like interface{}) *Model { - return m.Wheref(`%s LIKE ?`, m.db.GetCore().QuoteWord(column), like) + return m.Wheref(`%s LIKE ?`, m.QuoteWord(column), like) } // WhereIn builds `column IN (in)` statement. func (m *Model) WhereIn(column string, in interface{}) *Model { - return m.Wheref(`%s IN (?)`, m.db.GetCore().QuoteWord(column), in) + return m.Wheref(`%s IN (?)`, m.QuoteWord(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.db.GetCore().QuoteWord(column)) + model = m.Wheref(`%s IS NULL`, m.QuoteWord(column)) } return model } // 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.db.GetCore().QuoteWord(column), min, max) + return m.Wheref(`%s NOT BETWEEN ? AND ?`, m.QuoteWord(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.db.GetCore().QuoteWord(column), like) + return m.Wheref(`%s NOT LIKE ?`, m.QuoteWord(column), like) } // WhereNot builds `column != value` statement. func (m *Model) WhereNot(column string, value interface{}) *Model { - return m.Wheref(`%s != ?`, m.db.GetCore().QuoteWord(column), value) + return m.Wheref(`%s != ?`, m.QuoteWord(column), value) } // WhereNotIn builds `column NOT IN (in)` statement. func (m *Model) WhereNotIn(column string, in interface{}) *Model { - return m.Wheref(`%s NOT IN (?)`, m.db.GetCore().QuoteWord(column), in) + return m.Wheref(`%s NOT IN (?)`, m.QuoteWord(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.db.GetCore().QuoteWord(column)) + model = m.Wheref(`%s IS NOT NULL`, m.QuoteWord(column)) } return model } @@ -174,22 +162,10 @@ func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model { return model } -// WhereOrPrefix performs as WhereOr, but it adds prefix to each field in where statement. -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{ - Operator: whereHolderOperatorOr, - Where: where, - Args: args, - Prefix: prefix, - }) - return model -} - // 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 { var ( placeHolderCount = gstr.Count(format, "?") @@ -220,48 +196,48 @@ func (m *Model) WhereOrGTE(column string, value interface{}) *Model { // 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.db.GetCore().QuoteWord(column), min, max) + return m.WhereOrf(`%s BETWEEN ? AND ?`, m.QuoteWord(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.db.GetCore().QuoteWord(column), like) + return m.WhereOrf(`%s LIKE ?`, m.QuoteWord(column), like) } // WhereOrIn builds `column IN (in)` statement in `OR` conditions. func (m *Model) WhereOrIn(column string, in interface{}) *Model { - return m.WhereOrf(`%s IN (?)`, m.db.GetCore().QuoteWord(column), in) + return m.WhereOrf(`%s IN (?)`, m.QuoteWord(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.db.GetCore().QuoteWord(column)) + model = m.WhereOrf(`%s IS NULL`, m.QuoteWord(column)) } return model } // 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.db.GetCore().QuoteWord(column), min, max) + return m.WhereOrf(`%s NOT BETWEEN ? AND ?`, m.QuoteWord(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.db.GetCore().QuoteWord(column), like) + return m.WhereOrf(`%s NOT LIKE ?`, m.QuoteWord(column), like) } // WhereOrNotIn builds `column NOT IN (in)` statement. func (m *Model) WhereOrNotIn(column string, in interface{}) *Model { - return m.WhereOrf(`%s NOT IN (?)`, m.db.GetCore().QuoteWord(column), in) + return m.WhereOrf(`%s NOT IN (?)`, m.QuoteWord(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.db.GetCore().QuoteWord(column)) + model = m.WhereOrf(`%s IS NOT NULL`, m.QuoteWord(column)) } return model } @@ -317,7 +293,9 @@ func (m *Model) Page(page, limit int) *Model { func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { autoPrefix := "" if gstr.Contains(m.tables, " JOIN ") { - autoPrefix = m.db.GetCore().QuoteWord(m.tablesInit) + autoPrefix = m.db.GetCore().QuoteWord( + m.db.GetCore().guessPrimaryTableName(m.tablesInit), + ) } if len(m.whereHolder) > 0 { for _, v := range m.whereHolder { @@ -327,7 +305,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh switch v.Operator { case whereHolderOperatorWhere: if conditionWhere == "" { - newWhere, newArgs := formatWhere(m.db, formatWhereInput{ + newWhere, newArgs := formatWhereHolder(m.db, formatWhereHolderInput{ Where: v.Where, Args: v.Args, OmitNil: m.option&optionOmitNilWhere > 0, @@ -345,7 +323,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh fallthrough case whereHolderOperatorAnd: - newWhere, newArgs := formatWhere(m.db, formatWhereInput{ + newWhere, newArgs := formatWhereHolder(m.db, formatWhereHolderInput{ Where: v.Where, Args: v.Args, OmitNil: m.option&optionOmitNilWhere > 0, @@ -366,7 +344,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh } case whereHolderOperatorOr: - newWhere, newArgs := formatWhere(m.db, formatWhereInput{ + newWhere, newArgs := formatWhereHolder(m.db, formatWhereHolderInput{ Where: v.Where, Args: v.Args, OmitNil: m.option&optionOmitNilWhere > 0, @@ -414,7 +392,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh } // HAVING. if len(m.having) > 0 { - havingStr, havingArgs := formatWhere(m.db, formatWhereInput{ + havingStr, havingArgs := formatWhereHolder(m.db, formatWhereHolderInput{ Where: m.having[0], Args: gconv.Interfaces(m.having[1]), OmitNil: m.option&optionOmitNilWhere > 0, diff --git a/database/gdb/gdb_model_condition_prefix.go b/database/gdb/gdb_model_condition_prefix.go new file mode 100644 index 000000000..4ffbc7e72 --- /dev/null +++ b/database/gdb/gdb_model_condition_prefix.go @@ -0,0 +1,184 @@ +// 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 (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{ + Operator: whereHolderOperatorWhere, + Where: where, + Args: args, + Prefix: prefix, + }) + return model +} + +// 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) +} + +// 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) +} + +// 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) +} + +// 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) +} + +// 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) +} + +// 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) +} + +// WherePrefixIn builds `prefix.column IN (in)` statement. +func (m *Model) WherePrefixIn(prefix string, column string, in interface{}) *Model { + return m.Wheref(`%s.%s IN (?)`, m.QuoteWord(prefix), m.QuoteWord(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 +} + +// 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) +} + +// 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) +} + +// 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) +} + +// WherePrefixNotIn builds `prefix.column NOT IN (in)` statement. +func (m *Model) WherePrefixNotIn(prefix string, column string, in interface{}) *Model { + return m.Wheref(`%s.%s NOT IN (?)`, m.QuoteWord(prefix), m.QuoteWord(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 +} + +// 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{ + Operator: whereHolderOperatorOr, + Where: where, + Args: args, + Prefix: prefix, + }) + return model +} + +// 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) +} + +// 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) +} + +// 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) +} + +// 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) +} + +// 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) +} + +// 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) +} + +// WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions. +func (m *Model) WhereOrPrefixIn(prefix string, column string, in interface{}) *Model { + return m.WhereOrf(`%s.%s IN (?)`, m.QuoteWord(prefix), m.QuoteWord(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 +} + +// 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) +} + +// 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) +} + +// WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement. +func (m *Model) WhereOrPrefixNotIn(prefix string, column string, in interface{}) *Model { + return m.WhereOrf(`%s.%s NOT IN (?)`, m.QuoteWord(prefix), m.QuoteWord(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 +} diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index ff03f8b3e..a9daa9aea 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -111,7 +111,7 @@ func (m *Model) FieldCount(column string, as ...string) *Model { if len(as) > 0 && as[0] != "" { asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) } - return m.appendFieldsByStr(fmt.Sprintf(`COUNT(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) + return m.appendFieldsByStr(fmt.Sprintf(`COUNT(%s)%s`, m.QuoteWord(column), asStr)) } // FieldSum formats and appends commonly used field `SUM(column)` to the select fields of model. @@ -120,7 +120,7 @@ func (m *Model) FieldSum(column string, as ...string) *Model { if len(as) > 0 && as[0] != "" { asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) } - return m.appendFieldsByStr(fmt.Sprintf(`SUM(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) + return m.appendFieldsByStr(fmt.Sprintf(`SUM(%s)%s`, m.QuoteWord(column), asStr)) } // FieldMin formats and appends commonly used field `MIN(column)` to the select fields of model. @@ -129,7 +129,7 @@ func (m *Model) FieldMin(column string, as ...string) *Model { if len(as) > 0 && as[0] != "" { asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) } - return m.appendFieldsByStr(fmt.Sprintf(`MIN(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) + return m.appendFieldsByStr(fmt.Sprintf(`MIN(%s)%s`, m.QuoteWord(column), asStr)) } // FieldMax formats and appends commonly used field `MAX(column)` to the select fields of model. @@ -138,7 +138,7 @@ func (m *Model) FieldMax(column string, as ...string) *Model { if len(as) > 0 && as[0] != "" { asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) } - return m.appendFieldsByStr(fmt.Sprintf(`MAX(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) + return m.appendFieldsByStr(fmt.Sprintf(`MAX(%s)%s`, m.QuoteWord(column), asStr)) } // FieldAvg formats and appends commonly used field `AVG(column)` to the select fields of model. @@ -147,7 +147,7 @@ func (m *Model) FieldAvg(column string, as ...string) *Model { if len(as) > 0 && as[0] != "" { asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) } - return m.appendFieldsByStr(fmt.Sprintf(`AVG(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) + return m.appendFieldsByStr(fmt.Sprintf(`AVG(%s)%s`, m.QuoteWord(column), asStr)) } func (m *Model) appendFieldsByStr(fields string) *Model { diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index dd1061b28..12b5c5056 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -373,7 +373,7 @@ func (m *Model) Min(column string) (float64, error) { if len(column) == 0 { return 0, nil } - value, err := m.Fields(fmt.Sprintf(`MIN(%s)`, m.db.GetCore().QuoteWord(column))).Value() + value, err := m.Fields(fmt.Sprintf(`MIN(%s)`, m.QuoteWord(column))).Value() if err != nil { return 0, err } @@ -385,7 +385,7 @@ func (m *Model) Max(column string) (float64, error) { if len(column) == 0 { return 0, nil } - value, err := m.Fields(fmt.Sprintf(`MAX(%s)`, m.db.GetCore().QuoteWord(column))).Value() + value, err := m.Fields(fmt.Sprintf(`MAX(%s)`, m.QuoteWord(column))).Value() if err != nil { return 0, err } @@ -397,7 +397,7 @@ func (m *Model) Avg(column string) (float64, error) { if len(column) == 0 { return 0, nil } - value, err := m.Fields(fmt.Sprintf(`AVG(%s)`, m.db.GetCore().QuoteWord(column))).Value() + value, err := m.Fields(fmt.Sprintf(`AVG(%s)`, m.QuoteWord(column))).Value() if err != nil { return 0, err } @@ -409,7 +409,7 @@ func (m *Model) Sum(column string) (float64, error) { if len(column) == 0 { return 0, nil } - value, err := m.Fields(fmt.Sprintf(`SUM(%s)`, m.db.GetCore().QuoteWord(column))).Value() + value, err := m.Fields(fmt.Sprintf(`SUM(%s)`, m.QuoteWord(column))).Value() if err != nil { return 0, err } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 2df3df2c5..02e307396 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -17,6 +17,15 @@ import ( "github.com/gogf/gf/v2/util/gutil" ) +// QuoteWord checks given string `s` a word, +// if true it quotes `s` with security chars of the database +// and returns the quoted string; or else it returns `s` without any change. +// +// The meaning of a `word` can be considered as a column name. +func (m *Model) QuoteWord(s string) string { + return m.db.GetCore().QuoteWord(s) +} + // TableFields retrieves and returns the fields information of specified table of current // schema. // diff --git a/database/gdb/gdb_z_mysql_model_where_test.go b/database/gdb/gdb_z_mysql_model_where_prefix_test.go similarity index 70% rename from database/gdb/gdb_z_mysql_model_where_test.go rename to database/gdb/gdb_z_mysql_model_where_prefix_test.go index 55cc3bd7f..8c6357677 100644 --- a/database/gdb/gdb_z_mysql_model_where_test.go +++ b/database/gdb/gdb_z_mysql_model_where_prefix_test.go @@ -68,3 +68,31 @@ func Test_Model_WhereOrPrefix(t *testing.T) { t.Assert(r[3]["id"], "9") }) } + +func Test_Model_WherePrefixLike(t *testing.T) { + var ( + table1 = gtime.TimestampNanoStr() + "_table1" + table2 = gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + LeftJoinOnField(table2, "id"). + WherePrefix(table1, g.Map{ + "id": g.Slice{1, 2, 3}, + }). + WherePrefix(table2, g.Map{ + "id": g.Slice{3, 4, 5}, + }). + WherePrefixLike(table2, "nickname", "name%"). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 1) + t.Assert(r[0]["id"], "3") + }) +}