Skip to content

Researching - #3568

Open
Hydrocharged wants to merge 68 commits into
mainfrom
daylon/rust-port
Open

Hydrocharged wants to merge 68 commits into
mainfrom
daylon/rust-port

Conversation

@Hydrocharged

Copy link
Copy Markdown
Collaborator

No description provided.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor
Main PR
Total 42090 42090
Successful 20956 31648
Failures 21134 10442
Partial Successes1 5405 1582
Main PR
Successful 49.7885% 75.1913%
Failures 50.2115% 24.8087%

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

aggregates

QUERY:          select ten, sum(distinct four) from onek a
group by ten
having exists (select 1 from onek b where sum(distinct a.four) = b.four);
RECEIVED ERROR: aggregate functions are not allowed in WHERE
QUERY:          select f1 from t1 left join t2 using (f1) group by t1.f1;
RECEIVED ERROR: column "f1" must appear in the GROUP BY clause or be used in an aggregate function

alter_table

QUERY:          CREATE TABLE constraint_rename_test2 (a int CONSTRAINT con1 CHECK (a > 0), d int) INHERITS (constraint_rename_test);
RECEIVED ERROR: check constraint "con1" already exists
QUERY:          SELECT c.oid,
  n.nspname,
  c.relname
FROM pg_catalog.pg_class c
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname OPERATOR(pg_catalog.~) '^(constraint_rename_test2)$' COLLATE pg_catalog.default
  AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3;
RECEIVED ERROR: expected row count 1 but received 0
QUERY:          SELECT c.oid,
  n.nspname,
  c.relname
FROM pg_catalog.pg_class c
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname OPERATOR(pg_catalog.~) '^(constraint_rename_test2)$' COLLATE pg_catalog.default
  AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3;
RECEIVED ERROR: expected row count 1 but received 0
QUERY:          SELECT c.oid,
  n.nspname,
  c.relname
FROM pg_catalog.pg_class c
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname OPERATOR(pg_catalog.~) '^(constraint_rename_test2)$' COLLATE pg_catalog.default
  AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3;
RECEIVED ERROR: expected row count 1 but received 0
QUERY:          SELECT c.oid,
  n.nspname,
  c.relname
FROM pg_catalog.pg_class c
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname OPERATOR(pg_catalog.~) '^(constraint_rename_test2)$' COLLATE pg_catalog.default
  AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3;
RECEIVED ERROR: expected row count 1 but received 0
QUERY:          DROP TABLE constraint_rename_test2;
RECEIVED ERROR: table "constraint_rename_test2" does not exist
QUERY:          alter table at_partitioned attach partition at_part_2 for values from (1000) to (2000);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          alter table alterlock2 validate constraint alterlock2nv;
RECEIVED ERROR: constraint "alterlock2nv" of relation "alterlock2" does not exist
QUERY:          SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, false AS relhasoids, c.relispartition, '', c.reltablespace, CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, c.relpersistence, c.relreplident, am.amname
FROM pg_catalog.pg_class c
 LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)
LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid)
WHERE c.oid = '160816';
RECEIVED ERROR: row sets differ:
    Postgres:
        {0, 114, false, false, false, false, false, false, false, "", 0, "", 112, 100, "heap"}
    Doltgres:
        {1, 114, false, false, false, false, false, false, false, "", 0, "", 112, 100, "heap"}
QUERY:          CREATE TABLE fail_part (like unparted);
RECEIVED ERROR: relation "fail_part" already exists
QUERY:          CREATE TABLE perm_part (a int);
RECEIVED ERROR: relation "perm_part" already exists
QUERY:          ALTER TABLE list_parted ATTACH PARTITION part_1 FOR VALUES IN (1);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          ALTER TABLE list_parted ATTACH PARTITION def_part DEFAULT;
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          ALTER TABLE list_parted2 DETACH PARTITION part_3_4;
RECEIVED ERROR: ALTER TABLE AtDetachPartition is not yet supported
QUERY:          ALTER TABLE list_parted2 ATTACH PARTITION part_3_4 FOR VALUES IN (3, 4);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          CREATE TABLE part1 (
	a int NOT NULL CHECK (a = 1),
	b int NOT NULL CHECK (b >= 1 AND b <= 10)
);
RECEIVED ERROR: relation "part1" already exists
QUERY:          CREATE TABLE part2 (
	a int NOT NULL CHECK (a = 1),
	b int NOT NULL CHECK (b >= 10 AND b < 18)
);
RECEIVED ERROR: relation "part2" already exists
QUERY:          ALTER TABLE range_parted ATTACH PARTITION part2 FOR VALUES FROM (1, 10) TO (1, 20);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          CREATE TABLE part3 (LIKE range_parted);
RECEIVED ERROR: relation "part3" already exists
QUERY:          ALTER TABLE range_parted ATTACH partition part3 FOR VALUES FROM (3, 10) TO (3, 20);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          ALTER TABLE list_parted2 DETACH PARTITION part_5;
RECEIVED ERROR: ALTER TABLE AtDetachPartition is not yet supported
QUERY:          ALTER TABLE list_parted2 ATTACH PARTITION part_6 FOR VALUES IN (6);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          ALTER TABLE part_7 ATTACH PARTITION part_7_a_null FOR VALUES IN ('a', null);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          ALTER TABLE list_parted2 DETACH PARTITION part_7;
RECEIVED ERROR: ALTER TABLE AtDetachPartition is not yet supported
QUERY:          ALTER TABLE quuux ATTACH PARTITION quuux1 FOR VALUES IN (1);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          ALTER TABLE quuux ATTACH PARTITION quuux2 FOR VALUES IN (2);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          CREATE TABLE hpart_2 (LIKE hash_parted);
RECEIVED ERROR: relation "hpart_2" already exists
QUERY:          INSERT INTO hpart_2 VALUES (3, 0);
RECEIVED ERROR: INSERT has more expressions than target columns
QUERY:          ALTER TABLE list_parted2 DETACH PARTITION part_3_4;
RECEIVED ERROR: ALTER TABLE AtDetachPartition is not yet supported
QUERY:          CREATE TABLE range_parted2 (
    a int
) PARTITION BY RANGE(a);
RECEIVED ERROR: relation "range_parted2" already exists
QUERY:          ALTER TABLE range_parted2 DETACH PARTITION part_rp;
RECEIVED ERROR: ALTER TABLE AtDetachPartition is not yet supported
QUERY:          ALTER TABLE range_parted2 DETACH PARTITION part_rp100 CONCURRENTLY;
RECEIVED ERROR: ALTER TABLE AtDetachPartition is not yet supported
QUERY:          alter table p1 attach partition p11 for values from (2) to (5);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          alter table temp_part_parent attach partition temp_part_child default;
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          alter table at_test_sql_partop attach partition at_test_sql_partop_1 for values from (0) to (10);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          alter table bar1 attach partition bar2 default;
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported
QUERY:          alter table target_parted attach partition attach_parted for values in (1);
RECEIVED ERROR: ALTER TABLE AtAttachPartition is not yet supported

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

advisory_lock

QUERY: SELECT
	pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2),
	pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2);
QUERY: SELECT pg_advisory_unlock_all();
QUERY: SELECT
	pg_advisory_unlock(1), pg_advisory_unlock_shared(2),
	pg_advisory_unlock(1, 1), pg_advisory_unlock_shared(2, 2);
QUERY: SELECT
	pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2),
	pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2);
QUERY: SELECT
	pg_advisory_lock(1), pg_advisory_lock_shared(2),
	pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2);
QUERY: SELECT
	pg_advisory_unlock(1), pg_advisory_unlock(1),
	pg_advisory_unlock_shared(2), pg_advisory_unlock_shared(2),
	pg_advisory_unlock(1, 1), pg_advisory_unlock(1, 1),
	pg_advisory_unlock_shared(2, 2), pg_advisory_unlock_shared(2, 2);
QUERY: SELECT
	pg_advisory_lock(1), pg_advisory_lock_shared(2),
	pg_advisory_lock(1, 1), pg_advisory_lock_shared(2, 2);
QUERY: SELECT
	pg_advisory_xact_lock(1), pg_advisory_xact_lock_shared(2),
	pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock_shared(2, 2);
QUERY: SELECT pg_advisory_unlock_all();
QUERY: SELECT
	pg_advisory_xact_lock(1), pg_advisory_xact_lock(1),
	pg_advisory_xact_lock_shared(2), pg_advisory_xact_lock_shared(2),
	pg_advisory_xact_lock(1, 1), pg_advisory_xact_lock(1, 1),
	pg_advisory_xact_lock_shared(2, 2), pg_advisory_xact_lock_shared(2, 2);
QUERY: SELECT
	pg_advisory_lock(1), pg_advisory_lock(1),
	pg_advisory_lock_shared(2), pg_advisory_lock_shared(2),
	pg_advisory_lock(1, 1), pg_advisory_lock(1, 1),
	pg_advisory_lock_shared(2, 2), pg_advisory_lock_shared(2, 2);
QUERY: SELECT
	pg_advisory_unlock(1), pg_advisory_unlock(1),
	pg_advisory_unlock_shared(2), pg_advisory_unlock_shared(2),
	pg_advisory_unlock(1, 1), pg_advisory_unlock(1, 1),
	pg_advisory_unlock_shared(2, 2), pg_advisory_unlock_shared(2, 2);
QUERY: SELECT
	pg_advisory_lock(1), pg_advisory_lock(1),
	pg_advisory_lock_shared(2), pg_advisory_lock_shared(2),
	pg_advisory_lock(1, 1), pg_advisory_lock(1, 1),
	pg_advisory_lock_shared(2, 2), pg_advisory_lock_shared(2, 2);
QUERY: SELECT pg_advisory_unlock_all();

aggregates

QUERY: SELECT avg(four) AS avg_1 FROM onek;
QUERY: SELECT avg(gpa) AS avg_3_4 FROM ONLY student;
QUERY: SELECT sum(b) AS avg_431_773 FROM aggtest;
QUERY: SELECT sum(gpa) AS avg_6_8 FROM ONLY student;
QUERY: SELECT max(student.gpa) AS max_3_7 FROM student;
QUERY: SELECT stddev_pop(b::numeric) FROM aggtest;
QUERY: SELECT stddev_samp(b::numeric) FROM aggtest;
QUERY: SELECT var_pop(b::numeric) FROM aggtest;
QUERY: SELECT var_samp(b::numeric) FROM aggtest;
QUERY: SELECT var_pop('inf'::numeric), var_samp('inf'::numeric);
QUERY: SELECT stddev_pop('inf'::numeric), stddev_samp('inf'::numeric);
QUERY: SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric)
FROM (VALUES ('1'), ('infinity')) v(x);
QUERY: SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric)
FROM (VALUES ('infinity'), ('1')) v(x);
QUERY: SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric)
FROM (VALUES ('infinity'), ('infinity')) v(x);
QUERY: SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric)
FROM (VALUES ('-infinity'), ('infinity')) v(x);
QUERY: SELECT sum(x::numeric), avg(x::numeric), var_pop(x::numeric)
FROM (VALUES ('-infinity'), ('-infinity')) v(x);
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: COPY bitwise_test FROM STDIN NULL 'null';
QUERY: COPY bool_test FROM STDIN NULL 'null';
QUERY: SELECT
  BOOL_AND(b1)     AS "f",
  BOOL_AND(b2)     AS "t",
  BOOL_AND(b3)     AS "f",
  BOOL_AND(b4)     AS "n",
  BOOL_AND(NOT b2) AS "f",
  BOOL_AND(NOT b3) AS "t"
FROM bool_test;
QUERY: SELECT
  EVERY(b1)     AS "f",
  EVERY(b2)     AS "t",
  EVERY(b3)     AS "f",
  EVERY(b4)     AS "n",
  EVERY(NOT b2) AS "f",
  EVERY(NOT b3) AS "t"
FROM bool_test;
QUERY: SELECT
  BOOL_OR(b1)      AS "t",
  BOOL_OR(b2)      AS "t",
  BOOL_OR(b3)      AS "f",
  BOOL_OR(b4)      AS "n",
  BOOL_OR(NOT b2)  AS "f",
  BOOL_OR(NOT b3)  AS "t"
FROM bool_test;
QUERY: select max(unique2), generate_series(1,3) as g from tenk1 order by g desc;
QUERY: create temp table t3 (a int, b int, c int, primary key(a, b) deferrable);
QUERY: explain (costs off) select a,c from t1 group by a,c,d;
QUERY: explain (costs off) select * from t3 group by a,b,c;

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. ↩

@dolthub dolthub deleted a comment from coffeegoddd Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Mini sysbench, main 34f8e0f (Go) against 288c0a0 (Rust), with Postgres 15 for comparison:

Reads

Test main latency (ms) PR latency (ms) Change main tps PR tps Postgres tps PR / Postgres
oltp_read_only 5.06 1.87 -63.0% 197.40 533.97 615.87 86.7%
oltp_point_select 0.30 0.12 -60.0% 3291.97 8402.97 10513.59 79.9%
select_random_points 0.51 0.17 -66.7% 1944.37 5705.14 7604.44 75.0%
select_random_ranges 0.67 0.21 -68.7% 1480.70 4651.69 4554.74 102.1%
covering_index_scan_postgres 0.50 0.13 -74.0% 1980.34 7776.79 1031.02 754.3%
index_scan_postgres 30.65 13.08 -57.3% 32.62 76.42 107.14 71.3%
table_scan_postgres 31.15 13.35 -57.1% 32.09 74.87 105.30 71.1%
groupby_scan_postgres 7.41 3.45 -53.4% 134.94 289.86 350.31 82.7%
index_join_scan_postgres 1.21 0.67 -44.6% 828.44 1480.77 2529.31 58.5%
types_table_scan_postgres 70.35 26.12 -62.9% 14.21 38.28 45.64 83.9%
index_join_postgres 1.57 0.77 -51.0% 636.12 1297.16 1129.78 114.8%

Writes

Test main latency (ms) PR latency (ms) Change main tps PR tps Postgres tps PR / Postgres
oltp_read_write 8.13 3.45 -57.6% 122.91 289.64 434.62 66.6%
oltp_update_index 1.13 0.53 -53.1% 881.44 1902.93 4308.99 44.2%
oltp_update_non_index 1.04 0.43 -58.7% 964.95 2324.86 4484.82 51.8%
oltp_insert 1.17 0.51 -56.4% 856.01 1968.00 4947.42 39.8%
oltp_write_only 3.28 1.64 -50.0% 304.30 608.66 1554.48 39.2%
oltp_delete_insert_postgres 2.09 1.02 -51.2% 477.13 981.40 2424.17 40.5%
types_delete_insert_postgres 2.07 0.93 -55.1% 483.42 1074.25 2177.38 49.3%

Comment thread crates/sql/src/engine.rs
Comment thread crates/server/src/conn.rs
Comment thread crates/bench/src/main.rs
…regates, and user-defined forms, polymorphic user functions, and positional PL/pgSQL parameters
…ngs of the storage fixtures, and left column numbers out of the node client comparison
Comment thread crates/sql/src/plpgsql/compile.rs
Comment thread crates/sql/src/plpgsql/compile.rs
…ptions, reported view updatability in information_schema, and deferred view privilege checks to use
…rms, the two-array json_object forms, and jsonb_delete, and expanded composite results of functions in FROM
Comment thread crates/sql/src/updatable.rs
Comment thread crates/sql/src/engine.rs
Comment thread crates/sql/src/engine.rs
Comment thread crates/sql/src/views.rs
Comment thread crates/sql/src/updatable.rs
Comment thread crates/sql/src/engine.rs
Comment thread crates/sql/src/engine.rs
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@dolthub dolthub deleted a comment from itoqa Bot Oct 10, 2026
@itoqa

itoqa Bot commented Oct 10, 2026

Copy link
Copy Markdown

Ito QA test results
Ito Diff Report — 2d6b198 → 0f9bb0b: 13 test cases ran, 1 new failure ❌, 11 passing ✅, 1 additional finding ⚠️.

Diff Summary

The run covers core query behavior across joins, filtering, grouping, ordering, subqueries, indexing, window results, NULL and missing values, and set expansion, including both normal workflows and boundary cases. Most exercised behavior remains healthy, but a newly supported correlated query form still fails during planning rather than returning results.

Merge with caution — the PR introduces a medium-severity failure in correlated quantified subqueries, making a supported query pattern unusable, while the separate NULL-boundary panic is unrelated to this PR and is a flag for later. The attributable issue is limited in scope but should be weighed before merging.

Tests run by Ito

