github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/optbuilder/alter_table.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package optbuilder
    12  
    13  import (
    14  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    15  	"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
    16  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/privilege"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    24  )
    25  
    26  // buildAlterTableSplit builds an ALTER TABLE/INDEX .. SPLIT AT .. statement.
    27  func (b *Builder) buildAlterTableSplit(split *tree.Split, inScope *scope) (outScope *scope) {
    28  	flags := cat.Flags{
    29  		AvoidDescriptorCaches: true,
    30  		NoTableStats:          true,
    31  	}
    32  	index, tn, err := cat.ResolveTableIndex(b.ctx, b.catalog, flags, &split.TableOrIndex)
    33  	if err != nil {
    34  		panic(err)
    35  	}
    36  	table := index.Table()
    37  	if err := b.catalog.CheckPrivilege(b.ctx, table, privilege.INSERT); err != nil {
    38  		panic(err)
    39  	}
    40  
    41  	b.DisableMemoReuse = true
    42  
    43  	// Calculate the desired types for the input expression. It is OK if it
    44  	// returns fewer columns (the relevant prefix is used).
    45  	colNames, colTypes := getIndexColumnNamesAndTypes(index)
    46  
    47  	// We don't allow the input statement to reference outer columns, so we
    48  	// pass a "blank" scope rather than inScope.
    49  	emptyScope := b.allocScope()
    50  	inputScope := b.buildStmt(split.Rows, colTypes, emptyScope)
    51  	checkInputColumns("SPLIT AT", inputScope, colNames, colTypes, 1)
    52  
    53  	// Build the expiration scalar.
    54  	var expiration opt.ScalarExpr
    55  	if split.ExpireExpr != nil {
    56  		emptyScope.context = exprKindAlterTableSplitAt
    57  		// We need to save and restore the previous value of the field in
    58  		// semaCtx in case we are recursively called within a subquery
    59  		// context.
    60  		defer b.semaCtx.Properties.Restore(b.semaCtx.Properties)
    61  		b.semaCtx.Properties.Require(emptyScope.context.String(), tree.RejectSpecial)
    62  
    63  		texpr := emptyScope.resolveType(split.ExpireExpr, types.String)
    64  		expiration = b.buildScalar(texpr, emptyScope, nil /* outScope */, nil /* outCol */, nil /* colRefs */)
    65  	} else {
    66  		expiration = b.factory.ConstructNull(types.String)
    67  	}
    68  
    69  	outScope = inScope.push()
    70  	b.synthesizeResultColumns(outScope, sqlbase.AlterTableSplitColumns)
    71  	outScope.expr = b.factory.ConstructAlterTableSplit(
    72  		inputScope.expr.(memo.RelExpr),
    73  		expiration,
    74  		&memo.AlterTableSplitPrivate{
    75  			Table:   b.factory.Metadata().AddTable(table, &tn),
    76  			Index:   index.Ordinal(),
    77  			Columns: colsToColList(outScope.cols),
    78  			Props:   inputScope.makePhysicalProps(),
    79  		},
    80  	)
    81  	return outScope
    82  }
    83  
    84  // buildAlterTableUnsplit builds an ALTER TABLE/INDEX .. UNSPLIT AT/ALL .. statement.
    85  func (b *Builder) buildAlterTableUnsplit(unsplit *tree.Unsplit, inScope *scope) (outScope *scope) {
    86  	flags := cat.Flags{
    87  		AvoidDescriptorCaches: true,
    88  		NoTableStats:          true,
    89  	}
    90  	index, tn, err := cat.ResolveTableIndex(b.ctx, b.catalog, flags, &unsplit.TableOrIndex)
    91  	if err != nil {
    92  		panic(err)
    93  	}
    94  	table := index.Table()
    95  	if err := b.catalog.CheckPrivilege(b.ctx, table, privilege.INSERT); err != nil {
    96  		panic(err)
    97  	}
    98  
    99  	b.DisableMemoReuse = true
   100  
   101  	outScope = inScope.push()
   102  	b.synthesizeResultColumns(outScope, sqlbase.AlterTableUnsplitColumns)
   103  	private := &memo.AlterTableSplitPrivate{
   104  		Table:   b.factory.Metadata().AddTable(table, &tn),
   105  		Index:   index.Ordinal(),
   106  		Columns: colsToColList(outScope.cols),
   107  	}
   108  
   109  	if unsplit.All {
   110  		private.Props = physical.MinRequired
   111  		outScope.expr = b.factory.ConstructAlterTableUnsplitAll(private)
   112  		return outScope
   113  	}
   114  
   115  	// Calculate the desired types for the input expression. It is OK if it
   116  	// returns fewer columns (the relevant prefix is used).
   117  	colNames, colTypes := getIndexColumnNamesAndTypes(index)
   118  
   119  	// We don't allow the input statement to reference outer columns, so we
   120  	// pass a "blank" scope rather than inScope.
   121  	inputScope := b.buildStmt(unsplit.Rows, colTypes, b.allocScope())
   122  	checkInputColumns("UNSPLIT AT", inputScope, colNames, colTypes, 1)
   123  	private.Props = inputScope.makePhysicalProps()
   124  
   125  	outScope.expr = b.factory.ConstructAlterTableUnsplit(
   126  		inputScope.expr.(memo.RelExpr),
   127  		private,
   128  	)
   129  	return outScope
   130  }
   131  
   132  // buildAlterTableRelocate builds an ALTER TABLE/INDEX .. UNSPLIT AT/ALL .. statement.
   133  func (b *Builder) buildAlterTableRelocate(
   134  	relocate *tree.Relocate, inScope *scope,
   135  ) (outScope *scope) {
   136  	flags := cat.Flags{
   137  		AvoidDescriptorCaches: true,
   138  		NoTableStats:          true,
   139  	}
   140  	index, tn, err := cat.ResolveTableIndex(b.ctx, b.catalog, flags, &relocate.TableOrIndex)
   141  	if err != nil {
   142  		panic(err)
   143  	}
   144  	table := index.Table()
   145  	if err := b.catalog.CheckPrivilege(b.ctx, table, privilege.INSERT); err != nil {
   146  		panic(err)
   147  	}
   148  
   149  	b.DisableMemoReuse = true
   150  
   151  	outScope = inScope.push()
   152  	b.synthesizeResultColumns(outScope, sqlbase.AlterTableRelocateColumns)
   153  
   154  	// Calculate the desired types for the input expression. It is OK if it
   155  	// returns fewer columns (the relevant prefix is used).
   156  	colNames, colTypes := getIndexColumnNamesAndTypes(index)
   157  
   158  	// The first column is the target leaseholder or the relocation array,
   159  	// depending on variant.
   160  	cmdName := "EXPERIMENTAL_RELOCATE"
   161  	if relocate.RelocateLease {
   162  		cmdName += " LEASE"
   163  		colNames = append([]string{"target leaseholder"}, colNames...)
   164  		colTypes = append([]*types.T{types.Int}, colTypes...)
   165  	} else {
   166  		colNames = append([]string{"relocation array"}, colNames...)
   167  		colTypes = append([]*types.T{types.IntArray}, colTypes...)
   168  	}
   169  
   170  	// We don't allow the input statement to reference outer columns, so we
   171  	// pass a "blank" scope rather than inScope.
   172  	inputScope := b.buildStmt(relocate.Rows, colTypes, b.allocScope())
   173  	checkInputColumns(cmdName, inputScope, colNames, colTypes, 2)
   174  
   175  	outScope.expr = b.factory.ConstructAlterTableRelocate(
   176  		inputScope.expr.(memo.RelExpr),
   177  		&memo.AlterTableRelocatePrivate{
   178  			RelocateLease: relocate.RelocateLease,
   179  			AlterTableSplitPrivate: memo.AlterTableSplitPrivate{
   180  				Table:   b.factory.Metadata().AddTable(table, &tn),
   181  				Index:   index.Ordinal(),
   182  				Columns: colsToColList(outScope.cols),
   183  				Props:   inputScope.makePhysicalProps(),
   184  			},
   185  		},
   186  	)
   187  	return outScope
   188  }
   189  
   190  // getIndexColumnNamesAndTypes returns the names and types of the index columns.
   191  func getIndexColumnNamesAndTypes(index cat.Index) (colNames []string, colTypes []*types.T) {
   192  	colNames = make([]string, index.LaxKeyColumnCount())
   193  	colTypes = make([]*types.T, index.LaxKeyColumnCount())
   194  	for i := range colNames {
   195  		c := index.Column(i)
   196  		colNames[i] = string(c.ColName())
   197  		colTypes[i] = c.DatumType()
   198  	}
   199  	return colNames, colTypes
   200  }
   201  
   202  // checkInputColumns verifies the types of the columns in the given scope. The
   203  // input must have at least minPrefix columns, and their types must match that
   204  // prefix of colTypes.
   205  func checkInputColumns(
   206  	context string, inputScope *scope, colNames []string, colTypes []*types.T, minPrefix int,
   207  ) {
   208  	if len(inputScope.cols) < minPrefix {
   209  		if len(inputScope.cols) == 0 {
   210  			panic(pgerror.Newf(pgcode.Syntax, "no columns in %s data", context))
   211  		}
   212  		panic(pgerror.Newf(pgcode.Syntax, "less than %d columns in %s data", minPrefix, context))
   213  	}
   214  	if len(inputScope.cols) > len(colTypes) {
   215  		panic(pgerror.Newf(pgcode.Syntax, "too many columns in %s data", context))
   216  	}
   217  	for i := range inputScope.cols {
   218  		if !inputScope.cols[i].typ.Equivalent(colTypes[i]) {
   219  			panic(pgerror.Newf(
   220  				pgcode.Syntax, "%s data column %d (%s) must be of type %s, not type %s",
   221  				context, i+1, colNames[i], colTypes[i], inputScope.cols[i].typ,
   222  			))
   223  		}
   224  	}
   225  }