Skip to content

Commit dddfe4f

Browse files
feat(vet): Introduce a query annotation to opt out of sqlc vet rules (#2474)
* feat(vet): Introduce a query annotation to opt out of sqlc vet rules * Add proper logging, and a test * Don't remove comments when parsing query annotation metadata
1 parent 4d764a2 commit dddfe4f

File tree

8 files changed

+84
-7
lines changed

8 files changed

+84
-7
lines changed

docs/howto/vet.md

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
Rules are defined in the `sqlc` [configuration](../reference/config) file. They consist
88
of a name, message, and a [Common Expression Language (CEL)](https://github.com/google/cel-spec)
99
expression. Expressions are evaluated using [cel-go](https://github.com/google/cel-go).
10-
If an expression evaluates to `true`, an error is reported using the given message.
10+
If an expression evaluates to `true`, `sqlc vet` will report an error using the given message.
1111

12-
Each expression has access to variables from your sqlc configuration and queries,
13-
defined in the following struct:
12+
## Defining lint rules
13+
14+
Each lint rule's CEL expression has access to variables from your sqlc configuration and queries,
15+
defined in the following struct.
1416

1517
```proto
1618
message Config
@@ -109,4 +111,47 @@ example](https://github.com/kyleconroy/sqlc/blob/main/examples/authors/sqlc.yaml
109111

110112
Please note that `sqlc` does not manage or migrate your database. Use your
111113
migration tool of choice to create the necessary database tables and objects
112-
before running `sqlc vet`.
114+
before running `sqlc vet` with the `sqlc/db-prepare` rule.
115+
116+
## Running lint rules
117+
118+
When you add the name of a defined rule to the rules list
119+
for a [sql package](https://docs.sqlc.dev/en/stable/reference/config.html#sql),
120+
`sqlc vet` will evaluate that rule against every query in the package.
121+
122+
In the example below, two rules are defined but only one is enabled.
123+
124+
```yaml
125+
version: 2
126+
sql:
127+
- schema: "query.sql"
128+
queries: "query.sql"
129+
engine: "postgresql"
130+
gen:
131+
go:
132+
package: "authors"
133+
out: "db"
134+
rules:
135+
- no-delete
136+
rules:
137+
- name: no-pg
138+
message: "invalid engine: postgresql"
139+
rule: |
140+
config.engine == "postgresql"
141+
- name: no-delete
142+
message: "don't use delete statements"
143+
rule: |
144+
query.sql.contains("DELETE")
145+
```
146+
147+
### Opting-out of lint rules
148+
149+
For any query, you can tell `sqlc vet` not to evaluate lint rules using the
150+
`@sqlc-vet-disable` query annotation.
151+
152+
```sql
153+
/* name: GetAuthor :one */
154+
/* @sqlc-vet-disable */
155+
SELECT * FROM authors
156+
WHERE id = ? LIMIT 1;
157+
```

internal/cmd/vet.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"io"
9+
"log"
910
"os"
1011
"path/filepath"
1112
"runtime/trace"
@@ -31,6 +32,7 @@ import (
3132
var ErrFailedChecks = errors.New("failed checks")
3233

3334
const RuleDbPrepare = "sqlc/db-prepare"
35+
const QueryFlagSqlcVetDisable = "@sqlc-vet-disable"
3436

3537
func NewCmdVet() *cobra.Command {
3638
return &cobra.Command{
@@ -249,7 +251,6 @@ func (c *checker) checkSQL(ctx context.Context, s config.SQL) error {
249251
return ErrFailedChecks
250252
}
251253

252-
// TODO: Add MySQL support
253254
var prep preparer
254255
if s.Database != nil {
255256
if c.NoDatabase {
@@ -299,6 +300,12 @@ func (c *checker) checkSQL(ctx context.Context, s config.SQL) error {
299300
req := codeGenRequest(result, combo)
300301
cfg := vetConfig(req)
301302
for i, query := range req.Queries {
303+
if result.Queries[i].Flags[QueryFlagSqlcVetDisable] {
304+
if debug.Active {
305+
log.Printf("Skipping vet rules for query: %s\n", query.Name)
306+
}
307+
continue
308+
}
302309
q := vetQuery(query)
303310
for _, name := range s.Rules {
304311
// Built-in rule
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"command": "vet"
3+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- name: SkipVet :exec
2+
-- @sqlc-vet-disable
3+
SELECT true;
4+
5+
-- name: RunVet :exec
6+
SELECT true;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version: 2
2+
sql:
3+
- schema: "query.sql"
4+
queries: "query.sql"
5+
engine: "postgresql"
6+
gen:
7+
go:
8+
package: "db"
9+
out: "db"
10+
rules:
11+
- always-fail
12+
rules:
13+
- name: always-fail
14+
message: "Fail"
15+
rule: "true"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
query.sql: RunVet: always-fail: Fail

internal/engine/dolphin/convert.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package dolphin
22

33
import (
4-
"fmt"
54
"log"
65
"strings"
76

@@ -144,7 +143,7 @@ func (c *cc) convertAlterTableStmt(n *pcast.AlterTableStmt) ast.Node {
144143

145144
default:
146145
if debug.Active {
147-
fmt.Printf("dolphin.convert: Unknown alter table cmd %v\n", spec.Tp)
146+
log.Printf("dolphin.convert: Unknown alter table cmd %v\n", spec.Tp)
148147
}
149148
continue
150149
}

internal/metadata/meta.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func ParseQueryFlags(comments []string) (map[string]bool, error) {
115115
if strings.HasPrefix(cleanLine, "@") {
116116
flagName := strings.SplitN(cleanLine, " ", 2)[0]
117117
flags[flagName] = true
118+
continue
118119
}
119120
}
120121
return flags, nil

0 commit comments

Comments
 (0)