Skip to content

sql: Bound custom type resolution depth and total work#37581

Merged
def- merged 3 commits into
MaterializeInc:mainfrom
def-:pr-sql-514
Jul 14, 2026
Merged

sql: Bound custom type resolution depth and total work#37581
def- merged 3 commits into
MaterializeInc:mainfrom
def-:pr-sql-514

Conversation

@def-

@def- def- commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

scalar_type_from_catalog resolves a custom type into an in-memory SqlScalarType by recursing into its referenced sub-types. Two shapes of user-created type made this pathological, both reachable at CREATE TYPE plan time (and any later reference):

  • A deep chain of LIST/MAP types (l0 <- l1 <- ... <- lN) recursed N deep and overflowed the stack.
  • A record type whose fields reference the same sub-type (CREATE TYPE tN AS (a t{N-1}, b t{N-1})) resolves to a type tree that is exponential in its depth, because record fields hold owned copies rather than shared references. Building/cloning it exhausted memory and CPU.

Bound both: reject a type nested deeper than MAX_TYPE_NESTING_DEPTH, and cap the total number of sub-type resolutions (MAX_TYPE_RESOLUTION_NODES) so an exponentially large type is rejected before it is built, rather than after. Both now return a graceful planning error.

Closes: SQL-514, SQL-513

`scalar_type_from_catalog` resolves a custom type into an in-memory
`SqlScalarType` by recursing into its referenced sub-types. Two shapes of
user-created type made this pathological, both reachable at `CREATE TYPE`
plan time (and any later reference):

* A deep chain of LIST/MAP types (`l0 <- l1 <- ... <- lN`) recursed N deep
  and overflowed the stack.
* A record type whose fields reference the same sub-type
  (`CREATE TYPE tN AS (a t{N-1}, b t{N-1})`) resolves to a type tree that is
  exponential in its depth, because record fields hold owned copies rather
  than shared references. Building/cloning it exhausted memory and CPU.

Bound both: reject a type nested deeper than MAX_TYPE_NESTING_DEPTH, and cap
the total number of sub-type resolutions (MAX_TYPE_RESOLUTION_NODES) so an
exponentially large type is rejected before it is built, rather than after.
Both now return a graceful planning error.

Closes: SQL-514

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@def- def- marked this pull request as ready for review July 10, 2026 22:08
@def- def- requested a review from a team as a code owner July 10, 2026 22:08
def- and others added 2 commits July 11, 2026 06:26
`scalar_type_from_catalog` reset the node budget on every call, but
`plan_create_type` and `CatalogType::desc` call it once per record field.
A record whose fields are each just under the budget therefore passed
creation, then materialized an unbounded type tree during sequencing and
could exhaust environmentd's memory. This preserved the authenticated
denial of service SQL-514 aimed to close.

Thread one `TypeResolutionBudget` across all of a root type's children,
charging the root itself and resolving children at depth one. Creation
now rejects exactly the types a later direct resolution would reject, so
`d16` (two `d15` fields), a wide record of `d14` fields, and `l128` (deep
enough that its leaf resolves past the depth limit) are all refused up
front rather than stored unusable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The custom-type resolution depth and complexity bound rejects a type at
plan time. Bootstrap re-plans every persisted `create_sql`, so a wide
record or deep chain that an earlier version accepted would fail the new
bound and abort environmentd, crash-looping on every restart.

Lift the bound while re-planning persisted items, gated on
`unsafe_enable_unbounded_custom_type_resolution`, which
`enable_for_item_parsing` force-enables during bootstrap. A grandfathered
type resolves to a finite tree, so it loads without materializing an
unbounded one. A normal user session still applies the bound and returns
the graceful planning error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@def- def- requested a review from a team as a code owner July 11, 2026 22:02
@def- def- requested a review from mtabebe July 13, 2026 08:02

@mtabebe mtabebe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM thanks for the tests and thorough comments. Nice clean error messages 👍

@def- def- merged commit bfdf7c6 into MaterializeInc:main Jul 14, 2026
124 checks passed
@def- def- deleted the pr-sql-514 branch July 14, 2026 07:55
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