diff --git a/examples/src/authors/models.py b/examples/src/authors/models.py index 96553a5..b3b9554 100644 --- a/examples/src/authors/models.py +++ b/examples/src/authors/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses from typing import Optional diff --git a/examples/src/authors/query.py b/examples/src/authors/query.py index 019f877..b932e35 100644 --- a/examples/src/authors/query.py +++ b/examples/src/authors/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql from typing import AsyncIterator, Iterator, Optional diff --git a/examples/src/booktest/models.py b/examples/src/booktest/models.py index d7ee131..dcfbc20 100644 --- a/examples/src/booktest/models.py +++ b/examples/src/booktest/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses import datetime import enum diff --git a/examples/src/booktest/query.py b/examples/src/booktest/query.py index bc71f22..12d3717 100644 --- a/examples/src/booktest/query.py +++ b/examples/src/booktest/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql import dataclasses import datetime diff --git a/examples/src/jets/models.py b/examples/src/jets/models.py index 0d4eb5d..fc5464b 100644 --- a/examples/src/jets/models.py +++ b/examples/src/jets/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses diff --git a/examples/src/jets/query-building.py b/examples/src/jets/query-building.py index 7651116..adcdcdb 100644 --- a/examples/src/jets/query-building.py +++ b/examples/src/jets/query-building.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query-building.sql from typing import AsyncIterator, Optional diff --git a/examples/src/ondeck/city.py b/examples/src/ondeck/city.py index 5af93e9..2f2da93 100644 --- a/examples/src/ondeck/city.py +++ b/examples/src/ondeck/city.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: city.sql from typing import AsyncIterator, Optional diff --git a/examples/src/ondeck/models.py b/examples/src/ondeck/models.py index 1161408..a32fea2 100644 --- a/examples/src/ondeck/models.py +++ b/examples/src/ondeck/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses import datetime import enum diff --git a/examples/src/ondeck/venue.py b/examples/src/ondeck/venue.py index 6159bf6..1911cb3 100644 --- a/examples/src/ondeck/venue.py +++ b/examples/src/ondeck/venue.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: venue.sql import dataclasses from typing import AsyncIterator, List, Optional diff --git a/internal/endtoend/endtoend_test.go b/internal/endtoend/endtoend_test.go index bd66c27..09544c1 100644 --- a/internal/endtoend/endtoend_test.go +++ b/internal/endtoend/endtoend_test.go @@ -101,7 +101,7 @@ func TestGenerate(t *testing.T) { cmd.Dir = dir got, err := cmd.CombinedOutput() if diff := cmp.Diff(string(want), string(got)); diff != "" { - t.Errorf("sqlc diff mismatch (-want +got):\n%s", diff) + t.Errorf("sqlc diff mismatch (-want +got):\n%s", got) } if len(want) == 0 && err != nil { t.Error(err) diff --git a/internal/endtoend/testdata/dataclasses_with_reserved_keywords/db/__init__.py b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/internal/endtoend/testdata/dataclasses_with_reserved_keywords/db/models.py b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/db/models.py new file mode 100644 index 0000000..f2dd802 --- /dev/null +++ b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/db/models.py @@ -0,0 +1,10 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.29.0 +import dataclasses + + +@dataclasses.dataclass() +class Author: + id: int + class_: str diff --git a/internal/endtoend/testdata/dataclasses_with_reserved_keywords/db/query.py b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/db/query.py new file mode 100644 index 0000000..1a0ac38 --- /dev/null +++ b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/db/query.py @@ -0,0 +1,44 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.29.0 +# source: query.sql +from typing import Optional + +import sqlalchemy +import sqlalchemy.ext.asyncio + +from db import models + + +GET_AUTHOR = """-- name: get_author \\:one +SELECT id, class FROM authors +WHERE id = :p1 LIMIT 1 +""" + + +class Querier: + def __init__(self, conn: sqlalchemy.engine.Connection): + self._conn = conn + + def get_author(self, *, id: int) -> Optional[models.Author]: + row = self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id}).first() + if row is None: + return None + return models.Author( + id=row[0], + class_=row[1], + ) + + +class AsyncQuerier: + def __init__(self, conn: sqlalchemy.ext.asyncio.AsyncConnection): + self._conn = conn + + async def get_author(self, *, id: int) -> Optional[models.Author]: + row = (await self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id})).first() + if row is None: + return None + return models.Author( + id=row[0], + class_=row[1], + ) diff --git a/internal/endtoend/testdata/dataclasses_with_reserved_keywords/query.sql b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/query.sql new file mode 100644 index 0000000..6c0b1c8 --- /dev/null +++ b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/query.sql @@ -0,0 +1,3 @@ +-- name: GetAuthor :one +SELECT * FROM authors +WHERE id = $1 LIMIT 1; diff --git a/internal/endtoend/testdata/dataclasses_with_reserved_keywords/schema.sql b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/schema.sql new file mode 100644 index 0000000..0d611d3 --- /dev/null +++ b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE authors ( + id BIGSERIAL PRIMARY KEY, + class text NOT NULL +); diff --git a/internal/endtoend/testdata/dataclasses_with_reserved_keywords/sqlc.yaml b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/sqlc.yaml new file mode 100644 index 0000000..a7cfc22 --- /dev/null +++ b/internal/endtoend/testdata/dataclasses_with_reserved_keywords/sqlc.yaml @@ -0,0 +1,18 @@ +version: "2" +plugins: + - name: py + wasm: + url: file://../../../../bin/sqlc-gen-python.wasm + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" +sql: + - schema: schema.sql + queries: query.sql + engine: postgresql + codegen: + - plugin: py + out: db + options: + package: db + emit_sync_querier: true + emit_async_querier: true + emit_pydantic_models: false diff --git a/internal/endtoend/testdata/emit_pydantic_models/db/models.py b/internal/endtoend/testdata/emit_pydantic_models/db/models.py index 7676e5c..5c6947e 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/db/models.py +++ b/internal/endtoend/testdata/emit_pydantic_models/db/models.py @@ -1,11 +1,15 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import pydantic from typing import Optional class Author(pydantic.BaseModel): + model_config = pydantic.ConfigDict( + validate_by_alias=True, + validate_by_name=True, + ) id: int name: str bio: Optional[str] diff --git a/internal/endtoend/testdata/emit_pydantic_models/db/query.py b/internal/endtoend/testdata/emit_pydantic_models/db/query.py index 6f5b76f..cc36118 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/db/query.py +++ b/internal/endtoend/testdata/emit_pydantic_models/db/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql from typing import AsyncIterator, Iterator, Optional diff --git a/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml b/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml index beae200..bc03370 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml +++ b/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/db/__init__.py b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/db/models.py b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/db/models.py new file mode 100644 index 0000000..1403269 --- /dev/null +++ b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/db/models.py @@ -0,0 +1,15 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.29.0 +import pydantic + + +class Author(pydantic.BaseModel): + model_config = pydantic.ConfigDict( + validate_by_alias=True, + validate_by_name=True, + ) + id: int + class_: str = pydantic.Field( + alias="class", + ) diff --git a/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/db/query.py b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/db/query.py new file mode 100644 index 0000000..1a0ac38 --- /dev/null +++ b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/db/query.py @@ -0,0 +1,44 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.29.0 +# source: query.sql +from typing import Optional + +import sqlalchemy +import sqlalchemy.ext.asyncio + +from db import models + + +GET_AUTHOR = """-- name: get_author \\:one +SELECT id, class FROM authors +WHERE id = :p1 LIMIT 1 +""" + + +class Querier: + def __init__(self, conn: sqlalchemy.engine.Connection): + self._conn = conn + + def get_author(self, *, id: int) -> Optional[models.Author]: + row = self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id}).first() + if row is None: + return None + return models.Author( + id=row[0], + class_=row[1], + ) + + +class AsyncQuerier: + def __init__(self, conn: sqlalchemy.ext.asyncio.AsyncConnection): + self._conn = conn + + async def get_author(self, *, id: int) -> Optional[models.Author]: + row = (await self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id})).first() + if row is None: + return None + return models.Author( + id=row[0], + class_=row[1], + ) diff --git a/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/query.sql b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/query.sql new file mode 100644 index 0000000..6c0b1c8 --- /dev/null +++ b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/query.sql @@ -0,0 +1,3 @@ +-- name: GetAuthor :one +SELECT * FROM authors +WHERE id = $1 LIMIT 1; diff --git a/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/schema.sql b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/schema.sql new file mode 100644 index 0000000..0d611d3 --- /dev/null +++ b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE authors ( + id BIGSERIAL PRIMARY KEY, + class text NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/sqlc.yaml b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/sqlc.yaml new file mode 100644 index 0000000..bc03370 --- /dev/null +++ b/internal/endtoend/testdata/emit_pydantic_models_with_reserved_keywords/sqlc.yaml @@ -0,0 +1,18 @@ +version: '2' +plugins: +- name: py + wasm: + url: file://../../../../bin/sqlc-gen-python.wasm + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" +sql: +- schema: schema.sql + queries: query.sql + engine: postgresql + codegen: + - plugin: py + out: db + options: + package: db + emit_sync_querier: true + emit_async_querier: true + emit_pydantic_models: true \ No newline at end of file diff --git a/internal/endtoend/testdata/emit_str_enum/db/models.py b/internal/endtoend/testdata/emit_str_enum/db/models.py index 5fdf754..aa43ab1 100644 --- a/internal/endtoend/testdata/emit_str_enum/db/models.py +++ b/internal/endtoend/testdata/emit_str_enum/db/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses import enum from typing import Optional diff --git a/internal/endtoend/testdata/emit_str_enum/db/query.py b/internal/endtoend/testdata/emit_str_enum/db/query.py index 8082889..5ea0264 100644 --- a/internal/endtoend/testdata/emit_str_enum/db/query.py +++ b/internal/endtoend/testdata/emit_str_enum/db/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql from typing import AsyncIterator, Iterator, Optional diff --git a/internal/endtoend/testdata/emit_str_enum/sqlc.yaml b/internal/endtoend/testdata/emit_str_enum/sqlc.yaml index 04e3feb..1a80fbc 100644 --- a/internal/endtoend/testdata/emit_str_enum/sqlc.yaml +++ b/internal/endtoend/testdata/emit_str_enum/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/exec_result/python/models.py b/internal/endtoend/testdata/exec_result/python/models.py index 034fb2d..6d3e9f5 100644 --- a/internal/endtoend/testdata/exec_result/python/models.py +++ b/internal/endtoend/testdata/exec_result/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses diff --git a/internal/endtoend/testdata/exec_result/python/query.py b/internal/endtoend/testdata/exec_result/python/query.py index b68ce39..c9c6e21 100644 --- a/internal/endtoend/testdata/exec_result/python/query.py +++ b/internal/endtoend/testdata/exec_result/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/exec_result/sqlc.yaml b/internal/endtoend/testdata/exec_result/sqlc.yaml index ddffc83..9c6a7d3 100644 --- a/internal/endtoend/testdata/exec_result/sqlc.yaml +++ b/internal/endtoend/testdata/exec_result/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/exec_rows/python/models.py b/internal/endtoend/testdata/exec_rows/python/models.py index 034fb2d..6d3e9f5 100644 --- a/internal/endtoend/testdata/exec_rows/python/models.py +++ b/internal/endtoend/testdata/exec_rows/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses diff --git a/internal/endtoend/testdata/exec_rows/python/query.py b/internal/endtoend/testdata/exec_rows/python/query.py index 7a9b2a6..a678f3d 100644 --- a/internal/endtoend/testdata/exec_rows/python/query.py +++ b/internal/endtoend/testdata/exec_rows/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/exec_rows/sqlc.yaml b/internal/endtoend/testdata/exec_rows/sqlc.yaml index ddffc83..9c6a7d3 100644 --- a/internal/endtoend/testdata/exec_rows/sqlc.yaml +++ b/internal/endtoend/testdata/exec_rows/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py b/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py index 8ba8803..fc76620 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py +++ b/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py b/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py index 1e1e161..1fc92fd 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py +++ b/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql from typing import Optional diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml b/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml index efbb150..369271f 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml +++ b/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_two/python/models.py b/internal/endtoend/testdata/query_parameter_limit_two/python/models.py index 059675d..89c0f8d 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_two/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_two/python/query.py b/internal/endtoend/testdata/query_parameter_limit_two/python/query.py index e8b723e..0d9bd97 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_two/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml index 336bca7..6d7d162 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py b/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py index 30e80db..dc09dab 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py b/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py index 5a1fbbc..49b7bd1 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml index c20cd57..4ba6c57 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py b/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py index 059675d..89c0f8d 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py b/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py index 47bd6a9..38e0efb 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.28.0 +# sqlc v1.29.0 # source: query.sql import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml index 6e2cdeb..4c1abd2 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml b/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml index c432e4f..270c290 100644 --- a/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" + sha256: "24b0da217e85c9b952a4c746476aa761e9b293a4a68bef8409d97edc1c003016" sql: - schema: schema.sql queries: query.sql diff --git a/internal/gen.go b/internal/gen.go index 6e50fae..d339650 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -107,7 +107,7 @@ func (v QueryValue) RowNode(rowVar string) *pyast.Node { } for i, f := range v.Struct.Fields { call.Keywords = append(call.Keywords, &pyast.Keyword{ - Arg: f.Name, + Arg: poet.FieldName(f.Name), Value: subscriptNode( rowVar, constantInt(i), @@ -598,14 +598,110 @@ func pydanticNode(name string) *pyast.ClassDef { }, }, }, + Body: poet.Nodes( + &pyast.Assign{ + Targets: []*pyast.Node{ + { + Node: &pyast.Node_Name{ + Name: &pyast.Name{Id: "model_config"}, + }, + }, + }, + Value: poet.Node( + &pyast.Call{ + Func: &pyast.Node{ + Node: &pyast.Node_Attribute{ + Attribute: &pyast.Attribute{ + Value: &pyast.Node{ + Node: &pyast.Node_Name{ + Name: &pyast.Name{ + Id: "pydantic", + }, + }, + }, + Attr: "ConfigDict", + }, + }, + }, + Keywords: []*pyast.Keyword{ + { + Arg: "validate_by_alias", + Value: poet.Name("True"), + }, + { + Arg: "validate_by_name", + Value: poet.Name("True"), + }, + }, + }, + ), + }, + ), } } -func fieldNode(f Field) *pyast.Node { +func fieldNode(f Field, emitPydanticModels bool) *pyast.Node { + if !poet.IsReserved(f.Name) { + return &pyast.Node{ + Node: &pyast.Node_AnnAssign{ + AnnAssign: &pyast.AnnAssign{ + Target: &pyast.Name{Id: f.Name}, + Annotation: f.Type.Annotation(), + Comment: f.Comment, + }, + }, + } + } + + // At this point the field name is a reserved python keyword, so we need to + // update the field name to be a valid python identifier. + if emitPydanticModels { + // On Pydantic add `_` at the end of the field name to make it valid + // Also add a `= pydantic.Field(alias=...)` to the field to allow clean serde + return &pyast.Node{ + Node: &pyast.Node_Assign{ + Assign: &pyast.Assign{ + Targets: poet.Nodes( + &pyast.AnnAssign{ + Target: &pyast.Name{Id: poet.FieldName(f.Name)}, + Annotation: f.Type.Annotation(), + Comment: f.Comment, + }, + ), + Value: poet.Node( + &pyast.Call{ + Func: &pyast.Node{ + Node: &pyast.Node_Attribute{ + Attribute: &pyast.Attribute{ + Value: &pyast.Node{ + Node: &pyast.Node_Name{ + Name: &pyast.Name{ + Id: "pydantic", + }, + }, + }, + Attr: "Field", + }, + }, + }, + Keywords: []*pyast.Keyword{ + { + Arg: "alias", + Value: poet.Constant(f.Name), + }, + }, + }, + ), + }, + }, + } + } + + // On dataclasses add `_` at the end of the field name to make it valid return &pyast.Node{ Node: &pyast.Node_AnnAssign{ AnnAssign: &pyast.AnnAssign{ - Target: &pyast.Name{Id: f.Name}, + Target: &pyast.Name{Id: poet.FieldName(f.Name)}, Annotation: f.Type.Annotation(), Comment: f.Comment, }, @@ -731,7 +827,7 @@ func buildModelsTree(ctx *pyTmplCtx, i *importer) *pyast.Node { }) } for _, f := range m.Fields { - def.Body = append(def.Body, fieldNode(f)) + def.Body = append(def.Body, fieldNode(f, ctx.C.EmitPydanticModels)) } mod.Body = append(mod.Body, &pyast.Node{ Node: &pyast.Node_ClassDef{ @@ -857,7 +953,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { def = dataclassNode(arg.Struct.Name) } for _, f := range arg.Struct.Fields { - def.Body = append(def.Body, fieldNode(f)) + def.Body = append(def.Body, fieldNode(f, ctx.C.EmitPydanticModels)) } mod.Body = append(mod.Body, poet.Node(def)) } @@ -870,7 +966,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { def = dataclassNode(q.Ret.Struct.Name) } for _, f := range q.Ret.Struct.Fields { - def.Body = append(def.Body, fieldNode(f)) + def.Body = append(def.Body, fieldNode(f, ctx.C.EmitPydanticModels)) } mod.Body = append(mod.Body, poet.Node(def)) } diff --git a/internal/gen_test.go b/internal/gen_test.go new file mode 100644 index 0000000..f850eda --- /dev/null +++ b/internal/gen_test.go @@ -0,0 +1,53 @@ +package python + +import ( + "strconv" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/sqlc-dev/sqlc-gen-python/internal/printer" +) + +func Test_fieldNode(t *testing.T) { + cases := []struct { + Expected string + EmitPydanticModels bool + Field Field + }{ + { + Expected: "class_: int", + Field: Field{ + Name: "class", + Type: pyType{ + InnerType: "int", + IsNull: false, + IsArray: false, + }, + }, + EmitPydanticModels: false, + }, + { + Expected: "class_: int = pydantic.Field(\n alias=\"class\",\n)", + Field: Field{ + Name: "class", + Type: pyType{ + InnerType: "int", + IsNull: false, + IsArray: false, + }, + }, + EmitPydanticModels: true, + }, + } + + for _, tc := range cases { + t.Run(tc.Expected+" "+strconv.FormatBool(tc.EmitPydanticModels), func(t *testing.T) { + res := fieldNode(tc.Field, tc.EmitPydanticModels) + result := printer.Print(res, printer.Options{}) + if diff := cmp.Diff(strings.TrimSpace(tc.Expected), strings.TrimSpace(string(result.Python))); diff != "" { + t.Errorf("node to python code mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/internal/poet/builders.go b/internal/poet/builders.go index d38ed7c..4a36606 100644 --- a/internal/poet/builders.go +++ b/internal/poet/builders.go @@ -64,7 +64,7 @@ func Is() *ast.Node { func Name(id string) *ast.Node { return &ast.Node{ Node: &ast.Node_Name{ - Name: &ast.Name{Id: id}, + Name: &ast.Name{Id: FieldName(id)}, }, } } diff --git a/internal/poet/reserved.go b/internal/poet/reserved.go new file mode 100644 index 0000000..d9d3198 --- /dev/null +++ b/internal/poet/reserved.go @@ -0,0 +1,38 @@ +package poet + +import "slices" + +// TODO(quentin@escape.tech): check if this is complete +var reservedKeywords = []string{ + "class", + "if", + "else", + "elif", + "not", + "for", + "and", + "in", + "is", + "or", + "with", + "as", + "assert", + "break", + "except", + "finally", + "try", + "raise", + "return", + "yield", +} + +func IsReserved(name string) bool { + return slices.Contains(reservedKeywords, name) +} + +func FieldName(name string) string { + if IsReserved(name) { + return name + "_" + } + return name +} diff --git a/internal/poet/reserved_test.go b/internal/poet/reserved_test.go new file mode 100644 index 0000000..3449090 --- /dev/null +++ b/internal/poet/reserved_test.go @@ -0,0 +1,41 @@ +package poet + +import "testing" + +func Test_FieldName_ShouldNotChangeNonReservedKeywords(t *testing.T) { + cases := []string{ + "hello", + "world", + "iff", + "not_reserved", + "class_", + } + + for _, key := range cases { + t.Run(key, func(t *testing.T) { + got := FieldName(key) + if got != key { + t.Errorf("FieldName(%q) = %q, want %q", key, got, key) + } + }) + } +} + +func Test_FieldName_ShouldUdateReservedKeywords(t *testing.T) { + cases := []string{ + "if", + "class", + } + + for _, key := range cases { + t.Run(key, func(t *testing.T) { + if !IsReserved(key) { + t.Errorf("%s should be reserved", key) + } + got := FieldName(key) + if IsReserved(got) { + t.Errorf("FieldName(%s) = %s, should not be reserved", key, got) + } + }) + } +}
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: