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

     1  // Copyright 2017 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  
    16  	"github.com/cockroachdb/cockroach/pkg/clusterversion"
    17  	"github.com/cockroachdb/cockroach/pkg/geo/geoindex"
    18  	"github.com/cockroachdb/cockroach/pkg/server/telemetry"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgnotice"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/privilege"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/schemaexpr"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
    28  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    29  	"github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented"
    30  	"github.com/cockroachdb/errors"
    31  )
    32  
    33  type createIndexNode struct {
    34  	n         *tree.CreateIndex
    35  	tableDesc *sqlbase.MutableTableDescriptor
    36  }
    37  
    38  // CreateIndex creates an index.
    39  // Privileges: CREATE on table.
    40  //   notes: postgres requires CREATE on the table.
    41  //          mysql requires INDEX on the table.
    42  func (p *planner) CreateIndex(ctx context.Context, n *tree.CreateIndex) (planNode, error) {
    43  	tableDesc, err := p.ResolveMutableTableDescriptor(
    44  		ctx, &n.Table, true /*required*/, resolver.ResolveRequireTableDesc,
    45  	)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	if err := p.CheckPrivilege(ctx, tableDesc, privilege.CREATE); err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	return &createIndexNode{tableDesc: tableDesc, n: n}, nil
    55  }
    56  
    57  // setupFamilyAndConstraintForShard adds a newly-created shard column into its appropriate
    58  // family (see comment above GetColumnFamilyForShard) and adds a check constraint ensuring
    59  // that the shard column's value is within [0..ShardBuckets-1]. This method is called when
    60  // a `CREATE INDEX` statement is issued for the creation of a sharded index that *does
    61  // not* re-use a pre-existing shard column.
    62  func (p *planner) setupFamilyAndConstraintForShard(
    63  	ctx context.Context,
    64  	tableDesc *MutableTableDescriptor,
    65  	shardCol *sqlbase.ColumnDescriptor,
    66  	idxColumns []string,
    67  	buckets int32,
    68  ) error {
    69  	family := sqlbase.GetColumnFamilyForShard(tableDesc, idxColumns)
    70  	if family == "" {
    71  		return errors.AssertionFailedf("could not find column family for the first column in the index column set")
    72  	}
    73  	// Assign shard column to the family of the first column in its index set, and do it
    74  	// before `AllocateIDs()` assigns it to the primary column family.
    75  	if err := tableDesc.AddColumnToFamilyMaybeCreate(shardCol.Name, family, false, false); err != nil {
    76  		return err
    77  	}
    78  	// Assign an ID to the newly-added shard column, which is needed for the creation
    79  	// of a valid check constraint.
    80  	if err := tableDesc.AllocateIDs(); err != nil {
    81  		return err
    82  	}
    83  
    84  	ckDef, err := makeShardCheckConstraintDef(tableDesc, int(buckets), shardCol)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	info, err := tableDesc.GetConstraintInfo(ctx, nil, p.ExecCfg().Codec)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	inuseNames := make(map[string]struct{}, len(info))
    94  	for k := range info {
    95  		inuseNames[k] = struct{}{}
    96  	}
    97  
    98  	ckBuilder := schemaexpr.NewCheckConstraintBuilder(ctx, p.tableName, tableDesc, &p.semaCtx)
    99  	ckName, err := ckBuilder.DefaultName(ckDef.Expr)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	// Avoid creating duplicate check constraints.
   105  	if _, ok := inuseNames[ckName]; !ok {
   106  		ck, err := ckBuilder.Build(ckDef)
   107  		if err != nil {
   108  			return err
   109  		}
   110  		ck.Validity = sqlbase.ConstraintValidity_Validating
   111  		tableDesc.AddCheckMutation(ck, sqlbase.DescriptorMutation_ADD)
   112  	}
   113  	return nil
   114  }
   115  
   116  // MakeIndexDescriptor creates an index descriptor from a CreateIndex node and optionally
   117  // adds a hidden computed shard column (along with its check constraint) in case the index
   118  // is hash sharded. Note that `tableDesc` will be modified when this method is called for
   119  // a hash sharded index.
   120  func MakeIndexDescriptor(
   121  	params runParams, n *tree.CreateIndex, tableDesc *sqlbase.MutableTableDescriptor,
   122  ) (*sqlbase.IndexDescriptor, error) {
   123  	// Ensure that the columns we want to index exist before trying to create the
   124  	// index.
   125  	if err := validateIndexColumnsExist(tableDesc, n.Columns); err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	// Ensure that the index name does not exist before trying to create the index.
   130  	if err := tableDesc.ValidateIndexNameIsUnique(string(n.Name)); err != nil {
   131  		return nil, err
   132  	}
   133  	indexDesc := sqlbase.IndexDescriptor{
   134  		Name:              string(n.Name),
   135  		Unique:            n.Unique,
   136  		StoreColumnNames:  n.Storing.ToStrings(),
   137  		CreatedExplicitly: true,
   138  	}
   139  
   140  	if n.Inverted {
   141  		if n.Interleave != nil {
   142  			return nil, pgerror.New(pgcode.InvalidSQLStatementName, "inverted indexes don't support interleaved tables")
   143  		}
   144  
   145  		if n.PartitionBy != nil {
   146  			return nil, pgerror.New(pgcode.InvalidSQLStatementName, "inverted indexes don't support partitioning")
   147  		}
   148  
   149  		if n.Sharded != nil {
   150  			return nil, pgerror.New(pgcode.InvalidSQLStatementName, "inverted indexes don't support hash sharding")
   151  		}
   152  
   153  		if len(indexDesc.StoreColumnNames) > 0 {
   154  			return nil, pgerror.New(pgcode.InvalidSQLStatementName, "inverted indexes don't support stored columns")
   155  		}
   156  
   157  		if n.Unique {
   158  			return nil, pgerror.New(pgcode.InvalidSQLStatementName, "inverted indexes can't be unique")
   159  		}
   160  		indexDesc.Type = sqlbase.IndexDescriptor_INVERTED
   161  		columnDesc, _, err := tableDesc.FindColumnByName(n.Columns[0].Column)
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  		if columnDesc.Type.InternalType.Family == types.GeometryFamily {
   166  			indexDesc.GeoConfig = *geoindex.DefaultGeometryIndexConfig()
   167  			telemetry.Inc(sqltelemetry.GeometryInvertedIndexCounter)
   168  		}
   169  		if columnDesc.Type.InternalType.Family == types.GeographyFamily {
   170  			indexDesc.GeoConfig = *geoindex.DefaultGeographyIndexConfig()
   171  			telemetry.Inc(sqltelemetry.GeographyInvertedIndexCounter)
   172  		}
   173  		telemetry.Inc(sqltelemetry.InvertedIndexCounter)
   174  	}
   175  
   176  	if n.Sharded != nil {
   177  		if n.PartitionBy != nil {
   178  			return nil, pgerror.New(pgcode.FeatureNotSupported, "sharded indexes don't support partitioning")
   179  		}
   180  		if n.Interleave != nil {
   181  			return nil, pgerror.New(pgcode.FeatureNotSupported, "interleaved indexes cannot also be hash sharded")
   182  		}
   183  		shardCol, newColumn, err := setupShardedIndex(
   184  			params.ctx,
   185  			params.EvalContext(),
   186  			&params.p.semaCtx,
   187  			params.SessionData().HashShardedIndexesEnabled,
   188  			&n.Columns,
   189  			n.Sharded.ShardBuckets,
   190  			tableDesc,
   191  			&indexDesc,
   192  			false /* isNewTable */)
   193  		if err != nil {
   194  			return nil, err
   195  		}
   196  		if newColumn {
   197  			if err := params.p.setupFamilyAndConstraintForShard(params.ctx, tableDesc, shardCol,
   198  				indexDesc.Sharded.ColumnNames, indexDesc.Sharded.ShardBuckets); err != nil {
   199  				return nil, err
   200  			}
   201  		}
   202  		telemetry.Inc(sqltelemetry.HashShardedIndexCounter)
   203  	}
   204  
   205  	if n.Predicate != nil {
   206  		// TODO(mgartner): remove this once partial indexes are fully supported.
   207  		if !params.SessionData().PartialIndexes {
   208  			return nil, unimplemented.NewWithIssue(9683, "partial indexes are not supported")
   209  		}
   210  
   211  		idxValidator := schemaexpr.NewIndexPredicateValidator(params.ctx, n.Table, tableDesc, &params.p.semaCtx)
   212  		expr, err := idxValidator.Validate(n.Predicate)
   213  		if err != nil {
   214  			return nil, err
   215  		}
   216  
   217  		// Store the serialized predicate expression in the IndexDescriptor.
   218  		indexDesc.Predicate = tree.Serialize(expr)
   219  	}
   220  
   221  	if err := indexDesc.FillColumns(n.Columns); err != nil {
   222  		return nil, err
   223  	}
   224  	return &indexDesc, nil
   225  }
   226  
   227  // validateIndexColumnsExists validates that the columns for an index exist
   228  // in the table and are not being dropped prior to attempting to add the index.
   229  func validateIndexColumnsExist(
   230  	desc *sqlbase.MutableTableDescriptor, columns tree.IndexElemList,
   231  ) error {
   232  	for _, column := range columns {
   233  		_, dropping, err := desc.FindColumnByName(column.Column)
   234  		if err != nil {
   235  			return err
   236  		}
   237  		if dropping {
   238  			return sqlbase.NewUndefinedColumnError(string(column.Column))
   239  		}
   240  	}
   241  	return nil
   242  }
   243  
   244  // ReadingOwnWrites implements the planNodeReadingOwnWrites interface.
   245  // This is because CREATE INDEX performs multiple KV operations on descriptors
   246  // and expects to see its own writes.
   247  func (n *createIndexNode) ReadingOwnWrites() {}
   248  
   249  var invalidClusterForShardedIndexError = pgerror.Newf(pgcode.FeatureNotSupported,
   250  	"hash sharded indexes can only be created on a cluster that has fully migrated to version 20.1")
   251  
   252  var hashShardedIndexesDisabledError = pgerror.Newf(pgcode.FeatureNotSupported,
   253  	"hash sharded indexes require the experimental_enable_hash_sharded_indexes cluster setting")
   254  
   255  func setupShardedIndex(
   256  	ctx context.Context,
   257  	evalCtx *tree.EvalContext,
   258  	semaCtx *tree.SemaContext,
   259  	shardedIndexEnabled bool,
   260  	columns *tree.IndexElemList,
   261  	bucketsExpr tree.Expr,
   262  	tableDesc *sqlbase.MutableTableDescriptor,
   263  	indexDesc *sqlbase.IndexDescriptor,
   264  	isNewTable bool,
   265  ) (shard *sqlbase.ColumnDescriptor, newColumn bool, err error) {
   266  	st := evalCtx.Settings
   267  	if !st.Version.IsActive(ctx, clusterversion.VersionHashShardedIndexes) {
   268  		return nil, false, invalidClusterForShardedIndexError
   269  	}
   270  	if !shardedIndexEnabled {
   271  		return nil, false, hashShardedIndexesDisabledError
   272  	}
   273  
   274  	colNames := make([]string, 0, len(*columns))
   275  	for _, c := range *columns {
   276  		colNames = append(colNames, string(c.Column))
   277  	}
   278  	buckets, err := sqlbase.EvalShardBucketCount(ctx, semaCtx, evalCtx, bucketsExpr)
   279  	if err != nil {
   280  		return nil, false, err
   281  	}
   282  	shardCol, newColumn, err := maybeCreateAndAddShardCol(int(buckets), tableDesc,
   283  		colNames, isNewTable)
   284  	if err != nil {
   285  		return nil, false, err
   286  	}
   287  	shardIdxElem := tree.IndexElem{
   288  		Column:    tree.Name(shardCol.Name),
   289  		Direction: tree.Ascending,
   290  	}
   291  	*columns = append(tree.IndexElemList{shardIdxElem}, *columns...)
   292  	indexDesc.Sharded = sqlbase.ShardedDescriptor{
   293  		IsSharded:    true,
   294  		Name:         shardCol.Name,
   295  		ShardBuckets: buckets,
   296  		ColumnNames:  colNames,
   297  	}
   298  	return shardCol, newColumn, nil
   299  }
   300  
   301  // maybeCreateAndAddShardCol adds a new hidden computed shard column (or its mutation) to
   302  // `desc`, if one doesn't already exist for the given index column set and number of shard
   303  // buckets.
   304  func maybeCreateAndAddShardCol(
   305  	shardBuckets int, desc *sqlbase.MutableTableDescriptor, colNames []string, isNewTable bool,
   306  ) (col *sqlbase.ColumnDescriptor, created bool, err error) {
   307  	shardCol, err := makeShardColumnDesc(colNames, shardBuckets)
   308  	if err != nil {
   309  		return nil, false, err
   310  	}
   311  	existingShardCol, dropped, err := desc.FindColumnByName(tree.Name(shardCol.Name))
   312  	if err == nil && !dropped {
   313  		// TODO(ajwerner): In what ways is existingShardCol allowed to differ from
   314  		// the newly made shardCol? Should there be some validation of
   315  		// existingShardCol?
   316  		if !existingShardCol.Hidden {
   317  			// The user managed to reverse-engineer our crazy shard column name, so
   318  			// we'll return an error here rather than try to be tricky.
   319  			return nil, false, pgerror.Newf(pgcode.DuplicateColumn,
   320  				"column %s already specified; can't be used for sharding", shardCol.Name)
   321  		}
   322  		return existingShardCol, false, nil
   323  	}
   324  	columnIsUndefined := sqlbase.IsUndefinedColumnError(err)
   325  	if err != nil && !columnIsUndefined {
   326  		return nil, false, err
   327  	}
   328  	if columnIsUndefined || dropped {
   329  		if isNewTable {
   330  			desc.AddColumn(shardCol)
   331  		} else {
   332  			desc.AddColumnMutation(shardCol, sqlbase.DescriptorMutation_ADD)
   333  		}
   334  		created = true
   335  	}
   336  	return shardCol, created, nil
   337  }
   338  
   339  func (n *createIndexNode) startExec(params runParams) error {
   340  	telemetry.Inc(sqltelemetry.SchemaChangeCreateCounter("index"))
   341  	_, dropped, err := n.tableDesc.FindIndexByName(string(n.n.Name))
   342  	if err == nil {
   343  		if dropped {
   344  			return pgerror.Newf(pgcode.ObjectNotInPrerequisiteState,
   345  				"index %q being dropped, try again later", string(n.n.Name))
   346  		}
   347  		if n.n.IfNotExists {
   348  			return nil
   349  		}
   350  	}
   351  
   352  	if n.n.Concurrently {
   353  		params.p.SendClientNotice(
   354  			params.ctx,
   355  			pgnotice.Newf("CONCURRENTLY is not required as all indexes are created concurrently"),
   356  		)
   357  	}
   358  
   359  	// Warn against creating a non-partitioned index on a partitioned table,
   360  	// which is undesirable in most cases.
   361  	if n.n.PartitionBy == nil && n.tableDesc.PrimaryIndex.Partitioning.NumColumns > 0 {
   362  		params.p.SendClientNotice(
   363  			params.ctx,
   364  			errors.WithHint(
   365  				pgnotice.Newf("creating non-partitioned index on partitioned table may not be performant"),
   366  				"Consider modifying the index such that it is also partitioned.",
   367  			),
   368  		)
   369  	}
   370  
   371  	indexDesc, err := MakeIndexDescriptor(params, n.n, n.tableDesc)
   372  	if err != nil {
   373  		return err
   374  	}
   375  
   376  	// Increment the counter if this index could be storing data across multiple column families.
   377  	if len(indexDesc.StoreColumnNames) > 1 && len(n.tableDesc.Families) > 1 {
   378  		telemetry.Inc(sqltelemetry.SecondaryIndexColumnFamiliesCounter)
   379  	}
   380  
   381  	// If all nodes in the cluster know how to handle secondary indexes with column families,
   382  	// write the new version into the index descriptor.
   383  	encodingVersion := sqlbase.BaseIndexFormatVersion
   384  	if params.p.EvalContext().Settings.Version.IsActive(params.ctx, clusterversion.VersionSecondaryIndexColumnFamilies) {
   385  		encodingVersion = sqlbase.SecondaryIndexFamilyFormatVersion
   386  	}
   387  	indexDesc.Version = encodingVersion
   388  
   389  	if n.n.PartitionBy != nil {
   390  		partitioning, err := CreatePartitioning(params.ctx, params.p.ExecCfg().Settings,
   391  			params.EvalContext(), n.tableDesc, indexDesc, n.n.PartitionBy)
   392  		if err != nil {
   393  			return err
   394  		}
   395  		indexDesc.Partitioning = partitioning
   396  	}
   397  
   398  	mutationIdx := len(n.tableDesc.Mutations)
   399  	if err := n.tableDesc.AddIndexMutation(indexDesc, sqlbase.DescriptorMutation_ADD); err != nil {
   400  		return err
   401  	}
   402  	if err := n.tableDesc.AllocateIDs(); err != nil {
   403  		return err
   404  	}
   405  	// The index name may have changed as a result of
   406  	// AllocateIDs(). Retrieve it for the event log below.
   407  	index := n.tableDesc.Mutations[mutationIdx].GetIndex()
   408  	indexName := index.Name
   409  
   410  	if n.n.Interleave != nil {
   411  		if err := params.p.addInterleave(params.ctx, n.tableDesc, index, n.n.Interleave); err != nil {
   412  			return err
   413  		}
   414  		if err := params.p.finalizeInterleave(params.ctx, n.tableDesc, index); err != nil {
   415  			return err
   416  		}
   417  	}
   418  
   419  	mutationID := n.tableDesc.ClusterVersion.NextMutationID
   420  	if err := params.p.writeSchemaChange(
   421  		params.ctx, n.tableDesc, mutationID, tree.AsStringWithFQNames(n.n, params.Ann()),
   422  	); err != nil {
   423  		return err
   424  	}
   425  
   426  	// Record index creation in the event log. This is an auditable log
   427  	// event and is recorded in the same transaction as the table descriptor
   428  	// update.
   429  	return MakeEventLogger(params.extendedEvalCtx.ExecCfg).InsertEventRecord(
   430  		params.ctx,
   431  		params.p.txn,
   432  		EventLogCreateIndex,
   433  		int32(n.tableDesc.ID),
   434  		int32(params.extendedEvalCtx.NodeID.SQLInstanceID()),
   435  		struct {
   436  			TableName  string
   437  			IndexName  string
   438  			Statement  string
   439  			User       string
   440  			MutationID uint32
   441  		}{
   442  			n.n.Table.FQString(), indexName, n.n.String(),
   443  			params.SessionData().User, uint32(mutationID),
   444  		},
   445  	)
   446  }
   447  
   448  func (*createIndexNode) Next(runParams) (bool, error) { return false, nil }
   449  func (*createIndexNode) Values() tree.Datums          { return tree.Datums{} }
   450  func (*createIndexNode) Close(context.Context)        {}