github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/serial.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 sql 12 13 import ( 14 "context" 15 "fmt" 16 17 "github.com/cockroachdb/cockroach/pkg/server/telemetry" 18 "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 20 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 21 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 22 "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" 23 "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" 24 "github.com/cockroachdb/cockroach/pkg/sql/types" 25 "github.com/cockroachdb/cockroach/pkg/util/log" 26 "github.com/cockroachdb/errors" 27 ) 28 29 // uniqueRowIDExpr is used as default expression when 30 // SessionNormalizationMode is SerialUsesRowID. 31 var uniqueRowIDExpr = &tree.FuncExpr{Func: tree.WrapFunction("unique_rowid")} 32 33 // realSequenceOpts (nil) is used when SessionNormalizationMode is 34 // SerialUsesSQLSequences. 35 var realSequenceOpts tree.SequenceOptions 36 37 // virtualSequenceOpts is used when SessionNormalizationMode is 38 // SerialUsesVirtualSequences. 39 var virtualSequenceOpts = tree.SequenceOptions{ 40 tree.SequenceOption{Name: tree.SeqOptVirtual}, 41 } 42 43 // processSerialInColumnDef analyzes a column definition and determines 44 // whether to use a sequence if the requested type is SERIAL-like. 45 // If a sequence must be created, it returns an TableName to use 46 // to create the new sequence and the DatabaseDescriptor of the 47 // parent database where it should be created. 48 // The ColumnTableDef is not mutated in-place; instead a new one is returned. 49 func (p *planner) processSerialInColumnDef( 50 ctx context.Context, d *tree.ColumnTableDef, tableName *TableName, 51 ) (*tree.ColumnTableDef, *DatabaseDescriptor, *TableName, tree.SequenceOptions, error) { 52 if !d.IsSerial { 53 // Column is not SERIAL: nothing to do. 54 return d, nil, nil, nil, nil 55 } 56 57 if err := assertValidSerialColumnDef(d, tableName); err != nil { 58 return nil, nil, nil, nil, err 59 } 60 61 newSpec := *d 62 63 // Make the column non-nullable in all cases. PostgreSQL requires 64 // this. 65 newSpec.Nullable.Nullability = tree.NotNull 66 67 serialNormalizationMode := p.SessionData().SerialNormalizationMode 68 69 // Find the integer type that corresponds to the specification. 70 switch serialNormalizationMode { 71 case sessiondata.SerialUsesRowID, sessiondata.SerialUsesVirtualSequences: 72 // If unique_rowid() or virtual sequences are requested, we have 73 // no choice but to use the full-width integer type, no matter 74 // which serial size was requested, otherwise the values will not fit. 75 // 76 // TODO(bob): Follow up with https://github.com/cockroachdb/cockroach/issues/32534 77 // when the default is inverted to determine if we should also 78 // switch this behavior around. 79 newSpec.Type = types.Int 80 81 case sessiondata.SerialUsesSQLSequences: 82 // With real sequences we can use the requested type as-is. 83 84 default: 85 return nil, nil, nil, nil, 86 errors.AssertionFailedf("unknown serial normalization mode: %s", serialNormalizationMode) 87 } 88 89 // Clear the IsSerial bit now that it's been remapped. 90 newSpec.IsSerial = false 91 92 defType, err := tree.ResolveType(ctx, d.Type, p.semaCtx.GetTypeResolver()) 93 if err != nil { 94 return nil, nil, nil, nil, err 95 } 96 telemetry.Inc(sqltelemetry.SerialColumnNormalizationCounter( 97 defType.Name(), serialNormalizationMode.String())) 98 99 if serialNormalizationMode == sessiondata.SerialUsesRowID { 100 // We're not constructing a sequence for this SERIAL column. 101 // Use the "old school" CockroachDB default. 102 newSpec.DefaultExpr.Expr = uniqueRowIDExpr 103 return &newSpec, nil, nil, nil, nil 104 } 105 106 log.VEventf(ctx, 2, "creating sequence for new column %q of %q", d, tableName) 107 108 // We want a sequence; for this we need to generate a new sequence name. 109 // The constraint on the name is that an object of this name must not exist already. 110 seqName := tree.NewUnqualifiedTableName( 111 tree.Name(tableName.Table() + "_" + string(d.Name) + "_seq")) 112 113 // The first step in the search is to prepare the seqName to fill in 114 // the catalog/schema parent. This is what ResolveUncachedDatabase does. 115 // 116 // Here and below we skip the cache because name resolution using 117 // the cache does not work (well) if the txn retries and the 118 // descriptor was written already in an early txn attempt. 119 un := seqName.ToUnresolvedObjectName() 120 dbDesc, prefix, err := p.ResolveUncachedDatabase(ctx, un) 121 if err != nil { 122 return nil, nil, nil, nil, err 123 } 124 seqName.ObjectNamePrefix = prefix 125 126 // Now skip over all names that are already taken. 127 nameBase := seqName.ObjectName 128 for i := 0; ; i++ { 129 if i > 0 { 130 seqName.ObjectName = tree.Name(fmt.Sprintf("%s%d", nameBase, i)) 131 } 132 res, err := p.ResolveUncachedTableDescriptor(ctx, seqName, false /*required*/, resolver.ResolveAnyDescType) 133 if err != nil { 134 return nil, nil, nil, nil, err 135 } 136 if res == nil { 137 break 138 } 139 } 140 141 defaultExpr := &tree.FuncExpr{ 142 Func: tree.WrapFunction("nextval"), 143 Exprs: tree.Exprs{tree.NewStrVal(seqName.String())}, 144 } 145 146 seqType := "" 147 seqOpts := realSequenceOpts 148 if serialNormalizationMode == sessiondata.SerialUsesVirtualSequences { 149 seqType = "virtual " 150 seqOpts = virtualSequenceOpts 151 } 152 log.VEventf(ctx, 2, "new column %q of %q will have %s sequence name %q and default %q", 153 d, tableName, seqType, seqName, defaultExpr) 154 155 newSpec.DefaultExpr.Expr = defaultExpr 156 157 return &newSpec, dbDesc, seqName, seqOpts, nil 158 } 159 160 // SimplifySerialInColumnDefWithRowID analyzes a column definition and 161 // simplifies any use of SERIAL as if SerialNormalizationMode was set 162 // to SerialUsesRowID. No sequence needs to be created. 163 // 164 // This is currently used by bulk I/O import statements which do not 165 // (yet?) support customization of the SERIAL behavior. 166 func SimplifySerialInColumnDefWithRowID( 167 ctx context.Context, d *tree.ColumnTableDef, tableName *TableName, 168 ) error { 169 if !d.IsSerial { 170 // Column is not SERIAL: nothing to do. 171 return nil 172 } 173 174 if err := assertValidSerialColumnDef(d, tableName); err != nil { 175 return err 176 } 177 178 // Make the column non-nullable in all cases. PostgreSQL requires 179 // this. 180 d.Nullable.Nullability = tree.NotNull 181 182 // We're not constructing a sequence for this SERIAL column. 183 // Use the "old school" CockroachDB default. 184 d.Type = types.Int 185 d.DefaultExpr.Expr = uniqueRowIDExpr 186 187 // Clear the IsSerial bit now that it's been remapped. 188 d.IsSerial = false 189 190 return nil 191 } 192 193 func assertValidSerialColumnDef(d *tree.ColumnTableDef, tableName *TableName) error { 194 if d.HasDefaultExpr() { 195 // SERIAL implies a new default expression, we can't have one to 196 // start with. This is the error produced by pg in such case. 197 return pgerror.Newf(pgcode.Syntax, 198 "multiple default values specified for column %q of table %q", 199 tree.ErrString(&d.Name), tree.ErrString(tableName)) 200 } 201 202 if d.Nullable.Nullability == tree.Null { 203 // SERIAL implies a non-NULL column, we can't accept a nullability 204 // spec. This is the error produced by pg in such case. 205 return pgerror.Newf(pgcode.Syntax, 206 "conflicting NULL/NOT NULL declarations for column %q of table %q", 207 tree.ErrString(&d.Name), tree.ErrString(tableName)) 208 } 209 210 if d.Computed.Expr != nil { 211 // SERIAL cannot be a computed column. 212 return pgerror.Newf(pgcode.Syntax, 213 "SERIAL column %q of table %q cannot be computed", 214 tree.ErrString(&d.Name), tree.ErrString(tableName)) 215 } 216 217 return nil 218 }