Title: Type annotations for query parameters · Issue #2800 · sqlc-dev/sqlc · GitHub
Open Graph Title: Type annotations for query parameters · Issue #2800 · sqlc-dev/sqlc
X Title: Type annotations for query parameters · Issue #2800 · sqlc-dev/sqlc
Description: I propose adding type annotations for query parameters. Users will no longer need to cast parameters to the desired type. This proposal builds on Andrew's query annotation work for vet. Unified syntax We added the @sqlc-vet-disable annot...
Open Graph Description: I propose adding type annotations for query parameters. Users will no longer need to cast parameters to the desired type. This proposal builds on Andrew's query annotation work for vet. Unified syn...
X Description: I propose adding type annotations for query parameters. Users will no longer need to cast parameters to the desired type. This proposal builds on Andrew's query annotation work for vet. Unified...
Opengraph URL: https://github.com/sqlc-dev/sqlc/issues/2800
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Type annotations for query parameters","articleBody":"I propose adding type annotations for query parameters. Users will no longer need to cast parameters to the desired type. This proposal builds on Andrew's query annotation\r\nwork for `vet`.\r\n\r\n## Unified syntax\r\n\r\nWe added the `@sqlc-vet-disable` annotation to disable vet rules on a per-query\r\nbasis. We can extend this syntax to support other per-query configuration\r\noptions.\r\n\r\nThe current syntax for query name and command are different, so we'll\r\nstandardize on the `@` prefix. This will be the new, preferred syntax for name\r\nand command.\r\n\r\n```sql\r\n-- name: GetAuthor :one\r\nSELECT * FROM authors\r\nWHERE id = $1 LIMIT 1;\r\n\r\n-- becomes\r\n\r\n-- @name GetAuthor :one\r\nSELECT * FROM authors\r\nWHERE id = $1 LIMIT 1;\r\n```\r\n\r\nThe existing syntax will continue to work, but it will be an error to use both\r\nannotations on a single query.\r\n\r\n```sql\r\n-- INVALID!\r\n-- name: GetAuthor :one\r\n-- @name GetAuthor :one\r\nSELECT * FROM authors\r\nWHERE id = $1 LIMIT 1;\r\n```\r\n\r\n## Query command\r\n\r\nToday, queries must have a name and a command. With the new syntax, the\r\ncommand option will default to `exec`.\r\n\r\n```sql\r\n-- These two annotations are the same\r\n\r\n-- @name DeleteAuthor :exec\r\nDELETE FROM authors\r\nWHERE id = $1;\r\n\r\n-- @name DeleteAuthor\r\nDELETE FROM authors\r\nWHERE id = $1;\r\n```\r\n\r\n### Validation\r\n\r\nsqlc will delegate command validation to codegen plugins, allowing plugins to implement new commands without having\r\nto merge anything into sqlc.\r\n\r\nIf you want to still validate those, you can simulate the current behavior by\r\nusing this vet rule.\r\n\r\n```yaml\r\nrules:\r\n - name: validate-command\r\n rule: |\r\n !(query.cmd in [\"exec\", \"one\", \"many\", \"execrows\", \"execlastid\", \"execresult\", \"batchone\", \"batchmany\", \"batchexec\"])\r\n```\r\n\r\n### Type annotations for query parameters\r\n\r\nThe `@param` annotation supports passing type information without having to use\r\na cast. \r\n\r\n```sql\r\n-- @param name type\r\n```\r\n\r\nCasts are required today when sqlc infers an incorrect type, but these\r\ncasts are passed down to the engine itself, possibly hurting performance. \r\nFor example, this cast is required to get sqlc working correctly, but isn't needed at runtime.\r\n\r\n```sql\r\n-- @name ListAuthorsByIDs :many\r\nSELECT * FROM authors\r\nWHERE id = ANY(@ids::bigint[]);\r\n```\r\nHere's what it looks like with the new syntax. The type annotation is\r\nengine-specific and is the same that you'd pass to CAST or CREATE TABLE.\r\n\r\n```sql\r\n-- @name ListAuthorsByIDs :many\r\n-- @param ids BIGINT[]\r\nSELECT * FROM authors\r\nWHERE id = ANY(@ids);\r\n```\r\n\r\nIf a parameter has a type annotation, that will be used instead of inferring the\r\ntype from the query.\r\n\r\n### NULL values\r\n\r\nsqlc will infer the nullability of parameters by default. You can force a parameter to be nullable using the `?` operator, or not null using the `!` operator.\r\n\r\n```sql\r\n-- @name CreateAuthor :one\r\n-- @param name! TEXT -- force NOT NULL\r\n-- @param bio? TEXT -- force NULL\r\nINSERT INTO authors (\r\n name, bio\r\n) VALUES (\r\n @name, @bio\r\n)\r\nRETURNING *;\r\n```\r\n\r\n### Positional parameters\r\n\r\nIf your parameters do not have a given name, you can refer to them by number to add a type annotation and nullability.\r\n\r\nFor PostgreSQL:\r\n\r\n```sql\r\n-- @name CreateAuthor :one\r\n-- @param 1? TEXT\r\n-- @param 2? TEXT\r\nINSERT INTO authors (\r\n name, bio\r\n) VALUES (\r\n $1, $2\r\n)\r\nRETURNING *;\r\n```\r\n\r\nAnd for MySQL or SQLite:\r\n\r\n```sql\r\n-- @name CreateAuthor :one\r\n-- @param 1? TEXT\r\n-- @param 2? TEXT\r\nINSERT INTO authors (\r\n name, bio\r\n) VALUES (\r\n ?1, ?2\r\n);\r\n```\r\n\r\n## sqlc.arg / sqlc.narg\r\n\r\nUsing the proposed annotation syntax allows you to replace `sqlc.arg()` and `sqlc.narg()`.\r\n\r\nFor example this query with `sqlc.arg()` and `sqlc.narg()`\r\n\r\n```sql\r\n-- name: UpdateAuthor :one\r\nUPDATE author\r\nSET\r\n name = coalesce(sqlc.narg('name')::text, name),\r\n bio = coalesce(sqlc.narg('bio')::text, bio)\r\nWHERE id = sqlc.arg('id')::bigint\r\nRETURNING *;\r\n```\r\n\r\nis equivalent to this one without\r\n\r\n```sql\r\n-- name: UpdateAuthor :one\r\n-- @param name? TEXT\r\n-- @param bio? TEXT\r\n-- @param id! BIGINT\r\nUPDATE author\r\nSET\r\n name = coalesce(@name, name),\r\n bio = coalesce(@bio, bio)\r\nWHERE id = @id\r\nRETURNING *;\r\n```\r\n\r\n`sqlc.arg` and `sqlc.narg` will continue to work, but will likely be deprecated in favor of the `@foo` syntax.\r\nYou can use `sqlc.arg()` with the new `@param` annotation syntax (to avoid explicit casts), but not `sqlc.narg()`. This constraint\r\nis intended to eliminate confusion about precedence of nullability directives.\r\n\r\nSo for example this will work\r\n\r\n```sql\r\n-- name: UpdateAuthor :one\r\n-- @param id! BIGINT\r\nUPDATE author\r\nSET\r\n name = coalesce(sqlc.narg('name')::text, name),\r\n bio = coalesce(sqlc.narg('bio')::text, bio)\r\nWHERE id = sqlc.arg('id')\r\nRETURNING *;\r\n```\r\n\r\nbut this won't\r\n\r\n```sql\r\n-- name: UpdateAuthor :one\r\n-- @param name TEXT\r\n-- @param bio TEXT\r\nUPDATE author\r\nSET\r\n name = coalesce(sqlc.narg('name'), name),\r\n bio = coalesce(sqlc.narg('bio'), bio)\r\nWHERE id = sqlc.arg('id')::bigint\r\nRETURNING *;\r\n```\r\n\r\nTo make this work, switch `sqlc.narg` to `sqlc.arg` and add a `?` to the param annotation.\r\n\r\n```sql\r\n-- name: UpdateAuthor :one\r\n-- @param name? TEXT\r\n-- @param bio? TEXT\r\nUPDATE author\r\nSET\r\n name = coalesce(sqlc.arg('name'), name),\r\n bio = coalesce(sqlc.arg('bio'), bio)\r\nWHERE id = sqlc.arg('id')::bigint\r\nRETURNING *;\r\n```\r\n\r\n## Why comments?\r\n\r\nWe're using comments instead of `sqlc.*` functions to avoid engine-specific parsing issues. For example, we've run into issues with the MySQL parser not support functions in certain parameter locations.\r\n\r\n## Full example\r\n\r\nThis is the normal example from the playground using the new syntax.\r\n\r\n```sql\r\n-- @name GetAuthor :one\r\n-- @param id! BIGINT\r\nSELECT * FROM authors\r\nWHERE id = @id LIMIT 1;\r\n\r\n-- @name ListAuthors :many\r\nSELECT * FROM authors\r\nORDER BY name;\r\n\r\n-- @name CreateAuthor :one\r\n-- @param name! TEXT\r\n-- @param bio TEXT\r\nINSERT INTO authors (\r\n name, bio\r\n) VALUES (\r\n @name, @bio\r\n)\r\nRETURNING *;\r\n\r\n-- @name GetAuthor\r\n-- @param id! BIGINT\r\nDELETE FROM authors\r\nWHERE id = @id;\r\n```\r\n\r\n## Future\r\nThe plan is to use similar annotations to support type annotations for output columns and values, Go type overrides for parameters and outputs, and JSON unmarshal / marshal hints.","author":{"url":"https://github.com/kyleconroy","@type":"Person","name":"kyleconroy"},"datePublished":"2023-10-03T15:26:45.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":11},"url":"https://github.com/2800/sqlc/issues/2800"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:8bfada1b-4b07-28be-e4e0-d335e52c7abe |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 96CA:B97C3:A98541C:EDCA445:6A50ED5D |
| html-safe-nonce | 464a281ce94833321a490360c92c3f99a550e041c94162de9eb9d3366213eb70 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5NkNBOkI5N0MzOkE5ODU0MUM6RURDQTQ0NTo2QTUwRUQ1RCIsInZpc2l0b3JfaWQiOiIxNDM2MjUxODIzMDczNzE3NTk3IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 289d49d982b861acad559a2544c456de8791bd8cf1d8635cd21a28bd7c4ad78a |
| hovercard-subject-tag | issue:1924394815 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/sqlc-dev/sqlc/2800/issue_layout |
| twitter:image | https://opengraph.githubassets.com/c5b4c5f5792e2f2313c098f1803bce947d1fed0cc2c12f1e03f141d04d93ad01/sqlc-dev/sqlc/issues/2800 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/c5b4c5f5792e2f2313c098f1803bce947d1fed0cc2c12f1e03f141d04d93ad01/sqlc-dev/sqlc/issues/2800 |
| og:image:alt | I propose adding type annotations for query parameters. Users will no longer need to cast parameters to the desired type. This proposal builds on Andrew's query annotation work for vet. Unified syn... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | kyleconroy |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5266e58c17a510c403505cc811606465e90a881d2007ee7df1c4800d5c659838 |
| turbo-cache-control | no-preview |
| go-import | github.com/sqlc-dev/sqlc git https://github.com/sqlc-dev/sqlc.git |
| octolytics-dimension-user_id | 136738596 |
| octolytics-dimension-user_login | sqlc-dev |
| octolytics-dimension-repository_id | 193160679 |
| octolytics-dimension-repository_nwo | sqlc-dev/sqlc |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 193160679 |
| octolytics-dimension-repository_network_root_nwo | sqlc-dev/sqlc |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 5ec60191a48933536a90c8a19f47142fcd0d4739 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width