vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/planbuilder/builder.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 "strings" 21 22 "vitess.io/vitess/go/mysql" 23 "vitess.io/vitess/go/vt/vtgate/evalengine" 24 "vitess.io/vitess/go/vt/vtgate/semantics" 25 26 "vitess.io/vitess/go/vt/sqlparser" 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 func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan *Plan, err error) { 34 plan = &Plan{ 35 PlanID: PlanSelect, 36 FullQuery: GenerateLimitQuery(sel), 37 } 38 plan.Table, plan.AllTables = lookupTables(sel.From, tables) 39 40 if sel.Where != nil { 41 comp, ok := sel.Where.Expr.(*sqlparser.ComparisonExpr) 42 if ok && comp.IsImpossible() { 43 plan.PlanID = PlanSelectImpossible 44 return plan, nil 45 } 46 } 47 48 // Check if it's a NEXT VALUE statement. 49 if nextVal, ok := sel.SelectExprs[0].(*sqlparser.Nextval); ok { 50 if plan.Table == nil || plan.Table.Type != schema.Sequence { 51 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s is not a sequence", sqlparser.ToString(sel.From)) 52 } 53 plan.PlanID = PlanNextval 54 v, err := evalengine.Translate(nextVal.Expr, semantics.EmptySemTable()) 55 if err != nil { 56 return nil, err 57 } 58 plan.NextCount = v 59 plan.FullQuery = nil 60 } 61 62 if hasLockFunc(sel) { 63 plan.PlanID = PlanSelectLockFunc 64 plan.NeedsReservedConn = true 65 } 66 return plan, nil 67 } 68 69 // analyzeUpdate code is almost identical to analyzeDelete. 70 func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan *Plan, err error) { 71 plan = &Plan{ 72 PlanID: PlanUpdate, 73 } 74 plan.Table, plan.AllTables = lookupTables(upd.TableExprs, tables) 75 76 // Store the WHERE clause as string for the hot row protection (txserializer). 77 if upd.Where != nil { 78 buf := sqlparser.NewTrackedBuffer(nil) 79 buf.Myprintf("%v", upd.Where) 80 plan.WhereClause = buf.ParsedQuery() 81 } 82 83 // Situations when we pass-through: 84 // PassthroughDMLs flag is set. 85 // plan.Table==nil: it's likely a multi-table statement. MySQL doesn't allow limit clauses for multi-table dmls. 86 // If there's an explicit Limit. 87 if PassthroughDMLs || plan.Table == nil || upd.Limit != nil { 88 plan.FullQuery = GenerateFullQuery(upd) 89 return plan, nil 90 } 91 92 plan.PlanID = PlanUpdateLimit 93 upd.Limit = execLimit 94 plan.FullQuery = GenerateFullQuery(upd) 95 upd.Limit = nil 96 return plan, nil 97 } 98 99 // analyzeDelete code is almost identical to analyzeUpdate. 100 func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan *Plan, err error) { 101 plan = &Plan{ 102 PlanID: PlanDelete, 103 } 104 plan.Table, plan.AllTables = lookupTables(del.TableExprs, tables) 105 106 if del.Where != nil { 107 buf := sqlparser.NewTrackedBuffer(nil) 108 buf.Myprintf("%v", del.Where) 109 plan.WhereClause = buf.ParsedQuery() 110 } 111 112 if PassthroughDMLs || plan.Table == nil || del.Limit != nil { 113 plan.FullQuery = GenerateFullQuery(del) 114 return plan, nil 115 } 116 plan.PlanID = PlanDeleteLimit 117 del.Limit = execLimit 118 plan.FullQuery = GenerateFullQuery(del) 119 del.Limit = nil 120 return plan, nil 121 } 122 123 func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan *Plan, err error) { 124 plan = &Plan{ 125 PlanID: PlanInsert, 126 FullQuery: GenerateFullQuery(ins), 127 } 128 129 tableName := sqlparser.GetTableName(ins.Table) 130 plan.Table = tables[tableName.String()] 131 return plan, nil 132 } 133 134 func analyzeShow(show *sqlparser.Show, dbName string) (plan *Plan, err error) { 135 switch showInternal := show.Internal.(type) { 136 case *sqlparser.ShowBasic: 137 if showInternal.Command == sqlparser.Table { 138 // rewrite WHERE clause if it exists 139 // `where Tables_in_Keyspace` => `where Tables_in_DbName` 140 if showInternal.Filter != nil { 141 showTableRewrite(showInternal, dbName) 142 } 143 } 144 return &Plan{ 145 PlanID: PlanShow, 146 FullQuery: GenerateFullQuery(show), 147 }, nil 148 case *sqlparser.ShowCreate: 149 if showInternal.Command == sqlparser.CreateDb && !sqlparser.SystemSchema(showInternal.Op.Name.String()) { 150 showInternal.Op.Name = sqlparser.NewIdentifierCS(dbName) 151 } 152 return &Plan{ 153 PlanID: PlanShow, 154 FullQuery: GenerateFullQuery(show), 155 }, nil 156 } 157 return &Plan{PlanID: PlanOtherRead}, nil 158 } 159 160 func showTableRewrite(show *sqlparser.ShowBasic, dbName string) { 161 filter := show.Filter.Filter 162 if filter == nil { 163 return 164 } 165 _ = sqlparser.SafeRewrite(filter, nil, func(cursor *sqlparser.Cursor) bool { 166 switch n := cursor.Node().(type) { 167 case *sqlparser.ColName: 168 if n.Qualifier.IsEmpty() && strings.HasPrefix(n.Name.Lowered(), "tables_in_") { 169 cursor.Replace(sqlparser.NewColName("Tables_in_" + dbName)) 170 } 171 } 172 return true 173 }) 174 } 175 176 func analyzeSet(set *sqlparser.Set) (plan *Plan) { 177 return &Plan{ 178 PlanID: PlanSet, 179 FullQuery: GenerateFullQuery(set), 180 NeedsReservedConn: true, 181 } 182 } 183 184 func lookupTables(tableExprs sqlparser.TableExprs, tables map[string]*schema.Table) (singleTable *schema.Table, allTables []*schema.Table) { 185 for _, tableExpr := range tableExprs { 186 if t := lookupSingleTable(tableExpr, tables); t != nil { 187 allTables = append(allTables, t) 188 } 189 } 190 if len(allTables) == 1 { 191 singleTable = allTables[0] 192 } 193 return singleTable, allTables 194 } 195 196 func lookupSingleTable(tableExpr sqlparser.TableExpr, tables map[string]*schema.Table) *schema.Table { 197 aliased, ok := tableExpr.(*sqlparser.AliasedTableExpr) 198 if !ok { 199 return nil 200 } 201 tableName := sqlparser.GetTableName(aliased.Expr) 202 if tableName.IsEmpty() { 203 return nil 204 } 205 return tables[tableName.String()] 206 } 207 208 func analyzeDDL(stmt sqlparser.DDLStatement, viewsEnabled bool) (*Plan, error) { 209 switch stmt.(type) { 210 case *sqlparser.AlterView, *sqlparser.DropView, *sqlparser.CreateView: 211 if viewsEnabled { 212 return analyzeViewsDDL(stmt) 213 } 214 } 215 // DDLs and some other statements below don't get fully parsed. 216 // We have to use the original query at the time of execution. 217 // We are in the process of changing this 218 var fullQuery *sqlparser.ParsedQuery 219 // If the query is fully parsed, then use the ast and store the fullQuery 220 if stmt.IsFullyParsed() { 221 fullQuery = GenerateFullQuery(stmt) 222 } 223 return &Plan{PlanID: PlanDDL, FullQuery: fullQuery, FullStmt: stmt, NeedsReservedConn: stmt.IsTemporary()}, nil 224 } 225 226 func analyzeViewsDDL(stmt sqlparser.DDLStatement) (*Plan, error) { 227 switch viewDDL := stmt.(type) { 228 case *sqlparser.CreateView: 229 query := mysql.InsertIntoViewsTable 230 if viewDDL.IsReplace { 231 query = mysql.ReplaceIntoViewsTable 232 } 233 insert, err := sqlparser.Parse(query) 234 if err != nil { 235 return nil, err 236 } 237 return &Plan{PlanID: PlanViewDDL, FullQuery: GenerateFullQuery(insert), FullStmt: viewDDL}, nil 238 case *sqlparser.AlterView: 239 update, err := sqlparser.Parse(mysql.UpdateViewsTable) 240 if err != nil { 241 return nil, err 242 } 243 return &Plan{PlanID: PlanViewDDL, FullQuery: GenerateFullQuery(update), FullStmt: viewDDL}, nil 244 case *sqlparser.DropView: 245 del, err := sqlparser.Parse(mysql.DeleteFromViewsTable) 246 if err != nil { 247 return nil, err 248 } 249 return &Plan{PlanID: PlanViewDDL, FullQuery: GenerateFullQuery(del), FullStmt: viewDDL}, nil 250 } 251 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unknown view DDL type: %T", stmt) 252 }