From 057e68011680676ed1bc9b79d5acdd62d81f1e2d Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Wed, 11 Jan 2023 12:09:02 +0800 Subject: [PATCH] feat(parser): new SQL for create table (#46) CREATE TABLE t1 LIKE PARQUET 'hdfs://path' --- zetasql/parser/ast_node_kind.h | 3 +- zetasql/parser/bison_parser.y | 17 ++++++++- zetasql/parser/flex_tokenizer.l | 1 + zetasql/parser/keywords.cc | 1 + zetasql/parser/parse_tree.cc | 10 ++++++ zetasql/parser/parse_tree_manual.h | 32 +++++++++++++++++ zetasql/parser/testdata/create_table.test | 42 +++++++++++++++++++++++ zetasql/parser/unparser.cc | 15 ++++++++ zetasql/parser/unparser.h | 2 ++ 9 files changed, 121 insertions(+), 2 deletions(-) diff --git a/zetasql/parser/ast_node_kind.h b/zetasql/parser/ast_node_kind.h index aa728e20b..1a5b57552 100755 --- a/zetasql/parser/ast_node_kind.h +++ b/zetasql/parser/ast_node_kind.h @@ -327,7 +327,8 @@ enum ASTNodeKind { AST_WINDOW_ATTRIBUTE_EXCLUDE_CURRENT_ROW, AST_WINDOW_ATTRIBUTE_INST_NOT_IN_WINDOW, AST_WINDOW_ATTRIBUTE_LIST, - kLastASTNodeKind = AST_WINDOW_ATTRIBUTE_LIST, + AST_LIKE_TABLE_CLAUSE, + kLastASTNodeKind = AST_LIKE_TABLE_CLAUSE, }; } // namespace zetasql diff --git a/zetasql/parser/bison_parser.y b/zetasql/parser/bison_parser.y index ee349acbb..89238d8f6 100644 --- a/zetasql/parser/bison_parser.y +++ b/zetasql/parser/bison_parser.y @@ -881,6 +881,7 @@ using zetasql::ASTDropStatement; %token KW_OPTIONS "OPTIONS" %token KW_OUT "OUT" %token KW_OUTFILE "OUTFILE" +%token KW_PARQUET "PARQUET" %token KW_PERCENT "PERCENT" %token KW_PIVOT "PIVOT" %token KW_POLICIES "POLICIES" @@ -1185,6 +1186,7 @@ using zetasql::ASTDropStatement; %type opt_language %type opt_like_string_literal %type opt_like_path_expression +%type opt_like_in_create_table %type opt_limit_offset_clause %type opt_config_clause %type opt_maxsize @@ -2553,7 +2555,7 @@ create_table_function_statement: create_table_statement: "CREATE" opt_or_replace opt_create_scope "TABLE" opt_if_not_exists maybe_dashed_path_expression opt_table_element_list - opt_like_path_expression opt_clone_table + opt_like_in_create_table opt_clone_table opt_partition_by_clause_no_hint opt_cluster_by_clause_no_hint opt_options_list opt_as_query { @@ -3652,6 +3654,18 @@ opt_like_path_expression: | /* Nothing */ { $$ = nullptr; } ; +opt_like_in_create_table: + "LIKE" maybe_dashed_path_expression + { + $$ = $2; + } + | "LIKE" "PARQUET" string_literal + { + $$ = MAKE_NODE(ASTLikeTableClause, @$, {$3}); + } + | /* Nothing */ { $$ = nullptr; } + ; + opt_clone_table: "CLONE" clone_data_source { @@ -7531,6 +7545,7 @@ keyword_as_identifier: | "OPTIONS" | "OUT" | "OUTFILE" + | "PARQUET" | "PERCENT" | "PIVOT" | "POLICIES" diff --git a/zetasql/parser/flex_tokenizer.l b/zetasql/parser/flex_tokenizer.l index cf5b5129c..f790fe4c3 100644 --- a/zetasql/parser/flex_tokenizer.l +++ b/zetasql/parser/flex_tokenizer.l @@ -570,6 +570,7 @@ out { return BisonParserImpl::token::KW_OUT; } outfile { return BisonParserImpl::token::KW_OUTFILE; }; outer { return BisonParserImpl::token::KW_OUTER; } over { return BisonParserImpl::token::KW_OVER; } +parquet { return BisonParserImpl::token::KW_PARQUET; } partition { return BisonParserImpl::token::KW_PARTITION; } percent { return BisonParserImpl::token::KW_PERCENT; } policies { return BisonParserImpl::token::KW_POLICIES; } diff --git a/zetasql/parser/keywords.cc b/zetasql/parser/keywords.cc index c5c28d216..1350b844e 100644 --- a/zetasql/parser/keywords.cc +++ b/zetasql/parser/keywords.cc @@ -225,6 +225,7 @@ constexpr KeywordInfoPOD kAllKeywords[] = { {"outfile", KW_OUTFILE}, {"outer", KW_OUTER, KeywordInfo::kReserved}, {"over", KW_OVER, KeywordInfo::kReserved}, + {"parquet", KW_PARQUET}, {"partition", KW_PARTITION, KeywordInfo::kReserved}, {"percent", KW_PERCENT}, {"pivot", KW_PIVOT}, diff --git a/zetasql/parser/parse_tree.cc b/zetasql/parser/parse_tree.cc index 722f4f8bb..5f10d4b7d 100644 --- a/zetasql/parser/parse_tree.cc +++ b/zetasql/parser/parse_tree.cc @@ -364,6 +364,7 @@ static absl::flat_hash_map CreateNodeNamesMap() { map[AST_WINDOW_ATTRIBUTE_EXCLUDE_CURRENT_ROW] = "WindowAttributeExcludeCurrentRow"; map[AST_WINDOW_ATTRIBUTE_INST_NOT_IN_WINDOW] = "WindowAttributeInstNotInWindow"; map[AST_WINDOW_ATTRIBUTE_LIST] = "WindowAttributeList"; + map[AST_LIKE_TABLE_CLAUSE] = "LikeTableClause"; for (int kind = kFirstASTNodeKind; kind <= kLastASTNodeKind; ++kind) { ZETASQL_DCHECK(zetasql_base::ContainsKey(map, static_cast(kind))) @@ -1687,4 +1688,13 @@ absl::string_view SchemaObjectKindToName(SchemaObjectKind schema_object_kind) { } } +std::string ASTLikeTableClause::SingleNodeDebugString() const { + auto result = ASTNode::SingleNodeDebugString(); + switch (kind()) { + case PARQUET: + absl::StrAppend(&result, "(PARQUET)"); + } + return result; +} + } // namespace zetasql diff --git a/zetasql/parser/parse_tree_manual.h b/zetasql/parser/parse_tree_manual.h index fe2d02f24..18c74287d 100644 --- a/zetasql/parser/parse_tree_manual.h +++ b/zetasql/parser/parse_tree_manual.h @@ -4586,6 +4586,33 @@ class ASTCreateTableFunctionStatement final : public ASTCreateFunctionStmtBase { const ASTQuery* query_ = nullptr; }; +// create table like PARQUET '...' +// - create table LIKE is not covered by this class +class ASTLikeTableClause final : public ASTNode { + public: + static constexpr ASTNodeKind kConcreteNodeKind = AST_LIKE_TABLE_CLAUSE; + enum TableKind { PARQUET }; + + ASTLikeTableClause() : ASTNode(kConcreteNodeKind) {} + void Accept(ParseTreeVisitor* visitor, void* data) const override; + zetasql_base::StatusOr Accept( + NonRecursiveParseTreeVisitor* visitor) const override; + std::string SingleNodeDebugString() const override; + + auto kind() const { return kind_; } + void set_kind(TableKind k) { kind_ = k; } + auto path() const { return path_; } + + private: + void InitFields() final { + FieldLoader fl(this); + fl.AddRequired(&path_); + } + + TableKind kind_ = PARQUET; + const ASTStringLiteral* path_; +}; + class ASTCreateTableStmtBase : public ASTCreateStatement { public: explicit ASTCreateTableStmtBase(const ASTNodeKind kConcreteNodeKind) @@ -4619,6 +4646,9 @@ class ASTCreateTableStatement final : public ASTCreateTableStmtBase { const ASTCloneDataSource* clone_data_source() const { return clone_data_source_; } + // optional like clause for `LIKE PARQUET '...'` in create table + // use like_table_name() instead for `LIKE ` + auto like_table_clause() const { return like_table_clause_; } const ASTPartitionBy* partition_by() const { return partition_by_; } const ASTClusterBy* cluster_by() const { return cluster_by_; } const ASTQuery* query() const { return query_; } @@ -4629,6 +4659,7 @@ class ASTCreateTableStatement final : public ASTCreateTableStmtBase { fl.AddRequired(&name_); fl.AddOptional(&table_element_list_, AST_TABLE_ELEMENT_LIST); fl.AddOptional(&like_table_name_, AST_PATH_EXPRESSION); + fl.AddOptional(&like_table_clause_, AST_LIKE_TABLE_CLAUSE); fl.AddOptional(&clone_data_source_, AST_CLONE_DATA_SOURCE); fl.AddOptional(&partition_by_, AST_PARTITION_BY); fl.AddOptional(&cluster_by_, AST_CLUSTER_BY); @@ -4640,6 +4671,7 @@ class ASTCreateTableStatement final : public ASTCreateTableStmtBase { const ASTPartitionBy* partition_by_ = nullptr; // May be NULL. const ASTClusterBy* cluster_by_ = nullptr; // May be NULL. const ASTQuery* query_ = nullptr; // May be NULL. + const ASTLikeTableClause* like_table_clause_ = nullptr; // May be NULL. }; class ASTCreateEntityStatement final : public ASTCreateStatement { diff --git a/zetasql/parser/testdata/create_table.test b/zetasql/parser/testdata/create_table.test index 60b70e058..219127d6c 100644 --- a/zetasql/parser/testdata/create_table.test +++ b/zetasql/parser/testdata/create_table.test @@ -2777,6 +2777,48 @@ CREATE TABLE t t1 == +# CREATE TABLE LIKE PARQUET '...' +create table t LIKE PARQUET 'hdfs://path'; +-- +CreateTableStatement [0-41] + PathExpression [13-14] + Identifier(t) [13-14] + LikeTableClause(PARQUET) [15-41] + StringLiteral('hdfs://path') [28-41] +-- +CREATE TABLE t LIKE +PARQUET 'hdfs://path' +== + +# CREATE TABLE LIKE PARQUET, 'PARQUET' become like table name +create table t LIKE PARQUET; +-- +CreateTableStatement [0-27] + PathExpression [13-14] + Identifier(t) [13-14] + PathExpression [20-27] + Identifier(PARQUET) [20-27] +-- +CREATE TABLE t LIKE +PARQUET +== + +# ERROR: CREATE TABLE LIKE PARXX '...' +create table t LIKE PARXX 'hdfs://path'; +-- +ERROR: Syntax error: Expected end of input but got string literal 'hdfs://path' [at 1:27] +create table t LIKE PARXX 'hdfs://path'; + ^ +== + +# ERROR: CREATE TABLE LIKE '...' +create table t LIKE 'hdfs://path'; +-- +ERROR: Syntax error: Unexpected string literal 'hdfs://path' [at 1:21] +create table t LIKE 'hdfs://path'; + ^ +== + # CREATE TABLE LIKE without table name. create table t like; -- diff --git a/zetasql/parser/unparser.cc b/zetasql/parser/unparser.cc index 708ebf04c..1bd23bef5 100644 --- a/zetasql/parser/unparser.cc +++ b/zetasql/parser/unparser.cc @@ -528,6 +528,9 @@ void Unparser::visitASTCreateTableStatement( println("LIKE"); node->like_table_name()->Accept(this, data); } + if (node->like_table_clause()) { + node->like_table_clause()->Accept(this, data); + } if (node->clone_data_source() != nullptr) { println("CLONE"); node->clone_data_source()->Accept(this, data); @@ -2237,6 +2240,18 @@ void Unparser::visitASTWindowAttributeInstNotInWindow( print("INSTANCE_NOT_IN_WINDOW"); } +void Unparser::visitASTLikeTableClause(const ASTLikeTableClause *node, + void *data) { + println("LIKE"); + switch (node->kind()) { + case ASTLikeTableClause::PARQUET: { + print("PARQUET"); + break; + } + } + node->path()->Accept(this, data); +} + void Unparser::visitASTPartitionBy(const ASTPartitionBy* node, void* data) { print("PARTITION"); if (node->hint() != nullptr) { diff --git a/zetasql/parser/unparser.h b/zetasql/parser/unparser.h index 865a822f7..5aadfc202 100644 --- a/zetasql/parser/unparser.h +++ b/zetasql/parser/unparser.h @@ -436,6 +436,8 @@ class Unparser : public ParseTreeVisitor { const ASTWindowAttributeExcludeCurrentRow *node, void *data) override; void visitASTWindowAttributeInstNotInWindow( const ASTWindowAttributeInstNotInWindow *node, void *data) override; + void visitASTLikeTableClause(const ASTLikeTableClause *node, + void *data) override; void visitASTPartitionBy(const ASTPartitionBy* node, void* data) override; void visitASTClusterBy(const ASTClusterBy* node, void* data) override; void visitASTCloneDataSource(const ASTCloneDataSource* node,