vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/planbuilder/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 planbuilder 18 19 import ( 20 "encoding/json" 21 "strings" 22 23 "vitess.io/vitess/go/vt/vtgate/evalengine" 24 25 "vitess.io/vitess/go/vt/sqlparser" 26 "vitess.io/vitess/go/vt/tableacl" 27 "vitess.io/vitess/go/vt/vterrors" 28 "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" 29 30 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 31 ) 32 33 var ( 34 execLimit = &sqlparser.Limit{Rowcount: sqlparser.NewArgument("#maxLimit")} 35 36 // PassthroughDMLs will return plans that pass-through the DMLs without changing them. 37 PassthroughDMLs = false 38 ) 39 40 // _______________________________________________ 41 42 // PlanType indicates a query plan type. 43 type PlanType int 44 45 // The following are PlanType values. 46 const ( 47 PlanSelect PlanType = iota 48 PlanNextval 49 PlanSelectImpossible 50 PlanSelectLockFunc 51 PlanInsert 52 PlanInsertMessage 53 PlanUpdate 54 PlanUpdateLimit 55 PlanDelete 56 PlanDeleteLimit 57 PlanDDL 58 PlanSet 59 // PlanOtherRead is for statements like show, etc. 60 PlanOtherRead 61 // PlanOtherAdmin is for statements like repair, lock table, etc. 62 PlanOtherAdmin 63 PlanSelectStream 64 // PlanMessageStream is for "stream" statements. 65 PlanMessageStream 66 PlanSavepoint 67 PlanRelease 68 PlanSRollback 69 PlanShow 70 // PlanLoad is for Load data statements 71 PlanLoad 72 // PlanFlush is for FLUSH statements 73 PlanFlush 74 PlanLockTables 75 PlanUnlockTables 76 PlanCallProc 77 PlanAlterMigration 78 PlanRevertMigration 79 PlanShowMigrationLogs 80 PlanShowThrottledApps 81 PlanShowThrottlerStatus 82 PlanViewDDL 83 NumPlans 84 ) 85 86 // Must exactly match order of plan constants. 87 var planName = []string{ 88 "Select", 89 "Nextval", 90 "SelectImpossible", 91 "SelectLockFunc", 92 "Insert", 93 "InsertMessage", 94 "Update", 95 "UpdateLimit", 96 "Delete", 97 "DeleteLimit", 98 "DDL", 99 "Set", 100 "OtherRead", 101 "OtherAdmin", 102 "SelectStream", 103 "MessageStream", 104 "Savepoint", 105 "Release", 106 "RollbackSavepoint", 107 "Show", 108 "Load", 109 "Flush", 110 "LockTables", 111 "UnlockTables", 112 "CallProcedure", 113 "AlterMigration", 114 "RevertMigration", 115 "ShowMigrationLogs", 116 "ShowThrottledApps", 117 "ShowThrottlerStatus", 118 "ViewDDL", 119 } 120 121 func (pt PlanType) String() string { 122 if pt < 0 || pt >= NumPlans { 123 return "" 124 } 125 return planName[pt] 126 } 127 128 // PlanByName find a PlanType by its string name. 129 func PlanByName(s string) (pt PlanType, ok bool) { 130 for i, v := range planName { 131 if v == s { 132 return PlanType(i), true 133 } 134 } 135 return NumPlans, false 136 } 137 138 // PlanByNameIC finds a plan type by its string name without case sensitivity 139 func PlanByNameIC(s string) (pt PlanType, ok bool) { 140 for i, v := range planName { 141 if strings.EqualFold(v, s) { 142 return PlanType(i), true 143 } 144 } 145 return NumPlans, false 146 } 147 148 // MarshalJSON returns a json string for PlanType. 149 func (pt PlanType) MarshalJSON() ([]byte, error) { 150 return json.Marshal(pt.String()) 151 } 152 153 // _______________________________________________ 154 155 // Plan contains the parameters for executing a request. 156 type Plan struct { 157 PlanID PlanType 158 // When the query indicates a single table 159 Table *schema.Table 160 // SELECT, UPDATE, DELETE statements may list multiple tables 161 AllTables []*schema.Table 162 163 // Permissions stores the permissions for the tables accessed in the query. 164 Permissions []Permission 165 166 // FullQuery will be set for all plans. 167 FullQuery *sqlparser.ParsedQuery 168 169 // NextCount stores the count for "select next". 170 NextCount evalengine.Expr 171 172 // WhereClause is set for DMLs. It is used by the hot row protection 173 // to serialize e.g. UPDATEs going to the same row. 174 WhereClause *sqlparser.ParsedQuery 175 176 // FullStmt can be used when the query does not operate on tables 177 FullStmt sqlparser.Statement 178 179 // NeedsReservedConn indicates at a reserved connection is needed to execute this plan 180 NeedsReservedConn bool 181 } 182 183 // TableName returns the table name for the plan. 184 func (plan *Plan) TableName() sqlparser.IdentifierCS { 185 var tableName sqlparser.IdentifierCS 186 if plan.Table != nil { 187 tableName = plan.Table.Name 188 } 189 return tableName 190 } 191 192 // TableNames returns the table names for all tables in the plan. 193 func (plan *Plan) TableNames() (names []string) { 194 if len(plan.AllTables) == 0 { 195 tableName := plan.TableName() 196 return []string{tableName.String()} 197 } 198 for _, table := range plan.AllTables { 199 names = append(names, table.Name.String()) 200 } 201 return names 202 } 203 204 // Build builds a plan based on the schema. 205 func Build(statement sqlparser.Statement, tables map[string]*schema.Table, dbName string, viewsEnabled bool) (plan *Plan, err error) { 206 switch stmt := statement.(type) { 207 case *sqlparser.Union: 208 plan, err = &Plan{ 209 PlanID: PlanSelect, 210 FullQuery: GenerateLimitQuery(stmt), 211 }, nil 212 case *sqlparser.Select: 213 plan, err = analyzeSelect(stmt, tables) 214 case *sqlparser.Insert: 215 plan, err = analyzeInsert(stmt, tables) 216 case *sqlparser.Update: 217 plan, err = analyzeUpdate(stmt, tables) 218 case *sqlparser.Delete: 219 plan, err = analyzeDelete(stmt, tables) 220 case *sqlparser.Set: 221 plan, err = analyzeSet(stmt), nil 222 case sqlparser.DDLStatement: 223 plan, err = analyzeDDL(stmt, viewsEnabled) 224 case *sqlparser.AlterMigration: 225 plan, err = &Plan{PlanID: PlanAlterMigration, FullStmt: stmt}, nil 226 case *sqlparser.RevertMigration: 227 plan, err = &Plan{PlanID: PlanRevertMigration, FullStmt: stmt}, nil 228 case *sqlparser.ShowMigrationLogs: 229 plan, err = &Plan{PlanID: PlanShowMigrationLogs, FullStmt: stmt}, nil 230 case *sqlparser.ShowThrottledApps: 231 plan, err = &Plan{PlanID: PlanShowThrottledApps, FullStmt: stmt}, nil 232 case *sqlparser.ShowThrottlerStatus: 233 plan, err = &Plan{PlanID: PlanShowThrottlerStatus, FullStmt: stmt}, nil 234 case *sqlparser.Show: 235 plan, err = analyzeShow(stmt, dbName) 236 case *sqlparser.OtherRead, sqlparser.Explain: 237 plan, err = &Plan{PlanID: PlanOtherRead}, nil 238 case *sqlparser.OtherAdmin: 239 plan, err = &Plan{PlanID: PlanOtherAdmin}, nil 240 case *sqlparser.Savepoint: 241 plan, err = &Plan{PlanID: PlanSavepoint}, nil 242 case *sqlparser.Release: 243 plan, err = &Plan{PlanID: PlanRelease}, nil 244 case *sqlparser.SRollback: 245 plan, err = &Plan{PlanID: PlanSRollback}, nil 246 case *sqlparser.Load: 247 plan, err = &Plan{PlanID: PlanLoad}, nil 248 case *sqlparser.Flush: 249 plan, err = &Plan{PlanID: PlanFlush, FullQuery: GenerateFullQuery(stmt)}, nil 250 case *sqlparser.CallProc: 251 plan, err = &Plan{PlanID: PlanCallProc, FullQuery: GenerateFullQuery(stmt)}, nil 252 default: 253 return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "invalid SQL") 254 } 255 if err != nil { 256 return nil, err 257 } 258 plan.Permissions = BuildPermissions(statement) 259 return plan, nil 260 } 261 262 // BuildStreaming builds a streaming plan based on the schema. 263 func BuildStreaming(sql string, tables map[string]*schema.Table) (*Plan, error) { 264 statement, err := sqlparser.Parse(sql) 265 if err != nil { 266 return nil, err 267 } 268 269 plan := &Plan{ 270 PlanID: PlanSelectStream, 271 FullQuery: GenerateFullQuery(statement), 272 Permissions: BuildPermissions(statement), 273 } 274 275 switch stmt := statement.(type) { 276 case *sqlparser.Select: 277 if hasLockFunc(stmt) { 278 plan.NeedsReservedConn = true 279 } 280 plan.Table, plan.AllTables = lookupTables(stmt.From, tables) 281 case *sqlparser.OtherRead, *sqlparser.Show, *sqlparser.Union, *sqlparser.CallProc, sqlparser.Explain: 282 // pass 283 default: 284 return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s not allowed for streaming", sqlparser.ASTToStatementType(statement)) 285 } 286 287 return plan, nil 288 } 289 290 // BuildMessageStreaming builds a plan for message streaming. 291 func BuildMessageStreaming(name string, tables map[string]*schema.Table) (*Plan, error) { 292 plan := &Plan{ 293 PlanID: PlanMessageStream, 294 Table: tables[name], 295 } 296 if plan.Table == nil { 297 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "table %s not found in schema", name) 298 } 299 if plan.Table.Type != schema.Message { 300 return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "'%s' is not a message table", name) 301 } 302 plan.Permissions = []Permission{{ 303 TableName: plan.Table.Name.String(), 304 Role: tableacl.WRITER, 305 }} 306 return plan, nil 307 } 308 309 // hasLockFunc looks for get_lock function in the select query. 310 // If it is present then it returns true otherwise false 311 func hasLockFunc(sel *sqlparser.Select) bool { 312 var found bool 313 _ = sqlparser.Walk(func(in sqlparser.SQLNode) (bool, error) { 314 lFunc, isLFunc := in.(*sqlparser.LockingFunc) 315 if !isLFunc { 316 return true, nil 317 } 318 if lFunc.Type == sqlparser.GetLock { 319 found = true 320 return false, nil 321 } 322 return true, nil 323 }, sel.SelectExprs) 324 return found 325 } 326 327 // BuildSettingQuery builds a query for system settings. 328 func BuildSettingQuery(settings []string) (query string, resetQuery string, err error) { 329 if len(settings) == 0 { 330 return "", "", vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG]: plan called for empty system settings") 331 } 332 var setExprs sqlparser.SetExprs 333 var resetSetExprs sqlparser.SetExprs 334 lDefault := sqlparser.NewStrLiteral("default") 335 for _, setting := range settings { 336 stmt, err := sqlparser.Parse(setting) 337 if err != nil { 338 return "", "", vterrors.Wrapf(err, "[BUG]: failed to parse system setting: %s", setting) 339 } 340 set, ok := stmt.(*sqlparser.Set) 341 if !ok { 342 return "", "", vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG]: invalid set statement: %s", setting) 343 } 344 setExprs = append(setExprs, set.Exprs...) 345 for _, sExpr := range set.Exprs { 346 sysVar := sExpr.Var 347 if sysVar.Scope != sqlparser.SessionScope { 348 return "", "", vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG]: session scope expected, got: %s", sysVar.Scope.ToString()) 349 } 350 resetSetExprs = append(resetSetExprs, &sqlparser.SetExpr{Var: sysVar, Expr: lDefault}) 351 } 352 } 353 return sqlparser.String(&sqlparser.Set{Exprs: setExprs}), sqlparser.String(&sqlparser.Set{Exprs: resetSetExprs}), nil 354 }