diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 4769aaa4d..7ce229346 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -136,6 +136,9 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { extraArgs: extraArgs, } m.whereBuilder = m.Builder() + // Assign the safe attribute of WhereBuilder to nil, + // to make it use the safe attribute of its bound model. + m.whereBuilder.safe = nil if defaultModelSafe { m.safe = true } @@ -262,7 +265,7 @@ func (m *Model) Clone() *Model { // Basic attributes copy. *newModel = *m // WhereBuilder copy, note the attribute pointer. - newModel.whereBuilder = m.whereBuilder.Clone() + newModel.whereBuilder = m.whereBuilder.clone() newModel.whereBuilder.model = newModel // Shallow copy slice attributes. if n := len(m.extraArgs); n > 0 { diff --git a/database/gdb/gdb_model_builder.go b/database/gdb/gdb_model_builder.go index 0a1ccd6ff..7687da675 100644 --- a/database/gdb/gdb_model_builder.go +++ b/database/gdb/gdb_model_builder.go @@ -8,12 +8,11 @@ package gdb import ( "fmt" - - "github.com/gogf/gf/v2/text/gstr" ) type WhereBuilder struct { - model *Model // Bound to parent model. + 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. } @@ -27,7 +26,10 @@ type WhereHolder struct { } 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), } @@ -37,15 +39,22 @@ func (m *Model) Builder() *WhereBuilder { // getBuilder creates and returns a cloned WhereBuilder of current WhereBuilder if `safe` is true, // or else it returns the current WhereBuilder. func (b *WhereBuilder) getBuilder() *WhereBuilder { - if !b.model.safe { + var isSafe bool + if b.safe != nil { + isSafe = *b.safe + } else { + isSafe = b.model.safe + } + if !isSafe { return b } else { - return b.Clone() + return b.clone() } } -func (b *WhereBuilder) Clone() *WhereBuilder { +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 @@ -63,24 +72,7 @@ func (b *WhereBuilder) Build() (conditionWhere string, conditionArgs []interface holder.Prefix = autoPrefix } switch holder.Operator { - case whereHolderOperatorWhere: - if conditionWhere == "" { - newWhere, newArgs := formatWhereHolder(ctx, b.model.db, formatWhereHolderInput{ - WhereHolder: holder, - OmitNil: b.model.option&optionOmitNilWhere > 0, - OmitEmpty: b.model.option&optionOmitEmptyWhere > 0, - Schema: b.model.schema, - Table: tableForMappingAndFiltering, - }) - if len(newWhere) > 0 { - conditionWhere = newWhere - conditionArgs = newArgs - } - continue - } - fallthrough - - case whereHolderOperatorAnd: + case whereHolderOperatorWhere, whereHolderOperatorAnd: newWhere, newArgs := formatWhereHolder(ctx, b.model.db, formatWhereHolderInput{ WhereHolder: holder, OmitNil: b.model.option&optionOmitNilWhere > 0, @@ -120,24 +112,5 @@ func (b *WhereBuilder) Build() (conditionWhere string, conditionArgs []interface } } } - // Soft deletion. - softDeletingCondition := b.model.getConditionForSoftDeleting() - if b.model.rawSql != "" && conditionWhere != "" { - if gstr.ContainsI(b.model.rawSql, " WHERE ") { - conditionWhere = " AND " + conditionWhere - } else { - conditionWhere = " WHERE " + conditionWhere - } - } else if !b.model.unscoped && softDeletingCondition != "" { - if conditionWhere == "" { - conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) - } else { - conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition) - } - } else { - if conditionWhere != "" { - conditionWhere = " WHERE " + conditionWhere - } - } return } diff --git a/database/gdb/gdb_model_builder_where.go b/database/gdb/gdb_model_builder_where.go index 5803f8d82..0a4e45de7 100644 --- a/database/gdb/gdb_model_builder_where.go +++ b/database/gdb/gdb_model_builder_where.go @@ -15,20 +15,20 @@ import ( // doWhereType sets the condition statement for the model. The parameter `where` can be type of // string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, // multiple conditions will be joined into where statement using "AND". -func (b *WhereBuilder) doWhereType(t string, where interface{}, args ...interface{}) *WhereBuilder { +func (b *WhereBuilder) doWhereType(whereType string, where interface{}, args ...interface{}) *WhereBuilder { builder := b.getBuilder() if builder.whereHolder == nil { builder.whereHolder = make([]WhereHolder, 0) } - if t == "" { + if whereType == "" { if len(args) == 0 { - t = whereHolderTypeNoArgs + whereType = whereHolderTypeNoArgs } else { - t = whereHolderTypeDefault + whereType = whereHolderTypeDefault } } builder.whereHolder = append(builder.whereHolder, WhereHolder{ - Type: t, + Type: whereType, Operator: whereHolderOperatorWhere, Where: where, Args: args, @@ -59,6 +59,20 @@ func (b *WhereBuilder) doWherefType(t string, format string, args ...interface{} // Where("age IN(?,?)", 18, 50) // Where(User{ Id : 1, UserName : "john"}). func (b *WhereBuilder) Where(where interface{}, args ...interface{}) *WhereBuilder { + 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 b.doWhereType(``, conditionWhere, conditionArgs...) + } return b.doWhereType(``, where, args...) } diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index b7f782f4a..d39fc4728 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -616,6 +616,24 @@ func (m *Model) formatCondition(ctx context.Context, limit1 bool, isCountStateme } // WHERE conditionWhere, conditionArgs = m.whereBuilder.Build() + softDeletingCondition := m.getConditionForSoftDeleting() + if m.rawSql != "" && conditionWhere != "" { + if gstr.ContainsI(m.rawSql, " WHERE ") { + conditionWhere = " AND " + conditionWhere + } else { + conditionWhere = " WHERE " + conditionWhere + } + } else if !m.unscoped && softDeletingCondition != "" { + if conditionWhere == "" { + conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) + } else { + conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition) + } + } else { + if conditionWhere != "" { + conditionWhere = " WHERE " + conditionWhere + } + } // HAVING. if len(m.having) > 0 { havingHolder := WhereHolder{ diff --git a/database/gdb/gdb_core_sharding.go b/database/gdb/gdb_model_sharding.go similarity index 100% rename from database/gdb/gdb_core_sharding.go rename to database/gdb/gdb_model_sharding.go diff --git a/database/gdb/gdb_z_mysql_feature_model_builder_test.go b/database/gdb/gdb_z_mysql_feature_model_builder_test.go new file mode 100644 index 000000000..43e319731 --- /dev/null +++ b/database/gdb/gdb_z_mysql_feature_model_builder_test.go @@ -0,0 +1,46 @@ +// 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) + }) + + 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) + }) + +}