Skip to content

feat(vet): allow vet to lint anonymous parameter names#4469

Open
JasonDav wants to merge 6 commits into
sqlc-dev:mainfrom
JasonDav:vet-anonymous-params
Open

feat(vet): allow vet to lint anonymous parameter names#4469
JasonDav wants to merge 6 commits into
sqlc-dev:mainfrom
JasonDav:vet-anonymous-params

Conversation

@JasonDav
Copy link
Copy Markdown

@JasonDav JasonDav commented Jun 5, 2026

Allow vet to check for empty Parameter names

sqlc vet lets you write CEL lint rules against your queries, but today the only information exposed about a query parameter is its position (number):

message Parameter {
  int32 number = 1;
}

That makes it impossible to write a rule about anonymous parameters — parameters sqlc couldn't derive a name for, which it renders in generated code with names Column1, Column2, etc in the golang output.
Anonymous parameters mean ugly and borderline unreadable for large queries hence large param lists.
In this case we force engineers to go back and name them so that the Param structs properties are readable.
Catching them today requires a manual review pass; there's no way to fail sqlc vet on them.

This PR exposes the parameter's name to vet rules so you can lint for this yourself.

What this enables

# sqlc.yaml
rules:
  - name: no-anonymous-params
    message: "don't use anonymous parameters"
    rule: |
      query.params.exists(p, p.name == "")

Given a query whose parameter sqlc can't name:

-- name: ListByIDs :many
SELECT id FROM authors WHERE id = ANY($1::bigint[]);

sqlc vet now fails:

$ sqlc vet
query.sql: ListByIDs: no-anonymous-params: don't use anonymous parameters
# exit status 1

Naming the parameter clears it:

-- name: ListByIDs :many
SELECT id FROM authors WHERE id = ANY(sqlc.arg(ids)::bigint[]);   -- or @ids
$ sqlc vet
# exit status 0

Backward compatibility

Purely additive. name is a new optional proto field; existing vet rules and configs are unaffected, and the field defaults to "" for any parameter without a column.

Test plan

  • go build ./...
  • go test ./internal/endtoend/ -run 'TestReplay/.*/vet_' — passes (all vet replay cases, both new cases on base and managed-db)
  • Verified end-to-end against a built binary: anonymous param → vet exits 1 with the violation; named param → exits 0.

JasonDav and others added 6 commits June 5, 2026 21:52
Regenerated internal/vet/vet.pb.go via buf generate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Test currently fails because vetQuery does not yet populate the new
Parameter.name field, so named params are wrongly flagged as anonymous.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Anonymous parameters have an empty name, enabling lint rules such as
query.params.exists(p, p.name == "").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Companion to vet_anonymous_params: every parameter is named (inferred,
sqlc.arg(), and @param), so the no-anonymous-params rule must emit no
violations. Guards against false positives / a regression in
Parameter.name population.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JasonDav JasonDav changed the title feat(vet): expose query parameter name to vet rules feat(vet): allow vet to lint anonymous parameter names Jun 5, 2026
@JasonDav JasonDav marked this pull request as ready for review June 5, 2026 20:26
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.

1 participant