vitess.io/vitess@v0.16.2/go/vt/vtgate/semantics/table_collector.go (about)

     1  /*
     2  Copyright 2021 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 semantics
    18  
    19  import (
    20  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    21  	"vitess.io/vitess/go/vt/sqlparser"
    22  	"vitess.io/vitess/go/vt/vterrors"
    23  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    24  )
    25  
    26  // tableCollector is responsible for gathering information about the tables listed in the FROM clause,
    27  // and adding them to the current scope, plus keeping the global list of tables used in the query
    28  type tableCollector struct {
    29  	Tables    []TableInfo
    30  	scoper    *scoper
    31  	si        SchemaInformation
    32  	currentDb string
    33  	org       originable
    34  }
    35  
    36  func newTableCollector(scoper *scoper, si SchemaInformation, currentDb string) *tableCollector {
    37  	return &tableCollector{
    38  		scoper:    scoper,
    39  		si:        si,
    40  		currentDb: currentDb,
    41  	}
    42  }
    43  
    44  func (tc *tableCollector) up(cursor *sqlparser.Cursor) error {
    45  	node, ok := cursor.Node().(*sqlparser.AliasedTableExpr)
    46  	if !ok {
    47  		return nil
    48  	}
    49  	switch t := node.Expr.(type) {
    50  	case *sqlparser.DerivedTable:
    51  		switch sel := t.Select.(type) {
    52  		case *sqlparser.Select:
    53  			tables := tc.scoper.wScope[sel]
    54  			tableInfo := createDerivedTableForExpressions(sqlparser.GetFirstSelect(sel).SelectExprs, node.Columns, tables.tables, tc.org)
    55  			if err := tableInfo.checkForDuplicates(); err != nil {
    56  				return err
    57  			}
    58  
    59  			tableInfo.ASTNode = node
    60  			tableInfo.tableName = node.As.String()
    61  
    62  			tc.Tables = append(tc.Tables, tableInfo)
    63  			scope := tc.scoper.currentScope()
    64  			return scope.addTable(tableInfo)
    65  
    66  		case *sqlparser.Union:
    67  			firstSelect := sqlparser.GetFirstSelect(sel)
    68  			tables := tc.scoper.wScope[firstSelect]
    69  			tableInfo := createDerivedTableForExpressions(firstSelect.SelectExprs, node.Columns, tables.tables, tc.org)
    70  			if err := tableInfo.checkForDuplicates(); err != nil {
    71  				return err
    72  			}
    73  			tableInfo.ASTNode = node
    74  			tableInfo.tableName = node.As.String()
    75  
    76  			tc.Tables = append(tc.Tables, tableInfo)
    77  			scope := tc.scoper.currentScope()
    78  			return scope.addTable(tableInfo)
    79  
    80  		default:
    81  			return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] %T in a derived table", sel)
    82  		}
    83  
    84  	case sqlparser.TableName:
    85  		var tbl *vindexes.Table
    86  		var vindex vindexes.Vindex
    87  		isInfSchema := sqlparser.SystemSchema(t.Qualifier.String())
    88  		var err error
    89  		tbl, vindex, _, _, _, err = tc.si.FindTableOrVindex(t)
    90  		if err != nil && !isInfSchema {
    91  			// if we are dealing with a system table, it might not be available in the vschema, but that is OK
    92  			return err
    93  		}
    94  		if tbl == nil && vindex != nil {
    95  			tbl = newVindexTable(t.Name)
    96  		}
    97  
    98  		scope := tc.scoper.currentScope()
    99  		tableInfo := tc.createTable(t, node, tbl, isInfSchema, vindex)
   100  
   101  		tc.Tables = append(tc.Tables, tableInfo)
   102  		return scope.addTable(tableInfo)
   103  	}
   104  	return nil
   105  }
   106  
   107  func newVindexTable(t sqlparser.IdentifierCS) *vindexes.Table {
   108  	vindexCols := []vindexes.Column{
   109  		{Name: sqlparser.NewIdentifierCI("id")},
   110  		{Name: sqlparser.NewIdentifierCI("keyspace_id")},
   111  		{Name: sqlparser.NewIdentifierCI("range_start")},
   112  		{Name: sqlparser.NewIdentifierCI("range_end")},
   113  		{Name: sqlparser.NewIdentifierCI("hex_keyspace_id")},
   114  		{Name: sqlparser.NewIdentifierCI("shard")},
   115  	}
   116  
   117  	return &vindexes.Table{
   118  		Name:                    t,
   119  		Columns:                 vindexCols,
   120  		ColumnListAuthoritative: true,
   121  	}
   122  }
   123  
   124  // tabletSetFor implements the originable interface, and that is why it lives on the analyser struct.
   125  // The code lives in this file since it is only touching tableCollector data
   126  func (tc *tableCollector) tableSetFor(t *sqlparser.AliasedTableExpr) TableSet {
   127  	for i, t2 := range tc.Tables {
   128  		if t == t2.getExpr() {
   129  			return SingleTableSet(i)
   130  		}
   131  	}
   132  	panic("unknown table")
   133  }
   134  
   135  // tableInfoFor returns the table info for the table set. It should contains only single table.
   136  func (tc *tableCollector) tableInfoFor(id TableSet) (TableInfo, error) {
   137  	offset := id.TableOffset()
   138  	if offset < 0 {
   139  		return nil, ErrNotSingleTable
   140  	}
   141  	return tc.Tables[offset], nil
   142  }
   143  
   144  func (tc *tableCollector) createTable(
   145  	t sqlparser.TableName,
   146  	alias *sqlparser.AliasedTableExpr,
   147  	tbl *vindexes.Table,
   148  	isInfSchema bool,
   149  	vindex vindexes.Vindex,
   150  ) TableInfo {
   151  	table := &RealTable{
   152  		tableName:   alias.As.String(),
   153  		ASTNode:     alias,
   154  		Table:       tbl,
   155  		isInfSchema: isInfSchema,
   156  	}
   157  
   158  	if alias.As.IsEmpty() {
   159  		dbName := t.Qualifier.String()
   160  		if dbName == "" {
   161  			dbName = tc.currentDb
   162  		}
   163  
   164  		table.dbName = dbName
   165  		table.tableName = t.Name.String()
   166  	}
   167  
   168  	if vindex != nil {
   169  		return &VindexTable{
   170  			Table:  table,
   171  			Vindex: vindex,
   172  		}
   173  	}
   174  	return table
   175  }