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  }