diff --git a/pkg/sql/logictest/testdata/logic_test/udf_setof b/pkg/sql/logictest/testdata/logic_test/udf_setof index e50a1ff5a643..f4c7c682cc99 100644 --- a/pkg/sql/logictest/testdata/logic_test/udf_setof +++ b/pkg/sql/logictest/testdata/logic_test/udf_setof @@ -325,3 +325,63 @@ statement ok DROP FUNCTION f145414; subtest end + +# Test that ORDER BY is preserved in SQL UDFs with OUT parameters +subtest out_params_ordering + +statement ok +CREATE TABLE test_ordering (x INT PRIMARY KEY, y INT); + +statement ok +INSERT INTO test_ordering VALUES (1, 10), (2, 20), (3, 30), (4, 40); + +statement ok +CREATE FUNCTION f_out_desc(OUT a INT, OUT b INT) RETURNS SETOF RECORD AS $$ + SELECT x, y FROM test_ordering ORDER BY x DESC; +$$ LANGUAGE SQL; + +query T nosort +SELECT f_out_desc(); +---- +(4,40) +(3,30) +(2,20) +(1,10) + +query II nosort +SELECT * FROM f_out_desc(); +---- +4 40 +3 30 +2 20 +1 10 + +statement ok +DROP FUNCTION f_out_desc; + +statement ok +CREATE FUNCTION f_out_asc(OUT a INT, OUT b INT) RETURNS SETOF RECORD AS $$ + SELECT x, y FROM test_ordering ORDER BY x ASC; +$$ LANGUAGE SQL; + +query T nosort +SELECT f_out_asc(); +---- +(1,10) +(2,20) +(3,30) +(4,40) + +query II nosort +SELECT * FROM f_out_asc(); +---- +1 10 +2 20 +3 30 +4 40 + +statement ok +DROP FUNCTION f_out_asc; +DROP TABLE test_ordering; + +subtest end diff --git a/pkg/sql/opt/optbuilder/routine.go b/pkg/sql/opt/optbuilder/routine.go index b249afbc0a94..34908cfc9db9 100644 --- a/pkg/sql/opt/optbuilder/routine.go +++ b/pkg/sql/opt/optbuilder/routine.go @@ -653,6 +653,7 @@ func (b *Builder) finalizeRoutineReturnType( // into a single tuple column. func (b *Builder) combineRoutineColsIntoTuple(stmtScope *scope) *scope { outScope := stmtScope.push() + outScope.copyOrdering(stmtScope) elems := make(memo.ScalarListExpr, len(stmtScope.cols)) typContents := make([]*types.T, len(stmtScope.cols)) for i := range stmtScope.cols { @@ -721,6 +722,7 @@ func (b *Builder) maybeAddRoutineAssignmentCasts( return stmtScope } outScope := stmtScope.push() + outScope.copyOrdering(stmtScope) for i, col := range stmtScope.cols { scalar := b.factory.ConstructVariable(col.id) if !col.typ.Identical(desiredTypes[i]) {