From aa8a5bb14d147bbd64486dc10ba60ee54ca08e39 Mon Sep 17 00:00:00 2001 From: Jane Xing Date: Wed, 11 Aug 2021 12:03:28 -0500 Subject: [PATCH] sql/parser: support GENERATED {ALWAYS | BY DEFAULT} AS IDENTITY syntax This commit is to allow the parser to support the `GENERATED {ALWAYS | BY DEFAULT} AS IDENTITY` syntax under `CREATE TABLE` statement. Release note (sql change): Added support for `GENERATED {ALWAYS | BY DEFAULT} AS IDENTITY` syntax in a column definition. This will automatically create a sequence for the given column. This matches PostgreSQL syntax and functionality. See https://www.postgresql.org/docs/current/sql-createtable.html --- docs/generated/sql/bnf/col_qualification.bnf | 16 +-- docs/generated/sql/bnf/stmt_block.bnf | 10 +- pkg/cmd/docgen/diagrams.go | 2 +- pkg/sql/parser/lexer.go | 2 + pkg/sql/parser/sql.y | 39 ++++--- pkg/sql/parser/testdata/alter_table | 72 +++++++++++++ pkg/sql/parser/testdata/create_table | 108 +++++++++++++++++++ pkg/sql/sem/tree/create.go | 61 ++++++++++- pkg/sql/sem/tree/pretty.go | 13 +++ 9 files changed, 297 insertions(+), 26 deletions(-) diff --git a/docs/generated/sql/bnf/col_qualification.bnf b/docs/generated/sql/bnf/col_qualification.bnf index 434cba8896a7..49ab2caeff61 100644 --- a/docs/generated/sql/bnf/col_qualification.bnf +++ b/docs/generated/sql/bnf/col_qualification.bnf @@ -8,10 +8,10 @@ col_qualification ::= | 'CONSTRAINT' constraint_name 'CHECK' '(' a_expr ')' | 'CONSTRAINT' constraint_name 'DEFAULT' b_expr | 'CONSTRAINT' constraint_name 'REFERENCES' table_name opt_name_parens key_match reference_actions - | 'CONSTRAINT' constraint_name 'AS' '(' a_expr ')' 'STORED' - | 'CONSTRAINT' constraint_name 'GENERATED_ALWAYS' 'ALWAYS' 'AS' '(' a_expr ')' 'STORED' - | 'CONSTRAINT' constraint_name 'AS' '(' a_expr ')' 'VIRTUAL' - | 'CONSTRAINT' constraint_name 'GENERATED_ALWAYS' 'ALWAYS' 'AS' '(' a_expr ')' 'VIRTUAL' + | 'CONSTRAINT' constraint_name generated_as '(' a_expr ')' 'STORED' + | 'CONSTRAINT' constraint_name generated_as '(' a_expr ')' 'VIRTUAL' + | 'CONSTRAINT' constraint_name 'GENERATED_ALWAYS' 'ALWAYS' 'AS' 'IDENTITY' + | 'CONSTRAINT' constraint_name 'GENERATED_BY_DEFAULT' 'BY' 'DEFAULT' 'AS' 'IDENTITY' | 'NOT' 'NULL' | 'NULL' | 'NOT' 'VISIBLE' @@ -21,10 +21,10 @@ col_qualification ::= | 'CHECK' '(' a_expr ')' | 'DEFAULT' b_expr | 'REFERENCES' table_name opt_name_parens key_match reference_actions - | 'AS' '(' a_expr ')' 'STORED' - | 'GENERATED_ALWAYS' 'ALWAYS' 'AS' '(' a_expr ')' 'STORED' - | 'AS' '(' a_expr ')' 'VIRTUAL' - | 'GENERATED_ALWAYS' 'ALWAYS' 'AS' '(' a_expr ')' 'VIRTUAL' + | generated_as '(' a_expr ')' 'STORED' + | generated_as '(' a_expr ')' 'VIRTUAL' + | 'GENERATED_ALWAYS' 'ALWAYS' 'AS' 'IDENTITY' + | 'GENERATED_BY_DEFAULT' 'BY' 'DEFAULT' 'AS' 'IDENTITY' | 'COLLATE' collation_name | 'FAMILY' family_name | 'CREATE' 'FAMILY' family_name diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index db0d1896fed7..e6a7df3b67f4 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -3036,6 +3036,8 @@ col_qualification_elem ::= | 'REFERENCES' table_name opt_name_parens key_match reference_actions | generated_as '(' a_expr ')' 'STORED' | generated_as '(' a_expr ')' 'VIRTUAL' + | generated_always_as 'IDENTITY' + | generated_by_default_as 'IDENTITY' family_name ::= name @@ -3175,7 +3177,13 @@ opt_name_parens ::= generated_as ::= 'AS' - | 'GENERATED_ALWAYS' 'ALWAYS' 'AS' + | generated_always_as + +generated_always_as ::= + 'GENERATED_ALWAYS' 'ALWAYS' 'AS' + +generated_by_default_as ::= + 'GENERATED_BY_DEFAULT' 'BY' 'DEFAULT' 'AS' reference_action ::= 'NO' 'ACTION' diff --git a/pkg/cmd/docgen/diagrams.go b/pkg/cmd/docgen/diagrams.go index 6686711fadeb..9e3939ce9261 100644 --- a/pkg/cmd/docgen/diagrams.go +++ b/pkg/cmd/docgen/diagrams.go @@ -535,7 +535,7 @@ var specs = []stmtSpec{ { name: "col_qualification", stmt: "col_qualification", - inline: []string{"col_qualification_elem", "opt_hash_sharded", "generated_as"}, + inline: []string{"col_qualification_elem", "opt_hash_sharded", "generated_always_as", "generated_by_default_as"}, replace: map[string]string{ "'=' a_expr": "'=' n_buckets", }, diff --git a/pkg/sql/parser/lexer.go b/pkg/sql/parser/lexer.go index e5e528232638..e8777a62d474 100644 --- a/pkg/sql/parser/lexer.go +++ b/pkg/sql/parser/lexer.go @@ -102,6 +102,8 @@ func (l *lexer) Lex(lval *sqlSymType) int { switch nextID { case ALWAYS: lval.id = GENERATED_ALWAYS + case BY: + lval.id = GENERATED_BY_DEFAULT } case WITH: diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 07b3dbbad6d8..19c425d4fbc4 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -852,6 +852,7 @@ func (u *sqlSymUnion) setVar() *tree.SetVar { // The grammar thinks these are keywords, but they are not in any category // and so can never be entered directly. The filter in scan.go creates these // tokens when required (based on looking one token ahead). +// Reference: pkg/sql/parser/lexer.go // // NOT_LA exists so that productions such as NOT LIKE can be given the same // precedence as LIKE; otherwise they'd effectively have the same precedence as @@ -861,7 +862,7 @@ func (u *sqlSymUnion) setVar() *tree.SetVar { // extensions (CREATE FAMILY/CREATE FAMILY family_name). RESET_ALL is used // to differentiate `RESET var` from `RESET ALL`. ROLE_ALL and USER_ALL are // used in ALTER ROLE statements that affect all roles. -%token NOT_LA NULLS_LA WITH_LA AS_LA GENERATED_ALWAYS RESET_ALL ROLE_ALL USER_ALL +%token NOT_LA NULLS_LA WITH_LA AS_LA GENERATED_ALWAYS GENERATED_BY_DEFAULT RESET_ALL ROLE_ALL USER_ALL %union { id int32 @@ -6168,11 +6169,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 ( ) [{STORING | INCLUDE | COVERING} ( )] [] // CHECK ( ) // // Column qualifiers: -// [CONSTRAINT ] {NULL | NOT NULL | NOT VISIBLE | UNIQUE | PRIMARY KEY | CHECK () | DEFAULT } +// [CONSTRAINT ] {NULL | NOT NULL | NOT VISIBLE | UNIQUE | PRIMARY KEY | CHECK () | DEFAULT | GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [( )]} // FAMILY , CREATE [IF NOT EXISTS] FAMILY [] // REFERENCES [( )] [ON DELETE {NO ACTION | RESTRICT}] [ON UPDATE {NO ACTION | RESTRICT}] // COLLATE @@ -6703,7 +6704,7 @@ col_qualification_elem: $$.val = &tree.ColumnDefault{Expr: $2.expr()} } | REFERENCES table_name opt_name_parens key_match reference_actions - { + { name := $2.unresolvedObjectName().ToTableName() $$.val = &tree.ColumnFKConstraint{ Table: name, @@ -6711,20 +6712,28 @@ col_qualification_elem: Actions: $5.referenceActions(), Match: $4.compositeKeyMatchMethod(), } - } + } | generated_as '(' a_expr ')' STORED - { + { $$.val = &tree.ColumnComputedDef{Expr: $3.expr(), Virtual: false} - } + } | generated_as '(' a_expr ')' VIRTUAL - { + { $$.val = &tree.ColumnComputedDef{Expr: $3.expr(), Virtual: true} - } + } | generated_as error - { + { sqllex.Error("use AS ( ) STORED or AS ( ) VIRTUAL") return 1 - } + } +| generated_always_as IDENTITY + { + $$.val = &tree.GeneratedAlwaysAsIdentity{} + } +| generated_by_default_as IDENTITY + { + $$.val = &tree.GeneratedByDefAsIdentity{} + } opt_without_index: WITHOUT INDEX @@ -6737,11 +6746,15 @@ opt_without_index: $$.val = false } -// GENERATED ALWAYS is a noise word for compatibility with Postgres. generated_as: AS {} -| GENERATED_ALWAYS ALWAYS AS {} +| generated_always_as + +generated_always_as: + GENERATED_ALWAYS ALWAYS AS {} +generated_by_default_as: + GENERATED_BY_DEFAULT BY DEFAULT AS {} index_def: INDEX opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause diff --git a/pkg/sql/parser/testdata/alter_table b/pkg/sql/parser/testdata/alter_table index 8ee9516f6c80..abdf3f052388 100644 --- a/pkg/sql/parser/testdata/alter_table +++ b/pkg/sql/parser/testdata/alter_table @@ -289,6 +289,30 @@ ALTER TABLE a ADD COLUMN b INT8 CREATE IF NOT EXISTS FAMILY fam_b -- fully paren ALTER TABLE a ADD COLUMN b INT8 CREATE IF NOT EXISTS FAMILY fam_b -- literals removed ALTER TABLE _ ADD COLUMN _ INT8 CREATE IF NOT EXISTS FAMILY _ -- identifiers removed +parse +ALTER TABLE a ADD COLUMN b INT GENERATED ALWAYS AS IDENTITY +---- +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY -- normalized! +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY -- fully parenthesized +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY -- literals removed +ALTER TABLE _ ADD COLUMN _ INT8 NOT NULL GENERATED ALWAYS AS IDENTITY -- identifiers removed + +parse +ALTER TABLE a ADD COLUMN b INT GENERATED BY DEFAULT AS IDENTITY +---- +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY -- normalized! +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY -- fully parenthesized +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY -- literals removed +ALTER TABLE _ ADD COLUMN _ INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY -- identifiers removed + +parse +ALTER TABLE a ADD COLUMN b INT NOT NULL GENERATED BY DEFAULT AS IDENTITY +---- +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY -- normalized! +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY -- fully parenthesized +ALTER TABLE a ADD COLUMN b INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY -- literals removed +ALTER TABLE _ ADD COLUMN _ INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY -- identifiers removed + parse ALTER TABLE t ALTER PRIMARY KEY USING COLUMNS (a, b, c) ---- @@ -1075,3 +1099,51 @@ DETAIL: source SQL: ALTER PARTITION p OF TABLE tbl@* CONFIGURE ZONE USING num_replicas = 1 ^ HINT: try ALTER PARTITION OF INDEX @* + +error +ALTER TABLE t ADD COLUMN b INT NULL GENERATED ALWAYS AS IDENTITY +---- +at or near "EOF": syntax error: conflicting NULL/NOT NULL declarations for column "b" +DETAIL: source SQL: +ALTER TABLE t ADD COLUMN b INT NULL GENERATED ALWAYS AS IDENTITY + ^ + +error +ALTER TABLE t ADD COLUMN b INT GENERATED ALWAYS AS IDENTITY NULL +---- +at or near "EOF": syntax error: conflicting NULL/NOT NULL declarations for column "b" +DETAIL: source SQL: +ALTER TABLE t ADD COLUMN b INT GENERATED ALWAYS AS IDENTITY NULL + ^ + +error +ALTER TABLE a ADD COLUMN b INT GENERATED BY DEFAULT AS IDENTITY GENERATED ALWAYS AS IDENTITY +---- +at or near "EOF": syntax error: multiple identity specifications for column "b" +DETAIL: source SQL: +ALTER TABLE a ADD COLUMN b INT GENERATED BY DEFAULT AS IDENTITY GENERATED ALWAYS AS IDENTITY + ^ + +error +ALTER TABLE a ADD COLUMN b INT AS (a + 10) STORED GENERATED ALWAYS AS IDENTITY +---- +at or near "EOF": syntax error: both identity and generation expression specified for column "b" +DETAIL: source SQL: +ALTER TABLE a ADD COLUMN b INT AS (a + 10) STORED GENERATED ALWAYS AS IDENTITY + ^ + +error +ALTER TABLE a ADD COLUMN b INT GENERATED ALWAYS AS (1 + 1) STORED GENERATED ALWAYS AS IDENTITY +---- +at or near "EOF": syntax error: both identity and generation expression specified for column "b" +DETAIL: source SQL: +ALTER TABLE a ADD COLUMN b INT GENERATED ALWAYS AS (1 + 1) STORED GENERATED ALWAYS AS IDENTITY + ^ + +error +ALTER TABLE a ADD COLUMN b INT GENERATED ALWAYS AS (1+1) IDENTITY +---- +at or near "identity": syntax error: use AS ( ) STORED or AS ( ) VIRTUAL +DETAIL: source SQL: +ALTER TABLE a ADD COLUMN b INT GENERATED ALWAYS AS (1+1) IDENTITY + ^ diff --git a/pkg/sql/parser/testdata/create_table b/pkg/sql/parser/testdata/create_table index c0255fadcf39..1a66b39c202f 100644 --- a/pkg/sql/parser/testdata/create_table +++ b/pkg/sql/parser/testdata/create_table @@ -2260,3 +2260,111 @@ CREATE TABLE operator_tbl (a INT8 DEFAULT 1 OPERATOR(+) 2, b INT8 DEFAULT 1 OPER CREATE TABLE operator_tbl (a INT8 DEFAULT ((1) OPERATOR(+) (2)), b INT8 DEFAULT ((1) OPERATOR(+) (2))) -- fully parenthesized CREATE TABLE operator_tbl (a INT8 DEFAULT _ OPERATOR(+) _, b INT8 DEFAULT _ OPERATOR(+) _) -- literals removed CREATE TABLE _ (_ INT8 DEFAULT 1 OPERATOR(+) 2, _ INT8 DEFAULT 1 OPERATOR(+) 2) -- identifiers removed + +parse +CREATE TABLE generated_always_as_identity_tb ( + a INT UNIQUE, + b INT GENERATED ALWAYS AS IDENTITY, + c INT GENERATED BY DEFAULT AS IDENTITY +) +---- +CREATE TABLE generated_always_as_identity_tb (a INT8 UNIQUE, b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY, c INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY) -- normalized! +CREATE TABLE generated_always_as_identity_tb (a INT8 UNIQUE, b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY, c INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY) -- fully parenthesized +CREATE TABLE generated_always_as_identity_tb (a INT8 UNIQUE, b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY, c INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY) -- literals removed +CREATE TABLE _ (_ INT8 UNIQUE, _ INT8 NOT NULL GENERATED ALWAYS AS IDENTITY, _ INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY) -- identifiers removed + +parse +CREATE TABLE generated_not_null ( + a INT, + b INT NOT NULL GENERATED ALWAYS AS IDENTITY, + c INT NOT NULL GENERATED BY DEFAULT AS IDENTITY +) +---- +CREATE TABLE generated_not_null (a INT8, b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY, c INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY) -- normalized! +CREATE TABLE generated_not_null (a INT8, b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY, c INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY) -- fully parenthesized +CREATE TABLE generated_not_null (a INT8, b INT8 NOT NULL GENERATED ALWAYS AS IDENTITY, c INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY) -- literals removed +CREATE TABLE _ (_ INT8, _ INT8 NOT NULL GENERATED ALWAYS AS IDENTITY, _ INT8 NOT NULL GENERATED BY DEFAULT AS IDENTITY) -- identifiers removed + +error +CREATE TABLE generated_error ( + a INT UNIQUE, + b INT GENERATED BY DEFAULT AS IDENTITY GENERATED ALWAYS AS IDENTITY +) +---- +at or near ")": syntax error: multiple identity specifications for column "b" +DETAIL: source SQL: +CREATE TABLE generated_error ( + a INT UNIQUE, + b INT GENERATED BY DEFAULT AS IDENTITY GENERATED ALWAYS AS IDENTITY +) +^ + +error +CREATE TABLE generated_error ( + a INT UNIQUE, + b INT NULL GENERATED BY DEFAULT AS IDENTITY +) +---- +at or near ")": syntax error: conflicting NULL/NOT NULL declarations for column "b" +DETAIL: source SQL: +CREATE TABLE generated_error ( + a INT UNIQUE, + b INT NULL GENERATED BY DEFAULT AS IDENTITY +) +^ + +error +CREATE TABLE generated_error ( + a INT UNIQUE, + b INT GENERATED BY DEFAULT AS IDENTITY NULL +) +---- +at or near ")": syntax error: conflicting NULL/NOT NULL declarations for column "b" +DETAIL: source SQL: +CREATE TABLE generated_error ( + a INT UNIQUE, + b INT GENERATED BY DEFAULT AS IDENTITY NULL +) +^ + + +error +CREATE TABLE generated_error ( + a INT, + b INT AS (a + 10) STORED GENERATED ALWAYS AS IDENTITY +) +---- +at or near ")": syntax error: both identity and generation expression specified for column "b" +DETAIL: source SQL: +CREATE TABLE generated_error ( + a INT, + b INT AS (a + 10) STORED GENERATED ALWAYS AS IDENTITY +) +^ + +error +CREATE TABLE generated_error ( + a INT, + b INT GENERATED ALWAYS AS (a + 10) STORED GENERATED ALWAYS AS IDENTITY +) +---- +at or near ")": syntax error: both identity and generation expression specified for column "b" +DETAIL: source SQL: +CREATE TABLE generated_error ( + a INT, + b INT GENERATED ALWAYS AS (a + 10) STORED GENERATED ALWAYS AS IDENTITY +) +^ + +error +CREATE TABLE generated_error ( + a INT, + b INT GENERATED ALWAYS AS (a + 10) IDENTITY +) +---- +at or near "identity": syntax error: use AS ( ) STORED or AS ( ) VIRTUAL +DETAIL: source SQL: +CREATE TABLE generated_error ( + a INT, + b INT GENERATED ALWAYS AS (a + 10) IDENTITY + ^ diff --git a/pkg/sql/sem/tree/create.go b/pkg/sql/sem/tree/create.go index 92ff750e3431..d1c4bd31af2d 100644 --- a/pkg/sql/sem/tree/create.go +++ b/pkg/sql/sem/tree/create.go @@ -403,12 +403,26 @@ const ( SilentNull ) +// GeneratedIdentityType represents either GENERATED ALWAYS AS IDENTITY +// or GENERATED BY DEFAULT AS IDENTITY. +type GeneratedIdentityType int + +// The values of GeneratedIdentity.GeneratedAsIdentityType. +const ( + GeneratedAlways GeneratedIdentityType = iota + GeneratedByDefault +) + // ColumnTableDef represents a column definition within a CREATE TABLE // statement. type ColumnTableDef struct { - Name Name - Type ResolvableTypeReference - IsSerial bool + Name Name + Type ResolvableTypeReference + IsSerial bool + GeneratedIdentity struct { + IsGeneratedAsIdentity bool + GeneratedAsIdentityType GeneratedIdentityType + } Hidden bool Nullable struct { Nullability Nullability @@ -522,6 +536,27 @@ func NewColumnTableDef( } d.DefaultExpr.Expr = t.Expr d.DefaultExpr.ConstraintName = c.Name + case *GeneratedAlwaysAsIdentity, *GeneratedByDefAsIdentity: + if d.GeneratedIdentity.IsGeneratedAsIdentity { + return nil, pgerror.Newf(pgcode.Syntax, + "multiple identity specifications for column %q", name) + } + if d.Computed.Computed { + return nil, pgerror.Newf(pgcode.Syntax, + "both identity and generation expression specified for column %q", name) + } + if d.Nullable.Nullability == Null { + return nil, pgerror.Newf(pgcode.Syntax, + "conflicting NULL/NOT NULL declarations for column %q", name) + } + d.GeneratedIdentity.IsGeneratedAsIdentity = true + d.Nullable.Nullability = NotNull + switch c.Qualification.(type) { + case *GeneratedAlwaysAsIdentity: + d.GeneratedIdentity.GeneratedAsIdentityType = GeneratedAlways + case *GeneratedByDefAsIdentity: + d.GeneratedIdentity.GeneratedAsIdentityType = GeneratedByDefault + } case HiddenConstraint: d.Hidden = true case NotNullConstraint: @@ -567,6 +602,10 @@ func NewColumnTableDef( d.References.Actions = t.Actions d.References.Match = t.Match case *ColumnComputedDef: + if d.GeneratedIdentity.IsGeneratedAsIdentity { + return nil, pgerror.Newf(pgcode.Syntax, + "both identity and generation expression specified for column %q", name) + } d.Computed.Computed = true d.Computed.Expr = t.Expr d.Computed.Virtual = t.Virtual @@ -660,6 +699,14 @@ func (node *ColumnTableDef) Format(ctx *FmtCtx) { ctx.WriteString(" DEFAULT ") ctx.FormatNode(node.DefaultExpr.Expr) } + if node.GeneratedIdentity.IsGeneratedAsIdentity { + switch node.GeneratedIdentity.GeneratedAsIdentityType { + case GeneratedAlways: + ctx.WriteString(" GENERATED ALWAYS AS IDENTITY") + case GeneratedByDefault: + ctx.WriteString(" GENERATED BY DEFAULT AS IDENTITY") + } + } for _, checkExpr := range node.CheckExprs { if checkExpr.ConstraintName != "" { ctx.WriteString(" CONSTRAINT ") @@ -754,6 +801,8 @@ func (*ColumnCheckConstraint) columnQualification() {} func (*ColumnComputedDef) columnQualification() {} func (*ColumnFKConstraint) columnQualification() {} func (*ColumnFamilyConstraint) columnQualification() {} +func (*GeneratedAlwaysAsIdentity) columnQualification() {} +func (*GeneratedByDefAsIdentity) columnQualification() {} // ColumnCollation represents a COLLATE clause for a column. type ColumnCollation string @@ -763,6 +812,12 @@ type ColumnDefault struct { Expr Expr } +// GeneratedAlwaysAsIdentity represents a column generated always as identity. +type GeneratedAlwaysAsIdentity struct{} + +// GeneratedByDefAsIdentity represents a column generated by default as identity. +type GeneratedByDefAsIdentity struct{} + // NotNullConstraint represents NOT NULL on a column. type NotNullConstraint struct{} diff --git a/pkg/sql/sem/tree/pretty.go b/pkg/sql/sem/tree/pretty.go index 8901bf7ba519..337e8e27bcb9 100644 --- a/pkg/sql/sem/tree/pretty.go +++ b/pkg/sql/sem/tree/pretty.go @@ -1834,6 +1834,7 @@ func (node *ColumnTableDef) docRow(p *PrettyCfg) pretty.TableRow { // colname // type // [AS ( ... ) STORED] + // [GENERATED {ALWAYS|BY DEFAULT} AS IDENTITY] // [[CREATE [IF NOT EXISTS]] FAMILY [name]] // [[CONSTRAINT name] DEFAULT expr] // [[CONSTRAINT name] {NULL|NOT NULL}] @@ -1871,6 +1872,18 @@ func (node *ColumnTableDef) docRow(p *PrettyCfg) pretty.TableRow { )) } + // GENERATED ALWAYS/BY DEFAULT AS IDENTITY constraint. + if node.GeneratedIdentity.IsGeneratedAsIdentity { + var generatedConstraint pretty.Doc + switch node.GeneratedIdentity.GeneratedAsIdentityType { + case GeneratedAlways: + generatedConstraint = pretty.Keyword("GENERATED ALWAYS AS IDENTITY") + case GeneratedByDefault: + generatedConstraint = pretty.Keyword("GENERATED BY DEFAULT AS IDENTITY") + } + clauses = append(clauses, generatedConstraint) + } + // Column family. if node.HasColumnFamily() { d := pretty.Keyword("FAMILY")