cockroachdb / cockroachdb/cockroach

sql: support CREATE TABLE AS with data inside stored procedures

Open
#172,760 0 comments 0 reactions 0 assignees View on GitHub
A-sql-plpgsql C-enhancement O-agent T-sql-foundations
Dominant language
Go
Stars
32.5k
Forks
4.1k
PR merge metrics
PR metrics pending

Description

**Is your feature request related to a problem? Please describe.**

`CREATE TABLE ... AS ` with data (CTAS) does not work inside a PL/pgSQL stored procedure. Today it fails at `CALL` time with `table "…" is being added` (SQLSTATE 55000), and #172758 tracks adding a clean up-front block so users get an actionable error instead. This issue tracks the actual capability: making CTAS-with-data usable inside a procedure body.

CTAS as a step in a stored procedure — create a table from a query, then read/update/join it in later statements of the same body — is a common data-warehouse ETL pattern, and one that PostgreSQL supports. Once #172758 blocks it, users have to fall back to `CREATE TABLE ... AS ... WITH NO DATA` followed by `INSERT ... SELECT`, which is more verbose and less intuitive.

**Describe the solution you'd like**

Support `CREATE TABLE AS ... WITH DATA` (temporary and permanent) inside a stored procedure, such that the created table is populated and immediately usable by later statements in the same procedure body.

The underlying cause is that a procedure body runs in the enclosing `CALL` transaction (a genuinely multi-statement transaction) but inherits `TxnIsSingleStmt = true` from the outer `CALL`. That flag steers CTAS onto its single-statement async path (descriptor written in the `ADD` state, row backfill deferred to a post-commit schema-change job), which is invalid inside a procedure. Clearing `TxnIsSingleStmt` for the duration of procedure-body execution selects the existing synchronous CTAS path (`create_table.go`) that fills the table inline within the transaction and leaves it `PUBLIC` — the same path `BEGIN; CREATE TABLE t AS SELECT …; …; COMMIT;` already uses.

This supersedes the block from #172758: the block should be removed as part of delivering this support.

**Describe alternatives you've considered**

- **`WITH NO DATA` + `INSERT ... SELECT`** — works today and is the documented workaround, but it's a manual rewrite of every CTAS and obscures intent.
- **Keeping the async path but waiting for the backfill job inside the body** — would require running a post-commit schema-change job mid-transaction, which is not how the body transaction works; the synchronous fill is the natural fit.

**Additional context**

- Blocked-error tracking issue: #172758 (should be reverted when this lands).
- The synchronous multi-statement CTAS path already exists and is exercised by CTAS inside an explicit `BEGIN ... COMMIT`, so this is largely a matter of routing procedure-body execution onto it.
- Master-only; not a backport candidate (behavior change / new capability).

Epic CRDB-65937

Jira issue: CRDB-66022

Contributor guide

Open the contributing guide

Research direction

Start by reading create_table.go and the existing tests for CTAS inside an explicit BEGIN ... COMMIT, then trace procedure-body execution and the TxnIsSingleStmt handling described in the issue. Done means temporary and permanent CTAS with data work inside stored procedures, later statements can use the created table, and the block from #172758 is removed.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, sql
Domain
databases
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.