From 56785732cbef6346075d795e64bb5f494fdd92c2 Mon Sep 17 00:00:00 2001 From: Arno Dirlam Date: Fri, 18 Sep 2026 22:51:43 +0200 Subject: [PATCH 1/2] Formatter: Preserve enclosing indentation across CTEs --- lib/pgFormatter/Beautify.pm | 18 ++- t/02_regress.t | 2 +- t/pg-test-files/expected/plpgsql.sql | 16 +-- t/pg-test-files/expected/with.sql | 10 +- t/test-files/cte_indentation.sql | 38 ++++++ t/test-files/expected/cte_indentation.sql | 155 ++++++++++++++++++++++ 6 files changed, 221 insertions(+), 18 deletions(-) create mode 100644 t/test-files/cte_indentation.sql create mode 100644 t/test-files/expected/cte_indentation.sql diff --git a/lib/pgFormatter/Beautify.pm b/lib/pgFormatter/Beautify.pm index 517f6d0d..03e0778e 100755 --- a/lib/pgFormatter/Beautify.pm +++ b/lib/pgFormatter/Beautify.pm @@ -1859,7 +1859,10 @@ sub beautify { and ( !defined $last or - ( $last ne ')' and $self->_next_token !~ /^(TIME|FUNCTION)/i ) + ( ( $last ne ')' + or ( $self->{'_current_sql_stmt'} eq 'INSERT' + and !$self->{'_parenthesis_level'} ) ) + and $self->_next_token !~ /^(TIME|FUNCTION)/i ) ) ) { @@ -1875,6 +1878,11 @@ sub beautify { $self->{'no_break'} = 1 if ( uc( $self->_next_token ) eq 'ORDINALITY' ); } + # Sibling CTEs and the final query inherit the enclosing block's level. + if ( $self->{'_is_in_with'} == 1 and !$self->{'_parenthesis_level'} ) { + $self->{_cte_base_level} = $self->{'_level'}; + $self->{_cte_base_stack} = [ @{ $self->{'_level_stack'} } ]; + } $self->{'_is_in_materialized'} = 0; } elsif ( $token =~ /^WITH$/i @@ -1992,10 +2000,12 @@ sub beautify { $token, $last ); $self->_back( $token, $last ); } - $self->_add_token($token); - if ( !$self->{'_is_in_operator'} ) { - $self->_reset_level( $token, $last ); + if ( !$self->{'_is_in_operator'} and defined $self->{_cte_base_level} ) { + $self->_set_level( $self->{_cte_base_level}, $token, $last ); + @{ $self->{'_level_stack'} } = @{ $self->{_cte_base_stack} }; + $self->{'content'} .= $self->_indent if ( $self->{'_new_line'} ); } + $self->_add_token($token); if ( $self->{'_is_in_with'} ) { if ( defined $self->_next_token && $self->_next_token eq ',' ) diff --git a/t/02_regress.t b/t/02_regress.t index 29bc6092..840a2023 100755 --- a/t/02_regress.t +++ b/t/02_regress.t @@ -1,4 +1,4 @@ -use Test::Simple tests => 86; +use Test::Simple tests => 87; use File::Temp qw/ tempfile /; my $pg_format = $ENV{PG_FORMAT} // './pg_format'; # set to the full path to 'pg_format' to test installed binary in /usr/bin diff --git a/t/pg-test-files/expected/plpgsql.sql b/t/pg-test-files/expected/plpgsql.sql index 57873887..a6b858e1 100644 --- a/t/pg-test-files/expected/plpgsql.sql +++ b/t/pg-test-files/expected/plpgsql.sql @@ -6521,14 +6521,14 @@ BEGIN level1_no HAVING sum(delta) < 0 -) -SELECT - level1_no -FROM - p - JOIN transition_table_level2 c ON c.parent_no = p.level1_no -INTO - x; + ) + SELECT + level1_no + FROM + p + JOIN transition_table_level2 c ON c.parent_no = p.level1_no + INTO + x; IF FOUND THEN RAISE EXCEPTION 'RI error'; END IF; diff --git a/t/pg-test-files/expected/with.sql b/t/pg-test-files/expected/with.sql index 488dcca1..b1a05f61 100644 --- a/t/pg-test-files/expected/with.sql +++ b/t/pg-test-files/expected/with.sql @@ -1540,11 +1540,11 @@ CREATE RULE r2 AS ON UPDATE WITH t AS ( SELECT OLD.* -) -UPDATE - y SET - a = t.n FROM - t; + ) + UPDATE + y SET + a = t.n FROM + t; -- -- test for bug #4902 diff --git a/t/test-files/cte_indentation.sql b/t/test-files/cte_indentation.sql new file mode 100644 index 00000000..690913fa --- /dev/null +++ b/t/test-files/cte_indentation.sql @@ -0,0 +1,38 @@ +WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; + +DO $$ +BEGIN +WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; +END +$$; + +DO $$ +BEGIN +IF TRUE THEN +WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; +ELSE +PERFORM 0; +END IF; +END +$$; + +INSERT INTO results (id) +WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; + +DO $$ +BEGIN +INSERT INTO results (id) +WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; +END +$$; + +DO $$ +BEGIN +IF TRUE THEN +INSERT INTO results (id) +WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; +ELSE +PERFORM 0; +END IF; +END +$$; diff --git a/t/test-files/expected/cte_indentation.sql b/t/test-files/expected/cte_indentation.sql new file mode 100644 index 00000000..d46be5c5 --- /dev/null +++ b/t/test-files/expected/cte_indentation.sql @@ -0,0 +1,155 @@ +WITH first_cte AS ( + SELECT + 1 AS id +), +second_cte AS ( + SELECT + id + FROM + first_cte +), +third_cte AS ( + SELECT + id + FROM + second_cte +) +SELECT + id +FROM + third_cte; + +DO $$ +BEGIN + WITH first_cte AS ( + SELECT + 1 AS id + ), + second_cte AS ( + SELECT + id + FROM + first_cte + ), + third_cte AS ( + SELECT + id + FROM + second_cte + ) + SELECT + id + FROM + third_cte; +END +$$; + +DO $$ +BEGIN + IF TRUE THEN + WITH first_cte AS ( + SELECT + 1 AS id + ), + second_cte AS ( + SELECT + id + FROM + first_cte + ), + third_cte AS ( + SELECT + id + FROM + second_cte + ) + SELECT + id + FROM + third_cte; + ELSE + PERFORM + 0; + END IF; +END +$$; + +INSERT INTO results (id) +WITH first_cte AS ( + SELECT + 1 AS id +), +second_cte AS ( + SELECT + id + FROM + first_cte +), +third_cte AS ( + SELECT + id + FROM + second_cte +) +SELECT + id +FROM + third_cte; + +DO $$ +BEGIN + INSERT INTO results (id) + WITH first_cte AS ( + SELECT + 1 AS id + ), + second_cte AS ( + SELECT + id + FROM + first_cte + ), + third_cte AS ( + SELECT + id + FROM + second_cte + ) + SELECT + id + FROM + third_cte; +END +$$; + +DO $$ +BEGIN + IF TRUE THEN + INSERT INTO results (id) + WITH first_cte AS ( + SELECT + 1 AS id + ), + second_cte AS ( + SELECT + id + FROM + first_cte + ), + third_cte AS ( + SELECT + id + FROM + second_cte + ) + SELECT + id + FROM + third_cte; + ELSE + PERFORM + 0; + END IF; +END +$$; + From 7d761b50cf7a3370dbdbf3ec1868537e120d657d Mon Sep 17 00:00:00 2001 From: Arno Dirlam Date: Fri, 18 Sep 2026 23:24:41 +0200 Subject: [PATCH 2/2] Tests: Remove redundant CTE indentation cases --- t/test-files/cte_indentation.sql | 31 +++---- t/test-files/expected/cte_indentation.sql | 100 ++-------------------- 2 files changed, 16 insertions(+), 115 deletions(-) diff --git a/t/test-files/cte_indentation.sql b/t/test-files/cte_indentation.sql index 690913fa..196b1390 100644 --- a/t/test-files/cte_indentation.sql +++ b/t/test-files/cte_indentation.sql @@ -1,36 +1,27 @@ -WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; - -DO $$ -BEGIN -WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; -END -$$; +INSERT INTO results (id) +WITH first_cte AS (SELECT 1 AS id), +second_cte AS (SELECT id FROM first_cte) +SELECT id FROM second_cte; DO $$ BEGIN IF TRUE THEN -WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; +INSERT INTO results (id) +WITH first_cte AS (SELECT 1 AS id), +second_cte AS (SELECT id FROM first_cte) +SELECT id FROM second_cte; ELSE PERFORM 0; END IF; END $$; -INSERT INTO results (id) -WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; - -DO $$ -BEGIN -INSERT INTO results (id) -WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; -END -$$; - DO $$ BEGIN IF TRUE THEN -INSERT INTO results (id) -WITH first_cte AS (SELECT 1 AS id), second_cte AS (SELECT id FROM first_cte), third_cte AS (SELECT id FROM second_cte) SELECT id FROM third_cte; +WITH first_cte AS (SELECT 1 AS id), +second_cte AS (SELECT id FROM first_cte) +SELECT id FROM second_cte; ELSE PERFORM 0; END IF; diff --git a/t/test-files/expected/cte_indentation.sql b/t/test-files/expected/cte_indentation.sql index d46be5c5..a80144e9 100644 --- a/t/test-files/expected/cte_indentation.sql +++ b/t/test-files/expected/cte_indentation.sql @@ -1,3 +1,4 @@ +INSERT INTO results (id) WITH first_cte AS ( SELECT 1 AS id @@ -7,46 +8,16 @@ second_cte AS ( id FROM first_cte -), -third_cte AS ( - SELECT - id - FROM - second_cte ) SELECT id FROM - third_cte; - -DO $$ -BEGIN - WITH first_cte AS ( - SELECT - 1 AS id - ), - second_cte AS ( - SELECT - id - FROM - first_cte - ), - third_cte AS ( - SELECT - id - FROM - second_cte - ) - SELECT - id - FROM - third_cte; -END -$$; + second_cte; DO $$ BEGIN IF TRUE THEN + INSERT INTO results (id) WITH first_cte AS ( SELECT 1 AS id @@ -56,17 +27,11 @@ BEGIN id FROM first_cte - ), - third_cte AS ( - SELECT - id - FROM - second_cte ) SELECT id FROM - third_cte; + second_cte; ELSE PERFORM 0; @@ -74,58 +39,9 @@ BEGIN END $$; -INSERT INTO results (id) -WITH first_cte AS ( - SELECT - 1 AS id -), -second_cte AS ( - SELECT - id - FROM - first_cte -), -third_cte AS ( - SELECT - id - FROM - second_cte -) -SELECT - id -FROM - third_cte; - -DO $$ -BEGIN - INSERT INTO results (id) - WITH first_cte AS ( - SELECT - 1 AS id - ), - second_cte AS ( - SELECT - id - FROM - first_cte - ), - third_cte AS ( - SELECT - id - FROM - second_cte - ) - SELECT - id - FROM - third_cte; -END -$$; - DO $$ BEGIN IF TRUE THEN - INSERT INTO results (id) WITH first_cte AS ( SELECT 1 AS id @@ -135,17 +51,11 @@ BEGIN id FROM first_cte - ), - third_cte AS ( - SELECT - id - FROM - second_cte ) SELECT id FROM - third_cte; + second_cte; ELSE PERFORM 0;