From f7d5ca4d1ec4b66e94aeed5d1adc827885fff877 Mon Sep 17 00:00:00 2001 From: csynineyang Date: Tue, 6 Sep 2022 12:11:01 +0800 Subject: [PATCH 1/5] Feature: support shadow type match regex #365: 1. create shadow tables in init.sql & sharding.sql 2. hide shadow tables for "show tables" command 3. impl shadow match hint rule (only select) 4. support shadow type match regex (only select) --- conf/config.yaml | 12 +- pkg/constants/const.go | 6 + pkg/proto/rule/shadow.go | 35 +- pkg/runtime/ast/expression_atom.go | 2 +- pkg/runtime/optimize/dml/delete.go | 6 +- pkg/runtime/optimize/dml/insert.go | 6 +- pkg/runtime/optimize/dml/select.go | 18 +- pkg/runtime/optimize/dml/update.go | 3 +- pkg/runtime/optimize/sharder.go | 37 ++ pkg/runtime/plan/dal/show_tables.go | 13 +- pkg/runtime/rule/evaluator.go | 48 ++ scripts/init.sql | 1 - scripts/sharding.sql | 724 ++++------------------------ 13 files changed, 249 insertions(+), 662 deletions(-) diff --git a/conf/config.yaml b/conf/config.yaml index 795bf7ad..a083d207 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -87,15 +87,6 @@ data: password: "123456" database: employees_0003 weight: r10w10 - - name: employees_shadow - nodes: - - name: node_shadow - host: arana-mysql - port: 3306 - username: root - password: "123456" - database: employees_shadow - weight: r10w10 sharding_rule: tables: - name: employees.student @@ -121,8 +112,7 @@ data: shadow_rule: tables: - name: employees.student - enable: false - group_node: employees_shadow + enable: true match_rules: - operation: [insert,update] match_type: value diff --git a/pkg/constants/const.go b/pkg/constants/const.go index e4695f1b..86253840 100644 --- a/pkg/constants/const.go +++ b/pkg/constants/const.go @@ -28,6 +28,12 @@ const ( VariableNameMaxAllowedPacket = "max_allowed_packet" ) +const ( + HeaderPrefix = "Tables_in_" + AranaSystemTablePrefix = "__arana_" + ShadowTablePrefix = "__shadow_" +) + const ( ShadowMatchRegex = "regex" ShadowMatchValue = "value" diff --git a/pkg/proto/rule/shadow.go b/pkg/proto/rule/shadow.go index 79fceb6a..4eaedefc 100644 --- a/pkg/proto/rule/shadow.go +++ b/pkg/proto/rule/shadow.go @@ -18,6 +18,7 @@ package rule import ( + "regexp" "sync" ) @@ -30,6 +31,7 @@ type ShadowRuleManager interface { MatchHintBy(action, hint string) bool MatchRegexBy(action, column, value string) bool GetDatabase() string + GetTableName() string } // ShadowRule represents the shadow of databases and tables. @@ -70,11 +72,18 @@ func (s *ShadowRule) MatchRegexBy(tableName, action, column, value string) bool return rule.MatchRegexBy(action, column, value) } -func (s *ShadowRule) GetDatabase(tableName string) string { +func (s *ShadowRule) GetDatabase(database string) string { s.mu.RLock() defer s.mu.RUnlock() - return s.rules[tableName].GetDatabase() + return database +} + +func (s *ShadowRule) GetTableName(tableName string) string { + s.mu.RLock() + defer s.mu.RUnlock() + + return constants.ShadowTablePrefix + tableName } func (s *ShadowRule) SetRuleManager(tableName string, ruleManager ShadowRuleManager) { @@ -91,15 +100,20 @@ func NewShadowRule() *ShadowRule { } type Operation struct { - enable bool - database string - actions map[string][]*Attribute // map[action][]*Attribute, action in (select, update, delete, update) + enable bool + database string + tablename string + actions map[string][]*Attribute // map[action][]*Attribute, action in (select, update, delete, update) } func (o *Operation) GetDatabase() string { return o.database } +func (o *Operation) GetTableName() string { + return o.tablename +} + func (o *Operation) MatchValueBy(action, column, value string) bool { if !o.enable { return false @@ -140,11 +154,20 @@ func (o *Operation) MatchRegexBy(action, column, value string) bool { if !o.enable { return false } - _, ok := o.actions[action] + attrs, ok := o.actions[action] if !ok { return false } // TODO impl regex rule below + for _, attr := range attrs { + if attr.typ == constants.ShadowMatchRegex { + reg, err := regexp.Compile(attr.value) + if err != nil { + return false + } + return reg.MatchString(value) + } + } return false } diff --git a/pkg/runtime/ast/expression_atom.go b/pkg/runtime/ast/expression_atom.go index a0f61a61..ae67558f 100644 --- a/pkg/runtime/ast/expression_atom.go +++ b/pkg/runtime/ast/expression_atom.go @@ -132,7 +132,7 @@ func (u *UnaryExpressionAtom) Accept(visitor Visitor) (interface{}, error) { } func (u *UnaryExpressionAtom) IsOperatorNot() bool { - switch u.Operator { + switch strings.ToUpper(strings.Trim(u.Operator, " ")) { case "!", "NOT": return true } diff --git a/pkg/runtime/optimize/dml/delete.go b/pkg/runtime/optimize/dml/delete.go index fc69751a..fd6d4a10 100644 --- a/pkg/runtime/optimize/dml/delete.go +++ b/pkg/runtime/optimize/dml/delete.go @@ -60,13 +60,15 @@ func optimizeDelete(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err if shards == nil { transparent := plan.Transparent(stmt, o.Args) if matchShadow { - transparent.SetDB(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) + //TODO: fix it + //transparent.SetDB(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) } return transparent, nil } if matchShadow { - shards.ReplaceDb(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) + //TODO: fix it + //shards.ReplaceDb(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) } ret := dml.NewSimpleDeletePlan(stmt) diff --git a/pkg/runtime/optimize/dml/insert.go b/pkg/runtime/optimize/dml/insert.go index 3ac6578f..859a1c62 100644 --- a/pkg/runtime/optimize/dml/insert.go +++ b/pkg/runtime/optimize/dml/insert.go @@ -133,7 +133,8 @@ func optimizeInsert(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err for k, v := range shards { if matchShadow { - k = o.ShadowRule.GetDatabase(tableName.Suffix()) + //TODO: fix it + //k = o.ShadowRule.GetDatabase(tableName.Suffix()) } db = k table = v[0] @@ -148,7 +149,8 @@ func optimizeInsert(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err for db, slot := range slots { if matchShadow { - db = o.ShadowRule.GetDatabase(tableName.Suffix()) + //TODO: fix it + //db = o.ShadowRule.GetDatabase(tableName.Suffix()) } for table, indexes := range slot { // clone insert stmt without values diff --git a/pkg/runtime/optimize/dml/select.go b/pkg/runtime/optimize/dml/select.go index a720a935..9f8046ff 100644 --- a/pkg/runtime/optimize/dml/select.go +++ b/pkg/runtime/optimize/dml/select.go @@ -93,11 +93,17 @@ func optimizeSelect(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err return nil, errors.Wrap(err, "calculate hints failed") } - shards = hintLoader.GetShards() matchShadow = hintLoader.GetMatchBy(tableName.Suffix(), constants.ShadowSelect) } if shards == nil { + //first shadow_rule, and then sharding_rule + if o.ShadowRule != nil && !matchShadow { + if matchShadow, err = (*optimize.ShadowSharder)(o.ShadowRule).Shard(tableName, constants.ShadowSelect, stmt.Where, o.Args...); err != nil && fullScan == false { + return nil, errors.Wrap(err, "calculate shards failed") + } + } + if shards, fullScan, err = (*optimize.Sharder)(o.Rule).Shard(tableName, stmt.Where, o.Args...); err != nil && fullScan == false { return nil, errors.Wrap(err, "calculate shards failed") } @@ -137,7 +143,7 @@ func optimizeSelect(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err } if matchShadow { - db0 = o.ShadowRule.GetDatabase(tableName.Suffix()) + tbl0 = o.ShadowRule.GetTableName(tbl0) } return toSingle(db0, tbl0) } @@ -147,10 +153,10 @@ func optimizeSelect(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err var db, tbl string for k, v := range shards { db = k + tbl = v[0] if matchShadow { - db = o.ShadowRule.GetDatabase(tableName.Suffix()) + tbl = o.ShadowRule.GetTableName(v[0]) } - tbl = v[0] } return toSingle(db, tbl) } @@ -178,7 +184,9 @@ func optimizeSelect(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err plans := make([]proto.Plan, 0, len(shards)) for k, v := range shards { if matchShadow { - k = o.ShadowRule.GetDatabase(tableName.Suffix()) + for vi, vt := range v { + v[vi] = o.ShadowRule.GetTableName(vt) + } } next := &dml.SimpleQueryPlan{ Database: k, diff --git a/pkg/runtime/optimize/dml/update.go b/pkg/runtime/optimize/dml/update.go index 195c6f41..73dce2c2 100644 --- a/pkg/runtime/optimize/dml/update.go +++ b/pkg/runtime/optimize/dml/update.go @@ -103,7 +103,8 @@ func optimizeUpdate(_ context.Context, o *optimize.Optimizer) (proto.Plan, error } if matchShadow { - shards.ReplaceDb(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) + //TODO: fix it + //shards.ReplaceDb(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) } ret := dml.NewUpdatePlan(stmt) diff --git a/pkg/runtime/optimize/sharder.go b/pkg/runtime/optimize/sharder.go index d4d1fcf1..0297079f 100644 --- a/pkg/runtime/optimize/sharder.go +++ b/pkg/runtime/optimize/sharder.go @@ -46,6 +46,43 @@ func IsErrArgumentOutOfRange(err error) bool { // Sharder computes the shards from a SQL statement. type Sharder rule.Rule +type ShadowSharder rule.ShadowRule + +func (ss *ShadowSharder) rule() *rule.ShadowRule { + return (*rule.ShadowRule)(ss) +} + +// Shard returns shards. +func (ss *ShadowSharder) Shard(tableName ast.TableName, action string, filter ast.ExpressionNode, args ...interface{}) (matchShadow bool, err error) { + var ( + sh Sharder + sc shardCtx + lo logical.Logical + ev rrule.Evaluator + ) + + // 0. prepare shard context + sc.tableName = tableName + sc.args = args + + // 1. expression to logical + if lo, err = sh.processExpression(&sc, filter); err != nil { + err = errors.Wrap(err, "compute shard logical failed") + return + } + // 2. logical to evaluator + if ev, err = rrule.EvalShadow(lo, tableName.Suffix(), action, ss.rule()); err != nil { + err = errors.Wrap(err, "compute shard evaluator failed") + return + } + // 3. match regex + matchShadow = false + if ev != nil { + matchShadow = true + } + + return +} // Shard returns shards. func (sh *Sharder) Shard(tableName ast.TableName, filter ast.ExpressionNode, args ...interface{}) (shards rule.DatabaseTables, fullScan bool, err error) { diff --git a/pkg/runtime/plan/dal/show_tables.go b/pkg/runtime/plan/dal/show_tables.go index 8921d6b1..4d3f0e6d 100644 --- a/pkg/runtime/plan/dal/show_tables.go +++ b/pkg/runtime/plan/dal/show_tables.go @@ -28,6 +28,7 @@ import ( ) import ( + constdb "github.com/arana-db/arana/pkg/constants" constant "github.com/arana-db/arana/pkg/constants/mysql" "github.com/arana-db/arana/pkg/dataset" "github.com/arana-db/arana/pkg/mysql" @@ -41,11 +42,6 @@ import ( var _ proto.Plan = (*ShowTablesPlan)(nil) -const ( - headerPrefix = "Tables_in_" - aranaSystemTablePrefix = "__arana_" -) - type ShowTablesPlan struct { plan.BasePlan Database string @@ -94,7 +90,7 @@ func (st *ShowTablesPlan) ExecIn(ctx context.Context, conn proto.VConn) (proto.R fields, _ := ds.Fields() - fields[0] = mysql.NewField(headerPrefix+rcontext.Schema(ctx), constant.FieldTypeVarString) + fields[0] = mysql.NewField(constdb.HeaderPrefix+rcontext.Schema(ctx), constant.FieldTypeVarString) // filter duplicates duplicates := make(map[string]struct{}) @@ -132,7 +128,10 @@ func (st *ShowTablesPlan) ExecIn(ctx context.Context, conn proto.VConn) (proto.R } tableName := vr.Values()[0].(string) - if strings.HasPrefix(tableName, aranaSystemTablePrefix) { + if strings.HasPrefix(tableName, constdb.AranaSystemTablePrefix) { + return false + } + if strings.HasPrefix(tableName, constdb.ShadowTablePrefix) { return false } if _, ok := duplicates[tableName]; ok { diff --git a/pkg/runtime/rule/evaluator.go b/pkg/runtime/rule/evaluator.go index 72774e61..0d175201 100644 --- a/pkg/runtime/rule/evaluator.go +++ b/pkg/runtime/rule/evaluator.go @@ -317,6 +317,54 @@ func (t *KeyedEvaluator) Not() Evaluator { return ret } +func EvalShadow(l logical.Logical, tableName, action string, rule *rule.ShadowRule) (Evaluator, error) { + ret, err := logical.Eval(l, func(a, b interface{}) (interface{}, error) { + x := a.(Evaluator) + y := b.(Evaluator) + return mergeShadow(tableName, action, rule, x, y) + }, func(a, b interface{}) (interface{}, error) { + x := a.(Evaluator) + y := b.(Evaluator) + return mergeShadow(tableName, action, rule, x, y) + }, func(i interface{}) interface{} { + x := i.(Evaluator) + return x.Not() + }) + if err != nil { + return nil, err + } + //check atom logical, again + ret, err = mergeShadow(tableName, action, rule, ret.(Evaluator), _noopEvaluator) + if err != nil { + return nil, err + } + if ret == _emptyEvaluator || ret == _noopEvaluator { + return nil, nil + } + + return ret.(Evaluator), nil +} + +func mergeShadow(tableName, action string, rule *rule.ShadowRule, first, second Evaluator) (Evaluator, error) { + k1, ok1 := first.(*KeyedEvaluator) + k2, ok2 := second.(*KeyedEvaluator) + + if ok1 { + s1, ok := k1.v.(string) + if ok && k1.op == cmp.Ceq && rule.MatchRegexBy(tableName, action, k1.k, s1) { + return k1, nil + } + } + if ok2 { + s2, ok := k2.v.(string) + if ok && k2.op == cmp.Ceq && rule.MatchRegexBy(tableName, action, k2.k, s2) { + return k2, nil + } + } + + return _noopEvaluator, nil +} + func Eval(l logical.Logical, tableName string, rule *rule.Rule) (Evaluator, error) { ret, err := logical.Eval(l, func(a, b interface{}) (interface{}, error) { x := a.(Evaluator) diff --git a/scripts/init.sql b/scripts/init.sql index 6e0c9ddb..b737a6bd 100644 --- a/scripts/init.sql +++ b/scripts/init.sql @@ -40,7 +40,6 @@ -- CREATE DATABASE IF NOT EXISTS employees_0000 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE DATABASE IF NOT EXISTS employees_shadow CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE employees_0000; diff --git a/scripts/sharding.sql b/scripts/sharding.sql index 28238d25..d4b92445 100644 --- a/scripts/sharding.sql +++ b/scripts/sharding.sql @@ -22,6 +22,7 @@ CREATE DATABASE IF NOT EXISTS employees_0003 CHARACTER SET utf8mb4 COLLATE utf8m CREATE DATABASE IF NOT EXISTS employees_0000_r CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +-- employees_0000, student_0000~student_0007 CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0000` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, @@ -37,631 +38,102 @@ CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0000` UNIQUE KEY `uk_uid` (`uid`), KEY `nickname` (`nickname`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0001` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0002` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0003` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0004` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0005` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0006` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0007` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0008` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0009` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0010` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0011` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0012` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0013` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0014` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0015` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0016` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0017` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0018` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0019` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0020` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0021` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0022` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0023` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0024` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0025` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0026` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0027` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0028` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0029` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0030` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0031` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - - -CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0000` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0001` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0002` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0003` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0004` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0005` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0006` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0007` -( - `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - `uid` BIGINT(20) UNSIGNED NOT NULL, - `name` VARCHAR(255) NOT NULL, - `score` DECIMAL(6,2) DEFAULT '0', - `nickname` VARCHAR(255) DEFAULT NULL, - `gender` TINYINT(4) NULL, - `birth_year` SMALLINT(5) UNSIGNED DEFAULT '0', - `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_uid` (`uid`), - KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - +CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0001` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0002` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0003` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0004` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0005` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0006` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`student_0007` LIKE `employees_0000`.`student_0000`; + +-- employees_0001, student_0008~student_0015 +CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0008` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0009` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0010` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0011` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0012` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0013` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0014` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`student_0015` LIKE `employees_0000`.`student_0000`; + +-- employees_0002, student_0016~student_0023 +CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0016` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0017` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0018` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0019` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0020` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0021` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0022` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`student_0023` LIKE `employees_0000`.`student_0000`; + +-- employees_0003, student_0024~student_0031 +CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0024` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0025` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0026` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0027` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0028` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0029` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0030` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`student_0031` LIKE `employees_0000`.`student_0000`; + +-- employees_0000_r, student_0000~student_0007 +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0000` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0001` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0002` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0003` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0004` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0005` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0006` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`student_0007` LIKE `employees_0000`.`student_0000`; + +-- employees_0000, __shadow_student_0000~__shadow_student_0007 +CREATE TABLE IF NOT EXISTS `employees_0000`.`__shadow_student_0000` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`__shadow_student_0001` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`__shadow_student_0002` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`__shadow_student_0003` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`__shadow_student_0004` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`__shadow_student_0005` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`__shadow_student_0006` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000`.`__shadow_student_0007` LIKE `employees_0000`.`student_0000`; + +-- employees_0001, __shadow_student_0008~__shadow_student_0015 +CREATE TABLE IF NOT EXISTS `employees_0001`.`__shadow_student_0008` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`__shadow_student_0009` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`__shadow_student_0010` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`__shadow_student_0011` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`__shadow_student_0012` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`__shadow_student_0013` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`__shadow_student_0014` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0001`.`__shadow_student_0015` LIKE `employees_0000`.`student_0000`; + +-- employees_0002, __shadow_student_0016~__shadow_student_0023 +CREATE TABLE IF NOT EXISTS `employees_0002`.`__shadow_student_0016` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`__shadow_student_0017` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`__shadow_student_0018` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`__shadow_student_0019` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`__shadow_student_0020` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`__shadow_student_0021` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`__shadow_student_0022` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0002`.`__shadow_student_0023` LIKE `employees_0000`.`student_0000`; + +-- employees_0003, __shadow_student_0024~__shadow_student_0031 +CREATE TABLE IF NOT EXISTS `employees_0003`.`__shadow_student_0024` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`__shadow_student_0025` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`__shadow_student_0026` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`__shadow_student_0027` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`__shadow_student_0028` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`__shadow_student_0029` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`__shadow_student_0030` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0003`.`__shadow_student_0031` LIKE `employees_0000`.`student_0000`; + +-- employees_0000_r, __shadow_student_0000~__shadow_student_0007 +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0000` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0001` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0002` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0003` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0004` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0005` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0006` LIKE `employees_0000`.`student_0000`; +CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0007` LIKE `employees_0000`.`student_0000`; INSERT INTO employees_0000.student_0001(id,uid,name,score,nickname,gender,birth_year,created_at,modified_at) VALUES (1, 1, 'arana', 95, 'Awesome Arana', 0, 2021, NOW(), NOW()); From e6d01571c693ff8a4dea678bd9fae02c9edeb3f8 Mon Sep 17 00:00:00 2001 From: csynineyang Date: Sun, 11 Sep 2022 11:41:05 +0800 Subject: [PATCH 2/5] Feature: support shadow type match regex #365: add integration test for shadow tables --- conf/config.yaml | 2 +- integration_test/config/shadow/config.yaml | 10 ------ .../scene/shadow/integration_test.go | 25 ++------------ integration_test/testcase/casetest.yaml | 33 +++++++++++++++++-- pkg/runtime/ast/expression_atom.go | 2 +- scripts/sharding.sql | 1 + test/dataset.go | 2 +- 7 files changed, 38 insertions(+), 37 deletions(-) diff --git a/conf/config.yaml b/conf/config.yaml index a083d207..67602d0c 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -123,7 +123,7 @@ data: match_type: regex attributes: - column: name - regex: "^hanmeimei$" + value: "^hanmeimei$" - operation: [select] match_type: hint attributes: diff --git a/integration_test/config/shadow/config.yaml b/integration_test/config/shadow/config.yaml index 40a4afdd..ee336e01 100644 --- a/integration_test/config/shadow/config.yaml +++ b/integration_test/config/shadow/config.yaml @@ -87,15 +87,6 @@ data: password: "123456" database: employees_0003 weight: r10w10 - - name: employees_shadow - nodes: - - name: node_shadow - host: arana-mysql - port: 3306 - username: root - password: "123456" - database: employees_shadow - weight: r10w10 sharding_rule: tables: - name: employees.student @@ -121,7 +112,6 @@ data: tables: - name: employees.student enable: true - group_node: employees_shadow match_rules: - operation: [select] match_type: hint diff --git a/integration_test/scene/shadow/integration_test.go b/integration_test/scene/shadow/integration_test.go index a17c7472..c69f4898 100644 --- a/integration_test/scene/shadow/integration_test.go +++ b/integration_test/scene/shadow/integration_test.go @@ -58,28 +58,9 @@ func (s *IntegrationSuite) TestShadowScene() { assert.NoError(t, err, "should begin a new tx") cases := s.TestCases() - for _, sqlCase := range cases.ExecCases { - for _, sense := range sqlCase.Sense { - if strings.Compare(strings.TrimSpace(sense), "shadow") == 1 { - params := strings.Split(sqlCase.Parameters, ",") - args := make([]interface{}, 0, len(params)) - for _, param := range params { - k, _ := test.GetValueByType(param) - args = append(args, k) - } - - // Execute sql - result, err := tx.Exec(sqlCase.SQL, args...) - assert.NoError(t, err, "exec not right") - err = sqlCase.ExpectedResult.CompareRow(result) - assert.NoError(t, err, err) - } - } - } - for _, sqlCase := range cases.QueryRowCases { for _, sense := range sqlCase.Sense { - if strings.Compare(strings.TrimSpace(sense), "shadow") == 1 { + if strings.Compare(strings.TrimSpace(sense), "shadow") == 0 { params := strings.Split(sqlCase.Parameters, ",") args := make([]interface{}, 0, len(params)) for _, param := range params { @@ -87,8 +68,8 @@ func (s *IntegrationSuite) TestShadowScene() { args = append(args, k) } - result := tx.QueryRow(sqlCase.SQL, args...) - err = sqlCase.ExpectedResult.CompareRow(result) + result := tx.QueryRow(sqlCase.SQL) + err := sqlCase.ExpectedResult.CompareRow(result) assert.NoError(t, err, err) } } diff --git a/integration_test/testcase/casetest.yaml b/integration_test/testcase/casetest.yaml index 10eca060..fe271cd0 100644 --- a/integration_test/testcase/casetest.yaml +++ b/integration_test/testcase/casetest.yaml @@ -42,6 +42,35 @@ query_row_cases: expected: type: "valueInt" value: "1" + - sql: "SELECT COUNT(1) FROM student WHERE uid=1" + parameters: "1:int" + sense: + - shadow + expected: + type: "valueInt" + value: "1" + - sql: "/*A! shadow(shadow) */ SELECT COUNT(1) FROM student WHERE uid=1" + parameters: "1:int" + sense: + - shadow + expected: + type: "valueInt" + value: "0" + - sql: "SELECT COUNT(1) FROM student WHERE name='lilei'" + parameters: "lilei:string" + sense: + - shadow + expected: + type: "valueInt" + value: "0" + - sql: "SELECT COUNT(1) FROM student WHERE name='hanmeimei'" + parameters: "hanmeimei:string" + sense: + - shadow + expected: + type: "valueInt" + value: "0" + exec_cases: - sql: "INSERT INTO sequence(name,value,modified_at) VALUES(?,?,NOW())" parameters: "1:string, 2:string" @@ -60,9 +89,9 @@ exec_cases: value: 1 - sql: "/*A! shadow(shadow) */ update student set score=100.0 where uid = ?" parameters: "2:int" - sense: + sense: - shadow - expected: + expected: type: "rowAffect" value: 1 - sql: "/*A! shadow(shadow) */ delete from student" diff --git a/pkg/runtime/ast/expression_atom.go b/pkg/runtime/ast/expression_atom.go index ae67558f..656b9574 100644 --- a/pkg/runtime/ast/expression_atom.go +++ b/pkg/runtime/ast/expression_atom.go @@ -132,7 +132,7 @@ func (u *UnaryExpressionAtom) Accept(visitor Visitor) (interface{}, error) { } func (u *UnaryExpressionAtom) IsOperatorNot() bool { - switch strings.ToUpper(strings.Trim(u.Operator, " ")) { + switch strings.ToUpper(strings.TrimSpace(u.Operator)) { case "!", "NOT": return true } diff --git a/scripts/sharding.sql b/scripts/sharding.sql index d4b92445..c6570d91 100644 --- a/scripts/sharding.sql +++ b/scripts/sharding.sql @@ -137,3 +137,4 @@ CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0006` LIKE `empl CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0007` LIKE `employees_0000`.`student_0000`; INSERT INTO employees_0000.student_0001(id,uid,name,score,nickname,gender,birth_year,created_at,modified_at) VALUES (1, 1, 'arana', 95, 'Awesome Arana', 0, 2021, NOW(), NOW()); +INSERT INTO employees_0000.__shadow_student_0002(id,uid,name,score,nickname,gender,birth_year,created_at,modified_at) VALUES (2, 2, 'hanmeimei', 97, 'Shadow Arana', 0, 2021, NOW(), NOW()); diff --git a/test/dataset.go b/test/dataset.go index d2d754d3..15f7eab2 100644 --- a/test/dataset.go +++ b/test/dataset.go @@ -93,7 +93,7 @@ type ( } Expected struct { - ResultType string `yaml:"resultType"` + ResultType string `yaml:"type"` Value string `yaml:"value"` } ) From 45eb809df96781b22e9f10e07a0b503dffb9bb8c Mon Sep 17 00:00:00 2001 From: csynineyang Date: Thu, 15 Sep 2022 14:10:24 +0800 Subject: [PATCH 3/5] Feature: support shadow type match regex #365: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. impl shadow match hint rule (insert/update/delete) 2. support shadow type match regex (insert/update/delete) 3、integrate test --- integration_test/config/shadow/config.yaml | 2 +- .../scene/shadow/integration_test.go | 38 ++++++++++++++++++ integration_test/testcase/casetest.yaml | 40 +++++++++++++++---- pkg/proto/rule/database_table.go | 14 +++++-- pkg/proto/rule/database_table_test.go | 3 +- pkg/runtime/optimize/dml/delete.go | 28 ++++++++----- pkg/runtime/optimize/dml/insert.go | 23 ++++++----- pkg/runtime/optimize/dml/update.go | 11 +++-- scripts/sharding.sql | 1 - test/dataset.go | 2 + 10 files changed, 125 insertions(+), 37 deletions(-) diff --git a/integration_test/config/shadow/config.yaml b/integration_test/config/shadow/config.yaml index ee336e01..bb440bab 100644 --- a/integration_test/config/shadow/config.yaml +++ b/integration_test/config/shadow/config.yaml @@ -113,7 +113,7 @@ data: - name: employees.student enable: true match_rules: - - operation: [select] + - operation: [select, insert, update, delete] match_type: hint attributes: - value: "shadow" diff --git a/integration_test/scene/shadow/integration_test.go b/integration_test/scene/shadow/integration_test.go index c69f4898..6aa77080 100644 --- a/integration_test/scene/shadow/integration_test.go +++ b/integration_test/scene/shadow/integration_test.go @@ -74,4 +74,42 @@ func (s *IntegrationSuite) TestShadowScene() { } } } + + for _, sqlCase := range cases.ExShHintCases { + for _, sense := range sqlCase.Sense { + if strings.Compare(strings.TrimSpace(sense), "shadow") == 0 { + params := strings.Split(sqlCase.Parameters, ",") + args := make([]interface{}, 0, len(params)) + for _, param := range params { + k, _ := test.GetValueByType(param) + args = append(args, k) + } + + result, err := tx.Exec(sqlCase.SQL) + assert.NoError(t, err, err) + err = sqlCase.ExpectedResult.CompareRow(result) + assert.NoError(t, err, err) + } + } + } + + /* + for _, sqlCase := range cases.ExShRegexCases { + for _, sense := range sqlCase.Sense { + if strings.Compare(strings.TrimSpace(sense), "shadow") == 0 { + params := strings.Split(sqlCase.Parameters, ",") + args := make([]interface{}, 0, len(params)) + for _, param := range params { + k, _ := test.GetValueByType(param) + args = append(args, k) + } + + result, err := tx.Exec(sqlCase.SQL) + assert.NoError(t, err, err) + err = sqlCase.ExpectedResult.CompareRow(result) + assert.NoError(t, err, err) + } + } + } + */ } diff --git a/integration_test/testcase/casetest.yaml b/integration_test/testcase/casetest.yaml index fe271cd0..3478be89 100644 --- a/integration_test/testcase/casetest.yaml +++ b/integration_test/testcase/casetest.yaml @@ -80,23 +80,49 @@ exec_cases: expected: type: "rowAffect" value: 1 - - sql: "/*A! shadow(shadow) */ INSERT INTO student(id,uid,score,name,nickname,gender,birth_year) values (?,?,?,?,?,?,?)" - parameters: "1:int, 2:int, 100:int, test:string, test:string, 1:int, 1980:int" + +exec_shadow_hint_cases: + - sql: "/*A! shadow(shadow) */ INSERT INTO student(id,uid,score,name,nickname,gender,birth_year) values (1,3,100,'lilei','shadow test',1,1980)" + parameters: "1:int" + sense: + - shadow + expected: + type: "rowAffect" + value: 1 + - sql: "/*A! shadow(shadow) */ UPDATE student SET score = 98 WHERE uid = 3" + parameters: "1:int" + sense: + - shadow + expected: + type: "rowAffect" + value: 1 + - sql: "/*A! shadow(shadow) */ DELETE FROM student WHERE uid = 3" + parameters: "1:int" + sense: + - shadow + expected: + type: "rowAffect" + value: 1 + +exec_shadow_regex_cases: + - sql: "INSERT INTO student(id,uid,score,name,nickname,gender,birth_year) values (1,2,100,'hanmeimei','shadow test',1,1981)" + parameters: "1:int" sense: - shadow expected: type: "rowAffect" value: 1 - - sql: "/*A! shadow(shadow) */ update student set score=100.0 where uid = ?" - parameters: "2:int" + - sql: "UPDATE student SET score = 98 WHERE uid = 2 AND name = 'hanmeimei'" + parameters: "1:int" sense: - - shadow + - shadow expected: type: "rowAffect" value: 1 - - sql: "/*A! shadow(shadow) */ delete from student" + - sql: "DELETE FROM student WHERE uid = 2 AND name = 'hanmeimei'" + parameters: "1:int" sense: - shadow expected: type: "rowAffect" - value: 0 + value: 1 \ No newline at end of file diff --git a/pkg/proto/rule/database_table.go b/pkg/proto/rule/database_table.go index 2f3281c0..99718875 100644 --- a/pkg/proto/rule/database_table.go +++ b/pkg/proto/rule/database_table.go @@ -22,6 +22,10 @@ import ( "strings" ) +import ( + "github.com/arana-db/arana/pkg/constants" +) + // DatabaseTable represents the pair of database and table. type DatabaseTable struct { Database, Table string @@ -329,15 +333,17 @@ func (dt DatabaseTables) String() string { return sb.String() } -func (dt DatabaseTables) ReplaceDb(new string) { +func (dt DatabaseTables) ReplaceDb() { if dt.IsEmpty() { return } newTbls := make([]string, 0) for db, tbls := range dt { - newTbls = append(newTbls, tbls...) + for _, tb := range tbls { + newTb := constants.ShadowTablePrefix + tb + newTbls = append(newTbls, newTb) + } delete(dt, db) + dt[db] = newTbls } - - dt[new] = newTbls } diff --git a/pkg/proto/rule/database_table_test.go b/pkg/proto/rule/database_table_test.go index 50851557..b0e997a3 100644 --- a/pkg/proto/rule/database_table_test.go +++ b/pkg/proto/rule/database_table_test.go @@ -165,8 +165,7 @@ func TestDatabaseTables_Replace(t *testing.T) { } { t.Run(it.input, func(t *testing.T) { dt := parseDatabaseTablesFromString(it.input) - dt.ReplaceDb("shadow") - assert.Equal(t, 6, len(dt["shadow"])) + dt.ReplaceDb() assert.Equal(t, 1, len(dt)) }) } diff --git a/pkg/runtime/optimize/dml/delete.go b/pkg/runtime/optimize/dml/delete.go index fd6d4a10..936879be 100644 --- a/pkg/runtime/optimize/dml/delete.go +++ b/pkg/runtime/optimize/dml/delete.go @@ -28,6 +28,7 @@ import ( import ( "github.com/arana-db/arana/pkg/constants" "github.com/arana-db/arana/pkg/proto" + "github.com/arana-db/arana/pkg/proto/rule" "github.com/arana-db/arana/pkg/runtime/ast" "github.com/arana-db/arana/pkg/runtime/optimize" "github.com/arana-db/arana/pkg/runtime/plan" @@ -41,10 +42,10 @@ func init() { func optimizeDelete(ctx context.Context, o *optimize.Optimizer) (proto.Plan, error) { stmt := o.Stmt.(*ast.DeleteStatement) - shards, err := o.ComputeShards(stmt.Table, stmt.Where, o.Args) - if err != nil { - return nil, errors.Wrap(err, "failed to optimize DELETE statement") - } + var ( + shards rule.DatabaseTables + err error + ) var matchShadow bool if len(o.Hints) > 0 { @@ -56,19 +57,26 @@ func optimizeDelete(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err } // TODO: delete from a child sharding-table directly + if shards == nil { + //first shadow_rule, and then sharding_rule + if o.ShadowRule != nil && !matchShadow { + if matchShadow, err = (*optimize.ShadowSharder)(o.ShadowRule).Shard(stmt.Table, constants.ShadowDelete, stmt.Where, o.Args...); err != nil { + return nil, errors.Wrap(err, "calculate shards failed") + } + } + + if shards, _, err = (*optimize.Sharder)(o.Rule).Shard(stmt.Table, stmt.Where, o.Args...); err != nil { + return nil, errors.Wrap(err, "failed to optimize DELETE statement") + } + } if shards == nil { transparent := plan.Transparent(stmt, o.Args) - if matchShadow { - //TODO: fix it - //transparent.SetDB(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) - } return transparent, nil } if matchShadow { - //TODO: fix it - //shards.ReplaceDb(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) + shards.ReplaceDb() } ret := dml.NewSimpleDeletePlan(stmt) diff --git a/pkg/runtime/optimize/dml/insert.go b/pkg/runtime/optimize/dml/insert.go index 859a1c62..cca8ea5a 100644 --- a/pkg/runtime/optimize/dml/insert.go +++ b/pkg/runtime/optimize/dml/insert.go @@ -103,8 +103,6 @@ func optimizeInsert(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err for i, values := range stmt.Values { var shards rule.DatabaseTables - value := values[bingo] - resetFilter(stmt.Columns[bingo], value) if len(o.Hints) > 0 { var hintLoader optimize.HintResultLoader @@ -112,11 +110,23 @@ func optimizeInsert(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err return nil, errors.Wrap(err, "calculate hints failed") } - shards = hintLoader.GetShards() matchShadow = hintLoader.GetMatchBy(tableName.Suffix(), constants.ShadowInsert) } if shards == nil { + //first shadow_rule, and then sharding_rule + for v := range values { + value := values[v] + resetFilter(stmt.Columns[v], value) + if o.ShadowRule != nil && !matchShadow { + if matchShadow, err = (*optimize.ShadowSharder)(o.ShadowRule).Shard(tableName, constants.ShadowInsert, filter, o.Args...); err != nil { + return nil, errors.Wrap(err, "calculate shards failed") + } + } + } + + value := values[bingo] + resetFilter(stmt.Columns[bingo], value) if shards, _, err = sharder.Shard(tableName, filter, o.Args...); err != nil { return nil, errors.WithStack(err) } @@ -133,8 +143,7 @@ func optimizeInsert(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err for k, v := range shards { if matchShadow { - //TODO: fix it - //k = o.ShadowRule.GetDatabase(tableName.Suffix()) + v[0] = o.ShadowRule.GetTableName(v[0]) } db = k table = v[0] @@ -148,10 +157,6 @@ func optimizeInsert(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err } for db, slot := range slots { - if matchShadow { - //TODO: fix it - //db = o.ShadowRule.GetDatabase(tableName.Suffix()) - } for table, indexes := range slot { // clone insert stmt without values newborn := ast.NewInsertStatement(ast.TableName{table}, stmt.Columns) diff --git a/pkg/runtime/optimize/dml/update.go b/pkg/runtime/optimize/dml/update.go index 73dce2c2..c80aaeb4 100644 --- a/pkg/runtime/optimize/dml/update.go +++ b/pkg/runtime/optimize/dml/update.go @@ -75,11 +75,17 @@ func optimizeUpdate(_ context.Context, o *optimize.Optimizer) (proto.Plan, error if hintLoader, err = optimize.Hints(table, o.Hints, o.Rule, o.ShadowRule); err != nil { return nil, errors.Wrap(err, "calculate hints failed") } - shards = hintLoader.GetShards() matchShadow = hintLoader.GetMatchBy(table.Suffix(), constants.ShadowUpdate) } if shards == nil { + //first shadow_rule, and then sharding_rule + if o.ShadowRule != nil && !matchShadow { + if matchShadow, err = (*optimize.ShadowSharder)(o.ShadowRule).Shard(table, constants.ShadowUpdate, stmt.Where, o.Args...); err != nil { + return nil, errors.Wrap(err, "calculate shards failed") + } + } + if shards, fullScan, err = (*optimize.Sharder)(o.Rule).Shard(table, where, o.Args...); err != nil { return nil, errors.Wrap(err, "failed to update") } @@ -103,8 +109,7 @@ func optimizeUpdate(_ context.Context, o *optimize.Optimizer) (proto.Plan, error } if matchShadow { - //TODO: fix it - //shards.ReplaceDb(o.ShadowRule.GetDatabase(stmt.Table.Suffix())) + shards.ReplaceDb() } ret := dml.NewUpdatePlan(stmt) diff --git a/scripts/sharding.sql b/scripts/sharding.sql index c6570d91..d4b92445 100644 --- a/scripts/sharding.sql +++ b/scripts/sharding.sql @@ -137,4 +137,3 @@ CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0006` LIKE `empl CREATE TABLE IF NOT EXISTS `employees_0000_r`.`__shadow_student_0007` LIKE `employees_0000`.`student_0000`; INSERT INTO employees_0000.student_0001(id,uid,name,score,nickname,gender,birth_year,created_at,modified_at) VALUES (1, 1, 'arana', 95, 'Awesome Arana', 0, 2021, NOW(), NOW()); -INSERT INTO employees_0000.__shadow_student_0002(id,uid,name,score,nickname,gender,birth_year,created_at,modified_at) VALUES (2, 2, 'hanmeimei', 97, 'Shadow Arana', 0, 2021, NOW(), NOW()); diff --git a/test/dataset.go b/test/dataset.go index 15f7eab2..3b6c05d7 100644 --- a/test/dataset.go +++ b/test/dataset.go @@ -82,6 +82,8 @@ type ( ExecCases []*Case `yaml:"exec_cases"` QueryRowsCases []*Case `yaml:"query_rows_cases"` QueryRowCases []*Case `yaml:"query_row_cases"` + ExShHintCases []*Case `yaml:"exec_shadow_hint_cases"` + ExShRegexCases []*Case `yaml:"exec_shadow_regex_cases"` } Case struct { From e9af398810c1e1b400652412855f437a3883a0d0 Mon Sep 17 00:00:00 2001 From: csynineyang Date: Sat, 17 Sep 2022 19:26:42 +0800 Subject: [PATCH 4/5] Feature: support shadow type match regex #365 modify error messages and comments --- integration_test/testcase/casetest.yaml | 2 +- pkg/runtime/optimize/dml/delete.go | 7 +++---- pkg/runtime/optimize/dml/insert.go | 5 ++--- pkg/runtime/optimize/dml/select.go | 3 +-- pkg/runtime/optimize/dml/update.go | 5 ++--- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/integration_test/testcase/casetest.yaml b/integration_test/testcase/casetest.yaml index 3478be89..b902ed30 100644 --- a/integration_test/testcase/casetest.yaml +++ b/integration_test/testcase/casetest.yaml @@ -125,4 +125,4 @@ exec_shadow_regex_cases: - shadow expected: type: "rowAffect" - value: 1 \ No newline at end of file + value: 1 diff --git a/pkg/runtime/optimize/dml/delete.go b/pkg/runtime/optimize/dml/delete.go index 936879be..cb6c40d4 100644 --- a/pkg/runtime/optimize/dml/delete.go +++ b/pkg/runtime/optimize/dml/delete.go @@ -51,22 +51,21 @@ func optimizeDelete(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err if len(o.Hints) > 0 { shadowLoader, err := optimize.Hints(stmt.Table, o.Hints, o.Rule, o.ShadowRule) if err != nil { - return nil, errors.Wrap(err, "failed to optimize hint DELETE statement") + return nil, errors.Wrap(err, "calculate hits failed") } matchShadow = shadowLoader.GetMatchBy(stmt.Table.Suffix(), constants.ShadowDelete) } // TODO: delete from a child sharding-table directly if shards == nil { - //first shadow_rule, and then sharding_rule if o.ShadowRule != nil && !matchShadow { if matchShadow, err = (*optimize.ShadowSharder)(o.ShadowRule).Shard(stmt.Table, constants.ShadowDelete, stmt.Where, o.Args...); err != nil { - return nil, errors.Wrap(err, "calculate shards failed") + return nil, errors.Wrap(err, "calculate shadow regex failed") } } if shards, _, err = (*optimize.Sharder)(o.Rule).Shard(stmt.Table, stmt.Where, o.Args...); err != nil { - return nil, errors.Wrap(err, "failed to optimize DELETE statement") + return nil, errors.Wrap(err, "calculate shards failed") } } diff --git a/pkg/runtime/optimize/dml/insert.go b/pkg/runtime/optimize/dml/insert.go index cca8ea5a..6794dccd 100644 --- a/pkg/runtime/optimize/dml/insert.go +++ b/pkg/runtime/optimize/dml/insert.go @@ -114,13 +114,12 @@ func optimizeInsert(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err } if shards == nil { - //first shadow_rule, and then sharding_rule for v := range values { value := values[v] resetFilter(stmt.Columns[v], value) if o.ShadowRule != nil && !matchShadow { if matchShadow, err = (*optimize.ShadowSharder)(o.ShadowRule).Shard(tableName, constants.ShadowInsert, filter, o.Args...); err != nil { - return nil, errors.Wrap(err, "calculate shards failed") + return nil, errors.Wrap(err, "calculate shadow regex failed") } } } @@ -128,7 +127,7 @@ func optimizeInsert(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err value := values[bingo] resetFilter(stmt.Columns[bingo], value) if shards, _, err = sharder.Shard(tableName, filter, o.Args...); err != nil { - return nil, errors.WithStack(err) + return nil, errors.Wrap(err, "calculate shards failed") } } diff --git a/pkg/runtime/optimize/dml/select.go b/pkg/runtime/optimize/dml/select.go index 9f8046ff..22b67d9d 100644 --- a/pkg/runtime/optimize/dml/select.go +++ b/pkg/runtime/optimize/dml/select.go @@ -97,10 +97,9 @@ func optimizeSelect(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err } if shards == nil { - //first shadow_rule, and then sharding_rule if o.ShadowRule != nil && !matchShadow { if matchShadow, err = (*optimize.ShadowSharder)(o.ShadowRule).Shard(tableName, constants.ShadowSelect, stmt.Where, o.Args...); err != nil && fullScan == false { - return nil, errors.Wrap(err, "calculate shards failed") + return nil, errors.Wrap(err, "calculate shadow regex failed") } } diff --git a/pkg/runtime/optimize/dml/update.go b/pkg/runtime/optimize/dml/update.go index c80aaeb4..cdedcab8 100644 --- a/pkg/runtime/optimize/dml/update.go +++ b/pkg/runtime/optimize/dml/update.go @@ -79,15 +79,14 @@ func optimizeUpdate(_ context.Context, o *optimize.Optimizer) (proto.Plan, error } if shards == nil { - //first shadow_rule, and then sharding_rule if o.ShadowRule != nil && !matchShadow { if matchShadow, err = (*optimize.ShadowSharder)(o.ShadowRule).Shard(table, constants.ShadowUpdate, stmt.Where, o.Args...); err != nil { - return nil, errors.Wrap(err, "calculate shards failed") + return nil, errors.Wrap(err, "calculate shadow regex failed") } } if shards, fullScan, err = (*optimize.Sharder)(o.Rule).Shard(table, where, o.Args...); err != nil { - return nil, errors.Wrap(err, "failed to update") + return nil, errors.Wrap(err, "calculate shards failed") } } } From ee7ef25d895c22fa3fb388e63b64360579e7c2a1 Mon Sep 17 00:00:00 2001 From: csynineyang Date: Sat, 17 Sep 2022 19:39:05 +0800 Subject: [PATCH 5/5] Feature: support shadow type match regex #365 ignore parameters temporary --- .../scene/shadow/integration_test.go | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/integration_test/scene/shadow/integration_test.go b/integration_test/scene/shadow/integration_test.go index 6aa77080..ab5b55ef 100644 --- a/integration_test/scene/shadow/integration_test.go +++ b/integration_test/scene/shadow/integration_test.go @@ -61,13 +61,6 @@ func (s *IntegrationSuite) TestShadowScene() { for _, sqlCase := range cases.QueryRowCases { for _, sense := range sqlCase.Sense { if strings.Compare(strings.TrimSpace(sense), "shadow") == 0 { - params := strings.Split(sqlCase.Parameters, ",") - args := make([]interface{}, 0, len(params)) - for _, param := range params { - k, _ := test.GetValueByType(param) - args = append(args, k) - } - result := tx.QueryRow(sqlCase.SQL) err := sqlCase.ExpectedResult.CompareRow(result) assert.NoError(t, err, err) @@ -78,13 +71,6 @@ func (s *IntegrationSuite) TestShadowScene() { for _, sqlCase := range cases.ExShHintCases { for _, sense := range sqlCase.Sense { if strings.Compare(strings.TrimSpace(sense), "shadow") == 0 { - params := strings.Split(sqlCase.Parameters, ",") - args := make([]interface{}, 0, len(params)) - for _, param := range params { - k, _ := test.GetValueByType(param) - args = append(args, k) - } - result, err := tx.Exec(sqlCase.SQL) assert.NoError(t, err, err) err = sqlCase.ExpectedResult.CompareRow(result) @@ -97,13 +83,6 @@ func (s *IntegrationSuite) TestShadowScene() { for _, sqlCase := range cases.ExShRegexCases { for _, sense := range sqlCase.Sense { if strings.Compare(strings.TrimSpace(sense), "shadow") == 0 { - params := strings.Split(sqlCase.Parameters, ",") - args := make([]interface{}, 0, len(params)) - for _, param := range params { - k, _ := test.GetValueByType(param) - args = append(args, k) - } - result, err := tx.Exec(sqlCase.SQL) assert.NoError(t, err, err) err = sqlCase.ExpectedResult.CompareRow(result)