From 228372b8d05ed32240a32f9cacacffda74cbd348 Mon Sep 17 00:00:00 2001 From: Rushabh Lathia Date: Tue, 24 Mar 2026 11:34:50 +0530 Subject: [PATCH 2/2] Add documentation and tests for ORDER BY ALL - Documentation: Added comprehensive documentation in queries.sgml and reference pages (select.sgml, select_into.sgml) - Tests: Added comprehensive regression tests covering regular SQL usage, PL/pgSQL contexts (FOR loops, cursors, functions), deparsing of view definitions, and negative test cases --- doc/src/sgml/queries.sgml | 16 + doc/src/sgml/ref/select.sgml | 15 +- doc/src/sgml/ref/select_into.sgml | 2 +- src/pl/plpgsql/src/expected/plpgsql_misc.out | 153 +++++ src/pl/plpgsql/src/sql/plpgsql_misc.sql | 121 ++++ src/test/regress/expected/order_by_all.out | 614 +++++++++++++++++++ src/test/regress/parallel_schedule | 1 + src/test/regress/sql/order_by_all.sql | 247 ++++++++ 8 files changed, 1166 insertions(+), 3 deletions(-) create mode 100644 src/test/regress/expected/order_by_all.out create mode 100644 src/test/regress/sql/order_by_all.sql diff --git a/doc/src/sgml/queries.sgml b/doc/src/sgml/queries.sgml index 4b522213171..71dff217f05 100644 --- a/doc/src/sgml/queries.sgml +++ b/doc/src/sgml/queries.sgml @@ -1911,6 +1911,22 @@ SELECT a + b AS sum, c FROM table1 ORDER BY sum + c; -- wrong column to match some other table column's name. + + PostgreSQL also supports the syntax + ORDER BY ALL, which is equivalent to explicitly + writing all select-list columns. The sort direction (ascending or + descending) and null ordering can optionally be specified; these apply + to all columns. For example: + +SELECT a, b, c FROM table1 ORDER BY ALL; +SELECT a, b, c FROM table1 ORDER BY ALL DESC; +SELECT a, b, c FROM table1 ORDER BY ALL DESC NULLS LAST; + + The first example is equivalent to ORDER BY a, b, c. + The second is equivalent to ORDER BY a DESC, b DESC, c DESC. + This syntax can simplify ad-hoc exploration of data. + + ORDER BY can be applied to the result of a UNION, INTERSECT, or EXCEPT diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index ca5dd14d627..1355ba42d09 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -41,7 +41,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressioncondition ] [ WINDOW window_name AS ( window_definition ) [, ...] ] [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ] - [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] + [ ORDER BY { ALL [ ASC | DESC ] [ NULLS { FIRST | LAST } ] | expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] } ] [ LIMIT { count | ALL } ] [ OFFSET start [ ROW | ROWS ] ] [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ] @@ -1395,7 +1395,7 @@ SELECT DISTINCT ON (location) location, time, report The optional ORDER BY clause has this general form: -ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] +ORDER BY { ALL [ ASC | DESC ] [ NULLS { FIRST | LAST } ] | expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] } The ORDER BY clause causes the result rows to be sorted according to the specified expression(s). If two rows are @@ -1405,6 +1405,17 @@ ORDER BY expression [ ASC | DESC | an implementation-dependent order. + + The form ORDER BY ALL with no explicit + expression provided is + equivalent to writing ORDER BY with all columns of + the SELECT output list. The sort direction + (ascending or descending) and null ordering can optionally be specified + and apply to all columns. For example, ORDER BY ALL DESC + is equivalent to listing all output columns with DESC + after each. + + Each expression can be the name or ordinal number of an output column diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml index cbf865ff838..2e06b9da4e3 100644 --- a/doc/src/sgml/ref/select_into.sgml +++ b/doc/src/sgml/ref/select_into.sgml @@ -31,7 +31,7 @@ SELECT [ ALL | DISTINCT [ ON ( expressioncondition ] [ WINDOW window_name AS ( window_definition ) [, ...] ] [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ] - [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] + [ ORDER BY { ALL [ ASC | DESC ] [ NULLS { FIRST | LAST } ] | expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] } ] [ LIMIT { count | ALL } ] [ OFFSET start [ ROW | ROWS ] ] [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ] diff --git a/src/pl/plpgsql/src/expected/plpgsql_misc.out b/src/pl/plpgsql/src/expected/plpgsql_misc.out index ffb377f5f54..29748846439 100644 --- a/src/pl/plpgsql/src/expected/plpgsql_misc.out +++ b/src/pl/plpgsql/src/expected/plpgsql_misc.out @@ -101,3 +101,156 @@ begin end $$; NOTICE: r.x = 1 NOTICE: r."foreach" = 2 +-- Test ORDER BY ALL in PL/pgSQL contexts +-- This tests the PLpgSQL_Expr grammar rule changes for ORDER BY ALL support +-- Create test table for ORDER BY ALL tests +create table plpgsql_order_test ( + a int, + b text, + c float +); +insert into plpgsql_order_test values + (3, 'foo', 1.5), + (1, 'bar', 2.5), + (2, 'baz', 1.5), + (1, 'qux', 1.5); +-- Test ORDER BY ALL in FOR loop +do $$ +declare + r record; +begin + raise notice 'FOR loop with ORDER BY ALL:'; + for r in select * from plpgsql_order_test order by all + loop + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; +end $$; +NOTICE: FOR loop with ORDER BY ALL: +NOTICE: a=1, b=bar, c=2.5 +NOTICE: a=1, b=qux, c=1.5 +NOTICE: a=2, b=baz, c=1.5 +NOTICE: a=3, b=foo, c=1.5 +-- Test ORDER BY ALL DESC in FOR loop +do $$ +declare + r record; +begin + raise notice 'FOR loop with ORDER BY ALL DESC:'; + for r in select * from plpgsql_order_test order by all desc + loop + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; +end $$; +NOTICE: FOR loop with ORDER BY ALL DESC: +NOTICE: a=3, b=foo, c=1.5 +NOTICE: a=2, b=baz, c=1.5 +NOTICE: a=1, b=qux, c=1.5 +NOTICE: a=1, b=bar, c=2.5 +-- Test ORDER BY ALL with cursor +do $$ +declare + cur cursor for select * from plpgsql_order_test order by all; + r record; +begin + raise notice 'Cursor with ORDER BY ALL:'; + open cur; + loop + fetch cur into r; + exit when not found; + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; + close cur; +end $$; +NOTICE: Cursor with ORDER BY ALL: +NOTICE: a=1, b=bar, c=2.5 +NOTICE: a=1, b=qux, c=1.5 +NOTICE: a=2, b=baz, c=1.5 +NOTICE: a=3, b=foo, c=1.5 +-- Test ORDER BY ALL ASC NULLS FIRST in cursor +do $$ +declare + cur cursor for select * from plpgsql_order_test order by all asc nulls first; + r record; +begin + raise notice 'Cursor with ORDER BY ALL ASC NULLS FIRST:'; + open cur; + loop + fetch cur into r; + exit when not found; + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; + close cur; +end $$; +NOTICE: Cursor with ORDER BY ALL ASC NULLS FIRST: +NOTICE: a=1, b=bar, c=2.5 +NOTICE: a=1, b=qux, c=1.5 +NOTICE: a=2, b=baz, c=1.5 +NOTICE: a=3, b=foo, c=1.5 +-- Test ORDER BY ALL in function with RETURN QUERY +create function test_order_by_all_return() +returns table(a int, b text, c float) as $$ +begin + return query select * from plpgsql_order_test order by all; +end; +$$ language plpgsql; +select * from test_order_by_all_return(); + a | b | c +---+-----+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 3 | foo | 1.5 +(4 rows) + +-- Test ORDER BY ALL DESC in function with RETURN QUERY +create function test_order_by_all_desc_return() +returns table(a int, b text, c float) as $$ +begin + return query select * from plpgsql_order_test order by all desc; +end; +$$ language plpgsql; +select * from test_order_by_all_desc_return(); + a | b | c +---+-----+----- + 3 | foo | 1.5 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(4 rows) + +-- Test ORDER BY ALL with subset of columns +do $$ +declare + r record; +begin + raise notice 'ORDER BY ALL with column subset:'; + for r in select a, b from plpgsql_order_test order by all + loop + raise notice 'a=%, b=%', r.a, r.b; + end loop; +end $$; +NOTICE: ORDER BY ALL with column subset: +NOTICE: a=1, b=bar +NOTICE: a=1, b=qux +NOTICE: a=2, b=baz +NOTICE: a=3, b=foo +-- Test ORDER BY ALL NULLS LAST +do $$ +declare + r record; +begin + raise notice 'ORDER BY ALL NULLS LAST:'; + for r in select * from plpgsql_order_test order by all nulls last + loop + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; +end $$; +NOTICE: ORDER BY ALL NULLS LAST: +NOTICE: a=1, b=bar, c=2.5 +NOTICE: a=1, b=qux, c=1.5 +NOTICE: a=2, b=baz, c=1.5 +NOTICE: a=3, b=foo, c=1.5 +-- Clean up +drop function test_order_by_all_return(); +drop function test_order_by_all_desc_return(); +drop table plpgsql_order_test; diff --git a/src/pl/plpgsql/src/sql/plpgsql_misc.sql b/src/pl/plpgsql/src/sql/plpgsql_misc.sql index 0bc39fcf325..bfc7103146b 100644 --- a/src/pl/plpgsql/src/sql/plpgsql_misc.sql +++ b/src/pl/plpgsql/src/sql/plpgsql_misc.sql @@ -66,3 +66,124 @@ begin raise notice 'r.x = %', r.x; raise notice 'r."foreach" = %', r."foreach"; -- ok end $$; + +-- Test ORDER BY ALL in PL/pgSQL contexts +-- This tests the PLpgSQL_Expr grammar rule changes for ORDER BY ALL support + +-- Create test table for ORDER BY ALL tests +create table plpgsql_order_test ( + a int, + b text, + c float +); + +insert into plpgsql_order_test values + (3, 'foo', 1.5), + (1, 'bar', 2.5), + (2, 'baz', 1.5), + (1, 'qux', 1.5); + +-- Test ORDER BY ALL in FOR loop +do $$ +declare + r record; +begin + raise notice 'FOR loop with ORDER BY ALL:'; + for r in select * from plpgsql_order_test order by all + loop + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; +end $$; + +-- Test ORDER BY ALL DESC in FOR loop +do $$ +declare + r record; +begin + raise notice 'FOR loop with ORDER BY ALL DESC:'; + for r in select * from plpgsql_order_test order by all desc + loop + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; +end $$; + +-- Test ORDER BY ALL with cursor +do $$ +declare + cur cursor for select * from plpgsql_order_test order by all; + r record; +begin + raise notice 'Cursor with ORDER BY ALL:'; + open cur; + loop + fetch cur into r; + exit when not found; + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; + close cur; +end $$; + +-- Test ORDER BY ALL ASC NULLS FIRST in cursor +do $$ +declare + cur cursor for select * from plpgsql_order_test order by all asc nulls first; + r record; +begin + raise notice 'Cursor with ORDER BY ALL ASC NULLS FIRST:'; + open cur; + loop + fetch cur into r; + exit when not found; + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; + close cur; +end $$; + +-- Test ORDER BY ALL in function with RETURN QUERY +create function test_order_by_all_return() +returns table(a int, b text, c float) as $$ +begin + return query select * from plpgsql_order_test order by all; +end; +$$ language plpgsql; + +select * from test_order_by_all_return(); + +-- Test ORDER BY ALL DESC in function with RETURN QUERY +create function test_order_by_all_desc_return() +returns table(a int, b text, c float) as $$ +begin + return query select * from plpgsql_order_test order by all desc; +end; +$$ language plpgsql; + +select * from test_order_by_all_desc_return(); + +-- Test ORDER BY ALL with subset of columns +do $$ +declare + r record; +begin + raise notice 'ORDER BY ALL with column subset:'; + for r in select a, b from plpgsql_order_test order by all + loop + raise notice 'a=%, b=%', r.a, r.b; + end loop; +end $$; + +-- Test ORDER BY ALL NULLS LAST +do $$ +declare + r record; +begin + raise notice 'ORDER BY ALL NULLS LAST:'; + for r in select * from plpgsql_order_test order by all nulls last + loop + raise notice 'a=%, b=%, c=%', r.a, r.b, r.c; + end loop; +end $$; + +-- Clean up +drop function test_order_by_all_return(); +drop function test_order_by_all_desc_return(); +drop table plpgsql_order_test; diff --git a/src/test/regress/expected/order_by_all.out b/src/test/regress/expected/order_by_all.out new file mode 100644 index 00000000000..840af89b16f --- /dev/null +++ b/src/test/regress/expected/order_by_all.out @@ -0,0 +1,614 @@ +-- +-- Test ORDER BY ALL +-- +CREATE TEMP TABLE test_order ( + a int, + b text, + c float +); +INSERT INTO test_order VALUES + (3, 'foo', 1.5), + (1, 'bar', 2.5), + (2, 'baz', 1.5), + (1, 'qux', 1.5); +-- Basic ORDER BY ALL test (default ASC) +SELECT * FROM test_order ORDER BY ALL; + a | b | c +---+-----+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 3 | foo | 1.5 +(4 rows) + +-- ORDER BY ALL ASC (explicit) +SELECT * FROM test_order ORDER BY ALL ASC; + a | b | c +---+-----+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 3 | foo | 1.5 +(4 rows) + +-- ORDER BY ALL DESC +SELECT * FROM test_order ORDER BY ALL DESC; + a | b | c +---+-----+----- + 3 | foo | 1.5 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(4 rows) + +-- ORDER BY ALL with NULLS FIRST +SELECT * FROM test_order ORDER BY ALL NULLS FIRST; + a | b | c +---+-----+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 3 | foo | 1.5 +(4 rows) + +-- ORDER BY ALL DESC NULLS LAST +SELECT * FROM test_order ORDER BY ALL DESC NULLS LAST; + a | b | c +---+-----+----- + 3 | foo | 1.5 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(4 rows) + +-- ORDER BY ALL with specific columns in SELECT +SELECT a, b FROM test_order ORDER BY ALL; + a | b +---+----- + 1 | bar + 1 | qux + 2 | baz + 3 | foo +(4 rows) + +-- ORDER BY ALL with expressions +SELECT a, b, a + c AS sum FROM test_order ORDER BY ALL; + a | b | sum +---+-----+----- + 1 | bar | 3.5 + 1 | qux | 2.5 + 2 | baz | 3.5 + 3 | foo | 4.5 +(4 rows) + +-- ORDER BY ALL with aggregates (should only order by non-aggregated columns) +SELECT a, COUNT(*) FROM test_order GROUP BY a ORDER BY ALL; + a | count +---+------- + 1 | 2 + 2 | 1 + 3 | 1 +(3 rows) + +-- ORDER BY ALL with WHERE clause +SELECT * FROM test_order WHERE a > 1 ORDER BY ALL; + a | b | c +---+-----+----- + 2 | baz | 1.5 + 3 | foo | 1.5 +(2 rows) + +-- Verify that ORDER BY ALL is equivalent to listing all columns +SELECT * FROM test_order ORDER BY ALL; + a | b | c +---+-----+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 3 | foo | 1.5 +(4 rows) + +SELECT * FROM test_order ORDER BY a, b, c; + a | b | c +---+-----+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 3 | foo | 1.5 +(4 rows) + +-- ORDER BY ALL with LIMIT +SELECT * FROM test_order ORDER BY ALL LIMIT 2; + a | b | c +---+-----+----- + 1 | bar | 2.5 + 1 | qux | 1.5 +(2 rows) + +-- Test deparsing of ORDER BY ALL (ruleutils.c) +-- Verify that ORDER BY ALL with modifiers is correctly preserved in view definitions +-- View with ORDER BY ALL (default) +CREATE VIEW view_order_all AS + SELECT * FROM test_order ORDER BY ALL; +NOTICE: view "view_order_all" will be a temporary view +DETAIL: It depends on temporary table test_order. +SELECT pg_get_viewdef('view_order_all'::regclass, true); + pg_get_viewdef +-------------------- + SELECT a, + + b, + + c + + FROM test_order+ + ORDER BY ALL; +(1 row) + +-- View with ORDER BY ALL ASC +CREATE VIEW view_order_all_asc AS + SELECT * FROM test_order ORDER BY ALL ASC; +NOTICE: view "view_order_all_asc" will be a temporary view +DETAIL: It depends on temporary table test_order. +SELECT pg_get_viewdef('view_order_all_asc'::regclass, true); + pg_get_viewdef +-------------------- + SELECT a, + + b, + + c + + FROM test_order+ + ORDER BY ALL; +(1 row) + +-- View with ORDER BY ALL DESC +CREATE VIEW view_order_all_desc AS + SELECT * FROM test_order ORDER BY ALL DESC; +NOTICE: view "view_order_all_desc" will be a temporary view +DETAIL: It depends on temporary table test_order. +SELECT pg_get_viewdef('view_order_all_desc'::regclass, true); + pg_get_viewdef +---------------------- + SELECT a, + + b, + + c + + FROM test_order + + ORDER BY ALL DESC; +(1 row) + +-- View with ORDER BY ALL NULLS FIRST +CREATE VIEW view_order_all_nulls_first AS + SELECT * FROM test_order ORDER BY ALL NULLS FIRST; +NOTICE: view "view_order_all_nulls_first" will be a temporary view +DETAIL: It depends on temporary table test_order. +SELECT pg_get_viewdef('view_order_all_nulls_first'::regclass, true); + pg_get_viewdef +----------------------------- + SELECT a, + + b, + + c + + FROM test_order + + ORDER BY ALL NULLS FIRST; +(1 row) + +-- View with ORDER BY ALL NULLS LAST +CREATE VIEW view_order_all_nulls_last AS + SELECT * FROM test_order ORDER BY ALL NULLS LAST; +NOTICE: view "view_order_all_nulls_last" will be a temporary view +DETAIL: It depends on temporary table test_order. +SELECT pg_get_viewdef('view_order_all_nulls_last'::regclass, true); + pg_get_viewdef +-------------------- + SELECT a, + + b, + + c + + FROM test_order+ + ORDER BY ALL; +(1 row) + +-- View with ORDER BY ALL DESC NULLS FIRST +CREATE VIEW view_order_all_desc_nulls_first AS + SELECT * FROM test_order ORDER BY ALL DESC NULLS FIRST; +NOTICE: view "view_order_all_desc_nulls_first" will be a temporary view +DETAIL: It depends on temporary table test_order. +SELECT pg_get_viewdef('view_order_all_desc_nulls_first'::regclass, true); + pg_get_viewdef +---------------------- + SELECT a, + + b, + + c + + FROM test_order + + ORDER BY ALL DESC; +(1 row) + +-- View with ORDER BY ALL ASC NULLS LAST +CREATE VIEW view_order_all_asc_nulls_last AS + SELECT * FROM test_order ORDER BY ALL ASC NULLS LAST; +NOTICE: view "view_order_all_asc_nulls_last" will be a temporary view +DETAIL: It depends on temporary table test_order. +SELECT pg_get_viewdef('view_order_all_asc_nulls_last'::regclass, true); + pg_get_viewdef +-------------------- + SELECT a, + + b, + + c + + FROM test_order+ + ORDER BY ALL; +(1 row) + +-- Verify the views actually work +SELECT * FROM view_order_all; + a | b | c +---+-----+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 3 | foo | 1.5 +(4 rows) + +SELECT * FROM view_order_all_desc; + a | b | c +---+-----+----- + 3 | foo | 1.5 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(4 rows) + +-- Clean up views +DROP VIEW view_order_all; +DROP VIEW view_order_all_asc; +DROP VIEW view_order_all_desc; +DROP VIEW view_order_all_nulls_first; +DROP VIEW view_order_all_nulls_last; +DROP VIEW view_order_all_desc_nulls_first; +DROP VIEW view_order_all_asc_nulls_last; +-- Test with NULL values +CREATE TEMP TABLE test_order_nulls ( + a int, + b text, + c float +); +INSERT INTO test_order_nulls VALUES + (3, 'foo', 1.5), + (1, 'bar', 2.5), + (2, 'baz', 1.5), + (1, 'qux', 1.5), + (NULL, 'null_a', 3.0), + (2, NULL, 2.0), + (3, 'foo', NULL); +-- ORDER BY ALL with NULLs (default ASC) +SELECT * FROM test_order_nulls ORDER BY ALL; + a | b | c +---+--------+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 2 | | 2 + 3 | foo | 1.5 + 3 | foo | + | null_a | 3 +(7 rows) + +-- ORDER BY ALL ASC (explicit) with NULLs +SELECT * FROM test_order_nulls ORDER BY ALL ASC; + a | b | c +---+--------+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 2 | | 2 + 3 | foo | 1.5 + 3 | foo | + | null_a | 3 +(7 rows) + +-- ORDER BY ALL DESC with NULLs +SELECT * FROM test_order_nulls ORDER BY ALL DESC; + a | b | c +---+--------+----- + | null_a | 3 + 3 | foo | + 3 | foo | 1.5 + 2 | | 2 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(7 rows) + +-- ORDER BY ALL NULLS FIRST (with default ASC) +SELECT * FROM test_order_nulls ORDER BY ALL NULLS FIRST; + a | b | c +---+--------+----- + | null_a | 3 + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | | 2 + 2 | baz | 1.5 + 3 | foo | + 3 | foo | 1.5 +(7 rows) + +-- ORDER BY ALL NULLS LAST (with default ASC) +SELECT * FROM test_order_nulls ORDER BY ALL NULLS LAST; + a | b | c +---+--------+----- + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | baz | 1.5 + 2 | | 2 + 3 | foo | 1.5 + 3 | foo | + | null_a | 3 +(7 rows) + +-- ORDER BY ALL ASC NULLS FIRST +SELECT * FROM test_order_nulls ORDER BY ALL ASC NULLS FIRST; + a | b | c +---+--------+----- + | null_a | 3 + 1 | bar | 2.5 + 1 | qux | 1.5 + 2 | | 2 + 2 | baz | 1.5 + 3 | foo | + 3 | foo | 1.5 +(7 rows) + +-- ORDER BY ALL DESC NULLS LAST +SELECT * FROM test_order_nulls ORDER BY ALL DESC NULLS LAST; + a | b | c +---+--------+----- + 3 | foo | 1.5 + 3 | foo | + 2 | baz | 1.5 + 2 | | 2 + 1 | qux | 1.5 + 1 | bar | 2.5 + | null_a | 3 +(7 rows) + +-- ORDER BY ALL DESC NULLS FIRST +SELECT * FROM test_order_nulls ORDER BY ALL DESC NULLS FIRST; + a | b | c +---+--------+----- + | null_a | 3 + 3 | foo | + 3 | foo | 1.5 + 2 | | 2 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(7 rows) + +-- Verify ORDER BY ALL DESC is equivalent to listing all columns DESC +SELECT * FROM test_order_nulls ORDER BY ALL DESC; + a | b | c +---+--------+----- + | null_a | 3 + 3 | foo | + 3 | foo | 1.5 + 2 | | 2 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(7 rows) + +SELECT * FROM test_order_nulls ORDER BY a DESC, b DESC, c DESC; + a | b | c +---+--------+----- + | null_a | 3 + 3 | foo | + 3 | foo | 1.5 + 2 | | 2 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(7 rows) + +-- Test with WHERE clause and NULLs +SELECT * FROM test_order_nulls WHERE a IS NOT NULL ORDER BY ALL DESC; + a | b | c +---+-----+----- + 3 | foo | + 3 | foo | 1.5 + 2 | | 2 + 2 | baz | 1.5 + 1 | qux | 1.5 + 1 | bar | 2.5 +(6 rows) + +-- Test with LIMIT and NULLs +SELECT * FROM test_order_nulls ORDER BY ALL DESC LIMIT 3; + a | b | c +---+--------+----- + | null_a | 3 + 3 | foo | + 3 | foo | 1.5 +(3 rows) + +-- Test with subset of columns and NULLs +SELECT a, b FROM test_order_nulls ORDER BY ALL DESC; + a | b +---+-------- + | null_a + 3 | foo + 3 | foo + 2 | + 2 | baz + 1 | qux + 1 | bar +(7 rows) + +-- Clean up +DROP TABLE test_order_nulls; +-- Negative tests: invalid ORDER BY ALL syntax +-- ORDER BY ALL cannot be mixed with explicit column specifications +SELECT * FROM test_order ORDER BY ALL, a; +ERROR: syntax error at or near "," +LINE 1: SELECT * FROM test_order ORDER BY ALL, a; + ^ +-- ORDER BY ALL cannot use USING operator +SELECT * FROM test_order ORDER BY ALL USING <; +ERROR: syntax error at or near "USING" +LINE 1: SELECT * FROM test_order ORDER BY ALL USING <; + ^ +-- ORDER BY ALL with multiple ALL keywords (should fail) +SELECT * FROM test_order ORDER BY ALL, ALL; +ERROR: syntax error at or near "," +LINE 1: SELECT * FROM test_order ORDER BY ALL, ALL; + ^ +-- ORDER BY ALL with set operations (UNION) +SELECT a, b FROM test_order UNION SELECT a, b FROM test_order ORDER BY ALL; + a | b +---+----- + 1 | bar + 1 | qux + 2 | baz + 3 | foo +(4 rows) + +-- ORDER BY ALL with column number reference throw an error +SELECT a, b FROM test_order ORDER BY ALL, 1; +ERROR: syntax error at or near "," +LINE 1: SELECT a, b FROM test_order ORDER BY ALL, 1; + ^ +-- Additional test coverage for edge cases +-- Test ORDER BY ALL in subquery +SELECT * FROM (SELECT a, b FROM test_order ORDER BY ALL) sq; + a | b +---+----- + 1 | bar + 1 | qux + 2 | baz + 3 | foo +(4 rows) + +-- Test ORDER BY ALL with nested subquery +SELECT * FROM ( + SELECT a, b FROM ( + SELECT * FROM test_order ORDER BY ALL DESC + ) sub1 ORDER BY ALL +) sub2; + a | b +---+----- + 1 | bar + 1 | qux + 2 | baz + 3 | foo +(4 rows) + +-- Test ORDER BY ALL with DISTINCT +SELECT DISTINCT a, b FROM test_order ORDER BY ALL; + a | b +---+----- + 1 | bar + 1 | qux + 2 | baz + 3 | foo +(4 rows) + +-- Test ORDER BY ALL with JOIN +CREATE TEMP TABLE test_order2 ( + x int, + y text +); +INSERT INTO test_order2 VALUES (1, 'alpha'), (2, 'beta'), (3, 'gamma'); +SELECT t1.a, t1.b, t2.x, t2.y +FROM test_order t1 +JOIN test_order2 t2 ON t1.a = t2.x +ORDER BY ALL; + a | b | x | y +---+-----+---+------- + 1 | bar | 1 | alpha + 1 | qux | 1 | alpha + 2 | baz | 2 | beta + 3 | foo | 3 | gamma +(4 rows) + +DROP TABLE test_order2; +-- Test ORDER BY ALL with CTE +WITH cte AS ( + SELECT a, b FROM test_order WHERE a > 1 +) +SELECT * FROM cte ORDER BY ALL; + a | b +---+----- + 2 | baz + 3 | foo +(2 rows) + +-- Test ORDER BY ALL with window function (ORDER BY ALL in outer query) +SELECT a, b, ROW_NUMBER() OVER (PARTITION BY a ORDER BY b) as rn +FROM test_order +ORDER BY ALL; + a | b | rn +---+-----+---- + 1 | bar | 1 + 1 | qux | 2 + 2 | baz | 1 + 3 | foo | 1 +(4 rows) + +-- Test ORDER BY ALL with INTERSECT (should fail like UNION) +SELECT a, b FROM test_order INTERSECT SELECT a, b FROM test_order ORDER BY ALL; + a | b +---+----- + 1 | bar + 1 | qux + 2 | baz + 3 | foo +(4 rows) + +-- Test ORDER BY ALL with EXCEPT (should fail like UNION) +SELECT a, b FROM test_order EXCEPT SELECT a, b FROM test_order ORDER BY ALL; + a | b +---+--- +(0 rows) + +-- Test ORDER BY ALL with VALUES +SELECT * FROM (VALUES (3, 'foo'), (1, 'bar'), (2, 'baz')) AS t(x, y) ORDER BY ALL; + x | y +---+----- + 1 | bar + 2 | baz + 3 | foo +(3 rows) + +-- Test ORDER BY ALL with only computed columns (no junk) +SELECT a + 1 AS col1, b || '_test' AS col2 FROM test_order ORDER BY ALL; + col1 | col2 +------+---------- + 2 | bar_test + 2 | qux_test + 3 | baz_test + 4 | foo_test +(4 rows) + +-- Test behavior when all selected columns would be junk (using ctid/system columns) +-- This should still work as system columns aren't marked as junk in target list when explicitly selected +SELECT ctid FROM test_order ORDER BY ALL; + ctid +------- + (0,1) + (0,2) + (0,3) + (0,4) +(4 rows) + +-- Test ORDER BY ALL with UNION ALL in subquery (ORDER BY on outer query should work) +SELECT * FROM ( + SELECT a, b FROM test_order + UNION ALL + SELECT a, b FROM test_order +) sub ORDER BY ALL; + a | b +---+----- + 1 | bar + 1 | bar + 1 | qux + 1 | qux + 2 | baz + 2 | baz + 3 | foo + 3 | foo +(8 rows) + +-- Clean up +DROP TABLE test_order; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 549e9b2d7be..5caee1ac899 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -62,6 +62,7 @@ test: sanity_check # join depends on create_misc # ---------- test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update delete namespace prepared_xacts +test: order_by_all # ---------- # Another group of parallel tests diff --git a/src/test/regress/sql/order_by_all.sql b/src/test/regress/sql/order_by_all.sql new file mode 100644 index 00000000000..9b40831a0a9 --- /dev/null +++ b/src/test/regress/sql/order_by_all.sql @@ -0,0 +1,247 @@ +-- +-- Test ORDER BY ALL +-- + +CREATE TEMP TABLE test_order ( + a int, + b text, + c float +); + +INSERT INTO test_order VALUES + (3, 'foo', 1.5), + (1, 'bar', 2.5), + (2, 'baz', 1.5), + (1, 'qux', 1.5); + +-- Basic ORDER BY ALL test (default ASC) +SELECT * FROM test_order ORDER BY ALL; + +-- ORDER BY ALL ASC (explicit) +SELECT * FROM test_order ORDER BY ALL ASC; + +-- ORDER BY ALL DESC +SELECT * FROM test_order ORDER BY ALL DESC; + +-- ORDER BY ALL with NULLS FIRST +SELECT * FROM test_order ORDER BY ALL NULLS FIRST; + +-- ORDER BY ALL DESC NULLS LAST +SELECT * FROM test_order ORDER BY ALL DESC NULLS LAST; + +-- ORDER BY ALL with specific columns in SELECT +SELECT a, b FROM test_order ORDER BY ALL; + +-- ORDER BY ALL with expressions +SELECT a, b, a + c AS sum FROM test_order ORDER BY ALL; + +-- ORDER BY ALL with aggregates (should only order by non-aggregated columns) +SELECT a, COUNT(*) FROM test_order GROUP BY a ORDER BY ALL; + +-- ORDER BY ALL with WHERE clause +SELECT * FROM test_order WHERE a > 1 ORDER BY ALL; + +-- Verify that ORDER BY ALL is equivalent to listing all columns +SELECT * FROM test_order ORDER BY ALL; +SELECT * FROM test_order ORDER BY a, b, c; + +-- ORDER BY ALL with LIMIT +SELECT * FROM test_order ORDER BY ALL LIMIT 2; + +-- Test deparsing of ORDER BY ALL (ruleutils.c) +-- Verify that ORDER BY ALL with modifiers is correctly preserved in view definitions + +-- View with ORDER BY ALL (default) +CREATE VIEW view_order_all AS + SELECT * FROM test_order ORDER BY ALL; + +SELECT pg_get_viewdef('view_order_all'::regclass, true); + +-- View with ORDER BY ALL ASC +CREATE VIEW view_order_all_asc AS + SELECT * FROM test_order ORDER BY ALL ASC; + +SELECT pg_get_viewdef('view_order_all_asc'::regclass, true); + +-- View with ORDER BY ALL DESC +CREATE VIEW view_order_all_desc AS + SELECT * FROM test_order ORDER BY ALL DESC; + +SELECT pg_get_viewdef('view_order_all_desc'::regclass, true); + +-- View with ORDER BY ALL NULLS FIRST +CREATE VIEW view_order_all_nulls_first AS + SELECT * FROM test_order ORDER BY ALL NULLS FIRST; + +SELECT pg_get_viewdef('view_order_all_nulls_first'::regclass, true); + +-- View with ORDER BY ALL NULLS LAST +CREATE VIEW view_order_all_nulls_last AS + SELECT * FROM test_order ORDER BY ALL NULLS LAST; + +SELECT pg_get_viewdef('view_order_all_nulls_last'::regclass, true); + +-- View with ORDER BY ALL DESC NULLS FIRST +CREATE VIEW view_order_all_desc_nulls_first AS + SELECT * FROM test_order ORDER BY ALL DESC NULLS FIRST; + +SELECT pg_get_viewdef('view_order_all_desc_nulls_first'::regclass, true); + +-- View with ORDER BY ALL ASC NULLS LAST +CREATE VIEW view_order_all_asc_nulls_last AS + SELECT * FROM test_order ORDER BY ALL ASC NULLS LAST; + +SELECT pg_get_viewdef('view_order_all_asc_nulls_last'::regclass, true); + +-- Verify the views actually work +SELECT * FROM view_order_all; +SELECT * FROM view_order_all_desc; + +-- Clean up views +DROP VIEW view_order_all; +DROP VIEW view_order_all_asc; +DROP VIEW view_order_all_desc; +DROP VIEW view_order_all_nulls_first; +DROP VIEW view_order_all_nulls_last; +DROP VIEW view_order_all_desc_nulls_first; +DROP VIEW view_order_all_asc_nulls_last; + +-- Test with NULL values +CREATE TEMP TABLE test_order_nulls ( + a int, + b text, + c float +); + +INSERT INTO test_order_nulls VALUES + (3, 'foo', 1.5), + (1, 'bar', 2.5), + (2, 'baz', 1.5), + (1, 'qux', 1.5), + (NULL, 'null_a', 3.0), + (2, NULL, 2.0), + (3, 'foo', NULL); + +-- ORDER BY ALL with NULLs (default ASC) +SELECT * FROM test_order_nulls ORDER BY ALL; + +-- ORDER BY ALL ASC (explicit) with NULLs +SELECT * FROM test_order_nulls ORDER BY ALL ASC; + +-- ORDER BY ALL DESC with NULLs +SELECT * FROM test_order_nulls ORDER BY ALL DESC; + +-- ORDER BY ALL NULLS FIRST (with default ASC) +SELECT * FROM test_order_nulls ORDER BY ALL NULLS FIRST; + +-- ORDER BY ALL NULLS LAST (with default ASC) +SELECT * FROM test_order_nulls ORDER BY ALL NULLS LAST; + +-- ORDER BY ALL ASC NULLS FIRST +SELECT * FROM test_order_nulls ORDER BY ALL ASC NULLS FIRST; + +-- ORDER BY ALL DESC NULLS LAST +SELECT * FROM test_order_nulls ORDER BY ALL DESC NULLS LAST; + +-- ORDER BY ALL DESC NULLS FIRST +SELECT * FROM test_order_nulls ORDER BY ALL DESC NULLS FIRST; + +-- Verify ORDER BY ALL DESC is equivalent to listing all columns DESC +SELECT * FROM test_order_nulls ORDER BY ALL DESC; +SELECT * FROM test_order_nulls ORDER BY a DESC, b DESC, c DESC; + +-- Test with WHERE clause and NULLs +SELECT * FROM test_order_nulls WHERE a IS NOT NULL ORDER BY ALL DESC; + +-- Test with LIMIT and NULLs +SELECT * FROM test_order_nulls ORDER BY ALL DESC LIMIT 3; + +-- Test with subset of columns and NULLs +SELECT a, b FROM test_order_nulls ORDER BY ALL DESC; + +-- Clean up +DROP TABLE test_order_nulls; + +-- Negative tests: invalid ORDER BY ALL syntax + +-- ORDER BY ALL cannot be mixed with explicit column specifications +SELECT * FROM test_order ORDER BY ALL, a; + +-- ORDER BY ALL cannot use USING operator +SELECT * FROM test_order ORDER BY ALL USING <; + +-- ORDER BY ALL with multiple ALL keywords (should fail) +SELECT * FROM test_order ORDER BY ALL, ALL; + +-- ORDER BY ALL with set operations (UNION) +SELECT a, b FROM test_order UNION SELECT a, b FROM test_order ORDER BY ALL; + +-- ORDER BY ALL with column number reference throw an error +SELECT a, b FROM test_order ORDER BY ALL, 1; + +-- Additional test coverage for edge cases + +-- Test ORDER BY ALL in subquery +SELECT * FROM (SELECT a, b FROM test_order ORDER BY ALL) sq; + +-- Test ORDER BY ALL with nested subquery +SELECT * FROM ( + SELECT a, b FROM ( + SELECT * FROM test_order ORDER BY ALL DESC + ) sub1 ORDER BY ALL +) sub2; + +-- Test ORDER BY ALL with DISTINCT +SELECT DISTINCT a, b FROM test_order ORDER BY ALL; + +-- Test ORDER BY ALL with JOIN +CREATE TEMP TABLE test_order2 ( + x int, + y text +); + +INSERT INTO test_order2 VALUES (1, 'alpha'), (2, 'beta'), (3, 'gamma'); + +SELECT t1.a, t1.b, t2.x, t2.y +FROM test_order t1 +JOIN test_order2 t2 ON t1.a = t2.x +ORDER BY ALL; + +DROP TABLE test_order2; + +-- Test ORDER BY ALL with CTE +WITH cte AS ( + SELECT a, b FROM test_order WHERE a > 1 +) +SELECT * FROM cte ORDER BY ALL; + +-- Test ORDER BY ALL with window function (ORDER BY ALL in outer query) +SELECT a, b, ROW_NUMBER() OVER (PARTITION BY a ORDER BY b) as rn +FROM test_order +ORDER BY ALL; + +-- Test ORDER BY ALL with INTERSECT (should fail like UNION) +SELECT a, b FROM test_order INTERSECT SELECT a, b FROM test_order ORDER BY ALL; + +-- Test ORDER BY ALL with EXCEPT (should fail like UNION) +SELECT a, b FROM test_order EXCEPT SELECT a, b FROM test_order ORDER BY ALL; + +-- Test ORDER BY ALL with VALUES +SELECT * FROM (VALUES (3, 'foo'), (1, 'bar'), (2, 'baz')) AS t(x, y) ORDER BY ALL; + +-- Test ORDER BY ALL with only computed columns (no junk) +SELECT a + 1 AS col1, b || '_test' AS col2 FROM test_order ORDER BY ALL; + +-- Test behavior when all selected columns would be junk (using ctid/system columns) +-- This should still work as system columns aren't marked as junk in target list when explicitly selected +SELECT ctid FROM test_order ORDER BY ALL; + +-- Test ORDER BY ALL with UNION ALL in subquery (ORDER BY on outer query should work) +SELECT * FROM ( + SELECT a, b FROM test_order + UNION ALL + SELECT a, b FROM test_order +) sub ORDER BY ALL; + +-- Clean up +DROP TABLE test_order; -- 2.43.0