Repository navigation
duckdb: SELECT * EXCLUDE (...) and * REPLACE (...) give one untyped column and no error #4660
Description
Version
main at commit 3ad8b20 (2026-10-23), built with CGO_ENABLED=0 go install github.com/sqlc-dev/sqlc/cmd/sqlc@3ad8b20fb02b5bea81591b673da0758b8685d951.
sqlc version prints v1.31.1 because internal/info/facts.go hard-codes it.
The DuckDB engine was added in #4621 and is not in a tagged release.
What happened?
SELECT * EXCLUDE (col) and SELECT * REPLACE (...) are not expanded. The result is one column with no name and no type,
and sqlc analyze reports no error. DuckDB returns the remaining columns.
- On
main,sqlc generatefails by accident withillegal character U+003F '?'. The cause is the?column?name (see the separate issue about unnamed result columns). - If the
?column?name is fixed,sqlc generatesucceeds and writes[]any. The generated code fails at run time:
sql: expected 3 destination arguments in Scan, not 1.
Expected: either the remaining columns with their names and types, or an error at generate time.
What DuckDB v1.5.6 reports (DESCRIBE): id INTEGER, name VARCHAR, age INTEGER for * EXCLUDE (secret).
Cause
convertStar in internal/engine/duckdb/convert.go (lines 769–774) returns todo(e) for a star that has EXCLUDE,
REPLACE, RENAME or COLUMNS. The comment says "no equivalent". The analyzer treats an *ast.TODO expression as
untyped (internal/core/analyzer/expr.go, case *ast.TODO), and emitStar is not called for it.
Reproduction
repro6.sh (needs Go 1.27+ and a C compiler)
#!/usr/bin/env bash
# Needs Go 1.27+ and a C compiler (duckdb-go v2.10506.0).
rm -rf repro6 && mkdir repro6 && cd repro6
cat > sqlc.yaml <<'EOF'
version: "2"
sql:
- engine: "duckdb"
schema: "schema.sql"
queries: "query.sql"
gen:
go:
package: "repro"
out: "out"
EOF
cat > schema.sql <<'EOF'
CREATE TABLE people (id INTEGER NOT NULL, name VARCHAR NOT NULL, secret VARCHAR, age INTEGER);
EOF
echo "== what sqlc analyze reports"
for q in 'SELECT * EXCLUDE (secret) FROM people;' 'SELECT * FROM people;'; do
printf -- '-- name: Q :many\n%s\n' "$q" > query.sql
echo "-- $q"; sqlc analyze --dialect duckdb --schema schema.sql query.sql | grep -E '"name"' | grep -v '"name": "Q"'
done
echo; echo "== what sqlc generate does"
printf -- '-- name: Q :many\nSELECT * EXCLUDE (secret) FROM people;\n' > query.sql
rm -rf out; sqlc generate 2>&1 | grep -E 'error generating code' | head -2
if [ -f out/query.sql.go ]; then
echo "generated: $(grep -E '^func \(q \*Queries\) Q' out/query.sql.go | sed 's/^func (q \*Queries) //')"
cat > main.go <<'EOF'
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/duckdb/duckdb-go/v2"
"example.com/repro/out"
)
func main() {
db, _ := sql.Open("duckdb", "")
db.SetMaxOpenConns(1)
defer db.Close()
db.Exec(`CREATE TABLE people (id INTEGER NOT NULL, name VARCHAR NOT NULL, secret VARCHAR, age INTEGER);
INSERT INTO people VALUES (1, 'a', 's', 10);`)
var cols int
_ = db.QueryRow("SELECT count(*) FROM (DESCRIBE SELECT * EXCLUDE (secret) FROM people)").Scan(&cols)
fmt.Println("DuckDB returns columns:", cols)
_, err := repro.New(db).Q(context.Background())
fmt.Println("generated Q():", err)
}
EOF
printf 'module example.com/repro\n\ngo 1.27\n' > go.mod
go get github.com/duckdb/duckdb-go/[email protected] >/dev/null 2>&1
go mod tidy >/dev/null 2>&1
go run .
fiOutput with the ?column? name fixed:
== what sqlc analyze reports
-- SELECT * EXCLUDE (secret) FROM people;
"name": ""
-- SELECT * FROM people;
"name": "id",
"name": "integer"
"name": "name",
"name": "varchar"
"name": "secret",
"name": "varchar",
"name": "age",
"name": "integer",
== what sqlc generate does
generated: Q(ctx context.Context) ([]any, error) {
DuckDB returns columns: 3
generated Q(): sql: expected 3 destination arguments in Scan, not 1
Why there is no patch
A real fix needs the exclude and replace lists to travel from the converter, through the AST, to emitStar and to the
code that rewrites the star in the query text. REPLACE also needs the new expression. That is a design change.
A smaller change could report an error for a star with modifiers until they are supported. I did not make it,
because it changes how every unsupported expression is treated, and that is the maintainers' choice.
Relevant log output
== what sqlc analyze reports
-- SELECT * EXCLUDE (secret) FROM people;
"name": "?column?"
-- SELECT * FROM people;
"name": "id",
"name": "integer"
"name": "name",
"name": "varchar"
"name": "secret",
"name": "varchar",
"name": "age",
"name": "integer",
== what sqlc generate does
error generating code: source error: 42:13: illegal character U+003F '?' (and 10 more errors)Database schema
CREATE TABLE people (id INTEGER NOT NULL, name VARCHAR NOT NULL, secret VARCHAR, age INTEGER);SQL queries
SELECT * EXCLUDE (secret) FROM people;
SELECT * REPLACE (age + 1 AS age) FROM people;
SELECT * FROM people; -- worksConfiguration
version: "2"
sql:
- engine: "duckdb"
schema: "schema.sql"
queries: "query.sql"
gen:
go:
package: "repro"
out: "out"Playground URL
Not available. play.sqlc.dev runs only v1.18.0–v1.25.0, and the DuckDB engine is not in a release (v1.25.0 answers unknown engine: duckdb; main, a commit and every later release return HTTP 500). The report has a script that needs only go install github.com/sqlc-dev/sqlc/cmd/sqlc@3ad8b20fb02b5bea81591b673da0758b8685d951.
What operating system are you using?
Linux
What database engines are you using?
(none of the listed options; the engine is duckdb)
What type of code are you generating?
Go
Related
Related: #4657 (the ?column? name).
Activity