diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10c7f41a7c..7b4b87de10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.22.5' + go-version: '1.22.8' - name: install gotestsum run: go install gotest.tools/gotestsum@latest @@ -78,6 +78,6 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.22.5' + go-version: '1.22.8' - run: go install golang.org/x/vuln/cmd/govulncheck@latest - run: govulncheck ./... diff --git a/Dockerfile b/Dockerfile index a01a36c4fa..6950b2cb41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # STEP 1: Build sqlc -FROM golang:1.23.1 AS builder +FROM golang:1.23.3 AS builder COPY . /workspace WORKDIR /workspace diff --git a/docs/howto/vet.md b/docs/howto/vet.md index ff6ff3ceb0..3f6c6a025b 100644 --- a/docs/howto/vet.md +++ b/docs/howto/vet.md @@ -259,7 +259,24 @@ rules: ### Opting-out of lint rules For any query, you can tell `sqlc vet` not to evaluate lint rules using the -`@sqlc-vet-disable` query annotation. +`@sqlc-vet-disable` query annotation. The annotation accepts a list of rules to ignore. + +```sql +/* name: GetAuthor :one */ +/* @sqlc-vet-disable sqlc/db-prepare no-pg */ +SELECT * FROM authors +WHERE id = ? LIMIT 1; +``` +The rules can also be split across lines. +```sql +/* name: GetAuthor :one */ +/* @sqlc-vet-disable sqlc/db-prepare */ +/* @sqlc-vet-disable no-pg */ +SELECT * FROM authors +WHERE id = ? LIMIT 1; +``` + +To skip all rules for a query, you can provide the `@sqlc-vet-disable` annotation without any parameters. ```sql /* name: GetAuthor :one */ diff --git a/docs/reference/config.md b/docs/reference/config.md index 2629babf4b..9d334834f0 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -135,6 +135,8 @@ The `gen` mapping supports the following keys: - Output directory for generated code. - `sql_package`: - Either `pgx/v4`, `pgx/v5` or `database/sql`. Defaults to `database/sql`. +- `sql_driver`: + - Either `github.com/jackc/pgx/v4`, `github.com/jackc/pgx/v5`, `github.com/lib/pq` or `github.com/go-sql-driver/mysql`. No defaults. Required if query annotation `:copyfrom` is used. - `emit_db_tags`: - If true, add DB tags to generated structs. Defaults to `false`. - `emit_prepared_queries`: diff --git a/docs/reference/datatypes.md b/docs/reference/datatypes.md index e38e1ddc76..a440be657a 100644 --- a/docs/reference/datatypes.md +++ b/docs/reference/datatypes.md @@ -158,8 +158,8 @@ For MySQL, there is no native `uuid` data type. When using `UUID_TO_BIN` to stor ## JSON By default, sqlc will generate the `[]byte`, `pgtype.JSON` or `json.RawMessage` for JSON column type. -But if you use the `pgx/v5` sql package then you can specify a some struct instead of default type. -The `pgx` implementation will marshall/unmarshall the struct automatically. +But if you use the `pgx/v5` sql package then you can specify a struct instead of default type. +The `pgx` implementation will marshal/unmarshal the struct automatically. ```go package dto diff --git a/docs/reference/language-support.rst b/docs/reference/language-support.rst index 2548fad3b1..ccb62dc667 100644 --- a/docs/reference/language-support.rst +++ b/docs/reference/language-support.rst @@ -20,7 +20,7 @@ New languages can be added via :doc:`plugins <../guides/plugins>`. Language Plugin MySQL PostgreSQL SQLite ======== ================================= =============== ============ =============== F# `kaashyapan/sqlc-gen-fsharp`_ Not implemented Beta Beta -C# `DaredevilOSS/sqlc-gen-csharp`_ Beta Beta Not implemented +C# `DaredevilOSS/sqlc-gen-csharp`_ Beta Beta Beta Ruby `DaredevilOSS/sqlc-gen-ruby`_ Beta Beta Beta [Any] `fdietze/sqlc-gen-from-template`_ Stable Stable Stable ======== ================================= =============== ============ =============== diff --git a/docs/reference/query-annotations.md b/docs/reference/query-annotations.md index 0045f49fa7..4fabe05aae 100644 --- a/docs/reference/query-annotations.md +++ b/docs/reference/query-annotations.md @@ -222,3 +222,9 @@ func (b *CreateBookBatchResults) Close() error { //... } ``` + +## `:copyfrom` + +__NOTE: This command is driver and package specific, see [how to insert](../howto/insert.md#using-copyfrom) + +This command is used to insert rows a lot faster than sequential inserts. diff --git a/docs/requirements.txt b/docs/requirements.txt index b167cf18f6..bf3f7f60c8 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,22 +1,22 @@ Babel==2.16.0 Jinja2==3.1.4 -MarkupSafe==2.1.5 +MarkupSafe==3.0.2 Pygments==2.18.0 Sphinx==7.4.7 certifi==2024.8.30 chardet==5.2.0 commonmark==0.9.1 docutils==0.20.1 -idna==3.8 +idna==3.10 imagesize==1.4.1 myst-parser==4.0.0 -packaging==24.1 -pyparsing==3.1.4 -pytz==2024.1 +packaging==24.2 +pyparsing==3.2.0 +pytz==2024.2 requests==2.32.3 snowballstemmer==2.2.0 sphinx-favicon==1.0.1 -sphinx-rtd-theme==2.0.0 +sphinx-rtd-theme==3.0.2 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-devhelp==2.0.0 sphinxcontrib-htmlhelp==2.1.0 @@ -24,4 +24,4 @@ sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 sphinxext-rediraffe==0.2.7 -urllib3==2.2.2 +urllib3==2.2.3 diff --git a/docs/tutorials/getting-started-sqlite.md b/docs/tutorials/getting-started-sqlite.md index f855e41af6..b5dea7ee3a 100644 --- a/docs/tutorials/getting-started-sqlite.md +++ b/docs/tutorials/getting-started-sqlite.md @@ -128,7 +128,7 @@ import ( "log" "reflect" - _ "github.com/mattn/go-sqlite3" + _ "modernc.org/sqlite" "tutorial.sqlc.dev/app/tutorial" ) @@ -139,7 +139,7 @@ var ddl string func run() error { ctx := context.Background() - db, err := sql.Open("sqlite3", ":memory:") + db, err := sql.Open("sqlite", ":memory:") if err != nil { return err } diff --git a/go.mod b/go.mod index fe705481ca..151071b32c 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/sqlc-dev/sqlc -go 1.22 - -toolchain go1.22.5 +go 1.22.9 require ( github.com/antlr4-go/antlr/v4 v4.13.1 @@ -10,10 +8,10 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/fatih/structtag v1.2.0 github.com/go-sql-driver/mysql v1.8.1 - github.com/google/cel-go v0.21.0 + github.com/google/cel-go v0.22.1 github.com/google/go-cmp v0.6.0 github.com/jackc/pgx/v4 v4.18.3 - github.com/jackc/pgx/v5 v5.6.0 + github.com/jackc/pgx/v5 v5.7.1 github.com/jinzhu/inflection v1.0.0 github.com/lib/pq v1.10.9 github.com/pganalyze/pg_query_go/v5 v5.1.0 @@ -21,17 +19,18 @@ require ( github.com/riza-io/grpc-go v0.2.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 - github.com/tetratelabs/wazero v1.8.0 + github.com/tetratelabs/wazero v1.8.2 github.com/wasilibs/go-pgquery v0.0.0-20240606042535-c0843d6592cc github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/sync v0.8.0 - google.golang.org/grpc v1.66.0 - google.golang.org/protobuf v1.34.2 + golang.org/x/sync v0.9.0 + google.golang.org/grpc v1.68.0 + google.golang.org/protobuf v1.35.2 gopkg.in/yaml.v3 v3.0.1 - modernc.org/sqlite v1.32.0 + modernc.org/sqlite v1.34.2 ) require ( + cel.dev/expr v0.18.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -43,9 +42,9 @@ require ( github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgtype v1.14.0 // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect @@ -60,13 +59,13 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.27.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.55.3 // indirect diff --git a/go.sum b/go.sum index 6631673ad5..a7b687b2c4 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= +cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -30,8 +32,10 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI= -github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40= +github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -74,8 +78,8 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -88,13 +92,13 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= -github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -174,8 +178,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g= -github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= +github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= +github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/wasilibs/go-pgquery v0.0.0-20240606042535-c0843d6592cc h1:Hgim1Xgk1+viV7p0aZh9OOrMRfG+E4mGA+JsI2uB0+k= github.com/wasilibs/go-pgquery v0.0.0-20240606042535-c0843d6592cc/go.mod h1:ah6UfXIl/oA0K3SbourB/UHggVJOBXwPZ2XudDmmFac= github.com/wasilibs/wazero-helpers v0.0.0-20240604052452-61d7981e9a38 h1:RBu75fhabyxyGJ2zhkoNuRyObBMhVeMoXqmeaPTg2CQ= @@ -221,8 +225,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -235,11 +239,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -253,8 +257,8 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -262,8 +266,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -281,16 +285,16 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -329,8 +333,8 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= -modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= -modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= +modernc.org/sqlite v1.34.2 h1:J9n76TPsfYYkFkZ9Uy1QphILYifiVEwwOT7yP5b++2Y= +modernc.org/sqlite v1.34.2/go.mod h1:dnR723UrTtjKpoHCAMN0Q/gZ9MT4r+iRvIBb9umWFkU= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/internal/cmd/vet.go b/internal/cmd/vet.go index fe3f3b6bdc..8a7f8077db 100644 --- a/internal/cmd/vet.go +++ b/internal/cmd/vet.go @@ -6,11 +6,13 @@ import ( "encoding/json" "errors" "fmt" + "github.com/sqlc-dev/sqlc/internal/constants" "io" "log" "os" "path/filepath" "runtime/trace" + "slices" "strings" "time" @@ -37,9 +39,6 @@ var ErrFailedChecks = errors.New("failed checks") var pjson = protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true} -const RuleDbPrepare = "sqlc/db-prepare" -const QueryFlagSqlcVetDisable = "@sqlc-vet-disable" - func NewCmdVet() *cobra.Command { return &cobra.Command{ Use: "vet", @@ -109,7 +108,7 @@ func Vet(ctx context.Context, dir, filename string, opts *Options) error { } rules := map[string]rule{ - RuleDbPrepare: {NeedsPrepare: true}, + constants.QueryRuleDbPrepare: {NeedsPrepare: true}, } for _, c := range conf.Rules { @@ -538,11 +537,23 @@ func (c *checker) checkSQL(ctx context.Context, s config.SQL) error { req := codeGenRequest(result, combo) cfg := vetConfig(req) for i, query := range req.Queries { - if result.Queries[i].Metadata.Flags[QueryFlagSqlcVetDisable] { - if debug.Active { - log.Printf("Skipping vet rules for query: %s\n", query.Name) + md := result.Queries[i].Metadata + if md.Flags[constants.QueryFlagSqlcVetDisable] { + // If the vet disable flag is specified without any rules listed, all rules are ignored. + if len(md.RuleSkiplist) == 0 { + if debug.Active { + log.Printf("Skipping all vet rules for query: %s\n", query.Name) + } + continue + } + + // Rules which are listed to be disabled but not declared in the config file are rejected. + for r := range md.RuleSkiplist { + if !slices.Contains(s.Rules, r) { + fmt.Fprintf(c.Stderr, "%s: %s: rule-check error: rule %q does not exist in the config file\n", query.Filename, query.Name, r) + errored = true + } } - continue } evalMap := map[string]any{ @@ -551,74 +562,81 @@ func (c *checker) checkSQL(ctx context.Context, s config.SQL) error { } for _, name := range s.Rules { - rule, ok := c.Rules[name] - if !ok { - return fmt.Errorf("type-check error: a rule with the name '%s' does not exist", name) - } - - if rule.NeedsPrepare { - if prep == nil { - fmt.Fprintf(c.Stderr, "%s: %s: %s: error preparing query: database connection required\n", query.Filename, query.Name, name) - errored = true - continue + if _, skip := md.RuleSkiplist[name]; skip { + if debug.Active { + log.Printf("Skipping vet rule %q for query: %s\n", name, query.Name) } - prepName := fmt.Sprintf("sqlc_vet_%d_%d", time.Now().Unix(), i) - if err := prep.Prepare(ctx, prepName, query.Text); err != nil { - fmt.Fprintf(c.Stderr, "%s: %s: %s: error preparing query: %s\n", query.Filename, query.Name, name, err) - errored = true - continue + } else { + rule, ok := c.Rules[name] + if !ok { + return fmt.Errorf("type-check error: a rule with the name '%s' does not exist", name) } - } - - // short-circuit for "sqlc/db-prepare" rule which doesn't have a CEL program - if rule.Program == nil { - continue - } - // Get explain output for this query if we need it - _, pgsqlOK := evalMap["postgresql"] - _, mysqlOK := evalMap["mysql"] - if rule.NeedsExplain && !(pgsqlOK || mysqlOK) { - if expl == nil { - fmt.Fprintf(c.Stderr, "%s: %s: %s: error explaining query: database connection required\n", query.Filename, query.Name, name) - errored = true - continue + if rule.NeedsPrepare { + if prep == nil { + fmt.Fprintf(c.Stderr, "%s: %s: %s: error preparing query: database connection required\n", query.Filename, query.Name, name) + errored = true + continue + } + prepName := fmt.Sprintf("sqlc_vet_%d_%d", time.Now().Unix(), i) + if err := prep.Prepare(ctx, prepName, query.Text); err != nil { + fmt.Fprintf(c.Stderr, "%s: %s: %s: error preparing query: %s\n", query.Filename, query.Name, name, err) + errored = true + continue + } } - engineOutput, err := expl.Explain(ctx, query.Text, query.Params...) - if err != nil { - fmt.Fprintf(c.Stderr, "%s: %s: %s: error explaining query: %s\n", query.Filename, query.Name, name, err) - errored = true + + // short-circuit for "sqlc/db-prepare" rule which doesn't have a CEL program + if rule.Program == nil { continue } - evalMap["postgresql"] = engineOutput.PostgreSQL - evalMap["mysql"] = engineOutput.MySQL - } + // Get explain output for this query if we need it + _, pgsqlOK := evalMap["postgresql"] + _, mysqlOK := evalMap["mysql"] + if rule.NeedsExplain && !(pgsqlOK || mysqlOK) { + if expl == nil { + fmt.Fprintf(c.Stderr, "%s: %s: %s: error explaining query: database connection required\n", query.Filename, query.Name, name) + errored = true + continue + } + engineOutput, err := expl.Explain(ctx, query.Text, query.Params...) + if err != nil { + fmt.Fprintf(c.Stderr, "%s: %s: %s: error explaining query: %s\n", query.Filename, query.Name, name, err) + errored = true + continue + } + + evalMap["postgresql"] = engineOutput.PostgreSQL + evalMap["mysql"] = engineOutput.MySQL + } - if debug.Debug.DumpVetEnv { - fmt.Printf("vars for rule '%s' evaluating against query '%s':\n", name, query.Name) - debug.DumpAsJSON(evalMap) - } + if debug.Debug.DumpVetEnv { + fmt.Printf("vars for rule '%s' evaluating against query '%s':\n", name, query.Name) + debug.DumpAsJSON(evalMap) + } - out, _, err := (*rule.Program).Eval(evalMap) - if err != nil { - return err - } - tripped, ok := out.Value().(bool) - if !ok { - return fmt.Errorf("expression returned non-bool value: %v", out.Value()) - } - if tripped { - // TODO: Get line numbers in the output - if rule.Message == "" { - fmt.Fprintf(c.Stderr, "%s: %s: %s\n", query.Filename, query.Name, name) - } else { - fmt.Fprintf(c.Stderr, "%s: %s: %s: %s\n", query.Filename, query.Name, name, rule.Message) + out, _, err := (*rule.Program).Eval(evalMap) + if err != nil { + return err + } + tripped, ok := out.Value().(bool) + if !ok { + return fmt.Errorf("expression returned non-bool value: %v", out.Value()) + } + if tripped { + // TODO: Get line numbers in the output + if rule.Message == "" { + fmt.Fprintf(c.Stderr, "%s: %s: %s\n", query.Filename, query.Name, name) + } else { + fmt.Fprintf(c.Stderr, "%s: %s: %s: %s\n", query.Filename, query.Name, name, rule.Message) + } + errored = true } - errored = true } } } + if errored { return ErrFailedChecks } diff --git a/internal/codegen/golang/reserved.go b/internal/codegen/golang/reserved.go index fee711c57b..0461c4a2de 100644 --- a/internal/codegen/golang/reserved.go +++ b/internal/codegen/golang/reserved.go @@ -59,6 +59,8 @@ func IsReserved(s string) bool { return true case "var": return true + case "q": + return true default: return false } diff --git a/internal/compiler/find_params.go b/internal/compiler/find_params.go index ca38199b9d..8199addd33 100644 --- a/internal/compiler/find_params.go +++ b/internal/compiler/find_params.go @@ -195,7 +195,7 @@ func (p paramSearch) Visit(node ast.Node) astutils.Visitor { if n.Sel == nil { p.parent = node } else { - if sel, ok := n.Sel.(*ast.SelectStmt); ok && sel.FromClause != nil { + if sel, ok := n.Sel.(*ast.SelectStmt); ok && sel.FromClause != nil && len(sel.FromClause.Items) > 0 { from := sel.FromClause if schema, ok := from.Items[0].(*ast.RangeVar); ok && schema != nil { p.rangeVar = &ast.RangeVar{ diff --git a/internal/compiler/parse.go b/internal/compiler/parse.go index 022d23ea22..681d291122 100644 --- a/internal/compiler/parse.go +++ b/internal/compiler/parse.go @@ -65,7 +65,7 @@ func (c *Compiler) parseQuery(stmt ast.Node, src string, o opts.Parser) (*Query, return nil, err } - md.Params, md.Flags, err = metadata.ParseParamsAndFlags(cleanedComments) + md.Params, md.Flags, md.RuleSkiplist, err = metadata.ParseCommentFlags(cleanedComments) if err != nil { return nil, err } diff --git a/internal/constants/query.go b/internal/constants/query.go new file mode 100644 index 0000000000..a572c56c6f --- /dev/null +++ b/internal/constants/query.go @@ -0,0 +1,12 @@ +package constants + +// Flags +const ( + QueryFlagParam = "@param" + QueryFlagSqlcVetDisable = "@sqlc-vet-disable" +) + +// Rules +const ( + QueryRuleDbPrepare = "sqlc/db-prepare" +) diff --git a/internal/dbmanager/client.go b/internal/dbmanager/client.go index c189da5552..18aec947cb 100644 --- a/internal/dbmanager/client.go +++ b/internal/dbmanager/client.go @@ -106,7 +106,7 @@ func (m *ManagedClient) CreateDatabase(ctx context.Context, req *CreateDatabaseR conn, err := pgx.Connect(ctx, uri.String()) if err != nil { - pool.Exec(ctx, fmt.Sprintf(`DROP DATABASE "%s" IF EXISTS WITH (FORCE)`, name)) + pool.Exec(ctx, fmt.Sprintf(`DROP DATABASE IF EXISTS "%s" WITH (FORCE)`, name)) return nil, fmt.Errorf("connect %s: %s", name, err) } defer conn.Close(ctx) @@ -123,7 +123,7 @@ func (m *ManagedClient) CreateDatabase(ctx context.Context, req *CreateDatabaseR } if migrationErr != nil { - pool.Exec(ctx, fmt.Sprintf(`DROP DATABASE "%s" IF EXISTS WITH (FORCE)`, name)) + pool.Exec(ctx, fmt.Sprintf(`DROP DATABASE IF EXISTS "%s" WITH (FORCE)`, name)) return nil, migrationErr } diff --git a/internal/endtoend/testdata/in_union/mysql/go/db.go b/internal/endtoend/testdata/in_union/mysql/go/db.go new file mode 100644 index 0000000000..0ea90b328a --- /dev/null +++ b/internal/endtoend/testdata/in_union/mysql/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/in_union/mysql/go/models.go b/internal/endtoend/testdata/in_union/mysql/go/models.go new file mode 100644 index 0000000000..f9c4899c13 --- /dev/null +++ b/internal/endtoend/testdata/in_union/mysql/go/models.go @@ -0,0 +1,25 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package querytest + +import ( + "database/sql" +) + +type Author struct { + ID int32 + Name string + Bio sql.NullString +} + +type Book1 struct { + AuthorID int32 + Name sql.NullString +} + +type Book2 struct { + AuthorID int32 + Name sql.NullString +} diff --git a/internal/endtoend/testdata/in_union/mysql/go/query.sql.go b/internal/endtoend/testdata/in_union/mysql/go/query.sql.go new file mode 100644 index 0000000000..8606353613 --- /dev/null +++ b/internal/endtoend/testdata/in_union/mysql/go/query.sql.go @@ -0,0 +1,38 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const getAuthors = `-- name: GetAuthors :many +SELECT id, name, bio FROM authors +WHERE author_id IN (SELECT author_id FROM book1 UNION SELECT author_id FROM book2) +` + +func (q *Queries) GetAuthors(ctx context.Context) ([]Author, error) { + rows, err := q.db.QueryContext(ctx, getAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Author + for rows.Next() { + var i Author + if err := rows.Scan(&i.ID, &i.Name, &i.Bio); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/in_union/mysql/query.sql b/internal/endtoend/testdata/in_union/mysql/query.sql new file mode 100644 index 0000000000..69606f538b --- /dev/null +++ b/internal/endtoend/testdata/in_union/mysql/query.sql @@ -0,0 +1,3 @@ +-- name: GetAuthors :many +SELECT * FROM authors +WHERE author_id IN (SELECT author_id FROM book1 UNION SELECT author_id FROM book2); diff --git a/internal/endtoend/testdata/in_union/mysql/schema.sql b/internal/endtoend/testdata/in_union/mysql/schema.sql new file mode 100644 index 0000000000..47264e2bbc --- /dev/null +++ b/internal/endtoend/testdata/in_union/mysql/schema.sql @@ -0,0 +1,15 @@ +CREATE TABLE authors ( + id int PRIMARY KEY, + name text NOT NULL, + bio text +); +CREATE TABLE book1 ( + author_id int PRIMARY KEY, + name text, + FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`) ON DELETE CASCADE +); +CREATE TABLE book2 ( + author_id int PRIMARY KEY, + name text, + FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`) ON DELETE CASCADE +); diff --git a/internal/endtoend/testdata/in_union/mysql/sqlc.json b/internal/endtoend/testdata/in_union/mysql/sqlc.json new file mode 100644 index 0000000000..974aa9ff9e --- /dev/null +++ b/internal/endtoend/testdata/in_union/mysql/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "engine": "mysql", + "path": "go", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/inflection/mysql/go/models.go b/internal/endtoend/testdata/inflection/mysql/go/models.go index cc60f36582..1fa48e00f3 100644 --- a/internal/endtoend/testdata/inflection/mysql/go/models.go +++ b/internal/endtoend/testdata/inflection/mysql/go/models.go @@ -12,6 +12,10 @@ type Campus struct { ID string } +type ProductMetadatum struct { + ID string +} + type ProductMetum struct { ID string } diff --git a/internal/endtoend/testdata/inflection/mysql/go/query.sql.go b/internal/endtoend/testdata/inflection/mysql/go/query.sql.go index f6282cceed..c33eea852e 100644 --- a/internal/endtoend/testdata/inflection/mysql/go/query.sql.go +++ b/internal/endtoend/testdata/inflection/mysql/go/query.sql.go @@ -9,6 +9,33 @@ import ( "context" ) +const getProductMetadata = `-- name: GetProductMetadata :many +SELECT id FROM product_metadata +` + +func (q *Queries) GetProductMetadata(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, getProductMetadata) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listCalories = `-- name: ListCalories :many SELECT id FROM calories ` diff --git a/internal/endtoend/testdata/inflection/mysql/query.sql b/internal/endtoend/testdata/inflection/mysql/query.sql index e3917086b9..74e1aaf8c3 100644 --- a/internal/endtoend/testdata/inflection/mysql/query.sql +++ b/internal/endtoend/testdata/inflection/mysql/query.sql @@ -9,3 +9,6 @@ SELECT * FROM product_meta; /* name: ListCalories :many */ SELECT * FROM calories; + +/* name: GetProductMetadata :many */ +SELECT * FROM product_metadata; \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/mysql/schema.sql b/internal/endtoend/testdata/inflection/mysql/schema.sql index b0d88c8695..f7d5c7c2a5 100644 --- a/internal/endtoend/testdata/inflection/mysql/schema.sql +++ b/internal/endtoend/testdata/inflection/mysql/schema.sql @@ -2,4 +2,4 @@ CREATE TABLE campus (id text not null); CREATE TABLE students (id text not null); CREATE TABLE product_meta (id text not null); CREATE TABLE calories (id text not null); - +CREATE TABLE product_metadata (id text not null); diff --git a/internal/endtoend/testdata/inflection/postgresql/pgx/v4/go/models.go b/internal/endtoend/testdata/inflection/postgresql/pgx/v4/go/models.go index cc60f36582..1fa48e00f3 100644 --- a/internal/endtoend/testdata/inflection/postgresql/pgx/v4/go/models.go +++ b/internal/endtoend/testdata/inflection/postgresql/pgx/v4/go/models.go @@ -12,6 +12,10 @@ type Campus struct { ID string } +type ProductMetadatum struct { + ID string +} + type ProductMetum struct { ID string } diff --git a/internal/endtoend/testdata/inflection/postgresql/pgx/v4/go/query.sql.go b/internal/endtoend/testdata/inflection/postgresql/pgx/v4/go/query.sql.go index 2791144914..31a5440758 100644 --- a/internal/endtoend/testdata/inflection/postgresql/pgx/v4/go/query.sql.go +++ b/internal/endtoend/testdata/inflection/postgresql/pgx/v4/go/query.sql.go @@ -9,6 +9,30 @@ import ( "context" ) +const getProductMetadata = `-- name: GetProductMetadata :many +SELECT id FROM product_metadata +` + +func (q *Queries) GetProductMetadata(ctx context.Context) ([]string, error) { + rows, err := q.db.Query(ctx, getProductMetadata) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listCalories = `-- name: ListCalories :many SELECT id FROM calories ` diff --git a/internal/endtoend/testdata/inflection/postgresql/pgx/v4/query.sql b/internal/endtoend/testdata/inflection/postgresql/pgx/v4/query.sql index 6e2828d458..abc0da2ef2 100644 --- a/internal/endtoend/testdata/inflection/postgresql/pgx/v4/query.sql +++ b/internal/endtoend/testdata/inflection/postgresql/pgx/v4/query.sql @@ -9,3 +9,6 @@ SELECT * FROM product_meta; -- name: ListCalories :many SELECT * FROM calories; + +-- name: GetProductMetadata :many +SELECT * FROM product_metadata; \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/postgresql/pgx/v4/schema.sql b/internal/endtoend/testdata/inflection/postgresql/pgx/v4/schema.sql index b0d88c8695..ad17ad7427 100644 --- a/internal/endtoend/testdata/inflection/postgresql/pgx/v4/schema.sql +++ b/internal/endtoend/testdata/inflection/postgresql/pgx/v4/schema.sql @@ -2,4 +2,4 @@ CREATE TABLE campus (id text not null); CREATE TABLE students (id text not null); CREATE TABLE product_meta (id text not null); CREATE TABLE calories (id text not null); - +CREATE TABLE product_metadata (id text not null); \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/postgresql/pgx/v5/go/models.go b/internal/endtoend/testdata/inflection/postgresql/pgx/v5/go/models.go index cc60f36582..1fa48e00f3 100644 --- a/internal/endtoend/testdata/inflection/postgresql/pgx/v5/go/models.go +++ b/internal/endtoend/testdata/inflection/postgresql/pgx/v5/go/models.go @@ -12,6 +12,10 @@ type Campus struct { ID string } +type ProductMetadatum struct { + ID string +} + type ProductMetum struct { ID string } diff --git a/internal/endtoend/testdata/inflection/postgresql/pgx/v5/go/query.sql.go b/internal/endtoend/testdata/inflection/postgresql/pgx/v5/go/query.sql.go index 2791144914..31a5440758 100644 --- a/internal/endtoend/testdata/inflection/postgresql/pgx/v5/go/query.sql.go +++ b/internal/endtoend/testdata/inflection/postgresql/pgx/v5/go/query.sql.go @@ -9,6 +9,30 @@ import ( "context" ) +const getProductMetadata = `-- name: GetProductMetadata :many +SELECT id FROM product_metadata +` + +func (q *Queries) GetProductMetadata(ctx context.Context) ([]string, error) { + rows, err := q.db.Query(ctx, getProductMetadata) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listCalories = `-- name: ListCalories :many SELECT id FROM calories ` diff --git a/internal/endtoend/testdata/inflection/postgresql/pgx/v5/query.sql b/internal/endtoend/testdata/inflection/postgresql/pgx/v5/query.sql index 6e2828d458..abc0da2ef2 100644 --- a/internal/endtoend/testdata/inflection/postgresql/pgx/v5/query.sql +++ b/internal/endtoend/testdata/inflection/postgresql/pgx/v5/query.sql @@ -9,3 +9,6 @@ SELECT * FROM product_meta; -- name: ListCalories :many SELECT * FROM calories; + +-- name: GetProductMetadata :many +SELECT * FROM product_metadata; \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/postgresql/pgx/v5/schema.sql b/internal/endtoend/testdata/inflection/postgresql/pgx/v5/schema.sql index b0d88c8695..ad17ad7427 100644 --- a/internal/endtoend/testdata/inflection/postgresql/pgx/v5/schema.sql +++ b/internal/endtoend/testdata/inflection/postgresql/pgx/v5/schema.sql @@ -2,4 +2,4 @@ CREATE TABLE campus (id text not null); CREATE TABLE students (id text not null); CREATE TABLE product_meta (id text not null); CREATE TABLE calories (id text not null); - +CREATE TABLE product_metadata (id text not null); \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/postgresql/stdlib/go/models.go b/internal/endtoend/testdata/inflection/postgresql/stdlib/go/models.go index cc60f36582..1fa48e00f3 100644 --- a/internal/endtoend/testdata/inflection/postgresql/stdlib/go/models.go +++ b/internal/endtoend/testdata/inflection/postgresql/stdlib/go/models.go @@ -12,6 +12,10 @@ type Campus struct { ID string } +type ProductMetadatum struct { + ID string +} + type ProductMetum struct { ID string } diff --git a/internal/endtoend/testdata/inflection/postgresql/stdlib/go/query.sql.go b/internal/endtoend/testdata/inflection/postgresql/stdlib/go/query.sql.go index f6282cceed..c33eea852e 100644 --- a/internal/endtoend/testdata/inflection/postgresql/stdlib/go/query.sql.go +++ b/internal/endtoend/testdata/inflection/postgresql/stdlib/go/query.sql.go @@ -9,6 +9,33 @@ import ( "context" ) +const getProductMetadata = `-- name: GetProductMetadata :many +SELECT id FROM product_metadata +` + +func (q *Queries) GetProductMetadata(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, getProductMetadata) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listCalories = `-- name: ListCalories :many SELECT id FROM calories ` diff --git a/internal/endtoend/testdata/inflection/postgresql/stdlib/query.sql b/internal/endtoend/testdata/inflection/postgresql/stdlib/query.sql index 6e2828d458..abc0da2ef2 100644 --- a/internal/endtoend/testdata/inflection/postgresql/stdlib/query.sql +++ b/internal/endtoend/testdata/inflection/postgresql/stdlib/query.sql @@ -9,3 +9,6 @@ SELECT * FROM product_meta; -- name: ListCalories :many SELECT * FROM calories; + +-- name: GetProductMetadata :many +SELECT * FROM product_metadata; \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/postgresql/stdlib/schema.sql b/internal/endtoend/testdata/inflection/postgresql/stdlib/schema.sql index b0d88c8695..ad17ad7427 100644 --- a/internal/endtoend/testdata/inflection/postgresql/stdlib/schema.sql +++ b/internal/endtoend/testdata/inflection/postgresql/stdlib/schema.sql @@ -2,4 +2,4 @@ CREATE TABLE campus (id text not null); CREATE TABLE students (id text not null); CREATE TABLE product_meta (id text not null); CREATE TABLE calories (id text not null); - +CREATE TABLE product_metadata (id text not null); \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/sqlite/go/models.go b/internal/endtoend/testdata/inflection/sqlite/go/models.go index cc60f36582..1fa48e00f3 100644 --- a/internal/endtoend/testdata/inflection/sqlite/go/models.go +++ b/internal/endtoend/testdata/inflection/sqlite/go/models.go @@ -12,6 +12,10 @@ type Campus struct { ID string } +type ProductMetadatum struct { + ID string +} + type ProductMetum struct { ID string } diff --git a/internal/endtoend/testdata/inflection/sqlite/go/query.sql.go b/internal/endtoend/testdata/inflection/sqlite/go/query.sql.go index f6282cceed..c33eea852e 100644 --- a/internal/endtoend/testdata/inflection/sqlite/go/query.sql.go +++ b/internal/endtoend/testdata/inflection/sqlite/go/query.sql.go @@ -9,6 +9,33 @@ import ( "context" ) +const getProductMetadata = `-- name: GetProductMetadata :many +SELECT id FROM product_metadata +` + +func (q *Queries) GetProductMetadata(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, getProductMetadata) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listCalories = `-- name: ListCalories :many SELECT id FROM calories ` diff --git a/internal/endtoend/testdata/inflection/sqlite/query.sql b/internal/endtoend/testdata/inflection/sqlite/query.sql index e3917086b9..74e1aaf8c3 100644 --- a/internal/endtoend/testdata/inflection/sqlite/query.sql +++ b/internal/endtoend/testdata/inflection/sqlite/query.sql @@ -9,3 +9,6 @@ SELECT * FROM product_meta; /* name: ListCalories :many */ SELECT * FROM calories; + +/* name: GetProductMetadata :many */ +SELECT * FROM product_metadata; \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/sqlite/schema.sql b/internal/endtoend/testdata/inflection/sqlite/schema.sql index b0d88c8695..ad17ad7427 100644 --- a/internal/endtoend/testdata/inflection/sqlite/schema.sql +++ b/internal/endtoend/testdata/inflection/sqlite/schema.sql @@ -2,4 +2,4 @@ CREATE TABLE campus (id text not null); CREATE TABLE students (id text not null); CREATE TABLE product_meta (id text not null); CREATE TABLE calories (id text not null); - +CREATE TABLE product_metadata (id text not null); \ No newline at end of file diff --git a/internal/endtoend/testdata/params_go_keywords/postgresql/go/models.go b/internal/endtoend/testdata/params_go_keywords/postgresql/go/models.go index e6e549432d..1c6b61b891 100644 --- a/internal/endtoend/testdata/params_go_keywords/postgresql/go/models.go +++ b/internal/endtoend/testdata/params_go_keywords/postgresql/go/models.go @@ -34,4 +34,5 @@ type GoKeyword struct { Import pgtype.Text Return pgtype.Text Var pgtype.Text + Q pgtype.Text } diff --git a/internal/endtoend/testdata/params_go_keywords/postgresql/go/query.sql.go b/internal/endtoend/testdata/params_go_keywords/postgresql/go/query.sql.go index ba0ad88064..8fa0810d65 100644 --- a/internal/endtoend/testdata/params_go_keywords/postgresql/go/query.sql.go +++ b/internal/endtoend/testdata/params_go_keywords/postgresql/go/query.sql.go @@ -173,6 +173,15 @@ func (q *Queries) KeywordPackage(ctx context.Context, package_ string) error { return err } +const keywordQ = `-- name: KeywordQ :exec +SELECT $1::text +` + +func (q *Queries) KeywordQ(ctx context.Context, q_ string) error { + _, err := q.db.Exec(ctx, keywordQ, q_) + return err +} + const keywordRange = `-- name: KeywordRange :exec SELECT $1::text ` @@ -434,6 +443,17 @@ func (q *Queries) SelectPackage(ctx context.Context) (pgtype.Text, error) { return package_, err } +const selectQ = `-- name: SelectQ :one +SELECT "q" FROM go_keywords +` + +func (q *Queries) SelectQ(ctx context.Context) (pgtype.Text, error) { + row := q.db.QueryRow(ctx, selectQ) + var q_ pgtype.Text + err := row.Scan(&q_) + return q_, err +} + const selectRange = `-- name: SelectRange :one SELECT "range" FROM go_keywords ` diff --git a/internal/endtoend/testdata/params_go_keywords/postgresql/query.sql b/internal/endtoend/testdata/params_go_keywords/postgresql/query.sql index 70796e4e3e..bc45b1166f 100644 --- a/internal/endtoend/testdata/params_go_keywords/postgresql/query.sql +++ b/internal/endtoend/testdata/params_go_keywords/postgresql/query.sql @@ -73,6 +73,9 @@ SELECT sqlc.arg('return')::text; -- name: KeywordVar :exec SELECT sqlc.arg('var')::text; +-- name: KeywordQ :exec +SELECT sqlc.arg('q')::text; + -- name: SelectBreak :one SELECT "break" FROM go_keywords; @@ -147,3 +150,6 @@ SELECT "return" FROM go_keywords; -- name: SelectVar :one SELECT "var" FROM go_keywords; + +-- name: SelectQ :one +SELECT "q" FROM go_keywords; diff --git a/internal/endtoend/testdata/params_go_keywords/postgresql/schema.sql b/internal/endtoend/testdata/params_go_keywords/postgresql/schema.sql index 11a04beef6..100be50211 100644 --- a/internal/endtoend/testdata/params_go_keywords/postgresql/schema.sql +++ b/internal/endtoend/testdata/params_go_keywords/postgresql/schema.sql @@ -23,5 +23,6 @@ CREATE TABLE go_keywords ( "for" TEXT, "import" TEXT, "return" TEXT, - "var" TEXT + "var" TEXT, + "q" TEXT ); diff --git a/internal/endtoend/testdata/quoted_tablename/sqlite/go/db.go b/internal/endtoend/testdata/quoted_tablename/sqlite/go/db.go new file mode 100644 index 0000000000..0ea90b328a --- /dev/null +++ b/internal/endtoend/testdata/quoted_tablename/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/quoted_tablename/sqlite/go/models.go b/internal/endtoend/testdata/quoted_tablename/sqlite/go/models.go new file mode 100644 index 0000000000..69573be607 --- /dev/null +++ b/internal/endtoend/testdata/quoted_tablename/sqlite/go/models.go @@ -0,0 +1,9 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package querytest + +type User struct { + ID string +} diff --git a/internal/endtoend/testdata/quoted_tablename/sqlite/go/query.sql.go b/internal/endtoend/testdata/quoted_tablename/sqlite/go/query.sql.go new file mode 100644 index 0000000000..5cb25a79e3 --- /dev/null +++ b/internal/endtoend/testdata/quoted_tablename/sqlite/go/query.sql.go @@ -0,0 +1,37 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const testList = `-- name: TestList :many +SELECT id FROM users +` + +func (q *Queries) TestList(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, testList) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/quoted_tablename/sqlite/query.sql b/internal/endtoend/testdata/quoted_tablename/sqlite/query.sql new file mode 100644 index 0000000000..0d3b8e238b --- /dev/null +++ b/internal/endtoend/testdata/quoted_tablename/sqlite/query.sql @@ -0,0 +1,2 @@ +-- name: TestList :many +SELECT * FROM users; diff --git a/internal/endtoend/testdata/quoted_tablename/sqlite/schema.sql b/internal/endtoend/testdata/quoted_tablename/sqlite/schema.sql new file mode 100644 index 0000000000..078fa24bf6 --- /dev/null +++ b/internal/endtoend/testdata/quoted_tablename/sqlite/schema.sql @@ -0,0 +1,6 @@ +-- Example queries for sqlc +CREATE TABLE "users" +( + id TEXT NOT NULL +); + diff --git a/internal/endtoend/testdata/quoted_tablename/sqlite/sqlc.json b/internal/endtoend/testdata/quoted_tablename/sqlite/sqlc.json new file mode 100644 index 0000000000..ea4a23425f --- /dev/null +++ b/internal/endtoend/testdata/quoted_tablename/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "sqlite", + "schema": "schema.sql", + "queries": "query.sql", + "name": "querytest" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/vet_disable/query.sql b/internal/endtoend/testdata/vet_disable/query.sql index c1eb8d2219..50052de26b 100644 --- a/internal/endtoend/testdata/vet_disable/query.sql +++ b/internal/endtoend/testdata/vet_disable/query.sql @@ -1,6 +1,29 @@ --- name: SkipVet :exec +-- name: RunVetAll :exec +SELECT true; + +-- name: SkipVetAll :exec -- @sqlc-vet-disable SELECT true; --- name: RunVet :exec +-- name: SkipVetSingleLine :exec +-- @sqlc-vet-disable always-fail no-exec +SELECT true; + +-- name: SkipVetMultiLine :exec +-- @sqlc-vet-disable always-fail +-- @sqlc-vet-disable no-exec +SELECT true; + +-- name: SkipVet_always_fail :exec +-- @sqlc-vet-disable always-fail +SELECT true; + +-- name: SkipVet_no_exec :exec +-- @sqlc-vet-disable no-exec +SELECT true; + +-- name: SkipVetInvalidRule :exec +-- @sqlc-vet-disable always-fail +-- @sqlc-vet-disable block-delete +-- @sqlc-vet-disable no-exec SELECT true; \ No newline at end of file diff --git a/internal/endtoend/testdata/vet_disable/sqlc.yaml b/internal/endtoend/testdata/vet_disable/sqlc.yaml index ff7c1b5200..1e53bd7f2f 100644 --- a/internal/endtoend/testdata/vet_disable/sqlc.yaml +++ b/internal/endtoend/testdata/vet_disable/sqlc.yaml @@ -9,7 +9,13 @@ sql: out: "db" rules: - always-fail + - no-exec rules: - name: always-fail message: "Fail" rule: "true" + + - name: no-exec + message: "don't use exec" + rule: | + query.cmd == "exec" diff --git a/internal/endtoend/testdata/vet_disable/stderr.txt b/internal/endtoend/testdata/vet_disable/stderr.txt index 9acbb8b34b..3d9a6169f8 100644 --- a/internal/endtoend/testdata/vet_disable/stderr.txt +++ b/internal/endtoend/testdata/vet_disable/stderr.txt @@ -1 +1,5 @@ -query.sql: RunVet: always-fail: Fail +query.sql: RunVetAll: always-fail: Fail +query.sql: RunVetAll: no-exec: don't use exec +query.sql: SkipVet_always_fail: no-exec: don't use exec +query.sql: SkipVet_no_exec: always-fail: Fail +query.sql: SkipVetInvalidRule: rule-check error: rule "block-delete" does not exist in the config file \ No newline at end of file diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 02d80bc48c..29c06fb285 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -203,7 +203,7 @@ type Delete_stmt interface { func (c *cc) convertDelete_stmtContext(n Delete_stmt) ast.Node { if qualifiedName, ok := n.Qualified_table_name().(*parser.Qualified_table_nameContext); ok { - tableName := qualifiedName.Table_name().GetText() + tableName := identifier(qualifiedName.Table_name().GetText()) relation := &ast.RangeVar{ Relname: &tableName, } @@ -854,7 +854,7 @@ func (c *cc) convertReturning_caluseContext(n parser.IReturning_clauseContext) * } func (c *cc) convertInsert_stmtContext(n *parser.Insert_stmtContext) ast.Node { - tableName := n.Table_name().GetText() + tableName := identifier(n.Table_name().GetText()) rel := &ast.RangeVar{ Relname: &tableName, } @@ -936,7 +936,7 @@ func (c *cc) convertTablesOrSubquery(n []parser.ITable_or_subqueryContext) []ast } if from.Table_name() != nil { - rel := from.Table_name().GetText() + rel := identifier(from.Table_name().GetText()) rv := &ast.RangeVar{ Relname: &rel, Location: from.GetStart().GetStart(), diff --git a/internal/engine/sqlite/utils.go b/internal/engine/sqlite/utils.go index f3b5ee289d..874d53ab41 100644 --- a/internal/engine/sqlite/utils.go +++ b/internal/engine/sqlite/utils.go @@ -12,7 +12,7 @@ type tableNamer interface { func parseTableName(c tableNamer) *ast.TableName { name := ast.TableName{ - Name: c.Table_name().GetText(), + Name: identifier(c.Table_name().GetText()), } if c.Schema_name() != nil { name.Schema = c.Schema_name().GetText() diff --git a/internal/inflection/singular.go b/internal/inflection/singular.go index edac927603..64e042a6ab 100644 --- a/internal/inflection/singular.go +++ b/internal/inflection/singular.go @@ -43,5 +43,10 @@ func Singular(s SingularParams) string { if strings.ToLower(s.Name) == "waves" { return "wave" } + + if strings.ToLower(s.Name) == "metadata" { + return "metadata" + } + return upstream.Singular(s.Name) } diff --git a/internal/metadata/meta.go b/internal/metadata/meta.go index 97ff36dbd2..8f63624d2c 100644 --- a/internal/metadata/meta.go +++ b/internal/metadata/meta.go @@ -3,6 +3,7 @@ package metadata import ( "bufio" "fmt" + "github.com/sqlc-dev/sqlc/internal/constants" "strings" "unicode" @@ -18,6 +19,10 @@ type Metadata struct { Params map[string]string Flags map[string]bool + // RuleSkiplist contains the names of rules to disable vetting for. + // If the map is empty, but the disable vet flag is specified, then all rules are ignored. + RuleSkiplist map[string]struct{} + Filename string } @@ -113,9 +118,12 @@ func ParseQueryNameAndType(t string, commentStyle CommentSyntax) (string, string return "", "", nil } -func ParseParamsAndFlags(comments []string) (map[string]string, map[string]bool, error) { +// ParseCommentFlags processes the comments provided with queries to determine the metadata params, flags and rules to skip. +// All flags in query comments are prefixed with `@`, e.g. @param, @@sqlc-vet-disable. +func ParseCommentFlags(comments []string) (map[string]string, map[string]bool, map[string]struct{}, error) { params := make(map[string]string) flags := make(map[string]bool) + ruleSkiplist := make(map[string]struct{}) for _, line := range comments { s := bufio.NewScanner(strings.NewReader(line)) @@ -129,7 +137,7 @@ func ParseParamsAndFlags(comments []string) (map[string]string, map[string]bool, } switch token { - case "@param": + case constants.QueryFlagParam: s.Scan() name := s.Text() var rest []string @@ -138,14 +146,27 @@ func ParseParamsAndFlags(comments []string) (map[string]string, map[string]bool, rest = append(rest, paramToken) } params[name] = strings.Join(rest, " ") + + case constants.QueryFlagSqlcVetDisable: + flags[token] = true + + // Vet rules can all be disabled in the same line or split across lines .i.e. + // /* @sqlc-vet-disable sqlc/db-prepare delete-without-where */ + // is equivalent to: + // /* @sqlc-vet-disable sqlc/db-prepare */ + // /* @sqlc-vet-disable delete-without-where */ + for s.Scan() { + ruleSkiplist[s.Text()] = struct{}{} + } + default: flags[token] = true } if s.Err() != nil { - return params, flags, s.Err() + return params, flags, ruleSkiplist, s.Err() } } - return params, flags, nil + return params, flags, ruleSkiplist, nil } diff --git a/internal/metadata/meta_test.go b/internal/metadata/meta_test.go index 3c2be6d6de..e9ef54586e 100644 --- a/internal/metadata/meta_test.go +++ b/internal/metadata/meta_test.go @@ -1,6 +1,8 @@ package metadata -import "testing" +import ( + "testing" +) func TestParseQueryNameAndType(t *testing.T) { @@ -77,7 +79,7 @@ func TestParseQueryParams(t *testing.T) { " @param @invalid UUID ", }, } { - params, _, err := ParseParamsAndFlags(comments) + params, _, _, err := ParseCommentFlags(comments) if err != nil { t.Errorf("expected comments to parse, got err: %s", err) } @@ -123,7 +125,7 @@ func TestParseQueryFlags(t *testing.T) { " @param @flag-bar UUID", }, } { - _, flags, err := ParseParamsAndFlags(comments) + _, flags, _, err := ParseCommentFlags(comments) if err != nil { t.Errorf("expected comments to parse, got err: %s", err) } @@ -137,3 +139,44 @@ func TestParseQueryFlags(t *testing.T) { } } } + +func TestParseQueryRuleSkiplist(t *testing.T) { + for _, comments := range [][]string{ + { + " name: CreateFoo :one", + " @sqlc-vet-disable sqlc/db-prepare delete-without-where ", + }, + { + " name: CreateFoo :one ", + " @sqlc-vet-disable sqlc/db-prepare ", + " @sqlc-vet-disable delete-without-where ", + }, + { + " name: CreateFoo :one", + " @sqlc-vet-disable sqlc/db-prepare ", + " update-without where", + " @sqlc-vet-disable delete-without-where ", + }, + } { + _, flags, ruleSkiplist, err := ParseCommentFlags(comments) + if err != nil { + t.Errorf("expected comments to parse, got err: %s", err) + } + + if !flags["@sqlc-vet-disable"] { + t.Errorf("expected @sqlc-vet-disable flag not found") + } + + if _, ok := ruleSkiplist["sqlc/db-prepare"]; !ok { + t.Errorf("expected rule not found in skiplist") + } + + if _, ok := ruleSkiplist["delete-without-where"]; !ok { + t.Errorf("expected rule not found in skiplist") + } + + if _, ok := ruleSkiplist["update-without-where"]; ok { + t.Errorf("unexpected rule found in skiplist") + } + } +}