Skip to content

duckdb: SELECT * EXCLUDE (...) and * REPLACE (...) give one untyped column and no error #4660

Open

Description

@xen0l

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 generate fails by accident with illegal character U+003F '?'. The cause is the ?column? name (see the separate issue about unnamed result columns).
  • If the ?column? name is fixed, sqlc generate succeeds 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 .
fi

Output 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;                                -- works

Configuration

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions