Skip to content

Commit 8338079

Browse files
committed
optbuilder: preserve ORDER BY in set-returning UDFs with OUT parameters
When set-returning UDFs with OUT parameters are called directly in a SELECT list (e.g., SELECT f()), CockroachDB wraps multiple result columns into a single tuple column. During this transformation, two functions created new scopes without preserving ordering information, causing ORDER BY clauses in the UDF body to be ignored. This commit fixes the issue by calling copyOrdering() in two places: 1. combineRoutineColsIntoTuple - when wrapping columns into a tuple 2. maybeAddRoutineAssignmentCasts - when adding type casts The copyOrdering() method not only copies the ordering metadata but also adds the columns referenced by the ordering to extraCols, ensuring they remain available for the optimizer to enforce the ordering. Fixes #144013 Release note (bug fix): Fixed a bug where ORDER BY clauses in set-returning SQL user-defined functions with OUT parameters were ignored when the function was called directly in a SELECT list (e.g., SELECT f()). The ordering is now properly preserved and enforced. Epic: None
1 parent ae0b3d7 commit 8338079

File tree

3 files changed

+66
-4
lines changed

3 files changed

+66
-4
lines changed

pkg/sql/logictest/testdata/logic_test/udf_setof

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,63 @@ statement ok
325325
DROP FUNCTION f145414;
326326

327327
subtest end
328+
329+
# Test that ORDER BY is preserved in SQL UDFs with OUT parameters
330+
subtest out_params_ordering
331+
332+
statement ok
333+
CREATE TABLE test_ordering (x INT PRIMARY KEY, y INT);
334+
335+
statement ok
336+
INSERT INTO test_ordering VALUES (1, 10), (2, 20), (3, 30), (4, 40);
337+
338+
statement ok
339+
CREATE FUNCTION f_out_desc(OUT a INT, OUT b INT) RETURNS SETOF RECORD AS $$
340+
SELECT x, y FROM test_ordering ORDER BY x DESC;
341+
$$ LANGUAGE SQL;
342+
343+
query T nosort
344+
SELECT f_out_desc();
345+
----
346+
(4,40)
347+
(3,30)
348+
(2,20)
349+
(1,10)
350+
351+
query II nosort
352+
SELECT * FROM f_out_desc();
353+
----
354+
4 40
355+
3 30
356+
2 20
357+
1 10
358+
359+
statement ok
360+
DROP FUNCTION f_out_desc;
361+
362+
statement ok
363+
CREATE FUNCTION f_out_asc(OUT a INT, OUT b INT) RETURNS SETOF RECORD AS $$
364+
SELECT x, y FROM test_ordering ORDER BY x ASC;
365+
$$ LANGUAGE SQL;
366+
367+
query T nosort
368+
SELECT f_out_asc();
369+
----
370+
(1,10)
371+
(2,20)
372+
(3,30)
373+
(4,40)
374+
375+
query II nosort
376+
SELECT * FROM f_out_asc();
377+
----
378+
1 10
379+
2 20
380+
3 30
381+
4 40
382+
383+
statement ok
384+
DROP FUNCTION f_out_asc;
385+
DROP TABLE test_ordering;
386+
387+
subtest end

pkg/sql/opt/optbuilder/routine.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,7 @@ func (b *Builder) finalizeRoutineReturnType(
653653
// into a single tuple column.
654654
func (b *Builder) combineRoutineColsIntoTuple(stmtScope *scope) *scope {
655655
outScope := stmtScope.push()
656+
outScope.copyOrdering(stmtScope)
656657
elems := make(memo.ScalarListExpr, len(stmtScope.cols))
657658
typContents := make([]*types.T, len(stmtScope.cols))
658659
for i := range stmtScope.cols {
@@ -721,6 +722,7 @@ func (b *Builder) maybeAddRoutineAssignmentCasts(
721722
return stmtScope
722723
}
723724
outScope := stmtScope.push()
725+
outScope.copyOrdering(stmtScope)
724726
for i, col := range stmtScope.cols {
725727
scalar := b.factory.ConstructVariable(col.id)
726728
if !col.typ.Identical(desiredTypes[i]) {

pkg/sql/opt/optbuilder/testdata/udf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -865,9 +865,9 @@ project
865865
├── params: i:1
866866
└── body
867867
└── project
868-
├── columns: column8:8
868+
├── columns: column8:8 b:3
869869
├── project
870-
│ ├── columns: column7:7
870+
│ ├── columns: column7:7 b:3
871871
│ ├── limit
872872
│ │ ├── columns: a:2!null b:3 c:4!null
873873
│ │ ├── internal-ordering: -3
@@ -911,9 +911,9 @@ project
911911
├── params: i:1
912912
└── body
913913
└── project
914-
├── columns: column8:8
914+
├── columns: column8:8 b:3
915915
├── project
916-
│ ├── columns: column7:7
916+
│ ├── columns: column7:7 b:3
917917
│ ├── limit
918918
│ │ ├── columns: a:2!null b:3 c:4!null
919919
│ │ ├── internal-ordering: -3

0 commit comments

Comments
 (0)