diff --git a/docs/generated/sql/bnf/col_qualification.bnf b/docs/generated/sql/bnf/col_qualification.bnf index 2abaaa68cb03..dd766c1229b7 100644 --- a/docs/generated/sql/bnf/col_qualification.bnf +++ b/docs/generated/sql/bnf/col_qualification.bnf @@ -1,7 +1,7 @@ col_qualification ::= 'CONSTRAINT' constraint_name 'NOT' 'NULL' | 'CONSTRAINT' constraint_name 'NULL' - | 'CONSTRAINT' constraint_name 'UNIQUE' + | 'CONSTRAINT' constraint_name 'UNIQUE' opt_without_index | 'CONSTRAINT' constraint_name 'PRIMARY' 'KEY' | 'CONSTRAINT' constraint_name 'PRIMARY' 'KEY' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets | 'CONSTRAINT' constraint_name 'CHECK' '(' a_expr ')' @@ -10,7 +10,7 @@ col_qualification ::= | 'CONSTRAINT' constraint_name generated_as '(' a_expr ')' 'STORED' | 'NOT' 'NULL' | 'NULL' - | 'UNIQUE' + | 'UNIQUE' opt_without_index | 'PRIMARY' 'KEY' | 'PRIMARY' 'KEY' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets | 'CHECK' '(' a_expr ')' diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index 6b4d63c86ac8..bdb6f1f57b07 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -2198,7 +2198,7 @@ constraint_name ::= constraint_elem ::= 'CHECK' '(' a_expr ')' - | 'UNIQUE' '(' index_params ')' opt_storing opt_interleave opt_partition_by opt_where_clause + | 'UNIQUE' opt_without_index '(' index_params ')' opt_storing opt_interleave opt_partition_by opt_where_clause | 'PRIMARY' 'KEY' '(' index_params ')' opt_hash_sharded opt_interleave | 'FOREIGN' 'KEY' '(' name_list ')' 'REFERENCES' table_name opt_column_list key_match reference_actions @@ -2487,6 +2487,10 @@ col_qualification ::= | 'CREATE' 'FAMILY' | 'CREATE' 'IF' 'NOT' 'EXISTS' 'FAMILY' family_name +opt_without_index ::= + 'WITHOUT' 'INDEX' + | + key_match ::= 'MATCH' 'SIMPLE' | 'MATCH' 'FULL' @@ -2730,7 +2734,7 @@ create_as_constraint_elem ::= col_qualification_elem ::= 'NOT' 'NULL' | 'NULL' - | 'UNIQUE' + | 'UNIQUE' opt_without_index | 'PRIMARY' 'KEY' | 'PRIMARY' 'KEY' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' a_expr | 'CHECK' '(' a_expr ')' diff --git a/docs/generated/sql/bnf/table_constraint.bnf b/docs/generated/sql/bnf/table_constraint.bnf index 97986219e3b5..aae6bf3e3863 100644 --- a/docs/generated/sql/bnf/table_constraint.bnf +++ b/docs/generated/sql/bnf/table_constraint.bnf @@ -1,17 +1,17 @@ table_constraint ::= 'CONSTRAINT' constraint_name 'CHECK' '(' a_expr ')' - | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'COVERING' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause - | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'STORING' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause - | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause - | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' opt_interleave opt_partition_by opt_where_clause + | 'CONSTRAINT' constraint_name 'UNIQUE' opt_without_index '(' index_params ')' 'COVERING' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause + | 'CONSTRAINT' constraint_name 'UNIQUE' opt_without_index '(' index_params ')' 'STORING' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause + | 'CONSTRAINT' constraint_name 'UNIQUE' opt_without_index '(' index_params ')' 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause + | 'CONSTRAINT' constraint_name 'UNIQUE' opt_without_index '(' index_params ')' opt_interleave opt_partition_by opt_where_clause | 'CONSTRAINT' constraint_name 'PRIMARY' 'KEY' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_interleave | 'CONSTRAINT' constraint_name 'PRIMARY' 'KEY' '(' index_params ')' opt_interleave | 'CONSTRAINT' constraint_name 'FOREIGN' 'KEY' '(' name_list ')' 'REFERENCES' table_name opt_column_list key_match reference_actions | 'CHECK' '(' a_expr ')' - | 'UNIQUE' '(' index_params ')' 'COVERING' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause - | 'UNIQUE' '(' index_params ')' 'STORING' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause - | 'UNIQUE' '(' index_params ')' 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause - | 'UNIQUE' '(' index_params ')' opt_interleave opt_partition_by opt_where_clause + | 'UNIQUE' opt_without_index '(' index_params ')' 'COVERING' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause + | 'UNIQUE' opt_without_index '(' index_params ')' 'STORING' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause + | 'UNIQUE' opt_without_index '(' index_params ')' 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by opt_where_clause + | 'UNIQUE' opt_without_index '(' index_params ')' opt_interleave opt_partition_by opt_where_clause | 'PRIMARY' 'KEY' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_interleave | 'PRIMARY' 'KEY' '(' index_params ')' opt_interleave | 'FOREIGN' 'KEY' '(' name_list ')' 'REFERENCES' table_name opt_column_list key_match reference_actions diff --git a/pkg/sql/alter_table.go b/pkg/sql/alter_table.go index 6d1d437f3495..1c19a63ce505 100644 --- a/pkg/sql/alter_table.go +++ b/pkg/sql/alter_table.go @@ -163,6 +163,11 @@ func (n *alterTableNode) startExec(params runParams) error { case *tree.AlterTableAddConstraint: switch d := t.ConstraintDef.(type) { case *tree.UniqueConstraintTableDef: + if d.NoIndex { + return pgerror.New(pgcode.FeatureNotSupported, + "unique constraints without an index are not yet supported", + ) + } if d.PrimaryKey { // We only support "adding" a primary key when we are using the // default rowid primary index or if a DROP PRIMARY KEY statement diff --git a/pkg/sql/catalog/tabledesc/table.go b/pkg/sql/catalog/tabledesc/table.go index 5b06a9448dcf..535ac08f9a17 100644 --- a/pkg/sql/catalog/tabledesc/table.go +++ b/pkg/sql/catalog/tabledesc/table.go @@ -64,6 +64,11 @@ func MakeColumnDefDescs( // Should never happen since `HoistConstraints` moves these to table level return nil, nil, nil, errors.New("unexpected column REFERENCED constraint") } + if d.UniqueNoIndex { + return nil, nil, nil, pgerror.New(pgcode.FeatureNotSupported, + "unique constraints without an index are not yet supported", + ) + } col := &descpb.ColumnDescriptor{ Name: string(d.Name), @@ -107,7 +112,7 @@ func MakeColumnDefDescs( } var idx *descpb.IndexDescriptor - if d.PrimaryKey.IsPrimaryKey || d.Unique { + if d.PrimaryKey.IsPrimaryKey || (d.Unique && !d.UniqueNoIndex) { if !d.PrimaryKey.Sharded { idx = &descpb.IndexDescriptor{ Unique: true, diff --git a/pkg/sql/create_table.go b/pkg/sql/create_table.go index 2682547fba3a..9a93961d784a 100644 --- a/pkg/sql/create_table.go +++ b/pkg/sql/create_table.go @@ -1468,6 +1468,11 @@ func NewTableDesc( return nil, unimplemented.NewWithIssue(9148, "use CREATE INDEX to make interleaved indexes") } case *tree.UniqueConstraintTableDef: + if d.NoIndex { + return nil, pgerror.New(pgcode.FeatureNotSupported, + "unique constraints without an index are not yet supported", + ) + } idx := descpb.IndexDescriptor{ Name: string(d.Name), Unique: true, @@ -2077,7 +2082,11 @@ func incTelemetryForNewColumn(def *tree.ColumnTableDef, desc *descpb.ColumnDescr telemetry.Inc(sqltelemetry.SchemaNewColumnTypeQualificationCounter("default_expr")) } if def.Unique { - telemetry.Inc(sqltelemetry.SchemaNewColumnTypeQualificationCounter("unique")) + if def.UniqueNoIndex { + telemetry.Inc(sqltelemetry.SchemaNewColumnTypeQualificationCounter("unique_without_index")) + } else { + telemetry.Inc(sqltelemetry.SchemaNewColumnTypeQualificationCounter("unique")) + } } } diff --git a/pkg/sql/logictest/testdata/logic_test/alter_table b/pkg/sql/logictest/testdata/logic_test/alter_table index 7916c2ebd0f5..9ce3ffd637b7 100644 --- a/pkg/sql/logictest/testdata/logic_test/alter_table +++ b/pkg/sql/logictest/testdata/logic_test/alter_table @@ -1401,3 +1401,17 @@ ALTER TABLE t_cannot_rename_constraint_over_index RENAME CONSTRAINT v_unique TO statement error relation idx_v already exists ALTER TABLE t_cannot_rename_constraint_over_index RENAME CONSTRAINT "primary" TO idx_v; + +subtest unique_without_index + +statement ok +CREATE TABLE unique_without_index ( + a INT, + b INT +) + +statement error unique constraints without an index are not yet supported +ALTER TABLE unique_without_index ADD COLUMN c INT UNIQUE WITHOUT INDEX + +statement error unique constraints without an index are not yet supported +ALTER TABLE unique_without_index ADD CONSTRAINT ab UNIQUE WITHOUT INDEX (a, b) diff --git a/pkg/sql/logictest/testdata/logic_test/create_table b/pkg/sql/logictest/testdata/logic_test/create_table index f5c65367fe4b..0fb09a2f5d71 100644 --- a/pkg/sql/logictest/testdata/logic_test/create_table +++ b/pkg/sql/logictest/testdata/logic_test/create_table @@ -316,3 +316,11 @@ CREATE TABLE error (LIKE like_hash_base INCLUDING STATISTICS) statement error unimplemented CREATE TABLE error (LIKE like_hash_base INCLUDING STORAGE) + +subtest unique_without_index + +statement error unique constraints without an index are not yet supported +CREATE TABLE unique_without_index (a INT UNIQUE WITHOUT INDEX) + +statement error unique constraints without an index are not yet supported +CREATE TABLE unique_without_index (a INT, b INT, CONSTRAINT ab UNIQUE WITHOUT INDEX (a, b)) diff --git a/pkg/sql/mutations/mutations.go b/pkg/sql/mutations/mutations.go index c4452384bb0e..d952c5fae1b2 100644 --- a/pkg/sql/mutations/mutations.go +++ b/pkg/sql/mutations/mutations.go @@ -278,13 +278,15 @@ func statisticsMutator( DistinctCount: distinctCount, NullCount: nullCount, } - if def.Unique || def.PrimaryKey.IsPrimaryKey { + if (def.Unique && !def.UniqueNoIndex) || def.PrimaryKey.IsPrimaryKey { makeHistogram(def) } case *tree.IndexTableDef: makeHistogram(cols[def.Columns[0].Column]) case *tree.UniqueConstraintTableDef: - makeHistogram(cols[def.Columns[0].Column]) + if !def.NoIndex { + makeHistogram(cols[def.Columns[0].Column]) + } } } if len(colStats) > 0 { @@ -569,6 +571,10 @@ var postgresStatementMutator MultiStatementMutation = func(rng *rand.Rand, stmts def.Family.Create = false changed = true } + if def.UniqueNoIndex { + def.UniqueNoIndex = false + changed = true + } case *tree.UniqueConstraintTableDef: if def.Interleave != nil { def.Interleave = nil @@ -578,6 +584,10 @@ var postgresStatementMutator MultiStatementMutation = func(rng *rand.Rand, stmts def.PartitionBy = nil changed = true } + if def.NoIndex { + def.NoIndex = false + changed = true + } } } case *tree.AlterTable: diff --git a/pkg/sql/opt/testutils/testcat/create_table.go b/pkg/sql/opt/testutils/testcat/create_table.go index 410088aad141..20d86a03ce51 100644 --- a/pkg/sql/opt/testutils/testcat/create_table.go +++ b/pkg/sql/opt/testutils/testcat/create_table.go @@ -18,6 +18,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/geo/geoindex" "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/types" "github.com/cockroachdb/cockroach/pkg/util" @@ -178,6 +180,11 @@ func (tc *Catalog) CreateTable(stmt *tree.CreateTable) *Table { for _, def := range stmt.Defs { switch def := def.(type) { case *tree.UniqueConstraintTableDef: + if def.NoIndex { + panic(pgerror.New(pgcode.FeatureNotSupported, + "unique constraints without an index are not yet supported", + )) + } if !def.PrimaryKey { tab.addIndex(&def.IndexTableDef, uniqueIndex) } @@ -189,6 +196,11 @@ func (tc *Catalog) CreateTable(stmt *tree.CreateTable) *Table { tab.addFamily(def) case *tree.ColumnTableDef: + if def.UniqueNoIndex { + panic(pgerror.New(pgcode.FeatureNotSupported, + "unique constraints without an index are not yet supported", + )) + } if def.Unique { tab.addIndex( &tree.IndexTableDef{ diff --git a/pkg/sql/parser/parse_test.go b/pkg/sql/parser/parse_test.go index d72f7e4077d5..66b6bd150b83 100644 --- a/pkg/sql/parser/parse_test.go +++ b/pkg/sql/parser/parse_test.go @@ -176,6 +176,7 @@ func TestParse(t *testing.T) { {`CREATE TABLE a (b INT8 CONSTRAINT always NOT NULL)`}, {`CREATE TABLE a (b INT8 PRIMARY KEY)`}, {`CREATE TABLE a (b INT8 UNIQUE)`}, + {`CREATE TABLE a (b INT8 UNIQUE WITHOUT INDEX)`}, {`CREATE TABLE a (b INT8 NULL PRIMARY KEY)`}, {`CREATE TABLE a (b INT8 DEFAULT 1)`}, {`CREATE TABLE a (b INT8 CONSTRAINT one DEFAULT 1)`}, @@ -223,6 +224,7 @@ func TestParse(t *testing.T) { {`CREATE TABLE a (b INT8, c STRING, INDEX (b, c))`}, {`CREATE TABLE a (b INT8, c STRING, INDEX d (b, c))`}, {`CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE (b, c))`}, + {`CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE WITHOUT INDEX (b, c))`}, {`CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE (b, c) INTERLEAVE IN PARENT d (e, f))`}, {`CREATE TABLE a (b INT8, UNIQUE (b))`}, {`CREATE TABLE a (b INT8, UNIQUE (b) STORING (c))`}, @@ -271,6 +273,7 @@ func TestParse(t *testing.T) { {`CREATE TABLE a (b INT8 CONSTRAINT c PRIMARY KEY)`}, {`CREATE TABLE a (b INT8 CONSTRAINT c NULL)`}, {`CREATE TABLE a (b INT8 CONSTRAINT c UNIQUE)`}, + {`CREATE TABLE a (b INT8 CONSTRAINT c UNIQUE WITHOUT INDEX)`}, {`CREATE TABLE a (b INT8 CONSTRAINT c DEFAULT d)`}, {`CREATE TABLE a (b INT8 CONSTRAINT c CHECK (d))`}, {`CREATE TABLE a (b INT8 CONSTRAINT c REFERENCES d)`}, @@ -1351,7 +1354,7 @@ func TestParse(t *testing.T) { {`ALTER TABLE a ADD COLUMN IF NOT EXISTS b INT8, ADD CONSTRAINT a_idx UNIQUE (a)`}, {`ALTER TABLE IF EXISTS a ADD COLUMN b INT8, ADD CONSTRAINT a_idx UNIQUE (a)`}, {`ALTER TABLE IF EXISTS a ADD COLUMN IF NOT EXISTS b INT8, ADD CONSTRAINT a_idx UNIQUE (a)`}, - {`ALTER TABLE a ADD COLUMN b INT8, ADD CONSTRAINT a_idx UNIQUE (a)`}, + {`ALTER TABLE a ADD COLUMN b INT8 UNIQUE WITHOUT INDEX, ADD CONSTRAINT a_no_idx UNIQUE WITHOUT INDEX (a)`}, {`ALTER TABLE a ADD COLUMN IF NOT EXISTS b INT8, ADD CONSTRAINT a_idx UNIQUE (a) NOT VALID`}, {`ALTER TABLE IF EXISTS a ADD COLUMN b INT8, ADD CONSTRAINT a_idx UNIQUE (a)`}, {`ALTER TABLE IF EXISTS a ADD COLUMN IF NOT EXISTS b INT8, ADD CONSTRAINT a_idx UNIQUE (a)`}, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 5f574dbbb4ea..5ea2ea72279a 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -1020,7 +1020,7 @@ func (u *sqlSymUnion) refreshDataOption() tree.RefreshDataOption { %type opt_interval_qualifier interval_qualifier interval_second %type overlay_placing -%type opt_unique opt_concurrently opt_cluster +%type opt_unique opt_concurrently opt_cluster opt_without_index %type opt_index_access_method %type <*tree.Limit> limit_clause offset_clause opt_limit_clause @@ -1317,7 +1317,7 @@ alter_ddl_stmt: // ALTER TABLE ... SET SCHEMA // // Column qualifiers: -// [CONSTRAINT ] {NULL | NOT NULL | UNIQUE | PRIMARY KEY | CHECK () | DEFAULT } +// [CONSTRAINT ] {NULL | NOT NULL | UNIQUE [WITHOUT INDEX] | PRIMARY KEY | CHECK () | DEFAULT } // FAMILY , CREATE [IF NOT EXISTS] FAMILY [] // REFERENCES [( )] // COLLATE @@ -5426,11 +5426,11 @@ alter_schema_stmt: // Table constraints: // PRIMARY KEY ( ) [USING HASH WITH BUCKET_COUNT = ] // FOREIGN KEY ( ) REFERENCES [( )] [ON DELETE {NO ACTION | RESTRICT}] [ON UPDATE {NO ACTION | RESTRICT}] -// UNIQUE ( )] [] +// UNIQUE [WITHOUT INDEX] ( )] [] // CHECK ( ) // // Column qualifiers: -// [CONSTRAINT ] {NULL | NOT NULL | UNIQUE | PRIMARY KEY | CHECK () | DEFAULT } +// [CONSTRAINT ] {NULL | NOT NULL | UNIQUE [WITHOUT INDEX] | PRIMARY KEY | CHECK () | DEFAULT } // FAMILY , CREATE [IF NOT EXISTS] FAMILY [] // REFERENCES [( )] [ON DELETE {NO ACTION | RESTRICT}] [ON UPDATE {NO ACTION | RESTRICT}] // COLLATE @@ -5868,9 +5868,11 @@ col_qualification_elem: { $$.val = tree.NullConstraint{} } -| UNIQUE +| UNIQUE opt_without_index { - $$.val = tree.UniqueConstraint{} + $$.val = tree.UniqueConstraint{ + NoIndex: $2.bool(), + } } | PRIMARY KEY { @@ -5915,6 +5917,16 @@ col_qualification_elem: return 1 } +opt_without_index: + WITHOUT INDEX + { + $$.val = true + } +| /* EMPTY */ + { + $$.val = false + } + // GENERATED ALWAYS is a noise word for compatibility with Postgres. generated_as: AS {} @@ -5991,15 +6003,17 @@ constraint_elem: Expr: $3.expr(), } } -| UNIQUE '(' index_params ')' opt_storing opt_interleave opt_partition_by opt_deferrable opt_where_clause +| UNIQUE opt_without_index '(' index_params ')' + opt_storing opt_interleave opt_partition_by opt_deferrable opt_where_clause { $$.val = &tree.UniqueConstraintTableDef{ + NoIndex: $2.bool(), IndexTableDef: tree.IndexTableDef{ - Columns: $3.idxElems(), - Storing: $5.nameList(), - Interleave: $6.interleave(), - PartitionBy: $7.partitionBy(), - Predicate: $9.expr(), + Columns: $4.idxElems(), + Storing: $6.nameList(), + Interleave: $7.interleave(), + PartitionBy: $8.partitionBy(), + Predicate: $10.expr(), }, } } diff --git a/pkg/sql/rowenc/testutils.go b/pkg/sql/rowenc/testutils.go index d463ded70a94..5f094d3f0b6f 100644 --- a/pkg/sql/rowenc/testutils.go +++ b/pkg/sql/rowenc/testutils.go @@ -1368,7 +1368,7 @@ func IndexStoringMutator(rng *rand.Rand, stmts []tree.Statement) ([]tree.Stateme case *tree.IndexTableDef: idx = defType case *tree.UniqueConstraintTableDef: - if !defType.PrimaryKey { + if !defType.PrimaryKey && !defType.NoIndex { idx = &defType.IndexTableDef } } @@ -1422,7 +1422,7 @@ func PartialIndexMutator(rng *rand.Rand, stmts []tree.Statement) ([]tree.Stateme case *tree.IndexTableDef: idx = defType case *tree.UniqueConstraintTableDef: - if !defType.PrimaryKey { + if !defType.PrimaryKey && !defType.NoIndex { idx = &defType.IndexTableDef } } diff --git a/pkg/sql/sem/tree/create.go b/pkg/sql/sem/tree/create.go index ac11be23e0fe..032450b35f67 100644 --- a/pkg/sql/sem/tree/create.go +++ b/pkg/sql/sem/tree/create.go @@ -370,6 +370,7 @@ type ColumnTableDef struct { ShardBuckets Expr } Unique bool + UniqueNoIndex bool UniqueConstraintName Name DefaultExpr struct { Expr Expr @@ -487,6 +488,7 @@ func NewColumnTableDef( d.UniqueConstraintName = c.Name case UniqueConstraint: d.Unique = true + d.UniqueNoIndex = t.NoIndex d.UniqueConstraintName = c.Name case *ColumnCheckConstraint: d.CheckExprs = append(d.CheckExprs, ColumnTableDefCheckExpr{ @@ -575,6 +577,9 @@ func (node *ColumnTableDef) Format(ctx *FmtCtx) { } } else if node.Unique { ctx.WriteString(" UNIQUE") + if node.UniqueNoIndex { + ctx.WriteString(" WITHOUT INDEX") + } } } if node.HasDefaultExpr() { @@ -700,7 +705,9 @@ type ShardedPrimaryKeyConstraint struct { } // UniqueConstraint represents UNIQUE on a column. -type UniqueConstraint struct{} +type UniqueConstraint struct { + NoIndex bool +} // ColumnCheckConstraint represents either a check on a column. type ColumnCheckConstraint struct { @@ -800,6 +807,7 @@ func (*CheckConstraintTableDef) constraintTableDef() {} type UniqueConstraintTableDef struct { IndexTableDef PrimaryKey bool + NoIndex bool } // SetName implements the TableDef interface. @@ -819,6 +827,9 @@ func (node *UniqueConstraintTableDef) Format(ctx *FmtCtx) { } else { ctx.WriteString("UNIQUE ") } + if node.NoIndex { + ctx.WriteString("WITHOUT INDEX ") + } ctx.WriteByte('(') ctx.FormatNode(&node.Columns) ctx.WriteByte(')') diff --git a/pkg/sql/sem/tree/pretty.go b/pkg/sql/sem/tree/pretty.go index 48faa53f4e6a..191dbd03545b 100644 --- a/pkg/sql/sem/tree/pretty.go +++ b/pkg/sql/sem/tree/pretty.go @@ -1652,7 +1652,7 @@ func (node *IndexTableDef) doc(p *PrettyCfg) pretty.Doc { func (node *UniqueConstraintTableDef) doc(p *PrettyCfg) pretty.Doc { // Final layout: // [CONSTRAINT name] - // [PRIMARY KEY|UNIQUE] ( ... ) + // [PRIMARY KEY|UNIQUE [WITHOUT INDEX]] ( ... ) // [STORING ( ... )] // [INTERLEAVE ...] // [PARTITION BY ...] @@ -1660,7 +1660,7 @@ func (node *UniqueConstraintTableDef) doc(p *PrettyCfg) pretty.Doc { // // or (no constraint name): // - // [PRIMARY KEY|UNIQUE] ( ... ) + // [PRIMARY KEY|UNIQUE [WITHOUT INDEX]] ( ... ) // [STORING ( ... )] // [INTERLEAVE ...] // [PARTITION BY ...] @@ -1672,6 +1672,9 @@ func (node *UniqueConstraintTableDef) doc(p *PrettyCfg) pretty.Doc { title = pretty.Keyword("PRIMARY KEY") } else { title = pretty.Keyword("UNIQUE") + if node.NoIndex { + title = pretty.ConcatSpace(title, pretty.Keyword("WITHOUT INDEX")) + } } title = pretty.ConcatSpace(title, p.bracket("(", p.Doc(&node.Columns), ")")) if node.Name != "" { @@ -1768,7 +1771,7 @@ func (node *ColumnTableDef) docRow(p *PrettyCfg) pretty.TableRow { // [[CREATE [IF NOT EXISTS]] FAMILY [name]] // [[CONSTRAINT name] DEFAULT expr] // [[CONSTRAINT name] {NULL|NOT NULL}] - // [[CONSTRAINT name] {PRIMARY KEY|UNIQUE}] + // [[CONSTRAINT name] {PRIMARY KEY|UNIQUE [WITHOUT INDEX]}] // [[CONSTRAINT name] CHECK ...] // [[CONSTRAINT name] REFERENCES tbl (...) // [MATCH ...] @@ -1831,6 +1834,9 @@ func (node *ColumnTableDef) docRow(p *PrettyCfg) pretty.TableRow { pkConstraint = pretty.Keyword("PRIMARY KEY") } else if node.Unique { pkConstraint = pretty.Keyword("UNIQUE") + if node.UniqueNoIndex { + pkConstraint = pretty.ConcatSpace(pkConstraint, pretty.Keyword("WITHOUT INDEX")) + } } if pkConstraint != pretty.Nil { clauses = append(clauses, p.maybePrependConstraintName(&node.UniqueConstraintName, pkConstraint))