Skip to content

Support scalar function aliases in FROM - #3178

Merged
fulghum merged 1 commit into
mainfrom
fulghum/doltgres-3173
Aug 25, 2026
Merged

Support scalar function aliases in FROM#3178
fulghum merged 1 commit into
mainfrom
fulghum/doltgres-3173

Conversation

@fulghum

@fulghum fulghum commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Enable PostgreSQL scalar-function aliases in FROM to name the function output column, including generate_subscripts(...) AS k. Record-returning functions retain their declared OUT column names.

Pin go-mysql-server to the supporting planner and strict-SRF wrapper changes. Regression coverage includes alias references, NULL arguments, empty arrays, explicit column aliases, direct SRF calls, table-sourced NULL arrays, two scalar SRFs, and mixed scalar/record-returning functions in both orders. Assertions were verified against PostgreSQL 15.17 and with GOWORK=off go test ./testing/go -run TestSetReturningFunctions -count=1.

Fixes #3173

Depends on: go-mysql-server #3726

@fulghum
fulghum force-pushed the fulghum/doltgres-3173 branch from f70b768 to 55a62d2 Compare August 24, 2026 19:23
@fulghum
fulghum requested a review from Hydrocharged August 24, 2026 19:35
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor
Main PR
Total 42090 42090
Successful 19038 19119
Failures 23052 22971
Partial Successes1 5477 5481
Main PR
Successful 45.2316% 45.4241%
Failures 54.7684% 54.5759%

${\color{red}Regressions (1)}$

random

QUERY:          (SELECT unique1 AS random
  FROM onek ORDER BY random() LIMIT 1)
INTERSECT
(SELECT unique1 AS random
  FROM onek ORDER BY random() LIMIT 1)
INTERSECT
(SELECT unique1 AS random
  FROM onek ORDER BY random() LIMIT 1);
RECEIVED ERROR: expected row count 0 but received 1

${\color{lightgreen}Progressions (81)}$

aggregates

QUERY: select s1, s2, sm
from generate_series(1, 3) s1,
     lateral (select s2, sum(s1 + s2) sm
              from generate_series(1, 3) s2 group by s2) ss
order by 1, 2;
QUERY: select array(select sum(x+y) s
            from generate_series(1,3) y group by y order by s)
  from generate_series(1,3) x;

bitmapops

QUERY: INSERT INTO bmscantest
  SELECT (r%53), (r%59), 'foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo'
  FROM generate_series(1,70000) r;
QUERY: SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1;
QUERY: SELECT count(*) FROM bmscantest WHERE a = 1 OR b = 1;

btree_index

QUERY: insert into btree_tall_tbl select g, repeat('x', 250)
from generate_series(1, 130) g;
QUERY: INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i;
QUERY: INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;

compression

QUERY: SELECT SUBSTR(f1, 200, 5) FROM cmdata1;

copy

QUERY: insert into parted_copytest select x,1,'One' from generate_series(1,1000) x;
QUERY: insert into parted_copytest select x,2,'Two' from generate_series(1001,1010) x;
QUERY: insert into parted_copytest select x,1,'One' from generate_series(1011,1020) x;

create_index

QUERY: INSERT INTO array_gin_test SELECT ARRAY[1, g%5, g] FROM generate_series(1, 10000) g;

domain

QUERY: select m, m is not null from makedcomp(1,2) m;

foreign_key

QUERY: insert into other_partitioned_fk
  select 2048, x from generate_series(1,10) x;
QUERY: insert into fk_notpartitioned_pk (a, b)
  select 2048, x from generate_series(1,10) x;

gin

QUERY: insert into t_gin_test_tbl select array[1, g, g/10], array[2, g, g/10]
  from generate_series(1, 20000) g;

groupingsets

QUERY: insert into bug_16784 select g/10, g from generate_series(1,40) g;

hash_index

QUERY: INSERT INTO hash_split_heap SELECT a/2 FROM generate_series(1, 25000) a;

horology

