From 43f6b2cc7467cea2a9dff3d3f4b5d07d30aae8b9 Mon Sep 17 00:00:00 2001 From: Rohan Yadav Date: Mon, 25 May 2020 00:16:12 -0400 Subject: [PATCH] sql: add the `GENERATED ALWAYS` alias for computed columns Fixes #42418. This PR adds an alias to create computed columns with the Postgres syntax that uses `GENERATED ALWAYS`. Due an unfortunate ambiguity caused by the `CREATE FAMILY` column qualifications, the parser cannot tell with a single lookahead token what to do in the case of ``` CREATE TABLE t (x INT CREATE FAMILY GENERATED...) ``` Here, with only 1 token of lookahead, we don't know whether GENERATED is that start of the generated computed clause, or the name of the family. To rectify this, we use the same trick used in other cases, where the lexer will generate a new `GENERATED_ALWAYS` token that allows us to get past the ambiguity here. Release note (sql change): Add the Postgres syntax `GENERATED ALWAYS` alias for computed column construction. --- docs/generated/sql/bnf/stmt_block.bnf | 1 + pkg/sql/lex/predicates.go | 1 + pkg/sql/logictest/testdata/logic_test/computed | 14 ++++++++++++++ pkg/sql/parser/lexer.go | 7 ++++++- pkg/sql/parser/parse_test.go | 1 + pkg/sql/parser/sql.y | 8 +++++--- 6 files changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index c6c5ec34e22e..6afd89faaa38 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -2503,6 +2503,7 @@ opt_name_parens ::= generated_as ::= 'AS' + | 'GENERATED_ALWAYS' 'ALWAYS' 'AS' reference_action ::= 'NO' 'ACTION' diff --git a/pkg/sql/lex/predicates.go b/pkg/sql/lex/predicates.go index 4800ff280468..81fefc7ae6b3 100644 --- a/pkg/sql/lex/predicates.go +++ b/pkg/sql/lex/predicates.go @@ -54,6 +54,7 @@ func init() { "ordinality", "similar", "time", + "generated", } { reservedOrLookaheadKeywords[s] = struct{}{} } diff --git a/pkg/sql/logictest/testdata/logic_test/computed b/pkg/sql/logictest/testdata/logic_test/computed index 685ca58c6b7f..538acf02a14a 100644 --- a/pkg/sql/logictest/testdata/logic_test/computed +++ b/pkg/sql/logictest/testdata/logic_test/computed @@ -776,3 +776,17 @@ query TT SELECT * FROM t34901 ---- a ab + +# Regression test for #42418. +statement ok +CREATE TABLE t42418 (x INT GENERATED ALWAYS AS (1) STORED); +ALTER TABLE t42418 ADD COLUMN y INT GENERATED ALWAYS AS (1) STORED + +query TT +SHOW CREATE t42418 +---- +t42418 CREATE TABLE t42418 ( + x INT8 NULL AS (1) STORED, + y INT8 NULL AS (1) STORED, + FAMILY "primary" (x, rowid, y) +) diff --git a/pkg/sql/parser/lexer.go b/pkg/sql/parser/lexer.go index 91ae60203dec..6cd856ac4cd3 100644 --- a/pkg/sql/parser/lexer.go +++ b/pkg/sql/parser/lexer.go @@ -80,7 +80,7 @@ func (l *lexer) Lex(lval *sqlSymType) int { *lval = l.tokens[l.lastPos] switch lval.id { - case NOT, WITH, AS: + case NOT, WITH, AS, GENERATED: nextID := int32(0) if l.lastPos+1 < len(l.tokens) { nextID = l.tokens[l.lastPos+1].id @@ -98,6 +98,11 @@ func (l *lexer) Lex(lval *sqlSymType) int { case BETWEEN, IN, LIKE, ILIKE, SIMILAR: lval.id = NOT_LA } + case GENERATED: + switch nextID { + case ALWAYS: + lval.id = GENERATED_ALWAYS + } case WITH: switch nextID { diff --git a/pkg/sql/parser/parse_test.go b/pkg/sql/parser/parse_test.go index d41f86a54f26..016d658fd33f 100644 --- a/pkg/sql/parser/parse_test.go +++ b/pkg/sql/parser/parse_test.go @@ -2328,6 +2328,7 @@ $function$`, `CREATE TABLE a (b INT8, c STRING, FOREIGN KEY (b, c) REFERENCES other (x, y) MATCH SIMPLE ON DELETE CASCADE ON UPDATE SET NULL)`, `CREATE TABLE a (b INT8, c STRING, FOREIGN KEY (b, c) REFERENCES other (x, y) ON DELETE CASCADE ON UPDATE SET NULL)`, }, + {`CREATE TABLE a (b INT8 GENERATED ALWAYS AS (a + b) STORED)`, `CREATE TABLE a (b INT8 AS (a + b) STORED)`}, {`ALTER TABLE a ALTER b DROP STORED`, `ALTER TABLE a ALTER COLUMN b DROP STORED`}, {`ALTER TABLE a ADD b INT8`, `ALTER TABLE a ADD COLUMN b INT8`}, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index e2542ea3c1b6..13ecd5664f0a 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -647,8 +647,10 @@ func (u *sqlSymUnion) alterTypeAddValuePlacement() *tree.AlterTypeAddValuePlacem // 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 // NOT, at least with respect to their left-hand subexpression. WITH_LA is -// needed to make the grammar LALR(1). -%token NOT_LA WITH_LA AS_LA +// needed to make the grammar LALR(1). GENERATED_ALWAYS is needed to support +// the Postgres syntax for computed columns along with our family related +// extensions (CREATE FAMILY/CREATE FAMILY family_name). +%token NOT_LA WITH_LA AS_LA GENERATED_ALWAYS %union { id int32 @@ -4987,7 +4989,7 @@ col_qualification_elem: // GENERATED ALWAYS is a noise word for compatibility with Postgres. generated_as: AS {} -// GENERATED ALWAYS AS {} +| GENERATED_ALWAYS ALWAYS AS {} index_def: