diff --git a/zetasql/parser/bison_parser.y b/zetasql/parser/bison_parser.y index e8c76b99..20c20656 100644 --- a/zetasql/parser/bison_parser.y +++ b/zetasql/parser/bison_parser.y @@ -697,6 +697,7 @@ using zetasql::ASTDropStatement; %token KW_IGNORE "IGNORE" %token KW_IN "IN" %token KW_INNER "INNER" +%token KW_INSTANCE_NOT_IN_WINDOW "INSTANCE_NOT_IN_WINDOW" %token KW_INTERSECT "INTERSECT" %token KW_INTERVAL "INTERVAL" %token KW_INTO "INTO" @@ -797,6 +798,7 @@ using zetasql::ASTDropStatement; %token KW_CONTINUE "CONTINUE" %token KW_CONSTANT "CONSTANT" %token KW_CONSTRAINT "CONSTRAINT" +%token KW_CURRENT_TIME "CURRENT_TIME" %token KW_DATA "DATA" %token KW_DATABASE "DATABASE" %token KW_DATE "DATE" @@ -1366,6 +1368,8 @@ using zetasql::ASTDropStatement; %type opt_recursive %type opt_unique %type opt_search +%type opt_instance_not_in_window +%type opt_exclude_current_time %type opt_with_anonymization %type primary_key_column_attribute %type hidden_column_attribute @@ -6598,7 +6602,7 @@ frame_unit: ; opt_window_frame_clause: - frame_unit "BETWEEN" window_frame_bound "AND for BETWEEN" window_frame_bound opt_maxsize + frame_unit "BETWEEN" window_frame_bound "AND for BETWEEN" window_frame_bound opt_maxsize { auto* frame = MAKE_NODE(ASTWindowFrame, @$, {$3, $5, $6}); frame->set_unit($1); @@ -6611,23 +6615,40 @@ opt_window_frame_clause: $$ = frame; } | /* Nothing */ { $$ = nullptr; } - + ; opt_maxsize: "MAXSIZE" integer_literal { $$ = MAKE_NODE(ASTMaxSize, @$, {$2}); } | /* Nothing */ { $$ = nullptr; } - + ; +opt_instance_not_in_window: + "INSTANCE_NOT_IN_WINDOW" + { + $$ = true; + }; + | /* Nothing */ { $$ = false; } + ; +opt_exclude_current_time: + "EXCLUDE" "CURRENT_TIME" + { + $$ = true; + }; + | /* Nothing */ { $$ = false; } + ; window_specification: identifier { $$ = MAKE_NODE(ASTWindowSpecification, @$, {$1}); } | "(" opt_identifier opt_partition_by_clause opt_order_by_clause - opt_window_frame_clause ")" + opt_window_frame_clause opt_exclude_current_time opt_instance_not_in_window ")" { - $$ = MAKE_NODE(ASTWindowSpecification, @$, {$2, $3, $4, $5}); + auto *window_spec = MAKE_NODE(ASTWindowSpecification, @$, {$2, $3, $4, $5}); + window_spec->set_is_exclude_current_time($6); + window_spec->set_is_instance_not_in_window($7); + $$ = window_spec; } ; @@ -7009,6 +7030,7 @@ reserved_keyword_rule: | "IGNORE" | "IN" | "INNER" + | "INSTANCE_NOT_IN_WINDOW" | "INTERSECT" | "INTERVAL" | "INTO" @@ -7104,6 +7126,7 @@ keyword_as_identifier: | "CONSTANT" | "CONSTRAINT" | "CONTINUE" + | "CURRENT_TIME" | "DATA" | "DATABASE" | "DATE" diff --git a/zetasql/parser/flex_tokenizer.l b/zetasql/parser/flex_tokenizer.l index 01012235..c24d0d21 100644 --- a/zetasql/parser/flex_tokenizer.l +++ b/zetasql/parser/flex_tokenizer.l @@ -399,6 +399,7 @@ create { return BisonParserImpl::token::KW_CREATE; } cross { return BisonParserImpl::token::KW_CROSS; } cube { return BisonParserImpl::token::KW_CUBE; } current { return BisonParserImpl::token::KW_CURRENT; } +current_time { return BisonParserImpl::token::KW_CURRENT_TIME;} data { return BisonParserImpl::token::KW_DATA; } database { return BisonParserImpl::token::KW_DATABASE; } date { return BisonParserImpl::token::KW_DATE; } @@ -480,6 +481,7 @@ include { return BisonParserImpl::token::KW_INCLUDE; } inout { return BisonParserImpl::token::KW_INOUT; } index { return BisonParserImpl::token::KW_INDEX; } inner { return BisonParserImpl::token::KW_INNER; } +instance_not_in_window { return BisonParserImpl::token::KW_INSTANCE_NOT_IN_WINDOW; } insert { return BisonParserImpl::token::KW_INSERT; } intersect { return BisonParserImpl::token::KW_INTERSECT; } interval { return BisonParserImpl::token::KW_INTERVAL; } diff --git a/zetasql/parser/keywords.cc b/zetasql/parser/keywords.cc index a80e16c8..b9a3f9ea 100644 --- a/zetasql/parser/keywords.cc +++ b/zetasql/parser/keywords.cc @@ -94,6 +94,7 @@ constexpr KeywordInfoPOD kAllKeywords[] = { {"cross", KW_CROSS, KeywordInfo::kReserved}, {"cube", KW_CUBE, KeywordInfo::kReserved}, {"current", KW_CURRENT, KeywordInfo::kReserved}, + {"current_time", KW_CURRENT_TIME}, {"data", KW_DATA}, {"database", KW_DATABASE}, {"date", KW_DATE}, @@ -160,6 +161,7 @@ constexpr KeywordInfoPOD kAllKeywords[] = { {"index", KW_INDEX}, {"inner", KW_INNER, KeywordInfo::kReserved}, {"insert", KW_INSERT}, + {"instance_not_in_window", KW_INSTANCE_NOT_IN_WINDOW, KeywordInfo::kReserved}, {"intersect", KW_INTERSECT, KeywordInfo::kReserved}, {"interval", KW_INTERVAL, KeywordInfo::kReserved}, {"iterate", KW_ITERATE}, diff --git a/zetasql/parser/keywords_test.cc b/zetasql/parser/keywords_test.cc index 422aeae0..f71129ea 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(96 /* CAUTION */, num_reserved); + EXPECT_EQ(97 /* CAUTION */, num_reserved); } } // namespace diff --git a/zetasql/parser/parse_tree.cc b/zetasql/parser/parse_tree.cc index 5651c55a..dd48240c 100644 --- a/zetasql/parser/parse_tree.cc +++ b/zetasql/parser/parse_tree.cc @@ -906,6 +906,21 @@ std::string ASTWindowFrame::SingleNodeDebugString() const { return absl::StrCat(ASTNode::SingleNodeDebugString(), "(", GetFrameUnitString(), ")"); } +std::string ASTWindowSpecification::SingleNodeDebugString() const { + if (is_instance_not_in_window_ && is_exclude_current_time_) { + return absl::StrCat(ASTNode::SingleNodeDebugString(), + "(is_exclude_current_time, is_instance_not_in_window)"); + } + if (is_instance_not_in_window_) { + return absl::StrCat(ASTNode::SingleNodeDebugString(), + "(is_instance_not_in_window)"); + } + if (is_exclude_current_time_) { + return absl::StrCat(ASTNode::SingleNodeDebugString(), + "(is_exclude_current_time)"); + } + return ASTNode::SingleNodeDebugString(); +} // static std::string ASTWindowFrame::FrameUnitToString(FrameUnit unit) { diff --git a/zetasql/parser/parse_tree_manual.h b/zetasql/parser/parse_tree_manual.h index 22cc4b89..e656c623 100644 --- a/zetasql/parser/parse_tree_manual.h +++ b/zetasql/parser/parse_tree_manual.h @@ -2817,7 +2817,13 @@ class ASTWindowSpecification final : public ASTNode { const ASTWindowFrame* window_frame() const { return window_frame_; } const ASTIdentifier* base_window_name() const { return base_window_name_; } + + bool is_instance_not_in_window() const { return is_instance_not_in_window_; } + void set_is_instance_not_in_window(bool value) { is_instance_not_in_window_ = value; } + bool is_exclude_current_time() const { return is_exclude_current_time_; } + void set_is_exclude_current_time(bool value) { is_exclude_current_time_ = value; } + std::string SingleNodeDebugString() const override; private: void InitFields() final { FieldLoader fl(this); @@ -2832,6 +2838,9 @@ class ASTWindowSpecification final : public ASTNode { const ASTPartitionBy* partition_by_ = nullptr; const ASTOrderBy* order_by_ = nullptr; const ASTWindowFrame* window_frame_ = nullptr; + + bool is_instance_not_in_window_ = false; + bool is_exclude_current_time_ = false; }; class ASTWindowDefinition final : public ASTNode { diff --git a/zetasql/parser/testdata/analytic_functions.test b/zetasql/parser/testdata/analytic_functions.test index 4cf56c93..cc4bc7df 100644 --- a/zetasql/parser/testdata/analytic_functions.test +++ b/zetasql/parser/testdata/analytic_functions.test @@ -655,6 +655,68 @@ FROM T == +# window with exclude current_time +select f() over (rows_range between 5s preceding and current row maxsize 5 exclude current_time) +from T +-- +QueryStatement [0-103] + Query [0-103] + Select [0-103] + SelectList [7-96] + SelectColumn [7-96] + AnalyticFunctionCall [7-96] + FunctionCall [7-10] + PathExpression [7-8] + Identifier(f) [7-8] + WindowSpecification(is_exclude_current_time) [16-96] + WindowFrame(ROWS_RANGE) [17-74] + WindowFrameExpr(OFFSET PRECEDING) [36-48] + IntervalLiteral(5s) [36-38] + WindowFrameExpr(CURRENT ROW) [53-64] + MaxSize [65-74] + IntLiteral(5) [73-74] + FromClause [97-103] + TablePathExpression [102-103] + PathExpression [102-103] + Identifier(T) [102-103] +-- +SELECT + f() OVER (ROWS_RANGE BETWEEN 5s PRECEDING AND CURRENT ROW MAXSIZE 5 EXCLUDE CURRENT_TIME) +FROM + T +== + +# window with instance_not_in_window +select f() over (rows_range between 5s preceding and current row maxsize 5 instance_not_in_window) +from T +-- +QueryStatement [0-105] + Query [0-105] + Select [0-105] + SelectList [7-98] + SelectColumn [7-98] + AnalyticFunctionCall [7-98] + FunctionCall [7-10] + PathExpression [7-8] + Identifier(f) [7-8] + WindowSpecification(is_instance_not_in_window) [16-98] + WindowFrame(ROWS_RANGE) [17-74] + WindowFrameExpr(OFFSET PRECEDING) [36-48] + IntervalLiteral(5s) [36-38] + WindowFrameExpr(CURRENT ROW) [53-64] + MaxSize [65-74] + IntLiteral(5) [73-74] + FromClause [99-105] + TablePathExpression [104-105] + PathExpression [104-105] + Identifier(T) [104-105] +-- +SELECT + f() OVER (ROWS_RANGE BETWEEN 5s PRECEDING AND CURRENT ROW MAXSIZE 5 INSTANCE_NOT_IN_WINDOW) +FROM + T +== + select f() over (range between 5+5 preceding and current {{rows|blah}}) from T -- diff --git a/zetasql/parser/unparser.cc b/zetasql/parser/unparser.cc index 00121063..0d46a2e0 100644 --- a/zetasql/parser/unparser.cc +++ b/zetasql/parser/unparser.cc @@ -2111,6 +2111,13 @@ void Unparser::visitASTWindowDefinition( void Unparser::visitASTWindowSpecification( const ASTWindowSpecification* node, void* data) { UnparseChildrenWithSeparator(node, data, ""); + if (node->is_exclude_current_time()) { + print("EXCLUDE CURRENT_TIME"); + } + + if (node->is_instance_not_in_window()) { + print("INSTANCE_NOT_IN_WINDOW"); + } } void Unparser::visitASTPartitionBy(const ASTPartitionBy* node, void* data) {