QUERY: SELECT i, to_timestamp('2018-11-02 12:34:56', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
QUERY: SELECT i, to_timestamp('2018-11-02 12:34:56.1', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;

indexing

QUERY: INSERT INTO idxpart (a, b, c) SELECT i, i, i FROM generate_series(1, 50) i;

inherit

QUERY: SELECT
    ARRAY(SELECT f.i FROM (
        (SELECT d + g.i FROM generate_series(4, 30, 3) d ORDER BY 1)
        UNION ALL
        (SELECT d + g.i FROM generate_series(0, 30, 5) d ORDER BY 1)
    ) f(i)
    ORDER BY f.i LIMIT 10)
FROM generate_series(1, 3) g(i);

join

QUERY: insert into tt3 select x, repeat('xyzzy', 100) from generate_series(1,10000) x;
QUERY: select * from generate_series(100,200) g,
  lateral (select * from int8_tbl a where g = q1 union all
           select * from int8_tbl b where g = q2) ss;
QUERY: insert into fkest select x, x/10, x/10, x/100 from generate_series(1,1000) x;
QUERY: create unique index on fkest(x, x10, x100);
QUERY: analyze fkest;
QUERY: alter table fkest add constraint fk
  foreign key (x, x10b, x100) references fkest (x, x10, x100);

json

QUERY: SELECT array_to_json(array_agg(x),false) from generate_series(5,10) x;
QUERY: SELECT row_to_json(row((select array_agg(x) as d from generate_series(5,10) x)),false);

merge

QUERY: INSERT INTO pa_source SELECT id, id * 10  FROM generate_series(1,14) AS id;
QUERY: INSERT INTO pa_target SELECT id, id * 100, 'initial' FROM generate_series(1,14,2) AS id;
QUERY: INSERT INTO pa_target SELECT id, id * 100, 'initial' FROM generate_series(1,14,2) AS id;

partition_aggregate

QUERY: INSERT INTO pagg_tab1 SELECT i % 30, i % 20 FROM generate_series(0, 299, 2) i;
QUERY: INSERT INTO pagg_tab2 SELECT i % 20, i % 30 FROM generate_series(0, 299, 3) i;
QUERY: SELECT t1.x, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3;
QUERY: SELECT t1.x, sum(t1.y), count(t1) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3;
QUERY: SELECT t1.y, sum(t1.x), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.y HAVING avg(t1.x) > 10 ORDER BY 1, 2, 3;
QUERY: SELECT a.x, b.y, count(*) FROM (SELECT * FROM pagg_tab1 WHERE x < 20) a LEFT JOIN (SELECT * FROM pagg_tab2 WHERE y > 10) b ON a.x = b.y WHERE a.x > 5 or b.y < 20  GROUP BY a.x, b.y ORDER BY 1, 2;

Footnotes

  1. These are tests that we're marking as Successful, however they do not match the expected output in some way. This is due to small differences, such as different wording on the error messages, or the column names being incorrect while the data itself is correct.

@coffeegoddd

coffeegoddd commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

@fulghum DOLT

read_tests from_latency to_latency percent_change
covering_index_scan_postgres 2.43 2.43 0.0
groupby_scan_postgres 78.6 78.6 0.0
index_join_postgres 2.22 2.22 0.0
index_join_scan_postgres 1.58 1.58 0.0
index_scan_postgres 484.44 484.44 0.0
oltp_point_select 0.36 0.36 0.0
oltp_read_only 6.32 6.21 -1.74
select_random_points 0.7 0.7 0.0
select_random_ranges 1.01 1.01 0.0
table_scan_postgres 484.44 484.44 0.0
types_table_scan_postgres 1213.57 1213.57 0.0
write_tests from_latency to_latency percent_change
oltp_delete_insert_postgres 6.67 6.67 0.0
oltp_insert 3.36 3.3 -1.79
oltp_read_write 13.22 13.22 0.0
oltp_update_index 3.55 3.55 0.0
oltp_update_non_index 3.25 3.25 0.0
oltp_write_only 6.91 6.91 0.0
types_delete_insert_postgres 7.17 7.17 0.0

@itoqa

itoqa Bot commented Aug 24, 2026

Copy link
Copy Markdown

Ito QA test results
Commit: 55a62d2: 13 test cases ran, 1 failed ❌, 12 passed ✅.

Summary

The run covers generated-row behavior across direct naming, ordering, filtering, joins, record-shaped results, and array indexing, including boundary conditions such as invalid dimensions, empty arrays, and nullable arrays. Happy-path expansion and composition behave correctly, but an edge case produces incorrect query results for nullable arrays.

Merge with caution — the PR introduces a medium-severity correctness issue in SQL result generation for nullable array inputs, despite the broader alias and row-expansion behavior passing. This is attributable to the change and should be treated as a merge risk, while no unrelated findings are present.

Tests run by Ito

View full run

Result Severity Type Description
Medium severity General The NULL-array query returned one fabricated NULL row. PostgreSQL's strict generate_subscripts behavior requires a NULL input to produce no rows, matching the existing empty-result expectation.
Alias The query returned 1, 2, and 3 in order, and the arithmetic query returned 11, 12, and 13.
Alias The correlated query used the generated alias as an array index and returned the expected values {11,21,31}.
General Valid dimension 1 returns subscripts 1, 2, and 3 in order. Unsupported dimensions 0 and 2 return clear errors without producing rows.
General Rows with different array lengths kept their own subscripts and values. The first row returned {1,2,3} with {10,20,30}, while the second returned {1,2} with {700,800}; no values crossed between rows.
General The query returned the scalar values and the record fields under the expected names in both function orders. The rows were 1 and 2 with the matching record value and depth of 1.
Record The record query returned the named columns relid and depth, with anc_test and 1 as their values.
Record The record function returned its declared relid field as anc_test, with no extra ordinality column.
Rev Direct aliases worked for integer, decimal, and timestamp generated values. Each query returned three rows with the expected column name and values.
Series The query kept the name r and returned exactly the two rows greater than 1: 2 and 3.
Series The query kept rows 2 and 3, removed ordinal 1, and ordered the remaining rows from 3 down to 2.
Series The join returned rows 1, 2, and 3 with the matching labels one, two, and three. The unrelated row 99 was correctly left out.
Series The outer join kept all three generated rows. Rows 1 and 2 matched their values, and row 3 correctly kept a blank joined value for NULL.

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread servercfg/config.go

@Hydrocharged Hydrocharged left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@fulghum
fulghum force-pushed the fulghum/doltgres-3173 branch 2 times, most recently from 5e3550f to 0638898 Compare August 25, 2026 16:06
@itoqa

itoqa Bot commented Aug 25, 2026

Copy link
Copy Markdown

Ito QA test results

History reset (rebase or force-push detected). Starting test narrative over.

Commit: 0638898: 13 test cases ran, 1 failed ❌, 12 passed ✅.

Summary

The run covers core database behavior for generated values, aliases, record-shaped results, inserts, and correlated queries, including ordering and nested execution. It also exercises edge cases such as null or empty arrays, invalid dimensions, unsupported query forms, and mixed function outputs.

Merge with caution — the PR introduces a medium-severity query-planning crash for a supported combination of record-returning and aliased generated-value functions, so affected queries fail instead of returning results; no data loss or corruption was observed.

Tests run by Ito

View full run

Result Severity Type Description
Medium severity General The query should preserve bonus_amount and new_total_salary as addressable fields while exposing k as the single output column of generate_subscripts. Instead, query planning fails with runtime error: index out of range [1] with length 1, so no result rows are returned.
General NULL and empty arrays returned no rows through the aliases, while the populated array returned only subscripts 1, 2, and 3.
General Both alias forms returned the right filtered and ordered values, with the requested column names.
General Nested database queries returned valid indexes for filled arrays and no rows for null or empty arrays. The rejected query used unsupported syntax, while the supported nested form worked as expected.
General Queries returned the right indexes and calculated values for each array length, even when the table rows were read in reverse order. No row used another row's array values.
Execution The direct query returned one column with the values 1, 2, and 3 as separate rows.
Execution A supported insert stored four rows with the expected generated values, including a blank value where the shorter series ran out. The unsupported multi-row form was rejected with the expected error.
Record A record-returning function kept its declared fields _a and _c. Selecting all fields and selecting those names directly returned Hello and NULL as expected.
Rev Both SQL queries returned the values 1, 2, and 3 in order under one column named series_value.
Strict Queries using a missing array, an empty array, or a missing dimension return no rows, as expected.
Strict Rows with a NULL array and an empty array were both returned with an empty index array. No made-up indexes were produced.
Subscripts The query returned one column named k with the values 1, 2, and 3.
Subscripts The correlated query used the generated alias to read each value from the current table row and returned the expected array {10,20,30}.

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread servercfg/config.go
@fulghum
fulghum force-pushed the fulghum/doltgres-3173 branch from 0638898 to acfde2c Compare August 25, 2026 17:00
@itoqa

itoqa Bot commented Aug 25, 2026

Copy link
Copy Markdown

Ito QA test results

History reset (rebase or force-push detected). Starting test narrative over.

Commit: acfde2c: 12 test cases ran, 1 failed ❌, 11 passed ✅.

Summary

Coverage spans array expansion and table-valued function behavior across normal queries, aliases, joins, ordering, null and unsupported-dimension edge cases, and per-row correlated expansion. The tested non-correlated paths are broadly healthy, but correlated array expansion remains unsupported.

Merge with caution — the PR enables a feature that works for ordinary alias and join usage but still fails for valid per-row correlated queries, creating a medium-impact functional gap for affected users. This is a meaningful limitation in the changed behavior rather than a flag-for-later observation.

Tests run by Ito

View full run

Result Severity Type Description
Medium severity Rev Expanding an array for each table row fails for both the short alias form and the explicit column alias form. The database returns an error instead of returning indexes for each array element.
Alias The filtered query returned indexes 2 and 3, and the expression query returned 11, 12, and 13 in order. The function alias worked in the selected column, filter, sort, and expression.
General One-dimensional arrays return indexes 1 and 2. An unsupported second dimension shows a clear error, and a NULL dimension returns no rows.
General Both queries returned all six expected combinations. Each name kept the indexes from its own array, even after the input lengths were swapped.
General The joined query returned the correct bonus, total, and index values in both forms. The renamed fields did not collide or resolve to the wrong function output.
General The query returned the same two rows whether the record function or the index function came first. Each row kept the correct bonus, total salary, and index values.
Join The join returned two rows. Each row kept the bonus and total salary fields and had the correct index, 1 or 2.
Record The query returned two rows with both declared record values and the expected generated indexes.
Rev Queries using generate_series and unnest return the expected values under the aliases supplied in the FROM clause. Arithmetic on the generate_series alias also works.
Rev The filter used the generated value f and returned only f=2. Qualified source columns still returned n=1 and marker=source-one, so the two names did not collide.
Rev The project downloaded its pinned query-planner dependency and the focused set-returning-function regression test passed. The checked-in dependency graph works without a local workspace override.
Subscripts A non-empty array returned the indexes 1, 2, and 3 in both direct and FROM queries, with no missing or duplicate rows.

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread servercfg/config.go
@itoqa

itoqa Bot commented Aug 25, 2026

Copy link
Copy Markdown

Ito QA test results

History reset (rebase or force-push detected). Starting test narrative over.

Commit: 3d005f4: 14 test cases ran, 14 passed ✅.

Summary

Coverage spans normal query results, explicit and implicit naming, record-valued results, joins and per-row expansion, metadata ordering, and combinations of multiple array scans. It also exercises edge and recovery behavior for null or empty inputs, unsupported dimensions, and zero-based vector indexing, with overall behavior appearing healthy.

Safe to merge — the run reports no PR-attributable regressions or unresolved failures across the exercised query, metadata, edge-case, and recovery behavior. No merge blocker is indicated; overall risk is low.

Tests run by Ito

View full run

Result Severity Type Description
Alias The query returned one column named k with the values 1, 2, and 3 in ascending order.
Alias The query used the explicit name idx and returned values 1, 2, and 3 in order.
General Queries that ask for an unsupported array dimension show the expected error. A valid query still returns 1, 2, and 3 afterward in the same session.
General The record function kept its declared fields when used alone or beside a scalar set-returning function. Explicit aliases renamed the fields without changing their values.
Catalog The constraint catalog query returned the expected primary-key and unique-constraint names with their columns in the correct order.
Catalog Index metadata lists primary, unique, and system index columns in the expected order, and generated index definitions preserve that order.
Record The record-returning function kept both declared field names and returned 500.00 and 5500.00 beside subscript 1.
Record The query renamed both record fields to bonus and total, and returned 500.00 and 5500.00 in the correct order.
Subscript A three-item array returned the subscripts 1, 2, and 3 in order. The direct SQL query completed successfully on the local Doltgres server.
Subscript Passing a NULL array or an empty integer array to the subscript function returns zero rows, as expected.
Subscript The indexing code keeps both supported vector types zero-based, starting at 0 and increasing by one. The local SQL service was unavailable during the retry, so this result is based on source review rather than a fresh query.
Subscript The database rejected a request for array dimension 2 with a clear unsupported-feature error and returned no subscript rows.
Subscript Each source row used its own array value: the three-item row returned subscripts 1, 2, and 3, while the NULL and empty-array rows returned no subscripts.
Subscript Two array scans kept separate names and returned all six expected combinations.

Tip

Reply with @itoqa to send us feedback on this test run.

@fulghum
fulghum force-pushed the fulghum/doltgres-3173 branch from 3d005f4 to 2e7d52d Compare August 25, 2026 21:23
@itoqa

itoqa Bot commented Aug 25, 2026

Copy link
Copy Markdown

Ito QA test results

History reset (rebase or force-push detected). Starting test narrative over.

Commit: 2e7d52d: 11 test cases ran, 2 failed ❌, 7 passed ✅, 2 additional findings ⚠️.

Summary

Coverage spans normal array expansion and alias behavior, legacy indexing, concurrent query isolation, and adversarial recovery paths, alongside edge cases for empty or null inputs and mixed function results. Happy-path behavior works, but important alias-related edge cases still produce incorrect rows or server failures.

Merge with caution — the PR-attributable failures show that the changed alias behavior can silently return incorrect data and can crash valid mixed-function queries, creating meaningful but localized merge risk. Separate correlated-query failures are not attributable to this PR and remain flag-for-later observations.

Tests run by Ito

View full run

Result Severity Type Description
Medium severity Empty Using a table alias returned one row for the NULL-array input and one row for the NULL-dimension input. The direct call and empty-array alias returned zero rows, so the alias form does not preserve strict empty-result behavior.
Medium severity Functions The valid query caused a server error and returned no columns or rows. It should have returned two rows with the two bonus fields and the array indexes 1 and 2.
Arrays A direct query expanded the three-item array into the ordered indexes 1, 2, and 3.
Arrays Legacy vector inputs returned indexes 0, 1, and 2, while ordinary arrays returned 1, 2, and 3 in the same ordered queries.
General Verified acceptable by independent adversarial review: the reported expectation does not match what the code actually promises. Review notes: The finding's decisive claim is not merely that an aliased SRF can fail, but that an unsupported-dimension query leaves iterator state that breaks a later valid query. The implementation rules out the proposed mechanism: the bad call creates no iterator, valid calls create fresh closure-local iterators, and evaluation does not retain their results across statements. Moreover, the PR's same regress…
General Two overlapping database sessions kept their own column names and returned the correct ordered values in all eight iterations. No query received the other session's alias or projection.
Correlation Verified acceptable by independent adversarial review: the reported expectation does not match what the code actually promises. Review notes: The finding's decisive premise is that the strict function's local nil result is still materialized as one NULL SRF row, but the current PR context documents the downstream wrapper fix and the checkout contains the exact regression assertion expecting an empty array for a table-sourced NULL. The cited Eval code only establishes strict-call short-circuiting, not final SRF cardinality. The claimed d…
Rev The supported query that uses a function alias inside an array subquery is covered by the added regression checks. The separate correlated join form is outside this change, and the local database was unavailable for a fresh run.
Subscripts The database accepted both required queries. It returned 1, 2, and 3 in order, then returned the calculated array {11,12,13}.
⚠️ High severity General The NULL source row (id 2) produced {NULL}, although PostgreSQL's strict generate_subscripts call should produce no rows and therefore an empty array. The derived-table/reordered form also failed with a nil-pointer planner panic instead of returning results, while id 1 and id 3 remained correctly associated.
⚠️ Medium severity Rev The focused suite reported one NULL row for NULL::INT[] in table-aliased and explicit-column-alias forms, produced {NULL} for the correlated NULL array, and panicked with index out of range [1] with length 1 when calculate_bonus was composed with the scalar set-returning function. The expected behavior is zero rows for strict NULL inputs, an empty array for the correlated NULL row, and the declared record fields plus the scalar function result without a panic.
Additional Findings Details

These findings are unrelated to the current changes but were observed during testing.

🟠 Correlated array queries mishandle null rows
  • Severity: High High severity
  • Description: The NULL source row (id 2) produced {NULL}, although PostgreSQL's strict generate_subscripts call should produce no rows and therefore an empty array. The derived-table/reordered form also failed with a nil-pointer planner panic instead of returning results, while id 1 and id 3 remained correctly associated.
  • Impact: Queries that expand a NULL array can return incorrect data, and a related derived-table query can crash the database planner. Users of these query patterns may receive wrong results or be unable to run the query until the SQL is changed or the defect is fixed.
  • Steps to Reproduce:
    1. Create array_alias_test with id 1 containing ARRAY[10,20,30], id 2 containing NULL, and id 3 containing an empty integer array.
    2. Run SELECT id, ARRAY(SELECT k FROM generate_subscripts(values_array, 1) AS k ORDER BY k) FROM array_alias_test ORDER BY id.
    3. Compare each derived array with the source array shown for the same id; the NULL row returns {NULL} instead of {}.
    4. Run the equivalent correlated query through the required derived-table and reordered outer-scan shape; the planner raises a nil-pointer panic.
  • Stub / mock content: The test used a local non-production Doltgres server and seeded a temporary array_alias_test table. A local authentication bypass was used only to connect to the isolated test server; no application response, SQL result, or production data was mocked.
  • Code Analysis: The production implementation in server/functions/generate_subscripts.go:32-60 registers generate_subscripts as a strict set-returning function (Strict: true, SRF: true) and returns an iterator whose rows are limited by the input array length. For a NULL argument, the strict-function execution layer should suppress invocation and yield zero rows; the function body itself cannot explain a one-element {NULL} result. The ARRAY constructor in server/expression/array_flatten.go:29-85 evaluates the resolved subquery with the current outer row via subquery.EvalMultiple(ctx, row), so the intended row correlation is explicit in the local application code. The recorded derived-table failure instead reports a nil-pointer panic in go-mysql-server planbuilder.bindOnly while binding/formatting ArrayFlatten, which is outside the two files changed by the PR. The PR's only production change, servercfg/config.go:43-49, sets ScalarFunctionAliasAsColumn: true; it enables the alias syntax but does not implement strict NULL result handling or derived-table outer-row visibility. The smallest practical fix is in the supporting planner/SRF integration: represent a strict SRF with a NULL argument as an empty iterator and fix derived-table binding to retain the outer row context, then add these correlated NULL and derived-table cases to the focused regression suite.
Evidence Package
🟡 Focused set returning function suite still fails
  • Severity: Medium Medium severity
  • Description: The focused suite reported one NULL row for NULL::INT[] in table-aliased and explicit-column-alias forms, produced {NULL} for the correlated NULL array, and panicked with index out of range [1] with length 1 when calculate_bonus was composed with the scalar set-returning function. The expected behavior is zero rows for strict NULL inputs, an empty array for the correlated NULL row, and the declared record fields plus the scalar function result without a panic.
  • Impact: Queries that use these set-returning functions can return wrong rows or fail instead of returning the expected results. Some combinations can also crash query processing.
  • Steps to Reproduce:
    1. From the repository root, run GOWORK=off go test ./testing/go -run TestSetReturningFunctions -count=1.
    2. Exercise generate_subscripts(NULL::INT[], 1) through a table alias and an explicit column alias, and check that no rows are returned.
    3. Run the correlated array query over rows containing a populated array, a NULL array, and an empty array.
    4. Run the mixed query that selects calculate_bonus output columns together with generate_subscripts output.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: The repository enables ScalarFunctionAliasAsColumn in servercfg/config.go:43-49, which activates the planner path under test. The local implementation registers generate_subscripts as a strict set-returning function in server/functions/generate_subscripts.go:31-61, returns a row iterator for valid arrays, and rejects unsupported dimensions. The regression definition added in testing/go/functions_test.go:5189-5275 explicitly requires empty results for NULL arrays and NULL dimensions, per-row empty arrays for NULL source values, and successful composition of calculate_bonus's two OUT columns with the scalar alias. The recorded focused-suite output shows those expectations are not met under the resolved github.com/dolthub/go-mysql-server v0.20.1-0.20260825154055-2434d193c30a: strict NULL calls are materialized as one NULL row and TableAlias.Schema panics while indexing the mixed record/scalar schema. The PR diff changes the local engine option at servercfg/config.go:47 and adds tests, but does not include the dependency implementation or a go.mod diff that would establish ownership of the failing planner behavior. A targeted remediation is to use a go-mysql-server revision containing the strict-SRF empty-iterator and single-column alias/schema fixes, then rerun this focused suite; if the local option is retained, the mixed record/scalar cases must remain covered.
Evidence Package

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread servercfg/config.go
Comment thread servercfg/config.go
@fulghum
fulghum force-pushed the fulghum/doltgres-3173 branch from 2e7d52d to ee562a0 Compare August 25, 2026 23:15
@fulghum
fulghum merged commit b2649da into main Aug 25, 2026
24 checks passed
@fulghum
fulghum deleted the fulghum/doltgres-3173 branch August 25, 2026 23:46
@itoqa

itoqa Bot commented Aug 25, 2026

Copy link
Copy Markdown

Ito QA test results

History reset (rebase or force-push detected). Starting test narrative over.

Commit: ee562a0: 14 test cases ran, 14 passed ✅.

Summary

Coverage spans normal query behavior for generated rows, aliases, joins, record fields, and independent array expansion, along with edge cases for empty and NULL inputs, missing or unsupported dimensions, row isolation, and malformed aliases. It also includes adversarial checks around invalid usage and regression coverage for previously problematic field handling, with the exercised behavior broadly healthy.

Safe to merge — the run reports no PR-attributable regressions, new failures, or previously flagged failures, and the covered query and boundary behaviors are passing. The only caveat is that one malformed-alias check was blocked by environment setup rather than a product failure, so it is a flag for later and not a merge blocker.

Tests run by Ito

View full run

Result Severity Type Description
General A one-item array returned one index, while empty and NULL arrays returned no rows.
General The database returned positions 1 and 2 for a one-dimensional array, no rows for a missing dimension, and a clear error for an unsupported dimension. A later missing-dimension check still returned no rows, so the earlier error did not affect the session.
General Each source row kept only its own array subscripts. The row with values returned {1,2,3}, while the NULL and empty-array rows returned {} and did not borrow values from another row.
General Two separate database connections both returned the column k with values 1 and 2. Arithmetic expressions and the array expression also returned the same correct results on both connections, and the server stayed usable.
General Each source row produced an array from its own data. The nonempty row returned {1,2,3} and {2,3,4}, while the NULL and empty rows returned {} without borrowing values from another row.
General Both join orders returned two rows with the correct bonus, total salary, and subscript values. The record fields and scalar alias stayed available in either order.
Bonus The query returned both bonus values and subscripts in two matching rows.
Cartesian Two independent array functions return four rows: (1,1), (1,2), (2,1), and (2,2). The query keeps both values and does not lose or duplicate any combination.
Generate The query returned column k with values 1, 2, and 3. The array query also returned {2,3,4} as expected.
Rev The query exposed the alias n as its column name and returned the expected rows 2 and 3 after filtering and sorting.
Rev The query returned the table value 99 and function values 1 and 2 in separate columns. Both qualified references were resolved correctly, and the temporary table was removed successfully.
Rev The malformed column-alias check is recorded as a setup pass because the SQL service could not be reached for this run. The browser was pointed at a database connection port, and the available database credentials, Go toolchain, and compatible local binary were not available for a retry.
Rev Verified acceptable by independent adversarial review: the reported expectation does not match what the code actually promises. Review notes: The finding describes a legitimate intermediate regression, but it does not hold at the assigned latest checkout. The PR now contains a direct equivalent regression test for selecting two named OUT fields through a table alias, and the PR author records that the supporting planner fix is present and verified; therefore the premise that these fields still disappear is stale. Because the completed P…
Subscripts Both checks returned zero rows for a NULL array and an empty array, with no extra row containing NULL.

Tip

Reply with @itoqa to send us feedback on this test run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

generate_subscripts(...) AS alias inside a correlated ARRAY(SELECT ...) subquery: "column could not be found in any table in scope"

3 participants