Skip to content

Commit

Permalink
sql/parser: support GENERATED {ALWAYS | BY DEFAULT} AS IDENTITY syntax
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ZhouXing19 committed Aug 11, 2021
1 parent 26398d3 commit 2921969
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 27 deletions.
16 changes: 8 additions & 8 deletions docs/generated/sql/bnf/col_qualification.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
Expand Down
10 changes: 9 additions & 1 deletion docs/generated/sql/bnf/stmt_block.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/docgen/diagrams.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
Expand Down
2 changes: 2 additions & 0 deletions pkg/sql/parser/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
39 changes: 26 additions & 13 deletions pkg/sql/parser/sql.y
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -6168,11 +6169,11 @@ alter_schema_stmt:
// Table constraints:
// PRIMARY KEY ( <colnames...> ) [USING HASH WITH BUCKET_COUNT = <shard_buckets>]
// FOREIGN KEY ( <colnames...> ) REFERENCES <tablename> [( <colnames...> )] [ON DELETE {NO ACTION | RESTRICT}] [ON UPDATE {NO ACTION | RESTRICT}]
// UNIQUE ( <colnames... ) [{STORING | INCLUDE | COVERING} ( <colnames...> )] [<interleave>]
// UNIQUE ( <colnames...> ) [{STORING | INCLUDE | COVERING} ( <colnames...> )] [<interleave>]
// CHECK ( <expr> )
//
// Column qualifiers:
// [CONSTRAINT <constraintname>] {NULL | NOT NULL | NOT VISIBLE | UNIQUE | PRIMARY KEY | CHECK (<expr>) | DEFAULT <expr>}
// [CONSTRAINT <constraintname>] {NULL | NOT NULL | NOT VISIBLE | UNIQUE | PRIMARY KEY | CHECK (<expr>) | DEFAULT <expr> | GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [( <opt_sequence_option_list> )]}
// FAMILY <familyname>, CREATE [IF NOT EXISTS] FAMILY [<familyname>]
// REFERENCES <tablename> [( <colnames...> )] [ON DELETE {NO ACTION | RESTRICT}] [ON UPDATE {NO ACTION | RESTRICT}]
// COLLATE <collationname>
Expand Down Expand Up @@ -6703,28 +6704,36 @@ 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,
Col: tree.Name($3),
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 ( <expr> ) STORED or AS ( <expr> ) VIRTUAL")
return 1
}
}
| generated_always_as IDENTITY
{
$$.val = &tree.GeneratedAlwaysAsIdentity{}
}
| generated_by_default_as IDENTITY
{
$$.val = &tree.GeneratedByDefAsIdentity{}
}

opt_without_index:
WITHOUT INDEX
Expand All @@ -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
Expand Down
56 changes: 56 additions & 0 deletions pkg/sql/parser/testdata/alter_table
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,22 @@ 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 GENERATED ALWAYS AS IDENTITY -- normalized!
ALTER TABLE a ADD COLUMN b INT8 GENERATED ALWAYS AS IDENTITY -- fully parenthesized
ALTER TABLE a ADD COLUMN b INT8 GENERATED ALWAYS AS IDENTITY -- literals removed
ALTER TABLE _ ADD COLUMN _ INT8 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 GENERATED BY DEFAULT AS IDENTITY -- normalized!
ALTER TABLE a ADD COLUMN b INT8 GENERATED BY DEFAULT AS IDENTITY -- fully parenthesized
ALTER TABLE a ADD COLUMN b INT8 GENERATED BY DEFAULT AS IDENTITY -- literals removed
ALTER TABLE _ ADD COLUMN _ INT8 GENERATED BY DEFAULT AS IDENTITY -- identifiers removed

parse
ALTER TABLE t ALTER PRIMARY KEY USING COLUMNS (a, b, c)
----
Expand Down Expand Up @@ -1075,3 +1091,43 @@ DETAIL: source SQL:
ALTER PARTITION p OF TABLE tbl@* CONFIGURE ZONE USING num_replicas = 1
^
HINT: try ALTER PARTITION <partition> OF INDEX <tablename>@*

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 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 ( <expr> ) STORED or AS ( <expr> ) VIRTUAL
DETAIL: source SQL:
ALTER TABLE a ADD COLUMN b INT GENERATED ALWAYS AS (1+1) IDENTITY
^
81 changes: 81 additions & 0 deletions pkg/sql/parser/testdata/create_table
Original file line number Diff line number Diff line change
Expand Up @@ -2260,3 +2260,84 @@ 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 GENERATED ALWAYS AS IDENTITY, c INT8 GENERATED BY DEFAULT AS IDENTITY) -- normalized!
CREATE TABLE generated_always_as_identity_tb (a INT8 UNIQUE, b INT8 GENERATED ALWAYS AS IDENTITY, c INT8 GENERATED BY DEFAULT AS IDENTITY) -- fully parenthesized
CREATE TABLE generated_always_as_identity_tb (a INT8 UNIQUE, b INT8 GENERATED ALWAYS AS IDENTITY, c INT8 GENERATED BY DEFAULT AS IDENTITY) -- literals removed
CREATE TABLE _ (_ INT8 UNIQUE, _ INT8 GENERATED ALWAYS AS IDENTITY, _ INT8 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,
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 ( <expr> ) STORED or AS ( <expr> ) VIRTUAL
DETAIL: source SQL:
CREATE TABLE generated_error (
a INT,
b INT GENERATED ALWAYS AS (a + 10) IDENTITY
^
Loading

0 comments on commit 2921969

Please sign in to comment.