From e013875e9fbc7953c33296bb448443bb0b605d2b Mon Sep 17 00:00:00 2001 From: Michael Tibben Date: Mon, 15 Jun 2026 10:26:38 +1000 Subject: [PATCH 1/2] fix: handle postgres pgvector table layout --- crates/lib-core/src/dialects/syntax.rs | 1 + crates/lib-dialects/src/postgres.rs | 267 +++++++++++------- .../postgres/sqlfluff/alter_table.yml | 55 ++-- .../postgres/sqlfluff/create_table.yml | 67 +++-- .../issue_2747_pgvector_formatting.sql | 14 + .../issue_2747_pgvector_formatting.yml | 108 +++++++ crates/lib/src/core/default_config.cfg | 4 + .../std_rule_cases/LT01_LT02-postgres.yml | 21 ++ 8 files changed, 377 insertions(+), 160 deletions(-) create mode 100644 crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.sql create mode 100644 crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.yml create mode 100644 crates/lib/test/fixtures/rules/std_rule_cases/LT01_LT02-postgres.yml diff --git a/crates/lib-core/src/dialects/syntax.rs b/crates/lib-core/src/dialects/syntax.rs index 4cdfdd13a..431167ab6 100644 --- a/crates/lib-core/src/dialects/syntax.rs +++ b/crates/lib-core/src/dialects/syntax.rs @@ -54,6 +54,7 @@ pub enum SyntaxKind { WithCompoundStatement, CommonTableExpression, CTEColumnList, + ReferencedColumnList, TriggerReference, TableConstraint, JoinOnCondition, diff --git a/crates/lib-dialects/src/postgres.rs b/crates/lib-dialects/src/postgres.rs index bfd04686d..02022453b 100644 --- a/crates/lib-dialects/src/postgres.rs +++ b/crates/lib-dialects/src/postgres.rs @@ -277,7 +277,11 @@ fn build_datatype_segment_grammar(pgvector: bool) -> Matchable { Ref::new("WellKnownTextGeometrySegment").to_matchable(), Ref::new("DateTimeTypeIdentifier").to_matchable(), Sequence::new(vec![one_of(known_types).to_matchable()]).to_matchable(), - Ref::new("DatatypeIdentifierSegment").to_matchable(), + Sequence::new(vec![ + Ref::new("DatatypeIdentifierSegment").to_matchable(), + Ref::new("BracketedArguments").optional().to_matchable(), + ]) + .to_matchable(), ]) .to_matchable(), one_of(vec![ @@ -298,6 +302,112 @@ fn build_datatype_segment_grammar(pgvector: bool) -> Matchable { .to_matchable() } +fn table_constraint_body_grammar() -> Matchable { + Sequence::new(vec![ + one_of(vec![ + Sequence::new(vec![ + Ref::keyword("CHECK").to_matchable(), + Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()]).to_matchable(), + Sequence::new(vec![ + Ref::keyword("NO").to_matchable(), + Ref::keyword("INHERIT").to_matchable(), + ]) + .config(|this| this.optional()) + .to_matchable(), + ]) + .to_matchable(), + Sequence::new(vec![ + Ref::keyword("UNIQUE").to_matchable(), + Sequence::new(vec![ + Ref::keyword("NULLS").to_matchable(), + Ref::keyword("NOT").optional().to_matchable(), + Ref::keyword("DISTINCT").to_matchable(), + ]) + .config(|this| this.optional()) + .to_matchable(), + Ref::new("BracketedColumnReferenceListGrammar").to_matchable(), + Ref::new("IndexParametersSegment").optional().to_matchable(), + ]) + .to_matchable(), + Sequence::new(vec![ + Ref::new("PrimaryKeyGrammar").to_matchable(), + Ref::new("BracketedColumnReferenceListGrammar").to_matchable(), + Ref::new("IndexParametersSegment").optional().to_matchable(), + ]) + .to_matchable(), + Sequence::new(vec![ + Ref::keyword("EXCLUDE").to_matchable(), + Sequence::new(vec![ + Ref::keyword("USING").to_matchable(), + Ref::new("IndexAccessMethodSegment").to_matchable(), + ]) + .config(|this| this.optional()) + .to_matchable(), + Bracketed::new(vec![ + Delimited::new(vec![ + Ref::new("ExclusionConstraintElementSegment").to_matchable(), + ]) + .to_matchable(), + ]) + .to_matchable(), + Ref::new("IndexParametersSegment").optional().to_matchable(), + Sequence::new(vec![ + Ref::keyword("WHERE").to_matchable(), + Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()]) + .to_matchable(), + ]) + .config(|this| this.optional()) + .to_matchable(), + ]) + .to_matchable(), + Sequence::new(vec![ + Ref::keyword("FOREIGN").to_matchable(), + Ref::keyword("KEY").to_matchable(), + Ref::new("BracketedColumnReferenceListGrammar").to_matchable(), + Ref::new("ReferenceDefinitionGrammar").to_matchable(), + ]) + .to_matchable(), + ]) + .to_matchable(), + AnyNumberOf::new(vec![ + one_of(vec![ + Ref::keyword("DEFERRABLE").to_matchable(), + Sequence::new(vec![ + Ref::keyword("NOT").to_matchable(), + Ref::keyword("DEFERRABLE").to_matchable(), + ]) + .to_matchable(), + ]) + .to_matchable(), + one_of(vec![ + Sequence::new(vec![ + Ref::keyword("INITIALLY").to_matchable(), + Ref::keyword("DEFERRED").to_matchable(), + ]) + .to_matchable(), + Sequence::new(vec![ + Ref::keyword("INITIALLY").to_matchable(), + Ref::keyword("IMMEDIATE").to_matchable(), + ]) + .to_matchable(), + ]) + .to_matchable(), + Sequence::new(vec![ + Ref::keyword("NOT").to_matchable(), + Ref::keyword("VALID").to_matchable(), + ]) + .to_matchable(), + Sequence::new(vec![ + Ref::keyword("NO").to_matchable(), + Ref::keyword("INHERIT").to_matchable(), + ]) + .to_matchable(), + ]) + .to_matchable(), + ]) + .to_matchable() +} + pub fn raw_dialect() -> Dialect { let mut postgres = ansi::raw_dialect(); postgres.name = DialectKind::Postgres; @@ -1166,6 +1276,53 @@ pub fn raw_dialect() -> Dialect { postgres.replace_grammar("ArrayTypeSegment", Ref::keyword("ARRAY").to_matchable()); + postgres.add([( + "ReferencedColumnListGrammar".into(), + NodeMatcher::new(SyntaxKind::ReferencedColumnList, |_| { + Ref::new("BracketedColumnReferenceListGrammar").to_matchable() + }) + .to_matchable() + .into(), + )]); + + postgres.replace_grammar( + "ReferenceDefinitionGrammar", + Sequence::new(vec![ + Ref::keyword("REFERENCES").to_matchable(), + Ref::new("TableReferenceSegment").to_matchable(), + Ref::new("ReferencedColumnListGrammar") + .optional() + .to_matchable(), + Sequence::new(vec![ + Ref::keyword("MATCH").to_matchable(), + one_of(vec![ + Ref::keyword("FULL").to_matchable(), + Ref::keyword("PARTIAL").to_matchable(), + Ref::keyword("SIMPLE").to_matchable(), + ]) + .to_matchable(), + ]) + .config(|this| this.optional()) + .to_matchable(), + AnyNumberOf::new(vec![ + Sequence::new(vec![ + Ref::keyword("ON").to_matchable(), + Ref::keyword("DELETE").to_matchable(), + Ref::new("ReferentialActionGrammar").to_matchable(), + ]) + .to_matchable(), + Sequence::new(vec![ + Ref::keyword("ON").to_matchable(), + Ref::keyword("UPDATE").to_matchable(), + Ref::new("ReferentialActionGrammar").to_matchable(), + ]) + .to_matchable(), + ]) + .to_matchable(), + ]) + .to_matchable(), + ); + postgres.add([( "IndexAccessMethodSegment".into(), NodeMatcher::new(SyntaxKind::IndexAccessMethod, |_| { @@ -4881,114 +5038,16 @@ pub fn raw_dialect() -> Dialect { postgres.add([( "TableConstraintSegment".into(), NodeMatcher::new(SyntaxKind::TableConstraint, |_| { - Sequence::new(vec![ + one_of(vec![ Sequence::new(vec![ Ref::keyword("CONSTRAINT").to_matchable(), Ref::new("ObjectReferenceSegment").to_matchable(), - ]) - .config(|this| this.optional()) - .to_matchable(), - one_of(vec![ - Sequence::new(vec![ - Ref::keyword("CHECK").to_matchable(), - Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()]) - .to_matchable(), - Sequence::new(vec![ - Ref::keyword("NO").to_matchable(), - Ref::keyword("INHERIT").to_matchable(), - ]) - .config(|this| this.optional()) - .to_matchable(), - ]) - .to_matchable(), - Sequence::new(vec![ - Ref::keyword("UNIQUE").to_matchable(), - Sequence::new(vec![ - Ref::keyword("NULLS").to_matchable(), - Ref::keyword("NOT").optional().to_matchable(), - Ref::keyword("DISTINCT").to_matchable(), - ]) - .config(|this| this.optional()) - .to_matchable(), - Ref::new("BracketedColumnReferenceListGrammar").to_matchable(), - Ref::new("IndexParametersSegment").optional().to_matchable(), - ]) - .to_matchable(), - Sequence::new(vec![ - Ref::new("PrimaryKeyGrammar").to_matchable(), - Ref::new("BracketedColumnReferenceListGrammar").to_matchable(), - Ref::new("IndexParametersSegment").optional().to_matchable(), - ]) - .to_matchable(), - Sequence::new(vec![ - Ref::keyword("EXCLUDE").to_matchable(), - Sequence::new(vec![ - Ref::keyword("USING").to_matchable(), - Ref::new("IndexAccessMethodSegment").to_matchable(), - ]) - .config(|this| this.optional()) - .to_matchable(), - Bracketed::new(vec![ - Delimited::new(vec![ - Ref::new("ExclusionConstraintElementSegment").to_matchable(), - ]) - .to_matchable(), - ]) - .to_matchable(), - Ref::new("IndexParametersSegment").optional().to_matchable(), - Sequence::new(vec![ - Ref::keyword("WHERE").to_matchable(), - Bracketed::new(vec![Ref::new("ExpressionSegment").to_matchable()]) - .to_matchable(), - ]) - .config(|this| this.optional()) - .to_matchable(), - ]) - .to_matchable(), - Sequence::new(vec![ - Ref::keyword("FOREIGN").to_matchable(), - Ref::keyword("KEY").to_matchable(), - Ref::new("BracketedColumnReferenceListGrammar").to_matchable(), - Ref::new("ReferenceDefinitionGrammar").to_matchable(), - ]) - .to_matchable(), - ]) - .to_matchable(), - AnyNumberOf::new(vec![ - one_of(vec![ - Ref::keyword("DEFERRABLE").to_matchable(), - Sequence::new(vec![ - Ref::keyword("NOT").to_matchable(), - Ref::keyword("DEFERRABLE").to_matchable(), - ]) - .to_matchable(), - ]) - .to_matchable(), - one_of(vec![ - Sequence::new(vec![ - Ref::keyword("INITIALLY").to_matchable(), - Ref::keyword("DEFERRED").to_matchable(), - ]) - .to_matchable(), - Sequence::new(vec![ - Ref::keyword("INITIALLY").to_matchable(), - Ref::keyword("IMMEDIATE").to_matchable(), - ]) - .to_matchable(), - ]) - .to_matchable(), - Sequence::new(vec![ - Ref::keyword("NOT").to_matchable(), - Ref::keyword("VALID").to_matchable(), - ]) - .to_matchable(), - Sequence::new(vec![ - Ref::keyword("NO").to_matchable(), - Ref::keyword("INHERIT").to_matchable(), - ]) - .to_matchable(), + MetaSegment::indent().to_matchable(), + table_constraint_body_grammar(), + MetaSegment::dedent().to_matchable(), ]) .to_matchable(), + table_constraint_body_grammar(), ]) .to_matchable() }) diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/alter_table.yml b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/alter_table.yml index 890c49557..08791c05e 100644 --- a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/alter_table.yml +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/alter_table.yml @@ -810,11 +810,12 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: addresses - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: address - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: address + - end_bracket: ) - statement_terminator: ; - statement: - alter_table_statement: @@ -838,11 +839,12 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: addresses - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: address - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: address + - end_bracket: ) - keyword: MATCH - keyword: FULL - statement_terminator: ; @@ -868,11 +870,12 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: addresses - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: address - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: address + - end_bracket: ) - keyword: ON - keyword: DELETE - keyword: RESTRICT @@ -902,11 +905,12 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: addresses - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: address - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: address + - end_bracket: ) - keyword: NOT - keyword: VALID - statement_terminator: ; @@ -1198,11 +1202,12 @@ file: - naked_identifier: landing - dot: . - naked_identifier: workorder - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: id - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: id + - end_bracket: ) - statement_terminator: ; - statement: - alter_table_statement: diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_table.yml b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_table.yml index 3ffd95e7a..2f09e2f9e 100644 --- a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_table.yml +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_table.yml @@ -1339,11 +1339,12 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: groups - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: group_id - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: group_id + - end_bracket: ) - keyword: ON - keyword: DELETE - keyword: CASCADE @@ -1356,11 +1357,12 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: groups - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: group_id - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: group_id + - end_bracket: ) - keyword: ON - keyword: UPDATE - keyword: RESTRICT @@ -1373,11 +1375,12 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: groups - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: group_id - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: group_id + - end_bracket: ) - keyword: MATCH - keyword: SIMPLE - end_bracket: ) @@ -2351,14 +2354,15 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: table1 - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: col1 - - comma: ',' - - column_reference: - - naked_identifier: col2 - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: col1 + - comma: ',' + - column_reference: + - naked_identifier: col2 + - end_bracket: ) - keyword: ON - keyword: DELETE - keyword: SET @@ -2413,14 +2417,15 @@ file: - keyword: REFERENCES - table_reference: - naked_identifier: table1 - - bracketed: - - start_bracket: ( - - column_reference: - - naked_identifier: col1 - - comma: ',' - - column_reference: - - naked_identifier: col2 - - end_bracket: ) + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: col1 + - comma: ',' + - column_reference: + - naked_identifier: col2 + - end_bracket: ) - keyword: ON - keyword: DELETE - keyword: SET diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.sql b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.sql new file mode 100644 index 000000000..8c5708c32 --- /dev/null +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.sql @@ -0,0 +1,14 @@ +CREATE EXTENSION IF NOT EXISTS vector; + +CREATE TABLE IF NOT EXISTS example_embeddings ( + id BIGSERIAL NOT NULL, + source_id BIGINT NOT NULL, + content TEXT NOT NULL, + embedding vector(1536), + labels TEXT[], + PRIMARY KEY (source_id, id), + CONSTRAINT fk_example_source + FOREIGN KEY (source_id) + REFERENCES example_sources(source_id) + ON DELETE CASCADE +) PARTITION BY LIST (source_id); diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.yml b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.yml new file mode 100644 index 000000000..2cfa5e924 --- /dev/null +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.yml @@ -0,0 +1,108 @@ +file: +- statement: + - create_extension_statement: + - keyword: CREATE + - keyword: EXTENSION + - keyword: IF + - keyword: NOT + - keyword: EXISTS + - extension_reference: + - naked_identifier: vector +- statement_terminator: ; +- statement: + - create_table_statement: + - keyword: CREATE + - keyword: TABLE + - keyword: IF + - keyword: NOT + - keyword: EXISTS + - table_reference: + - naked_identifier: example_embeddings + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: id + - data_type: + - keyword: BIGSERIAL + - column_constraint_segment: + - keyword: NOT + - keyword: 'NULL' + - comma: ',' + - column_reference: + - naked_identifier: source_id + - data_type: + - keyword: BIGINT + - column_constraint_segment: + - keyword: NOT + - keyword: 'NULL' + - comma: ',' + - column_reference: + - naked_identifier: content + - data_type: + - keyword: TEXT + - column_constraint_segment: + - keyword: NOT + - keyword: 'NULL' + - comma: ',' + - column_reference: + - naked_identifier: embedding + - data_type: + - data_type_identifier: vector + - bracketed_arguments: + - bracketed: + - start_bracket: ( + - numeric_literal: '1536' + - end_bracket: ) + - comma: ',' + - column_reference: + - naked_identifier: labels + - data_type: + - keyword: TEXT + - start_square_bracket: '[' + - end_square_bracket: ']' + - comma: ',' + - table_constraint: + - keyword: PRIMARY + - keyword: KEY + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: source_id + - comma: ',' + - column_reference: + - naked_identifier: id + - end_bracket: ) + - comma: ',' + - table_constraint: + - keyword: CONSTRAINT + - object_reference: + - naked_identifier: fk_example_source + - keyword: FOREIGN + - keyword: KEY + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: source_id + - end_bracket: ) + - keyword: REFERENCES + - table_reference: + - naked_identifier: example_sources + - referenced_column_list: + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: source_id + - end_bracket: ) + - keyword: ON + - keyword: DELETE + - keyword: CASCADE + - end_bracket: ) + - keyword: PARTITION + - keyword: BY + - keyword: LIST + - bracketed: + - start_bracket: ( + - column_reference: + - naked_identifier: source_id + - end_bracket: ) +- statement_terminator: ; diff --git a/crates/lib/src/core/default_config.cfg b/crates/lib/src/core/default_config.cfg index 4fd68039c..e7f202a28 100644 --- a/crates/lib/src/core/default_config.cfg +++ b/crates/lib/src/core/default_config.cfg @@ -98,6 +98,7 @@ spacing_after = touch spacing_before = touch [sqruff:layout:type:start_square_bracket] +spacing_before = touch:inline spacing_after = touch [sqruff:layout:type:end_square_bracket] @@ -178,6 +179,9 @@ spacing_before = touch:inline [sqruff:layout:type:array_accessor] spacing_before = touch:inline +[sqruff:layout:type:referenced_column_list] +spacing_before = touch:inline + [sqruff:layout:type:colon] spacing_before = touch diff --git a/crates/lib/test/fixtures/rules/std_rule_cases/LT01_LT02-postgres.yml b/crates/lib/test/fixtures/rules/std_rule_cases/LT01_LT02-postgres.yml new file mode 100644 index 000000000..21ef7f80c --- /dev/null +++ b/crates/lib/test/fixtures/rules/std_rule_cases/LT01_LT02-postgres.yml @@ -0,0 +1,21 @@ +rule: LT01,LT02 + +test_pass_postgres_pgvector_create_table_layout: + pass_str: | + CREATE EXTENSION IF NOT EXISTS vector; + + CREATE TABLE IF NOT EXISTS example_embeddings ( + id BIGSERIAL NOT NULL, + source_id BIGINT NOT NULL, + content TEXT NOT NULL, + embedding vector(1536), + labels TEXT[], + PRIMARY KEY (source_id, id), + CONSTRAINT fk_example_source + FOREIGN KEY (source_id) + REFERENCES example_sources(source_id) + ON DELETE CASCADE + ) PARTITION BY LIST (source_id); + configs: + core: + dialect: postgres From 00818b8bae72d43d602c3a7c66225104baf3598b Mon Sep 17 00:00:00 2001 From: Michael Tibben Date: Wed, 17 Jun 2026 06:25:36 +1000 Subject: [PATCH 2/2] fix: scope postgres array suffix spacing --- crates/lib-core/src/dialects/syntax.rs | 1 + crates/lib-dialects/src/postgres.rs | 26 ++++++++---- .../dialects/postgres/sqlfluff/array.yml | 36 +++++++++-------- .../postgres/sqlfluff/create_table.yml | 9 +++-- .../postgres/sqlfluff/create_view.yml | 15 ++++--- .../dialects/postgres/sqlfluff/datatypes.yml | 40 ++++++++++--------- .../issue_2747_pgvector_formatting.yml | 5 ++- crates/lib/src/core/default_config.cfg | 5 ++- .../rules/std_rule_cases/LT01-excessive.yml | 21 ++++++++++ 9 files changed, 103 insertions(+), 55 deletions(-) diff --git a/crates/lib-core/src/dialects/syntax.rs b/crates/lib-core/src/dialects/syntax.rs index 431167ab6..f5aea788c 100644 --- a/crates/lib-core/src/dialects/syntax.rs +++ b/crates/lib-core/src/dialects/syntax.rs @@ -134,6 +134,7 @@ pub enum SyntaxKind { IntervalExpression, ArrayType, SizedArrayType, + ArrayTypeSuffix, SelectStatement, OverlapsClause, SelectClause, diff --git a/crates/lib-dialects/src/postgres.rs b/crates/lib-dialects/src/postgres.rs index 02022453b..a77b34002 100644 --- a/crates/lib-dialects/src/postgres.rs +++ b/crates/lib-dialects/src/postgres.rs @@ -285,14 +285,7 @@ fn build_datatype_segment_grammar(pgvector: bool) -> Matchable { ]) .to_matchable(), one_of(vec![ - AnyNumberOf::new(vec![ - Bracketed::new(vec![ - Ref::new("ExpressionSegment").optional().to_matchable(), - ]) - .config(|this| this.bracket_type("square")) - .to_matchable(), - ]) - .to_matchable(), + Ref::new("ArrayTypeSuffixSegment").to_matchable(), Ref::new("ArrayTypeSegment").to_matchable(), Ref::new("SizedArrayTypeSegment").to_matchable(), ]) @@ -1272,6 +1265,23 @@ pub fn raw_dialect() -> Dialect { .into(), )]); + postgres.add([( + "ArrayTypeSuffixSegment".into(), + NodeMatcher::new(SyntaxKind::ArrayTypeSuffix, |_| { + AnyNumberOf::new(vec![ + Bracketed::new(vec![ + Ref::new("ExpressionSegment").optional().to_matchable(), + ]) + .config(|this| this.bracket_type("square")) + .to_matchable(), + ]) + .config(|this| this.min_times(1)) + .to_matchable() + }) + .to_matchable() + .into(), + )]); + postgres.replace_grammar("DatatypeSegment", build_datatype_segment_grammar(false)); postgres.replace_grammar("ArrayTypeSegment", Ref::keyword("ARRAY").to_matchable()); diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/array.yml b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/array.yml index fcb6472e0..22b5c1cde 100644 --- a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/array.yml +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/array.yml @@ -69,17 +69,19 @@ file: - naked_identifier: pay_by_quarter - data_type: - keyword: integer - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' - comma: ',' - column_reference: - naked_identifier: schedule - data_type: - keyword: text - - start_square_bracket: '[' - - end_square_bracket: ']' - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' + - start_square_bracket: '[' + - end_square_bracket: ']' - end_bracket: ) - statement_terminator: ; - statement: @@ -94,14 +96,15 @@ file: - naked_identifier: squares - data_type: - keyword: integer - - start_square_bracket: '[' - - expression: - - numeric_literal: '3' - - end_square_bracket: ']' - - start_square_bracket: '[' - - expression: - - numeric_literal: '3' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - expression: + - numeric_literal: '3' + - end_square_bracket: ']' + - start_square_bracket: '[' + - expression: + - numeric_literal: '3' + - end_square_bracket: ']' - end_bracket: ) - statement_terminator: ; - statement: @@ -587,8 +590,9 @@ file: - casting_operator: '::' - data_type: - keyword: int - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' - alias_expression: - keyword: AS - naked_identifier: f1 diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_table.yml b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_table.yml index 2f09e2f9e..cfb4d3ab6 100644 --- a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_table.yml +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_table.yml @@ -75,10 +75,11 @@ file: - naked_identifier: vector - data_type: - keyword: int - - start_square_bracket: '[' - - end_square_bracket: ']' - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' + - start_square_bracket: '[' + - end_square_bracket: ']' - end_bracket: ) - statement_terminator: ; - statement: diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_view.yml b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_view.yml index 2d58a8a54..373469325 100644 --- a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_view.yml +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/create_view.yml @@ -505,8 +505,9 @@ file: - casting_operator: '::' - data_type: - keyword: INTEGER - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' - alias_expression: - keyword: AS - quoted_identifier: '"ancestors"' @@ -529,8 +530,9 @@ file: - casting_operator: '::' - data_type: - keyword: text - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' - alias_expression: - keyword: AS - quoted_identifier: '"path"' @@ -549,8 +551,9 @@ file: - casting_operator: '::' - data_type: - keyword: INTEGER - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' - alias_expression: - keyword: AS - quoted_identifier: '"path_nodes"' diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/datatypes.yml b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/datatypes.yml index 283af6bc4..9d764b4ee 100644 --- a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/datatypes.yml +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/datatypes.yml @@ -639,39 +639,43 @@ file: - naked_identifier: a - data_type: - keyword: integer - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' - comma: ',' - column_reference: - naked_identifier: b - data_type: - keyword: float - - start_square_bracket: '[' - - end_square_bracket: ']' - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' + - start_square_bracket: '[' + - end_square_bracket: ']' - comma: ',' - column_reference: - naked_identifier: c - data_type: - keyword: char - - start_square_bracket: '[' - - expression: - - numeric_literal: '1' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - expression: + - numeric_literal: '1' + - end_square_bracket: ']' - comma: ',' - column_reference: - naked_identifier: d - data_type: - keyword: jsonb - - start_square_bracket: '[' - - expression: - - numeric_literal: '3' - - end_square_bracket: ']' - - start_square_bracket: '[' - - expression: - - numeric_literal: '5' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - expression: + - numeric_literal: '3' + - end_square_bracket: ']' + - start_square_bracket: '[' + - expression: + - numeric_literal: '5' + - end_square_bracket: ']' - comma: ',' - column_reference: - naked_identifier: e diff --git a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.yml b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.yml index 2cfa5e924..b597b01d8 100644 --- a/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.yml +++ b/crates/lib-dialects/test/fixtures/dialects/postgres/sqlfluff/issue_2747_pgvector_formatting.yml @@ -58,8 +58,9 @@ file: - naked_identifier: labels - data_type: - keyword: TEXT - - start_square_bracket: '[' - - end_square_bracket: ']' + - array_type_suffix: + - start_square_bracket: '[' + - end_square_bracket: ']' - comma: ',' - table_constraint: - keyword: PRIMARY diff --git a/crates/lib/src/core/default_config.cfg b/crates/lib/src/core/default_config.cfg index e7f202a28..4ec022203 100644 --- a/crates/lib/src/core/default_config.cfg +++ b/crates/lib/src/core/default_config.cfg @@ -98,7 +98,6 @@ spacing_after = touch spacing_before = touch [sqruff:layout:type:start_square_bracket] -spacing_before = touch:inline spacing_after = touch [sqruff:layout:type:end_square_bracket] @@ -163,6 +162,10 @@ spacing_within = touch [sqruff:layout:type:sized_array_type] spacing_within = touch +[sqruff:layout:type:array_type_suffix] +spacing_before = touch:inline +spacing_within = touch:inline + [sqruff:layout:type:struct_type] spacing_within = touch:inline diff --git a/crates/lib/test/fixtures/rules/std_rule_cases/LT01-excessive.yml b/crates/lib/test/fixtures/rules/std_rule_cases/LT01-excessive.yml index 284b2707f..d2d372040 100644 --- a/crates/lib/test/fixtures/rules/std_rule_cases/LT01-excessive.yml +++ b/crates/lib/test/fixtures/rules/std_rule_cases/LT01-excessive.yml @@ -288,6 +288,20 @@ test_bigquery_datatype: core: dialect: bigquery +test_bigquery_array_literal_spacing: + pass_str: | + SELECT [1, 2, 3] AS arr + configs: + core: + dialect: bigquery + +test_duckdb_array_literal_spacing: + pass_str: | + SELECT [1, 2, 3] AS arr + configs: + core: + dialect: duckdb + test_athena_datatype: pass_str: | select @@ -340,6 +354,13 @@ test_sparksql_datatype: core: dialect: sparksql +test_sparksql_array_literal_spacing: + pass_str: | + SELECT [1, 2, 3] AS arr + configs: + core: + dialect: sparksql + test_exasol_datatype: pass_str: | select