View full run

Result State Severity Type Description
❌ ❌ New Failure Medium severity Subquery Running a query with a correlated ANY subquery shows a planner error and returns no results. The other correlated checks and the independent IN/count checks return the expected values.
✅ Passing — General The indexed join returned the right inner row for each matching outer row and kept empty placeholders for NULL and absent keys. The indexed result matched the broad reference with zero differences.
✅ Passing — General Grouped queries returned the same visible labels, counts, and computed values whether the ordering key was hidden or shown. Correlated values changed for each group, independent values stayed constant, and empty groups kept their correct zero or NULL results.
✅ Passing — General The correlated value changed for each outer row, while the independent count stayed at 3 for every row. Rows with NULL and no matching value also kept the expected results.
✅ Passing — Cost The planner chose indexed access for a selective query and repeated indexed lookups for the join. Both queries returned the expected rows without duplicates.
✅ Passing — Cte Verified acceptable by independent adversarial review: the scenario cannot be reached through any real application path. Review notes: The obligation is real, but the finding's proposed mechanism is contradicted by the complete source path: binding creates one Arc, references and optimizer transformations retain clones of that Arc, and execution caches rows by the shared Arc pointer for the statement. No application path shown can produce the finding's claimed independently rebuilt per-alias definitions, and the PR diff a…
✅ Passing — Index The query returned 14 unique qualifying rows. The plan used the table's index for the first condition and still checked the second condition before returning rows.
✅ Passing — Join Each outer row used its own values for the lookup. Matching rows returned their matching inner data, while NULL and missing keys kept empty placeholders.
✅ Passing — Partial The query with the full index condition used the partial index and returned the correct row. The query missing one required condition avoided that index and still returned the correct row.
✅ Passing — Scope Correlated queries returned values for the correct outer row. Joined and windowed results kept repeated matches, empty matches, NULL values, visible columns, and ordering.
✅ Passing — Select The grouped query returned the expected beta and delta rows with only the requested category and total columns. Grouping, filtering, sorting, DISTINCT, LIMIT, OFFSET, and LIMIT 0 all behaved correctly.
✅ Passing — Set The query produced one row for each returned value, kept the neighboring columns, and filled missing values with NULL.
⏸️ Skipped — General The indexed query returned ids 4, 5, 6, and 7, exactly matching the reference query. Rows that only passed the first column bound were filtered out by the complete two-column condition.
⏸️ Skipped — General The join returned only the matching inner rows for key 10. The NULL key and the missing key each produced their own empty match result, with no rows copied from another outer key.
⏸️ Skipped — General Queries with a changing predicate returned the same rows with constraint exclusion on and off. The planner kept a table path instead of incorrectly treating the predicate as a proof that the table was empty.
⏸️ Skipped — General The indexed and sequential queries returned the same four rows in the same order. Each row kept its correct values, join tag, and single occurrence.
⏸️ Skipped — General The combined SQL query kept every window value attached to the correct source row. All five rows matched independent reference calculations, and no mismatches were found.
⏸️ Skipped — General The indexed comparison returned the same rows and NULL results as the equivalent expanded condition. The database also used the expected two-column index.
⏸️ Skipped — General The query kept each window value with the correct source row and returned the rows in the declared final order.
⏸️ Skipped — General Repeated outer rows used their own key on every iteration, while unmatched and NULL keys stayed unmatched. The indexed join returned the same rows as the reference join, including the remaining value filter.
⏸️ Skipped — General Two concurrent database sessions returned the same matches as their sequential checks. Matching keys, NULL values, and missing keys stayed isolated with no stale results reused.
⏸️ Skipped — Comparisons The indexed query returned the same 9,900 rows as the equivalent first-difference comparison. No rows differed, and the query used the two-column index.
⏸️ Skipped — Constraints The local database accepted connections, but the test harness could not complete the planned constraint query after its authentication and retry setup failed. Repository review found the empty-relation planning path and the related mutable-predicate boundary check, so this run provides no evidence of an application failure.
⏸️ Skipped — Indexes The indexed query returned all four matching rows exactly once, with the same results as the reference query.
⏸️ Skipped — Nestedloops Repeated outer keys returned the same matching inner rows, while NULL and missing keys produced separate empty-match results.
⏸️ Skipped — Parameters Matching outer rows returned only their own inner matches. Rows with no match or a NULL key did not reuse another row's result.
⏸️ Skipped — Partial The partial-index query returned the same two rows as the index-disabled reference, and the query used the index only after all predicate conditions were satisfied.
⏸️ Skipped — Volatility The query engine kept changing predicates in execution while still using an indexed range for the fixed expression. Repeated sequence calls returned different rows as expected.
⚠️ Additional Finding Medium severity General Using a NULL LIMIT caused an internal server panic instead of a safe, defined SQL response.
Tests that are no longer relevant

Below are tests that previously ran and are no longer relevant:

Type Test Description
Windows Window queries return rows in the wrong order Dropped because No current candidate or changed claim requires carrying this prior window test forward; AC-11 primary is explicitly deferred for budget.
Additional Findings Details

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

