Skip to content

fix(sqlite): Extract columns from CREATE TABLE ... AS statement - #4684

Open
manuelschoene wants to merge 2 commits into
sqlc-dev:mainfrom
manuelschoene:fix/sqlite-parse-ctas
Open

manuelschoene wants to merge 2 commits into
sqlc-dev:mainfrom
manuelschoene:fix/sqlite-parse-ctas

Conversation

@manuelschoene

Copy link
Copy Markdown

Fixes #4683

In the SQLite engine, CREATE TABLE … AS SELECT added a table to the catalog with no columns. Any query that read from that table failed with column "…" does not exist. This happened on both the default and the core analyzer.

Change

convertCreateTableStmt (internal/engine/sqlite/convert.go) always built an ast.CreateTableStmt from n.Columns, and that list is empty for a CTAS. The parser does return the query in meyer.CreateTableStmt.Select, but the converter only used it to mark the statement Incomplete.

Now, when Select is set, the converter returns an ast.CreateTableAsStmt with the converted query and an IntoClause naming the table, as the DuckDB converter already does. Both catalogs already support this node: createTableAs in the legacy catalog and applyView in the core schema. They take the columns from the query's output. The n.Select != nil check in Incomplete could no longer be reached, so I removed it.

Tests

New end-to-end case internal/endtoend/testdata/create_table_as/sqlite/. It covers CREATE TABLE … AS SELECT * and CREATE TABLE IF NOT EXISTS … AS SELECT with a column alias and a WHERE, and reads from both tables. The generated models are CopyAll{ID, Val, Score} and CopySome{ID, Label}.

Verified on a clean checkout of this branch:

  • gofmt -l on the changed Go files reports nothing.
  • go vet ./internal/engine/sqlite/... ./internal/endtoend/... reports nothing.
  • go build ./... succeeds.
  • go test ./internal/endtoend -run 'TestReplay/base|TestFormat|TestJsonSchema' passes, including the new case.
  • go test ./... passes, except for tests that need a live PostgreSQL or MySQL server (TestValidSchema, internal/x/expander). No database was running locally, so these failed with connection timeouts. They do not touch the SQLite engine.

The opt-in core context (SQLC_TEST_CORE=1) generates a ListCopyAllRow row struct for the new case instead of the CopyAll model. The SQLite conversion itself works there. The core catalog dump lists only kind r relations, so views and CTAS tables get no model, and create_table_as/postgresql and the create_view cases fail the same way. #4614 fixes this ("Views absent from models", which also covers CREATE TABLE AS). Once that lands, this case should pass in the core context too.

Note

The columns keep the nullability of the SELECT, so val TEXT NOT NULL becomes Val string. SQLite does not copy constraints into a CTAS table, so strictly speaking the column is nullable there. This PR keeps the behavior sqlc already has for PostgreSQL. Changing it would affect both engines and could be a separate change.

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.

sqlite: CREATE TABLE ... AS SELECT creates a table with no columns

1 participant