From 52ab3d21f637a81fe8a8dfd0b0a9e48f5032724a Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Thu, 16 Dec 2021 20:33:54 +0800 Subject: [PATCH 1/2] support with config clause inside load data --- zetasql/parser/ast_node_kind.h | 1 + zetasql/parser/bison_parser.y | 15 +++++++++++-- zetasql/parser/flex_tokenizer.l | 1 + zetasql/parser/gen_parse_tree.py | 3 +++ zetasql/parser/keywords.cc | 1 + zetasql/parser/parse_tree.cc | 1 + zetasql/parser/parse_tree_manual.h | 23 +++++++++++++++++++ zetasql/parser/testdata/load.test | 36 ++++++++++++++++++++++++++++++ zetasql/parser/unparser.cc | 8 +++++++ zetasql/parser/unparser.h | 1 + 10 files changed, 88 insertions(+), 2 deletions(-) diff --git a/zetasql/parser/ast_node_kind.h b/zetasql/parser/ast_node_kind.h index 2d1de616b..b00a46063 100755 --- a/zetasql/parser/ast_node_kind.h +++ b/zetasql/parser/ast_node_kind.h @@ -320,6 +320,7 @@ enum ASTNodeKind { AST_SELECT_INTO_STATEMENT, AST_ESCAPED_EXPRESSION, AST_STOP_STATEMENT, + AST_WITH_CONFIG_CLAUSE, AST_WITH_WEIGHT, kLastASTNodeKind = AST_WITH_WEIGHT }; diff --git a/zetasql/parser/bison_parser.y b/zetasql/parser/bison_parser.y index 053251d54..9fb6ab05a 100644 --- a/zetasql/parser/bison_parser.y +++ b/zetasql/parser/bison_parser.y @@ -806,6 +806,7 @@ using zetasql::ASTDropStatement; %token KW_COLUMNS "COLUMNS" %token KW_COMMIT "COMMIT" %token KW_CONNECTION "CONNECTION" +%token KW_CONFIG "CONFIG" %token KW_CONTINUE "CONTINUE" %token KW_CONST "CONST" %token KW_CONSTANT "CONSTANT" @@ -1182,6 +1183,7 @@ using zetasql::ASTDropStatement; %type opt_like_string_literal %type opt_like_path_expression %type opt_limit_offset_clause +%type opt_with_config_clause %type opt_maxsize %type opt_on_or_using_clause_list %type on_or_using_clause_list @@ -3420,9 +3422,9 @@ select_into_statement: ; load_data_statement: - "LOAD" "DATA" "INFILE" string_literal "INTO" "TABLE" path_expression opt_options_list + "LOAD" "DATA" "INFILE" string_literal "INTO" "TABLE" path_expression opt_options_list opt_with_config_clause { - $$ = MAKE_NODE(ASTLoadDataStatement, @$, {$4, $7, $8}); + $$ = MAKE_NODE(ASTLoadDataStatement, @$, {$4, $7, $8, $9}); } ; @@ -5074,6 +5076,14 @@ opt_limit_offset_clause: | /* Nothing */ { $$ = nullptr; } ; +opt_with_config_clause: + "WITH" "CONFIG" options_list + { + $$ = MAKE_NODE(ASTWithConfigClause, @$, {$3}); + } + | /* Nothing */ { $$ = nullptr; } + ; + opt_having_modifier: "HAVING" "MAX" expression { @@ -7365,6 +7375,7 @@ keyword_as_identifier: | "COLUMN" | "COLUMNS" | "COMMIT" + | "CONFIG" | "CONNECTION" | "CONST" | "CONSTANT" diff --git a/zetasql/parser/flex_tokenizer.l b/zetasql/parser/flex_tokenizer.l index ecb741abd..9638bab59 100644 --- a/zetasql/parser/flex_tokenizer.l +++ b/zetasql/parser/flex_tokenizer.l @@ -392,6 +392,7 @@ collate { return BisonParserImpl::token::KW_COLLATE; } column { return BisonParserImpl::token::KW_COLUMN; } columns { return BisonParserImpl::token::KW_COLUMNS; } commit { return BisonParserImpl::token::KW_COMMIT; } +config { return BisonParserImpl::token::KW_CONFIG; } connection { return BisonParserImpl::token::KW_CONNECTION; } const { return BisonParserImpl::token::KW_CONST; } constant { return BisonParserImpl::token::KW_CONSTANT; } diff --git a/zetasql/parser/gen_parse_tree.py b/zetasql/parser/gen_parse_tree.py index 1d947e23f..828bdf493 100644 --- a/zetasql/parser/gen_parse_tree.py +++ b/zetasql/parser/gen_parse_tree.py @@ -376,6 +376,9 @@ def main(argv): Field( 'window_clause', 'ASTWindowClause'), + Field( + 'with_config_clause', + 'ASTWithConfigClause'), ]) gen.AddNode( diff --git a/zetasql/parser/keywords.cc b/zetasql/parser/keywords.cc index 5176f592b..2beb79284 100644 --- a/zetasql/parser/keywords.cc +++ b/zetasql/parser/keywords.cc @@ -84,6 +84,7 @@ constexpr KeywordInfoPOD kAllKeywords[] = { {"column", KW_COLUMN}, {"columns", KW_COLUMNS}, {"commit", KW_COMMIT}, + {"config", KW_CONFIG}, {"connection", KW_CONNECTION}, {"const", KW_CONST}, {"constant", KW_CONSTANT}, diff --git a/zetasql/parser/parse_tree.cc b/zetasql/parser/parse_tree.cc index b0dd66bd3..866d1e7af 100644 --- a/zetasql/parser/parse_tree.cc +++ b/zetasql/parser/parse_tree.cc @@ -356,6 +356,7 @@ static absl::flat_hash_map CreateNodeNamesMap() { map[AST_SELECT_INTO_STATEMENT] = "SelectIntoStatement"; map[AST_ESCAPED_EXPRESSION] = "EscapedExpression"; map[AST_STOP_STATEMENT] = "StopStatement"; + map[AST_WITH_CONFIG_CLAUSE] = "WithConfigClause"; map[AST_WITH_WEIGHT] = "WithWeight"; map[AST_WITH_PARTITION_COLUMNS_CLAUSE] = "WithPartitionColumnsClause"; for (int kind = kFirstASTNodeKind; kind <= kLastASTNodeKind; diff --git a/zetasql/parser/parse_tree_manual.h b/zetasql/parser/parse_tree_manual.h index 9bf983a09..9a59dd3b4 100644 --- a/zetasql/parser/parse_tree_manual.h +++ b/zetasql/parser/parse_tree_manual.h @@ -805,6 +805,7 @@ class ASTLoadDataStatement final : public ASTLoadStatement { const ASTStringLiteral* in_file() const { return in_file_; } const ASTPathExpression* table_name() const { return table_name_; } const ASTOptionsList* options_list() const { return options_list_; } + const ASTWithConfigClause* opt_with_config() const { return opt_with_config_; } private: void InitFields() final { @@ -812,11 +813,13 @@ class ASTLoadDataStatement final : public ASTLoadStatement { fl.AddRequired(&in_file_); fl.AddRequired(&table_name_); fl.AddOptional(&options_list_, AST_OPTIONS_LIST); + fl.AddOptional(&opt_with_config_, AST_WITH_CONFIG_CLAUSE); } const ASTStringLiteral* in_file_ = nullptr; const ASTPathExpression* table_name_ = nullptr; const ASTOptionsList* options_list_ = nullptr; + const ASTWithConfigClause* opt_with_config_ = nullptr; }; class ASTUseStatement final : public ASTStatement { @@ -1886,6 +1889,26 @@ class ASTLimitOffset final : public ASTNode { const ASTExpression* offset_ = nullptr; }; +class ASTWithConfigClause final : public ASTNode { + public: + static constexpr ASTNodeKind kConcreteNodeKind = AST_WITH_CONFIG_CLAUSE; + + ASTWithConfigClause() : ASTNode(kConcreteNodeKind) {} + void Accept(ParseTreeVisitor* visitor, void* data) const override; + zetasql_base::StatusOr Accept( + NonRecursiveParseTreeVisitor* visitor) const override; + + const ASTOptionsList* options_list() const { return options_list_; } + + private: + void InitFields() final { + FieldLoader fl(this); + fl.AddRequired(&options_list_); + } + + const ASTOptionsList* options_list_ = nullptr; +}; + class ASTHavingModifier final : public ASTNode { public: static constexpr ASTNodeKind kConcreteNodeKind = AST_HAVING_MODIFIER; diff --git a/zetasql/parser/testdata/load.test b/zetasql/parser/testdata/load.test index bcadb3f37..f8f61b8a4 100644 --- a/zetasql/parser/testdata/load.test +++ b/zetasql/parser/testdata/load.test @@ -36,3 +36,39 @@ ERROR: Syntax error: Unexpected ";" [at 1:40] load data infile 'data.file' into table; ^ == + +# load data with config +load data infile 'data.csv' into table foo with config (charset = 'utf-8'); +-- +LoadDataStatement [0-74] + StringLiteral('data.csv') [17-27] + PathExpression [39-42] + Identifier(foo) [39-42] + WithConfigClause [43-74] + OptionsList [55-74] + OptionsEntry [56-73] + Identifier(charset) [56-63] + StringLiteral('utf-8') [66-73] +-- +LOAD DATA INFILE 'data.csv' INTO TABLE foo WITH CONFIG(charset = 'utf-8') +== + +# load data with config +load data infile 'data.csv' into table foo options (charset = 'utf-8') with config (charset = 'utf-8'); +-- +LoadDataStatement [0-102] + StringLiteral('data.csv') [17-27] + PathExpression [39-42] + Identifier(foo) [39-42] + OptionsList [51-70] + OptionsEntry [52-69] + Identifier(charset) [52-59] + StringLiteral('utf-8') [62-69] + WithConfigClause [71-102] + OptionsList [83-102] + OptionsEntry [84-101] + Identifier(charset) [84-91] + StringLiteral('utf-8') [94-101] +-- +LOAD DATA INFILE 'data.csv' INTO TABLE foo OPTIONS(charset = 'utf-8') WITH CONFIG(charset = 'utf-8') +== diff --git a/zetasql/parser/unparser.cc b/zetasql/parser/unparser.cc index 626581fa9..2eb0a4bba 100644 --- a/zetasql/parser/unparser.cc +++ b/zetasql/parser/unparser.cc @@ -1090,6 +1090,9 @@ void Unparser::visitASTLoadDataStatement(const ASTLoadDataStatement* node, void* print("OPTIONS"); node->options_list()->Accept(this, data); } + if (node->opt_with_config() != nullptr) { + node->opt_with_config()->Accept(this, data); + } } void Unparser::visitASTSelectIntoStatement(const ASTSelectIntoStatement* node, void* data) { @@ -1465,6 +1468,11 @@ void Unparser::visitASTLimitOffset(const ASTLimitOffset* node, void* data) { UnparseChildrenWithSeparator(node, data, "OFFSET"); } +void Unparser::visitASTWithConfigClause(const ASTWithConfigClause* node, void* data) { + print("WITH CONFIG"); + node->options_list()->Accept(this, data); +} + void Unparser::visitASTHavingModifier(const ASTHavingModifier* node, void* data) { println(); diff --git a/zetasql/parser/unparser.h b/zetasql/parser/unparser.h index 4d74c386d..bf17eac34 100644 --- a/zetasql/parser/unparser.h +++ b/zetasql/parser/unparser.h @@ -296,6 +296,7 @@ class Unparser : public ParseTreeVisitor { void visitASTOrderBy(const ASTOrderBy* node, void* data) override; void visitASTLambda(const ASTLambda* node, void* data) override; void visitASTLimitOffset(const ASTLimitOffset* node, void* data) override; + void visitASTWithConfigClause(const ASTWithConfigClause* node, void* data) override; void visitASTHavingModifier(const ASTHavingModifier* node, void* data) override; void visitASTClampedBetweenModifier(const ASTClampedBetweenModifier* node, From 8088a0e0bb43bd3147d82f3589ddf8d6506186f9 Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Fri, 17 Dec 2021 11:42:56 +0800 Subject: [PATCH 2/2] refactor: config clause for query/select_into/load_data resolve https://github.com/4paradigm/OpenMLDB/issues/907 --- zetasql/parser/ast_node_kind.h | 2 +- zetasql/parser/bison_parser.y | 22 ++++----- zetasql/parser/gen_parse_tree.py | 6 +-- zetasql/parser/keywords.cc | 2 +- zetasql/parser/keywords_test.cc | 2 +- zetasql/parser/parse_tree.cc | 2 +- zetasql/parser/parse_tree_manual.h | 15 +++--- zetasql/parser/testdata/from.test | 59 ++++++++++++++++++++++++ zetasql/parser/testdata/load.test | 34 +++++++------- zetasql/parser/testdata/select_into.test | 46 ++++++++++++++++++ zetasql/parser/unparser.cc | 17 +++++-- zetasql/parser/unparser.h | 2 +- 12 files changed, 164 insertions(+), 45 deletions(-) diff --git a/zetasql/parser/ast_node_kind.h b/zetasql/parser/ast_node_kind.h index b00a46063..4489d3c1b 100755 --- a/zetasql/parser/ast_node_kind.h +++ b/zetasql/parser/ast_node_kind.h @@ -320,7 +320,7 @@ enum ASTNodeKind { AST_SELECT_INTO_STATEMENT, AST_ESCAPED_EXPRESSION, AST_STOP_STATEMENT, - AST_WITH_CONFIG_CLAUSE, + AST_CONFIG_CLAUSE, AST_WITH_WEIGHT, kLastASTNodeKind = AST_WITH_WEIGHT }; diff --git a/zetasql/parser/bison_parser.y b/zetasql/parser/bison_parser.y index 9fb6ab05a..bb1c7dcca 100644 --- a/zetasql/parser/bison_parser.y +++ b/zetasql/parser/bison_parser.y @@ -668,6 +668,7 @@ using zetasql::ASTDropStatement; %token KW_CASE "CASE" %token KW_CAST "CAST" %token KW_COLLATE "COLLATE" +%token KW_CONFIG "CONFIG" %token KW_CREATE "CREATE" %token KW_CROSS "CROSS" %token KW_CURRENT "CURRENT" @@ -806,7 +807,6 @@ using zetasql::ASTDropStatement; %token KW_COLUMNS "COLUMNS" %token KW_COMMIT "COMMIT" %token KW_CONNECTION "CONNECTION" -%token KW_CONFIG "CONFIG" %token KW_CONTINUE "CONTINUE" %token KW_CONST "CONST" %token KW_CONSTANT "CONSTANT" @@ -1183,7 +1183,7 @@ using zetasql::ASTDropStatement; %type opt_like_string_literal %type opt_like_path_expression %type opt_limit_offset_clause -%type opt_with_config_clause +%type opt_config_clause %type opt_maxsize %type opt_on_or_using_clause_list %type on_or_using_clause_list @@ -1607,9 +1607,9 @@ sql_statement_body: ; query_statement: - query + query opt_config_clause { - $$ = MAKE_NODE(ASTQueryStatement, @$, {$1}); + $$ = MAKE_NODE(ASTQueryStatement, @$, {$1, $2}); } ; @@ -3415,14 +3415,14 @@ into_statement: ; select_into_statement: - query "INTO" "OUTFILE" string_literal opt_options_list + query "INTO" "OUTFILE" string_literal opt_options_list opt_config_clause { - $$ = MAKE_NODE(ASTSelectIntoStatement, @$, {$1, $4, $5}) + $$ = MAKE_NODE(ASTSelectIntoStatement, @$, {$1, $4, $5, $6}) } ; load_data_statement: - "LOAD" "DATA" "INFILE" string_literal "INTO" "TABLE" path_expression opt_options_list opt_with_config_clause + "LOAD" "DATA" "INFILE" string_literal "INTO" "TABLE" path_expression opt_options_list opt_config_clause { $$ = MAKE_NODE(ASTLoadDataStatement, @$, {$4, $7, $8, $9}); } @@ -5076,10 +5076,10 @@ opt_limit_offset_clause: | /* Nothing */ { $$ = nullptr; } ; -opt_with_config_clause: - "WITH" "CONFIG" options_list +opt_config_clause: + "CONFIG" options_list { - $$ = MAKE_NODE(ASTWithConfigClause, @$, {$3}); + $$ = MAKE_NODE(ASTConfigClause, @$, {$2}); } | /* Nothing */ { $$ = nullptr; } ; @@ -7251,6 +7251,7 @@ reserved_keyword_rule: | "CASE" | "CAST" | "COLLATE" + | "CONFIG" | "CREATE" | "CROSS" | "CURRENT" @@ -7375,7 +7376,6 @@ keyword_as_identifier: | "COLUMN" | "COLUMNS" | "COMMIT" - | "CONFIG" | "CONNECTION" | "CONST" | "CONSTANT" diff --git a/zetasql/parser/gen_parse_tree.py b/zetasql/parser/gen_parse_tree.py index 828bdf493..ea9c283d7 100644 --- a/zetasql/parser/gen_parse_tree.py +++ b/zetasql/parser/gen_parse_tree.py @@ -335,6 +335,9 @@ def main(argv): 'query', 'ASTQuery', field_loader=FieldLoaderMethod.REQUIRED), + Field( + 'config_clause', + 'ASTConfigClause'), ]) gen.AddNode( @@ -376,9 +379,6 @@ def main(argv): Field( 'window_clause', 'ASTWindowClause'), - Field( - 'with_config_clause', - 'ASTWithConfigClause'), ]) gen.AddNode( diff --git a/zetasql/parser/keywords.cc b/zetasql/parser/keywords.cc index 2beb79284..a0b319328 100644 --- a/zetasql/parser/keywords.cc +++ b/zetasql/parser/keywords.cc @@ -84,7 +84,7 @@ constexpr KeywordInfoPOD kAllKeywords[] = { {"column", KW_COLUMN}, {"columns", KW_COLUMNS}, {"commit", KW_COMMIT}, - {"config", KW_CONFIG}, + {"config", KW_CONFIG, KeywordInfo::kReserved}, {"connection", KW_CONNECTION}, {"const", KW_CONST}, {"constant", KW_CONSTANT}, diff --git a/zetasql/parser/keywords_test.cc b/zetasql/parser/keywords_test.cc index 04631d249..b196b879d 100644 --- a/zetasql/parser/keywords_test.cc +++ b/zetasql/parser/keywords_test.cc @@ -237,7 +237,7 @@ TEST(ParserTest, DontAddNewReservedKeywords) { // allows new queries to work that will not work on older code. // Before changing this, co-ordinate with all engines to make sure the change // is done safely. - EXPECT_EQ(104 /* CAUTION */, num_reserved); + EXPECT_EQ(105 /* CAUTION */, num_reserved); } } // namespace diff --git a/zetasql/parser/parse_tree.cc b/zetasql/parser/parse_tree.cc index 866d1e7af..a3dbe82ef 100644 --- a/zetasql/parser/parse_tree.cc +++ b/zetasql/parser/parse_tree.cc @@ -356,7 +356,7 @@ static absl::flat_hash_map CreateNodeNamesMap() { map[AST_SELECT_INTO_STATEMENT] = "SelectIntoStatement"; map[AST_ESCAPED_EXPRESSION] = "EscapedExpression"; map[AST_STOP_STATEMENT] = "StopStatement"; - map[AST_WITH_CONFIG_CLAUSE] = "WithConfigClause"; + map[AST_CONFIG_CLAUSE] = "ConfigClause"; map[AST_WITH_WEIGHT] = "WithWeight"; map[AST_WITH_PARTITION_COLUMNS_CLAUSE] = "WithPartitionColumnsClause"; for (int kind = kFirstASTNodeKind; kind <= kLastASTNodeKind; diff --git a/zetasql/parser/parse_tree_manual.h b/zetasql/parser/parse_tree_manual.h index 9a59dd3b4..53314248a 100644 --- a/zetasql/parser/parse_tree_manual.h +++ b/zetasql/parser/parse_tree_manual.h @@ -772,6 +772,7 @@ class ASTSelectIntoStatement final : public ASTStatement { const ASTQuery* query() const { return query_; } const ASTStringLiteral* out_file() const { return out_file_; } const ASTOptionsList* options_list() const { return options_list_; } + const ASTConfigClause* opt_config() const { return opt_config_; } std::string UnparseQuery() const; @@ -781,11 +782,13 @@ class ASTSelectIntoStatement final : public ASTStatement { fl.AddRequired(&query_); fl.AddRequired(&out_file_); fl.AddOptional(&options_list_, AST_OPTIONS_LIST); + fl.AddOptional(&opt_config_, AST_CONFIG_CLAUSE); } const ASTQuery* query_ = nullptr; const ASTStringLiteral* out_file_ = nullptr; const ASTOptionsList* options_list_ = nullptr; + const ASTConfigClause* opt_config_ = nullptr; }; // super class of all load statements class ASTLoadStatement : public ASTStatement { @@ -805,7 +808,7 @@ class ASTLoadDataStatement final : public ASTLoadStatement { const ASTStringLiteral* in_file() const { return in_file_; } const ASTPathExpression* table_name() const { return table_name_; } const ASTOptionsList* options_list() const { return options_list_; } - const ASTWithConfigClause* opt_with_config() const { return opt_with_config_; } + const ASTConfigClause* opt_config() const { return opt_config_; } private: void InitFields() final { @@ -813,13 +816,13 @@ class ASTLoadDataStatement final : public ASTLoadStatement { fl.AddRequired(&in_file_); fl.AddRequired(&table_name_); fl.AddOptional(&options_list_, AST_OPTIONS_LIST); - fl.AddOptional(&opt_with_config_, AST_WITH_CONFIG_CLAUSE); + fl.AddOptional(&opt_config_, AST_CONFIG_CLAUSE); } const ASTStringLiteral* in_file_ = nullptr; const ASTPathExpression* table_name_ = nullptr; const ASTOptionsList* options_list_ = nullptr; - const ASTWithConfigClause* opt_with_config_ = nullptr; + const ASTConfigClause* opt_config_ = nullptr; }; class ASTUseStatement final : public ASTStatement { @@ -1889,11 +1892,11 @@ class ASTLimitOffset final : public ASTNode { const ASTExpression* offset_ = nullptr; }; -class ASTWithConfigClause final : public ASTNode { +class ASTConfigClause final : public ASTNode { public: - static constexpr ASTNodeKind kConcreteNodeKind = AST_WITH_CONFIG_CLAUSE; + static constexpr ASTNodeKind kConcreteNodeKind = AST_CONFIG_CLAUSE; - ASTWithConfigClause() : ASTNode(kConcreteNodeKind) {} + ASTConfigClause() : ASTNode(kConcreteNodeKind) {} void Accept(ParseTreeVisitor* visitor, void* data) const override; zetasql_base::StatusOr Accept( NonRecursiveParseTreeVisitor* visitor) const override; diff --git a/zetasql/parser/testdata/from.test b/zetasql/parser/testdata/from.test index beb9185f3..891359622 100644 --- a/zetasql/parser/testdata/from.test +++ b/zetasql/parser/testdata/from.test @@ -1934,6 +1934,65 @@ SELECT 'foo' AS b == +# query statement with config clause +select T1.a as a, T2.b as b from Table1 as T1 last join Table2 T2 using (c, d) config ( a = 'k' ); +-- +QueryStatement [0-97] + Query [0-78] + Select [0-78] + SelectList [7-27] + SelectColumn [7-16] + PathExpression [7-11] + Identifier(T1) [7-9] + Identifier(a) [10-11] + Alias [12-16] + Identifier(a) [15-16] + SelectColumn [18-27] + PathExpression [18-22] + Identifier(T2) [18-20] + Identifier(b) [21-22] + Alias [23-27] + Identifier(b) [26-27] + FromClause [28-78] + Join(LAST) [46-78] + TablePathExpression [33-45] + PathExpression [33-39] + Identifier(Table1) [33-39] + Alias [40-45] + Identifier(T1) [43-45] + TablePathExpression [56-65] + PathExpression [56-62] + Identifier(Table2) [56-62] + Alias [63-65] + Identifier(T2) [63-65] + UsingClause [66-78] + Identifier(c) [73-74] + Identifier(d) [76-77] + ConfigClause [79-97] + OptionsList [86-97] + OptionsEntry [88-95] + Identifier(a) [88-89] + StringLiteral('k') [92-95] +-- +SELECT + T1.a AS a, + T2.b AS b +FROM + Table1 AS T1 + LAST JOIN + Table2 AS T2 + USING(c, d) +CONFIG(a = 'k') +== + +# config clause can't appear inside subquery +select T1.a as a, T2.b as b from Table1 as T1 config (k='k') last join Table2 T2 using (c, d); +-- +ERROR: Syntax error: Expected end of input but got keyword LAST [at 1:62] +...a as a, T2.b as b from Table1 as T1 config (k='k') last join Table2 T2 usi... + ^ +== + # Empty SELECT list select {{|@{a=b}|ALL|DISTINCT|AS foo|@{a=b} DISTINCT AS foo}} from t -- diff --git a/zetasql/parser/testdata/load.test b/zetasql/parser/testdata/load.test index f8f61b8a4..acc8b7bc4 100644 --- a/zetasql/parser/testdata/load.test +++ b/zetasql/parser/testdata/load.test @@ -38,25 +38,26 @@ load data infile 'data.file' into table; == # load data with config -load data infile 'data.csv' into table foo with config (charset = 'utf-8'); +load data infile 'data.csv' into table foo config (charset = 'utf-8'); -- -LoadDataStatement [0-74] +LoadDataStatement [0-69] StringLiteral('data.csv') [17-27] PathExpression [39-42] Identifier(foo) [39-42] - WithConfigClause [43-74] - OptionsList [55-74] - OptionsEntry [56-73] - Identifier(charset) [56-63] - StringLiteral('utf-8') [66-73] + ConfigClause [43-69] + OptionsList [50-69] + OptionsEntry [51-68] + Identifier(charset) [51-58] + StringLiteral('utf-8') [61-68] -- -LOAD DATA INFILE 'data.csv' INTO TABLE foo WITH CONFIG(charset = 'utf-8') +LOAD DATA INFILE 'data.csv' INTO TABLE foo +CONFIG(charset = 'utf-8') == # load data with config -load data infile 'data.csv' into table foo options (charset = 'utf-8') with config (charset = 'utf-8'); +load data infile 'data.csv' into table foo options (charset = 'utf-8') config (charset = 'utf-8'); -- -LoadDataStatement [0-102] +LoadDataStatement [0-97] StringLiteral('data.csv') [17-27] PathExpression [39-42] Identifier(foo) [39-42] @@ -64,11 +65,12 @@ LoadDataStatement [0-102] OptionsEntry [52-69] Identifier(charset) [52-59] StringLiteral('utf-8') [62-69] - WithConfigClause [71-102] - OptionsList [83-102] - OptionsEntry [84-101] - Identifier(charset) [84-91] - StringLiteral('utf-8') [94-101] + ConfigClause [71-97] + OptionsList [78-97] + OptionsEntry [79-96] + Identifier(charset) [79-86] + StringLiteral('utf-8') [89-96] -- -LOAD DATA INFILE 'data.csv' INTO TABLE foo OPTIONS(charset = 'utf-8') WITH CONFIG(charset = 'utf-8') +LOAD DATA INFILE 'data.csv' INTO TABLE foo OPTIONS(charset = 'utf-8') +CONFIG(charset = 'utf-8') == diff --git a/zetasql/parser/testdata/select_into.test b/zetasql/parser/testdata/select_into.test index a45b4330c..ed206dd8e 100644 --- a/zetasql/parser/testdata/select_into.test +++ b/zetasql/parser/testdata/select_into.test @@ -85,6 +85,52 @@ FROM INTO OUTFILE 'data.csv' OPTIONS(charset = 'utf-8') == +# select into with config clause +select col1, col2 from db1.t1 into outfile 'data.csv' options (charset = 'utf-8') config (val=12); +-- +SelectIntoStatement [0-97] + Query [0-29] + Select [0-29] + SelectList [7-17] + SelectColumn [7-11] + PathExpression [7-11] + Identifier(col1) [7-11] + SelectColumn [13-17] + PathExpression [13-17] + Identifier(col2) [13-17] + FromClause [18-29] + TablePathExpression [23-29] + PathExpression [23-29] + Identifier(db1) [23-26] + Identifier(t1) [27-29] + StringLiteral('data.csv') [43-53] + OptionsList [62-81] + OptionsEntry [63-80] + Identifier(charset) [63-70] + StringLiteral('utf-8') [73-80] + ConfigClause [82-97] + OptionsList [89-97] + OptionsEntry [90-96] + Identifier(val) [90-93] + IntLiteral(12) [94-96] +-- +SELECT + col1, + col2 +FROM + db1.t1 +INTO OUTFILE 'data.csv' OPTIONS(charset = 'utf-8') +CONFIG(val = 12) +== + +# config clause can't appear inside subquery +select T1.a as a, T2.b as b from Table1 as T1 config (k='k') last join Table2 T2 using (c, d) into outfile 'data.csv'; +-- +ERROR: Syntax error: Expected end of input but got keyword LAST [at 1:62] +...a as a, T2.b as b from Table1 as T1 config (k='k') last join Table2 T2 usi... + ^ +== + select col1, col2 from db1.t1 into outfile; -- ERROR: Syntax error: Expected string literal but got ";" [at 1:43] diff --git a/zetasql/parser/unparser.cc b/zetasql/parser/unparser.cc index 2eb0a4bba..d4a81daf4 100644 --- a/zetasql/parser/unparser.cc +++ b/zetasql/parser/unparser.cc @@ -237,6 +237,10 @@ void Unparser::visitASTExplainStatement(const ASTExplainStatement* node, void Unparser::visitASTQueryStatement(const ASTQueryStatement* node, void* data) { visitASTQuery(node->query(), data); + if (node->config_clause() != nullptr) { + println(); + node->config_clause()->Accept(this, data); + } } void Unparser::visitASTFunctionParameter( @@ -1090,8 +1094,9 @@ void Unparser::visitASTLoadDataStatement(const ASTLoadDataStatement* node, void* print("OPTIONS"); node->options_list()->Accept(this, data); } - if (node->opt_with_config() != nullptr) { - node->opt_with_config()->Accept(this, data); + if (node->opt_config() != nullptr) { + println(); + node->opt_config()->Accept(this, data); } } @@ -1103,6 +1108,10 @@ void Unparser::visitASTSelectIntoStatement(const ASTSelectIntoStatement* node, v print("OPTIONS"); node->options_list()->Accept(this, data); } + if (node->opt_config() != nullptr) { + println(); + node->opt_config()->Accept(this, data); + } } void Unparser::visitASTModuleStatement(const ASTModuleStatement* node, void* data) { @@ -1468,8 +1477,8 @@ void Unparser::visitASTLimitOffset(const ASTLimitOffset* node, void* data) { UnparseChildrenWithSeparator(node, data, "OFFSET"); } -void Unparser::visitASTWithConfigClause(const ASTWithConfigClause* node, void* data) { - print("WITH CONFIG"); +void Unparser::visitASTConfigClause(const ASTConfigClause* node, void* data) { + print("CONFIG"); node->options_list()->Accept(this, data); } diff --git a/zetasql/parser/unparser.h b/zetasql/parser/unparser.h index bf17eac34..68e67c650 100644 --- a/zetasql/parser/unparser.h +++ b/zetasql/parser/unparser.h @@ -296,7 +296,7 @@ class Unparser : public ParseTreeVisitor { void visitASTOrderBy(const ASTOrderBy* node, void* data) override; void visitASTLambda(const ASTLambda* node, void* data) override; void visitASTLimitOffset(const ASTLimitOffset* node, void* data) override; - void visitASTWithConfigClause(const ASTWithConfigClause* node, void* data) override; + void visitASTConfigClause(const ASTConfigClause* node, void* data) override; void visitASTHavingModifier(const ASTHavingModifier* node, void* data) override; void visitASTClampedBetweenModifier(const ASTClampedBetweenModifier* node,