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  }