🟡 NULL LIMIT crashes the SQL session
  • Severity: Medium Medium severity
  • Description: Using a NULL LIMIT caused an internal server panic instead of a safe, defined SQL response.
  • Impact: Queries that use a NULL LIMIT fail with an internal server error instead of a defined SQL response. Other queries can continue to work, and no data loss or corruption was observed.
  • Steps to Reproduce:
    1. Create a table with several rows.
    2. Run a SELECT with ORDER BY and a NULL LIMIT expression.
    3. Observe that the request returns an internal panic instead of a defined SQL error.
  • Stub / mock content: A local SQL fixture table was created for the boundary queries. No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: The recorded server error identifies go-mysql-server/sql/analyzer.validateOffsetAndLimit and reports an interface conversion from nil to int64. The repository's server/ast/limit.go converts LIMIT and OFFSET expressions into a vitess.Limit, but its validation only checks negative values when the expression is an injected literal whose value can be converted by int64ValueForLimit (lines 29-80). The NULL expression is therefore allowed to reach downstream validation. server/analyzer/type_sanitizer.go deliberately leaves LIMIT and OFFSET literals in their GMS form at lines 117-126 and includes a TODO stating that limit and offset validation still needs to handle Doltgres types. Together, these paths plausibly explain why a NULL value reaches downstream code that assumes an int64 and panics. The smallest practical fix is to detect a NULL LIMIT or OFFSET during limit analysis and return the PostgreSQL-defined error or no-limit semantics before the downstream validator performs an unchecked type assertion.
Evidence Package

Tip

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

e
}

/// process_sublinks turns each subquery expression of an expression over a row of `width` columns into a SubPlan,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

View All Evidence

🆕 New Failure: identified in this diff run

Medium severity Correlated ANY subqueries fail during planning

What failed: Running a query with a correlated ANY subquery shows a planner error and returns no results. The other correlated checks and the independent IN/count checks return the expected values.

Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
  • Severity: Medium Medium severity
  • Impact: Applications using a correlated ANY subquery receive a database error instead of query results. Other tested subquery forms continue to work, and no data loss or corruption was observed.
  • Steps to Reproduce:
    1. Create the sub_outer and sub_inner SQL fixtures with outer keys and matching inner keys.
    2. Run a SELECT that includes o.k = ANY (SELECT i.k FROM sub_inner i WHERE i.k >= o.k) together with the correlated EXISTS and scalar subqueries.
    3. Observe that planning stops with expression.subqueryAnyExpr: expected right child to return 3 values but returned 2 instead of returning one result per outer row.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: The PR changed crates/sql/src/optimizer/subselect.rs to process every AnySubquery through make_subplan at lines 328-335. make_subplan replaces correlated outer references with SubPlan arguments at lines 72-83, and build_subplan stores the linked AnySubquery and its arguments at lines 131-158. The PR also added SubPlan in crates/sql/src/expr.rs at lines 132-160; its eval recomputes arguments for the current enclosing row and then evaluates the linked subquery. This is the production path exercised by the failing correlated quantified query. The observed planner error says the Any expression receives a right child with two values when it expects three, which is a result-shape/field-mapping failure in this newly added subquery planning path, not a database setup failure. A targeted fix should preserve the single-column right-hand result for this scalar ANY query and only construct field comparisons when the subquery actually returns a matching row shape.
  • Why this is likely a bug: The SQL form is a normal correlated quantified subquery: its right side returns one column, and the outer value should be compared with that column for each outer row. Instead, the planner raises an internal result-shape error before execution. The repository's newly added subquery code explicitly claims to support AnySubquery and per-row SubPlan arguments, and the same run proves that the database and fixtures can execute the neighboring subquery forms. The smallest practical remediation is to correct the correlated ANY target/result mapping in the new SubPlan path, then add a regression query covering a one-column correlated ANY subquery.
Relevant code

crates/sql/src/optimizer/subselect.rs:328-335

