vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vreplication/controller_plan.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package vreplication 18 19 import ( 20 "fmt" 21 22 "vitess.io/vitess/go/vt/sqlparser" 23 ) 24 25 // controllerPlan is the plan for vreplication control statements. 26 type controllerPlan struct { 27 query string 28 opcode int 29 30 // numInserts is set for insertQuery. 31 numInserts int 32 33 // selector and applier are set for updateQuery and deleteQuery. 34 selector string 35 applier *sqlparser.ParsedQuery 36 37 // delCopyState deletes related copy state. 38 delCopyState *sqlparser.ParsedQuery 39 40 // delPostCopyAction deletes related post copy actions. 41 delPostCopyAction *sqlparser.ParsedQuery 42 } 43 44 const ( 45 insertQuery = iota 46 updateQuery 47 deleteQuery 48 selectQuery 49 reshardingJournalQuery 50 ) 51 52 // buildControllerPlan parses the input query and returns an appropriate plan. 53 func buildControllerPlan(query string) (*controllerPlan, error) { 54 stmt, err := sqlparser.Parse(query) 55 if err != nil { 56 return nil, err 57 } 58 var plan *controllerPlan 59 switch stmt := stmt.(type) { 60 case *sqlparser.Insert: 61 plan, err = buildInsertPlan(stmt) 62 case *sqlparser.Update: 63 plan, err = buildUpdatePlan(stmt) 64 case *sqlparser.Delete: 65 plan, err = buildDeletePlan(stmt) 66 case *sqlparser.Select: 67 plan, err = buildSelectPlan(stmt) 68 default: 69 return nil, fmt.Errorf("unsupported construct: %s", sqlparser.String(stmt)) 70 } 71 if err != nil { 72 return nil, err 73 } 74 plan.query = query 75 return plan, nil 76 } 77 78 func buildInsertPlan(ins *sqlparser.Insert) (*controllerPlan, error) { 79 switch sqlparser.String(ins.Table) { 80 case reshardingJournalTableName: 81 return &controllerPlan{ 82 opcode: reshardingJournalQuery, 83 }, nil 84 case vreplicationTableName: 85 // no-op 86 default: 87 return nil, fmt.Errorf("invalid table name: %v", sqlparser.String(ins.Table)) 88 } 89 if ins.Action != sqlparser.InsertAct { 90 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins)) 91 } 92 if ins.Ignore { 93 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins)) 94 } 95 if ins.Partitions != nil { 96 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins)) 97 } 98 if ins.OnDup != nil { 99 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins)) 100 } 101 rows, ok := ins.Rows.(sqlparser.Values) 102 if !ok { 103 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins)) 104 } 105 idPos := 0 106 if len(ins.Columns) != 0 { 107 idPos = -1 108 for i, col := range ins.Columns { 109 if col.EqualString("id") { 110 idPos = i 111 break 112 } 113 } 114 } 115 if idPos >= 0 { 116 for _, row := range rows { 117 if idPos >= len(row) { 118 return nil, fmt.Errorf("malformed statement: %v", sqlparser.String(ins)) 119 } 120 if _, ok := row[idPos].(*sqlparser.NullVal); !ok { 121 return nil, fmt.Errorf("id should not have a value: %v", sqlparser.String(ins)) 122 } 123 } 124 } 125 return &controllerPlan{ 126 opcode: insertQuery, 127 numInserts: len(rows), 128 }, nil 129 } 130 131 func buildUpdatePlan(upd *sqlparser.Update) (*controllerPlan, error) { 132 switch sqlparser.String(upd.TableExprs) { 133 case reshardingJournalTableName: 134 return &controllerPlan{ 135 opcode: reshardingJournalQuery, 136 }, nil 137 case vreplicationTableName: 138 // no-op 139 default: 140 return nil, fmt.Errorf("invalid table name: %v", sqlparser.String(upd.TableExprs)) 141 } 142 if upd.OrderBy != nil || upd.Limit != nil { 143 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(upd)) 144 } 145 for _, expr := range upd.Exprs { 146 if expr.Name.Name.EqualString("id") { 147 return nil, fmt.Errorf("id cannot be changed: %v", sqlparser.String(expr)) 148 } 149 } 150 151 buf1 := sqlparser.NewTrackedBuffer(nil) 152 buf1.Myprintf("select id from %s%v", vreplicationTableName, upd.Where) 153 upd.Where = &sqlparser.Where{ 154 Type: sqlparser.WhereClause, 155 Expr: &sqlparser.ComparisonExpr{ 156 Left: &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("id")}, 157 Operator: sqlparser.InOp, 158 Right: sqlparser.ListArg("ids"), 159 }, 160 } 161 162 buf2 := sqlparser.NewTrackedBuffer(nil) 163 buf2.Myprintf("%v", upd) 164 165 return &controllerPlan{ 166 opcode: updateQuery, 167 selector: buf1.String(), 168 applier: buf2.ParsedQuery(), 169 }, nil 170 } 171 172 func buildDeletePlan(del *sqlparser.Delete) (*controllerPlan, error) { 173 switch sqlparser.String(del.TableExprs) { 174 case reshardingJournalTableName: 175 return &controllerPlan{ 176 opcode: reshardingJournalQuery, 177 }, nil 178 case vreplicationTableName: 179 // no-op 180 default: 181 return nil, fmt.Errorf("invalid table name: %v", sqlparser.String(del.TableExprs)) 182 } 183 if del.Targets != nil { 184 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(del)) 185 } 186 if del.Partitions != nil { 187 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(del)) 188 } 189 if del.OrderBy != nil || del.Limit != nil { 190 return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(del)) 191 } 192 193 buf1 := sqlparser.NewTrackedBuffer(nil) 194 buf1.Myprintf("select id from %s%v", vreplicationTableName, del.Where) 195 del.Where = &sqlparser.Where{ 196 Type: sqlparser.WhereClause, 197 Expr: &sqlparser.ComparisonExpr{ 198 Left: &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("id")}, 199 Operator: sqlparser.InOp, 200 Right: sqlparser.ListArg("ids"), 201 }, 202 } 203 204 buf2 := sqlparser.NewTrackedBuffer(nil) 205 buf2.Myprintf("%v", del) 206 207 copyStateWhere := &sqlparser.Where{ 208 Type: sqlparser.WhereClause, 209 Expr: &sqlparser.ComparisonExpr{ 210 Left: &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("vrepl_id")}, 211 Operator: sqlparser.InOp, 212 Right: sqlparser.ListArg("ids"), 213 }, 214 } 215 buf3 := sqlparser.NewTrackedBuffer(nil) 216 buf3.Myprintf("delete from %s%v", copyStateTableName, copyStateWhere) 217 218 buf4 := sqlparser.NewTrackedBuffer(nil) 219 buf4.Myprintf("delete from %s%v", postCopyActionTableName, copyStateWhere) 220 221 return &controllerPlan{ 222 opcode: deleteQuery, 223 selector: buf1.String(), 224 applier: buf2.ParsedQuery(), 225 delCopyState: buf3.ParsedQuery(), 226 delPostCopyAction: buf4.ParsedQuery(), 227 }, nil 228 } 229 230 func buildSelectPlan(sel *sqlparser.Select) (*controllerPlan, error) { 231 switch sqlparser.ToString(sel.From) { 232 case vreplicationTableName, reshardingJournalTableName, copyStateTableName, vreplicationLogTableName: 233 return &controllerPlan{ 234 opcode: selectQuery, 235 }, nil 236 default: 237 return nil, fmt.Errorf("invalid table name: %v", sqlparser.ToString(sel.From)) 238 } 239 }