Skip to content

sql: Don't panic when a to-text cast needs a disallowed subquery#37067

Merged
aljoscha merged 1 commit into
MaterializeInc:mainfrom
aljoscha:fix-sql-270-typeconv-to-string-panic
Jun 16, 2026
Merged

sql: Don't panic when a to-text cast needs a disallowed subquery#37067
aljoscha merged 1 commit into
MaterializeInc:mainfrom
aljoscha:fix-sql-270-typeconv-to-string-panic

Conversation

@aljoscha

@aljoscha aljoscha commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Motivation

Fixes SQL-270
Fixes SQL-269

Several SQLancer/SQLsmith builds hit a coordinator/worker panic:

thread 'tokio:work-9' panicked at src/sql/src/plan/typeconv.rs:1162:10:
cast known to exist: InvalidCast { name: "pg_catalog.concat", ccx: Explicit, from: "regtype", to: "text" }

typeconv::to_string and to_jsonb assumed that casting any type to text
always succeeds and called .expect("cast known to exist"). That assumption is
wrong: the mz_aclitem and reg* (regtype, regclass, ...) text casts are
implemented as catalog subqueries, and plan_cast rejects subqueries in
contexts that disallow them -- a RETURNING clause, or the element-cast context
of to_jsonb on an array/list. The cast returns an error, and .expect()
turned that into a panic that crashes the worker. Reachable from plain user
input, e.g.:

CREATE TABLE t (a regtype);
INSERT INTO t VALUES ('date'::regtype) RETURNING concat(a);   -- SQL-270
SELECT to_jsonb(ARRAY[mz_internal.make_mz_aclitem('u1', 'u2', 'USAGE')]);  -- SQL-269
SELECT to_jsonb(ARRAY['mz_databases']::regclass[]);            -- SQL-269

Description

Change to_string and to_jsonb to return Result<HirScalarExpr, PlanError>
and thread the plan_cast error through their (few) callers with ?. The
casts that genuinely never fail (numeric widening for JSON numbers) keep their
.expect().

These queries now produce a graceful, query-level planning error instead of
panicking, e.g. pg_catalog.concat does not support casting from regtype to text and to_jsonb does not support casting from mz_aclitem to text.

This is the "stop the bleeding" fix that the team agreed on in the issue
discussion. It does not make these casts work in subquery-disallowed
contexts, so it diverges from PostgreSQL, which evaluates them via the type's
output function and succeeds. Closing that gap (planning such casts as Rust
functions that read the catalog directly, or lifting the allow_subqueries
restriction where a dataflow is involved) is a larger change left as follow-up.

Verification

New regression tests:

  • test/sqllogictest/regtype.slt -- concat(regtype) succeeds in a plain
    SELECT but errors gracefully in a RETURNING clause (SQL-270).
  • test/sqllogictest/jsonb.slt -- to_jsonb / jsonb_build_array over
    mz_aclitem[] and regclass[] error gracefully (SQL-269).

I also confirmed against a real PostgreSQL 16 that all of these queries succeed
there, which is why full parity is called out as follow-up rather than the
behavior asserted here.

cargo check, cargo fmt, and cargo clippy -p mz-sql --all-targets -- -D warnings pass locally.

@aljoscha aljoscha requested a review from a team as a code owner June 16, 2026 11:18
@ggevay ggevay self-requested a review June 16, 2026 11:21
`typeconv::to_string` and `to_jsonb` assumed casting any type to text always
succeeds and called `.expect("cast known to exist")`. That assumption is
wrong: the `mz_aclitem` and `reg*` text casts are implemented as catalog
subqueries, and `plan_cast` rejects subqueries in contexts that disallow them
-- a `RETURNING` clause, or the element-cast context of `to_jsonb` on an array
or list. The cast then returns an error that `.expect()` turned into a panic,
crashing the worker.

We now thread the cast error through `to_string`/`to_jsonb` and their callers,
so these cases produce a graceful planning error (e.g. "pg_catalog.concat does
not support casting from regtype to text") instead of panicking.

This only stops the panic. Making these casts work in subquery-disallowed
contexts to match PostgreSQL (which evaluates them via the type's output
function) is a larger change and is not attempted here.

Fixes SQL-270
Fixes SQL-269
@aljoscha aljoscha force-pushed the fix-sql-270-typeconv-to-string-panic branch from c15f639 to cc71823 Compare June 16, 2026 11:37
@aljoscha aljoscha merged commit 8d8fc0d into MaterializeInc:main Jun 16, 2026
116 checks passed
@aljoscha aljoscha deleted the fix-sql-270-typeconv-to-string-panic branch June 16, 2026 11:49
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.

2 participants