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 }