From 9f1446fea80c949c6912bd0447606784038c5c96 Mon Sep 17 00:00:00 2001 From: stephnan Date: Sun, 9 Jun 2024 17:14:47 +0200 Subject: [PATCH 1/3] Exasol: Use ANSI value_clause to handle insert_stmts correctly (#5959) --- src/sqlfluff/dialects/dialect_exasol.py | 23 ---- .../dialects/exasol/insert_statement.sql | 2 + .../dialects/exasol/insert_statement.yml | 121 ++++++++++++++---- .../dialects/exasol/select_statement.sql | 2 + .../dialects/exasol/select_statement.yml | 22 +++- 5 files changed, 123 insertions(+), 47 deletions(-) diff --git a/src/sqlfluff/dialects/dialect_exasol.py b/src/sqlfluff/dialects/dialect_exasol.py index 71573992689..0e6e0205d17 100644 --- a/src/sqlfluff/dialects/dialect_exasol.py +++ b/src/sqlfluff/dialects/dialect_exasol.py @@ -1530,7 +1530,6 @@ class InsertStatementSegment(BaseSegment): Ref.keyword("INTO", optional=True), Ref("TableReferenceSegment"), AnyNumberOf( - Ref("ValuesInsertClauseSegment"), Ref("ValuesRangeClauseSegment"), Sequence("DEFAULT", "VALUES"), Ref("SelectableGrammar"), @@ -1539,28 +1538,6 @@ class InsertStatementSegment(BaseSegment): ) -class ValuesInsertClauseSegment(BaseSegment): - """A `VALUES` clause like in `INSERT`.""" - - type = "values_insert_clause" - match_grammar = Sequence( - "VALUES", - Delimited( - Bracketed( - Delimited( - Ref("LiteralGrammar"), - Ref("IntervalExpressionSegment"), - Ref("FunctionSegment"), - Ref("BareFunctionSegment"), - "DEFAULT", - Ref("SelectableGrammar"), - ), - parse_mode=ParseMode.GREEDY, - ), - ), - ) - - ############################ # UPDATE ############################ diff --git a/test/fixtures/dialects/exasol/insert_statement.sql b/test/fixtures/dialects/exasol/insert_statement.sql index 63fb9fc01b1..d19be62b956 100644 --- a/test/fixtures/dialects/exasol/insert_statement.sql +++ b/test/fixtures/dialects/exasol/insert_statement.sql @@ -9,3 +9,5 @@ INSERT INTO s.t(c1, c2, c3) VALUES((SELECT x FROM y), 'val1', 'val2'); INSERT INTO t (adate) values(current_timestamp); INSERT INTO t VALUES BETWEEN 1 AND 100; INSERT INTO t (i) VALUES BETWEEN 1 AND 100 WITH STEP 4; +INSERT INTO t (d1, n1, v1) values (add_days(current_date, -1), 15.0, 'myvalue'); +INSERT INTO t (d1, n1, v1) values (current_date +1, 15.0, 'myvalue'); diff --git a/test/fixtures/dialects/exasol/insert_statement.yml b/test/fixtures/dialects/exasol/insert_statement.yml index 523269c1ca6..c4131f4ff28 100644 --- a/test/fixtures/dialects/exasol/insert_statement.yml +++ b/test/fixtures/dialects/exasol/insert_statement.yml @@ -3,7 +3,7 @@ # computed by SQLFluff when running the tests. Please run # `python test/generate_parse_fixture_yml.py` to generate them after adding or # altering SQL files. -_hash: 0ca0a589ad98d8a2f91b99db2f29bc962416ea5cef4eaaee8fb4d1d07bd1e7c3 +_hash: ae3b8347b6d652d4e75500c1e5661774581425658e7aa09b5b9ef4fee40a49f4 file: - statement: insert_statement: @@ -22,7 +22,7 @@ file: - column_reference: naked_identifier: t1 - end_bracket: ) - - values_insert_clause: + - values_clause: keyword: VALUES bracketed: - start_bracket: ( @@ -39,7 +39,7 @@ file: - keyword: INTO - table_reference: naked_identifier: t - - values_insert_clause: + - values_clause: - keyword: VALUES - bracketed: - start_bracket: ( @@ -65,7 +65,7 @@ file: - keyword: INTO - table_reference: naked_identifier: t - - values_insert_clause: + - values_clause: keyword: VALUES bracketed: - start_bracket: ( @@ -189,26 +189,28 @@ file: - column_reference: naked_identifier: c3 - end_bracket: ) - - values_insert_clause: + - values_clause: keyword: VALUES bracketed: - start_bracket: ( - - bracketed: - start_bracket: ( - select_statement: - select_clause: - keyword: SELECT - select_clause_element: - column_reference: - naked_identifier: x - from_clause: - keyword: FROM - from_expression: - from_expression_element: - table_expression: - table_reference: - naked_identifier: y - end_bracket: ) + - expression: + bracketed: + start_bracket: ( + expression: + select_statement: + select_clause: + keyword: SELECT + select_clause_element: + column_reference: + naked_identifier: x + from_clause: + keyword: FROM + from_expression: + from_expression_element: + table_expression: + table_reference: + naked_identifier: y + end_bracket: ) - comma: ',' - quoted_literal: "'val1'" - comma: ',' @@ -226,11 +228,12 @@ file: column_reference: naked_identifier: adate end_bracket: ) - - values_insert_clause: + - values_clause: keyword: values bracketed: start_bracket: ( - bare_function: current_timestamp + expression: + bare_function: current_timestamp end_bracket: ) - statement_terminator: ; - statement: @@ -267,3 +270,75 @@ file: - keyword: STEP - numeric_literal: '4' - statement_terminator: ; +- statement: + insert_statement: + - keyword: INSERT + - keyword: INTO + - table_reference: + naked_identifier: t + - bracketed: + - start_bracket: ( + - column_reference: + naked_identifier: d1 + - comma: ',' + - column_reference: + naked_identifier: n1 + - comma: ',' + - column_reference: + naked_identifier: v1 + - end_bracket: ) + - values_clause: + keyword: values + bracketed: + - start_bracket: ( + - expression: + function: + function_name: + function_name_identifier: add_days + bracketed: + - start_bracket: ( + - expression: + bare_function: current_date + - comma: ',' + - expression: + numeric_literal: + sign_indicator: '-' + numeric_literal: '1' + - end_bracket: ) + - comma: ',' + - numeric_literal: '15.0' + - comma: ',' + - quoted_literal: "'myvalue'" + - end_bracket: ) +- statement_terminator: ; +- statement: + insert_statement: + - keyword: INSERT + - keyword: INTO + - table_reference: + naked_identifier: t + - bracketed: + - start_bracket: ( + - column_reference: + naked_identifier: d1 + - comma: ',' + - column_reference: + naked_identifier: n1 + - comma: ',' + - column_reference: + naked_identifier: v1 + - end_bracket: ) + - values_clause: + keyword: values + bracketed: + - start_bracket: ( + - expression: + bare_function: current_date + binary_operator: + + numeric_literal: '1' + - comma: ',' + - numeric_literal: '15.0' + - comma: ',' + - quoted_literal: "'myvalue'" + - end_bracket: ) +- statement_terminator: ; diff --git a/test/fixtures/dialects/exasol/select_statement.sql b/test/fixtures/dialects/exasol/select_statement.sql index 8c884e2f13d..50644f68bc4 100644 --- a/test/fixtures/dialects/exasol/select_statement.sql +++ b/test/fixtures/dialects/exasol/select_statement.sql @@ -158,3 +158,5 @@ SELECT 'My mail address is my_mail@exasol.com' SELECT 'My mail address is my_mail@exasol.com' NOT REGEXP_LIKE '(?i).*[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}.*' AS contains_email; +-- +SELECT current_date -1 as dt from dual; diff --git a/test/fixtures/dialects/exasol/select_statement.yml b/test/fixtures/dialects/exasol/select_statement.yml index f03c19fb055..5dbd3036c27 100644 --- a/test/fixtures/dialects/exasol/select_statement.yml +++ b/test/fixtures/dialects/exasol/select_statement.yml @@ -3,7 +3,7 @@ # computed by SQLFluff when running the tests. Please run # `python test/generate_parse_fixture_yml.py` to generate them after adding or # altering SQL files. -_hash: 1b5fd3bd0d3c91581cd2ee9f167c2a4607a98296c43717424aa44d4dba77243f +_hash: 405ab9d1f5df047af29966b099705a1830b65db2c3918e8f4f78dadab5ffbc70 file: - statement: select_statement: @@ -1869,3 +1869,23 @@ file: keyword: AS naked_identifier: contains_email - statement_terminator: ; +- statement: + select_statement: + select_clause: + keyword: SELECT + select_clause_element: + expression: + bare_function: current_date + binary_operator: '-' + numeric_literal: '1' + alias_expression: + keyword: as + naked_identifier: dt + from_clause: + keyword: from + from_expression: + from_expression_element: + table_expression: + table_reference: + naked_identifier: dual +- statement_terminator: ; From 695132823318df594cdb939c175da0243a7e9182 Mon Sep 17 00:00:00 2001 From: Danny Jones <51742311+WittierDinosaur@users.noreply.github.com> Date: Sun, 9 Jun 2024 20:28:33 +0100 Subject: [PATCH 2/3] Capitalisation: Add camelCase (#5777) Co-authored-by: Alan Cruickshank --- src/sqlfluff/core/rules/config_info.py | 19 ++++- src/sqlfluff/rules/capitalisation/CP01.py | 29 ++++---- test/fixtures/rules/std_rule_cases/CP02.yml | 78 ++++++++++++++++++--- test/fixtures/rules/std_rule_cases/CP05.yml | 12 +++- 4 files changed, 111 insertions(+), 27 deletions(-) diff --git a/src/sqlfluff/core/rules/config_info.py b/src/sqlfluff/core/rules/config_info.py index 68d73e6bb53..723a2a140fe 100644 --- a/src/sqlfluff/core/rules/config_info.py +++ b/src/sqlfluff/core/rules/config_info.py @@ -60,12 +60,25 @@ "definition": "The capitalisation policy to enforce.", }, "extended_capitalisation_policy": { - "validation": ["consistent", "upper", "lower", "pascal", "capitalise", "snake"], + "validation": [ + "consistent", + "upper", + "lower", + "pascal", + "capitalise", + "snake", + "camel", + ], "definition": ( - "The capitalisation policy to enforce, extended with PascalCase " - "and snake_case. " + "The capitalisation policy to enforce, extended with PascalCase, " + "snake_case, and camelCase. " "This is separate from ``capitalisation_policy`` as it should not be " "applied to keywords." + "Camel, Pascal, and Snake will never be inferred when the policy is set" + "to consistent. This is because snake can cause destructive changes to" + "the identifier, and unlinted code is too easily mistaken for camel and " + "pascal. If, when set to consistent, no consistent case is found, it will" + "default to upper." ), }, "select_clause_trailing_comma": { diff --git a/src/sqlfluff/rules/capitalisation/CP01.py b/src/sqlfluff/rules/capitalisation/CP01.py index 18840ec40c1..7f6ba468ed0 100644 --- a/src/sqlfluff/rules/capitalisation/CP01.py +++ b/src/sqlfluff/rules/capitalisation/CP01.py @@ -136,17 +136,20 @@ def _handle_segment(self, segment: BaseSegment, context: RuleContext) -> LintRes refuted_cases = memory.get("refuted_cases", set()) # Which cases are definitely inconsistent with the segment? + first_letter_is_lowercase = False for character in segment.raw: if is_capitalizable(character): first_letter_is_lowercase = character != character.upper() break - # If none of the characters are letters there will be a parsing - # error, so not sure we need this statement - first_letter_is_lowercase = False + # We refute inference of camel, pascal, and snake case. + # snake, if not explicitly set, can be destructive to + # variable names, adding underscores. + # camel and Pascal could allow poorly linted code in, + # so must be explicitly chosen. + refuted_cases.update(["camel", "pascal", "snake"]) if first_letter_is_lowercase: - # snake added here as it cannot be inferred (presents as lower) - refuted_cases.update(["upper", "capitalise", "pascal", "snake"]) + refuted_cases.update(["upper", "capitalise"]) if segment.raw != segment.raw.lower(): refuted_cases.update(["lower"]) else: @@ -155,8 +158,6 @@ def _handle_segment(self, segment: BaseSegment, context: RuleContext) -> LintRes refuted_cases.update(["upper"]) if segment.raw != segment.raw.capitalize(): refuted_cases.update(["capitalise"]) - if not segment.raw.isalnum(): - refuted_cases.update(["pascal", "snake"]) # Update the memory memory["refuted_cases"] = refuted_cases @@ -219,6 +220,14 @@ def _handle_segment(self, segment: BaseSegment, context: RuleContext) -> LintRes lambda match: match.group(1) + match.group(2).upper() + match.group(3), segment.raw, ) + elif concrete_policy == "camel": + # Similar to Pascal, for Camel, we can only do a best efforts approach. + # This presents as us never changing case mid-string. + fixed_raw = regex.sub( + "([^a-zA-Z0-9]+|^)([a-zA-Z0-9])([a-zA-Z0-9]*)", + lambda match: match.group(1) + match.group(2).lower() + match.group(3), + segment.raw, + ) elif concrete_policy == "snake": if segment.raw.isupper(): fixed_raw = segment.raw.lower() @@ -240,14 +249,10 @@ def _handle_segment(self, segment: BaseSegment, context: RuleContext) -> LintRes # build description based on the policy in use consistency = "consistently " if cap_policy == "consistent" else "" - if concrete_policy in ["upper", "lower"]: + if concrete_policy in ["upper", "lower", "pascal", "camel", "snake"]: policy = f"{concrete_policy} case." elif concrete_policy == "capitalise": policy = "capitalised." - elif concrete_policy == "pascal": - policy = "pascal case." - elif concrete_policy == "snake": - policy = "snake case." # Return the fixed segment self.logger.debug( diff --git a/test/fixtures/rules/std_rule_cases/CP02.yml b/test/fixtures/rules/std_rule_cases/CP02.yml index 754bf9f2f40..2b27dc20056 100644 --- a/test/fixtures/rules/std_rule_cases/CP02.yml +++ b/test/fixtures/rules/std_rule_cases/CP02.yml @@ -15,16 +15,18 @@ test_pass_consistent_capitalisation_with_single_letter_upper: pass_str: SELECT A, Boo test_pass_consistent_capitalisation_with_single_word_snake: - # Single-word ambiguity: Pascal vs Capitalise - pass_str: SELECT Apple, Banana_split + # Snake is refuted as ambiguous + pass_str: SELECT apple, banana_split -test_pass_consistent_capitalisation_with_single_word_pascal: +test_fail_consistent_capitalisation_with_single_word_pascal: # Single-word ambiguity: Pascal vs Capitalise - pass_str: SELECT AppleFritter, Banana + fail_str: SELECT AppleFritter, Banana + fix_str: SELECT APPLEFRITTER, BANANA -test_pass_consistent_capitalisation_with_multiple_words_with_numbers: +test_fail_consistent_capitalisation_with_multiple_words_with_numbers: # Numbers count as part of words so following letter can be upper or lower - pass_str: SELECT AppleFritter, Apple123fritter, Apple123Fritter + fail_str: SELECT AppleFritter, Apple123fritter, Apple123Fritter + fix_str: SELECT APPLEFRITTER, APPLE123FRITTER, APPLE123FRITTER test_pass_consistent_capitalisation_with_leading_underscore: pass_str: SELECT _a, b @@ -133,6 +135,36 @@ test_fail_inconsistent_capitalisation_pascal_v_capitalise: # Pascal vs Capitalise fail_str: SELECT AppleFritter, Banana_split fix_str: SELECT AppleFritter, Banana_Split + configs: + rules: + capitalisation.identifiers: + extended_capitalisation_policy: pascal + +test_fail_inconsistent_capitalisation_policy_camel_1: + fail_str: SELECT Camelcase + fix_str: SELECT camelcase + configs: + rules: + capitalisation.identifiers: + extended_capitalisation_policy: camel + +test_fail_inconsistent_capitalisation_policy_camel_2: + fail_str: SELECT Camel_Case + fix_str: SELECT camel_case + configs: + rules: + capitalisation.identifiers: + extended_capitalisation_policy: camel + +test_fail_inconsistent_capitalisation_policy_camel_3: + # Similar to above, you could argue the fixed string is + # Not really Pascal Case, but it's closer than it was! + fail_str: SELECT cAMEL_CASE + fix_str: SELECT cAMEL_cASE + configs: + rules: + capitalisation.identifiers: + extended_capitalisation_policy: camel test_pass_policy_unquoted_identifiers_aliases_1: pass_str: SELECT a, B @@ -167,7 +199,7 @@ test_pass_policy_unquoted_identifiers_aliases_4: test_policy_unquoted_identifiers_aliases_5: fail_str: SELECT UPPER_CASE AS PascalCase, PascalCase AS lower_case - fix_str: SELECT UPPER_CASE AS PascalCase, PascalCase AS Lower_Case + fix_str: SELECT UPPER_CASE AS PASCALCASE, PascalCase AS LOWER_CASE configs: rules: capitalisation.identifiers: @@ -192,7 +224,7 @@ test_policy_unquoted_identifiers_aliases_7: test_policy_unquoted_identifiers_aliases_8: fail_str: SELECT UPPER_CASE AS PascalCase, PascalCase AS lower_case FROM lower_case AS lower_case - fix_str: SELECT UPPER_CASE AS PascalCase, PascalCase AS Lower_Case FROM lower_case AS Lower_Case + fix_str: SELECT UPPER_CASE AS PASCALCASE, PascalCase AS LOWER_CASE FROM lower_case AS LOWER_CASE configs: rules: capitalisation.identifiers: @@ -207,7 +239,7 @@ test_policy_unquoted_identifiers_column_aliases_1: test_policy_unquoted_identifiers_aliases_2: fail_str: SELECT UPPER_CASE AS PascalCase, PascalCase AS lower_case FROM lower_case AS lower_case - fix_str: SELECT UPPER_CASE AS PascalCase, PascalCase AS Lower_Case FROM lower_case AS lower_case + fix_str: SELECT UPPER_CASE AS PASCALCASE, PascalCase AS LOWER_CASE FROM lower_case AS lower_case configs: rules: capitalisation.identifiers: @@ -281,3 +313,31 @@ test_fail_snake_aliases: rules: capitalisation.identifiers: extended_capitalisation_policy: snake + +test_fail_camel_aliases: + # Test for issue #5470 + # similar to PascalCase, case logic defined in CP01, but tested in CP02 + fail_str: | + SELECT + test1, + test_2, + testColumn3, + TestColumn4, + TESTCOLUMN5, + TEST_COLUMN6, + test_colUmn_7 + fix_str: | + SELECT + test1, + test_2, + testColumn3, + testColumn4, + tESTCOLUMN5, + tEST_cOLUMN6, + test_colUmn_7 + configs: + core: + dialect: tsql + rules: + capitalisation.identifiers: + extended_capitalisation_policy: camel diff --git a/test/fixtures/rules/std_rule_cases/CP05.yml b/test/fixtures/rules/std_rule_cases/CP05.yml index 511358ac7ea..2878c2225e5 100644 --- a/test/fixtures/rules/std_rule_cases/CP05.yml +++ b/test/fixtures/rules/std_rule_cases/CP05.yml @@ -27,14 +27,20 @@ test_pass_default_consistent_capitalised: ts Time With Time Zone ); -test_pass_default_consistent_pascal: - # Test that we don't have the "inconsistent" bug - pass_str: | +test_fail_default_consistent_pascal: + # Attempting Pascal without config defaults to capitalize + fail_str: | CREATE TABLE distributors ( did Integer, name VarChar(40), ts Time With Time Zone ); + fix_str: | + CREATE TABLE distributors ( + did Integer, + name Varchar(40), + ts Time With Time Zone + ); test_fail_data_type_inconsistent_capitalisation_1: # Test that we don't have the "inconsistent" bug From 69b4f968b75c194b2dba3736655149f71f5e101b Mon Sep 17 00:00:00 2001 From: Dmytro Samodurov Date: Mon, 10 Jun 2024 02:52:33 +0300 Subject: [PATCH 3/3] Clickhouse 'create view' support (#5910) Co-authored-by: Dmytro Samodurov --- src/sqlfluff/dialects/dialect_clickhouse.py | 21 ++ .../dialects/clickhouse/create_view.sql | 44 ++++ .../dialects/clickhouse/create_view.yml | 235 ++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 test/fixtures/dialects/clickhouse/create_view.sql create mode 100644 test/fixtures/dialects/clickhouse/create_view.yml diff --git a/src/sqlfluff/dialects/dialect_clickhouse.py b/src/sqlfluff/dialects/dialect_clickhouse.py index b3a0993a84b..42900cae6fe 100644 --- a/src/sqlfluff/dialects/dialect_clickhouse.py +++ b/src/sqlfluff/dialects/dialect_clickhouse.py @@ -888,6 +888,27 @@ class CreateTableStatementSegment(ansi.CreateTableStatementSegment): ) +class CreateViewStatementSegment(BaseSegment): + """A `CREATE VIEW` statement. + + https://clickhouse.com/docs/en/sql-reference/statements/create/view + """ + + type = "create_view_statement" + + match_grammar = Sequence( + "CREATE", + Ref("OrReplaceGrammar", optional=True), + "VIEW", + Ref("IfNotExistsGrammar", optional=True), + Ref("TableReferenceSegment"), + Ref("OnClusterClauseSegment", optional=True), + "AS", + Ref("SelectableGrammar"), + Ref("TableEndClauseSegment", optional=True), + ) + + class CreateMaterializedViewStatementSegment(BaseSegment): """A `CREATE MATERIALIZED VIEW` statement. diff --git a/test/fixtures/dialects/clickhouse/create_view.sql b/test/fixtures/dialects/clickhouse/create_view.sql new file mode 100644 index 00000000000..89afee31fea --- /dev/null +++ b/test/fixtures/dialects/clickhouse/create_view.sql @@ -0,0 +1,44 @@ +CREATE VIEW db.view_mv +AS SELECT + column1, + column2 +FROM db.table_kafka; + +CREATE VIEW db.view_mv +ON CLUSTER mycluster +AS SELECT + column1, + column2 +FROM db.table_kafka; + +CREATE OR REPLACE VIEW db.view_mv +AS SELECT + column1, + column2 +FROM db.table_kafka; + +CREATE OR REPLACE VIEW db.view_mv +ON CLUSTER mycluster +AS SELECT + column1, + column2 +FROM db.table_kafka; + +CREATE VIEW IF NOT EXISTS db.view_mv +AS SELECT + column1, + column2 +FROM db.table_kafka; + +CREATE VIEW IF NOT EXISTS db.view_mv +AS SELECT + column1, + column2 +FROM db.table_kafka; + +CREATE VIEW IF NOT EXISTS db.view_mv +ON CLUSTER mycluster +AS SELECT + column1, + column2 +FROM db.table_kafka; diff --git a/test/fixtures/dialects/clickhouse/create_view.yml b/test/fixtures/dialects/clickhouse/create_view.yml new file mode 100644 index 00000000000..59856964d30 --- /dev/null +++ b/test/fixtures/dialects/clickhouse/create_view.yml @@ -0,0 +1,235 @@ +# YML test files are auto-generated from SQL files and should not be edited by +# hand. To help enforce this, the "hash" field in the file must match a hash +# computed by SQLFluff when running the tests. Please run +# `python test/generate_parse_fixture_yml.py` to generate them after adding or +# altering SQL files. +_hash: af0957a107ce336e42dd0c44c631861a71c55040f0e2cd10b257ae93439e9248 +file: +- statement: + create_view_statement: + - keyword: CREATE + - keyword: VIEW + - table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: view_mv + - keyword: AS + - select_statement: + select_clause: + - keyword: SELECT + - select_clause_element: + column_reference: + naked_identifier: column1 + - comma: ',' + - select_clause_element: + column_reference: + naked_identifier: column2 + from_clause: + keyword: FROM + from_expression: + from_expression_element: + table_expression: + table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: table_kafka +- statement_terminator: ; +- statement: + create_view_statement: + - keyword: CREATE + - keyword: VIEW + - table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: view_mv + - on_cluster_clause: + - keyword: 'ON' + - keyword: CLUSTER + - naked_identifier: mycluster + - keyword: AS + - select_statement: + select_clause: + - keyword: SELECT + - select_clause_element: + column_reference: + naked_identifier: column1 + - comma: ',' + - select_clause_element: + column_reference: + naked_identifier: column2 + from_clause: + keyword: FROM + from_expression: + from_expression_element: + table_expression: + table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: table_kafka +- statement_terminator: ; +- statement: + create_view_statement: + - keyword: CREATE + - keyword: OR + - keyword: REPLACE + - keyword: VIEW + - table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: view_mv + - keyword: AS + - select_statement: + select_clause: + - keyword: SELECT + - select_clause_element: + column_reference: + naked_identifier: column1 + - comma: ',' + - select_clause_element: + column_reference: + naked_identifier: column2 + from_clause: + keyword: FROM + from_expression: + from_expression_element: + table_expression: + table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: table_kafka +- statement_terminator: ; +- statement: + create_view_statement: + - keyword: CREATE + - keyword: OR + - keyword: REPLACE + - keyword: VIEW + - table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: view_mv + - on_cluster_clause: + - keyword: 'ON' + - keyword: CLUSTER + - naked_identifier: mycluster + - keyword: AS + - select_statement: + select_clause: + - keyword: SELECT + - select_clause_element: + column_reference: + naked_identifier: column1 + - comma: ',' + - select_clause_element: + column_reference: + naked_identifier: column2 + from_clause: + keyword: FROM + from_expression: + from_expression_element: + table_expression: + table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: table_kafka +- statement_terminator: ; +- statement: + create_view_statement: + - keyword: CREATE + - keyword: VIEW + - keyword: IF + - keyword: NOT + - keyword: EXISTS + - table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: view_mv + - keyword: AS + - select_statement: + select_clause: + - keyword: SELECT + - select_clause_element: + column_reference: + naked_identifier: column1 + - comma: ',' + - select_clause_element: + column_reference: + naked_identifier: column2 + from_clause: + keyword: FROM + from_expression: + from_expression_element: + table_expression: + table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: table_kafka +- statement_terminator: ; +- statement: + create_view_statement: + - keyword: CREATE + - keyword: VIEW + - keyword: IF + - keyword: NOT + - keyword: EXISTS + - table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: view_mv + - keyword: AS + - select_statement: + select_clause: + - keyword: SELECT + - select_clause_element: + column_reference: + naked_identifier: column1 + - comma: ',' + - select_clause_element: + column_reference: + naked_identifier: column2 + from_clause: + keyword: FROM + from_expression: + from_expression_element: + table_expression: + table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: table_kafka +- statement_terminator: ; +- statement: + create_view_statement: + - keyword: CREATE + - keyword: VIEW + - keyword: IF + - keyword: NOT + - keyword: EXISTS + - table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: view_mv + - on_cluster_clause: + - keyword: 'ON' + - keyword: CLUSTER + - naked_identifier: mycluster + - keyword: AS + - select_statement: + select_clause: + - keyword: SELECT + - select_clause_element: + column_reference: + naked_identifier: column1 + - comma: ',' + - select_clause_element: + column_reference: + naked_identifier: column2 + from_clause: + keyword: FROM + from_expression: + from_expression_element: + table_expression: + table_reference: + - naked_identifier: db + - dot: . + - naked_identifier: table_kafka +- statement_terminator: ;