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

     1  /*
     2  Copyright 2020 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/collations"
    23  
    24  	"vitess.io/vitess/go/vt/vterrors"
    25  
    26  	"vitess.io/vitess/go/sqltypes"
    27  	"vitess.io/vitess/go/vt/sqlparser"
    28  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    29  )
    30  
    31  type notImplementedSchemaInfoConverter struct{}
    32  
    33  func (f *notImplementedSchemaInfoConverter) ColumnLookup(*sqlparser.ColName) (int, error) {
    34  	return 0, vterrors.VT12001("comparing table schema name with a column name")
    35  }
    36  
    37  func (f *notImplementedSchemaInfoConverter) CollationForExpr(sqlparser.Expr) collations.ID {
    38  	return collations.Unknown
    39  }
    40  
    41  func (f *notImplementedSchemaInfoConverter) DefaultCollation() collations.ID {
    42  	return collations.Default()
    43  }
    44  
    45  func (pb *primitiveBuilder) findSysInfoRoutingPredicates(expr sqlparser.Expr, rut *route, reservedVars *sqlparser.ReservedVars) error {
    46  	isTableSchema, bvName, out, err := extractInfoSchemaRoutingPredicate(expr, reservedVars)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	if out == nil {
    51  		// we didn't find a predicate to use for routing, so we just exit early
    52  		return nil
    53  	}
    54  
    55  	if isTableSchema {
    56  		rut.eroute.SysTableTableSchema = append(rut.eroute.SysTableTableSchema, out)
    57  	} else {
    58  		if rut.eroute.SysTableTableName == nil {
    59  			rut.eroute.SysTableTableName = map[string]evalengine.Expr{}
    60  		}
    61  		rut.eroute.SysTableTableName[bvName] = out
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  func findOtherComparator(cmp *sqlparser.ComparisonExpr) (bool, sqlparser.Expr, sqlparser.Expr, func(arg sqlparser.Argument)) {
    68  	if schema, table := isTableSchemaOrName(cmp.Left); schema || table {
    69  		return schema, cmp.Left, cmp.Right, func(arg sqlparser.Argument) {
    70  			cmp.Right = arg
    71  		}
    72  	}
    73  	if schema, table := isTableSchemaOrName(cmp.Right); schema || table {
    74  		return schema, cmp.Right, cmp.Left, func(arg sqlparser.Argument) {
    75  			cmp.Left = arg
    76  		}
    77  	}
    78  
    79  	return false, nil, nil, nil
    80  }
    81  
    82  func isTableSchemaOrName(e sqlparser.Expr) (isTableSchema bool, isTableName bool) {
    83  	col, ok := e.(*sqlparser.ColName)
    84  	if !ok {
    85  		return false, false
    86  	}
    87  	return isDbNameCol(col), isTableNameCol(col)
    88  }
    89  
    90  var schemaColumns = map[string]any{
    91  	"table_schema":            nil,
    92  	"constraint_schema":       nil,
    93  	"schema_name":             nil,
    94  	"routine_schema":          nil,
    95  	"specific_schema":         nil,
    96  	"event_schema":            nil,
    97  	"referenced_table_schema": nil,
    98  	"index_schema":            nil,
    99  	"trigger_schema":          nil,
   100  	"event_object_schema":     nil,
   101  }
   102  
   103  func isDbNameCol(col *sqlparser.ColName) bool {
   104  	_, found := schemaColumns[col.Name.Lowered()]
   105  	return found
   106  }
   107  
   108  func isTableNameCol(col *sqlparser.ColName) bool {
   109  	return col.Name.EqualString("table_name") || col.Name.EqualString("referenced_table_name")
   110  }
   111  
   112  func extractInfoSchemaRoutingPredicate(
   113  	in sqlparser.Expr,
   114  	reservedVars *sqlparser.ReservedVars,
   115  ) (isSchemaName bool, name string, evalExpr evalengine.Expr, err error) {
   116  	cmp, ok := in.(*sqlparser.ComparisonExpr)
   117  	if !ok || cmp.Operator != sqlparser.EqualOp {
   118  		return
   119  	}
   120  
   121  	isSchemaName, col, other, replaceOther := findOtherComparator(cmp)
   122  	if col == nil || !shouldRewrite(other) {
   123  		return
   124  	}
   125  
   126  	evalExpr, err = evalengine.Translate(other, &notImplementedSchemaInfoConverter{})
   127  	if err != nil {
   128  		if strings.Contains(err.Error(), evalengine.ErrTranslateExprNotSupported) {
   129  			// This just means we can't rewrite this particular expression,
   130  			// not that we have to exit altogether
   131  			err = nil
   132  			return
   133  		}
   134  		return false, "", nil, err
   135  	}
   136  
   137  	if isSchemaName {
   138  		name = sqltypes.BvSchemaName
   139  	} else {
   140  		name = reservedVars.ReserveColName(col.(*sqlparser.ColName))
   141  	}
   142  	replaceOther(sqlparser.NewArgument(name))
   143  	return isSchemaName, name, evalExpr, nil
   144  }
   145  
   146  func shouldRewrite(e sqlparser.Expr) bool {
   147  	switch node := e.(type) {
   148  	case *sqlparser.FuncExpr:
   149  		// we should not rewrite database() calls against information_schema
   150  		return !(node.Name.EqualString("database") || node.Name.EqualString("schema"))
   151  	}
   152  	return true
   153  }