Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: support shadow type match regex (#365) #405

Merged
merged 2 commits into from
Sep 12, 2022
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
14 changes: 2 additions & 12 deletions conf/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -133,7 +123,7 @@ data:
match_type: regex
attributes:
- column: name
regex: "^hanmeimei$"
value: "^hanmeimei$"
- operation: [select]
match_type: hint
attributes:
Expand Down
10 changes: 0 additions & 10 deletions integration_test/config/shadow/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -121,7 +112,6 @@ data:
tables:
- name: employees.student
enable: true
group_node: employees_shadow
match_rules:
- operation: [select]
match_type: hint
Expand Down
25 changes: 3 additions & 22 deletions integration_test/scene/shadow/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,37 +58,18 @@ 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 {
k, _ := test.GetValueByType(param)
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)
}
}
Expand Down
33 changes: 31 additions & 2 deletions integration_test/testcase/casetest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down
6 changes: 6 additions & 0 deletions pkg/constants/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ const (
VariableNameMaxAllowedPacket = "max_allowed_packet"
)

const (
HeaderPrefix = "Tables_in_"
AranaSystemTablePrefix = "__arana_"
ShadowTablePrefix = "__shadow_"
)

const (
ShadowMatchRegex = "regex"
ShadowMatchValue = "value"
Expand Down
35 changes: 29 additions & 6 deletions pkg/proto/rule/shadow.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package rule

import (
"regexp"
"sync"
)

Expand All @@ -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.
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/runtime/ast/expression_atom.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (u *UnaryExpressionAtom) Accept(visitor Visitor) (interface{}, error) {
}

func (u *UnaryExpressionAtom) IsOperatorNot() bool {
switch u.Operator {
switch strings.ToUpper(strings.TrimSpace(u.Operator)) {
case "!", "NOT":
return true
}
Expand Down
6 changes: 4 additions & 2 deletions pkg/runtime/optimize/dml/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions pkg/runtime/optimize/dml/insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand Down
18 changes: 13 additions & 5 deletions pkg/runtime/optimize/dml/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion pkg/runtime/optimize/dml/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
37 changes: 37 additions & 0 deletions pkg/runtime/optimize/sharder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading