Skip to content

sql: reject DDL in PL/pgSQL procedure bodies when late binding is off#170902

Draft
spilchen wants to merge 2 commits into
cockroachdb:masterfrom
spilchen:spilchen/gh-170651/pr2-ddl-rejection
Draft

sql: reject DDL in PL/pgSQL procedure bodies when late binding is off#170902
spilchen wants to merge 2 commits into
cockroachdb:masterfrom
spilchen:spilchen/gh-170651/pr2-ddl-rejection

Conversation

@spilchen
Copy link
Copy Markdown
Contributor

Stacked PR, only look at the last commit

Previously, when late binding is disabled, a PL/pgSQL procedure body containing allowlisted DDL (CREATE TABLE, DROP TABLE, CREATE SCHEMA, DROP SCHEMA, CREATE ROLE, DROP ROLE) was built by the optbuilder. If subsequent body statements referenced the just-created object, the early-bound build could not resolve them and the user saw a confusing
"relation does not exist" error.

This commit adds a pre-build pass (ddlVisitor) that walks the PL/pgSQL body to classify DDL statements before the optbuilder attempts to build them. The classifier drives two new error paths in CREATE PROCEDURE:

  1. Allowlisted DDL with late binding off (and V26_3 active) is rejected with a hint pointing at the late-binding cluster setting.
  2. Unsupported DDL (e.g. ALTER TABLE, CREATE INDEX) is rejected with an "unimplemented" error before the optbuilder reaches its existing per-statement DDL gate.

The allowlist is also expanded to include CREATE SCHEMA, DROP SCHEMA, CREATE ROLE, and DROP ROLE in stored procedure bodies, factored into a shared isAllowlistedProcedureDDL helper used by both ddlVisitor and the
optbuilder's existing buildStmtAtRoot gate.

Epic: CRDB-31256
Resolves: #170651

Release note (sql change): A PL/pgSQL procedure body containing DDL is
now rejected at CREATE PROCEDURE time with a hint suggesting the
sql.procedures.plpgsql.late_binding.enabled cluster setting be
enabled. Additionally, CREATE SCHEMA, DROP SCHEMA, CREATE ROLE, and
DROP ROLE are now allowed inside stored procedure bodies (subject to
the same late-binding requirement).

spilchen and others added 2 commits May 25, 2026 17:13
Previously, PL/pgSQL procedure bodies were always analyzed at CREATE
PROCEDURE time, recording back-references on referenced descriptors.
References had to exist at CREATE time.

This commit adds a new public cluster setting
`sql.procedures.plpgsql.late_binding.enabled` (default false). When
enabled, PL/pgSQL procedure bodies are parsed but not analyzed;
references are resolved at CALL time, matching PostgreSQL PL/pgSQL
semantics. LANGUAGE SQL procedures and UDFs always use early binding
regardless of the setting.

The gate is centralized in PLpgSQLProcedureLateBindingEnabled (which
also checks clusterversion.V26_3) and applied at the three CREATE /
CREATE OR REPLACE entry points: the legacy planner, the declarative
schema changer, and the optbuilder.

Improving the CREATE-time error message when a PL/pgSQL procedure
body contains DDL but late binding is off is left to a follow-up;
this commit preserves the existing V26_3 DDL-in-procedure behavior
unchanged.

Epic: CRDB-31256
Informs: cockroachdb#170651

Release note (sql change): Added public cluster setting
`sql.procedures.plpgsql.late_binding.enabled` (default false). When
enabled, PL/pgSQL procedure bodies are not resolved at CREATE
PROCEDURE time; references are resolved at CALL time instead,
matching PostgreSQL PL/pgSQL semantics. LANGUAGE SQL procedures and
functions are unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously, when late binding is disabled, a PL/pgSQL procedure body
containing allowlisted DDL (CREATE TABLE, DROP TABLE, CREATE SCHEMA,
DROP SCHEMA, CREATE ROLE, DROP ROLE) was built by the optbuilder. If
subsequent body statements referenced the just-created object, the
early-bound build could not resolve them and the user saw a confusing
"relation does not exist" error.

This commit adds a pre-build pass (ddlVisitor) that walks the PL/pgSQL
body to classify DDL statements before the optbuilder attempts to build
them. The classifier drives two new error paths in CREATE PROCEDURE:

  1. Allowlisted DDL with late binding off (and V26_3 active) is
     rejected with a hint pointing at the late-binding cluster setting.
  2. Unsupported DDL (e.g. ALTER TABLE, CREATE INDEX) is rejected with
     an "unimplemented" error before the optbuilder reaches its
     existing per-statement DDL gate.

The allowlist is also expanded to include CREATE SCHEMA, DROP SCHEMA,
CREATE ROLE, and DROP ROLE in stored procedure bodies, factored into a
shared isAllowlistedProcedureDDL helper used by both ddlVisitor and the
optbuilder's existing buildStmtAtRoot gate.

Epic: CRDB-31256
Resolves: cockroachdb#170651

Release note (sql change): A PL/pgSQL procedure body containing DDL is
now rejected at CREATE PROCEDURE time with a hint suggesting the
`sql.procedures.plpgsql.late_binding.enabled` cluster setting be
enabled. Additionally, CREATE SCHEMA, DROP SCHEMA, CREATE ROLE, and
DROP ROLE are now allowed inside stored procedure bodies (subject to
the same late-binding requirement).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@spilchen spilchen self-assigned this May 25, 2026
@trunk-io
Copy link
Copy Markdown
Contributor

trunk-io Bot commented May 25, 2026

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@blathers-crl
Copy link
Copy Markdown

blathers-crl Bot commented May 25, 2026

Your pull request contains more than 1000 changes. It is strongly encouraged to split big PRs into smaller chunks.

🦉 Hoot! I am a Blathers, a bot for CockroachDB. My owner is dev-inf.

@cockroach-teamcity
Copy link
Copy Markdown
Member

This change is Reviewable

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.

sql: support late binding within stored procedures

2 participants