vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/operators/system_tables.go (about)

     1  /*
     2  Copyright 2022 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 operators
    18  
    19  import (
    20  	"strings"
    21  
    22  	"vitess.io/vitess/go/mysql/collations"
    23  	"vitess.io/vitess/go/sqltypes"
    24  	"vitess.io/vitess/go/vt/sqlparser"
    25  	"vitess.io/vitess/go/vt/vterrors"
    26  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    27  )
    28  
    29  func (r *Route) findSysInfoRoutingPredicatesGen4(predicates []sqlparser.Expr, reservedVars *sqlparser.ReservedVars) error {
    30  	for _, pred := range predicates {
    31  		isTableSchema, bvName, out, err := extractInfoSchemaRoutingPredicate(pred, reservedVars)
    32  		if err != nil {
    33  			return err
    34  		}
    35  		if out == nil {
    36  			// we didn't find a predicate to use for routing, continue to look for next predicate
    37  			continue
    38  		}
    39  
    40  		if r.SysTableTableName == nil {
    41  			r.SysTableTableName = map[string]evalengine.Expr{}
    42  		}
    43  
    44  		if isTableSchema {
    45  			r.SysTableTableSchema = append(r.SysTableTableSchema, out)
    46  		} else {
    47  			r.SysTableTableName[bvName] = out
    48  		}
    49  	}
    50  	return nil
    51  }
    52  
    53  func extractInfoSchemaRoutingPredicate(
    54  	in sqlparser.Expr,
    55  	reservedVars *sqlparser.ReservedVars,
    56  ) (isSchemaName bool, name string, evalExpr evalengine.Expr, err error) {
    57  	cmp, ok := in.(*sqlparser.ComparisonExpr)
    58  	if !ok || cmp.Operator != sqlparser.EqualOp {
    59  		return
    60  	}
    61  
    62  	isSchemaName, col := isTableOrSchemaRouteable(cmp)
    63  	if col == nil || !shouldRewrite(cmp.Right) {
    64  		return
    65  	}
    66  
    67  	evalExpr, err = evalengine.Translate(cmp.Right, &notImplementedSchemaInfoConverter{})
    68  	if err != nil {
    69  		if strings.Contains(err.Error(), evalengine.ErrTranslateExprNotSupported) {
    70  			// This just means we can't rewrite this particular expression,
    71  			// not that we have to exit altogether
    72  			err = nil
    73  			return
    74  		}
    75  		return
    76  	}
    77  	if isSchemaName {
    78  		name = sqltypes.BvSchemaName
    79  	} else {
    80  		name = reservedVars.ReserveColName(col)
    81  	}
    82  	cmp.Right = sqlparser.NewArgument(name)
    83  	return isSchemaName, name, evalExpr, nil
    84  }
    85  
    86  // isTableOrSchemaRouteable searches for a comparison where one side is a table or schema name column.
    87  // if it finds the correct column name being used,
    88  // it also makes sure that the LHS of the comparison contains the column, and the RHS the value sought after
    89  func isTableOrSchemaRouteable(cmp *sqlparser.ComparisonExpr) (
    90  	isSchema bool, // tells if we are dealing with a table or a schema name comparator
    91  	col *sqlparser.ColName, // which is the colName we are comparing against
    92  ) {
    93  	if col, schema, table := isTableSchemaOrName(cmp.Left); schema || table {
    94  		return schema, col
    95  	}
    96  	if col, schema, table := isTableSchemaOrName(cmp.Right); schema || table {
    97  		// to make the rest of the code easier, we shuffle these around so the ColName is always on the LHS
    98  		cmp.Right, cmp.Left = cmp.Left, cmp.Right
    99  		return schema, col
   100  	}
   101  
   102  	return false, nil
   103  }
   104  
   105  func shouldRewrite(e sqlparser.Expr) bool {
   106  	switch node := e.(type) {
   107  	case *sqlparser.FuncExpr:
   108  		// we should not rewrite database() calls against information_schema
   109  		return !(node.Name.EqualString("database") || node.Name.EqualString("schema"))
   110  	}
   111  	return true
   112  }
   113  
   114  func isTableSchemaOrName(e sqlparser.Expr) (col *sqlparser.ColName, isTableSchema bool, isTableName bool) {
   115  	col, ok := e.(*sqlparser.ColName)
   116  	if !ok {
   117  		return nil, false, false
   118  	}
   119  	return col, isDbNameCol(col), isTableNameCol(col)
   120  }
   121  
   122  func isDbNameCol(col *sqlparser.ColName) bool {
   123  	return col.Name.EqualString("table_schema") || col.Name.EqualString("constraint_schema") || col.Name.EqualString("schema_name") || col.Name.EqualString("routine_schema")
   124  }
   125  
   126  func isTableNameCol(col *sqlparser.ColName) bool {
   127  	return col.Name.EqualString("table_name")
   128  }
   129  
   130  type notImplementedSchemaInfoConverter struct{}
   131  
   132  func (f *notImplementedSchemaInfoConverter) ColumnLookup(*sqlparser.ColName) (int, error) {
   133  	return 0, vterrors.VT12001("comparing table schema name with a column name")
   134  }
   135  
   136  func (f *notImplementedSchemaInfoConverter) CollationForExpr(sqlparser.Expr) collations.ID {
   137  	return collations.Unknown
   138  }
   139  
   140  func (f *notImplementedSchemaInfoConverter) DefaultCollation() collations.ID {
   141  	return collations.Default()
   142  }