Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 229 additions & 2 deletions contrib/drivers/mysql/mysql_z_unit_feature_model_sharding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,234 @@ import (
"github.com/gogf/gf/v2/test/gtest"
)

func Test_Model_Sharding_Table(t *testing.T) {
const (
TestDbNameSh0 = "test_0"
TestDbNameSh1 = "test_1"
TestTableName = "user"
)

type ShardingUser struct {
Id int
Name string
}

// createShardingDatabase creates test databases and tables for sharding
func createShardingDatabase(t *gtest.T) {
// Create databases
dbs := []string{TestDbNameSh0, TestDbNameSh1}
for _, dbName := range dbs {
sql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", dbName)
_, err := db.Exec(ctx, sql)
t.AssertNil(err)

// Switch to the database
sql = fmt.Sprintf("USE `%s`", dbName)
_, err = db.Exec(ctx, sql)
t.AssertNil(err)

// Create tables
tables := []string{"user_0", "user_1", "user_2", "user_3"}
for _, table := range tables {
sql := fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id int(11) NOT NULL,
name varchar(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`, table)
_, err := db.Exec(ctx, sql)
t.AssertNil(err)
}
}
}

// dropShardingDatabase drops test databases
func dropShardingDatabase(t *gtest.T) {
dbs := []string{TestDbNameSh0, TestDbNameSh1}
for _, dbName := range dbs {
sql := fmt.Sprintf("DROP DATABASE IF EXISTS `%s`", dbName)
_, err := db.Exec(ctx, sql)
t.AssertNil(err)
}
}

func Test_Sharding_Basic(t *testing.T) {
return
gtest.C(t, func(t *gtest.T) {
var (
tablePrefix = "user_"
schemaPrefix = "test_"
)

// Create test databases and tables
createShardingDatabase(t)
defer dropShardingDatabase(t)

// Create sharding configuration
shardingConfig := gdb.ShardingConfig{
Table: gdb.ShardingTableConfig{
Enable: true,
Prefix: tablePrefix,
Rule: &gdb.DefaultShardingRule{
TableCount: 4,
},
},
Schema: gdb.ShardingSchemaConfig{
Enable: true,
Prefix: schemaPrefix,
Rule: &gdb.DefaultShardingRule{
SchemaCount: 2,
},
},
}

// Prepare test data
user := ShardingUser{
Id: 1,
Name: "John",
}

model := db.Model(TestTableName).
Sharding(shardingConfig).
ShardingValue(user.Id).
Safe()

// Test Insert
_, err := model.Data(user).Insert()
t.AssertNil(err)

// Test Select
var result ShardingUser
err = model.Where("id", user.Id).Scan(&result)
t.AssertNil(err)
t.Assert(result.Id, user.Id)
t.Assert(result.Name, user.Name)

// Test Update
_, err = model.Data(g.Map{"name": "John Doe"}).
Where("id", user.Id).
Update()
t.AssertNil(err)

// Verify Update
err = model.Where("id", user.Id).Scan(&result)
t.AssertNil(err)
t.Assert(result.Name, "John Doe")

// Test Delete
_, err = model.Where("id", user.Id).Delete()
t.AssertNil(err)

// Verify Delete
count, err := model.Where("id", user.Id).Count()
t.AssertNil(err)
t.Assert(count, 0)
})
}

// Test_Sharding_Error tests error cases
func Test_Sharding_Error(t *testing.T) {
return
gtest.C(t, func(t *gtest.T) {
// Create test databases and tables
createShardingDatabase(t)
defer dropShardingDatabase(t)

// Test missing sharding value
model := db.Model(TestTableName).
Sharding(gdb.ShardingConfig{
Table: gdb.ShardingTableConfig{
Enable: true,
Prefix: "user_",
Rule: &gdb.DefaultShardingRule{TableCount: 4},
},
}).Safe()

_, err := model.Insert(g.Map{"id": 1, "name": "test"})
t.AssertNE(err, nil)
t.Assert(err.Error(), "sharding value is required when sharding feature enabled")

// Test missing sharding rule
model = db.Model(TestTableName).
Sharding(gdb.ShardingConfig{
Table: gdb.ShardingTableConfig{
Enable: true,
Prefix: "user_",
},
}).
ShardingValue(1)

_, err = model.Insert(g.Map{"id": 1, "name": "test"})
t.AssertNE(err, nil)
t.Assert(err.Error(), "sharding rule is required when sharding feature enabled")
})
}

// Test_Sharding_Complex tests complex sharding scenarios
func Test_Sharding_Complex(t *testing.T) {
return
gtest.C(t, func(t *gtest.T) {
// Create test databases and tables
createShardingDatabase(t)
defer dropShardingDatabase(t)

shardingConfig := gdb.ShardingConfig{
Table: gdb.ShardingTableConfig{
Enable: true,
Prefix: "user_",
Rule: &gdb.DefaultShardingRule{TableCount: 4},
},
Schema: gdb.ShardingSchemaConfig{
Enable: true,
Prefix: "test_",
Rule: &gdb.DefaultShardingRule{SchemaCount: 2},
},
}

users := []ShardingUser{
{Id: 1, Name: "User1"},
{Id: 2, Name: "User2"},
{Id: 3, Name: "User3"},
}

for _, user := range users {
model := db.Model(TestTableName).
Sharding(shardingConfig).
ShardingValue(user.Id).
Safe()

_, err := model.Data(user).Insert()
t.AssertNil(err)
}

// Test batch query
for _, user := range users {
model := db.Model(TestTableName).
Sharding(shardingConfig).
ShardingValue(user.Id).
Safe()

var result ShardingUser
err := model.Where("id", user.Id).Scan(&result)
t.AssertNil(err)
t.Assert(result.Id, user.Id)
t.Assert(result.Name, user.Name)
}

// Clean up
for _, user := range users {
model := db.Model(TestTableName).
Sharding(shardingConfig).
ShardingValue(user.Id).
Safe()

_, err := model.Where("id", user.Id).Delete()
t.AssertNil(err)
}
})
}

func Test_Model_Sharding_Table_Using_Hook(t *testing.T) {
var (
table1 = gtime.TimestampNanoStr() + "_table1"
table2 = gtime.TimestampNanoStr() + "_table2"
Expand Down Expand Up @@ -127,7 +354,7 @@ func Test_Model_Sharding_Table(t *testing.T) {
})
}

func Test_Model_Sharding_Schema(t *testing.T) {
func Test_Model_Sharding_Schema_Using_Hook(t *testing.T) {
var (
table = gtime.TimestampNanoStr() + "_table"
)
Expand Down
2 changes: 2 additions & 0 deletions database/gdb/gdb_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type Model struct {
onConflict interface{} // onConflict is used for conflict keys on Upsert clause.
tableAliasMap map[string]string // Table alias to true table name, usually used in join statements.
softTimeOption SoftTimeOption // SoftTimeOption is the option to customize soft time feature for Model.
shardingConfig ShardingConfig // ShardingConfig for database/table sharding feature.
shardingValue any // Sharding value for sharding feature.
}

// ModelHandler is a function that handles given Model and returns a new Model that is custom modified.
Expand Down
2 changes: 2 additions & 0 deletions database/gdb/gdb_model_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
},
Model: m,
Table: m.tables,
Schema: m.schema,
Data: dataHolder,
Condition: conditionStr,
Args: append([]interface{}{dataValue}, conditionArgs...),
Expand All @@ -80,6 +81,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
},
Model: m,
Table: m.tables,
Schema: m.schema,
Condition: conditionStr,
Args: conditionArgs,
}
Expand Down
49 changes: 49 additions & 0 deletions database/gdb/gdb_model_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ func (h *HookSelectInput) Next(ctx context.Context) (result Result, err error) {
if h.originalSchemaName.IsNil() {
h.originalSchemaName = gvar.New(h.Schema)
}

// Sharding feature.
h.Schema, err = h.Model.getActualSchema(ctx, h.Schema)
if err != nil {
return nil, err
}
h.Table, err = h.Model.getActualTable(ctx, h.Table)
if err != nil {
return nil, err
}

// Custom hook handler call.
if h.handler != nil && !h.handlerCalled {
h.handlerCalled = true
Expand Down Expand Up @@ -161,11 +172,23 @@ func (h *HookInsertInput) Next(ctx context.Context) (result sql.Result, err erro
h.originalSchemaName = gvar.New(h.Schema)
}

// Sharding feature.
h.Schema, err = h.Model.getActualSchema(ctx, h.Schema)
if err != nil {
return nil, err
}
h.Table, err = h.Model.getActualTable(ctx, h.Table)
if err != nil {
return nil, err
}

if h.handler != nil && !h.handlerCalled {
h.handlerCalled = true
return h.handler(ctx, h)
}

// No need to handle table change.

// Schema change.
if h.Schema != "" && h.Schema != h.originalSchemaName.String() {
h.link, err = h.Model.db.GetCore().MasterLink(h.Schema)
Expand All @@ -185,6 +208,16 @@ func (h *HookUpdateInput) Next(ctx context.Context) (result sql.Result, err erro
h.originalSchemaName = gvar.New(h.Schema)
}

// Sharding feature.
h.Schema, err = h.Model.getActualSchema(ctx, h.Schema)
if err != nil {
return nil, err
}
h.Table, err = h.Model.getActualTable(ctx, h.Table)
if err != nil {
return nil, err
}

if h.handler != nil && !h.handlerCalled {
h.handlerCalled = true
if gstr.HasPrefix(h.Condition, whereKeyInCondition) {
Expand All @@ -196,6 +229,9 @@ func (h *HookUpdateInput) Next(ctx context.Context) (result sql.Result, err erro
if h.removedWhere {
h.Condition = whereKeyInCondition + h.Condition
}

// No need to handle table change.

// Schema change.
if h.Schema != "" && h.Schema != h.originalSchemaName.String() {
h.link, err = h.Model.db.GetCore().MasterLink(h.Schema)
Expand All @@ -215,6 +251,16 @@ func (h *HookDeleteInput) Next(ctx context.Context) (result sql.Result, err erro
h.originalSchemaName = gvar.New(h.Schema)
}

// Sharding feature.
h.Schema, err = h.Model.getActualSchema(ctx, h.Schema)
if err != nil {
return nil, err
}
h.Table, err = h.Model.getActualTable(ctx, h.Table)
if err != nil {
return nil, err
}

if h.handler != nil && !h.handlerCalled {
h.handlerCalled = true
if gstr.HasPrefix(h.Condition, whereKeyInCondition) {
Expand All @@ -226,6 +272,9 @@ func (h *HookDeleteInput) Next(ctx context.Context) (result sql.Result, err erro
if h.removedWhere {
h.Condition = whereKeyInCondition + h.Condition
}

// No need to handle table change.

// Schema change.
if h.Schema != "" && h.Schema != h.originalSchemaName.String() {
h.link, err = h.Model.db.GetCore().MasterLink(h.Schema)
Expand Down
1 change: 1 addition & 0 deletions database/gdb/gdb_model_insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ func (m *Model) doInsertWithOption(ctx context.Context, insertOption InsertOptio
},
Model: m,
Table: m.tables,
Schema: m.schema,
Data: list,
Option: doInsertOption,
}
Expand Down
1 change: 1 addition & 0 deletions database/gdb/gdb_model_select.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ func (m *Model) doGetAllBySql(
},
Model: m,
Table: m.tables,
Schema: m.schema,
Sql: sql,
Args: m.mergeArguments(args),
SelectType: selectType,
Expand Down
Loading
Loading