pub fn process_sublinks(ctx: &mut Ctx<'_>, e: Expr, width: usize) -> Expr {
    match e {
        link @ (Expr::Exists(_) | Expr::Scalar(_) | Expr::ArraySubquery(..) | Expr::AnySubquery(..)) => {
            let link = link.map_children(&mut |c| process_sublinks(ctx, c, width));
            make_subplan(ctx, link, false, width)
        }

crates/sql/src/optimizer/subselect.rs:131-158

fn build_subplan(ctx: &Ctx<'_>, mut link: Expr, plan: Plan, mut path: Option<Rc<Path>>, args: Vec<Expr>) -> SubPlan {
    let init_plan = args.is_empty() && !matches!(link, Expr::AnySubquery(..));
    let use_hash_table = match (&link, &path) {
        (Expr::AnySubquery(test, _, false), Some(path)) => {
            args.is_empty() && subpath_is_hashable(path) && testexpr_is_hashable(test)
        }
        _ => false,
    };
    ...
    *link.subquery_mut().expect("a subquery expression") = crate::plan::share_subquery(plan, uncorrelated);
    let mut subplan = SubPlan { link, args, init_plan, planned: true, startup_cost: 0.0, per_call_cost: 0.0 };

crates/sql/src/expr.rs:147-152

fn eval(&self, ctx: &mut Ctx<'_>, row: &[Value]) -> Result<Value> {
    let args = self.args.iter().map(|a| a.eval(ctx, row)).collect::<Result<Vec<Value>>>()?;
    self.link.eval_sublink(ctx, row, &args)
}
Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.

**Medium severity — Correlated ANY subqueries fail during planning**

**What failed:** Running a query with a correlated ANY subquery shows a planner error and returns no results. The other correlated checks and the independent IN/count checks return the expected values.

- **Impact:** Applications using a correlated ANY subquery receive a database error instead of query results. Other tested subquery forms continue to work, and no data loss or corruption was observed.
- **Steps to reproduce:**
  1. Create the sub_outer and sub_inner SQL fixtures with outer keys and matching inner keys.
  2. Run a SELECT that includes `o.k = ANY (SELECT i.k FROM sub_inner i WHERE i.k >= o.k)` together with the correlated EXISTS and scalar subqueries.
  3. Observe that planning stops with `expression.subqueryAnyExpr: expected right child to return 3 values but returned 2` instead of returning one result per outer row.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** The PR changed crates/sql/src/optimizer/subselect.rs to process every `AnySubquery` through `make_subplan` at lines 328-335. `make_subplan` replaces correlated outer references with SubPlan arguments at lines 72-83, and `build_subplan` stores the linked AnySubquery and its arguments at lines 131-158. The PR also added `SubPlan` in crates/sql/src/expr.rs at lines 132-160; its `eval` recomputes arguments for the current enclosing row and then evaluates the linked subquery. This is the production path exercised by the failing correlated quantified query. The observed planner error says the Any expression receives a right child with two values when it expects three, which is a result-shape/field-mapping failure in this newly added subquery planning path, not a database setup failure. A targeted fix should preserve the single-column right-hand result for this scalar ANY query and only construct field comparisons when the subquery actually returns a matching row shape.
- **Why this is likely a bug:** The SQL form is a normal correlated quantified subquery: its right side returns one column, and the outer value should be compared with that column for each outer row. Instead, the planner raises an internal result-shape error before execution. The repository's newly added subquery code explicitly claims to support AnySubquery and per-row SubPlan arguments, and the same run proves that the database and fixtures can execute the neighboring subquery forms. The smallest practical remediation is to correct the correlated ANY target/result mapping in the new SubPlan path, then add a regression query covering a one-column correlated ANY subquery.

**Relevant code:**

`crates/sql/src/optimizer/subselect.rs:328-335`

~~~rust
pub fn process_sublinks(ctx: &mut Ctx<'_>, e: Expr, width: usize) -> Expr {
    match e {
        link @ (Expr::Exists(_) | Expr::Scalar(_) | Expr::ArraySubquery(..) | Expr::AnySubquery(..)) => {
            let link = link.map_children(&mut |c| process_sublinks(ctx, c, width));
            make_subplan(ctx, link, false, width)
        }
~~~

`crates/sql/src/optimizer/subselect.rs:131-158`

~~~rust
fn build_subplan(ctx: &Ctx<'_>, mut link: Expr, plan: Plan, mut path: Option<Rc<Path>>, args: Vec<Expr>) -> SubPlan {
    let init_plan = args.is_empty() && !matches!(link, Expr::AnySubquery(..));
    let use_hash_table = match (&link, &path) {
        (Expr::AnySubquery(test, _, false), Some(path)) => {
            args.is_empty() && subpath_is_hashable(path) && testexpr_is_hashable(test)
        }
        _ => false,
    };
    ...
    *link.subquery_mut().expect("a subquery expression") = crate::plan::share_subquery(plan, uncorrelated);
    let mut subplan = SubPlan { link, args, init_plan, planned: true, startup_cost: 0.0, per_call_cost: 0.0 };
~~~

`crates/sql/src/expr.rs:147-152`

~~~rust
fn eval(&self, ctx: &mut Ctx<'_>, row: &[Value]) -> Result<Value> {
    let args = self.args.iter().map(|a| a.eval(ctx, row)).collect::<Result<Vec<Value>>>()?;
    self.link.eval_sublink(ctx, row, &args)
}
~~~

@itoqa

itoqa Bot commented Oct 10, 2026

Copy link
Copy Markdown

Ito QA test results
Ito Diff Report — 0f9bb0b → 9294719: 20 test cases ran, 1 new failure ❌, 2 fixed ✅, 17 passing ✅.

Diff Summary

The run broadly exercised SQL query correctness across set operations, recursion, aggregation, grouping, joins, correlated subqueries, ordering, boundary conditions, and recovery after errors. Coverage included normal results, duplicate and NULL handling, type combinations, planner-sensitive cases, and adversarial edge conditions, with most tested behavior appearing healthy.

Merge with caution — the PR introduces a medium-severity type-handling defect in mixed numeric set-operation queries, causing ordinary filters to fail and affecting valid user queries without an explicit cast. The issue is attributable to this PR and should be considered a merge risk, while the remaining target-build and coverage observations are not additional product failures.

Tests run by Ito

View full run

Result State Severity Type Description
❌ ❌ New Failure Medium severity Rev Filtering the mixed numeric UNION with x >= 2 raises an operator error. The UNION and EXCEPT result columns are reported as text rather than a common numeric type, even though the same rows work when explicitly cast back to bigint.
✅ ❌->✅ Fixed — General LIMIT 0 returned no rows, invalid limits returned clear errors, and later queries still worked in both sessions.
✅ ❌->✅ Fixed — Subquery Each outer row received the correct correlated value, while the independent count stayed at 3. The same session also recovered and returned 42 after the expected division-by-zero error.
✅ Passing — General The database returned the expected rows when nested UNION, INTERSECT, and EXCEPT queries used both ALL and DISTINCT.
✅ Passing — General The SQL server accepted the 10,000-row fixture, but the running target did not include GROUPING SETS syntax, so this check did not exercise the checked-out grouping-set implementation. Repository verification found the relevant planner and executor paths plus existing grouping-set tests; the apparent failure is therefore a target-build mismatch.
✅ Passing — General Indexed MIN and MAX returned the correct values. Queries with grouping, windows, joins, or another aggregate also returned the correct results without using an incorrect one-row shortcut.
✅ Passing — General Nested UNION, INTERSECT, and EXCEPT queries returned the expected rows and duplicate counts for compatible integer and bigint columns.
✅ Passing — General An empty filtered table returned NULL for MIN and MAX, count zero, and the correct HAVING result. A later query on the same session still returned 42.
✅ Passing — General The invalid query returned a clear error without partial rows or a crash. A valid retry on the same connection returned only 1, 2, and 3, matching a fresh connection.
✅ Passing — General Recursive UNION ALL returned the terminal duplicate, while UNION removed it. Both queries stopped after the next round produced no rows.
✅ Passing — General Nested queries returned the expected value for outer IDs 2 and 3 with deferred planning on and off. The unsafe restriction also matched the materialized reference, so correlation stayed correct.
✅ Passing — Grouping The test environment started a PostgreSQL wire-protocol server instead of a browser page, and the running target revision did not include the grouping-set syntax. The repository revision under review contains grouping-set planning code and matching existing tests, so this result is an environment reclassification rather than a product failure.
✅ Passing — Merge The ordered UNION returned 1/a, 1/b, 2/d, and 3/c. The rows were globally sorted, and the two rows with key 1 kept their earlier-input order.
✅ Passing — Minimum The indexed numeric query returned the correct minimum of -7 and maximum of 100, including the expected handling of NULL values.
✅ Passing — Recursion The recursive query returned the starting row and the four rows it generated, then finished after reaching 5.
✅ Passing — Rev UNION ALL kept every duplicate and NULL value, while UNION kept one copy of each distinct value. A nested UNION with integer and bigint inputs also returned the expected rows.
✅ Passing — Rev INTERSECT and EXCEPT returned the correct rows for repeated numbers and NULL values. The ALL forms kept the right duplicate counts, and EXCEPT removed values only from the left side.
✅ Passing — Rev The local server rejected the grouping query before it could exercise the tested source. The server was running commit fc7bd6c2, while this test targets PR commit 9294719; the checked-out source includes grouping-set planning and matching regression tests.
✅ Passing — Setop The nested SQL statement returned the correct projected row: 2.
✅ Passing — Subquery The query returned the expected sum for each outer row and kept the independent count at five.
⏸️ Skipped — General The indexed join returned the right inner row for each matching outer row and kept empty placeholders for NULL and absent keys. The indexed result matched the broad reference with zero differences.
⏸️ Skipped — General Grouped queries returned the same visible labels, counts, and computed values whether the ordering key was hidden or shown. Correlated values changed for each group, independent values stayed constant, and empty groups kept their correct zero or NULL results.
⏸️ Skipped — General The correlated value changed for each outer row, while the independent count stayed at 3 for every row. Rows with NULL and no matching value also kept the expected results.
⏸️ Skipped — Cost The planner chose indexed access for a selective query and repeated indexed lookups for the join. Both queries returned the expected rows without duplicates.
⏸️ Skipped — Cte Verified acceptable by independent adversarial review: the scenario cannot be reached through any real application path. Review notes: The obligation is real, but the finding's proposed mechanism is contradicted by the complete source path: binding creates one Arc, references and optimizer transformations retain clones of that Arc, and execution caches rows by the shared Arc pointer for the statement. No application path shown can produce the finding's claimed independently rebuilt per-alias definitions, and the PR diff a…
⏸️ Skipped — Index The query returned 14 unique qualifying rows. The plan used the table's index for the first condition and still checked the second condition before returning rows.
⏸️ Skipped — Join Each outer row used its own values for the lookup. Matching rows returned their matching inner data, while NULL and missing keys kept empty placeholders.
⏸️ Skipped — Partial The query with the full index condition used the partial index and returned the correct row. The query missing one required condition avoided that index and still returned the correct row.
⏸️ Skipped — Scope Correlated queries returned values for the correct outer row. Joined and windowed results kept repeated matches, empty matches, NULL values, visible columns, and ordering.
⏸️ Skipped — Select The grouped query returned the expected beta and delta rows with only the requested category and total columns. Grouping, filtering, sorting, DISTINCT, LIMIT, OFFSET, and LIMIT 0 all behaved correctly.
⏸️ Skipped — Set The query produced one row for each returned value, kept the neighboring columns, and filled missing values with NULL.

Tip

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

Comment thread crates/sql/src/plan.rs

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

View All Evidence

🆕 New Failure: identified in this diff run

Medium severity Mixed numeric set queries reject normal filters

What failed: Filtering the mixed numeric UNION with x >= 2 raises an operator error. The UNION and EXCEPT result columns are reported as text rather than a common numeric type, even though the same rows work when explicitly cast back to bigint.

Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
  • Severity: Medium Medium severity
  • Impact: Queries that combine different numeric types with UNION or EXCEPT can fail when users apply a normal numeric filter. Users can work around this by adding an explicit cast, but the unmodified query does not return its expected rows.
  • Steps to Reproduce:
    1. Run SELECT x FROM (SELECT 1::smallint AS x UNION SELECT 2::bigint UNION SELECT NULL::bigint) u WHERE x >= 2 OR x IS NULL;.
    2. Inspect the type with SELECT x, pg_typeof(x) FROM (SELECT 1::smallint AS x UNION SELECT 2::bigint UNION SELECT NULL::bigint) u;.
    3. Repeat with an EXCEPT query that combines integer and bigint leaves, then inspect pg_typeof(x).
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: The normal planner path establishes a common type for each set-operation column in crates/sql/src/plan.rs:869-899. It calls common_type for both branches, coerces each branch to the selected type, and stores that type in the result columns before constructing Plan::SetOp. The PR's optimizer reconstruction then rebuilds a Query in crates/sql/src/optimizer/query.rs:193-211. At line 208 it takes col_types from rtable.first().coltypes and assigns those types to every set-operation node at lines 209 and 234-240, rather than carrying the set operation's already-resolved common output type through the reconstructed query. The outer set-operation columns are represented as relation-zero variables at query.rs:210, and crates/sql/src/optimizer/nodefuncs.rs:34-40 resolves those variables from parse.set_operations.col_types. The resulting metadata is inconsistent with the original common-type contract, so the executor exposes text values and the comparison binder rejects text >= integer. The smallest targeted fix is to preserve the common output type vector from the planned set operation when constructing set_operations and use it consistently for the reconstructed target list and relation-zero variables; avoid a broad optimizer rewrite.
  • Why this is likely a bug: The failure is deterministic for ordinary SQL and is independently confirmed by the type inspection: both UNION and EXCEPT return pg_typeof(x) as text for numeric inputs, while typed post-materialization references return bigint and the casted workaround returns the expected rows. The repository's planner contract says set-operation columns use the common type of both branches, and existing EXCEPT tests expect an integer result column. This is therefore a production type-propagation defect in the PR's new optimizer path, not a browser or connection problem. A targeted correction to carry the common column types into the reconstructed Query should restore numeric predicates without changing set-operation duplicate or direction semantics.
Relevant code

crates/sql/src/plan.rs:869-899

plan_set_operation documents that set-operation columns use the common types of both branches, calls common_type for each pair, coerces each branch to those types, and stores them in the result columns.

crates/sql/src/optimizer/query.rs:193-211

set_operation_query reconstructs the set-operation Query, derives col_types from rtable.first().coltypes, propagates them, and creates relation-zero output variables.

crates/sql/src/optimizer/nodefuncs.rs:33-42

query_expr_type resolves a relation-zero variable from parse.set_operations.col_types, so incorrect reconstructed metadata controls the type seen by outer predicates.

crates/sql/src/optimizer/prepunion.rs:341-347

generate_setop_tlist builds output variables from col_types and assumes the binder already coerced every input to the set-operation column types.

crates/tests/tests/scripts/union.rs:35-50

Existing EXCEPT coverage expects the result column to retain the integer type (INT4), establishing that set-operation output is typed rather than generic text.
Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.

**Medium severity — Mixed numeric set queries reject normal filters**

**What failed:** Filtering the mixed numeric UNION with x >= 2 raises an operator error. The UNION and EXCEPT result columns are reported as text rather than a common numeric type, even though the same rows work when explicitly cast back to bigint.

- **Impact:** Queries that combine different numeric types with UNION or EXCEPT can fail when users apply a normal numeric filter. Users can work around this by adding an explicit cast, but the unmodified query does not return its expected rows.
- **Steps to reproduce:**
  1. Run SELECT x FROM (SELECT 1::smallint AS x UNION SELECT 2::bigint UNION SELECT NULL::bigint) u WHERE x >= 2 OR x IS NULL;.
  2. Inspect the type with SELECT x, pg_typeof(x) FROM (SELECT 1::smallint AS x UNION SELECT 2::bigint UNION SELECT NULL::bigint) u;.
  3. Repeat with an EXCEPT query that combines integer and bigint leaves, then inspect pg_typeof(x).
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** The normal planner path establishes a common type for each set-operation column in crates/sql/src/plan.rs:869-899. It calls common_type for both branches, coerces each branch to the selected type, and stores that type in the result columns before constructing Plan::SetOp. The PR's optimizer reconstruction then rebuilds a Query in crates/sql/src/optimizer/query.rs:193-211. At line 208 it takes col_types from rtable.first().coltypes and assigns those types to every set-operation node at lines 209 and 234-240, rather than carrying the set operation's already-resolved common output type through the reconstructed query. The outer set-operation columns are represented as relation-zero variables at query.rs:210, and crates/sql/src/optimizer/nodefuncs.rs:34-40 resolves those variables from parse.set_operations.col_types. The resulting metadata is inconsistent with the original common-type contract, so the executor exposes text values and the comparison binder rejects text >= integer. The smallest targeted fix is to preserve the common output type vector from the planned set operation when constructing set_operations and use it consistently for the reconstructed target list and relation-zero variables; avoid a broad optimizer rewrite.
- **Why this is likely a bug:** The failure is deterministic for ordinary SQL and is independently confirmed by the type inspection: both UNION and EXCEPT return pg_typeof(x) as text for numeric inputs, while typed post-materialization references return bigint and the casted workaround returns the expected rows. The repository's planner contract says set-operation columns use the common type of both branches, and existing EXCEPT tests expect an integer result column. This is therefore a production type-propagation defect in the PR's new optimizer path, not a browser or connection problem. A targeted correction to carry the common column types into the reconstructed Query should restore numeric predicates without changing set-operation duplicate or direction semantics.

**Relevant code:**

`crates/sql/src/plan.rs:869-899`

~~~rust
plan_set_operation documents that set-operation columns use the common types of both branches, calls common_type for each pair, coerces each branch to those types, and stores them in the result columns.
~~~

`crates/sql/src/optimizer/query.rs:193-211`

~~~rust
set_operation_query reconstructs the set-operation Query, derives col_types from rtable.first().coltypes, propagates them, and creates relation-zero output variables.
~~~

`crates/sql/src/optimizer/nodefuncs.rs:33-42`

~~~rust
query_expr_type resolves a relation-zero variable from parse.set_operations.col_types, so incorrect reconstructed metadata controls the type seen by outer predicates.
~~~

`crates/sql/src/optimizer/prepunion.rs:341-347`

~~~rust
generate_setop_tlist builds output variables from col_types and assumes the binder already coerced every input to the set-operation column types.
~~~

`crates/tests/tests/scripts/union.rs:35-50`

~~~rust
Existing EXCEPT coverage expects the result column to retain the integer type (INT4), establishing that set-operation output is typed rather than generic text.
~~~

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.

1 participant