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  }