diff --git a/.changes/unreleased/Added-20260813-120000.yaml b/.changes/unreleased/Added-20260813-120000.yaml new file mode 100644 index 00000000..7b3e6312 --- /dev/null +++ b/.changes/unreleased/Added-20260813-120000.yaml @@ -0,0 +1,6 @@ +kind: Added +body: 'MySQL support: the new `sql_driver` values `asyncmy` and `pymysql` generate code for [asyncmy](https://pypi.org/project/asyncmy/) (asyncio) and [PyMySQL](https://pypi.org/project/PyMySQL/) (synchronous), using `engine: "mysql"` and the same `?` placeholders as the SQLite drivers - the plugin rewrites them to the drivers'' pyformat `%s` at generation time. Inline `ENUM` (and `SET`) columns generate `enums.py` classes, `tinyint(1)` maps to `bool`, `time` to `datetime.timedelta`, and returned `json` columns stay `str` like on every other driver. `:execlastid` returns `cursor.lastrowid` (`None` when nothing was inserted); `:copyfrom` stays PostgreSQL-only. Reused parameters (`sqlc.arg` or `sqlc.slice` used at several sites) merge into one function argument even though MySQL binds every occurrence separately.' +time: 2026-08-13T12:00:00.0000000Z +custom: + Author: Rayakame + PR: "248" diff --git a/.changes/unreleased/Fixed-20260813-120000.yaml b/.changes/unreleased/Fixed-20260813-120000.yaml new file mode 100644 index 00000000..6b8ac692 --- /dev/null +++ b/.changes/unreleased/Fixed-20260813-120000.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: 'With `omit_unused_models: true`, an enum referenced only through an overridden column no longer breaks generation in either direction: an overridden enum parameter kept its `enums.X(...)` conversion while the class was filtered away (a `NameError` at import), and an enum used only by overridden return columns was retained as a dead `enums.py` nothing imports.' +time: 2026-08-13T12:00:00.0000000Z +custom: + Author: Rayakame + PR: "248" diff --git a/.changes/unreleased/Fixed-20260813-120001.yaml b/.changes/unreleased/Fixed-20260813-120001.yaml new file mode 100644 index 00000000..728b15ee --- /dev/null +++ b/.changes/unreleased/Fixed-20260813-120001.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: 'Query modules whose only queries return whole model rows (no `:many`) no longer import the row''s column-type modules (`datetime`, `decimal`, ...) into their `TYPE_CHECKING` block: nothing in such a module spells those types, and ruff flagged the imports as unused on generated output.' +time: 2026-08-13T12:00:01.0000000Z +custom: + Author: Rayakame + PR: "248" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3230833..4d758271 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,18 @@ jobs: --health-interval=10s --health-timeout=5s --health-retries=5 + mysql: + image: mysql:9 + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: testdb + ports: + - 3306/tcp # host-port is picked automatically + options: >- + --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot" + --health-interval=10s + --health-timeout=5s + --health-retries=10 strategy: fail-fast: false @@ -54,6 +66,7 @@ jobs: env: # GitHub tells you which host-port was assigned via the `job.services` context POSTGRES_URI: postgres://postgres:postgres@localhost:${{ job.services.postgres.ports['5432'] }}/testdb + MYSQL_URI: mysql://root:root@localhost:${{ job.services.mysql.ports['3306'] }}/testdb run: | uv run nox -s pytest -- --coverage @@ -310,6 +323,54 @@ jobs: run: | uv run nox -s turso_async_check + pymysql: + runs-on: ubuntu-latest + name: "Run pymysql check via nox" + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install uv + uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0 + with: + version: "0.12.3" + python-version: "3.13" + + - name: Install sqlc + uses: sqlc-dev/setup-sqlc@bac53b7fb28c039a6c7f5736fd1e89744021bdd6 # v5 + with: + sqlc-version: '1.31.1' + + - name: Run sqlc verify via nox + run: | + uv run nox -s pymysql_check + + asyncmy: + runs-on: ubuntu-latest + name: "Run asyncmy check via nox" + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install uv + uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0 + with: + version: "0.12.3" + python-version: "3.13" + + - name: Install sqlc + uses: sqlc-dev/setup-sqlc@bac53b7fb28c039a6c7f5736fd1e89744021bdd6 # v5 + with: + sqlc-version: '1.31.1' + + - name: Run sqlc verify via nox + run: | + uv run nox -s asyncmy_check + go-test: runs-on: ubuntu-latest name: "Run go tests" @@ -386,7 +447,7 @@ jobs: retention-days: 30 ci-done: - needs: [test, upload-coverage, asyncpg, psycopg-async, psycopg-sync, aiosqlite, sqlite3, turso-sync, turso-async, pyright, ruff, go-test, go-lint, test-build] + needs: [test, upload-coverage, asyncpg, psycopg-async, psycopg-sync, aiosqlite, sqlite3, turso-sync, turso-async, pymysql, asyncmy, pyright, ruff, go-test, go-lint, test-build] if: always() && !cancelled() runs-on: ubuntu-latest diff --git a/.golangci.yml b/.golangci.yml index 4c79db6f..66fe2ecc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -115,6 +115,7 @@ linters: - '.*/internal/model\.QueryValue$' - '.*/internal/model\.Column$' - '.*/internal/render\.importSpec$' + - '.*/internal/driver\.placeholderStyle$' - '.*/plugin\.Identifier$' allow-empty: true tagliatelle: diff --git a/CLAUDE.md b/CLAUDE.md index dcf99902..12196f90 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,10 +31,11 @@ A sqlc WASM plugin written in Go that generates Python database code (models + query functions + enums) from SQL. The plugin is compiled to `wasip1/wasm` and executed by `sqlc generate`. Supported Python drivers: `asyncpg`, `psycopg_async`, `psycopg_sync` (PostgreSQL engine); `aiosqlite`, `sqlite3`, -`turso_async`, `turso_sync` (SQLite engine; turso is pyturso, experimental). +`turso_async`, `turso_sync` (SQLite engine; turso is pyturso, experimental); +`pymysql`, `asyncmy` (MySQL engine). Model types: `dataclass`, `attrs`, `msgspec`, `pydantic`. Command support differs by family: `:copyfrom` is postgres-only, `:execlastid` is -sqlite/turso-only, `:batch*` is unsupported everywhere. +sqlite/turso/mysql-only, `:batch*` is unsupported everywhere. Generated code targets Python 3.12+ (PEP 695 type aliases and generics, `enum.StrEnum`). Generated output must be deterministic and byte-identical @@ -82,7 +83,7 @@ Python tooling is uv + nox. One-time setup: `uv sync --group dev`. Requires uv run nox # all default sessions uv run nox -s asyncpg # regenerate test/driver_asyncpg via sqlc, then pyright + ruff on it # (one session per driver: asyncpg, psycopg_async, psycopg_sync, - # aiosqlite, sqlite3, turso_sync, turso_async) + # aiosqlite, sqlite3, turso_sync, turso_async, pymysql, asyncmy) uv run nox -s asyncpg_check # `sqlc diff` variant: verifies committed generated code is up to date # (CI uses these; every driver has a *_check session) uv run nox -s pyright ruff # type-check / lint the test suite itself @@ -92,12 +93,14 @@ uv run nox -s pytest # runtime tests (needs postgres, see below) Extra pytest args pass through after `--`, e.g. `uv run nox -s pytest -- test/driver_asyncpg/msgspec/test_msgspec_classes.py -k test_name`. -pytest needs a local PostgreSQL, configured via the `POSTGRES_URI` env var -(default `postgresql://root:187187@localhost:5432/root`). CONTRIBUTING.md has -a `docker run` one-liner for it. +pytest needs a local PostgreSQL (`POSTGRES_URI`, default +`postgresql://root:187187@localhost:5432/root`) AND a local MySQL +(`MYSQL_URI`, default `mysql://root:187187@localhost:3306/root`); the +session-end cleanup connects to both unconditionally. CONTRIBUTING.md has +`docker run` one-liners for both. The full verification loop after a generator change: `go build ./...` -> -rebuild wasm -> `uv run nox` -> the seven `*_check` sessions -> commit the +rebuild wasm -> `uv run nox` -> the nine `*_check` sessions -> commit the regenerated fixtures together with the Go change (when told to commit). ### Changelog @@ -117,25 +120,30 @@ generation pipeline lives in `internal/handler.go`: referenced by overrides; they resolve before override parsing (the override inherits the converter's py_type). 2. **`internal/types`** - engine-specific SQL-type -> Python-type mapping - (`postgresql.go`, `sqlite.go`), selected by `GetTypeConversionFunc(engine)`. + (`postgresql.go`, `sqlite.go`, `mysql.go`), selected by + `GetTypeConversionFunc(engine)`. 3. **`internal/transform`** - turns the sqlc catalog/queries into the IR: `BuildEnums()`, `BuildTables()`, `BuildQueries(tables)`, `FilterUnusedModels()`. `type.go` builds `PyType` and normalizes `SQLType` (lowercased once here; every downstream consumer relies on it). - `psycopg_sql.go` rewrites `$N` placeholders to psycopg's `%(pN)s` at IR - build time (a small PostgreSQL lexer: skips strings, dollar quotes, quoted - identifiers, nested comments; doubles literal `%`). `plainParams` - pre-reserves every local the driver bodies emit (`conn`/`self`, `sql` for - slice queries, psycopg's `sql_params`/`cur`/`row`/`_decode_hook`); a new - local in a driver body needs a matching seed or a param can shadow it. + `psycopg_sql.go` and `mysql_sql.go` rewrite placeholders at IR build time + (psycopg: `$N` -> `%(pN)s`; MySQL: `?` -> `%s` with `%` doubled - small + SQL lexers matching each engine's rules). `plainParams` pre-reserves + every local the driver bodies emit (`conn`/`self`, `sql` for slice + queries, psycopg's and MySQL's `sql_params`/`cur`/`row`/`_decode_hook`); + a new local in a driver body needs a matching seed or a param can shadow + it. It also merges MySQL's per-occurrence duplicates of reused NAMED + parameters (`IsNamedParam`; the `Repeated` flag keeps their binding + slots) - bare `?` params that share a column name stay distinct. 4. **`internal/model`** - the IR structs (`Enum`, `Table`, `Query`, `PyType`, ...) plus naming logic: initialisms, table-name singularization (jinzhu/inflection; exclusions match bare AND schema-qualified names), Python reserved-word escaping (`reserved.go`), and `DedupName`. -5. **`internal/driver`** - the `Driver` interface (`driver.go`) with four +5. **`internal/driver`** - the `Driver` interface (`driver.go`) with five implementations: `asyncpg.go`; `psycopg.go` for BOTH psycopg flavors (parameterized by an async flag); `sqlite_base.go` for BOTH sqlite drivers - (module name + async flag); `turso.go` for BOTH turso flavors. A driver + (module name + async flag); `turso.go` for BOTH turso flavors; + `mysql_base.go` for BOTH MySQL drivers (module name + async flag). A driver knows which query commands it supports and emits query function bodies and the `QueryResults` class. `conversion.go` holds the asyncpg conversion set and the ordered sqlite adapter/converter spec table; adapters are @@ -180,6 +188,10 @@ buffer is emitted as an extra output file. emits only what that module needs (params -> adapters, non-overridden returns -> converters). psycopg's loader registration follows the same policy (returned json/jsonb types only). +- The MySQL drivers interpolate pyformat placeholders client-side: rewritten + SQL constants carry `%s`/`%%`, and the slice-expansion/placeholder-scanner + machinery in `internal/driver/common.go` must lex them exactly like the + rewriter emitted them. - `speedups: true` swaps date/datetime decoding to `ciso8601` (sqlite converter bodies, turso inline decodes); the import resolver tracks which variant is emitted. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 435deb8a..04dfcc50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ that you should follow to ensure that your contribution is at its best. The easiest way to get exactly that version without a Go toolchain is [sqlc-bin](https://pypi.org/project/sqlc-bin/), whose package version tracks the sqlc version: `uv tool install "sqlc-bin==1.31.1"`. -- **Docker** (or a local PostgreSQL) - only needed for the runtime tests. +- **Docker** (or a local PostgreSQL plus a local MySQL) - only needed for the runtime tests. One-time setup for the Python tooling: @@ -67,14 +67,15 @@ sessions run with `uv run nox -s name1 name2`: | Session | What it does | |-----------------------------------------------------|----------------------------------------------------------------------------------------| -| `asyncpg`, `psycopg_async`, `psycopg_sync`, `sqlite3`, `aiosqlite`, `turso_sync`, `turso_async` | Regenerate the driver's test fixtures via sqlc, then pyright + ruff | +| `asyncpg`, `psycopg_async`, `psycopg_sync`, `sqlite3`, `aiosqlite`, `pymysql`, `asyncmy`, `turso_sync`, `turso_async` | Regenerate the driver's test fixtures via sqlc, then pyright + ruff | | the `_check` variants of the driver sessions | `sqlc diff` variant: verify the committed generated code is up to date (CI uses these) | | `pyright` | Type-check the repository | | `ruff_check` | Non-mutating format + lint check (the CI gate) | | `ruff`, `ruff_format` | Format and auto-fix the repository - these sessions rewrite files | | `pytest` | Runtime tests against real databases | -The `pytest` session needs a local PostgreSQL. The connection URI is read from the +The `pytest` session needs BOTH a local PostgreSQL and a local MySQL - the +session-end cleanup connects to each unconditionally. The connection URI is read from the `POSTGRES_URI` environment variable and defaults to `postgresql://root:187187@localhost:5432/root`; set the variable only if your instance differs from that. To start a matching instance with docker, run @@ -88,11 +89,23 @@ docker run --rm --name sqlc-gen-better-python-postgres \ -d postgres ``` -and stop it (after running the tests) with the command below; `--rm` removes the container -on stop, so the `docker run` command above can be reused as is next time. +It also needs a local MySQL for the `pymysql` and `asyncmy` suites, read from +`MYSQL_URI` with the default `mysql://root:187187@localhost:3306/root`: + +```bash +docker run --rm --name sqlc-gen-better-python-mysql \ + -e MYSQL_ROOT_PASSWORD=187187 \ + -e MYSQL_DATABASE=root \ + -p 3306:3306 \ + -d mysql:9 +``` + +Stop the containers (after running the tests) with the commands below; `--rm` removes a +container on stop, so the `docker run` commands above can be reused as is next time. ```bash docker stop sqlc-gen-better-python-postgres +docker stop sqlc-gen-better-python-mysql ``` Extra pytest arguments pass through after `--`, e.g. @@ -103,7 +116,7 @@ Extra pytest arguments pass through after `--`, e.g. 1. Change the Go code and run `make tests` / `make lint`. 2. Rebuild the WASM plugin (see above). 3. `uv run nox` - regenerates the fixtures and runs every check on them. The default - sessions include `pytest`, so have the PostgreSQL from the section above running. + sessions include `pytest`, so have the PostgreSQL and MySQL from the section above running. The `_check` sessions are not needed locally: they verify committed fixtures against a fresh regeneration, which is what CI does with the files you commit. 4. If your change affects generated output, add coverage: a query/schema case in the test matrix diff --git a/README.md b/README.md index 61654775..55ee6f01 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,10 @@ Questions or feedback? Join the [Discord](https://discord.gg/hikari). - **Four model types** - `dataclass`, `attrs`, `msgspec`, or `pydantic` ([docs](https://sqlc-gen-better-python.rayakame.dev/docs/guide/model-types/)). -- **Seven drivers** - `asyncpg`, `psycopg_async`, and `psycopg_sync` for - PostgreSQL, `aiosqlite` and `sqlite3` for SQLite, plus experimental - `turso_async` and `turso_sync` for [Turso](https://github.com/tursodatabase/turso) +- **Nine drivers** - `asyncpg`, `psycopg_async`, and `psycopg_sync` for + PostgreSQL, `aiosqlite` and `sqlite3` for SQLite, `asyncmy` and `pymysql` + for MySQL, plus experimental `turso_async` and `turso_sync` for + [Turso](https://github.com/tursodatabase/turso) ([docs](https://sqlc-gen-better-python.rayakame.dev/docs/guide/drivers/)). - **Typed query functions** - one module per query file, one function per query ([docs](https://sqlc-gen-better-python.rayakame.dev/docs/guide/writing-queries/)). diff --git a/docs/content/_index.md b/docs/content/_index.md index 890c50df..034d55a8 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -1,7 +1,7 @@ --- title: sqlc-gen-better-python description: >- - A sqlc plugin that generates type-safe Python from SQL: dataclass, attrs, msgspec, or pydantic models plus fully typed query functions for asyncpg, psycopg, sqlite3, aiosqlite, and turso. + A sqlc plugin that generates type-safe Python from SQL: dataclass, attrs, msgspec, or pydantic models plus fully typed query functions for asyncpg, psycopg, sqlite3, aiosqlite, asyncmy, pymysql, and turso. layout: hextra-home --- @@ -36,9 +36,9 @@ layout: hextra-home subtitle="Generate dataclass, attrs, msgspec, or pydantic models - pick per codegen block." >}} {{< hextra/feature-card - title="Seven drivers" + title="Nine drivers" link="docs/guide/drivers" - subtitle="asyncpg and psycopg for PostgreSQL, aiosqlite and sqlite3 for SQLite, and experimental turso support." + subtitle="asyncpg and psycopg for PostgreSQL, aiosqlite and sqlite3 for SQLite, asyncmy and pymysql for MySQL, and experimental turso support." >}} {{< hextra/feature-card title="Strictly typed output" diff --git a/docs/content/docs/getting-started.md b/docs/content/docs/getting-started.md index b3a5bb14..38eeb21b 100644 --- a/docs/content/docs/getting-started.md +++ b/docs/content/docs/getting-started.md @@ -94,6 +94,25 @@ Nothing to install - sqlite3 is in the standard library. {{< /tab >}} + {{< tab name="asyncmy" >}} + +```bash +uv add asyncmy +``` + + {{< /tab >}} + + {{< tab name="pymysql" >}} + +```bash +uv add pymysql types-PyMySQL +``` + +PyMySQL has no strict typing of its own; [types-PyMySQL](https://pypi.org/project/types-PyMySQL/) +makes the generated annotations work under pyright and mypy. + + {{< /tab >}} + {{< /tabs >}} Using pip instead of uv? Swap `uv add` for `pip install` in any command above. @@ -235,6 +254,58 @@ sql: {{< /tab >}} + {{< tab name="asyncmy" >}} + +```yaml +# filename: sqlc.yaml +version: "2" +plugins: + - name: python + wasm: + url: https://github.com/rayakame/sqlc-gen-better-python/releases/download/v0.8.0/sqlc-gen-better-python.wasm + sha256: c98cffe9024c3c8e802426a4babec460c2d17adc440181324e3d707b1e723c48 +sql: + - engine: "mysql" + queries: "query.sql" + schema: "schema.sql" + codegen: + - out: "app/db" + plugin: python + options: + package: "db" + emit_init_file: true + sql_driver: "asyncmy" + model_type: "dataclass" +``` + + {{< /tab >}} + + {{< tab name="pymysql" >}} + +```yaml +# filename: sqlc.yaml +version: "2" +plugins: + - name: python + wasm: + url: https://github.com/rayakame/sqlc-gen-better-python/releases/download/v0.8.0/sqlc-gen-better-python.wasm + sha256: c98cffe9024c3c8e802426a4babec460c2d17adc440181324e3d707b1e723c48 +sql: + - engine: "mysql" + queries: "query.sql" + schema: "schema.sql" + codegen: + - out: "app/db" + plugin: python + options: + package: "db" + emit_init_file: true + sql_driver: "pymysql" + model_type: "dataclass" +``` + + {{< /tab >}} + {{< /tabs >}} {{< callout type="warning" >}} @@ -303,6 +374,32 @@ CREATE TABLE users {{< tab name="sqlite3" >}} +```sql +-- filename: schema.sql +CREATE TABLE users +( + id INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL +); +``` + + {{< /tab >}} + + {{< tab name="asyncmy" >}} + +```sql +-- filename: schema.sql +CREATE TABLE users +( + id INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL +); +``` + + {{< /tab >}} + + {{< tab name="pymysql" >}} + ```sql -- filename: schema.sql CREATE TABLE users @@ -383,6 +480,32 @@ SELECT * FROM users ORDER BY name; -- name: GetUser :one SELECT * FROM users WHERE id = ?; +-- name: ListUsers :many +SELECT * FROM users ORDER BY name; +``` + + {{< /tab >}} + + {{< tab name="asyncmy" >}} + +```sql +-- filename: query.sql +-- name: GetUser :one +SELECT * FROM users WHERE id = ?; + +-- name: ListUsers :many +SELECT * FROM users ORDER BY name; +``` + + {{< /tab >}} + + {{< tab name="pymysql" >}} + +```sql +-- filename: query.sql +-- name: GetUser :one +SELECT * FROM users WHERE id = ?; + -- name: ListUsers :many SELECT * FROM users ORDER BY name; ``` @@ -391,9 +514,9 @@ SELECT * FROM users ORDER BY name; {{< /tabs >}} -PostgreSQL uses `$1` placeholders, SQLite uses `?`. Everything else is the same. -(You write `$1` for psycopg too - the plugin rewrites the placeholders to -psycopg's format at generation time.) +PostgreSQL uses `$1` placeholders, SQLite and MySQL use `?`. Everything else is +the same. (You write `$1` for psycopg and `?` for the MySQL drivers - the plugin +rewrites the placeholders to each driver's format at generation time.) ## 4. Generate @@ -523,6 +646,56 @@ def get_user(conn: sqlite3.Connection, *, id_: int) -> models.User | None: def list_users(conn: sqlite3.Connection) -> QueryResults[models.User]: ... +``` + + {{< /tab >}} + + {{< tab name="asyncmy" >}} + +```python +# models.py +@dataclasses.dataclass() +class User: + id_: int + name: str + + +# query.py +async def get_user(conn: asyncmy.Connection, *, id_: int) -> models.User | None: + async with conn.cursor() as cur: + await cur.execute(GET_USER, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.User(id_=row[0], name=row[1]) + + +def list_users(conn: asyncmy.Connection) -> QueryResults[models.User]: ... +``` + + {{< /tab >}} + + {{< tab name="pymysql" >}} + +```python +# models.py +@dataclasses.dataclass() +class User: + id_: int + name: str + + +# query.py +def get_user(conn: pymysql.Connection, *, id_: int) -> models.User | None: + with conn.cursor() as cur: + cur.execute(GET_USER, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.User(id_=row[0], name=row[1]) + + +def list_users(conn: pymysql.Connection) -> QueryResults[models.User]: ... ``` {{< /tab >}} @@ -690,6 +863,59 @@ for user in query.list_users(conn): {{< /tab >}} + {{< tab name="asyncmy" >}} + +```python +import asyncio + +import asyncmy + +from app.db import query + + +async def main() -> None: + conn = await asyncmy.connect(host="localhost", user="user", password="pass", database="mydb") + + user = await query.get_user(conn, id_=1) + if user is not None: + print(user.name) + + # every row at once + users = await query.list_users(conn) + + # or stream them + async for user in query.list_users(conn): + print(user.name) + + +asyncio.run(main()) +``` + + {{< /tab >}} + + {{< tab name="pymysql" >}} + +```python +import pymysql + +from app.db import query + +conn = pymysql.connect(host="localhost", user="user", password="pass", database="mydb") + +user = query.get_user(conn, id_=1) +if user is not None: + print(user.name) + +# every row at once - call the result +users = query.list_users(conn)() + +# or iterate +for user in query.list_users(conn): + print(user.name) +``` + + {{< /tab >}} + {{< /tabs >}} ## Next steps diff --git a/docs/content/docs/guide/configuration.md b/docs/content/docs/guide/configuration.md index fad697e5..2e9c536b 100644 --- a/docs/content/docs/guide/configuration.md +++ b/docs/content/docs/guide/configuration.md @@ -56,7 +56,7 @@ sql: | Option | What it does | |---|---| | `package` | The name of the generated package. | -| `sql_driver` | `asyncpg`, `psycopg_async`, `psycopg_sync`, `aiosqlite`, `sqlite3`, `turso_async`, or `turso_sync` - must match the `engine`. See [Drivers](/docs/guide/drivers). | +| `sql_driver` | `asyncpg`, `psycopg_async`, `psycopg_sync`, `aiosqlite`, `sqlite3`, `asyncmy`, `pymysql`, `turso_async`, or `turso_sync` - must match the `engine`. See [Drivers](/docs/guide/drivers). | | `emit_init_file` | Whether to emit `__init__.py`. Must be set explicitly. | Everything else is optional and has a sensible default. The most common ones to @@ -92,8 +92,8 @@ queries - for example a `msgspec` package and a `dataclass` package: - **Driver/engine mismatch.** `sql_driver: asyncpg`, `psycopg_async`, and `psycopg_sync` require `engine: "postgresql"`; `aiosqlite`/`sqlite3` and the - turso drivers require - `engine: "sqlite"`. A mismatch is an error. + turso drivers require `engine: "sqlite"`; `asyncmy`/`pymysql` require + `engine: "mysql"`. A mismatch is an error. - **Forgetting `emit_init_file`.** It has no default and generation fails if it is omitted. Set it to `true` unless the package already has an `__init__.py`. - **A stale `sha256`.** When you bump the plugin version, update the hash too. diff --git a/docs/content/docs/guide/drivers.md b/docs/content/docs/guide/drivers.md index 4311aee7..dcf139ab 100644 --- a/docs/content/docs/guide/drivers.md +++ b/docs/content/docs/guide/drivers.md @@ -1,14 +1,14 @@ --- title: Drivers description: >- - Pick between asyncpg, psycopg_async, psycopg_sync, aiosqlite, sqlite3, and the experimental turso drivers - connection examples and per-driver behavior. + Pick between asyncpg, psycopg, aiosqlite, sqlite3, asyncmy, pymysql, and the experimental turso drivers - connection examples and per-driver behavior. weight: 20 prev: /docs/guide/configuration next: /docs/guide/model-types --- The `sql_driver` option picks which database library the generated code targets. -It must match your `engine`. Seven drivers are supported: +It must match your `engine`. Nine drivers are supported: | Driver | Engine | Style | |---|---|---| @@ -17,6 +17,8 @@ It must match your `engine`. Seven drivers are supported: | `psycopg_sync` | `postgresql` | sync | | `aiosqlite` | `sqlite` | async | | `sqlite3` | `sqlite` | sync | +| `asyncmy` | `mysql` | async | +| `pymysql` | `mysql` | sync | | `turso_async` | `sqlite` | async (experimental) | | `turso_sync` | `sqlite` | sync (experimental) | @@ -145,6 +147,73 @@ user = queries.get_field_naming(conn, id_=1) [SQLite type conversion](/docs/guide/sqlite-type-conversion). {{< /callout >}} +## asyncmy (async MySQL) + +```python +import asyncio + +import asyncmy + +from app.db import queries + + +async def main() -> None: + conn = await asyncmy.connect(host="localhost", user="user", password="pass", database="db") + user = await queries.get_field_naming(conn, id_=1) + + +asyncio.run(main()) +``` + +The generated code targets [asyncmy](https://github.com/long2ice/asyncmy) +with its default tuple cursors; the connection annotation is +`asyncmy.Connection`. No connection flags are needed - values convert +inline, so `date`/`datetime`/`decimal` columns round-trip out of the box. + +{{< callout type="info" >}} + asyncmy's shipped stubs leave the cursor methods unannotated, so every + generated queries module starts with + `# pyright: reportUnknownMemberType=false`. The rest of the module is + checked under pyright strict as usual; the line disappears once asyncmy + annotates its stubs. +{{< /callout >}} + +## pymysql (sync MySQL) + +```python +import pymysql + +from app.db import queries + +conn = pymysql.connect(host="localhost", user="user", password="pass", database="db") +user = queries.get_field_naming(conn, id_=1) +``` + +Same contract as `asyncmy`, emitted as plain functions without +`async`/`await`. The connection annotation is `pymysql.Connection`. + +{{< callout type="info" >}} + PyMySQL ships without type annotations - install + [types-PyMySQL](https://pypi.org/project/types-PyMySQL/) so pyright and + mypy understand the generated code. Type checking only; never evaluated + at runtime. +{{< /callout >}} + +Behavior shared by both MySQL drivers: + +- Queries are written with `?` placeholders like on SQLite; the generated SQL + constants hold the pyformat `%s` form the drivers expect, with literal `%` + doubled. The rewrite happens at generation time - your `.sql` files stay + plain MySQL. +- `time` columns map to `datetime.timedelta` and `tinyint(1)` to `bool`, + matching what the drivers return. The full table is in the + [type mappings reference](/docs/reference/type-mappings). +- `memoryview` parameters bind as `bytes` automatically - the PyMySQL family + cannot encode a raw memoryview. +- Inline `ENUM` (and `SET`) columns generate + [enum classes](/docs/guide/enums#mysql). +- `:execlastid` returns `cursor.lastrowid`; `:copyfrom` is not supported. + ## turso_sync / turso_async (Turso) [Turso](https://github.com/tursodatabase/turso) is an SQLite-compatible @@ -199,5 +268,5 @@ asyncio.run(main()) Not every [query command](/docs/guide/writing-queries) works on every driver - for example `:copyfrom` is PostgreSQL-only and `:execlastid` is limited to the -SQLite-engine drivers. The full matrix is in the +SQLite-engine and MySQL drivers. The full matrix is in the [feature support reference](/docs/reference/feature-support). diff --git a/docs/content/docs/guide/enums.md b/docs/content/docs/guide/enums.md index 2d2f3b7c..4c22acad 100644 --- a/docs/content/docs/guide/enums.md +++ b/docs/content/docs/guide/enums.md @@ -1,15 +1,16 @@ --- title: Enums description: >- - PostgreSQL enum types become Python enum.StrEnum classes, wired through the generated models and query parameters. + PostgreSQL enum types and MySQL enum columns become Python enum.StrEnum classes, wired through the generated models and query parameters. weight: 50 prev: /docs/guide/writing-queries next: /docs/guide/type-overrides --- -PostgreSQL enum types become `enum.StrEnum` classes in a generated `enums.py` -module. Columns of that type are annotated with the class, and values read from -the database are coerced into it. +PostgreSQL enum types and MySQL inline `ENUM(...)` columns become +`enum.StrEnum` classes in a generated `enums.py` module. Columns of that type +are annotated with the class, and values read from the database are coerced +into it. ## Example @@ -63,7 +64,31 @@ Enums in a non-default schema get schema-qualified class names so same-named enums never collide - for example `custom.mood` becomes `CustomMood`, distinct from a `public.mood` that would become `Mood`. +## MySQL + +MySQL has no named enum types - `ENUM` is declared inline on the column, so +the class is named after the table and column: + +```sql +CREATE TABLE test_enum_override +( + id bigint PRIMARY KEY NOT NULL, + mood_test enum('sad','ok','happy') NOT NULL +); +``` + +generates `TestEnumOverrideMoodTest`, used exactly like a PostgreSQL enum +class. `SET(...)` columns generate a class the same way. + +{{< callout type="warning" >}} + A `SET` column can hold several members at once, but the database returns + them as one comma-joined string - coercing `"alpha,beta"` into the enum + class raises `ValueError`. Only single-valued sets round-trip; for + multi-valued sets add a [type override](/docs/guide/type-overrides) to + `str`. +{{< /callout >}} + {{< callout type="info" >}} - Enum classes are a PostgreSQL feature - SQLite has no native enum type, so this - applies to the PostgreSQL drivers (`asyncpg`, `psycopg_async`, and `psycopg_sync`). + SQLite has no native enum type, so `enums.py` is generated for the + PostgreSQL and MySQL drivers only. {{< /callout >}} diff --git a/docs/content/docs/guide/writing-queries.md b/docs/content/docs/guide/writing-queries.md index 5370c25b..38b695c3 100644 --- a/docs/content/docs/guide/writing-queries.md +++ b/docs/content/docs/guide/writing-queries.md @@ -104,16 +104,19 @@ async def set_field_naming_outputs(conn: ConnectionLike, *, id_: int, outputs: s Variants of `:exec` that return something about the write: - **`:execrows`** - the number of affected rows (`int`). For statements that - affect no rows, such as `CREATE TABLE`, asyncpg and the turso drivers report - `0` while psycopg and the SQLite drivers report `-1`. + affect no rows, such as `CREATE TABLE`, asyncpg, the MySQL drivers, and the + turso drivers report `0` while psycopg and the SQLite drivers report `-1`. - **`:execlastid`** - the cursor's `lastrowid`, typed `int | None` - it is `None` - when no row was affected. SQLite-engine drivers only, and note it is the last - *affected* row, not strictly the last inserted one. On turso it is `None` for - `UPDATE`/`DELETE`. + when no row was affected. SQLite-engine and MySQL drivers only, and note it + is the last *affected* row, not strictly the last inserted one (on MySQL it + is the `AUTO_INCREMENT` id of the cursor's own `INSERT`). On turso it is + `None` for `UPDATE`/`DELETE`. - **`:execresult`** - the driver's raw result, which differs per driver: a `str` status tag on asyncpg, a `psycopg.AsyncCursor` / `psycopg.Cursor` on the psycopg drivers, a `sqlite3.Cursor` / `aiosqlite.Cursor` on the SQLite - drivers, and a `turso.Cursor` / `turso.aio.Cursor` on the turso drivers. + drivers, an open `asyncmy.cursors.Cursor` / `pymysql.cursors.Cursor` on the + MySQL drivers (close it when done), and a `turso.Cursor` / + `turso.aio.Cursor` on the turso drivers. See the [feature support matrix](/docs/reference/feature-support) for which driver supports which. diff --git a/docs/content/docs/reference/configuration-options.md b/docs/content/docs/reference/configuration-options.md index d51d2f15..df72645f 100644 --- a/docs/content/docs/reference/configuration-options.md +++ b/docs/content/docs/reference/configuration-options.md @@ -19,7 +19,7 @@ optional. | Option | Type | Default | Description | |---|---|---|---| | `package` | string | *required* | Name of the generated package. | -| `sql_driver` | string | *required* | One of `asyncpg`, `psycopg_async`, `psycopg_sync`, `aiosqlite`, `sqlite3`, `turso_async`, `turso_sync`. Must match the engine (the postgres drivers -> `postgresql`; the sqlite and turso drivers -> `sqlite`). | +| `sql_driver` | string | *required* | One of `asyncpg`, `psycopg_async`, `psycopg_sync`, `aiosqlite`, `sqlite3`, `asyncmy`, `pymysql`, `turso_async`, `turso_sync`. Must match the engine (the postgres drivers -> `postgresql`; the sqlite and turso drivers -> `sqlite`; the mysql drivers -> `mysql`). | | `emit_init_file` | bool | *required* | Whether to emit an `__init__.py` in the package. Must be set explicitly. Set `false` only if the package already has one. | | `model_type` | string | `dataclass` | One of `dataclass`, `attrs`, `msgspec`, `pydantic`. See [Model types](/docs/guide/model-types). | | `initialisms` | list[string] | `["id"]` | Identifier segments to upper-case, e.g. `app_id` -> `AppID`. | diff --git a/docs/content/docs/reference/feature-support.md b/docs/content/docs/reference/feature-support.md index b8a84af3..e1ef37b7 100644 --- a/docs/content/docs/reference/feature-support.md +++ b/docs/content/docs/reference/feature-support.md @@ -14,12 +14,12 @@ Every [sqlc macro](https://docs.sqlc.dev/en/latest/reference/macros.html) is supported (`sqlc.arg`, `sqlc.narg`, `sqlc.embed`, `sqlc.slice`). {{< callout type="info" >}} - `sqlc.slice` is for the SQLite drivers, where a list cannot be passed to the - `IN` operator: the generated function expands the placeholder at call time, - one `?` per element, and an empty sequence matches no rows. Because the SQL - is built per call, it cannot be used with prepared statements. On PostgreSQL - the macro is not needed - use `= ANY($1::type[])`, which accepts the sequence - directly. + `sqlc.slice` is for the SQLite and MySQL drivers, where a list cannot be + passed to the `IN` operator: the generated function expands the placeholder + at call time, one placeholder per element, and an empty sequence matches no + rows. Because the SQL is built per call, it cannot be used with prepared + statements. On PostgreSQL the macro is not needed - use `= ANY($1::type[])`, + which accepts the sequence directly. {{< /callout >}} ## Query commands @@ -27,15 +27,15 @@ supported (`sqlc.arg`, `sqlc.narg`, `sqlc.embed`, `sqlc.slice`). The supported [query annotations](https://docs.sqlc.dev/en/latest/reference/query-annotations.html) depend on the driver: -| Command | aiosqlite | sqlite3 | asyncpg | psycopg_async | psycopg_sync | turso_async | turso_sync | -|---|---|---|---|---|---|---|---| -| `:one` | yes | yes | yes | yes | yes | yes | yes | -| `:many` | yes | yes | yes | yes | yes | yes | yes | -| `:exec` | yes | yes | yes | yes | yes | yes | yes | -| `:execresult` | yes | yes | yes | yes | yes | yes | yes | -| `:execrows` | yes | yes | yes | yes | yes | yes | yes | -| `:execlastid` | yes | yes | no | no | no | yes | yes | -| `:copyfrom` | no | no | yes | yes | yes | no | no | +| Command | aiosqlite | sqlite3 | asyncpg | psycopg_async | psycopg_sync | asyncmy | pymysql | turso_async | turso_sync | +|---|---|---|---|---|---|---|---|---|---| +| `:one` | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `:many` | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `:exec` | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `:execresult` | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `:execrows` | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `:execlastid` | yes | yes | no | no | no | yes | yes | yes | yes | +| `:copyfrom` | no | no | yes | yes | yes | no | no | no | no | See [Writing queries](/docs/guide/writing-queries) for what each command generates. @@ -47,7 +47,9 @@ generates. statements - turso's `lastrowid` only reflects the cursor's own `INSERT`. - `:copyfrom` maps to PostgreSQL's bulk `COPY` protocol (`copy_records_to_table` on asyncpg, `cursor.copy()` on psycopg), which - the SQLite-engine drivers have no equivalent for. + the SQLite-engine and MySQL drivers have no equivalent for. MySQL's + `LOAD DATA LOCAL INFILE` exposes no row-streaming driver API to + generate against; if you need it, run it as a plain `:exec` statement. {{< /callout >}} ### Prepared queries @@ -84,9 +86,13 @@ controls it: `cached_statements` argument of `connect()` if you have more distinct queries than that. -- **turso_sync / turso_async** (experimental) are the exception: pyturso - currently has no statement cache and no tuning knob, so every execution - prepares the statement anew. +- **asyncmy / pymysql** use MySQL's text protocol: parameters are + interpolated client-side and nothing is prepared server-side, so there is + no knob and nothing to disable behind a pooler. + +- **turso_sync / turso_async** (experimental) are the exception among the + prepared-statement drivers: pyturso currently has no statement cache and no + tuning knob, so every execution prepares the statement anew. {{< callout type="warning" >}} Behind PgBouncer in transaction-pooling mode, server-side prepared @@ -98,6 +104,8 @@ controls it: - **`:batch*` commands** (`:batchexec`, `:batchmany`, `:batchone`) are not supported and likely never will be. -- **`psycopg2` and `mysql`** drivers are not currently supported; Psycopg 3 - is, via the `psycopg_async` (asyncio) and `psycopg_sync` (synchronous) - drivers. +- **`psycopg2`** is not supported; Psycopg 3 is, via the `psycopg_async` + (asyncio) and `psycopg_sync` (synchronous) drivers. For MySQL, use + `asyncmy` or `pymysql` - `mysqlclient` and `mysql-connector-python` are + not codegen targets (PyMySQL-targeted code is source-compatible with + `mysqlclient` except that `json` columns arrive as `bytes` there). diff --git a/docs/content/docs/reference/type-mappings.md b/docs/content/docs/reference/type-mappings.md index c90e042b..c3e669d1 100644 --- a/docs/content/docs/reference/type-mappings.md +++ b/docs/content/docs/reference/type-mappings.md @@ -1,7 +1,7 @@ --- title: Type mappings description: >- - The built-in SQL-to-Python type mappings for PostgreSQL and SQLite, and what nullable columns and arrays map to. + The built-in SQL-to-Python type mappings for PostgreSQL, SQLite, and MySQL, and what nullable columns and arrays map to. weight: 20 prev: /docs/reference/configuration-options next: /docs/reference/feature-support @@ -74,3 +74,29 @@ prefix (so `varchar(255)` matches `varchar`, and `decimal(10,5)` matches For the two SQLite drivers, several of these types also need runtime adapters and converters (registered in the generated code) to round-trip correctly - see [SQLite type conversion](/docs/guide/sqlite-type-conversion). + +## MySQL + +MySQL type names are matched case-insensitively. + +| SQL type | Python type | +|---|---| +| `tinyint(1)`, `bool`, `boolean` | `bool` | +| `tinyint`, `smallint`, `mediumint`, `int`, `integer`, `bigint`, `year`, `serial` (signed or `unsigned`) | `int` | +| `float`, `double`, `double precision`, `real` | `float` | +| `decimal`, `dec`, `fixed`, `numeric` | `decimal.Decimal` | +| `char`, `varchar`, `tinytext`, `text`, `mediumtext`, `longtext` | `str` | +| `binary`, `varbinary`, `tinyblob`, `blob`, `mediumblob`, `longblob`, `bit` | `memoryview` | +| `date` | `datetime.date` | +| `datetime`, `timestamp` | `datetime.datetime` | +| `time` | `datetime.timedelta` | +| `json` | `str` | +| an inline `ENUM(...)` or `SET(...)` column | the generated [enum class](/docs/guide/enums#mysql) - only single-valued sets round-trip | +| anything else | `typing.Any` | + +{{< callout type="info" >}} + Two mappings follow what the PyMySQL-family drivers actually return rather + than the SQL standard: `time` is a `datetime.timedelta` (MySQL `TIME` values + span more than 24 hours), and `tinyint(1)` is `bool` only when the display + width is literally 1 in the DDL - a plain `tinyint` stays `int`. +{{< /callout >}} diff --git a/internal/config/constants.go b/internal/config/constants.go index 1e4d2a97..cacbda0b 100644 --- a/internal/config/constants.go +++ b/internal/config/constants.go @@ -22,6 +22,8 @@ const ( SQLDriverPsycopgSync SQLDriver = "psycopg_sync" SQLDriverTursoSync SQLDriver = "turso_sync" SQLDriverTursoAsync SQLDriver = "turso_async" + SQLDriverPymysql SQLDriver = "pymysql" + SQLDriverAsyncmy SQLDriver = "asyncmy" ) // IsPsycopg reports whether the driver is one of the two psycopg flavors, @@ -39,6 +41,13 @@ func (dr SQLDriver) IsTurso() bool { return dr == SQLDriverTursoSync || dr == SQLDriverTursoAsync } +// IsMysql reports whether the driver is one of the two PyMySQL-family +// flavors, which share the pyformat placeholder rewrite (? -> %s), the +// cursor-based call shape, and PyMySQL's type conversion defaults. +func (dr SQLDriver) IsMysql() bool { + return dr == SQLDriverPymysql || dr == SQLDriverAsyncmy +} + const ( ModelTypeDataclass ModelType = "dataclass" ModelTypeAttrs ModelType = "attrs" @@ -49,6 +58,7 @@ const ( const ( engineSQLite = "sqlite" enginePostgreSQL = "postgresql" + engineMySQL = "mysql" ) var driversEngine = map[SQLDriver]string{ @@ -59,6 +69,8 @@ var driversEngine = map[SQLDriver]string{ SQLDriverPsycopgSync: enginePostgreSQL, SQLDriverTursoSync: engineSQLite, SQLDriverTursoAsync: engineSQLite, + SQLDriverPymysql: engineMySQL, + SQLDriverAsyncmy: engineMySQL, } const ( diff --git a/internal/config/constants_test.go b/internal/config/constants_test.go index 68470c34..cd4231cc 100644 --- a/internal/config/constants_test.go +++ b/internal/config/constants_test.go @@ -43,6 +43,20 @@ func TestSQLDriverValidate(t *testing.T) { {name: "psycopg_sync with postgresql", driver: config.SQLDriverPsycopgSync, engine: "postgresql"}, {name: "turso_sync with sqlite", driver: config.SQLDriverTursoSync, engine: "sqlite"}, {name: "turso_async with sqlite", driver: config.SQLDriverTursoAsync, engine: "sqlite"}, + {name: "pymysql with mysql", driver: config.SQLDriverPymysql, engine: "mysql"}, + {name: "asyncmy with mysql", driver: config.SQLDriverAsyncmy, engine: "mysql"}, + { + name: "pymysql with postgresql", + driver: config.SQLDriverPymysql, + engine: "postgresql", + wantErr: "SQL driver pymysql does not support postgresql", + }, + { + name: "asyncmy with sqlite", + driver: config.SQLDriverAsyncmy, + engine: "sqlite", + wantErr: "SQL driver asyncmy does not support sqlite", + }, { name: "turso_async with postgresql", driver: config.SQLDriverTursoAsync, @@ -160,6 +174,8 @@ func TestSQLDriverIsPsycopg(t *testing.T) { config.SQLDriverAsyncpg: false, config.SQLDriverAioSQLite: false, config.SQLDriverSQLite: false, + config.SQLDriverPymysql: false, + config.SQLDriverAsyncmy: false, } { if got := driver.IsPsycopg(); got != want { t.Errorf("IsPsycopg(%q) = %v, want %v", driver, got, want) @@ -177,9 +193,30 @@ func TestSQLDriverIsTurso(t *testing.T) { config.SQLDriverAsyncpg: false, config.SQLDriverPsycopgSync: false, config.SQLDriverPsycopgAsync: false, + config.SQLDriverPymysql: false, + config.SQLDriverAsyncmy: false, } { if got := driver.IsTurso(); got != want { t.Errorf("IsTurso(%q) = %v, want %v", driver, got, want) } } } + +func TestSQLDriverIsMysql(t *testing.T) { + t.Parallel() + for driver, want := range map[config.SQLDriver]bool{ + config.SQLDriverPymysql: true, + config.SQLDriverAsyncmy: true, + config.SQLDriverSQLite: false, + config.SQLDriverAioSQLite: false, + config.SQLDriverAsyncpg: false, + config.SQLDriverPsycopgSync: false, + config.SQLDriverPsycopgAsync: false, + config.SQLDriverTursoSync: false, + config.SQLDriverTursoAsync: false, + } { + if got := driver.IsMysql(); got != want { + t.Errorf("IsMysql(%q) = %v, want %v", driver, got, want) + } + } +} diff --git a/internal/driver/common.go b/internal/driver/common.go index 07be905d..c51e7558 100644 --- a/internal/driver/common.go +++ b/internal/driver/common.go @@ -30,11 +30,21 @@ func writeFuncSignature( asyncPrefix = "async " } + signatureParams := 0 + for _, param := range query.Params { + if !param.Repeated { + signatureParams++ + } + } args := []string{first} - if len(query.Params) > config.OmitKwargsLimit { + if signatureParams > config.OmitKwargsLimit { args = append(args, "*") } for _, param := range query.Params { + // A repeated MySQL parameter binds again but is one argument. + if param.Repeated { + continue + } args = append(args, fmt.Sprintf("%s: %s", param.Name, param.Type.Print())) } body.WriteWrappedCall(indent, @@ -52,27 +62,85 @@ func writeFuncSignature( // type natively. type wireConvertFunc func(sqlType string) (string, bool) +// placeholderStyle describes how bindable placeholders appear in a query's +// final SQL text. The sqlite-family drivers keep sqlc's native "?"; the +// MySQL drivers rewrite to pyformat "%s" at IR build time, which also +// changes the lexing rules for the surrounding text. +type placeholderStyle struct { + // token is one bindable placeholder as it appears in the SQL. + token string + // joinExpr is the Sprintf template (one %s verb: the sequence + // expression) for the runtime slice expansion - one comma-joined + // placeholder per element, "NULL" for an empty sequence. + joinExpr string + // numbered marks placeholders that may carry a digit suffix ("?2", + // sqlite only); the digits belong to the token. + numbered bool + // backslashEscapes marks '...' and "..." literals as honoring + // backslash escapes in addition to doubled quotes (MySQL). + backslashEscapes bool + // hashComments marks "#" as a line-comment introducer (MySQL). + hashComments bool + // dashCommentNeedsGap requires whitespace (or end of input) after "--" + // for it to start a comment (MySQL; "a--1" is arithmetic). + dashCommentNeedsGap bool + // backtickIdents marks `...` as quoted identifiers (MySQL). + backtickIdents bool + // versionComments marks /*! comment bodies as live SQL that can hold + // placeholders (MySQL; sqlc's parser emits parameters for them). + versionComments bool + // doubledToken is a non-placeholder escape sequence to skip as a unit + // ("%%" in pyformat text); empty when not applicable. + doubledToken string +} + +var ( + questionPlaceholders = placeholderStyle{ + token: "?", + joinExpr: `",".join("?" * len(%s)) or "NULL"`, + numbered: true, + } + pyformatPlaceholders = placeholderStyle{ + token: "%s", + // A tuple repeat, not a string repeat: join iterates strings + // per-character, which only works for one-char placeholders. + joinExpr: `",".join(("%%s",) * len(%s)) or "NULL"`, + backslashEscapes: true, + hashComments: true, + dashCommentNeedsGap: true, + backtickIdents: true, + versionComments: true, + doubledToken: "%%", + } +) + // expandParams returns the Python argument expressions for a query's parameters. // Bundled Params classes (query_parameter_limit) are expanded into their fields // ("params.a, params.b") so drivers receive positional values. :copyfrom params // are never passed through here - writeCopyFromBody builds its own records list. func expandParams(query model.Query) []string { - return expandParamsImpl(query, false, nil) + return expandParamsImpl(query, false, nil, questionPlaceholders) } // expandParamsFlattenSlices additionally star-unpacks sqlc.slice parameters // ("*ids"), so after runtime placeholder expansion every "?" binds one element. func expandParamsFlattenSlices(query model.Query) []string { - return expandParamsImpl(query, true, nil) + return expandParamsImpl(query, true, nil, questionPlaceholders) } // expandParamsFlattenSlicesWire is expandParamsFlattenSlices for drivers that // additionally convert parameters to their wire type inline. func expandParamsFlattenSlicesWire(query model.Query, wire wireConvertFunc) []string { - return expandParamsImpl(query, true, wire) + return expandParamsImpl(query, true, wire, questionPlaceholders) +} + +// expandParamsPyformat is the MySQL variant: wire conversion plus the +// pyformat placeholder style of the rewritten SQL text. +func expandParamsPyformat(query model.Query, wire wireConvertFunc) []string { + return expandParamsImpl(query, true, wire, pyformatPlaceholders) } -func expandParamsImpl(query model.Query, flattenSlices bool, wire wireConvertFunc) []string { +func expandParamsImpl(query model.Query, flattenSlices bool, wire wireConvertFunc, ph placeholderStyle) []string { type part struct { expr string // slice is the raw marker name for slice params, "" otherwise. @@ -104,7 +172,7 @@ func expandParamsImpl(query model.Query, flattenSlices bool, wire wireConvertFun reused := false for _, p := range parts { - if p.slice != "" && sliceMarkerCount(query, p.slice) > 1 { + if p.slice != "" && sliceMarkerCount(query, p.slice, ph) > 1 { reused = true break @@ -131,7 +199,7 @@ func expandParamsImpl(query model.Query, flattenSlices bool, wire wireConvertFun starred[p.slice] = p.expr } } - if ordered, ok := orderByPlaceholders(query.SQL, plain, starred); ok { + if ordered, ok := orderByPlaceholders(query.SQL, plain, starred, ph); ok { return ordered } @@ -140,7 +208,7 @@ func expandParamsImpl(query model.Query, flattenSlices bool, wire wireConvertFun out := make([]string, 0, len(parts)) for _, p := range parts { if p.slice != "" { - for range sliceMarkerCount(query, p.slice) { + for range sliceMarkerCount(query, p.slice, ph) { out = append(out, p.expr) } @@ -156,10 +224,11 @@ func expandParamsImpl(query model.Query, flattenSlices bool, wire wireConvertFun // placeholder sequence: plain expressions fill "?" slots in order, and every // marker occurrence gets its slice's starred copy. Reports false when the SQL // does not account for exactly the given arguments. -func orderByPlaceholders(sql string, plain []string, starred map[string]string) ([]string, bool) { - seq := placeholderSequence(sql) +func orderByPlaceholders(sql string, plain []string, starred map[string]string, ph placeholderStyle) ([]string, bool) { + seq := placeholderSequence(sql, ph) out := make([]string, 0, len(seq)) next := 0 + used := make(map[string]struct{}, len(starred)) for _, name := range seq { if name == "" { if next >= len(plain) { @@ -170,13 +239,16 @@ func orderByPlaceholders(sql string, plain []string, starred map[string]string) continue } - expr, ok := starred[name] - if !ok { + expr, found := starred[name] + if !found { return nil, false } + used[name] = struct{}{} out = append(out, expr) } - if next != len(plain) { + // A slice whose marker the scan never saw must fail too, or a truncated + // scan would silently drop its arguments instead of using the fallback. + if next != len(plain) || len(used) != len(starred) { return nil, false } @@ -184,21 +256,28 @@ func orderByPlaceholders(sql string, plain []string, starred map[string]string) } // placeholderSequence scans the SQL for bindable placeholders in text order: -// the raw slice name for a /*SLICE:name*/? marker, "" for a plain (possibly -// numbered) "?". String literals, quoted identifiers, and comments are -// skipped, so a "?" inside them never counts as a placeholder. -func placeholderSequence(sql string) []string { +// the raw slice name for a /*SLICE:name*/ marker, "" for a plain +// (possibly numbered) token. String literals, quoted identifiers, and +// comments are skipped, so a token inside them never counts as a +// placeholder. The lexing rules follow the style: MySQL text adds backslash +// escapes, backtick identifiers, "#" comments, the "--"+whitespace rule, +// and the "%%" literal escape. +func placeholderSequence(sql string, ph placeholderStyle) []string { var seq []string for i := 0; i < len(sql); { rest := sql[i:] switch { case strings.HasPrefix(rest, "/*SLICE:"): - end := strings.Index(rest, "*/?") + end := strings.Index(rest, "*/"+ph.token) if end == -1 { return seq } seq = append(seq, rest[len("/*SLICE:"):end]) - i += end + len("*/?") + i += end + len("*/") + len(ph.token) + case ph.versionComments && strings.HasPrefix(rest, "/*!"): + // The body is live SQL: keep scanning it; the closing */ passes + // through the default case as ordinary text. + i += len("/*!") case strings.HasPrefix(rest, "/*"): end := strings.Index(rest[len("/*"):], "*/") if end == -1 { @@ -206,35 +285,38 @@ func placeholderSequence(sql string) []string { } i += len("/*") + end + len("*/") case strings.HasPrefix(rest, "--"): + if ph.dashCommentNeedsGap && len(rest) > 2 && rest[2] > ' ' { + // MySQL: "--x" is double unary minus, not a comment. Advance + // one byte, not two: in an odd-length dash run the comment + // starts mid-run, and the rewriter re-examines every + // position the same way. + i++ + + continue + } end := strings.IndexByte(rest, '\n') if end == -1 { return seq } i += end + 1 - case rest[0] == '\'' || rest[0] == '"': - quote := rest[0] - j := i + 1 - for j < len(sql) { - if sql[j] != quote { - j++ - - continue - } - if j+1 < len(sql) && sql[j+1] == quote { - // A doubled quote is an escape, not the end. - j += 2 - - continue - } - - break + case ph.hashComments && rest[0] == '#': + end := strings.IndexByte(rest, '\n') + if end == -1 { + return seq } - i = j + 1 - case rest[0] == '?': + i += end + 1 + case rest[0] == '\'' || rest[0] == '"' || (ph.backtickIdents && rest[0] == '`'): + // Backslash escapes never apply inside backticks. + i = scanQuotedRegion(sql, i, ph.backslashEscapes && rest[0] != '`') + case ph.doubledToken != "" && strings.HasPrefix(rest, ph.doubledToken): + i += len(ph.doubledToken) + case strings.HasPrefix(rest, ph.token): seq = append(seq, "") - i++ - for i < len(sql) && sql[i] >= '0' && sql[i] <= '9' { - i++ + i += len(ph.token) + if ph.numbered { + for i < len(sql) && sql[i] >= '0' && sql[i] <= '9' { + i++ + } } default: i++ @@ -244,6 +326,29 @@ func placeholderSequence(sql string) []string { return seq } +// scanQuotedRegion returns the index just past the closing quote of the +// quoted region starting at sql[i]. A doubled quote is an escape; with +// escapes, a backslash escapes the following byte. An unterminated region +// consumes the rest of the input. +func scanQuotedRegion(sql string, i int, escapes bool) int { + quote := sql[i] + j := i + 1 + for j < len(sql) { + switch { + case escapes && sql[j] == '\\' && j+1 < len(sql): + j += 2 + case sql[j] != quote: + j++ + case j+1 < len(sql) && sql[j+1] == quote: + j += 2 + default: + return j + 1 + } + } + + return j +} + type sliceParam struct { // marker is the raw sqlc.slice name inside the /*SLICE:name*/? placeholder. marker string @@ -251,17 +356,18 @@ type sliceParam struct { expr string } -// sliceMarker returns the placeholder sqlc leaves in the SQL for a slice name. -func sliceMarker(name string) string { - return "/*SLICE:" + name + "*/?" +// sliceMarker returns the placeholder left in the SQL for a slice name: +// sqlc's raw marker for "?" styles, its rewritten form for pyformat. +func sliceMarker(name string, ph placeholderStyle) string { + return "/*SLICE:" + name + "*/" + ph.token } // sliceMarkerCount reports how often a slice parameter's placeholder occurs in // the query. sqlc merges same-named sqlc.slice uses into ONE parameter but // keeps a marker per use site, so each occurrence needs its own expansion and // its own copy of the arguments. Clamped to 1 for queries without the marker. -func sliceMarkerCount(query model.Query, name string) int { - if count := strings.Count(query.SQL, sliceMarker(name)); count > 1 { +func sliceMarkerCount(query model.Query, name string, ph placeholderStyle) int { + if count := strings.Count(query.SQL, sliceMarker(name, ph)); count > 1 { return count } @@ -330,7 +436,7 @@ func writeQueryDocstring(body *writer.CodeWriter, d Driver, cfg *config.Config, } args := make([]writer.DocArg, 0, len(query.Params)) for _, param := range query.Params { - if param.IsEmpty() { + if param.IsEmpty() || param.Repeated { continue } extra := "" @@ -398,3 +504,60 @@ func writeExecRowsReturn(body *writer.CodeWriter, config *config.Config, indent body.WriteIndentedLine(indent, "return int(n) if (p := r.split()) and (n := p[-1]).isdigit() else 0") } } + +// writeSliceExpansion writes the runtime replacement of every sqlc.slice +// placeholder - one placeholder per element, or "NULL" for an empty sequence +// so that "IN (NULL)" matches no rows - and returns the expression holding +// the final SQL: a local "sql" variable, or the untouched constant without +// slices. +func writeSliceExpansion(body *writer.CodeWriter, indent int, query model.Query, ph placeholderStyle) string { + params := sliceParams(query) + if len(params) == 0 { + return query.ConstantName + } + src := query.ConstantName + for _, param := range params { + args := []string{ + writer.PyQuote(sliceMarker(param.marker, ph)), + fmt.Sprintf(ph.joinExpr, param.expr), + } + // A reused slice has one marker per use site: replace them all, with + // the flattening param expansion supplying a copy of the args for each. + if sliceMarkerCount(query, param.marker, ph) == 1 { + args = append(args, "1") + } + body.WriteWrappedCall(indent, "sql = "+src+".replace(", args, ")") + src = "sql" + } + + return "sql" +} + +// writeCursorCall writes stmtHead+argsSegment+stmtTail on one line, hoisting a +// too-long parameter tuple into a local _args variable first so the statement +// stays within the line limit. parts are the already-expanded (and, for wire- +// converting drivers, already-converted) argument expressions. Shared by the +// sqlite, turso, and MySQL drivers. +func writeCursorCall(body *writer.CodeWriter, indent int, parts []string, stmtHead, stmtTail string) { + segment := "" + switch { + case len(parts) == 1: + segment = fmt.Sprintf(", (%s,)", parts[0]) + case len(parts) > 1: + segment = fmt.Sprintf(", (%s)", strings.Join(parts, ", ")) + } + + stmt := stmtHead + segment + stmtTail + if body.FitsLine(indent, stmt) { + body.WriteIndentedLine(indent, stmt) + + return + } + + body.WriteIndentedLine(indent, "sql_args = (") + for _, part := range parts { + body.WriteIndentedLine(indent+1, part+",") + } + body.WriteIndentedLine(indent, ")") + body.WriteIndentedLine(indent, stmtHead+", sql_args"+stmtTail) +} diff --git a/internal/driver/common_test.go b/internal/driver/common_test.go index 3c06db3c..b65b36a2 100644 --- a/internal/driver/common_test.go +++ b/internal/driver/common_test.go @@ -83,6 +83,21 @@ func TestWriteFuncSignature(t *testing.T) { want: "def list_ids(conn: ConnectionLike, author_id: int) -> QueryResults[int]:\n", wantConn: commonConnExpr, }, + { + name: "repeated mysql param appears once in the signature", + drv: newMysqlDriver("pymysql", false), + query: model.Query{ + Cmd: metadata.CmdExec, + FuncName: "rename_author", + Params: []model.QueryValue{ + {Name: "n", Type: model.PyType{Type: "str", SQLType: "text"}}, + {Name: "n", Type: model.PyType{Type: "str", SQLType: "text"}, Repeated: true}, + }, + }, + annotation: "None", + want: "def rename_author(conn: pymysql.Connection, *, n: str) -> None:\n", + wantConn: commonConnExpr, + }, { name: "sync driver has no async prefix", drv: newSqliteDriver("sqlite3", false), @@ -355,11 +370,134 @@ func TestPlaceholderSequence(t *testing.T) { sql: "WHERE a = ? AND s = 'open?", want: []string{""}, }, + // The question style must keep sqlite's lexing rules where they + // differ from the pyformat flags: -- comments need no gap, "#", + // backticks, and backslashes are ordinary text. + { + name: "dash dash glued to text is still a comment", + sql: "SELECT a--1 dead ?\nFROM t WHERE b = ?", + want: []string{""}, + }, + { + name: "hash is not a comment", + sql: "WHERE x = ? # not a comment ?", + want: []string{"", ""}, + }, + { + name: "backtick is ordinary text", + sql: "SELECT `weird?col` FROM t WHERE a = ?", + want: []string{"", ""}, + }, + { + name: "backslash does not escape a quote", + sql: `WHERE s = 'a\' AND b = ?`, + want: []string{""}, + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - if got := placeholderSequence(tc.sql); !slices.Equal(got, tc.want) { + if got := placeholderSequence(tc.sql, questionPlaceholders); !slices.Equal(got, tc.want) { + t.Errorf("placeholderSequence() = %q, want %q", got, tc.want) + } + }) + } +} + +func TestPlaceholderSequencePyformat(t *testing.T) { + t.Parallel() + cases := []struct { + name string + sql string + want []string + }{ + { + name: "percent-s slots in text order", + sql: "WHERE a = %s AND b = %s", + want: []string{"", ""}, + }, + { + name: "doubled percent is a literal, not a slot", + sql: "WHERE a %% 2 = 0 AND b = %s", + want: []string{""}, + }, + { + name: "lone percent is not a slot", + sql: "WHERE a % 2 = 0 AND b = %s", + want: []string{""}, + }, + { + name: "slice marker yields the name", + sql: "WHERE id IN (/*SLICE:ids*/%s)", + want: []string{"ids"}, + }, + { + name: "slots inside string literals do not count despite backslash escapes", + sql: "WHERE s = 'It\\'s %s' AND t = \"quote \\\" %s\" AND a = %s", + want: []string{""}, + }, + { + name: "backticked identifier swallows its slot", + sql: "SELECT `weird %s col` FROM t WHERE a = %s", + want: []string{""}, + }, + { + // If backslash escaped the closing backtick, the identifier would + // swallow the rest of the input and the slot with it. + name: "backslash is not an escape inside backticks", + sql: "SELECT `dir\\` FROM t WHERE a = %s", + want: []string{""}, + }, + { + name: "hash comment is dead to the newline", + sql: "WHERE a = %s # dead %s\nAND b = %s", + want: []string{"", ""}, + }, + { + name: "dash dash with whitespace starts a comment", + sql: "WHERE a = %s -- x %s\nAND b = %s", + want: []string{"", ""}, + }, + { + name: "dash dash without whitespace is arithmetic, slot stays live", + sql: "WHERE a = b--1 + %s", + want: []string{""}, + }, + { + name: "block comment hides its slot", + sql: "WHERE a = %s /* %s */ AND b = %s", + want: []string{"", ""}, + }, + { + name: "unterminated string swallows the rest", + sql: "WHERE a = %s AND s = 'open %s", + want: []string{""}, + }, + { + name: "unterminated hash comment swallows the rest", + sql: "WHERE a = %s # tail %s", + want: []string{""}, + }, + { + name: "version comment body is live", + sql: "SELECT id /*! WHERE a = %s */ AND b = %s", + want: []string{"", ""}, + }, + { + name: "odd dash run still starts a comment", + sql: "WHERE a = %s --------- don't edit\nAND id IN (/*SLICE:ids*/%s)", + want: []string{"", "ids"}, + }, + { + name: "numbered question placeholder is not a slot", + sql: "WHERE a = ?1 AND b = %s", + want: []string{""}, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if got := placeholderSequence(tc.sql, pyformatPlaceholders); !slices.Equal(got, tc.want) { t.Errorf("placeholderSequence() = %q, want %q", got, tc.want) } }) @@ -467,6 +605,33 @@ func TestWriteQueryDocstring(t *testing.T) { retType: "models.Author", want: "", }, + { + name: "repeated mysql param is documented once", + convention: config.DocstringConventionGoogle, + query: model.Query{ + Cmd: metadata.CmdExec, + QueryName: "RenameAuthor", + SQL: "UPDATE authors SET name = %s WHERE name = %s", + Params: []model.QueryValue{ + {Name: "n", Type: model.PyType{Type: "str", SQLType: "text"}}, + {Name: "n", Type: model.PyType{Type: "str", SQLType: "text"}, Repeated: true}, + }, + }, + want: strings.Join([]string{ + " \"\"\"Execute SQL query with `name: RenameAuthor :exec`.", + "", + " ```sql", + " UPDATE authors SET name = %s WHERE name = %s", + " ```", + "", + " Args:", + " conn:", + " Connection object of type `ConnectionLike` used to execute the query.", + " n: str.", + " \"\"\"", + "", + }, "\n"), + }, { name: "google one with conn and sql", convention: config.DocstringConventionGoogle, @@ -801,6 +966,124 @@ func TestExpandParamsFlattenSlicesWire(t *testing.T) { } } +func TestSliceMarkerStyles(t *testing.T) { + t.Parallel() + markers := []struct { + name string + ph placeholderStyle + want string + }{ + {name: "question marker keeps sqlc's raw form", ph: questionPlaceholders, want: "/*SLICE:ids*/?"}, + {name: "pyformat marker uses the rewritten token", ph: pyformatPlaceholders, want: "/*SLICE:ids*/%s"}, + } + for _, tc := range markers { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if got := sliceMarker("ids", tc.ph); got != tc.want { + t.Errorf("sliceMarker(%q) = %q, want %q", "ids", got, tc.want) + } + }) + } + counts := []struct { + name string + sql string + want int + }{ + { + name: "two pyformat markers count both", + sql: "WHERE id IN (/*SLICE:ids*/%s) OR ref_id IN (/*SLICE:ids*/%s)", + want: 2, + }, + { + name: "missing marker clamps to one", + sql: "WHERE id = %s", + want: 1, + }, + } + for _, tc := range counts { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + query := model.Query{SQL: tc.sql} + if got := sliceMarkerCount(query, "ids", pyformatPlaceholders); got != tc.want { + t.Errorf("sliceMarkerCount(%q) = %d, want %d", tc.sql, got, tc.want) + } + }) + } +} + +// mysqlTestWire is a stand-in wire conversion for the pyformat expansion +// tests: blobs go over the wire as bytes, everything else binds natively. +func mysqlTestWire(sqlType string) (string, bool) { + if sqlType == "blob" { + return "bytes(%s)", true + } + + return "", false +} + +func TestExpandParamsPyformat(t *testing.T) { + t.Parallel() + cases := []struct { + name string + query model.Query + want []string + }{ + { + name: "wire conversion wraps the blob param", + query: model.Query{ + Params: []model.QueryValue{ + {Name: "data", Type: model.PyType{Type: "memoryview", SQLType: "blob"}}, + {Name: "name", Type: model.PyType{Type: "str", SQLType: "text"}}, + }, + }, + want: []string{"bytes(data)", "name"}, + }, + { + name: "slice param with pyformat marker is star-unpacked", + query: model.Query{ + SQL: "SELECT id FROM t WHERE id IN (/*SLICE:ids*/%s)", + Params: []model.QueryValue{ + {Name: "ids", Type: model.PyType{Type: "int", SQLType: "integer", IsList: true, SqlcSliceName: "ids"}}, + }, + }, + want: []string{"*ids"}, + }, + { + name: "reused slice binds a copy per marker between plain params", + query: model.Query{ + SQL: "SELECT id FROM t WHERE a = %s AND id IN (/*SLICE:ids*/%s) OR id IN (/*SLICE:ids*/%s) AND b = %s", + Params: []model.QueryValue{ + {Name: "a", Type: model.PyType{Type: "int", SQLType: "integer"}}, + {Name: "ids", Type: model.PyType{Type: "int", SQLType: "integer", IsList: true, SqlcSliceName: "ids"}}, + {Name: "b", Type: model.PyType{Type: "str", SQLType: "text"}}, + }, + }, + want: []string{"a", "*ids", "*ids", "b"}, + }, + { + // The parameter array puts the merged slice first, but the SQL + // binds a before the two use sites: text order must win. + name: "reused slice interleaves plain params in SQL text order", + query: model.Query{ + SQL: "SELECT id FROM t WHERE a = %s AND id IN (/*SLICE:ids*/%s) OR ref_id IN (/*SLICE:ids*/%s)", + Params: []model.QueryValue{ + {Name: "ids", Type: model.PyType{Type: "int", SQLType: "integer", IsList: true, SqlcSliceName: "ids"}}, + {Name: "a", Type: model.PyType{Type: "int", SQLType: "integer"}}, + }, + }, + want: []string{"a", "*ids", "*ids"}, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if got := expandParamsPyformat(tc.query, mysqlTestWire); !slices.Equal(got, tc.want) { + t.Errorf("expandParamsPyformat() = %q, want %q", got, tc.want) + } + }) + } +} + func TestFindTursoConversion(t *testing.T) { t.Parallel() cases := []struct { diff --git a/internal/driver/conversion.go b/internal/driver/conversion.go index dcceab1a..1a732816 100644 --- a/internal/driver/conversion.go +++ b/internal/driver/conversion.go @@ -14,7 +14,11 @@ const ( sqlTypeTimestamp = "timestamp" sqlTypeDecimal = "decimal" sqlTypeBlob = "blob" - pyDatetimeDate = "datetime.date" + // wireBytes converts a memoryview parameter to bytes for drivers that + // cannot bind memoryview (pyturso rejects it; the PyMySQL encoders + // silently stringify it). + wireBytes = "bytes(%s)" + pyDatetimeDate = "datetime.date" ) // asyncpgConversions lists SQL types that need explicit Python-side diff --git a/internal/driver/driver.go b/internal/driver/driver.go index fceae5f7..e3930315 100644 --- a/internal/driver/driver.go +++ b/internal/driver/driver.go @@ -73,6 +73,10 @@ func New(conf *config.Config) (Driver, error) { return newTursoDriver(true, conf.Speedups), nil case config.SQLDriverTursoSync: return newTursoDriver(false, conf.Speedups), nil + case config.SQLDriverPymysql: + return newMysqlDriver("pymysql", false), nil + case config.SQLDriverAsyncmy: + return newMysqlDriver("asyncmy", true), nil default: return nil, fmt.Errorf("unsupported driver: %s", conf.SqlDriver) } diff --git a/internal/driver/driver_test.go b/internal/driver/driver_test.go index dbd6e1ac..cea0f0dd 100644 --- a/internal/driver/driver_test.go +++ b/internal/driver/driver_test.go @@ -22,6 +22,8 @@ func TestNew(t *testing.T) { {name: "sqlite3", sqlDriver: config.SQLDriverSQLite, wantName: "sqlite3", wantAsync: false}, {name: "turso_async", sqlDriver: config.SQLDriverTursoAsync, wantName: "turso.aio", wantAsync: true}, {name: "turso_sync", sqlDriver: config.SQLDriverTursoSync, wantName: "turso", wantAsync: false}, + {name: "pymysql", sqlDriver: config.SQLDriverPymysql, wantName: "pymysql", wantAsync: false}, + {name: "asyncmy", sqlDriver: config.SQLDriverAsyncmy, wantName: "asyncmy", wantAsync: true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/driver/mysql_base.go b/internal/driver/mysql_base.go new file mode 100644 index 00000000..a76b2f83 --- /dev/null +++ b/internal/driver/mysql_base.go @@ -0,0 +1,255 @@ +package driver + +import ( + "fmt" + + "github.com/rayakame/sqlc-gen-better-python/internal/config" + "github.com/rayakame/sqlc-gen-better-python/internal/model" + "github.com/rayakame/sqlc-gen-better-python/internal/types" + "github.com/rayakame/sqlc-gen-better-python/internal/writer" + "github.com/sqlc-dev/plugin-sdk-go/metadata" +) + +// mysqlResultType is the row type of the default (tuple) cursors of both +// MySQL modules. +const mysqlResultType = "tuple[typing.Any, ...]" + +// mysqlBinaryTypes are the SQL types carried as raw bytes on the wire. +var mysqlBinaryTypes = map[string]struct{}{ + sqlTypeBlob: {}, + "binary": {}, + "varbinary": {}, + "tinyblob": {}, + "mediumblob": {}, + "longblob": {}, + "bit": {}, +} + +// mysqlNeedsConversion reports the SQL types whose values convert inline. +// The PyMySQL family returns datetime/date/timedelta/Decimal natively, so +// only the binary family (bytes -> memoryview) and the tinyint spellings +// (int -> bool for tinyint(1) columns) remain. No decode templates are +// needed: RowBuilder's constructor-call fallback produces memoryview(...) +// and bool(...) - a plain tinyint column gets a redundant but harmless +// int(...) wrap, the price of keying conversions off the SQL type alone. +func mysqlNeedsConversion(sqlType string) bool { + if _, found := mysqlBinaryTypes[sqlType]; found { + return true + } + switch sqlType { + case "tinyint", types.Bool, types.Boolean: + return true + default: + return false + } +} + +// mysqlWire converts parameters the drivers cannot bind natively: the +// PyMySQL encoder table has no memoryview entry and silently stringifies +// one, so binary values bind as bytes. +func mysqlWire(sqlType string) (string, bool) { + if _, found := mysqlBinaryTypes[sqlType]; found { + return wireBytes, true + } + + return "", false +} + +// mysqlBase is the complete driver implementation for both MySQL modules - +// pymysql (sync) and asyncmy (async). All emission differences between the +// two are derived from moduleName and the async flag. +type mysqlBase struct { + moduleName string // "pymysql" or "asyncmy" + async bool + rows *RowBuilder +} + +var _ Driver = (*mysqlBase)(nil) + +func newMysqlDriver(moduleName string, async bool) *mysqlBase { + return &mysqlBase{ + moduleName: moduleName, + async: async, + rows: newRowBuilder(mysqlNeedsConversion), + } +} + +// Name returns the Python module name ("pymysql" or "asyncmy"). +func (mb *mysqlBase) Name() string { return mb.moduleName } + +// ConnType returns the connection type annotation, e.g. "pymysql.Connection". +func (mb *mysqlBase) ConnType() string { return mb.moduleName + ".Connection" } + +// IsAsync reports whether this is the asyncmy (async) driver. +func (mb *mysqlBase) IsAsync() bool { return mb.async } + +// SupportsCommand returns if the driver supports the command; the set +// matches the sqlite drivers (:copyfrom has no honest MySQL-driver +// equivalent - LOAD DATA LOCAL INFILE is not expressible through them). +func (mb *mysqlBase) SupportsCommand(cmd string) bool { + switch cmd { + case metadata.CmdExec, + metadata.CmdExecResult, + metadata.CmdExecLastId, + metadata.CmdExecRows, + metadata.CmdOne, + metadata.CmdMany: + return true + default: + return false + } +} + +// TypeCheckingHook returns nil (no type-checking hook for MySQL drivers). +func (mb *mysqlBase) TypeCheckingHook() []string { + return nil +} + +// NeedsConversion reports whether a SQL type needs runtime conversion. +func (mb *mysqlBase) NeedsConversion(sqlType string) bool { + return mysqlNeedsConversion(sqlType) +} + +// ConvertsInline reports whether a SQL type converts inline in decode code; +// MySQL conversions are all inline (the drivers' converter maps are +// per-connection, so generated modules cannot register anything). +func (mb *mysqlBase) ConvertsInline(sqlType string) bool { + return mysqlNeedsConversion(sqlType) +} + +// WriteConversionSetup is a no-op for MySQL: nothing runs at module import +// time. +func (mb *mysqlBase) WriteConversionSetup(_ *writer.CodeWriter, _ *config.Config, _ []model.Query) bool { + return false +} + +// WriteQueryResultsClass writes the QueryResults class for :many queries, in +// its sync (pymysql) or async (asyncmy) variant. Neither module's connection +// has an execute method, so both paths open a cursor first; iteration steps +// with fetchone like the turso drivers, keeping the state machine identical +// in both flavors. +func (mb *mysqlBase) WriteQueryResultsClass(body *writer.CodeWriter) string { + awaitKw, nextDef, stopExc, article := "", defNextSync, stopIteration, "a " + if mb.async { + awaitKw, nextDef, stopExc, article = awaitPrefix, defNextAsync, stopAsyncIteration, "an " + } + + body.QueryResults.WriteQueryResultsClassHeaderNoIterator(mb.ConnType(), []string{ + "self._cursor: " + mb.cursorType() + " | None = None", + }, mysqlResultType, mb.async) + fetchLines := []string{ + "cur = self._conn.cursor()", + awaitKw + "cur.execute(self._sql, self._args)", + fmt.Sprintf("result = %scur.fetchall()", awaitKw), + awaitKw + "cur.close()", + decodeRowsExpr, + } + if mb.async { + body.QueryResults.WriteQueryResultsAwaitFunction(fetchLines) + } else { + body.QueryResults.WriteQueryResultsCallFunction(fetchLines) + } + body.NewLine() + body.WriteIndentedLine(1, nextDef+"(self) -> T:") + body.WriteQueryResultsNextDocstring(article+mb.moduleName+" cursor", mb.async) + body.WriteIndentedLine(2, "if self._cursor is None:") + body.WriteIndentedLine(3, "self._cursor = self._conn.cursor()") + body.WriteIndentedLine(3, awaitKw+"self._cursor.execute(self._sql, self._args)") + body.WriteIndentedLine(2, "record = "+awaitKw+"self._cursor.fetchone()") + body.WriteIndentedLine(2, "if record is None:") + body.WriteIndentedLine(3, "self._cursor = None") + body.WriteIndentedLine(3, "raise "+stopExc) + body.WriteIndentedLine(2, "return self._decode_hook(record)") + + return queryResultsClassName +} + +func (mb *mysqlBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Config, query model.Query, indent int) { + var annotation, docRetType string + switch query.Cmd { + case metadata.CmdExec: + annotation, docRetType = query.Returns.Type.Print(), "" + case metadata.CmdExecResult: + annotation, docRetType = mb.cursorType(), mb.cursorType() + case metadata.CmdExecRows, metadata.CmdExecLastId: + annotation, docRetType = query.Returns.Type.Print(), query.Returns.Type.Type + case metadata.CmdOne: + annotation, docRetType = query.Returns.Type.PrintOptional(), query.Returns.Type.Type + case metadata.CmdMany: + annotation, docRetType = "QueryResults["+query.Returns.Type.Print()+"]", query.Returns.Type.Print() + } + + conn := writeFuncSignature(body, mb, config, indent, query, annotation) + + indent++ + writeQueryDocstring(body, mb, config, query, indent, docRetType) + // :many delays this until after the decode hook, whose trailing blank + // line keeps the assignment from touching the nested def (ruff E306). + sqlRef := query.ConstantName + if query.Cmd != metadata.CmdMany { + sqlRef = writeSliceExpansion(body, indent, query, pyformatPlaceholders) + } + + // Neither MySQL module has conn.execute: every body opens a cursor. The + // with-block closes it; :execresult hands the open cursor to the caller + // instead. + withStmt, execKw := "with ", "" + if mb.async { + withStmt, execKw = "async with ", awaitPrefix + } + withStmt += conn + ".cursor() as cur:" + parts := expandParamsPyformat(query, mysqlWire) + writeExec := func(indent int, prefix string) { + writeCursorCall(body, indent, parts, prefix+execKw+"cur.execute("+sqlRef, ")") + } + + switch query.Cmd { + case metadata.CmdExec: + body.WriteIndentedLine(indent, withStmt) + writeExec(indent+1, "") + + case metadata.CmdExecResult: + body.WriteIndentedLine(indent, "cur = "+conn+".cursor()") + writeExec(indent, "") + body.WriteIndentedLine(indent, "return cur") + + case metadata.CmdExecRows: + // execute() returns the affected-row count typed int in both + // modules' stubs; asyncmy's cursor stub types rowcount as object, + // which pyright strict rejects as a return value. + body.WriteIndentedLine(indent, withStmt) + writeExec(indent+1, "return ") + + case metadata.CmdExecLastId: + // lastrowid is 0 (never None) when the statement inserted nothing; + // AUTO_INCREMENT ids start at 1, so 0 maps to the documented None. + body.WriteIndentedLine(indent, withStmt) + writeExec(indent+1, "") + body.WriteIndentedLine(indent+1, "return cur.lastrowid or None") + + case metadata.CmdOne: + body.WriteIndentedLine(indent, withStmt) + writeExec(indent+1, "") + body.WriteIndentedLine(indent+1, fmt.Sprintf("row = %scur.fetchone()", execKw)) + body.WriteIndentedLine(indent, "if row is None:") + body.WriteIndentedLine(indent+1, "return None") + + if query.Returns.IsStruct() { + mb.rows.WriteStructReturn(body, indent, query.Returns) + } else { + mb.rows.WriteScalarReturn(body, indent, query.Returns) + } + + case metadata.CmdMany: + decodeHook := mb.rows.WriteDecodeHook(body, indent, query, mysqlResultType) + sqlRef = writeSliceExpansion(body, indent, query, pyformatPlaceholders) + manyArgs := append([]string{conn, sqlRef, decodeHook}, parts...) + // Deliberately unsubscripted: QueryResults[T](...) would go through + // typing's _GenericAlias.__call__ on every invocation (~10x call + // overhead) for zero benefit - the return annotation carries the type. + body.WriteWrappedCall(indent, "return QueryResults(", manyArgs, ")") + } +} + +// cursorType returns the annotation of the default cursor class. +func (mb *mysqlBase) cursorType() string { return mb.moduleName + ".cursors.Cursor" } diff --git a/internal/driver/mysql_test.go b/internal/driver/mysql_test.go new file mode 100644 index 00000000..088fb6f2 --- /dev/null +++ b/internal/driver/mysql_test.go @@ -0,0 +1,743 @@ +package driver_test + +import ( + "strings" + "testing" + + "github.com/rayakame/sqlc-gen-better-python/internal/config" + "github.com/rayakame/sqlc-gen-better-python/internal/driver" + "github.com/rayakame/sqlc-gen-better-python/internal/model" + "github.com/rayakame/sqlc-gen-better-python/internal/utils" + "github.com/rayakame/sqlc-gen-better-python/internal/writer" + "github.com/sqlc-dev/plugin-sdk-go/metadata" +) + +func mysqlTestConfig(async bool) *config.Config { + sqlDriver := config.SQLDriverPymysql + if async { + sqlDriver = config.SQLDriverAsyncmy + } + + return &config.Config{ + SqlDriver: sqlDriver, + EmitDocstrings: config.DocstringConventionNone, + EmitDocstringsSQL: utils.ToPtr(true), + IndentChar: " ", + CharsPerIndentLevel: 4, + OmitKwargsLimit: 8, + } +} + +func newMysql(t *testing.T, async bool) driver.Driver { + t.Helper() + d, err := driver.New(mysqlTestConfig(async)) + if err != nil { + t.Fatalf("driver.New() error = %v", err) + } + + return d +} + +func mysqlUserReturn() model.QueryValue { + return model.QueryValue{ + Table: &model.Table{ + Name: "User", + Columns: []model.Column{ + {Name: "id_", Type: model.PyType{Type: "int", SQLType: "int"}}, + {Name: "avatar", Type: model.PyType{Type: "memoryview", SQLType: "blob"}}, + {Name: "active", Type: model.PyType{Type: "bool", SQLType: "tinyint"}}, + {Name: "mood", Type: model.PyType{Type: "enums.Mood", SQLType: "enum", IsEnum: true}}, + {Name: "name", Type: model.PyType{Type: "str", SQLType: "varchar"}}, + }, + }, + Type: model.PyType{Type: "models.User"}, + } +} + +// mysqlUserDecode is the row-decoding expression for mysqlUserReturn: the +// binary family becomes memoryview, tinyint (the tinyint(1) bool spelling) +// wraps in bool, enums wrap in their class, everything else passes through. +const mysqlUserDecode = "models.User(id_=row[0], avatar=memoryview(row[1]), active=bool(row[2]), mood=enums.Mood(row[3]), name=row[4])" + +func TestMysqlDriverMetadata(t *testing.T) { + t.Parallel() + sync := newMysql(t, false) + if got := sync.Name(); got != "pymysql" { + t.Errorf("sync Name() = %q, want %q", got, "pymysql") + } + if got := sync.ConnType(); got != "pymysql.Connection" { + t.Errorf("sync ConnType() = %q, want %q", got, "pymysql.Connection") + } + if sync.IsAsync() { + t.Error("sync IsAsync() = true, want false") + } + if got := sync.TypeCheckingHook(); got != nil { + t.Errorf("sync TypeCheckingHook() = %v, want nil", got) + } + + async := newMysql(t, true) + if got := async.Name(); got != "asyncmy" { + t.Errorf("async Name() = %q, want %q", got, "asyncmy") + } + if got := async.ConnType(); got != "asyncmy.Connection" { + t.Errorf("async ConnType() = %q, want %q", got, "asyncmy.Connection") + } + if !async.IsAsync() { + t.Error("async IsAsync() = false, want true") + } + if got := async.TypeCheckingHook(); got != nil { + t.Errorf("async TypeCheckingHook() = %v, want nil", got) + } +} + +func TestMysqlSupportsCommand(t *testing.T) { + t.Parallel() + for _, async := range []bool{false, true} { + d := newMysql(t, async) + for cmd, want := range map[string]bool{ + metadata.CmdExec: true, + metadata.CmdExecResult: true, + metadata.CmdExecLastId: true, + metadata.CmdExecRows: true, + metadata.CmdOne: true, + metadata.CmdMany: true, + metadata.CmdCopyFrom: false, + metadata.CmdBatchExec: false, + metadata.CmdBatchMany: false, + metadata.CmdBatchOne: false, + } { + if got := d.SupportsCommand(cmd); got != want { + t.Errorf("async=%v SupportsCommand(%q) = %v, want %v", async, cmd, got, want) + } + } + } +} + +func TestMysqlConversions(t *testing.T) { + t.Parallel() + d := newMysql(t, false) + // Everything converts inline: the drivers' converter maps are + // per-connection, so both checks agree on the same type set. + for sqlType, want := range map[string]bool{ + "blob": true, "binary": true, "varbinary": true, "tinyblob": true, + "mediumblob": true, "longblob": true, "bit": true, + "tinyint": true, "bool": true, "boolean": true, + "text": false, "int": false, "datetime": false, "json": false, + "decimal": false, "varchar": false, + } { + if got := d.NeedsConversion(sqlType); got != want { + t.Errorf("NeedsConversion(%q) = %v, want %v", sqlType, got, want) + } + if got := d.ConvertsInline(sqlType); got != want { + t.Errorf("ConvertsInline(%q) = %v, want %v", sqlType, got, want) + } + } +} + +func TestMysqlWriteConversionSetup(t *testing.T) { + t.Parallel() + for _, async := range []bool{false, true} { + d := newMysql(t, async) + conf := mysqlTestConfig(async) + w := writer.NewCodeWriter(conf) + queries := []model.Query{{Returns: model.QueryValue{Type: model.PyType{Type: "memoryview", SQLType: "blob"}}}} + if d.WriteConversionSetup(w, conf, queries) { + t.Errorf("async=%v WriteConversionSetup() = true, want false: mysql converts inline", async) + } + if got := w.String(); got != "" { + t.Errorf("async=%v WriteConversionSetup() wrote %q, want nothing", async, got) + } + } +} + +func TestMysqlWriteQueryResultsClassSync(t *testing.T) { + t.Parallel() + d := newMysql(t, false) + w := writer.NewCodeWriter(mysqlTestConfig(false)) + if got := d.WriteQueryResultsClass(w); got != "QueryResults" { + t.Errorf("WriteQueryResultsClass() = %q, want %q", got, "QueryResults") + } + want := strings.Join([]string{ + "class QueryResults[T]:", + ` __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql")`, + "", + " def __init__(", + " self,", + " conn: pymysql.Connection,", + " sql: str,", + " decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T],", + " *args: QueryResultsArgsType,", + " ) -> None:", + " self._conn = conn", + " self._sql = sql", + " self._decode_hook = decode_hook", + " self._args = args", + " self._cursor: pymysql.cursors.Cursor | None = None", + "", + " def __iter__(self) -> QueryResults[T]:", + " return self", + "", + " def __call__(", + " self,", + " ) -> collections.abc.Sequence[T]:", + " cur = self._conn.cursor()", + " cur.execute(self._sql, self._args)", + " result = cur.fetchall()", + " cur.close()", + " return [self._decode_hook(row) for row in result]", + "", + " def __next__(self) -> T:", + " if self._cursor is None:", + " self._cursor = self._conn.cursor()", + " self._cursor.execute(self._sql, self._args)", + " record = self._cursor.fetchone()", + " if record is None:", + " self._cursor = None", + " raise StopIteration", + " return self._decode_hook(record)", + }, "\n") + "\n" + if got := w.String(); got != want { + t.Errorf("WriteQueryResultsClass() wrote %q, want %q", got, want) + } +} + +func TestMysqlWriteQueryResultsClassAsync(t *testing.T) { + t.Parallel() + d := newMysql(t, true) + w := writer.NewCodeWriter(mysqlTestConfig(true)) + if got := d.WriteQueryResultsClass(w); got != "QueryResults" { + t.Errorf("WriteQueryResultsClass() = %q, want %q", got, "QueryResults") + } + want := strings.Join([]string{ + "class QueryResults[T]:", + ` __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql")`, + "", + " def __init__(", + " self,", + " conn: asyncmy.Connection,", + " sql: str,", + " decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T],", + " *args: QueryResultsArgsType,", + " ) -> None:", + " self._conn = conn", + " self._sql = sql", + " self._decode_hook = decode_hook", + " self._args = args", + " self._cursor: asyncmy.cursors.Cursor | None = None", + "", + " def __aiter__(self) -> QueryResults[T]:", + " return self", + "", + " def __await__(", + " self,", + " ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]:", + " async def _wrapper() -> collections.abc.Sequence[T]:", + " cur = self._conn.cursor()", + " await cur.execute(self._sql, self._args)", + " result = await cur.fetchall()", + " await cur.close()", + " return [self._decode_hook(row) for row in result]", + "", + " return _wrapper().__await__()", + "", + " async def __anext__(self) -> T:", + " if self._cursor is None:", + " self._cursor = self._conn.cursor()", + " await self._cursor.execute(self._sql, self._args)", + " record = await self._cursor.fetchone()", + " if record is None:", + " self._cursor = None", + " raise StopAsyncIteration", + " return self._decode_hook(record)", + }, "\n") + "\n" + if got := w.String(); got != want { + t.Errorf("WriteQueryResultsClass() wrote %q, want %q", got, want) + } +} + +func TestMysqlWriteQueryFuncSync(t *testing.T) { + t.Parallel() + cases := []struct { + name string + query model.Query + want string + }{ + { + name: "exec opens a cursor in a with block", + query: model.Query{ + Cmd: metadata.CmdExec, + ConstantName: "TOUCH", + FuncName: "touch", + Params: []model.QueryValue{ + {Name: "a", Type: model.PyType{Type: "int", SQLType: "int"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + }, + want: strings.Join([]string{ + "def touch(conn: pymysql.Connection, a: int) -> None:", + " with conn.cursor() as cur:", + " cur.execute(TOUCH, (a,))", + "", + }, "\n"), + }, + { + name: "exec blob param wire-converts to bytes", + query: model.Query{ + Cmd: metadata.CmdExec, + ConstantName: "SET_AVATAR", + FuncName: "set_avatar", + Params: []model.QueryValue{ + {Name: "avatar", Type: model.PyType{Type: "memoryview", SQLType: "blob"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + }, + want: strings.Join([]string{ + "def set_avatar(conn: pymysql.Connection, avatar: memoryview) -> None:", + " with conn.cursor() as cur:", + " cur.execute(SET_AVATAR, (bytes(avatar),))", + "", + }, "\n"), + }, + { + name: "exec nullable blob param guards the wire conversion", + query: model.Query{ + Cmd: metadata.CmdExec, + ConstantName: "SET_AVATAR", + FuncName: "set_avatar", + Params: []model.QueryValue{ + {Name: "avatar", Type: model.PyType{Type: "memoryview", SQLType: "blob", IsNullable: true}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + }, + want: strings.Join([]string{ + "def set_avatar(conn: pymysql.Connection, avatar: memoryview | None) -> None:", + " with conn.cursor() as cur:", + " cur.execute(SET_AVATAR, (bytes(avatar) if avatar is not None else None,))", + "", + }, "\n"), + }, + { + name: "execresult hands the open cursor to the caller without a with block", + query: model.Query{ + Cmd: metadata.CmdExecResult, + ConstantName: "RUN", + FuncName: "run", + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + }, + want: strings.Join([]string{ + "def run(conn: pymysql.Connection) -> pymysql.cursors.Cursor:", + " cur = conn.cursor()", + " cur.execute(RUN)", + " return cur", + "", + }, "\n"), + }, + { + name: "execrows returns the execute call inside the with block", + query: model.Query{ + Cmd: metadata.CmdExecRows, + ConstantName: "BUMP", + FuncName: "bump", + Returns: model.QueryValue{Type: model.PyType{Type: "int"}}, + }, + want: strings.Join([]string{ + "def bump(conn: pymysql.Connection) -> int:", + " with conn.cursor() as cur:", + " return cur.execute(BUMP)", + "", + }, "\n"), + }, + { + name: "execlastid executes then returns lastrowid", + query: model.Query{ + Cmd: metadata.CmdExecLastId, + ConstantName: "ADD", + FuncName: "add", + Params: []model.QueryValue{ + {Name: "name", Type: model.PyType{Type: "str", SQLType: "varchar"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "int", IsNullable: true}}, + }, + want: strings.Join([]string{ + "def add(conn: pymysql.Connection, name: str) -> int | None:", + " with conn.cursor() as cur:", + " cur.execute(ADD, (name,))", + " return cur.lastrowid or None", + "", + }, "\n"), + }, + { + name: "one scalar fetches inside the with block and returns outside", + query: model.Query{ + Cmd: metadata.CmdOne, + ConstantName: "COUNT", + FuncName: "count", + Returns: model.QueryValue{Type: model.PyType{Type: "int", SQLType: "bigint"}}, + }, + want: strings.Join([]string{ + "def count(conn: pymysql.Connection) -> int | None:", + " with conn.cursor() as cur:", + " cur.execute(COUNT)", + " row = cur.fetchone()", + " if row is None:", + " return None", + " return row[0]", + "", + }, "\n"), + }, + { + name: "one struct converts blob and tinyint-bool and enum columns", + query: model.Query{ + Cmd: metadata.CmdOne, + ConstantName: "GET_USER", + FuncName: "get_user", + Params: []model.QueryValue{ + {Name: "id_", Type: model.PyType{Type: "int", SQLType: "int"}}, + }, + Returns: mysqlUserReturn(), + }, + want: strings.Join([]string{ + "def get_user(conn: pymysql.Connection, id_: int) -> models.User | None:", + " with conn.cursor() as cur:", + " cur.execute(GET_USER, (id_,))", + " row = cur.fetchone()", + " if row is None:", + " return None", + " return " + mysqlUserDecode, + "", + }, "\n"), + }, + { + name: "many scalar without conversion uses itemgetter", + query: model.Query{ + Cmd: metadata.CmdMany, + ConstantName: "LIST_IDS", + FuncName: "list_ids", + Returns: model.QueryValue{Type: model.PyType{Type: "int", SQLType: "int"}}, + }, + want: strings.Join([]string{ + "def list_ids(conn: pymysql.Connection) -> QueryResults[int]:", + " return QueryResults(conn, LIST_IDS, operator.itemgetter(0))", + "", + }, "\n"), + }, + { + name: "many struct emits a decode hook and forwards params", + query: model.Query{ + Cmd: metadata.CmdMany, + ConstantName: "LIST_USERS", + FuncName: "list_users", + Params: []model.QueryValue{ + {Name: "name", Type: model.PyType{Type: "str", SQLType: "varchar"}}, + }, + Returns: mysqlUserReturn(), + }, + want: strings.Join([]string{ + "def list_users(conn: pymysql.Connection, name: str) -> QueryResults[models.User]:", + " def _decode_hook(row: tuple[typing.Any, ...]) -> models.User:", + " return " + mysqlUserDecode, + "", + " return QueryResults(conn, LIST_USERS, _decode_hook, name)", + "", + }, "\n"), + }, + { + name: "many struct slice expanded after decode hook", + query: model.Query{ + Cmd: metadata.CmdMany, + ConstantName: "LIST_BY_IDS", + FuncName: "list_by_ids", + SQL: "SELECT id FROM users WHERE id IN (/*SLICE:ids*/%s)", + Params: []model.QueryValue{ + {Name: "ids", Type: model.PyType{Type: "int", SQLType: "int", IsList: true, SqlcSliceName: "ids"}}, + }, + Returns: mysqlUserReturn(), + }, + want: strings.Join([]string{ + "def list_by_ids(conn: pymysql.Connection, ids: collections.abc.Sequence[int]) -> QueryResults[models.User]:", + " def _decode_hook(row: tuple[typing.Any, ...]) -> models.User:", + " return " + mysqlUserDecode, + "", + ` sql = LIST_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1)`, + " return QueryResults(conn, sql, _decode_hook, *ids)", + "", + }, "\n"), + }, + { + name: "one single slice marker replaces once and star-unpacks", + query: model.Query{ + Cmd: metadata.CmdOne, + ConstantName: "GET_BY_IDS", + FuncName: "get_by_ids", + SQL: "SELECT count(*) FROM t WHERE id IN (/*SLICE:ids*/%s)", + Params: []model.QueryValue{ + {Name: "ids", Type: model.PyType{Type: "int", SQLType: "int", IsList: true, SqlcSliceName: "ids"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "int", SQLType: "bigint"}}, + }, + want: strings.Join([]string{ + "def get_by_ids(conn: pymysql.Connection, ids: collections.abc.Sequence[int]) -> int | None:", + ` sql = GET_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1)`, + " with conn.cursor() as cur:", + " cur.execute(sql, (*ids,))", + " row = cur.fetchone()", + " if row is None:", + " return None", + " return row[0]", + "", + }, "\n"), + }, + { + // sqlc merges same-named sqlc.slice uses into one parameter but + // keeps a marker per use site: all of them are replaced and the + // arguments are repeated once per occurrence. + name: "exec reused slice replaces all markers and repeats args", + query: model.Query{ + Cmd: metadata.CmdExec, + ConstantName: "DELETE_LINKED", + FuncName: "delete_linked", + SQL: "DELETE FROM t WHERE id IN (/*SLICE:ids*/%s) OR ref_id IN (/*SLICE:ids*/%s)", + Params: []model.QueryValue{ + {Name: "ids", Type: model.PyType{Type: "int", SQLType: "int", IsList: true, SqlcSliceName: "ids"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + }, + want: strings.Join([]string{ + "def delete_linked(conn: pymysql.Connection, ids: collections.abc.Sequence[int]) -> None:", + ` sql = DELETE_LINKED.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL")`, + " with conn.cursor() as cur:", + " cur.execute(sql, (*ids, *ids))", + "", + }, "\n"), + }, + { + // A plain placeholder between the reuse sites: arguments follow + // the SQL text order, not the parameter order. + name: "exec reused slice keeps text order around plain params", + query: model.Query{ + Cmd: metadata.CmdExec, + ConstantName: "DELETE_BETWEEN", + FuncName: "delete_between", + SQL: "DELETE FROM t WHERE id IN (/*SLICE:ids*/%s) AND name = %s AND ref_id IN (/*SLICE:ids*/%s)", + Params: []model.QueryValue{ + {Name: "ids", Type: model.PyType{Type: "int", SQLType: "int", IsList: true, SqlcSliceName: "ids"}}, + {Name: "name", Type: model.PyType{Type: "str", SQLType: "varchar"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + }, + want: strings.Join([]string{ + "def delete_between(conn: pymysql.Connection, ids: collections.abc.Sequence[int], name: str) -> None:", + ` sql = DELETE_BETWEEN.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL")`, + " with conn.cursor() as cur:", + " cur.execute(sql, (*ids, name, *ids))", + "", + }, "\n"), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + d := newMysql(t, false) + conf := mysqlTestConfig(false) + body := writer.NewCodeWriter(conf) + d.WriteQueryFunc(body, conf, tc.query, 0) + if got := body.String(); got != tc.want { + t.Errorf("WriteQueryFunc() = %q, want %q", got, tc.want) + } + }) + } +} + +func TestMysqlWriteQueryFuncAsync(t *testing.T) { + t.Parallel() + cases := []struct { + name string + query model.Query + want string + }{ + { + name: "exec awaits inside an async with block", + query: model.Query{ + Cmd: metadata.CmdExec, + ConstantName: "TOUCH", + FuncName: "touch", + Params: []model.QueryValue{ + {Name: "a", Type: model.PyType{Type: "int", SQLType: "int"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + }, + want: strings.Join([]string{ + "async def touch(conn: asyncmy.Connection, a: int) -> None:", + " async with conn.cursor() as cur:", + " await cur.execute(TOUCH, (a,))", + "", + }, "\n"), + }, + { + name: "execresult awaits execute and returns the cursor", + query: model.Query{ + Cmd: metadata.CmdExecResult, + ConstantName: "RUN", + FuncName: "run", + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + }, + want: strings.Join([]string{ + "async def run(conn: asyncmy.Connection) -> asyncmy.cursors.Cursor:", + " cur = conn.cursor()", + " await cur.execute(RUN)", + " return cur", + "", + }, "\n"), + }, + { + name: "execrows returns the awaited execute call", + query: model.Query{ + Cmd: metadata.CmdExecRows, + ConstantName: "BUMP", + FuncName: "bump", + Returns: model.QueryValue{Type: model.PyType{Type: "int"}}, + }, + want: strings.Join([]string{ + "async def bump(conn: asyncmy.Connection) -> int:", + " async with conn.cursor() as cur:", + " return await cur.execute(BUMP)", + "", + }, "\n"), + }, + { + name: "execlastid awaits execute but not lastrowid", + query: model.Query{ + Cmd: metadata.CmdExecLastId, + ConstantName: "ADD", + FuncName: "add", + Params: []model.QueryValue{ + {Name: "name", Type: model.PyType{Type: "str", SQLType: "varchar"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "int", IsNullable: true}}, + }, + want: strings.Join([]string{ + "async def add(conn: asyncmy.Connection, name: str) -> int | None:", + " async with conn.cursor() as cur:", + " await cur.execute(ADD, (name,))", + " return cur.lastrowid or None", + "", + }, "\n"), + }, + { + name: "one struct awaits execute and fetchone", + query: model.Query{ + Cmd: metadata.CmdOne, + ConstantName: "GET_USER", + FuncName: "get_user", + Params: []model.QueryValue{ + {Name: "id_", Type: model.PyType{Type: "int", SQLType: "int"}}, + }, + Returns: mysqlUserReturn(), + }, + want: strings.Join([]string{ + "async def get_user(conn: asyncmy.Connection, id_: int) -> models.User | None:", + " async with conn.cursor() as cur:", + " await cur.execute(GET_USER, (id_,))", + " row = await cur.fetchone()", + " if row is None:", + " return None", + " return " + mysqlUserDecode, + "", + }, "\n"), + }, + { + name: "many struct stays a plain def", + query: model.Query{ + Cmd: metadata.CmdMany, + ConstantName: "LIST_USERS", + FuncName: "list_users", + Returns: mysqlUserReturn(), + }, + want: strings.Join([]string{ + "def list_users(conn: asyncmy.Connection) -> QueryResults[models.User]:", + " def _decode_hook(row: tuple[typing.Any, ...]) -> models.User:", + " return " + mysqlUserDecode, + "", + " return QueryResults(conn, LIST_USERS, _decode_hook)", + "", + }, "\n"), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + d := newMysql(t, true) + conf := mysqlTestConfig(true) + body := writer.NewCodeWriter(conf) + d.WriteQueryFunc(body, conf, tc.query, 0) + if got := body.String(); got != tc.want { + t.Errorf("WriteQueryFunc() = %q, want %q", got, tc.want) + } + }) + } +} + +func TestMysqlWriteQueryFuncClassesMode(t *testing.T) { + t.Parallel() + d := newMysql(t, false) + conf := mysqlTestConfig(false) + conf.EmitClasses = true + body := writer.NewCodeWriter(conf) + query := model.Query{ + Cmd: metadata.CmdExec, + ConstantName: "TOUCH", + FuncName: "touch", + Params: []model.QueryValue{ + {Name: "a", Type: model.PyType{Type: "int", SQLType: "int"}}, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + } + d.WriteQueryFunc(body, conf, query, 1) + want := strings.Join([]string{ + " def touch(self, a: int) -> None:", + " with self._conn.cursor() as cur:", + " cur.execute(TOUCH, (a,))", + "", + }, "\n") + if got := body.String(); got != want { + t.Errorf("WriteQueryFunc() = %q, want %q", got, want) + } +} + +func TestMysqlWriteQueryFuncBundledParams(t *testing.T) { + t.Parallel() + d := newMysql(t, false) + conf := mysqlTestConfig(false) + body := writer.NewCodeWriter(conf) + query := model.Query{ + Cmd: metadata.CmdExec, + ConstantName: "UPDATE_USER", + FuncName: "update_user", + Params: []model.QueryValue{ + { + EmitTable: true, + Name: "params", + Type: model.PyType{Type: "UpdateUserParams"}, + Table: &model.Table{ + Name: "UpdateUserParams", + Columns: []model.Column{ + {Name: "name", DBName: "name", Type: model.PyType{Type: "str", SQLType: "varchar"}}, + {Name: "avatar", DBName: "avatar", Type: model.PyType{Type: "memoryview", SQLType: "blob"}}, + {Name: "id_", DBName: "id", Type: model.PyType{Type: "int", SQLType: "int"}}, + }, + }, + }, + }, + Returns: model.QueryValue{Type: model.PyType{Type: "None"}}, + } + d.WriteQueryFunc(body, conf, query, 0) + want := strings.Join([]string{ + "def update_user(conn: pymysql.Connection, params: UpdateUserParams) -> None:", + " with conn.cursor() as cur:", + " cur.execute(UPDATE_USER, (params.name, bytes(params.avatar), params.id_))", + "", + }, "\n") + if got := body.String(); got != want { + t.Errorf("WriteQueryFunc() = %q, want %q", got, want) + } +} diff --git a/internal/driver/sqlite_base.go b/internal/driver/sqlite_base.go index 7507f5e4..e3561cb1 100644 --- a/internal/driver/sqlite_base.go +++ b/internal/driver/sqlite_base.go @@ -2,7 +2,6 @@ package driver import ( "fmt" - "strings" "github.com/rayakame/sqlc-gen-better-python/internal/config" "github.com/rayakame/sqlc-gen-better-python/internal/model" @@ -197,7 +196,7 @@ func (sb *sqliteBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Con // line keeps the assignment from touching the nested def (ruff E306). sqlRef := query.ConstantName if query.Cmd != metadata.CmdMany { - sqlRef = writeSliceExpansion(body, indent, query) + sqlRef = writeSliceExpansion(body, indent, query, questionPlaceholders) } // stmt builds the execute-statement head/tail with the correct await @@ -218,19 +217,19 @@ func (sb *sqliteBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Con switch query.Cmd { case metadata.CmdExec: head, tail := stmt("", "") - writeSqliteCall(body, indent, expandParamsFlattenSlices(query), head, tail) + writeCursorCall(body, indent, expandParamsFlattenSlices(query), head, tail) case metadata.CmdExecResult: head, tail := stmt("return ", "") - writeSqliteCall(body, indent, expandParamsFlattenSlices(query), head, tail) + writeCursorCall(body, indent, expandParamsFlattenSlices(query), head, tail) case metadata.CmdExecRows: head, tail := stmt("return ", ".rowcount") - writeSqliteCall(body, indent, expandParamsFlattenSlices(query), head, tail) + writeCursorCall(body, indent, expandParamsFlattenSlices(query), head, tail) case metadata.CmdExecLastId: head, tail := stmt("return ", ".lastrowid") - writeSqliteCall(body, indent, expandParamsFlattenSlices(query), head, tail) + writeCursorCall(body, indent, expandParamsFlattenSlices(query), head, tail) case metadata.CmdOne: prefix := "row = " @@ -239,7 +238,7 @@ func (sb *sqliteBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Con prefix = "row = await " } head, tail := stmt(prefix, ".fetchone()") - writeSqliteCall(body, indent, expandParamsFlattenSlices(query), head, tail) + writeCursorCall(body, indent, expandParamsFlattenSlices(query), head, tail) body.WriteIndentedLine(indent, "if row is None:") body.WriteIndentedLine(indent+1, "return None") @@ -251,7 +250,7 @@ func (sb *sqliteBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Con case metadata.CmdMany: decodeHook := sb.rows.WriteDecodeHook(body, indent, query, sqliteResultType) - sqlRef = writeSliceExpansion(body, indent, query) + sqlRef = writeSliceExpansion(body, indent, query, questionPlaceholders) manyArgs := append([]string{conn, sqlRef, decodeHook}, expandParamsFlattenSlices(query)...) // Deliberately unsubscripted: QueryResults[T](...) would go through // typing's _GenericAlias.__call__ on every invocation (~10x call @@ -259,58 +258,3 @@ func (sb *sqliteBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Con body.WriteWrappedCall(indent, "return QueryResults(", manyArgs, ")") } } - -// writeSliceExpansion writes the runtime replacement of every sqlc.slice -// placeholder - one "?" per element, or "NULL" for an empty sequence so that -// "IN (NULL)" matches no rows - and returns the expression holding the final -// SQL: a local "sql" variable, or the untouched constant without slices. -func writeSliceExpansion(body *writer.CodeWriter, indent int, query model.Query) string { - params := sliceParams(query) - if len(params) == 0 { - return query.ConstantName - } - src := query.ConstantName - for _, param := range params { - args := []string{ - writer.PyQuote(sliceMarker(param.marker)), - fmt.Sprintf(`",".join("?" * len(%s)) or "NULL"`, param.expr), - } - // A reused slice has one marker per use site: replace them all, with - // expandParamsFlattenSlices supplying a copy of the args for each. - if sliceMarkerCount(query, param.marker) == 1 { - args = append(args, "1") - } - body.WriteWrappedCall(indent, "sql = "+src+".replace(", args, ")") - src = "sql" - } - - return "sql" -} - -// writeSqliteCall writes stmtHead+argsSegment+stmtTail on one line, hoisting a -// too-long parameter tuple into a local _args variable first so the statement -// stays within the line limit. parts are the already-expanded (and, for wire- -// converting drivers, already-converted) argument expressions. -func writeSqliteCall(body *writer.CodeWriter, indent int, parts []string, stmtHead, stmtTail string) { - segment := "" - switch { - case len(parts) == 1: - segment = fmt.Sprintf(", (%s,)", parts[0]) - case len(parts) > 1: - segment = fmt.Sprintf(", (%s)", strings.Join(parts, ", ")) - } - - stmt := stmtHead + segment + stmtTail - if body.FitsLine(indent, stmt) { - body.WriteIndentedLine(indent, stmt) - - return - } - - body.WriteIndentedLine(indent, "sql_args = (") - for _, part := range parts { - body.WriteIndentedLine(indent+1, part+",") - } - body.WriteIndentedLine(indent, ")") - body.WriteIndentedLine(indent, stmtHead+", sql_args"+stmtTail) -} diff --git a/internal/driver/turso.go b/internal/driver/turso.go index 7536d3d7..1e215ece 100644 --- a/internal/driver/turso.go +++ b/internal/driver/turso.go @@ -62,7 +62,7 @@ var tursoConversions = []tursoConversion{ // needs the wrap back from the stored integer. {sqlTypes: []string{types.Bool, types.Boolean}, decodeFmt: tursoDecodeBool, wireFmt: "", speedupsFmt: ""}, // pyturso rejects memoryview parameters, so blobs bind as bytes. - {sqlTypes: []string{sqlTypeBlob}, decodeFmt: tursoDecodeMemview, wireFmt: "bytes(%s)", speedupsFmt: ""}, + {sqlTypes: []string{sqlTypeBlob}, decodeFmt: tursoDecodeMemview, wireFmt: wireBytes, speedupsFmt: ""}, } // findTursoConversion returns the conversion spec for a SQL type, or nil. @@ -310,7 +310,7 @@ func (tb *tursoBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Conf // line keeps the assignment from touching the nested def (ruff E306). sqlRef := query.ConstantName if query.Cmd != metadata.CmdMany { - sqlRef = writeSliceExpansion(body, indent, query) + sqlRef = writeSliceExpansion(body, indent, query, questionPlaceholders) } // stmt builds the execute-statement head/tail with the correct await @@ -332,19 +332,19 @@ func (tb *tursoBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Conf switch query.Cmd { case metadata.CmdExec: head, tail := stmt("", "") - writeSqliteCall(body, indent, parts, head, tail) + writeCursorCall(body, indent, parts, head, tail) case metadata.CmdExecResult: head, tail := stmt("return ", "") - writeSqliteCall(body, indent, parts, head, tail) + writeCursorCall(body, indent, parts, head, tail) case metadata.CmdExecRows: head, tail := stmt("return ", ".rowcount") - writeSqliteCall(body, indent, parts, head, tail) + writeCursorCall(body, indent, parts, head, tail) case metadata.CmdExecLastId: head, tail := stmt("return ", ".lastrowid") - writeSqliteCall(body, indent, parts, head, tail) + writeCursorCall(body, indent, parts, head, tail) case metadata.CmdOne: prefix := "row = " @@ -353,7 +353,7 @@ func (tb *tursoBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Conf prefix = "row = await " } head, tail := stmt(prefix, ".fetchone()") - writeSqliteCall(body, indent, parts, head, tail) + writeCursorCall(body, indent, parts, head, tail) body.WriteIndentedLine(indent, "if row is None:") body.WriteIndentedLine(indent+1, "return None") @@ -365,7 +365,7 @@ func (tb *tursoBase) WriteQueryFunc(body *writer.CodeWriter, config *config.Conf case metadata.CmdMany: decodeHook := tb.rows.WriteDecodeHook(body, indent, query, tursoResultType) - sqlRef = writeSliceExpansion(body, indent, query) + sqlRef = writeSliceExpansion(body, indent, query, questionPlaceholders) manyArgs := append([]string{conn, sqlRef, decodeHook}, parts...) // Deliberately unsubscripted: QueryResults[T](...) would go through // typing's _GenericAlias.__call__ on every invocation (~10x call diff --git a/internal/model/types.go b/internal/model/types.go index 8510eb6b..63db354b 100644 --- a/internal/model/types.go +++ b/internal/model/types.go @@ -127,6 +127,11 @@ type QueryValue struct { // Number is the 1-based sqlc parameter number, used by drivers that // bind by name (psycopg's %(pN)s); 0 for return values. Number int32 + + // Repeated marks a later occurrence of a reused MySQL parameter: it + // keeps its positional binding slot (same Name as the first occurrence) + // but is skipped in signatures and docstrings. + Repeated bool } type Embed struct { diff --git a/internal/render/imports.go b/internal/render/imports.go index 1c55a83e..d5dacd03 100644 --- a/internal/render/imports.go +++ b/internal/render/imports.go @@ -185,6 +185,7 @@ func (r *ImportResolver) ModelImports(tables []model.Table) ImportResult { } func (r *ImportResolver) QueryImports(queries []model.Query) ImportResult { + hasMany := isAnyQueryMany(queries) // "uses" checks whether any query arg/return uses a given Python type. // Returns (isUsed, goesInTypeChecking). uses := func(name string) (bool, bool) { @@ -202,11 +203,11 @@ func (r *ImportResolver) QueryImports(queries []model.Query) ImportResult { } for _, query := range queries { - if used, tc := r.queryValueUses(name, query.Returns, true); used { + if used, tc := r.queryValueUses(name, query.Returns, true, hasMany); used { update(used, tc) } for _, arg := range query.Params { - if used, tc := r.queryValueUses(name, arg, false); used { + if used, tc := r.queryValueUses(name, arg, false, hasMany); used { update(used, tc) } // Overridden params are converted back to their DefaultType at @@ -595,9 +596,33 @@ func (r *ImportResolver) addDriverImports( if r.conf.Speedups && driver.TursoSpeedupsUsed(queries) { std["ciso8601"] = importSpec{Module: "ciso8601"} } + + case config.SQLDriverPymysql, config.SQLDriverAsyncmy: + // Nothing registers at import time and every MySQL conversion is a + // builtin constructor, so the module is annotation-only. The cursor + // class lives in the cursors submodule, referenced by QueryResults + // state and :execresult return annotations. + typeChecking[driverName] = importSpec{Module: driverName} + if hasMany || isAnyQueryExecResult(queries) { + typeChecking[driverName+".cursors"] = importSpec{Module: driverName + ".cursors"} + } + if hasMany && r.hasSimpleReturn(queries) { + std["operator"] = importSpec{Module: moduleOperator} + } } } +// isAnyQueryExecResult reports whether any query returns the raw cursor. +func isAnyQueryExecResult(queries []model.Query) bool { + for _, query := range queries { + if query.Cmd == metadata.CmdExecResult { + return true + } + } + + return false +} + // hasSimpleReturn checks if any query has a non-struct return that doesn't need // conversion. Must mirror RowBuilder.columnNeedsConversion: only these returns // use operator.itemgetter instead of a _decode_hook. @@ -621,41 +646,16 @@ func (r *ImportResolver) hasSimpleReturn(queries []model.Query) bool { // reference is annotation-only. Only decoded return values construct the type // at runtime; parameters are annotated but passed through (an overridden one is // converted via its DefaultType, tracked by overrideDefaultTypeUses). -func (r *ImportResolver) queryValueUses(name string, queryValue model.QueryValue, isReturn bool) (bool, bool) { +// hasMany marks modules with a :many query: only those spell struct column +// types in the QueryResultsArgsType alias, so an annotation-only match from a +// NON-emitted return struct (the class lives in models.py) counts only there. +func (r *ImportResolver) queryValueUses(name string, queryValue model.QueryValue, isReturn, hasMany bool) (bool, bool) { if queryValue.IsEmpty() { return false, false } if queryValue.IsStruct() { - // Scan ALL columns (including embed columns): any occurrence that - // needs runtime conversion must force a runtime import, even when an - // earlier annotation-only occurrence of the same type exists. - used := false - typeChecking := true - check := func(typ model.PyType) { - if typ.Type != name { - return - } - used = true - if isReturn && !typ.HasConverter() && (r.convertsInlineWithType(typ.SQLType) || typ.DoOverride()) { - typeChecking = false - } - } - for _, column := range queryValue.Table.Columns { - if column.Embed != nil { - for _, embedColumn := range column.Embed.Columns { - check(embedColumn.Type) - } - - continue - } - check(column.Type) - } - if !used { - return false, false - } - - return true, typeChecking + return r.structUses(name, queryValue, isReturn, hasMany) } if queryValue.Type.Type == name { @@ -668,6 +668,44 @@ func (r *ImportResolver) queryValueUses(name string, queryValue model.QueryValue return false, false } +// structUses is queryValueUses for struct values. It scans ALL columns +// (including embed columns): any occurrence that needs runtime conversion +// must force a runtime import, even when an earlier annotation-only +// occurrence of the same type exists. +func (r *ImportResolver) structUses(name string, queryValue model.QueryValue, isReturn, hasMany bool) (bool, bool) { + used := false + typeChecking := true + check := func(typ model.PyType) { + if typ.Type != name { + return + } + used = true + if isReturn && !typ.HasConverter() && (r.convertsInlineWithType(typ.SQLType) || typ.DoOverride()) { + typeChecking = false + } + } + for _, column := range queryValue.Table.Columns { + if column.Embed != nil { + for _, embedColumn := range column.Embed.Columns { + check(embedColumn.Type) + } + + continue + } + check(column.Type) + } + if !used { + return false, false + } + if typeChecking && isReturn && !queryValue.EmitTable && !hasMany { + // The module spells only "models.X"; without the :many alias the + // column types appear nowhere, and importing them trips F401. + return false, false + } + + return true, typeChecking +} + // convertsInlineWithType reports whether an inline conversion references the // column's Python type at runtime. With turso speedups, the date/datetime // decodes call ciso8601 instead, so the type stays annotation-only; this must @@ -720,10 +758,11 @@ func (r *ImportResolver) buildQueryResult(std, typeChecking, local map[string]im // modules with :many queries. func (r *ImportResolver) queryResultsArgsType(std, typeChecking map[string]importSpec, queries []model.Query) string { members := []string{types.Int, types.Float, types.Str, types.Memoryview} - if r.conf.SqlDriver.IsTurso() { + if r.conf.SqlDriver.IsTurso() || r.conf.SqlDriver.IsMysql() { // Blob parameters reach QueryResults already wire-converted to - // bytes (pyturso rejects memoryview); the memoryview member stays - // for pass-through override values. + // bytes (pyturso rejects memoryview; the PyMySQL encoders silently + // stringify it); the memoryview member stays for pass-through + // override values. members = append(members, "bytes") } allSpecs := mergeMaps(std, typeChecking) diff --git a/internal/render/imports_test.go b/internal/render/imports_test.go index 301264c6..a6914503 100644 --- a/internal/render/imports_test.go +++ b/internal/render/imports_test.go @@ -609,6 +609,78 @@ func TestQueryImports(t *testing.T) { }, }, }, + { + name: "pymysql one scalar stays typechecking", + conf: newImportsConfig(config.SQLDriverPymysql), + queries: []model.Query{ + {Cmd: metadata.CmdOne, Returns: impScalar(model.PyType{SQLType: "int", Type: "int"})}, + }, + want: ImportResult{ + Std: []string{"import typing"}, + TypeChecking: []string{"import collections.abc", "import pymysql"}, + }, + }, + { + name: "pymysql execresult imports cursors without operator", + conf: newImportsConfig(config.SQLDriverPymysql), + queries: []model.Query{ + {Cmd: metadata.CmdExecResult}, + }, + want: ImportResult{ + Std: []string{"import typing"}, + TypeChecking: []string{"import collections.abc", "import pymysql", "import pymysql.cursors"}, + }, + }, + { + name: "pymysql many simple return imports operator and adds the bytes member", + conf: newImportsConfig(config.SQLDriverPymysql), + queries: []model.Query{ + {Cmd: metadata.CmdMany, Returns: impScalar(model.PyType{SQLType: "int", Type: "int"})}, + }, + want: ImportResult{ + Std: []string{"import operator", "import typing"}, + TypeChecking: []string{ + "import collections.abc", + "import pymysql", + "import pymysql.cursors\n", + "type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None", + }, + }, + }, + { + name: "pymysql many struct return skips operator", + conf: newImportsConfig(config.SQLDriverPymysql), + queries: []model.Query{ + {Cmd: metadata.CmdMany, Returns: impStruct(true, + impCol("id", model.PyType{SQLType: "int", Type: "int"}), + )}, + }, + want: ImportResult{ + Std: []string{"import dataclasses", "import typing"}, + TypeChecking: []string{ + "import collections.abc", + "import pymysql", + "import pymysql.cursors\n", + "type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None", + }, + }, + }, + { + name: "asyncmy many simple return imports operator and adds the bytes member", + conf: newImportsConfig(config.SQLDriverAsyncmy), + queries: []model.Query{ + {Cmd: metadata.CmdMany, Returns: impScalar(model.PyType{SQLType: "int", Type: "int"})}, + }, + want: ImportResult{ + Std: []string{"import operator", "import typing"}, + TypeChecking: []string{ + "import asyncmy", + "import asyncmy.cursors", + "import collections.abc\n", + "type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None", + }, + }, + }, { name: "psycopg without json returns keeps the module lazy", conf: newImportsConfig(config.SQLDriverPsycopgAsync), @@ -1532,10 +1604,43 @@ func TestQueryValueUses(t *testing.T) { lookup string qv model.QueryValue isReturn bool + hasMany bool wantUsed bool wantTC bool }{ {name: "empty value", lookup: "int", qv: model.QueryValue{}, isReturn: true, wantUsed: false, wantTC: false}, + { + // Without :many there is no QueryResultsArgsType alias, and a + // models.py class's column types are spelled nowhere else. + name: "non-emitted return struct annotation only without many", + lookup: typeDate, + qv: impStruct(false, impCol("a", model.PyType{SQLType: "date", Type: typeDate})), + isReturn: true, + wantUsed: false, + wantTC: false, + }, + { + name: "non-emitted return struct annotation only with many", + lookup: typeDate, + qv: impStruct(false, impCol("a", model.PyType{SQLType: "date", Type: typeDate})), + isReturn: true, + hasMany: true, + wantUsed: true, + wantTC: true, + }, + { + // A decode-hook conversion spells the type at runtime even in a + // module without :many. + name: "non-emitted return struct overridden column without many", + lookup: typeDate, + qv: impStruct( + false, + impCol("a", model.PyType{SQLType: "date", Type: typeDate, IsOverride: true, DefaultType: "str"}), + ), + isReturn: true, + wantUsed: true, + wantTC: false, + }, { name: "scalar annotation only", lookup: typeDate, @@ -1645,7 +1750,7 @@ func TestQueryValueUses(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() resolver := newImportsResolver(t, newImportsConfig(config.SQLDriverAsyncpg)) - gotUsed, gotTC := resolver.queryValueUses(tc.lookup, tc.qv, tc.isReturn) + gotUsed, gotTC := resolver.queryValueUses(tc.lookup, tc.qv, tc.isReturn, tc.hasMany) if gotUsed != tc.wantUsed || gotTC != tc.wantTC { t.Errorf("queryValueUses() = (%v, %v), want (%v, %v)", gotUsed, gotTC, tc.wantUsed, tc.wantTC) } diff --git a/internal/render/queries.go b/internal/render/queries.go index a7734a9b..70bc6aec 100644 --- a/internal/render/queries.go +++ b/internal/render/queries.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/rayakame/sqlc-gen-better-python/internal/config" "github.com/rayakame/sqlc-gen-better-python/internal/model" "github.com/rayakame/sqlc-gen-better-python/internal/types" "github.com/rayakame/sqlc-gen-better-python/internal/utils" @@ -14,6 +15,14 @@ import ( func (r *Renderer) renderQueriesModule(moduleName string, queries []model.Query) *plugin.File { fileBody := r.getCodeWriter() fileBody.WriteSqlcHeader(utils.ToPtr(queries[0])) + // asyncmy's shipped cursor stubs leave execute()'s parameters + // unannotated, so pyright strict flags every cursor.execute access as + // partially unknown. File-level suppression (queries modules only; the + // directive must precede the module docstring) until upstream annotates: + // https://github.com/long2ice/asyncmy + if r.config.SqlDriver == config.SQLDriverAsyncmy { + fileBody.WriteLine("# pyright: reportUnknownMemberType=false") + } fileBody.WriteQueryFileModuleDocstring(queries[0].FileName) fileBody.WriteFutureImport() diff --git a/internal/render/render_queries_test.go b/internal/render/render_queries_test.go index d4f27746..3ab9729c 100644 --- a/internal/render/render_queries_test.go +++ b/internal/render/render_queries_test.go @@ -491,6 +491,108 @@ async def get_created(conn: turso.aio.Connection) -> datetime.date | None: if row is None: return None return datetime.date.fromisoformat(row[0]) +`, + }, + { + // No pyright suppression line for pymysql: only asyncmy's stubs + // need it. + name: "pymysql exec rewrites placeholders without a pyright directive", + engine: "mysql", + options: `{"package":"testpkg","sql_driver":"pymysql","emit_init_file":false}`, + queries: []*plugin.Query{{ + Name: "InsertItem", + Cmd: metadata.CmdExec, + Text: "INSERT INTO test_items (id) VALUES (?)", + Filename: "queries.sql", + Params: []*plugin.Parameter{pgParam(pgColumn("id", "bigint", true))}, + }}, + want: sqlcFileHeader("queries.sql") + `from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("insert_item",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + + +INSERT_ITEM: typing.Final[str] = """-- name: InsertItem :exec +INSERT INTO test_items (id) VALUES (%s) +""" + + +def insert_item(conn: pymysql.Connection, *, id_: int) -> None: + with conn.cursor() as cur: + cur.execute(INSERT_ITEM, (id_,)) +`, + }, + { + // The suppression comment must sit between the sqlc header and + // the first statement or pyright ignores it. + name: "asyncmy emits the pyright directive before the module body", + engine: "mysql", + options: `{"package":"testpkg","sql_driver":"asyncmy","emit_init_file":false}`, + queries: []*plugin.Query{{ + Name: "InsertItem", + Cmd: metadata.CmdExec, + Text: "INSERT INTO test_items (id) VALUES (?)", + Filename: "queries.sql", + Params: []*plugin.Parameter{pgParam(pgColumn("id", "bigint", true))}, + }}, + want: sqlcFileHeader("queries.sql") + `# pyright: reportUnknownMemberType=false +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("insert_item",) + +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import collections.abc + + +INSERT_ITEM: typing.Final[str] = """-- name: InsertItem :exec +INSERT INTO test_items (id) VALUES (%s) +""" + + +async def insert_item(conn: asyncmy.Connection, *, id_: int) -> None: + async with conn.cursor() as cur: + await cur.execute(INSERT_ITEM, (id_,)) +`, + }, + { + // The suppression line and the hoisted module-level imports must + // coexist: the directive still precedes the first statement. + name: "asyncmy with omit_typechecking_block keeps the directive first", + engine: "mysql", + options: `{"package":"testpkg","sql_driver":"asyncmy","emit_init_file":false,"omit_typechecking_block":true}`, + queries: []*plugin.Query{{ + Name: "InsertItem", + Cmd: metadata.CmdExec, + Text: "INSERT INTO test_items (id) VALUES (?)", + Filename: "queries.sql", + Params: []*plugin.Parameter{pgParam(pgColumn("id", "bigint", true))}, + }}, + want: sqlcFileHeader("queries.sql") + `# pyright: reportUnknownMemberType=false +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("insert_item",) + +import typing +import asyncmy +import collections.abc + + +INSERT_ITEM: typing.Final[str] = """-- name: InsertItem :exec +INSERT INTO test_items (id) VALUES (%s) +""" + + +async def insert_item(conn: asyncmy.Connection, *, id_: int) -> None: + async with conn.cursor() as cur: + await cur.execute(INSERT_ITEM, (id_,)) `, }, } diff --git a/internal/transform/filter.go b/internal/transform/filter.go index 76c54974..e8e9f7c4 100644 --- a/internal/transform/filter.go +++ b/internal/transform/filter.go @@ -16,11 +16,23 @@ func FilterUnusedModels(enums []model.Enum, tables []model.Table, queries []mode typeName = strings.TrimPrefix(typeName, "enums.") keep[typeName] = struct{}{} } - collect := func(qv model.QueryValue) { + addPyType := func(typ model.PyType, isParam bool) { + addType(typ.Type) + // An overridden enum PARAM still calls the enum class at runtime - + // it converts back through its DefaultType. Overridden returns + // convert through the override type only, and converter overrides + // call the user's to_db function instead, so their DefaultType + // would be a dead retention (mirrors render.overrideDefaultTypeUses + // and driver.convertParamExprWire). + if isParam && typ.DoOverride() && !typ.HasConverter() { + addType(typ.DefaultType) + } + } + collect := func(qv model.QueryValue, isParam bool) { if qv.IsEmpty() { return } - addType(qv.Type.Type) + addPyType(qv.Type, isParam) if qv.Table == nil { return } @@ -28,18 +40,18 @@ func FilterUnusedModels(enums []model.Enum, tables []model.Table, queries []mode if col.Embed != nil { addType(col.Embed.ModelName) for _, embedCol := range col.Embed.Columns { - addType(embedCol.Type.Type) + addPyType(embedCol.Type, isParam) } continue } - addType(col.Type.Type) + addPyType(col.Type, isParam) } } for _, query := range queries { - collect(query.Returns) + collect(query.Returns, false) for _, param := range query.Params { - collect(param) + collect(param, true) } } diff --git a/internal/transform/filter_test.go b/internal/transform/filter_test.go index fc80a33b..06fcada0 100644 --- a/internal/transform/filter_test.go +++ b/internal/transform/filter_test.go @@ -48,6 +48,84 @@ func TestFilterUnusedModelsKeepsReferencedModels(t *testing.T) { } } +func TestFilterUnusedModelsKeepsOverriddenEnumDefaults(t *testing.T) { + t.Parallel() + // An overridden enum column references the enum class only through its + // DefaultType: parameters convert back via enums.X(...) at runtime, so + // the enum must survive the filter. + enums := []model.Enum{{Name: "TestEnumOverrideMoodTest"}, {Name: "UnusedEnum"}} + queries := []model.Query{ + { + Params: []model.QueryValue{{ + Name: "mood_test", + Type: model.PyType{ + Type: "str", + IsOverride: true, + DefaultType: "enums.TestEnumOverrideMoodTest", + }, + }}, + }, + } + + keptEnums, keptTables := transform.FilterUnusedModels(enums, nil, queries) + if want := []model.Enum{{Name: "TestEnumOverrideMoodTest"}}; !reflect.DeepEqual(keptEnums, want) { + t.Errorf("FilterUnusedModels() enums = %v, want %v", keptEnums, want) + } + if len(keptTables) != 0 { + t.Errorf("FilterUnusedModels() tables = %v, want none", keptTables) + } +} + +func TestFilterUnusedModelsDropsConverterOverrideDefaults(t *testing.T) { + t.Parallel() + // Converter-overridden params call the user's to_db function, never the + // DefaultType enum class; keeping it would generate a dead enums.py that + // nothing imports. + enums := []model.Enum{{Name: "TestEnumOverrideMoodTest"}} + queries := []model.Query{ + { + Params: []model.QueryValue{{ + Name: "mood_test", + Type: model.PyType{ + Type: "str", + IsOverride: true, + DefaultType: "enums.TestEnumOverrideMoodTest", + ConverterTo: "converters.mood_to_db", + }, + }}, + }, + } + + keptEnums, _ := transform.FilterUnusedModels(enums, nil, queries) + if len(keptEnums) != 0 { + t.Errorf("FilterUnusedModels() enums = %v, want none", keptEnums) + } +} + +func TestFilterUnusedModelsDropsReturnOnlyOverrideDefaults(t *testing.T) { + t.Parallel() + // Overridden RETURNS convert through the override type only; their + // DefaultType enum is never referenced and must not survive. + enums := []model.Enum{{Name: "TestEnumOverrideMoodTest"}} + queries := []model.Query{ + { + Returns: model.QueryValue{ + Name: "mood_test", + Type: model.PyType{ + Type: "str", + IsOverride: true, + DefaultType: "enums.TestEnumOverrideMoodTest", + }, + }, + }, + } + + keptEnums, _ := transform.FilterUnusedModels(enums, nil, queries) + if len(keptEnums) != 0 { + t.Errorf("FilterUnusedModels() enums = %v, want none", keptEnums) + } +} + func TestFilterUnusedModelsDropsEverything(t *testing.T) { t.Parallel() cases := []struct { diff --git a/internal/transform/mysql_sql.go b/internal/transform/mysql_sql.go new file mode 100644 index 00000000..cb1c28d1 --- /dev/null +++ b/internal/transform/mysql_sql.go @@ -0,0 +1,127 @@ +package transform + +import ( + "strings" +) + +// rewriteMySQLSQL converts sqlc's MySQL placeholders into pyformat style: +// every ? becomes %s, and every literal % is doubled, since PyMySQL and +// asyncmy interpolate the whole query text with Python %-formatting once +// parameters are passed - including string literals and comments. String +// literals, backtick identifiers, and comments are tracked so a ? inside +// them stays text. Only default sql_mode lexing is supported: sqlc's +// dolphin (TiDB) parser lexes with backslash escapes on and treats "..." +// as a string, so any query that reached the plugin already parsed under +// those rules; NO_BACKSLASH_ESCAPES and ANSI_QUOTES are deliberately +// unsupported. +func rewriteMySQLSQL(sql string) string { + var out strings.Builder + out.Grow(len(sql) + len(sql)/8) + for i := 0; i < len(sql); { + c := sql[i] + switch { + case c == '?': + // MySQL has no ?N syntax (that is sqlite-only), so digits after + // ? are ordinary text. + out.WriteString("%s") + i++ + case c == '%': + out.WriteString("%%") + i++ + case c == '\'' || c == '"': + end := scanEscapedString(sql, i, c) + writeDoubled(&out, sql[i:end]) + i = end + case c == '`': + end := scanQuoted(sql, i, '`') + writeDoubled(&out, sql[i:end]) + i = end + case c == '#': + end := scanLineEnd(sql, i) + writeDoubled(&out, sql[i:end]) + i = end + case c == '-' && strings.HasPrefix(sql[i:], "--") && isMySQLLineComment(sql, i): + end := scanLineEnd(sql, i) + writeDoubled(&out, sql[i:end]) + i = end + case c == '/' && strings.HasPrefix(sql[i:], "/*!"): + // MySQL executes /*! version comments and sqlc's parser agrees: + // the body is live SQL and a ? inside it is a real parameter. + // Emit the opener and scan the body with the normal rules; the + // closing */ falls through the default case as ordinary text. + out.WriteString("/*!") + i += len("/*!") + case c == '/' && strings.HasPrefix(sql[i:], "/*"): + end := scanMySQLBlockComment(sql, i) + writeDoubled(&out, sql[i:end]) + i = end + default: + out.WriteByte(c) + i++ + } + } + + return out.String() +} + +// isMySQLLineComment reports whether the -- at i starts a comment. MySQL +// requires the second dash to be followed by whitespace, a control +// character, or end of input; "a--1" is double unary minus, not a comment. +// When it is not a comment the caller copies the dashes as ordinary text. +// (DEL needs no arm: sqlc's parser rejects a bare 0x7f at generate time.) +func isMySQLLineComment(sql string, i int) bool { + if i+2 >= len(sql) { + return true + } + + return sql[i+2] <= ' ' +} + +// scanLineEnd returns the index of the \n terminating a line comment at or +// after i, or end of input. MySQL and sqlc's dolphin parser end -- and # +// comments only at \n (a bare \r is comment text, unlike PostgreSQL). The +// terminator itself is not consumed; the caller copies it as ordinary text. +func scanLineEnd(sql string, i int) int { + end := strings.IndexByte(sql[i:], '\n') + if end == -1 { + return len(sql) + } + + return i + end +} + +// scanEscapedString returns the index after a string literal starting at i, +// honoring backslash escapes and quote doubling. MySQL applies these rules +// to both '...' and "..."; PostgreSQL to E'...' (via scanStringLiteral). An +// unterminated literal swallows the rest of the input. +func scanEscapedString(sql string, i int, quote byte) int { + j := i + 1 + for j < len(sql) { + switch { + case sql[j] == '\\': + j += 2 + case sql[j] != quote: + j++ + case j+1 < len(sql) && sql[j+1] == quote: + j += 2 + default: + return j + 1 + } + } + + return len(sql) +} + +// scanMySQLBlockComment returns the index after a /* */ comment starting at +// i. MySQL block comments do not nest: the first */ ends the comment. /*+ +// optimizer hints and sqlc's /*SLICE:name*/ markers scan the same way; /*! +// version comments never reach here (their body is live SQL). +func scanMySQLBlockComment(sql string, i int) int { + body := i + len("/*") + end := strings.Index(sql[body:], "*/") + if end == -1 { + return len(sql) + } + + return body + end + len("*/") +} diff --git a/internal/transform/mysql_sql_test.go b/internal/transform/mysql_sql_test.go new file mode 100644 index 00000000..538dd1a6 --- /dev/null +++ b/internal/transform/mysql_sql_test.go @@ -0,0 +1,211 @@ +package transform + +import "testing" + +func TestRewriteMySQLSQL(t *testing.T) { + t.Parallel() + cases := []struct { + name string + sql string + want string + }{ + { + name: "no placeholders or percents is unchanged", + sql: "SELECT id, name FROM t", + want: "SELECT id, name FROM t", + }, + { + name: "single parameter at end of input", + sql: "SELECT id FROM t WHERE id = ?", + want: "SELECT id FROM t WHERE id = %s", + }, + { + name: "multiple parameters", + sql: "INSERT INTO t (a, b, c) VALUES (?, ?, ?)", + want: "INSERT INTO t (a, b, c) VALUES (%s, %s, %s)", + }, + { + name: "parameter at start of input", + sql: "? = ?", + want: "%s = %s", + }, + { + name: "digits after a placeholder stay text", + sql: "SELECT ?1", + want: "SELECT %s1", + }, + { + name: "modulo operator is doubled", + sql: "SELECT id % 2 FROM t WHERE id = ?", + want: "SELECT id %% 2 FROM t WHERE id = %s", + }, + { + name: "literal percent in string is doubled", + sql: "SELECT ? WHERE note LIKE '50%' OR note LIKE 'a%b'", + want: "SELECT %s WHERE note LIKE '50%%' OR note LIKE 'a%%b'", + }, + { + name: "percent in comments is doubled", + sql: "SELECT ? -- 50%\n# 10%\n/* 5% */", + want: "SELECT %s -- 50%%\n# 10%%\n/* 5%% */", + }, + { + name: "trailing lone percent", + sql: "SELECT 100 %", + want: "SELECT 100 %%", + }, + { + name: "placeholder inside single-quoted string stays text", + sql: "SELECT '?', 'it''s ?', ?", + want: "SELECT '?', 'it''s ?', %s", + }, + { + name: "backslash-escaped quote keeps the string closed", + sql: `SELECT 'It\'s ok', ?`, + want: `SELECT 'It\'s ok', %s`, + }, + { + name: "escaped backslash before closing quote", + sql: `SELECT 'a\\', ?`, + want: `SELECT 'a\\', %s`, + }, + { + name: "placeholder inside double-quoted string stays text", + sql: `SELECT "?", "a""b ?", ?`, + want: `SELECT "?", "a""b ?", %s`, + }, + { + name: "backslash-escaped double quote keeps the string closed", + sql: `SELECT "he said \" ?", ?`, + want: `SELECT "he said \" ?", %s`, + }, + { + name: "backtick identifier stays text", + sql: "SELECT `weird?col`, `a``b ?` FROM t WHERE x = ?", + want: "SELECT `weird?col`, `a``b ?` FROM t WHERE x = %s", + }, + { + name: "backslash is not an escape inside backticks", + sql: "SELECT `a\\` FROM t WHERE x = ?", + want: "SELECT `a\\` FROM t WHERE x = %s", + }, + { + name: "double dash before digit is not a comment", + sql: "SELECT a--1 FROM t WHERE b = ?", + want: "SELECT a--1 FROM t WHERE b = %s", + }, + { + name: "double dash before letter is not a comment", + sql: "SELECT a--x, ?", + want: "SELECT a--x, %s", + }, + { + name: "line comment kills the first placeholder only", + sql: "SELECT a -- comment ?\nFROM t WHERE b = ?", + want: "SELECT a -- comment ?\nFROM t WHERE b = %s", + }, + { + name: "double dash followed by tab is a comment", + sql: "SELECT a --\tdead ?\n, ?", + want: "SELECT a --\tdead ?\n, %s", + }, + { + name: "double dash at end of input is a comment", + sql: "SELECT ? --", + want: "SELECT %s --", + }, + { + name: "bare carriage return stays inside a line comment", + sql: "SELECT ? -- note ?\r, ?\nAND ?", + want: "SELECT %s -- note ?\r, ?\nAND %s", + }, + { + name: "hash comment stays text", + sql: "#comment ?\n?", + want: "#comment ?\n%s", + }, + { + name: "bare carriage return stays inside a hash comment", + sql: "SELECT 1 # note ?\r, ?\n? ", + want: "SELECT 1 # note ?\r, ?\n%s ", + }, + { + name: "block comment stays text", + sql: "SELECT ? /* not ? */ FROM t", + want: "SELECT %s /* not ? */ FROM t", + }, + { + name: "block comments do not nest", + sql: "SELECT /* a /* b */ ? */ 1", + want: "SELECT /* a /* b */ %s */ 1", + }, + { + name: "version comment body is live SQL", + sql: "/*!40101 SET x=1, y='50%'*/ SELECT ?", + want: "/*!40101 SET x=1, y='50%%'*/ SELECT %s", + }, + { + name: "placeholder inside a version comment is rewritten", + sql: "SELECT id FROM t /*! WHERE id = ? AND s = 'a?b' */", + want: "SELECT id FROM t /*! WHERE id = %s AND s = 'a?b' */", + }, + { + name: "optimizer hint comment stays text", + sql: "SELECT /*+ MAX_EXECUTION_TIME(1000) ? */ id FROM t WHERE id = ?", + want: "SELECT /*+ MAX_EXECUTION_TIME(1000) ? */ id FROM t WHERE id = %s", + }, + { + name: "slice marker placeholder is rewritten", + sql: "SELECT * FROM t WHERE id IN (/*SLICE:ids*/?)", + want: "SELECT * FROM t WHERE id IN (/*SLICE:ids*/%s)", + }, + { + name: "reused slice marker", + sql: "SELECT * FROM t WHERE a IN (/*SLICE:ids*/?) AND b IN (/*SLICE:ids*/?)", + want: "SELECT * FROM t WHERE a IN (/*SLICE:ids*/%s) AND b IN (/*SLICE:ids*/%s)", + }, + { + name: "unterminated string swallows the rest", + sql: "SELECT ?, 'open ?", + want: "SELECT %s, 'open ?", + }, + { + name: "unterminated string with trailing backslash swallows the rest", + sql: `SELECT ?, 'open\`, + want: `SELECT %s, 'open\`, + }, + { + name: "unterminated backtick identifier swallows the rest", + sql: "SELECT ?, `open ?", + want: "SELECT %s, `open ?", + }, + { + name: "unterminated block comment swallows the rest", + sql: "SELECT ? /* dangling ?", + want: "SELECT %s /* dangling ?", + }, + { + name: "multi-byte characters inside a string stay text", + sql: "SELECT 'entr\xc3\xa9e ?', ?", + want: "SELECT 'entr\xc3\xa9e ?', %s", + }, + { + name: "slash without comment is copied", + sql: "SELECT ? / 2", + want: "SELECT %s / 2", + }, + { + name: "single dash is copied", + sql: "SELECT ? - 2", + want: "SELECT %s - 2", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if got := rewriteMySQLSQL(tc.sql); got != tc.want { + t.Errorf("rewriteMySQLSQL() = %q, want %q", got, tc.want) + } + }) + } +} diff --git a/internal/transform/psycopg_sql.go b/internal/transform/psycopg_sql.go index c82d02ac..dc8f7169 100644 --- a/internal/transform/psycopg_sql.go +++ b/internal/transform/psycopg_sql.go @@ -137,21 +137,8 @@ func scanStringLiteral(sql string, i int, escapes bool) int { if !escapes { return scanQuoted(sql, i, '\'') } - j := i + 1 - for j < len(sql) { - switch { - case sql[j] == '\\': - j += 2 - case sql[j] != '\'': - j++ - case j+1 < len(sql) && sql[j+1] == '\'': - j += 2 - default: - return j + 1 - } - } - return len(sql) + return scanEscapedString(sql, i, '\'') } // scanQuoted returns the index after a quoted region starting at i, where a diff --git a/internal/transform/queries.go b/internal/transform/queries.go index 328c299e..e2f9dff3 100644 --- a/internal/transform/queries.go +++ b/internal/transform/queries.go @@ -13,9 +13,9 @@ import ( // plainParams builds the expanded parameter list of a query, keeping names // clear of the implicit first argument and of the locals generated bodies // introduce. -func (t *Transformer) plainParams(pluginQuery *plugin.Query) []model.QueryValue { - params := make([]model.QueryValue, 0, len(pluginQuery.Params)) - seen := make(map[string]int, len(pluginQuery.Params)+1) +func (t *Transformer) plainParams(pluginQuery *plugin.Query, pluginParams []*plugin.Parameter) []model.QueryValue { + params := make([]model.QueryValue, 0, len(pluginParams)) + seen := make(map[string]int, len(pluginParams)+1) // The implicit first argument of every generated function must never // collide with a parameter name: a column literally named "conn" (or // "self" in classes mode) would otherwise produce a duplicate argument @@ -28,7 +28,7 @@ func (t *Transformer) plainParams(pluginQuery *plugin.Query) []model.QueryValue // Slice queries materialize the expanded SQL into a local named "sql" // before any parameter is read; a parameter with that name would be // silently overwritten by the query text. - for _, param := range pluginQuery.Params { + for _, param := range pluginParams { if param.GetColumn().GetIsSqlcSlice() { seen["sql"]++ @@ -50,23 +50,125 @@ func (t *Transformer) plainParams(pluginQuery *plugin.Query) []model.QueryValue seen["_decode_hook"]++ } } - for _, param := range pluginQuery.Params { + // MySQL bodies are cursor-based: every command except :many opens "cur", + // :one fetches into "row", and :many may define a nested "_decode_hook". + if t.config.SqlDriver.IsMysql() { + switch pluginQuery.Cmd { + case metadata.CmdMany: + seen["_decode_hook"]++ + case metadata.CmdOne: + seen["cur"]++ + seen["row"]++ + default: + seen["cur"]++ + } + } + // sqlc's MySQL engine emits one parameter per occurrence of a reused + // named argument too (its dialect binds every use site separately). + // Same-named NAMED parameters are one logical argument - sqlc's own + // MySQL codegen merges them - so repeats keep their positional binding + // slot but drop out of the signature. A use site that rejects NULL + // makes the merged parameter non-optional. Bare "?" parameters that + // merely inherit the same column name stay distinct (IsNamedParam is + // false for them; sqlc generates name/name_2 arguments). + firstIdx := make(map[string]int, len(pluginParams)) + for _, param := range pluginParams { + typ := t.buildPyType(param.Column) + rawName := model.ParamName(param) + if t.config.SqlDriver.IsMysql() && typ.SqlcSliceName == "" && param.GetColumn().GetIsNamedParam() { + if idx, found := firstIdx[rawName]; found { + if !typ.IsNullable { + params[idx].Type.IsNullable = false + } + params = append(params, model.QueryValue{ + Name: params[idx].Name, + Type: params[idx].Type, + Number: param.Number, + Repeated: true, + }) + + continue + } + firstIdx[rawName] = len(params) + } params = append(params, model.QueryValue{ - Name: model.DedupName(model.ParamName(param), seen), - Type: t.buildPyType(param.Column), + Name: model.DedupName(rawName, seen), + Type: typ, Number: param.Number, }) } + // A later occurrence can have tightened the first one's nullability + // after repeats were copied; conversions must use one type everywhere. + firstByName := make(map[string]model.PyType, len(firstIdx)) + for _, idx := range firstIdx { + firstByName[params[idx].Name] = params[idx].Type + } + for i := range params { + if params[i].Repeated { + params[i].Type = firstByName[params[i].Name] + } + } return params } +// logicalParamCount counts parameters as the generated signature will show +// them: MySQL's per-occurrence duplicates of one reused NAMED argument count +// once; bare "?" parameters count per occurrence even when they share a name. +func (t *Transformer) logicalParamCount(params []*plugin.Parameter) int { + if !t.config.SqlDriver.IsMysql() { + return len(params) + } + count := 0 + names := make(map[string]struct{}, len(params)) + for _, param := range params { + if !param.GetColumn().GetIsNamedParam() { + count++ + + continue + } + name := model.ParamName(param) + if _, found := names[name]; found { + continue + } + names[name] = struct{}{} + count++ + } + + return count +} + +// dedupSliceParams collapses the per-occurrence duplicates sqlc's MySQL +// engine emits for a reused sqlc.slice (one parameter per marker use site; +// the other engines merge them). Without the collapse both the plain +// signature and a bundled Params class would repeat the argument. The +// driver still binds one copy of the sequence per marker occurrence. +func (t *Transformer) dedupSliceParams(params []*plugin.Parameter) []*plugin.Parameter { + if !t.config.SqlDriver.IsMysql() { + return params + } + seen := make(map[string]struct{}) + out := make([]*plugin.Parameter, 0, len(params)) + for _, param := range params { + if param.GetColumn().GetIsSqlcSlice() { + name := param.GetColumn().GetName() + if _, found := seen[name]; found { + continue + } + seen[name] = struct{}{} + } + out = append(out, param) + } + + return out +} + // bundledParams builds the single Params-class parameter used by :copyfrom // and query_parameter_limit queries. Field order follows the sqlc parameter // array, and each field keeps its parameter number for name-binding drivers. -func (t *Transformer) bundledParams(pluginQuery *plugin.Query, queryName string, isCopyFrom bool) []model.QueryValue { - columns := make([]pyColumn, 0, len(pluginQuery.Params)) - for _, param := range pluginQuery.Params { +func (t *Transformer) bundledParams(pluginParams []*plugin.Parameter, queryName string, isCopyFrom bool) []model.QueryValue { + columns := make([]pyColumn, 0, len(pluginParams)) + for _, param := range pluginParams { columns = append(columns, pyColumn{ column: param.Column, embed: nil, @@ -74,7 +176,7 @@ func (t *Transformer) bundledParams(pluginQuery *plugin.Query, queryName string, } table := t.columnsToClass(queryName+"Params", columns) for i := range table.Columns { - table.Columns[i].Number = pluginQuery.Params[i].Number + table.Columns[i].Number = pluginParams[i].Number } return []model.QueryValue{ @@ -125,11 +227,25 @@ func (t *Transformer) BuildQueries(tables []model.Table) []model.Query { len(pluginQuery.Params) > 0 && query.Cmd != metadata.CmdCopyFrom { query.SQL = rewritePsycopgSQL(pluginQuery.Text) } + // The MySQL drivers interpolate pyformat placeholders the same way, + // but sqlc's MySQL engine emits "?": rewrite parameterized queries to + // "%s" once here. Parameterless queries stay untouched EXCEPT :many: + // QueryResults always passes its (possibly empty) args tuple, and the + // drivers interpolate whenever args is not None, so a :many query + // needs its "%" doubled even without parameters. + if t.config.SqlDriver.IsMysql() && + (len(pluginQuery.Params) > 0 || query.Cmd == metadata.CmdMany) { + query.SQL = rewriteMySQLSQL(pluginQuery.Text) + } - if query.Cmd == metadata.CmdCopyFrom || t.config.IsOverQueryParameterLimit(len(pluginQuery.Params)) { - query.Params = t.bundledParams(pluginQuery, query.QueryName, query.Cmd == metadata.CmdCopyFrom) + // Dedup before the limit check: a reused sqlc.slice or named + // argument is one logical parameter and must not push a query into + // bundled mode by itself. + pluginParams := t.dedupSliceParams(pluginQuery.Params) + if query.Cmd == metadata.CmdCopyFrom || t.config.IsOverQueryParameterLimit(t.logicalParamCount(pluginParams)) { + query.Params = t.bundledParams(pluginParams, query.QueryName, query.Cmd == metadata.CmdCopyFrom) } else { - query.Params = t.plainParams(pluginQuery) + query.Params = t.plainParams(pluginQuery, pluginParams) } if query.Cmd == metadata.CmdExecLastId { diff --git a/internal/transform/queries_test.go b/internal/transform/queries_test.go index bfbf46ef..3697a2f8 100644 --- a/internal/transform/queries_test.go +++ b/internal/transform/queries_test.go @@ -232,6 +232,51 @@ func TestBuildQueriesImplicitArgCollision(t *testing.T) { column: "row", want: "row_2", }, + // MySQL bodies open a cursor named "cur" for every command except + // :many, :one also fetches into "row", and :many may define a nested + // "_decode_hook". + { + name: "cur collides in a pymysql exec query", + driver: config.SQLDriverPymysql, + column: "cur", + want: "cur_2", + }, + { + name: "cur collides in a pymysql one query", + driver: config.SQLDriverPymysql, + cmd: ":one", + column: "cur", + want: "cur_2", + }, + { + name: "row collides in a pymysql one query", + driver: config.SQLDriverPymysql, + cmd: ":one", + column: "row", + want: "row_2", + }, + { + name: "decode hook collides in a pymysql many query", + driver: config.SQLDriverPymysql, + cmd: ":many", + column: "_decode_hook", + want: "_decode_hook_2", + }, + {name: "cur is free in a pymysql many query", driver: config.SQLDriverPymysql, cmd: ":many", column: "cur", want: "cur"}, + // The async flavor shares every MySQL reservation. + { + name: "cur collides in an asyncmy exec query", + driver: config.SQLDriverAsyncmy, + column: "cur", + want: "cur_2", + }, + { + name: "row collides in an asyncmy one query", + driver: config.SQLDriverAsyncmy, + cmd: ":one", + column: "row", + want: "row_2", + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -653,3 +698,212 @@ func TestBuildQueriesPsycopgSQLRewrite(t *testing.T) { }) } } + +func TestBuildQueriesMySQLReusedNamedArgMerge(t *testing.T) { + t.Parallel() + // sqlc's MySQL engine emits one parameter per occurrence of a reused + // named argument (IsNamedParam); the merged signature shows it once, + // repeats keep their positional binding slot, and a non-null use site + // tightens the merged nullability on every occurrence. + nullableCol := queryCol("n", "text", nil) + nullableCol.NotNull = false + nullableCol.IsNamedParam = true + namedCol := queryCol("n", "text", nil) + namedCol.IsNamedParam = true + for _, driver := range []config.SQLDriver{config.SQLDriverPymysql, config.SQLDriverAsyncmy} { + query := buildSingleQuery(t, &config.Config{SqlDriver: driver}, &plugin.Query{ + Name: "ReusedNamedArg", + Cmd: ":exec", + Text: "UPDATE test_authors SET name = sqlc.arg(n) WHERE name = sqlc.arg(n)", + Params: []*plugin.Parameter{ + {Number: 1, Column: nullableCol}, + {Number: 2, Column: namedCol}, + }, + }) + want := []model.QueryValue{ + {Name: "n", Type: model.PyType{SQLType: "text", Type: "str", DefaultType: "str"}, Number: 1}, + {Name: "n", Type: model.PyType{SQLType: "text", Type: "str", DefaultType: "str"}, Number: 2, Repeated: true}, + } + if !reflect.DeepEqual(query.Params, want) { + t.Errorf("BuildQueries(%s) params = %+v, want %+v", driver, query.Params, want) + } + } +} + +func TestBuildQueriesMySQLKeepsPositionalSameNamedParams(t *testing.T) { + t.Parallel() + // Bare "?" parameters carry IsNamedParam false. Two of them on columns + // that share a name are distinct arguments (sqlc's own MySQL codegen + // generates Name and Name_2): merging would make a rename such as + // "SET name = ? WHERE name = ?" bind one value to both slots. + query := buildSingleQuery(t, &config.Config{SqlDriver: config.SQLDriverPymysql}, &plugin.Query{ + Name: "RenameAuthor", + Cmd: ":exec", + Text: "UPDATE test_authors SET name = ? WHERE name = ?", + Params: []*plugin.Parameter{ + {Number: 1, Column: queryCol("n", "text", nil)}, + {Number: 2, Column: queryCol("n", "text", nil)}, + }, + }) + if len(query.Params) != 2 || query.Params[0].Name != "n" || query.Params[1].Name != "n_2" { + t.Fatalf("params = %+v, want distinct n and n_2", query.Params) + } + if query.Params[0].Repeated || query.Params[1].Repeated { + t.Fatal("positional params must not be marked Repeated") + } +} + +func TestBuildQueriesSqliteKeepsSameNamedParams(t *testing.T) { + t.Parallel() + // sqlite numbers its placeholders, so same-named parameters are + // distinct arguments and must stay separate. + query := buildSingleQuery(t, &config.Config{SqlDriver: config.SQLDriverSQLite}, &plugin.Query{ + Name: "SameNames", + Cmd: ":exec", + Text: "UPDATE test_authors SET name = ?1 WHERE name != ?2", + Params: []*plugin.Parameter{ + {Number: 1, Column: queryCol("n", "text", nil)}, + {Number: 2, Column: queryCol("n", "text", nil)}, + }, + }) + if len(query.Params) != 2 || query.Params[0].Name != "n" || query.Params[1].Name != "n_2" { + t.Fatalf("params = %+v, want distinct n and n_2", query.Params) + } + if query.Params[0].Repeated || query.Params[1].Repeated { + t.Fatal("sqlite params must not be marked Repeated") + } +} + +func TestBuildQueriesMySQLSliceDedup(t *testing.T) { + t.Parallel() + sliceCol := func() *plugin.Column { + column := queryCol("ids", "int4", nil) + column.IsSqlcSlice = true + + return column + } + pySlice := model.PyType{SQLType: "int4", Type: "int", DefaultType: "int", IsList: true, SqlcSliceName: "ids"} + // sqlc's MySQL engine emits one parameter per occurrence of a reused + // sqlc.slice, which the MySQL drivers collapse into a single argument; + // the sqlite drivers keep one parameter per occurrence. + cases := []struct { + name string + driver config.SQLDriver + want []model.QueryValue + }{ + { + name: "pymysql collapses repeated slice params", + driver: config.SQLDriverPymysql, + want: []model.QueryValue{{Name: "ids", Type: pySlice, Number: 1}}, + }, + { + name: "asyncmy collapses repeated slice params", + driver: config.SQLDriverAsyncmy, + want: []model.QueryValue{{Name: "ids", Type: pySlice, Number: 1}}, + }, + { + name: "sqlite3 keeps one param per occurrence", + driver: config.SQLDriverSQLite, + want: []model.QueryValue{ + {Name: "ids", Type: pySlice, Number: 1}, + {Name: "ids_2", Type: pySlice, Number: 2}, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + query := buildSingleQuery(t, &config.Config{SqlDriver: tc.driver}, &plugin.Query{ + Name: "DeleteAuthors", + Cmd: ":exec", + Text: "DELETE FROM test_authors WHERE id IN (/*SLICE:ids*/?) OR id IN (/*SLICE:ids*/?)", + Params: []*plugin.Parameter{ + {Number: 1, Column: sliceCol()}, + {Number: 2, Column: sliceCol()}, + }, + }) + + if len(query.Params) != len(tc.want) { + t.Fatalf("Params = %+v, want %d params", query.Params, len(tc.want)) + } + for i, want := range tc.want { + if query.Params[i] != want { + t.Errorf("Params[%d] = %+v, want %+v", i, query.Params[i], want) + } + } + }) + } +} + +func TestBuildQueriesMySQLSQLRewrite(t *testing.T) { + t.Parallel() + cases := []struct { + name string + driver config.SQLDriver + query *plugin.Query + wantSQL string + }{ + { + name: "parameterized query is rewritten for pymysql", + driver: config.SQLDriverPymysql, + query: &plugin.Query{ + Name: "GetAuthor", + Cmd: ":one", + Text: "SELECT name FROM test_authors WHERE id = ? AND name LIKE 'a%'", + Params: []*plugin.Parameter{ + {Number: 1, Column: queryCol("id", "int4", nil)}, + }, + Columns: []*plugin.Column{queryCol("name", "text", nil)}, + }, + wantSQL: "SELECT name FROM test_authors WHERE id = %s AND name LIKE 'a%%'", + }, + { + name: "parameterized query is rewritten for asyncmy", + driver: config.SQLDriverAsyncmy, + query: &plugin.Query{ + Name: "GetAuthor", + Cmd: ":one", + Text: "SELECT name FROM test_authors WHERE id = ?", + Params: []*plugin.Parameter{ + {Number: 1, Column: queryCol("id", "int4", nil)}, + }, + Columns: []*plugin.Column{queryCol("name", "text", nil)}, + }, + wantSQL: "SELECT name FROM test_authors WHERE id = %s", + }, + { + name: "parameterless query stays untouched", + driver: config.SQLDriverPymysql, + query: &plugin.Query{ + Name: "CountAuthors", + Cmd: ":one", + Text: "SELECT count(*) FROM test_authors WHERE name LIKE 'a%'", + Columns: []*plugin.Column{queryCol("count", "int8", nil)}, + }, + wantSQL: "SELECT count(*) FROM test_authors WHERE name LIKE 'a%'", + }, + { + name: "asyncpg keeps native placeholders", + driver: config.SQLDriverAsyncpg, + query: &plugin.Query{ + Name: "GetAuthor", + Cmd: ":one", + Text: "SELECT name FROM test_authors WHERE id = $1 AND name LIKE 'a%'", + Params: []*plugin.Parameter{ + {Number: 1, Column: queryCol("id", "int4", nil)}, + }, + Columns: []*plugin.Column{queryCol("name", "text", nil)}, + }, + wantSQL: "SELECT name FROM test_authors WHERE id = $1 AND name LIKE 'a%'", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + query := buildSingleQuery(t, &config.Config{SqlDriver: tc.driver}, tc.query) + if query.SQL != tc.wantSQL { + t.Errorf("SQL = %q, want %q", query.SQL, tc.wantSQL) + } + }) + } +} diff --git a/internal/transform/type.go b/internal/transform/type.go index d5abdb48..ee4f2f12 100644 --- a/internal/transform/type.go +++ b/internal/transform/type.go @@ -10,8 +10,8 @@ import ( "github.com/sqlc-dev/plugin-sdk-go/sdk" ) -func (t *Transformer) convertType(columnType *plugin.Identifier) string { - return t.typeConversionFunc(t.req, t.config, columnType) +func (t *Transformer) convertType(pluginColumn *plugin.Column) string { + return t.typeConversionFunc(t.req, t.config, pluginColumn) } func (t *Transformer) buildPyType(pluginColumn *plugin.Column) model.PyType { @@ -19,7 +19,7 @@ func (t *Transformer) buildPyType(pluginColumn *plugin.Column) model.PyType { // (conversion registration, docstrings): sqlite DDL keeps the author's // casing ("DATETIME"), so normalize once here instead of in every consumer. columnType := strings.ToLower(sdk.DataType(pluginColumn.Type)) - strType := t.convertType(pluginColumn.Type) + strType := t.convertType(pluginColumn) // A sqlc.slice parameter is never optional, even on a nullable column: // the generated expansion calls len() on it, and "no values" is an empty diff --git a/internal/types/common.go b/internal/types/common.go index 101ff151..559ed0d8 100644 --- a/internal/types/common.go +++ b/internal/types/common.go @@ -4,10 +4,15 @@ import ( "fmt" "github.com/rayakame/sqlc-gen-better-python/internal/config" + "github.com/rayakame/sqlc-gen-better-python/internal/log" + "github.com/rayakame/sqlc-gen-better-python/internal/model" "github.com/sqlc-dev/plugin-sdk-go/plugin" ) -type TypeConversionFunc func(*plugin.GenerateRequest, *config.Config, *plugin.Identifier) string +// TypeConversionFunc maps one column to its Python type annotation. It +// receives the whole column, not just the type identifier: MySQL needs +// Column.Length to tell tinyint(1) (bool) from tinyint (int). +type TypeConversionFunc func(*plugin.GenerateRequest, *config.Config, *plugin.Column) string func GetTypeConversionFunc(engine string) (TypeConversionFunc, error) { switch engine { @@ -15,7 +20,48 @@ func GetTypeConversionFunc(engine string) (TypeConversionFunc, error) { return PostgresTypeToPython, nil case "sqlite": return SqliteTypeToPython, nil + case "mysql": + return MysqlTypeToPython, nil default: return nil, fmt.Errorf("engine %q is not supported", engine) } } + +// resolveCatalogEnum resolves an unrecognized column type against the +// catalog's enums, shared by the PostgreSQL and MySQL mappers: parse the +// (possibly schema-qualified) identifier, fall back to the default schema, +// skip the system schemas, and qualify the enum name only outside the +// default schema. engineName appears in the unknown-type debug log; Any is +// returned when nothing matches. +func resolveCatalogEnum(req *plugin.GenerateRequest, config *config.Config, engineName, columnType string) string { + columnRelation, err := parseIdentifierString(columnType) + if err != nil { + log.L().LogErr("error trying to parse identifier string", err) + + return Any + } + if columnRelation.Schema == "" { + columnRelation.Schema = req.Catalog.DefaultSchema + } + for _, schema := range req.Catalog.Schemas { + if schema.Name == PgCatalog || schema.Name == InformationSchema { + continue + } + if schema.Name != columnRelation.Schema { + continue + } + for _, enum := range schema.Enums { + if columnRelation.Name != enum.Name { + continue + } + if schema.Name == req.Catalog.DefaultSchema { + return enumsPrefix + model.EnumName(config, enum.Name, "") + } + + return enumsPrefix + model.EnumName(config, enum.Name, schema.Name) + } + } + log.L().Log("unknown " + engineName + " type: " + columnType) + + return Any +} diff --git a/internal/types/common_test.go b/internal/types/common_test.go index de2aedc8..c07eb51f 100644 --- a/internal/types/common_test.go +++ b/internal/types/common_test.go @@ -17,7 +17,8 @@ func TestGetTypeConversionFunc(t *testing.T) { }{ {name: "postgresql", engine: "postgresql", want: types.PostgresTypeToPython}, {name: "sqlite", engine: "sqlite", want: types.SqliteTypeToPython}, - {name: "unsupported engine", engine: "mysql", wantErr: `engine "mysql" is not supported`}, + {name: "mysql", engine: "mysql", want: types.MysqlTypeToPython}, + {name: "unsupported engine", engine: "clickhouse", wantErr: `engine "clickhouse" is not supported`}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/types/mysql.go b/internal/types/mysql.go new file mode 100644 index 00000000..943606fc --- /dev/null +++ b/internal/types/mysql.go @@ -0,0 +1,84 @@ +package types + +import ( + "strings" + + "github.com/rayakame/sqlc-gen-better-python/internal/config" + "github.com/sqlc-dev/plugin-sdk-go/plugin" + "github.com/sqlc-dev/plugin-sdk-go/sdk" +) + +// Spellings shared with postgresql.go and sqlite.go, hoisted so goconst does +// not flag the third literal occurrence. +const ( + sqlInteger = "integer" + sqlSmallint = "smallint" + sqlBigint = "bigint" + sqlDoublePrecision = "double precision" + sqlReal = "real" + sqlDecimal = "decimal" + sqlNumeric = "numeric" + sqlText = "text" + sqlJSON = "json" + sqlBlob = "blob" + sqlDate = "date" + pyDate = "datetime.date" + pyDatetime = "datetime.datetime" + enumsPrefix = "enums." +) + +func MysqlTypeToPython(req *plugin.GenerateRequest, config *config.Config, pluginColumn *plugin.Column) string { + columnType := strings.ToLower(sdk.DataType(pluginColumn.Type)) + + switch columnType { + case "tinyint": + // MySQL bool columns are tinyint(1); any other length is a plain int. + if pluginColumn.GetLength() == 1 { + return Bool + } + + return Int + case Bool, Boolean: + return Bool + case Int, sqlInteger, "mediumint", sqlSmallint, sqlBigint, "year", "serial", "bigint unsigned", "bigint signed": + return Int + case Float, "double", sqlDoublePrecision, sqlReal: + return Float + case sqlDecimal, "dec", "fixed", sqlNumeric: + // Drivers return decimal.Decimal for all of these. Differs from + // sqlite, where numeric maps to float. + return Decimal + case "varchar", "char", sqlText, "tinytext", "mediumtext", "longtext": + return Str + case "set": + // PyMySQL-family drivers return SET as a comma-joined str. + return Str + case "enum": + // Expression-derived enum columns lose their column identity and + // arrive as the bare "enum" type. Column-typed enums arrive as + // synthesized named types handled in the default branch. + return Str + case sqlBlob, "binary", "varbinary", "tinyblob", "mediumblob", "longblob": + return Memoryview + case "bit": + // Drivers return BIT as raw bytes. + return Memoryview + case sqlDate: + return pyDate + case "datetime", "timestamp": + return pyDatetime + case "time": + // PyMySQL-family drivers return TIME columns as timedelta, not + // datetime.time. + return "datetime.timedelta" + case sqlJSON: + return Str + case "any": + return Any + default: + // sqlc's dolphin engine materializes each MySQL enum column as a + // catalog enum named "{table}_{column}" in the default schema + // ("public"), so the same catalog scan as PostgreSQL resolves them. + return resolveCatalogEnum(req, config, "MySQL", columnType) + } +} diff --git a/internal/types/mysql_test.go b/internal/types/mysql_test.go new file mode 100644 index 00000000..902ed2eb --- /dev/null +++ b/internal/types/mysql_test.go @@ -0,0 +1,104 @@ +package types_test + +import ( + "testing" + + "github.com/rayakame/sqlc-gen-better-python/internal/config" + "github.com/rayakame/sqlc-gen-better-python/internal/types" + "github.com/sqlc-dev/plugin-sdk-go/plugin" +) + +func TestMysqlTypeToPython(t *testing.T) { + t.Parallel() + req := &plugin.GenerateRequest{ + Catalog: &plugin.Catalog{ + DefaultSchema: "public", + Schemas: []*plugin.Schema{ + // The same enum name inside system schemas proves they are + // skipped during enum resolution. + {Name: types.PgCatalog, Enums: []*plugin.Enum{{Name: "authors_status", Vals: []string{"x"}}}}, + {Name: types.InformationSchema, Enums: []*plugin.Enum{{Name: "authors_status", Vals: []string{"x"}}}}, + {Name: "public", Enums: []*plugin.Enum{{Name: "authors_status", Vals: []string{"draft"}}}}, + {Name: "other", Enums: []*plugin.Enum{{Name: "other_mood", Vals: []string{"y"}}}}, + }, + }, + } + conf := &config.Config{} + cases := []struct { + name string + pluginType *plugin.Identifier + length int32 + want string + }{ + {"tinyint length 1 is bool", &plugin.Identifier{Name: "tinyint"}, 1, types.Bool}, + {"tinyint length 4", &plugin.Identifier{Name: "tinyint"}, 4, types.Int}, + {"tinyint without length", &plugin.Identifier{Name: "tinyint"}, 0, types.Int}, + {"tinyint uppercase is lowered", &plugin.Identifier{Name: "TINYINT"}, 0, types.Int}, + {"bool", &plugin.Identifier{Name: "bool"}, 0, types.Bool}, + {"boolean", &plugin.Identifier{Name: "boolean"}, 0, types.Bool}, + {"int", &plugin.Identifier{Name: "int"}, 0, types.Int}, + {"integer", &plugin.Identifier{Name: "integer"}, 0, types.Int}, + {"mediumint", &plugin.Identifier{Name: "mediumint"}, 0, types.Int}, + {"smallint", &plugin.Identifier{Name: "smallint"}, 0, types.Int}, + {"bigint", &plugin.Identifier{Name: "bigint"}, 0, types.Int}, + {"year", &plugin.Identifier{Name: "year"}, 0, types.Int}, + {"serial", &plugin.Identifier{Name: "serial"}, 0, types.Int}, + {"bigint unsigned", &plugin.Identifier{Name: "bigint unsigned"}, 0, types.Int}, + {"bigint signed", &plugin.Identifier{Name: "bigint signed"}, 0, types.Int}, + {"float", &plugin.Identifier{Name: "float"}, 0, types.Float}, + {"double", &plugin.Identifier{Name: "double"}, 0, types.Float}, + {"double precision", &plugin.Identifier{Name: "double precision"}, 0, types.Float}, + {"real", &plugin.Identifier{Name: "real"}, 0, types.Float}, + {"decimal", &plugin.Identifier{Name: "decimal"}, 0, types.Decimal}, + {"dec", &plugin.Identifier{Name: "dec"}, 0, types.Decimal}, + {"fixed", &plugin.Identifier{Name: "fixed"}, 0, types.Decimal}, + {"numeric", &plugin.Identifier{Name: "numeric"}, 0, types.Decimal}, + {"varchar", &plugin.Identifier{Name: "varchar"}, 0, types.Str}, + {"char", &plugin.Identifier{Name: "char"}, 0, types.Str}, + {"text", &plugin.Identifier{Name: "text"}, 0, types.Str}, + {"tinytext", &plugin.Identifier{Name: "tinytext"}, 0, types.Str}, + {"mediumtext", &plugin.Identifier{Name: "mediumtext"}, 0, types.Str}, + {"longtext", &plugin.Identifier{Name: "longtext"}, 0, types.Str}, + {"set", &plugin.Identifier{Name: "set"}, 0, types.Str}, + {"bare enum", &plugin.Identifier{Name: "enum"}, 0, types.Str}, + {"blob", &plugin.Identifier{Name: "blob"}, 0, "memoryview"}, + {"binary", &plugin.Identifier{Name: "binary"}, 0, "memoryview"}, + {"varbinary", &plugin.Identifier{Name: "varbinary"}, 0, "memoryview"}, + {"tinyblob", &plugin.Identifier{Name: "tinyblob"}, 0, "memoryview"}, + {"mediumblob", &plugin.Identifier{Name: "mediumblob"}, 0, "memoryview"}, + {"longblob", &plugin.Identifier{Name: "longblob"}, 0, "memoryview"}, + {"bit", &plugin.Identifier{Name: "bit"}, 0, "memoryview"}, + {"date", &plugin.Identifier{Name: "date"}, 0, "datetime.date"}, + {"datetime", &plugin.Identifier{Name: "datetime"}, 0, "datetime.datetime"}, + {"datetime uppercase is lowered", &plugin.Identifier{Name: "DATETIME"}, 0, "datetime.datetime"}, + {"timestamp", &plugin.Identifier{Name: "timestamp"}, 0, "datetime.datetime"}, + {"time", &plugin.Identifier{Name: "time"}, 0, "datetime.timedelta"}, + {"json", &plugin.Identifier{Name: "json"}, 0, types.Str}, + {"any", &plugin.Identifier{Name: "any"}, 0, types.Any}, + {"enum in default schema", &plugin.Identifier{Name: "authors_status"}, 0, "enums.AuthorsStatus"}, + {"enum in named schema", &plugin.Identifier{Schema: "other", Name: "other_mood"}, 0, "enums.OtherOtherMood"}, + { + "system-schema qualified enum is not resolved", + &plugin.Identifier{Schema: types.PgCatalog, Name: "authors_status"}, + 0, + types.Any, + }, + { + "information_schema qualified enum is not resolved", + &plugin.Identifier{Schema: types.InformationSchema, Name: "authors_status"}, + 0, + types.Any, + }, + {"invalid four-part identifier", &plugin.Identifier{Name: "a.b.c.d"}, 0, types.Any}, + {"unknown type", &plugin.Identifier{Name: "geometry"}, 0, types.Any}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + col := &plugin.Column{Type: tc.pluginType, Length: tc.length} + if got := types.MysqlTypeToPython(req, conf, col); got != tc.want { + t.Errorf("MysqlTypeToPython(%+v, length=%d) = %q, want %q", tc.pluginType, tc.length, got, tc.want) + } + }) + } +} diff --git a/internal/types/postgresql.go b/internal/types/postgresql.go index 9634d0e3..c8b75a24 100644 --- a/internal/types/postgresql.go +++ b/internal/types/postgresql.go @@ -5,8 +5,6 @@ import ( "strings" "github.com/rayakame/sqlc-gen-better-python/internal/config" - "github.com/rayakame/sqlc-gen-better-python/internal/log" - "github.com/rayakame/sqlc-gen-better-python/internal/model" "github.com/sqlc-dev/plugin-sdk-go/plugin" "github.com/sqlc-dev/plugin-sdk-go/sdk" ) @@ -39,8 +37,8 @@ func parseIdentifierString(name string) (*plugin.Identifier, error) { } } -func PostgresTypeToPython(req *plugin.GenerateRequest, config *config.Config, pluginType *plugin.Identifier) string { - columnType := sdk.DataType(pluginType) +func PostgresTypeToPython(req *plugin.GenerateRequest, config *config.Config, pluginColumn *plugin.Column) string { + columnType := sdk.DataType(pluginColumn.Type) switch columnType { case "serial", "serial4", @@ -94,35 +92,6 @@ func PostgresTypeToPython(req *plugin.GenerateRequest, config *config.Config, pl case "ltree", "lquery", "ltxtquery": return Str default: - columnRelation, err := parseIdentifierString(columnType) - if err != nil { - log.L().LogErr("error trying to parse identifier string", err) - - return Any - } - if columnRelation.Schema == "" { - columnRelation.Schema = req.Catalog.DefaultSchema - } - for _, schema := range req.Catalog.Schemas { - if schema.Name == PgCatalog || schema.Name == InformationSchema { - continue - } - if schema.Name != columnRelation.Schema { - continue - } - for _, enum := range schema.Enums { - if columnRelation.Name != enum.Name { - continue - } - if schema.Name == req.Catalog.DefaultSchema { - return "enums." + model.EnumName(config, enum.Name, "") - } - - return "enums." + model.EnumName(config, enum.Name, schema.Name) - } - } - log.L().Log("unknown PostgreSQL type: " + columnType) - - return Any + return resolveCatalogEnum(req, config, "PostgreSQL", columnType) } } diff --git a/internal/types/postgresql_test.go b/internal/types/postgresql_test.go index 89fdd062..73ac373e 100644 --- a/internal/types/postgresql_test.go +++ b/internal/types/postgresql_test.go @@ -116,7 +116,7 @@ func TestPostgresTypeToPython(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - if got := types.PostgresTypeToPython(req, conf, tc.pluginType); got != tc.want { + if got := types.PostgresTypeToPython(req, conf, &plugin.Column{Type: tc.pluginType}); got != tc.want { t.Errorf("PostgresTypeToPython(%+v) = %q, want %q", tc.pluginType, got, tc.want) } }) diff --git a/internal/types/sqlite.go b/internal/types/sqlite.go index bcc61840..1dc116c5 100644 --- a/internal/types/sqlite.go +++ b/internal/types/sqlite.go @@ -9,8 +9,8 @@ import ( "github.com/sqlc-dev/plugin-sdk-go/sdk" ) -func SqliteTypeToPython(_ *plugin.GenerateRequest, _ *config.Config, pluginType *plugin.Identifier) string { - columnType := strings.ToLower(sdk.DataType(pluginType)) +func SqliteTypeToPython(_ *plugin.GenerateRequest, _ *config.Config, pluginColumn *plugin.Column) string { + columnType := strings.ToLower(sdk.DataType(pluginColumn.Type)) switch columnType { case Int, "integer", "tinyint", "smallint", "mediumint", "bigint", "unsignedbigint", "int2", "int8", "bigserial": diff --git a/internal/types/sqlite_test.go b/internal/types/sqlite_test.go index 163f2c7b..9a441830 100644 --- a/internal/types/sqlite_test.go +++ b/internal/types/sqlite_test.go @@ -52,7 +52,7 @@ func TestSqliteTypeToPython(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - if got := types.SqliteTypeToPython(nil, nil, tc.pluginType); got != tc.want { + if got := types.SqliteTypeToPython(nil, nil, &plugin.Column{Type: tc.pluginType}); got != tc.want { t.Errorf("SqliteTypeToPython(%+v) = %q, want %q", tc.pluginType, got, tc.want) } }) diff --git a/internal/writer/docstrings.go b/internal/writer/docstrings.go index 0e0e7609..34d2f9d2 100644 --- a/internal/writer/docstrings.go +++ b/internal/writer/docstrings.go @@ -393,8 +393,9 @@ func (w *CodeWriter) WriteQueryFunctionDocstring(lvl int, query *model.Query, co case metadata.CmdExecRows: summaryFmt = "Execute SQL query with `name: %s %s` and return the number of affected rows." // The sqlite drivers and psycopg return cursor.rowcount, which is -1 - // for statements without a row count; only asyncpg's status-string - // parse falls back to 0. + // for statements without a row count; asyncpg's status-string parse + // and the MySQL drivers' affected-row count (0 in the OK packet for + // DDL) fall back to 0. noRows := "0" if w.docstringDriver == config.SQLDriverAioSQLite || w.docstringDriver == config.SQLDriverSQLite || w.docstringDriver.IsPsycopg() { diff --git a/internal/writer/docstrings_test.go b/internal/writer/docstrings_test.go index 79febfac..4bdf978e 100644 --- a/internal/writer/docstrings_test.go +++ b/internal/writer/docstrings_test.go @@ -774,6 +774,46 @@ func TestWriteQueryFunctionDocstring(t *testing.T) { ` """`, ), }, + { + name: "execrows pymysql documents the OK packet's 0", + conv: config.DocstringConventionGoogle, + driver: config.SQLDriverPymysql, + omitSQL: true, + write: func(w *writer.CodeWriter) { + w.WriteQueryFunctionDocstring(1, execRowsQuery, "pymysql.Connection", nil, "int") + }, + want: lines( + " \"\"\"Execute SQL query with `name: TouchAuthors :execrows` and return the number of affected rows.", + ``, + ` Args:`, + ` conn:`, + " Connection object of type `pymysql.Connection` used to execute the query.", + ``, + ` Returns:`, + " The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`.", + ` """`, + ), + }, + { + name: "execrows asyncmy documents the OK packet's 0", + conv: config.DocstringConventionGoogle, + driver: config.SQLDriverAsyncmy, + omitSQL: true, + write: func(w *writer.CodeWriter) { + w.WriteQueryFunctionDocstring(1, execRowsQuery, "asyncmy.Connection", nil, "int") + }, + want: lines( + " \"\"\"Execute SQL query with `name: TouchAuthors :execrows` and return the number of affected rows.", + ``, + ` Args:`, + ` conn:`, + " Connection object of type `asyncmy.Connection` used to execute the query.", + ``, + ` Returns:`, + " The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`.", + ` """`, + ), + }, { name: "execrows sqlite3 pep257 normalizes sql lines", conv: config.DocstringConventionPEP257, diff --git a/noxfile.py b/noxfile.py index 5ae250bc..c93f29df 100644 --- a/noxfile.py +++ b/noxfile.py @@ -21,14 +21,17 @@ "sqlite3": PATH_TO_PROJECT / "test" / "driver_sqlite3", "turso_sync": PATH_TO_PROJECT / "test" / "driver_turso_sync", "turso_async": PATH_TO_PROJECT / "test" / "driver_turso_async", + "pymysql": PATH_TO_PROJECT / "test" / "driver_pymysql", + "asyncmy": PATH_TO_PROJECT / "test" / "driver_asyncmy", } SQLC_CONFIGS = ["sqlc.yaml"] options.default_venv_backend = "uv" -options.sessions = ["ruff_format", "asyncpg", "psycopg_async", "psycopg_sync", "sqlite3", "aiosqlite", "turso_sync", "turso_async", "pyright", "ruff", "pytest"] +options.sessions = ["ruff_format", "asyncpg", "psycopg_async", "psycopg_sync", "sqlite3", "aiosqlite", "turso_sync", "turso_async", "pymysql", "asyncmy", "pyright", "ruff", "pytest"] DEFAULT_POSTGRES_URI = os.getenv("POSTGRES_URI", "postgresql://root:187187@localhost:5432/root") +DEFAULT_MYSQL_URI = os.getenv("MYSQL_URI", "mysql://root:187187@localhost:3306/root") # uv_sync taken from: https://github.com/hikari-py/hikari/blob/master/pipelines/nox.py#L48 @@ -244,11 +247,48 @@ def ruff_check(session: nox.Session) -> None: session.run("ruff", "check", *session.posargs) +@nox.session(reuse_venv=True) +def pymysql(session: nox.Session) -> None: + uv_sync(session, include_self=True, groups=["pyright", "ruff"]) + + sqlc_generate(session, "pymysql") + session.run("pyright", DRIVER_PATHS["pymysql"]) + session.run("ruff", "check", *session.posargs, DRIVER_PATHS["pymysql"]) + + +@nox.session(reuse_venv=True) +def pymysql_check(session: nox.Session) -> None: + uv_sync(session, include_self=True, groups=["pyright", "ruff"]) + + sqlc_check(session, "pymysql") + session.run("pyright", DRIVER_PATHS["pymysql"]) + session.run("ruff", "check", *session.posargs, DRIVER_PATHS["pymysql"]) + + +@nox.session(reuse_venv=True) +def asyncmy(session: nox.Session) -> None: + uv_sync(session, include_self=True, groups=["pyright", "ruff"]) + + sqlc_generate(session, "asyncmy") + session.run("pyright", DRIVER_PATHS["asyncmy"]) + session.run("ruff", "check", *session.posargs, DRIVER_PATHS["asyncmy"]) + + +@nox.session(reuse_venv=True) +def asyncmy_check(session: nox.Session) -> None: + uv_sync(session, include_self=True, groups=["pyright", "ruff"]) + + sqlc_check(session, "asyncmy") + session.run("pyright", DRIVER_PATHS["asyncmy"]) + session.run("ruff", "check", *session.posargs, DRIVER_PATHS["asyncmy"]) + + PYTEST_RUN_FLAGS = [ "--showlocals", "--show-capture", "all", f"--db={DEFAULT_POSTGRES_URI}", + f"--mysql-db={DEFAULT_MYSQL_URI}", ] PYTESTCOVERAGE_FLAGS = [ "--cov", diff --git a/pyproject.toml b/pyproject.toml index 9e4f80e9..409db6d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,12 @@ dependencies = [ "msgspec>=0.19.0", "pydantic>=2.9.0", "pyturso>=0.7.0", + "pymysql>=1.2.0", + "asyncmy>=0.2.14", + # caching_sha2_password full auth (the mysql:9 default) over plaintext + # TCP needs cryptography in both MySQL drivers; without it the first + # connection against a fresh server raises RuntimeError. + "cryptography>=45.0.0", ] [dependency-groups] @@ -27,7 +33,8 @@ dev-complete = [ pyright = [ "asyncpg-stubs>=0.30.1", "pyright>=1.1.400", - { include-group = "pytest" } + "types-pymysql>=1.2.0.20260807", + { include-group = "pytest" }, ] coverage = [ "coverage[toml]>=7.8.0", diff --git a/ruff.toml b/ruff.toml index 643d24a6..58dee917 100644 --- a/ruff.toml +++ b/ruff.toml @@ -47,6 +47,9 @@ ignore = [ # emitted at runtime so annotations stay introspectable. "**/omit_tc/{classes,functions}/*.py" = [ "typing-only-standard-library-import", + # Drivers without a module-level alias referencing their own module + # (turso, the MySQL drivers) have no runtime use ruff can see. + "typing-only-third-party-import", ] "**/{msgspec}/{classes,functions}/*.py" = [ # Ruff doesn't understand that we actually documented the exceptions raised when using pep257 diff --git a/scripts/build/build.bat b/scripts/build/build.bat index 1e49a851..6cd5d773 100644 --- a/scripts/build/build.bat +++ b/scripts/build/build.bat @@ -5,7 +5,7 @@ REM ------------------------------ REM 1) CONFIGURATION - add folders here REM (paths are relative to repo root) REM ------------------------------ -set "TARGET_DIRS=test\driver_asyncpg test\driver_psycopg_async test\driver_psycopg_sync test\driver_aiosqlite test\driver_sqlite3 test\driver_turso_sync test\driver_turso_async" +set "TARGET_DIRS=test\driver_asyncpg test\driver_psycopg_async test\driver_psycopg_sync test\driver_aiosqlite test\driver_sqlite3 test\driver_turso_sync test\driver_turso_async test\driver_pymysql test\driver_asyncmy" set "SQLC_CONFIG_NAMES=sqlc.yaml" REM ------------------------------ diff --git a/scripts/build/build.sh b/scripts/build/build.sh index 3298938d..23047046 100644 --- a/scripts/build/build.sh +++ b/scripts/build/build.sh @@ -4,7 +4,7 @@ set -euo pipefail # ------------------------------ # 1) CONFIGURATION # ------------------------------ -TARGET_DIRS=("test/driver_asyncpg" "test/driver_psycopg_async" "test/driver_psycopg_sync" "test/driver_aiosqlite" "test/driver_sqlite3" "test/driver_turso_sync" "test/driver_turso_async") +TARGET_DIRS=("test/driver_asyncpg" "test/driver_psycopg_async" "test/driver_psycopg_sync" "test/driver_aiosqlite" "test/driver_sqlite3" "test/driver_turso_sync" "test/driver_turso_async" "test/driver_pymysql" "test/driver_asyncmy") SQLC_CONFIG_NAMES=("sqlc.yaml") # ------------------------------ diff --git a/sqlc.yaml b/sqlc.yaml index 6d70f5d2..53d4d545 100644 --- a/sqlc.yaml +++ b/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: python wasm: url: file://sqlc-gen-better-python.wasm - sha256: eb001e364c1b8088e47bb43d4c9addf51ed7249f97db2fd1052cc14893989bed + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 sql: - schema: test/schema.sql queries: test/queries.sql diff --git a/test/conftest.py b/test/conftest.py index c1e5576b..0e0e2455 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -24,11 +24,14 @@ import sqlite3 import sys import typing +import urllib.parse import aiosqlite +import asyncmy import asyncpg import psycopg import psycopg.rows +import pymysql import pytest import turso import turso.aio @@ -44,6 +47,8 @@ SQLITE3_PATH = pathlib.Path(__file__).parent / "driver_sqlite3" TURSO_SYNC_PATH = pathlib.Path(__file__).parent / "driver_turso_sync" TURSO_ASYNC_PATH = pathlib.Path(__file__).parent / "driver_turso_async" +PYMYSQL_PATH = pathlib.Path(__file__).parent / "driver_pymysql" +ASYNCMY_PATH = pathlib.Path(__file__).parent / "driver_asyncmy" # All postgres suites share the same tables, so their session teardowns must # clean the same list; a single constant keeps them from diverging. @@ -61,6 +66,26 @@ """ +# Both MySQL suites share the same tables, so their session teardowns must +# clean the same list; DELETE keeps the AUTO_INCREMENT counters, which the +# suites therefore never assert on. +_MYSQL_CLEANUP: typing.Final = ( + "DELETE FROM test_mysql_types", + "DELETE FROM test_inner_mysql_types", + "DELETE FROM test_type_override", + "DELETE FROM test_enum_override", + "DELETE FROM test_case_sensitivity", + "DELETE FROM test_reserved_args", + "DELETE FROM test_execlastid", + "DELETE FROM test_field_namings", + "DELETE FROM test_invalid_identifiers", + "DELETE FROM `3rd_party_stats`", + "DELETE FROM test_slice", + "DELETE FROM test_converters", + "DELETE FROM test_dbtype_override", +) + + def pytest_addoption(parser: pytest.Parser) -> None: parser.addoption( "--db", @@ -74,6 +99,12 @@ def pytest_addoption(parser: pytest.Parser) -> None: default="sqlite.db", help="the sqlite db uri needed to connect to the db", ) + parser.addoption( + "--mysql-db", + action="store", + default="mysql://root:187187@localhost:3306/root", + help="the mysql db uri needed to connect to the db", + ) def get_dsn(config: pytest.Config) -> str: @@ -84,6 +115,29 @@ def get_dsn(config: pytest.Config) -> str: return dsn +def get_mysql_kwargs(config: pytest.Config) -> dict[str, typing.Any]: + dsn = config.getoption("--mysql-db") + if dsn is None or not isinstance(dsn, str): + msg = "--mysql-db option is missing" + raise ValueError(msg) + # PyMySQL and asyncmy take keyword arguments, not a URI. Credentials + # may be percent-encoded in the URI form. + parsed = urllib.parse.urlsplit(dsn) + return { + "host": parsed.hostname or "localhost", + "port": parsed.port or 3306, + "user": urllib.parse.unquote(parsed.username or "root"), + "password": urllib.parse.unquote(parsed.password or ""), + "database": urllib.parse.unquote(parsed.path.lstrip("/")), + } + + +def _mysql_statements(schema: str) -> list[str]: + # Neither MySQL driver has executescript, and execute runs a single + # statement; the schema files contain no semicolons inside literals. + return [stmt for stmt in schema.split(";") if stmt.strip()] + + def get_sqlite_dsn(config: pytest.Config) -> str: dsn = config.getoption("--sqlite-db") if dsn is None or not isinstance(dsn, str): @@ -199,6 +253,53 @@ async def turso_async_conn() -> collections.abc.AsyncGenerator[turso.aio.Connect await conn.close() +@pytest.fixture(scope="session") +def pymysql_conn( + request: pytest.FixtureRequest, +) -> collections.abc.Generator[pymysql.Connection, typing.Any]: + # autocommit matches the psycopg fixtures' per-statement semantics and + # keeps one failing test from poisoning the shared connection. + conn = pymysql.connect(autocommit=True, **get_mysql_kwargs(request.config)) + with conn.cursor() as cur: + for stmt in _mysql_statements((PYMYSQL_PATH / "schema.sql").read_text()): + cur.execute(stmt) + yield conn + with conn.cursor() as cur: + for stmt in _MYSQL_CLEANUP: + cur.execute(stmt) + conn.close() + + +@pytest_asyncio.fixture(scope="session", loop_scope="session") +async def asyncmy_conn( + request: pytest.FixtureRequest, +) -> collections.abc.AsyncGenerator[asyncmy.Connection, typing.Any]: + # asyncmy's stubs leave connect/execute parameters unannotated, which + # pyright strict reports; the generated asyncmy modules carry the same + # suppression. + conn = await asyncmy.connect(autocommit=True, **get_mysql_kwargs(request.config)) # pyright: ignore[reportUnknownMemberType] + async with conn.cursor() as cur: + for stmt in _mysql_statements((ASYNCMY_PATH / "schema.sql").read_text()): + await cur.execute(stmt) # pyright: ignore[reportUnknownMemberType] + yield conn + async with conn.cursor() as cur: + for stmt in _MYSQL_CLEANUP: + await cur.execute(stmt) # pyright: ignore[reportUnknownMemberType] + await conn.ensure_closed() + + +def pymysql_delete_all(config: pytest.Config) -> None: + # An aborted run leaves the fixed-id rows behind and the next run fails + # with an IntegrityError; the schemas recreate everything (IF NOT EXISTS). + conn = pymysql.connect(autocommit=True, **get_mysql_kwargs(config)) + with conn.cursor() as cur: + for stmt in _mysql_statements((PYMYSQL_PATH / "schema.sql").read_text()): + cur.execute(stmt) + for stmt in _MYSQL_CLEANUP: + cur.execute(stmt) + conn.close() + + async def asyncpg_delete_all(dsn: str) -> None: conn = await asyncpg.connect(dsn) @@ -244,3 +345,4 @@ async def _delete_all(conf: pytest.Config) -> None: await aiosqlite_delete_all(aiosqlite_dsn) asyncio.run(_delete_all(session.config)) + pymysql_delete_all(session.config) diff --git a/test/converters.py b/test/converters.py index 62deacc3..fe7cfe8c 100644 --- a/test/converters.py +++ b/test/converters.py @@ -22,6 +22,7 @@ from __future__ import annotations import dataclasses +import datetime import json import pathlib import typing @@ -101,3 +102,27 @@ def decode_label(value: str) -> pathlib.PurePosixPath: The parsed label. """ return pathlib.PurePosixPath(value) + + +def encode_stamp(value: str) -> datetime.datetime: + """Deserialize an ISO string back into the datetime the column expects. + + Used by the DATETIME db_type override of the MySQL fixtures. + + Returns + ------- + datetime.datetime + The parsed datetime to store. + """ + return datetime.datetime.fromisoformat(value) + + +def decode_stamp(value: datetime.datetime) -> str: + """Serialize a datetime column value into its ISO string form. + + Returns + ------- + str + The ISO 8601 string form of the stored datetime. + """ + return value.isoformat() diff --git a/test/driver_aiosqlite/attrs/classes/__init__.py b/test/driver_aiosqlite/attrs/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_aiosqlite/attrs/classes/__init__.py +++ b/test/driver_aiosqlite/attrs/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_aiosqlite/attrs/classes/models.py b/test/driver_aiosqlite/attrs/classes/models.py index 9f2e2abb..73abe31d 100644 --- a/test/driver_aiosqlite/attrs/classes/models.py +++ b/test/driver_aiosqlite/attrs/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_aiosqlite/attrs/classes/queries.py b/test/driver_aiosqlite/attrs/classes/queries.py index 1e1380a2..4bc3b707 100644 --- a/test/driver_aiosqlite/attrs/classes/queries.py +++ b/test/driver_aiosqlite/attrs/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_aiosqlite/attrs/functions/__init__.py b/test/driver_aiosqlite/attrs/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_aiosqlite/attrs/functions/__init__.py +++ b/test/driver_aiosqlite/attrs/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_aiosqlite/attrs/functions/models.py b/test/driver_aiosqlite/attrs/functions/models.py index 9f2e2abb..73abe31d 100644 --- a/test/driver_aiosqlite/attrs/functions/models.py +++ b/test/driver_aiosqlite/attrs/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_aiosqlite/attrs/functions/queries.py b/test/driver_aiosqlite/attrs/functions/queries.py index db7d908e..6dc899cb 100644 --- a/test/driver_aiosqlite/attrs/functions/queries.py +++ b/test/driver_aiosqlite/attrs/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_aiosqlite/dataclass/classes/__init__.py b/test/driver_aiosqlite/dataclass/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_aiosqlite/dataclass/classes/__init__.py +++ b/test/driver_aiosqlite/dataclass/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_aiosqlite/dataclass/classes/models.py b/test/driver_aiosqlite/dataclass/classes/models.py index dc3b902c..aba98a13 100644 --- a/test/driver_aiosqlite/dataclass/classes/models.py +++ b/test/driver_aiosqlite/dataclass/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_aiosqlite/dataclass/classes/queries.py b/test/driver_aiosqlite/dataclass/classes/queries.py index 8a4de0d7..8923a48c 100644 --- a/test/driver_aiosqlite/dataclass/classes/queries.py +++ b/test/driver_aiosqlite/dataclass/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_aiosqlite/dataclass/functions/__init__.py b/test/driver_aiosqlite/dataclass/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_aiosqlite/dataclass/functions/__init__.py +++ b/test/driver_aiosqlite/dataclass/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_aiosqlite/dataclass/functions/models.py b/test/driver_aiosqlite/dataclass/functions/models.py index edba6f59..03436fa9 100644 --- a/test/driver_aiosqlite/dataclass/functions/models.py +++ b/test/driver_aiosqlite/dataclass/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_aiosqlite/dataclass/functions/queries.py b/test/driver_aiosqlite/dataclass/functions/queries.py index 9cd1ca89..cdd1a8fe 100644 --- a/test/driver_aiosqlite/dataclass/functions/queries.py +++ b/test/driver_aiosqlite/dataclass/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_aiosqlite/dataclass/functions/queries_slice.py b/test/driver_aiosqlite/dataclass/functions/queries_slice.py index 098cf7eb..05c47356 100644 --- a/test/driver_aiosqlite/dataclass/functions/queries_slice.py +++ b/test/driver_aiosqlite/dataclass/functions/queries_slice.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_slice.sql """Module containing queries from file queries_slice.sql.""" diff --git a/test/driver_aiosqlite/msgspec/classes/__init__.py b/test/driver_aiosqlite/msgspec/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_aiosqlite/msgspec/classes/__init__.py +++ b/test/driver_aiosqlite/msgspec/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_aiosqlite/msgspec/classes/models.py b/test/driver_aiosqlite/msgspec/classes/models.py index eabe2ef3..219c0288 100644 --- a/test/driver_aiosqlite/msgspec/classes/models.py +++ b/test/driver_aiosqlite/msgspec/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_aiosqlite/msgspec/classes/queries.py b/test/driver_aiosqlite/msgspec/classes/queries.py index b7686f7d..0ae1f56c 100644 --- a/test/driver_aiosqlite/msgspec/classes/queries.py +++ b/test/driver_aiosqlite/msgspec/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_aiosqlite/msgspec/functions/__init__.py b/test/driver_aiosqlite/msgspec/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_aiosqlite/msgspec/functions/__init__.py +++ b/test/driver_aiosqlite/msgspec/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_aiosqlite/msgspec/functions/models.py b/test/driver_aiosqlite/msgspec/functions/models.py index eabe2ef3..219c0288 100644 --- a/test/driver_aiosqlite/msgspec/functions/models.py +++ b/test/driver_aiosqlite/msgspec/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_aiosqlite/msgspec/functions/queries.py b/test/driver_aiosqlite/msgspec/functions/queries.py index 4a6f5c2a..df0b1e6a 100644 --- a/test/driver_aiosqlite/msgspec/functions/queries.py +++ b/test/driver_aiosqlite/msgspec/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_aiosqlite/pydantic/classes/__init__.py b/test/driver_aiosqlite/pydantic/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_aiosqlite/pydantic/classes/__init__.py +++ b/test/driver_aiosqlite/pydantic/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_aiosqlite/pydantic/classes/models.py b/test/driver_aiosqlite/pydantic/classes/models.py index 3bb851b3..ccd04019 100644 --- a/test/driver_aiosqlite/pydantic/classes/models.py +++ b/test/driver_aiosqlite/pydantic/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_aiosqlite/pydantic/classes/queries.py b/test/driver_aiosqlite/pydantic/classes/queries.py index 90d46cf0..75662b02 100644 --- a/test/driver_aiosqlite/pydantic/classes/queries.py +++ b/test/driver_aiosqlite/pydantic/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_aiosqlite/pydantic/functions/__init__.py b/test/driver_aiosqlite/pydantic/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_aiosqlite/pydantic/functions/__init__.py +++ b/test/driver_aiosqlite/pydantic/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_aiosqlite/pydantic/functions/models.py b/test/driver_aiosqlite/pydantic/functions/models.py index 3bb851b3..ccd04019 100644 --- a/test/driver_aiosqlite/pydantic/functions/models.py +++ b/test/driver_aiosqlite/pydantic/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_aiosqlite/pydantic/functions/queries.py b/test/driver_aiosqlite/pydantic/functions/queries.py index 2688ff75..04ad2b87 100644 --- a/test/driver_aiosqlite/pydantic/functions/queries.py +++ b/test/driver_aiosqlite/pydantic/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_aiosqlite/sqlc-gen-better-python.wasm b/test/driver_aiosqlite/sqlc-gen-better-python.wasm index 7d2fae76..dfb69b04 100644 Binary files a/test/driver_aiosqlite/sqlc-gen-better-python.wasm and b/test/driver_aiosqlite/sqlc-gen-better-python.wasm differ diff --git a/test/driver_aiosqlite/sqlc.yaml b/test/driver_aiosqlite/sqlc.yaml index 32b2f67e..0ef00e37 100644 --- a/test/driver_aiosqlite/sqlc.yaml +++ b/test/driver_aiosqlite/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: python wasm: url: file://sqlc-gen-better-python.wasm - sha256: eb001e364c1b8088e47bb43d4c9addf51ed7249f97db2fd1052cc14893989bed + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 sql: - schema: schema.sql queries: queries.sql diff --git a/test/driver_asyncmy/__init__.py b/test/driver_asyncmy/__init__.py new file mode 100644 index 00000000..0b34101d --- /dev/null +++ b/test/driver_asyncmy/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for asyncmy tests.""" diff --git a/test/driver_asyncmy/attrs/__init__.py b/test/driver_asyncmy/attrs/__init__.py new file mode 100644 index 00000000..0b34101d --- /dev/null +++ b/test/driver_asyncmy/attrs/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for asyncmy tests.""" diff --git a/test/driver_asyncmy/attrs/classes/__init__.py b/test/driver_asyncmy/attrs/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_asyncmy/attrs/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncmy/attrs/classes/enums.py b/test/driver_asyncmy/attrs/classes/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_asyncmy/attrs/classes/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_asyncmy/attrs/classes/models.py b/test/driver_asyncmy/attrs/classes/models.py new file mode 100644 index 00000000..06cc170d --- /dev/null +++ b/test/driver_asyncmy/attrs/classes/models.py @@ -0,0 +1,392 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestConverter", + "TestDbtypeOverride", + "TestEnumOverride", + "TestExeclastid", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestSlice", + "TestTypeOverride", +) + +import attrs +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_asyncmy.attrs.classes import enums + import collections.abc + import datetime + import decimal + + +@attrs.define() +class Model3RdPartyStat: + """Model representing Model3RdPartyStat. + + Attributes + ---------- + id_ : int + total : int + + """ + + id_: int + total: int + + +@attrs.define() +class TestCaseSensitivity: + """Model representing TestCaseSensitivity. + + Attributes + ---------- + id_ : int + upper_dt : datetime.datetime + prec_dec : decimal.Decimal + + """ + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +@attrs.define() +class TestConverter: + """Model representing TestConverter. + + Attributes + ---------- + id_ : int + prefs : str + maybe_prefs : str | None + tags : str + + """ + + id_: int + prefs: str + maybe_prefs: str | None + tags: str + + +@attrs.define() +class TestDbtypeOverride: + """Model representing TestDbtypeOverride. + + Attributes + ---------- + id_ : int + happened_at : datetime.datetime + + """ + + id_: int + happened_at: datetime.datetime + + +@attrs.define() +class TestEnumOverride: + """Model representing TestEnumOverride. + + Attributes + ---------- + id_ : int + mood_test : str + + """ + + id_: int + mood_test: str + + +@attrs.define() +class TestExeclastid: + """Model representing TestExeclastid. + + Attributes + ---------- + id_ : int + name : str + + """ + + id_: int + name: str + + +@attrs.define() +class TestFieldNaming: + """Model representing TestFieldNaming. + + Attributes + ---------- + id_ : int + outputs : str + + """ + + id_: int + outputs: str + + +@attrs.define() +class TestInnerMysqlType: + """Model representing TestInnerMysqlType. + + Attributes + ---------- + table_id : int + int_test : int | None + integer_test : int | None + mediumint_test : int | None + smallint_test : int | None + tinyint_test : int | None + bigint_test : int | None + int_unsigned_test : int | None + bigint_unsigned_test : int | None + year_test : int | None + tinyint1_test : bool | None + bool_test : bool | None + boolean_test : bool | None + float_test : float | None + double_test : float | None + double_precision_test : float | None + real_test : float | None + decimal_test : decimal.Decimal | None + numeric_test : decimal.Decimal | None + char_test : str | None + varchar_test : str | None + tinytext_test : str | None + text_test : str | None + mediumtext_test : str | None + longtext_test : str | None + binary_test : memoryview | None + varbinary_test : memoryview | None + tinyblob_test : memoryview | None + blob_test : memoryview | None + mediumblob_test : memoryview | None + longblob_test : memoryview | None + bit_test : memoryview | None + date_test : datetime.date | None + datetime_test : datetime.datetime | None + datetime6_test : datetime.datetime | None + timestamp_test : datetime.datetime | None + time_test : datetime.timedelta | None + json_test : str | None + mood : enums.TestInnerMysqlTypesMood | None + tag : enums.TestInnerMysqlTypesTag | None + + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +@attrs.define() +class TestInvalidIdentifier: + """Model representing TestInvalidIdentifier. + + Attributes + ---------- + id_ : int + column_3p_ : str | None + new_notes : str + column__pct : str | None + + """ + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +@attrs.define() +class TestMysqlType: + """Model representing TestMysqlType. + + Attributes + ---------- + id_ : int + int_test : int + integer_test : int + mediumint_test : int + smallint_test : int + tinyint_test : int + bigint_test : int + int_unsigned_test : int + bigint_unsigned_test : int + year_test : int + tinyint1_test : bool + bool_test : bool + boolean_test : bool + float_test : float + double_test : float + double_precision_test : float + real_test : float + decimal_test : decimal.Decimal + numeric_test : decimal.Decimal + char_test : str + varchar_test : str + tinytext_test : str + text_test : str + mediumtext_test : str + longtext_test : str + binary_test : memoryview + varbinary_test : memoryview + tinyblob_test : memoryview + blob_test : memoryview + mediumblob_test : memoryview + longblob_test : memoryview + bit_test : memoryview + date_test : datetime.date + datetime_test : datetime.datetime + datetime6_test : datetime.datetime + timestamp_test : datetime.datetime + time_test : datetime.timedelta + json_test : str + mood : enums.TestMysqlTypesMood + tag : enums.TestMysqlTypesTag + + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +@attrs.define() +class TestReservedArg: + """Model representing TestReservedArg. + + Attributes + ---------- + id_ : int + conn : str + + """ + + id_: int + conn: str + + +@attrs.define() +class TestSlice: + """Model representing TestSlice. + + Attributes + ---------- + id_ : int + name : str + note : str | None + + """ + + id_: int + name: str + note: str | None + + +@attrs.define() +class TestTypeOverride: + """Model representing TestTypeOverride. + + Attributes + ---------- + id_ : int + text_test : UserString | None + + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_asyncmy/attrs/classes/queries.py b/test/driver_asyncmy/attrs/classes/queries.py new file mode 100644 index 00000000..f41b3aee --- /dev/null +++ b/test/driver_asyncmy/attrs/classes/queries.py @@ -0,0 +1,1578 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Queries", + "QueryResults", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + import datetime + import decimal + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.attrs.classes import enums +from test.driver_asyncmy.attrs.classes import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db. + + Parameters + ---------- + conn + The connection object of type `asyncmy.Connection` used to execute queries. + sql + The SQL statement that will be executed when fetching/iterating. + decode_hook + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args + Arguments that should be sent when executing the sql query. + + """ + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance.""" + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns + ------- + QueryResults[T] + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns + ------- + collections.abc.Sequence[T] + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns + ------- + T + The next decoded result. + + Raises + ------ + StopAsyncIteration + When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +class Queries: + """Queries from file queries.sql. + + Parameters + ---------- + conn : asyncmy.Connection + The connection object used to execute queries. + + """ + + __slots__ = ("_conn",) + + def __init__(self, conn: asyncmy.Connection) -> None: + """Initialize the instance using the connection.""" + self._conn = conn + + @property + def conn(self) -> asyncmy.Connection: + """Connection object used to make queries. + + Returns + ------- + asyncmy.Connection + + """ + return self._conn + + async def insert_one_mysql_type( + self, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, + ) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Parameters + ---------- + id_ : int + int_test : int + integer_test : int + mediumint_test : int + smallint_test : int + tinyint_test : int + bigint_test : int + int_unsigned_test : int + bigint_unsigned_test : int + year_test : int + tinyint1_test : bool + bool_test : bool + boolean_test : bool + float_test : float + double_test : float + double_precision_test : float + real_test : float + decimal_test : decimal.Decimal + numeric_test : decimal.Decimal + char_test : str + varchar_test : str + tinytext_test : str + text_test : str + mediumtext_test : str + longtext_test : str + binary_test : memoryview + varbinary_test : memoryview + tinyblob_test : memoryview + blob_test : memoryview + mediumblob_test : memoryview + longblob_test : memoryview + bit_test : memoryview + date_test : datetime.date + datetime_test : datetime.datetime + datetime6_test : datetime.datetime + timestamp_test : datetime.datetime + time_test : datetime.timedelta + json_test : str + mood : enums.TestMysqlTypesMood + tag : enums.TestMysqlTypesTag + + """ + async with self._conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + async def insert_one_inner_mysql_type( + self, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, + ) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Parameters + ---------- + table_id : int + int_test : int | None + integer_test : int | None + mediumint_test : int | None + smallint_test : int | None + tinyint_test : int | None + bigint_test : int | None + int_unsigned_test : int | None + bigint_unsigned_test : int | None + year_test : int | None + tinyint1_test : bool | None + bool_test : bool | None + boolean_test : bool | None + float_test : float | None + double_test : float | None + double_precision_test : float | None + real_test : float | None + decimal_test : decimal.Decimal | None + numeric_test : decimal.Decimal | None + char_test : str | None + varchar_test : str | None + tinytext_test : str | None + text_test : str | None + mediumtext_test : str | None + longtext_test : str | None + binary_test : memoryview | None + varbinary_test : memoryview | None + tinyblob_test : memoryview | None + blob_test : memoryview | None + mediumblob_test : memoryview | None + longblob_test : memoryview | None + bit_test : memoryview | None + date_test : datetime.date | None + datetime_test : datetime.datetime | None + datetime6_test : datetime.datetime | None + timestamp_test : datetime.datetime | None + time_test : datetime.timedelta | None + json_test : str | None + mood : enums.TestInnerMysqlTypesMood | None + tag : enums.TestInnerMysqlTypesTag | None + + """ + async with self._conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + async def get_one_mysql_type(self, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + models.TestMysqlType + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + async def get_one_inner_mysql_type(self, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Parameters + ---------- + table_id : int + + Returns + ------- + models.TestInnerMysqlType + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + def get_many_mysql_type(self, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + QueryResults[models.TestMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(self._conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + def get_many_inner_mysql_type(self, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Parameters + ---------- + table_id : int + + Returns + ------- + QueryResults[models.TestInnerMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + def get_many_nullable_inner_mysql_type(self, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Parameters + ---------- + table_id : int + int_test : int | None + + Returns + ------- + QueryResults[models.TestInnerMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + async def get_one_date(self, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Parameters + ---------- + id_ : int + date_test : datetime.date + + Returns + ------- + datetime.date + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DATE, (id_, date_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_datetime(self, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Parameters + ---------- + id_ : int + datetime_test : datetime.datetime + + Returns + ------- + datetime.datetime + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_time(self, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Parameters + ---------- + id_ : int + time_test : datetime.timedelta + + Returns + ------- + datetime.timedelta + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_TIME, (id_, time_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_bool(self, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Parameters + ---------- + id_ : int + tinyint1_test : bool + + Returns + ------- + bool + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = await cur.fetchone() + if row is None: + return None + return bool(row[0]) + + async def get_one_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Parameters + ---------- + id_ : int + decimal_test : decimal.Decimal + + Returns + ------- + decimal.Decimal + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_blob(self, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Parameters + ---------- + id_ : int + blob_test : memoryview + + Returns + ------- + memoryview + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + async def get_one_bit(self, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + memoryview + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BIT, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + async def get_one_year(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_YEAR, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_json(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_JSON, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_mood(self, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Parameters + ---------- + id_ : int + mood : enums.TestMysqlTypesMood + + Returns + ------- + enums.TestMysqlTypesMood + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_MOOD, (id_, mood)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + async def get_one_tag(self, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + enums.TestMysqlTypesTag + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_TAG, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + def get_many_date(self, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Parameters + ---------- + id_ : int + date_test : datetime.date + + Returns + ------- + QueryResults[datetime.date] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(self._conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + def get_many_time(self, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Parameters + ---------- + id_ : int + time_test : datetime.timedelta + + Returns + ------- + QueryResults[datetime.timedelta] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(self._conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + def get_many_bool(self, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Parameters + ---------- + id_ : int + tinyint1_test : bool + + Returns + ------- + QueryResults[bool] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(self._conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + def get_many_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Parameters + ---------- + id_ : int + decimal_test : decimal.Decimal + + Returns + ------- + QueryResults[decimal.Decimal] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(self._conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + def get_many_mood(self, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Parameters + ---------- + mood : enums.TestMysqlTypesMood + + Returns + ------- + QueryResults[enums.TestMysqlTypesMood] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(self._conn, GET_MANY_MOOD, _decode_hook, mood) + + def list_months(self) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Returns + ------- + QueryResults[str] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(self._conn, LIST_MONTHS, operator.itemgetter(0)) + + async def count_mysql_types(self) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(COUNT_MYSQL_TYPES) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def update_varchar_test(self, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Parameters + ---------- + varchar_test : str + id_ : int + + Returns + ------- + int + The number of affected rows. This will be 0 for queries like `CREATE TABLE`. + + """ + async with self._conn.cursor() as cur: + return await cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + async def delete_one_mysql_type(self, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + """ + async with self._conn.cursor() as cur: + await cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + async def all_mysql_types_cursor(self) -> asyncmy.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Returns + ------- + asyncmy.cursors.Cursor + The result returned when executing the query. + + """ + cur = self._conn.cursor() + await cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + async def insert_exec_last_id(self, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Parameters + ---------- + name : str + + Returns + ------- + int + The id of the last affected row. Will be `None` if no rows are affected. + + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + async def get_exec_last_id_name(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def insert_type_override(self, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Parameters + ---------- + id_ : int + text_test : UserString | None + + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + async def get_type_override(self, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + models.TestTypeOverride + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + async def get_reserved_arg(self, *, conn: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Parameters + ---------- + conn : str + + Returns + ------- + models.TestReservedArg + Result fetched from the db. Will be `None` if not found. + + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_RESERVED_ARG, (conn,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + async def insert_reserved_arg(self, *, id_: int, conn: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Parameters + ---------- + id_ : int + conn : str + + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_RESERVED_ARG, (id_, conn)) + + async def touch_exec_last_id(self, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Parameters + ---------- + name : str + id_ : int + + Returns + ------- + int + The id of the last affected row. Will be `None` if no rows are affected. + + """ + async with self._conn.cursor() as cur: + await cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_asyncmy/attrs/functions/__init__.py b/test/driver_asyncmy/attrs/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_asyncmy/attrs/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncmy/attrs/functions/enums.py b/test/driver_asyncmy/attrs/functions/enums.py new file mode 100644 index 00000000..873f5d33 --- /dev/null +++ b/test/driver_asyncmy/attrs/functions/enums.py @@ -0,0 +1,56 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_asyncmy/attrs/functions/models.py b/test/driver_asyncmy/attrs/functions/models.py new file mode 100644 index 00000000..3ab1a7ff --- /dev/null +++ b/test/driver_asyncmy/attrs/functions/models.py @@ -0,0 +1,236 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlType", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +import attrs +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_asyncmy.attrs.functions import enums + import collections.abc + import datetime + import decimal + + +@attrs.define() +class TestInnerMysqlType: + """Model representing TestInnerMysqlType. + + Attributes + ---------- + table_id : int + int_test : int | None + integer_test : int | None + mediumint_test : int | None + smallint_test : int | None + tinyint_test : int | None + bigint_test : int | None + int_unsigned_test : int | None + bigint_unsigned_test : int | None + year_test : int | None + tinyint1_test : bool | None + bool_test : bool | None + boolean_test : bool | None + float_test : float | None + double_test : float | None + double_precision_test : float | None + real_test : float | None + decimal_test : decimal.Decimal | None + numeric_test : decimal.Decimal | None + char_test : str | None + varchar_test : str | None + tinytext_test : str | None + text_test : str | None + mediumtext_test : str | None + longtext_test : str | None + binary_test : memoryview | None + varbinary_test : memoryview | None + tinyblob_test : memoryview | None + blob_test : memoryview | None + mediumblob_test : memoryview | None + longblob_test : memoryview | None + bit_test : memoryview | None + date_test : datetime.date | None + datetime_test : datetime.datetime | None + datetime6_test : datetime.datetime | None + timestamp_test : datetime.datetime | None + time_test : datetime.timedelta | None + json_test : str | None + mood : enums.TestInnerMysqlTypesMood | None + tag : enums.TestInnerMysqlTypesTag | None + + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +@attrs.define() +class TestMysqlType: + """Model representing TestMysqlType. + + Attributes + ---------- + id_ : int + int_test : int + integer_test : int + mediumint_test : int + smallint_test : int + tinyint_test : int + bigint_test : int + int_unsigned_test : int + bigint_unsigned_test : int + year_test : int + tinyint1_test : bool + bool_test : bool + boolean_test : bool + float_test : float + double_test : float + double_precision_test : float + real_test : float + decimal_test : decimal.Decimal + numeric_test : decimal.Decimal + char_test : str + varchar_test : str + tinytext_test : str + text_test : str + mediumtext_test : str + longtext_test : str + binary_test : memoryview + varbinary_test : memoryview + tinyblob_test : memoryview + blob_test : memoryview + mediumblob_test : memoryview + longblob_test : memoryview + bit_test : memoryview + date_test : datetime.date + datetime_test : datetime.datetime + datetime6_test : datetime.datetime + timestamp_test : datetime.datetime + time_test : datetime.timedelta + json_test : str + mood : enums.TestMysqlTypesMood + tag : enums.TestMysqlTypesTag + + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +@attrs.define() +class TestReservedArg: + """Model representing TestReservedArg. + + Attributes + ---------- + id_ : int + conn : str + + """ + + id_: int + conn: str + + +@attrs.define() +class TestTypeOverride: + """Model representing TestTypeOverride. + + Attributes + ---------- + id_ : int + text_test : UserString | None + + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_asyncmy/attrs/functions/queries.py b/test/driver_asyncmy/attrs/functions/queries.py new file mode 100644 index 00000000..295929a0 --- /dev/null +++ b/test/driver_asyncmy/attrs/functions/queries.py @@ -0,0 +1,1698 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "all_mysql_types_cursor", + "count_mysql_types", + "delete_one_mysql_type", + "get_exec_last_id_name", + "get_many_bool", + "get_many_date", + "get_many_decimal", + "get_many_inner_mysql_type", + "get_many_mood", + "get_many_mysql_type", + "get_many_nullable_inner_mysql_type", + "get_many_time", + "get_one_bit", + "get_one_blob", + "get_one_bool", + "get_one_date", + "get_one_datetime", + "get_one_decimal", + "get_one_inner_mysql_type", + "get_one_json", + "get_one_mood", + "get_one_mysql_type", + "get_one_tag", + "get_one_time", + "get_one_year", + "get_reserved_arg", + "get_type_override", + "insert_exec_last_id", + "insert_one_inner_mysql_type", + "insert_one_mysql_type", + "insert_reserved_arg", + "insert_type_override", + "list_months", + "touch_exec_last_id", + "update_varchar_test", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + import datetime + import decimal + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.attrs.functions import enums +from test.driver_asyncmy.attrs.functions import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db. + + Parameters + ---------- + conn + The connection object of type `asyncmy.Connection` used to execute queries. + sql + The SQL statement that will be executed when fetching/iterating. + decode_hook + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args + Arguments that should be sent when executing the sql query. + + """ + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance.""" + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns + ------- + QueryResults[T] + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns + ------- + collections.abc.Sequence[T] + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns + ------- + T + The next decoded result. + + Raises + ------ + StopAsyncIteration + When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +async def insert_one_mysql_type( + conn: asyncmy.Connection, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, +) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + int_test : int + integer_test : int + mediumint_test : int + smallint_test : int + tinyint_test : int + bigint_test : int + int_unsigned_test : int + bigint_unsigned_test : int + year_test : int + tinyint1_test : bool + bool_test : bool + boolean_test : bool + float_test : float + double_test : float + double_precision_test : float + real_test : float + decimal_test : decimal.Decimal + numeric_test : decimal.Decimal + char_test : str + varchar_test : str + tinytext_test : str + text_test : str + mediumtext_test : str + longtext_test : str + binary_test : memoryview + varbinary_test : memoryview + tinyblob_test : memoryview + blob_test : memoryview + mediumblob_test : memoryview + longblob_test : memoryview + bit_test : memoryview + date_test : datetime.date + datetime_test : datetime.datetime + datetime6_test : datetime.datetime + timestamp_test : datetime.datetime + time_test : datetime.timedelta + json_test : str + mood : enums.TestMysqlTypesMood + tag : enums.TestMysqlTypesTag + + """ + async with conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + +async def insert_one_inner_mysql_type( + conn: asyncmy.Connection, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, +) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + table_id : int + int_test : int | None + integer_test : int | None + mediumint_test : int | None + smallint_test : int | None + tinyint_test : int | None + bigint_test : int | None + int_unsigned_test : int | None + bigint_unsigned_test : int | None + year_test : int | None + tinyint1_test : bool | None + bool_test : bool | None + boolean_test : bool | None + float_test : float | None + double_test : float | None + double_precision_test : float | None + real_test : float | None + decimal_test : decimal.Decimal | None + numeric_test : decimal.Decimal | None + char_test : str | None + varchar_test : str | None + tinytext_test : str | None + text_test : str | None + mediumtext_test : str | None + longtext_test : str | None + binary_test : memoryview | None + varbinary_test : memoryview | None + tinyblob_test : memoryview | None + blob_test : memoryview | None + mediumblob_test : memoryview | None + longblob_test : memoryview | None + bit_test : memoryview | None + date_test : datetime.date | None + datetime_test : datetime.datetime | None + datetime6_test : datetime.datetime | None + timestamp_test : datetime.datetime | None + time_test : datetime.timedelta | None + json_test : str | None + mood : enums.TestInnerMysqlTypesMood | None + tag : enums.TestInnerMysqlTypesTag | None + + """ + async with conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + +async def get_one_mysql_type(conn: asyncmy.Connection, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + Returns + ------- + models.TestMysqlType + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + +async def get_one_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + table_id : int + + Returns + ------- + models.TestInnerMysqlType + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + +def get_many_mysql_type(conn: asyncmy.Connection, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + Returns + ------- + QueryResults[models.TestMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + +def get_many_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + table_id : int + + Returns + ------- + QueryResults[models.TestInnerMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + +def get_many_nullable_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + table_id : int + int_test : int | None + + Returns + ------- + QueryResults[models.TestInnerMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + +async def get_one_date(conn: asyncmy.Connection, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + date_test : datetime.date + + Returns + ------- + datetime.date + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DATE, (id_, date_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_datetime(conn: asyncmy.Connection, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + datetime_test : datetime.datetime + + Returns + ------- + datetime.datetime + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_time(conn: asyncmy.Connection, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + time_test : datetime.timedelta + + Returns + ------- + datetime.timedelta + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_TIME, (id_, time_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_bool(conn: asyncmy.Connection, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + tinyint1_test : bool + + Returns + ------- + bool + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = await cur.fetchone() + if row is None: + return None + return bool(row[0]) + + +async def get_one_decimal(conn: asyncmy.Connection, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + decimal_test : decimal.Decimal + + Returns + ------- + decimal.Decimal + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_blob(conn: asyncmy.Connection, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + blob_test : memoryview + + Returns + ------- + memoryview + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +async def get_one_bit(conn: asyncmy.Connection, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + Returns + ------- + memoryview + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BIT, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +async def get_one_year(conn: asyncmy.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_YEAR, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_json(conn: asyncmy.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_JSON, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_mood(conn: asyncmy.Connection, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + mood : enums.TestMysqlTypesMood + + Returns + ------- + enums.TestMysqlTypesMood + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_MOOD, (id_, mood)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + +async def get_one_tag(conn: asyncmy.Connection, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + Returns + ------- + enums.TestMysqlTypesTag + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_TAG, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + +def get_many_date(conn: asyncmy.Connection, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + date_test : datetime.date + + Returns + ------- + QueryResults[datetime.date] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + +def get_many_time(conn: asyncmy.Connection, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + time_test : datetime.timedelta + + Returns + ------- + QueryResults[datetime.timedelta] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + +def get_many_bool(conn: asyncmy.Connection, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + tinyint1_test : bool + + Returns + ------- + QueryResults[bool] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + +def get_many_decimal(conn: asyncmy.Connection, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + decimal_test : decimal.Decimal + + Returns + ------- + QueryResults[decimal.Decimal] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + +def get_many_mood(conn: asyncmy.Connection, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + mood : enums.TestMysqlTypesMood + + Returns + ------- + QueryResults[enums.TestMysqlTypesMood] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(conn, GET_MANY_MOOD, _decode_hook, mood) + + +def list_months(conn: asyncmy.Connection) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns + ------- + QueryResults[str] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(conn, LIST_MONTHS, operator.itemgetter(0)) + + +async def count_mysql_types(conn: asyncmy.Connection) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(COUNT_MYSQL_TYPES) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def update_varchar_test(conn: asyncmy.Connection, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + varchar_test : str + id_ : int + + Returns + ------- + int + The number of affected rows. This will be 0 for queries like `CREATE TABLE`. + + """ + async with conn.cursor() as cur: + return await cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + +async def delete_one_mysql_type(conn: asyncmy.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + """ + async with conn.cursor() as cur: + await cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + +async def all_mysql_types_cursor(conn: asyncmy.Connection) -> asyncmy.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns + ------- + asyncmy.cursors.Cursor + The result returned when executing the query. + + """ + cur = conn.cursor() + await cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + +async def insert_exec_last_id(conn: asyncmy.Connection, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + name : str + + Returns + ------- + int + The id of the last affected row. Will be `None` if no rows are affected. + + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + +async def get_exec_last_id_name(conn: asyncmy.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def insert_type_override(conn: asyncmy.Connection, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + text_test : UserString | None + + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + +async def get_type_override(conn: asyncmy.Connection, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + + Returns + ------- + models.TestTypeOverride + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + +async def get_reserved_arg(conn: asyncmy.Connection, *, conn_2: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + conn_2 : str + + Returns + ------- + models.TestReservedArg + Result fetched from the db. Will be `None` if not found. + + """ + async with conn.cursor() as cur: + await cur.execute(GET_RESERVED_ARG, (conn_2,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + +async def insert_reserved_arg(conn: asyncmy.Connection, *, id_: int, conn_2: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + id_ : int + conn_2 : str + + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_RESERVED_ARG, (id_, conn_2)) + + +async def touch_exec_last_id(conn: asyncmy.Connection, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Parameters + ---------- + conn : asyncmy.Connection + Connection object of type `asyncmy.Connection` used to execute the query. + name : str + id_ : int + + Returns + ------- + int + The id of the last affected row. Will be `None` if no rows are affected. + + """ + async with conn.cursor() as cur: + await cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_asyncmy/attrs/ruff.toml b/test/driver_asyncmy/attrs/ruff.toml new file mode 100644 index 00000000..3047247e --- /dev/null +++ b/test/driver_asyncmy/attrs/ruff.toml @@ -0,0 +1,5 @@ +extend="../../../ruff.toml" + + +[lint.pydocstyle] +convention = "numpy" \ No newline at end of file diff --git a/test/driver_asyncmy/attrs/test_asyncmy_attrs_classes.py b/test/driver_asyncmy/attrs/test_asyncmy_attrs_classes.py new file mode 100644 index 00000000..f2c40f8a --- /dev/null +++ b/test/driver_asyncmy/attrs/test_asyncmy_attrs_classes.py @@ -0,0 +1,999 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import asyncmy +import asyncmy.cursors +import attrs +import pytest +import pytest_asyncio + +from test.driver_asyncmy import no_row_conn +from test.driver_asyncmy.attrs.classes import enums +from test.driver_asyncmy.attrs.classes import models +from test.driver_asyncmy.attrs.classes import queries + +MODEL_ID = 7000 +NORMALIZATION_ID = 7001 +OVERRIDE_ID = 7100 +OVERRIDE_NULL_ID = 7101 +RESERVED_ARG_ID = 7150 +RESERVED_ARG_VALUE = "attrs-classes-conn" + + +@pytest.mark.asyncio(loop_scope="session") +class TestAsyncmyAttrsClasses: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MODEL_ID, + int_test=42, + integer_test=-7, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 10, + year_test=2024, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.e, + double_precision_test=1.41421, + real_test=math.pi, + decimal_test=decimal.Decimal("12.3400"), + numeric_test=decimal.Decimal("3.50"), + char_test="ABCDEFGHIJ", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"0123456789abcdef"), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tiny blob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"medium blob"), + longblob_test=memoryview(b"long blob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 5), + datetime_test=datetime.datetime(2026, 1, 5, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 5, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 5, 12, 30, 45), + time_test=datetime.timedelta(hours=13, minutes=14, seconds=15), + json_test=json.dumps({"foo": "bar", "count": 2}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.ALPHA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=None, + bool_test=True, + boolean_test=None, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=None, + tinyblob_test=model.tinyblob_test, + blob_test=None, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=None, + datetime6_test=model.datetime6_test, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=enums.TestInnerMysqlTypesTag.BETA, + ) + + @pytest_asyncio.fixture(scope="class", loop_scope="session") + async def queries_obj(self, asyncmy_conn: asyncmy.Connection) -> queries.Queries: + return queries.Queries(conn=asyncmy_conn) + + @pytest.mark.asyncio(loop_scope="session") + async def test_conn_attr(self, queries_obj: queries.Queries) -> None: + assert isinstance(queries_obj.conn, asyncmy.Connection) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::insert") + async def test_insert( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + await queries_obj.insert_one_mysql_type( + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::inner_insert", depends=["AsyncmyTestAttrsClasses::insert"]) + async def test_inner_insert( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + await queries_obj.insert_one_inner_mysql_type( + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_one", depends=["AsyncmyTestAttrsClasses::inner_insert"]) + async def test_get_one( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mysql_type(id_=model.id_) + + assert result is not None + + assert isinstance(result, models.TestMysqlType) + + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + # MySQL normalizes JSON spacing, so the raw string may differ. + assert json.loads(result.json_test) == json.loads(model.json_test) + assert attrs.evolve(result, json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_one_none", depends=["AsyncmyTestAttrsClasses::get_one"]) + async def test_get_one_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_mysql_type(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_one_inner", depends=["AsyncmyTestAttrsClasses::get_one_none"]) + async def test_get_one_inner( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = await queries_obj.get_one_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_one_inner_none", depends=["AsyncmyTestAttrsClasses::get_one_inner"]) + async def test_get_one_inner_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_inner_mysql_type(table_id=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_date", depends=["AsyncmyTestAttrsClasses::get_one_inner_none"]) + async def test_get_date( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_date(id_=model.id_, date_test=model.date_test) + + assert result is not None + + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_date_none", depends=["AsyncmyTestAttrsClasses::get_date"]) + async def test_get_date_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_date(id_=0, date_test=model.date_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_datetime", depends=["AsyncmyTestAttrsClasses::get_date_none"]) + async def test_get_datetime( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_datetime(id_=model.id_, datetime_test=model.datetime_test) + + assert result is not None + + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_datetime_none", depends=["AsyncmyTestAttrsClasses::get_datetime"]) + async def test_get_datetime_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_datetime(id_=0, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_time", depends=["AsyncmyTestAttrsClasses::get_datetime_none"]) + async def test_get_time( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_time(id_=model.id_, time_test=model.time_test) + + assert result is not None + + # MySQL time columns arrive as timedelta, not datetime.time. + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_time_none", depends=["AsyncmyTestAttrsClasses::get_time"]) + async def test_get_time_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_time(id_=0, time_test=model.time_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_bool", depends=["AsyncmyTestAttrsClasses::get_time_none"]) + async def test_get_bool( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_bool(id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + + assert isinstance(result, bool) + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_bool_none", depends=["AsyncmyTestAttrsClasses::get_bool"]) + async def test_get_bool_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_bool(id_=0, tinyint1_test=False) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_decimal", depends=["AsyncmyTestAttrsClasses::get_bool_none"]) + async def test_get_decimal( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_decimal(id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + + assert isinstance(result, decimal.Decimal) + # decimal(12,4) always comes back padded to scale. + assert str(result) == "12.3400" + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_decimal_none", depends=["AsyncmyTestAttrsClasses::get_decimal"]) + async def test_get_decimal_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_decimal(id_=0, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_blob", depends=["AsyncmyTestAttrsClasses::get_decimal_none"]) + async def test_get_blob( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_blob(id_=model.id_, blob_test=model.blob_test) + + assert result is not None + + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_blob_none", depends=["AsyncmyTestAttrsClasses::get_blob"]) + async def test_get_blob_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_blob(id_=0, blob_test=memoryview(b"test")) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_bit", depends=["AsyncmyTestAttrsClasses::get_blob_none"]) + async def test_get_bit( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_bit(id_=model.id_) + + assert result is not None + + # bit(8) arrives as a single byte of raw bits. + assert isinstance(result, memoryview) + assert len(result) == 1 + assert bytes(result) == b"\x80" + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_bit_none", depends=["AsyncmyTestAttrsClasses::get_bit"]) + async def test_get_bit_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_bit(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_year", depends=["AsyncmyTestAttrsClasses::get_bit_none"]) + async def test_get_year( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_year(id_=model.id_) + + assert result is not None + + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_year_none", depends=["AsyncmyTestAttrsClasses::get_year"]) + async def test_get_year_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_year(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_json", depends=["AsyncmyTestAttrsClasses::get_year_none"]) + async def test_get_json( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_json(id_=model.id_) + + assert result is not None + + assert isinstance(result, str) + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_json_none", depends=["AsyncmyTestAttrsClasses::get_json"]) + async def test_get_json_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_json(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_mood", depends=["AsyncmyTestAttrsClasses::get_json_none"]) + async def test_get_mood( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mood(id_=model.id_, mood=model.mood) + + assert result is not None + + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_mood_none", depends=["AsyncmyTestAttrsClasses::get_mood"]) + async def test_get_mood_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mood(id_=0, mood=model.mood) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_tag", depends=["AsyncmyTestAttrsClasses::get_mood_none"]) + async def test_get_tag( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_tag(id_=MODEL_ID) + + assert result is not None + + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.ALPHA + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_tag_none", depends=["AsyncmyTestAttrsClasses::get_tag"]) + async def test_get_tag_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_tag(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::value_normalization", depends=["AsyncmyTestAttrsClasses::get_tag_none"]) + async def test_value_normalization( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + # decimal(12,4) pads to scale, char(10) strips trailing spaces and + # binary(16) is right-padded with NUL bytes on return. + await queries_obj.insert_one_mysql_type( + id_=NORMALIZATION_ID, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("3.5"), + char_test="AB ", + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=memoryview(b"abc"), + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + result = await queries_obj.get_one_mysql_type(id_=NORMALIZATION_ID) + assert result is not None + assert str(result.decimal_test) == "12.3400" + assert str(result.numeric_test) == "3.50" + assert result.char_test == "AB" + assert bytes(result.binary_test) == b"abc" + b"\x00" * 13 + await queries_obj.delete_one_mysql_type(id_=NORMALIZATION_ID) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many", depends=["AsyncmyTestAttrsClasses::value_normalization"]) + async def test_get_many(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mysql_type(id_=model.id_) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + + assert attrs.evolve(results[0], json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_iter", depends=["AsyncmyTestAttrsClasses::get_many"]) + async def test_get_many_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_mysql_type(id_=model.id_): + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert attrs.evolve(result, json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_inner", depends=["AsyncmyTestAttrsClasses::get_many_iter"]) + async def test_get_many_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestInnerMysqlType) + + assert results[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_inner_iter", depends=["AsyncmyTestAttrsClasses::get_many_inner"]) + async def test_get_many_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::get_many_nullable_inner", + depends=["AsyncmyTestAttrsClasses::get_many_inner_iter"], + ) + async def test_get_many_nullable_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the <=> in the query is NULL-safe equality. + result = queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestInnerMysqlType) + + assert results[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::get_many_nullable_inner_iter", + depends=["AsyncmyTestAttrsClasses::get_many_nullable_inner"], + ) + async def test_get_many_nullable_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::get_many_date", + depends=["AsyncmyTestAttrsClasses::get_many_nullable_inner_iter"], + ) + async def test_get_many_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_date(id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], datetime.date) + + assert results[0] == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_date_iter", depends=["AsyncmyTestAttrsClasses::get_many_date"]) + async def test_get_many_date_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_date(id_=model.id_, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_time", depends=["AsyncmyTestAttrsClasses::get_many_date_iter"]) + async def test_get_many_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_time(id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], datetime.timedelta) + + assert results[0] == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_time_iter", depends=["AsyncmyTestAttrsClasses::get_many_time"]) + async def test_get_many_time_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_time(id_=model.id_, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_bool", depends=["AsyncmyTestAttrsClasses::get_many_time_iter"]) + async def test_get_many_bool(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_bool(id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], bool) + + assert results[0] is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_bool_iter", depends=["AsyncmyTestAttrsClasses::get_many_bool"]) + async def test_get_many_bool_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_bool(id_=model.id_, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_decimal", depends=["AsyncmyTestAttrsClasses::get_many_bool_iter"]) + async def test_get_many_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_decimal(id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], decimal.Decimal) + + assert results[0] == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::get_many_decimal_iter", + depends=["AsyncmyTestAttrsClasses::get_many_decimal"], + ) + async def test_get_many_decimal_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_decimal(id_=model.id_, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_mood", depends=["AsyncmyTestAttrsClasses::get_many_decimal_iter"]) + async def test_get_many_mood(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mood(mood=model.mood) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], enums.TestMysqlTypesMood) + + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::get_many_mood_iter", depends=["AsyncmyTestAttrsClasses::get_many_mood"]) + async def test_get_many_mood_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_mood(mood=model.mood): + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::list_months", depends=["AsyncmyTestAttrsClasses::get_many_mood_iter"]) + async def test_list_months(self, queries_obj: queries.Queries) -> None: + # Parameterless :many with literal percents in the SQL; regression + # test for the percent-doubling bug. + result = queries_obj.list_months() + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert results == ["2026-01"] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::list_months_iter", depends=["AsyncmyTestAttrsClasses::list_months"]) + async def test_list_months_iter(self, queries_obj: queries.Queries) -> None: + months = [month async for month in queries_obj.list_months()] + assert months == ["2026-01"] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::count", depends=["AsyncmyTestAttrsClasses::list_months_iter"]) + async def test_count(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.count_mysql_types() + + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::update_varchar_rows", depends=["AsyncmyTestAttrsClasses::count"]) + async def test_update_varchar_rows(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.update_varchar_test(varchar_test="updated varchar", id_=model.id_) + assert isinstance(result, int) + assert result == 1 + + result = await queries_obj.update_varchar_test(varchar_test="updated varchar", id_=0) + assert result == 0 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::all_types_cursor", depends=["AsyncmyTestAttrsClasses::update_varchar_rows"]) + async def test_all_types_cursor(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + cursor = await queries_obj.all_mysql_types_cursor() + assert isinstance(cursor, asyncmy.cursors.Cursor) + + rows = await cursor.fetchall() + await cursor.close() + # The shared table may carry other files' rows; assert on our own. + assert model.id_ in {row[0] for row in rows} + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::insert_exec_last_id", depends=["AsyncmyTestAttrsClasses::all_types_cursor"]) + async def test_insert_exec_last_id(self, queries_obj: queries.Queries) -> None: + # AUTO_INCREMENT counters persist across runs, so only relative + # assertions are safe. + first_id = await queries_obj.insert_exec_last_id(name="attrs-classes-first") + assert first_id is not None + assert isinstance(first_id, int) + assert first_id > 0 + + name = await queries_obj.get_exec_last_id_name(id_=first_id) + assert name == "attrs-classes-first" + + second_id = await queries_obj.insert_exec_last_id(name="attrs-classes-second") + assert second_id is not None + assert second_id > first_id + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::get_exec_last_id_name_none", + depends=["AsyncmyTestAttrsClasses::insert_exec_last_id"], + ) + async def test_get_exec_last_id_name_none(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.get_exec_last_id_name(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::delete_mysql_type", + depends=["AsyncmyTestAttrsClasses::get_exec_last_id_name_none"], + ) + async def test_delete_mysql_type(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + await queries_obj.delete_one_mysql_type(id_=model.id_) + + result = await queries_obj.get_one_mysql_type(id_=model.id_) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::insert_type_override", + ) + async def test_insert_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + await queries_obj.insert_type_override(id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::get_type_override", + depends=["AsyncmyTestAttrsClasses::insert_type_override"], + ) + async def test_get_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = await queries_obj.get_type_override(id_=override_model.id_) + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::get_type_override_none", + depends=["AsyncmyTestAttrsClasses::get_type_override"], + ) + async def test_get_type_override_none(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = await queries_obj.get_type_override(id_=override_model.id_ - 1) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::type_override_null_value", + depends=["AsyncmyTestAttrsClasses::get_type_override_none"], + ) + async def test_type_override_null_value(self, queries_obj: queries.Queries) -> None: + # The UserString override sits on a nullable column. + await queries_obj.insert_type_override(id_=OVERRIDE_NULL_ID, text_test=None) + + result = await queries_obj.get_type_override(id_=OVERRIDE_NULL_ID) + assert result is not None + assert result.text_test is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsClasses::insert_reserved_arg") + async def test_insert_reserved_arg(self, queries_obj: queries.Queries) -> None: + # The column is literally named "conn"; on methods no deduplication + # against an implicit connection argument is needed. + await queries_obj.insert_reserved_arg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsClasses::get_reserved_arg", + depends=["AsyncmyTestAttrsClasses::insert_reserved_arg"], + ) + async def test_get_reserved_arg(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.get_reserved_arg(conn=RESERVED_ARG_VALUE) + assert result == models.TestReservedArg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(depends=["AsyncmyTestAttrsClasses::insert_reserved_arg"]) + async def test_get_reserved_arg_not_found(self, queries_obj: queries.Queries) -> None: + assert await queries_obj.get_reserved_arg(conn="missing-reserved-arg-value") is None + + @pytest.mark.asyncio(loop_scope="session") + async def test_one_missing_rows_return_none(self, asyncmy_conn: asyncmy.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. + obj = queries.Queries(conn=asyncmy_conn) + assert await obj.get_one_mysql_type(id_=-1) is None + assert await obj.get_one_inner_mysql_type(table_id=-1) is None + assert await obj.get_one_date(id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert await obj.get_one_datetime(id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert await obj.get_one_time(id_=-1, time_test=datetime.timedelta()) is None + assert await obj.get_one_bool(id_=-1, tinyint1_test=False) is None + assert await obj.get_one_decimal(id_=-1, decimal_test=decimal.Decimal(0)) is None + assert await obj.get_one_blob(id_=-1, blob_test=memoryview(b"")) is None + assert await obj.get_one_bit(id_=-1) is None + assert await obj.get_one_year(id_=-1) is None + assert await obj.get_one_json(id_=-1) is None + assert await obj.get_one_mood(id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert await obj.get_one_tag(id_=-1) is None + assert await obj.get_exec_last_id_name(id_=-1) is None + assert await obj.get_type_override(id_=-1) is None + assert await obj.get_reserved_arg(conn="missing") is None + assert await obj.touch_exec_last_id(name="untouched", id_=-1) is None + + # count(*) always returns a row; its miss branch needs the stub. + stub = typing.cast("asyncmy.Connection", no_row_conn.NoRowConn()) + assert await queries.Queries(conn=stub).count_mysql_types() is None diff --git a/test/driver_asyncmy/attrs/test_asyncmy_attrs_functions.py b/test/driver_asyncmy/attrs/test_asyncmy_attrs_functions.py new file mode 100644 index 00000000..a0358c39 --- /dev/null +++ b/test/driver_asyncmy/attrs/test_asyncmy_attrs_functions.py @@ -0,0 +1,1004 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import asyncmy +import asyncmy.cursors +import attrs +import pytest + +from test.driver_asyncmy import no_row_conn +from test.driver_asyncmy.attrs.functions import enums +from test.driver_asyncmy.attrs.functions import models +from test.driver_asyncmy.attrs.functions import queries + +MODEL_ID = 7500 +NORMALIZATION_ID = 7501 +OVERRIDE_ID = 7600 +OVERRIDE_NULL_ID = 7601 +RESERVED_ARG_ID = 7650 +RESERVED_ARG_VALUE = "attrs-functions-conn" + + +@pytest.mark.asyncio(loop_scope="session") +class TestAsyncmyAttrsFunctions: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MODEL_ID, + int_test=42, + integer_test=-7, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 10, + year_test=2024, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.e, + double_precision_test=1.41421, + real_test=math.pi, + decimal_test=decimal.Decimal("12.3400"), + numeric_test=decimal.Decimal("3.50"), + char_test="ABCDEFGHIJ", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"0123456789abcdef"), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tiny blob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"medium blob"), + longblob_test=memoryview(b"long blob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 5), + datetime_test=datetime.datetime(2026, 1, 5, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 5, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 5, 12, 30, 45), + time_test=datetime.timedelta(hours=13, minutes=14, seconds=15), + json_test=json.dumps({"foo": "bar", "count": 2}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.ALPHA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=None, + bool_test=True, + boolean_test=None, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=None, + tinyblob_test=model.tinyblob_test, + blob_test=None, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=None, + datetime6_test=model.datetime6_test, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=enums.TestInnerMysqlTypesTag.BETA, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::insert") + async def test_insert( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + await queries.insert_one_mysql_type( + conn=asyncmy_conn, + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::inner_insert", depends=["AsyncmyTestAttrsFunctions::insert"]) + async def test_inner_insert( + self, + asyncmy_conn: asyncmy.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + await queries.insert_one_inner_mysql_type( + conn=asyncmy_conn, + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_one", depends=["AsyncmyTestAttrsFunctions::inner_insert"]) + async def test_get_one( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + + assert isinstance(result, models.TestMysqlType) + + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + # MySQL normalizes JSON spacing, so the raw string may differ. + assert json.loads(result.json_test) == json.loads(model.json_test) + assert attrs.evolve(result, json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_one_none", depends=["AsyncmyTestAttrsFunctions::get_one"]) + async def test_get_one_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_one_inner", depends=["AsyncmyTestAttrsFunctions::get_one_none"]) + async def test_get_one_inner( + self, + asyncmy_conn: asyncmy.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id) + + assert result is not None + + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_one_inner_none", + depends=["AsyncmyTestAttrsFunctions::get_one_inner"], + ) + async def test_get_one_inner_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_date", depends=["AsyncmyTestAttrsFunctions::get_one_inner_none"]) + async def test_get_date( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_date(conn=asyncmy_conn, id_=model.id_, date_test=model.date_test) + + assert result is not None + + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_date_none", depends=["AsyncmyTestAttrsFunctions::get_date"]) + async def test_get_date_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_date(conn=asyncmy_conn, id_=0, date_test=model.date_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_datetime", depends=["AsyncmyTestAttrsFunctions::get_date_none"]) + async def test_get_datetime( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_datetime(conn=asyncmy_conn, id_=model.id_, datetime_test=model.datetime_test) + + assert result is not None + + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_datetime_none", depends=["AsyncmyTestAttrsFunctions::get_datetime"]) + async def test_get_datetime_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_datetime(conn=asyncmy_conn, id_=0, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_time", depends=["AsyncmyTestAttrsFunctions::get_datetime_none"]) + async def test_get_time( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_time(conn=asyncmy_conn, id_=model.id_, time_test=model.time_test) + + assert result is not None + + # MySQL time columns arrive as timedelta, not datetime.time. + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_time_none", depends=["AsyncmyTestAttrsFunctions::get_time"]) + async def test_get_time_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_time(conn=asyncmy_conn, id_=0, time_test=model.time_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_bool", depends=["AsyncmyTestAttrsFunctions::get_time_none"]) + async def test_get_bool( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_bool(conn=asyncmy_conn, id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + + assert isinstance(result, bool) + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_bool_none", depends=["AsyncmyTestAttrsFunctions::get_bool"]) + async def test_get_bool_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_bool(conn=asyncmy_conn, id_=0, tinyint1_test=False) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_decimal", depends=["AsyncmyTestAttrsFunctions::get_bool_none"]) + async def test_get_decimal( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_decimal(conn=asyncmy_conn, id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + + assert isinstance(result, decimal.Decimal) + # decimal(12,4) always comes back padded to scale. + assert str(result) == "12.3400" + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_decimal_none", depends=["AsyncmyTestAttrsFunctions::get_decimal"]) + async def test_get_decimal_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_decimal(conn=asyncmy_conn, id_=0, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_blob", depends=["AsyncmyTestAttrsFunctions::get_decimal_none"]) + async def test_get_blob( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_blob(conn=asyncmy_conn, id_=model.id_, blob_test=model.blob_test) + + assert result is not None + + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_blob_none", depends=["AsyncmyTestAttrsFunctions::get_blob"]) + async def test_get_blob_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_blob(conn=asyncmy_conn, id_=0, blob_test=memoryview(b"test")) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_bit", depends=["AsyncmyTestAttrsFunctions::get_blob_none"]) + async def test_get_bit( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_bit(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + + # bit(8) arrives as a single byte of raw bits. + assert isinstance(result, memoryview) + assert len(result) == 1 + assert bytes(result) == b"\x80" + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_bit_none", depends=["AsyncmyTestAttrsFunctions::get_bit"]) + async def test_get_bit_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_bit(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_year", depends=["AsyncmyTestAttrsFunctions::get_bit_none"]) + async def test_get_year( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_year(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_year_none", depends=["AsyncmyTestAttrsFunctions::get_year"]) + async def test_get_year_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_year(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_json", depends=["AsyncmyTestAttrsFunctions::get_year_none"]) + async def test_get_json( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_json(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + + assert isinstance(result, str) + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_json_none", depends=["AsyncmyTestAttrsFunctions::get_json"]) + async def test_get_json_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_json(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_mood", depends=["AsyncmyTestAttrsFunctions::get_json_none"]) + async def test_get_mood( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mood(conn=asyncmy_conn, id_=model.id_, mood=model.mood) + + assert result is not None + + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_mood_none", depends=["AsyncmyTestAttrsFunctions::get_mood"]) + async def test_get_mood_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mood(conn=asyncmy_conn, id_=0, mood=model.mood) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_tag", depends=["AsyncmyTestAttrsFunctions::get_mood_none"]) + async def test_get_tag( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_tag(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.ALPHA + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_tag_none", depends=["AsyncmyTestAttrsFunctions::get_tag"]) + async def test_get_tag_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_tag(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::value_normalization", depends=["AsyncmyTestAttrsFunctions::get_tag_none"]) + async def test_value_normalization( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + # decimal(12,4) pads to scale, char(10) strips trailing spaces and + # binary(16) is right-padded with NUL bytes on return. + await queries.insert_one_mysql_type( + conn=asyncmy_conn, + id_=NORMALIZATION_ID, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("3.5"), + char_test="AB ", + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=memoryview(b"abc"), + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=NORMALIZATION_ID) + assert result is not None + assert str(result.decimal_test) == "12.3400" + assert str(result.numeric_test) == "3.50" + assert result.char_test == "AB" + assert bytes(result.binary_test) == b"abc" + b"\x00" * 13 + await queries.delete_one_mysql_type(conn=asyncmy_conn, id_=NORMALIZATION_ID) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many", depends=["AsyncmyTestAttrsFunctions::value_normalization"]) + async def test_get_many(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mysql_type(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + + assert attrs.evolve(results[0], json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many_iter", depends=["AsyncmyTestAttrsFunctions::get_many"]) + async def test_get_many_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_mysql_type(conn=asyncmy_conn, id_=model.id_): + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert attrs.evolve(result, json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many_inner", depends=["AsyncmyTestAttrsFunctions::get_many_iter"]) + async def test_get_many_inner(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = queries.get_many_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestInnerMysqlType) + + assert results[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_many_inner_iter", + depends=["AsyncmyTestAttrsFunctions::get_many_inner"], + ) + async def test_get_many_inner_iter(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries.get_many_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_many_nullable_inner", + depends=["AsyncmyTestAttrsFunctions::get_many_inner_iter"], + ) + async def test_get_many_nullable_inner(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the <=> in the query is NULL-safe equality. + result = queries.get_many_nullable_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestInnerMysqlType) + + assert results[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_many_nullable_inner_iter", + depends=["AsyncmyTestAttrsFunctions::get_many_nullable_inner"], + ) + async def test_get_many_nullable_inner_iter(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries.get_many_nullable_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_many_date", + depends=["AsyncmyTestAttrsFunctions::get_many_nullable_inner_iter"], + ) + async def test_get_many_date(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_date(conn=asyncmy_conn, id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], datetime.date) + + assert results[0] == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many_date_iter", depends=["AsyncmyTestAttrsFunctions::get_many_date"]) + async def test_get_many_date_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_date(conn=asyncmy_conn, id_=model.id_, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many_time", depends=["AsyncmyTestAttrsFunctions::get_many_date_iter"]) + async def test_get_many_time(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_time(conn=asyncmy_conn, id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], datetime.timedelta) + + assert results[0] == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many_time_iter", depends=["AsyncmyTestAttrsFunctions::get_many_time"]) + async def test_get_many_time_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_time(conn=asyncmy_conn, id_=model.id_, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many_bool", depends=["AsyncmyTestAttrsFunctions::get_many_time_iter"]) + async def test_get_many_bool(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_bool(conn=asyncmy_conn, id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], bool) + + assert results[0] is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many_bool_iter", depends=["AsyncmyTestAttrsFunctions::get_many_bool"]) + async def test_get_many_bool_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_bool(conn=asyncmy_conn, id_=model.id_, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_many_decimal", + depends=["AsyncmyTestAttrsFunctions::get_many_bool_iter"], + ) + async def test_get_many_decimal(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_decimal(conn=asyncmy_conn, id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], decimal.Decimal) + + assert results[0] == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_many_decimal_iter", + depends=["AsyncmyTestAttrsFunctions::get_many_decimal"], + ) + async def test_get_many_decimal_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_decimal(conn=asyncmy_conn, id_=model.id_, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_many_mood", + depends=["AsyncmyTestAttrsFunctions::get_many_decimal_iter"], + ) + async def test_get_many_mood(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mood(conn=asyncmy_conn, mood=model.mood) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], enums.TestMysqlTypesMood) + + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::get_many_mood_iter", depends=["AsyncmyTestAttrsFunctions::get_many_mood"]) + async def test_get_many_mood_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_mood(conn=asyncmy_conn, mood=model.mood): + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::list_months", depends=["AsyncmyTestAttrsFunctions::get_many_mood_iter"]) + async def test_list_months(self, asyncmy_conn: asyncmy.Connection) -> None: + # Parameterless :many with literal percents in the SQL; regression + # test for the percent-doubling bug. + result = queries.list_months(conn=asyncmy_conn) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert results == ["2026-01"] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::list_months_iter", depends=["AsyncmyTestAttrsFunctions::list_months"]) + async def test_list_months_iter(self, asyncmy_conn: asyncmy.Connection) -> None: + months = [month async for month in queries.list_months(conn=asyncmy_conn)] + assert months == ["2026-01"] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::count", depends=["AsyncmyTestAttrsFunctions::list_months_iter"]) + async def test_count(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.count_mysql_types(conn=asyncmy_conn) + + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::update_varchar_rows", depends=["AsyncmyTestAttrsFunctions::count"]) + async def test_update_varchar_rows(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.update_varchar_test(conn=asyncmy_conn, varchar_test="updated varchar", id_=model.id_) + assert isinstance(result, int) + assert result == 1 + + result = await queries.update_varchar_test(conn=asyncmy_conn, varchar_test="updated varchar", id_=0) + assert result == 0 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::all_types_cursor", depends=["AsyncmyTestAttrsFunctions::update_varchar_rows"]) + async def test_all_types_cursor(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + cursor = await queries.all_mysql_types_cursor(conn=asyncmy_conn) + assert isinstance(cursor, asyncmy.cursors.Cursor) + + rows = await cursor.fetchall() + await cursor.close() + # The shared table may carry other files' rows; assert on our own. + assert model.id_ in {row[0] for row in rows} + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::insert_exec_last_id", depends=["AsyncmyTestAttrsFunctions::all_types_cursor"]) + async def test_insert_exec_last_id(self, asyncmy_conn: asyncmy.Connection) -> None: + # AUTO_INCREMENT counters persist across runs, so only relative + # assertions are safe. + first_id = await queries.insert_exec_last_id(conn=asyncmy_conn, name="attrs-functions-first") + assert first_id is not None + assert isinstance(first_id, int) + assert first_id > 0 + + name = await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=first_id) + assert name == "attrs-functions-first" + + second_id = await queries.insert_exec_last_id(conn=asyncmy_conn, name="attrs-functions-second") + assert second_id is not None + assert second_id > first_id + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_exec_last_id_name_none", + depends=["AsyncmyTestAttrsFunctions::insert_exec_last_id"], + ) + async def test_get_exec_last_id_name_none(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::delete_mysql_type", + depends=["AsyncmyTestAttrsFunctions::get_exec_last_id_name_none"], + ) + async def test_delete_mysql_type(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + await queries.delete_one_mysql_type(conn=asyncmy_conn, id_=model.id_) + + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=model.id_) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::insert_type_override", + ) + async def test_insert_type_override(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + await queries.insert_type_override(conn=asyncmy_conn, id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_type_override", + depends=["AsyncmyTestAttrsFunctions::insert_type_override"], + ) + async def test_get_type_override(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + result = await queries.get_type_override(conn=asyncmy_conn, id_=override_model.id_) + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_type_override_none", + depends=["AsyncmyTestAttrsFunctions::get_type_override"], + ) + async def test_get_type_override_none(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + result = await queries.get_type_override(conn=asyncmy_conn, id_=override_model.id_ - 1) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::type_override_null_value", + depends=["AsyncmyTestAttrsFunctions::get_type_override_none"], + ) + async def test_type_override_null_value(self, asyncmy_conn: asyncmy.Connection) -> None: + # The UserString override sits on a nullable column. + await queries.insert_type_override(conn=asyncmy_conn, id_=OVERRIDE_NULL_ID, text_test=None) + + result = await queries.get_type_override(conn=asyncmy_conn, id_=OVERRIDE_NULL_ID) + assert result is not None + assert result.text_test is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestAttrsFunctions::insert_reserved_arg") + async def test_insert_reserved_arg(self, asyncmy_conn: asyncmy.Connection) -> None: + # The column is literally named "conn"; the generated parameter must + # be deduplicated against the implicit connection argument. + await queries.insert_reserved_arg(conn=asyncmy_conn, id_=RESERVED_ARG_ID, conn_2=RESERVED_ARG_VALUE) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestAttrsFunctions::get_reserved_arg", + depends=["AsyncmyTestAttrsFunctions::insert_reserved_arg"], + ) + async def test_get_reserved_arg(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.get_reserved_arg(conn=asyncmy_conn, conn_2=RESERVED_ARG_VALUE) + assert result == models.TestReservedArg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(depends=["AsyncmyTestAttrsFunctions::insert_reserved_arg"]) + async def test_get_reserved_arg_not_found(self, asyncmy_conn: asyncmy.Connection) -> None: + assert await queries.get_reserved_arg(conn=asyncmy_conn, conn_2="missing-reserved-arg-value") is None + + @pytest.mark.asyncio(loop_scope="session") + async def test_one_missing_rows_return_none(self, asyncmy_conn: asyncmy.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. + assert await queries.get_one_mysql_type(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=-1) is None + assert await queries.get_one_date(conn=asyncmy_conn, id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert await queries.get_one_datetime(conn=asyncmy_conn, id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert await queries.get_one_time(conn=asyncmy_conn, id_=-1, time_test=datetime.timedelta()) is None + assert await queries.get_one_bool(conn=asyncmy_conn, id_=-1, tinyint1_test=False) is None + assert await queries.get_one_decimal(conn=asyncmy_conn, id_=-1, decimal_test=decimal.Decimal(0)) is None + assert await queries.get_one_blob(conn=asyncmy_conn, id_=-1, blob_test=memoryview(b"")) is None + assert await queries.get_one_bit(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_year(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_json(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_mood(conn=asyncmy_conn, id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert await queries.get_one_tag(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_type_override(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_reserved_arg(conn=asyncmy_conn, conn_2="missing") is None + assert await queries.touch_exec_last_id(conn=asyncmy_conn, name="untouched", id_=-1) is None + + # count(*) always returns a row; its miss branch needs the stub. + stub = typing.cast("asyncmy.Connection", no_row_conn.NoRowConn()) + assert await queries.count_mysql_types(conn=stub) is None diff --git a/test/driver_asyncmy/dataclass/__init__.py b/test/driver_asyncmy/dataclass/__init__.py new file mode 100644 index 00000000..0b34101d --- /dev/null +++ b/test/driver_asyncmy/dataclass/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for asyncmy tests.""" diff --git a/test/driver_asyncmy/dataclass/classes/__init__.py b/test/driver_asyncmy/dataclass/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_asyncmy/dataclass/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncmy/dataclass/classes/enums.py b/test/driver_asyncmy/dataclass/classes/enums.py new file mode 100644 index 00000000..873f5d33 --- /dev/null +++ b/test/driver_asyncmy/dataclass/classes/enums.py @@ -0,0 +1,56 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_asyncmy/dataclass/classes/models.py b/test/driver_asyncmy/dataclass/classes/models.py new file mode 100644 index 00000000..1d7219ea --- /dev/null +++ b/test/driver_asyncmy/dataclass/classes/models.py @@ -0,0 +1,228 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlType", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +import dataclasses +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_asyncmy.dataclass.classes import enums + import collections.abc + import datetime + import decimal + + +@dataclasses.dataclass() +class TestInnerMysqlType: + """Model representing TestInnerMysqlType. + + Attributes: + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +@dataclasses.dataclass() +class TestMysqlType: + """Model representing TestMysqlType. + + Attributes: + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +@dataclasses.dataclass() +class TestReservedArg: + """Model representing TestReservedArg. + + Attributes: + id_: int + conn: str + """ + + id_: int + conn: str + + +@dataclasses.dataclass() +class TestTypeOverride: + """Model representing TestTypeOverride. + + Attributes: + id_: int + text_test: UserString | None + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_asyncmy/dataclass/classes/queries.py b/test/driver_asyncmy/dataclass/classes/queries.py new file mode 100644 index 00000000..b2865b4f --- /dev/null +++ b/test/driver_asyncmy/dataclass/classes/queries.py @@ -0,0 +1,1437 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Queries", + "QueryResults", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + import datetime + import decimal + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.dataclass.classes import enums +from test.driver_asyncmy.dataclass.classes import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `asyncmy.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns: + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopAsyncIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +class Queries: + """Queries from file queries.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: asyncmy.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> asyncmy.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `asyncmy.Connection` used to make queries. + """ + return self._conn + + async def insert_one_mysql_type( + self, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, + ) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + id_: int. + int_test: int. + integer_test: int. + mediumint_test: int. + smallint_test: int. + tinyint_test: int. + bigint_test: int. + int_unsigned_test: int. + bigint_unsigned_test: int. + year_test: int. + tinyint1_test: bool. + bool_test: bool. + boolean_test: bool. + float_test: float. + double_test: float. + double_precision_test: float. + real_test: float. + decimal_test: decimal.Decimal. + numeric_test: decimal.Decimal. + char_test: str. + varchar_test: str. + tinytext_test: str. + text_test: str. + mediumtext_test: str. + longtext_test: str. + binary_test: memoryview. + varbinary_test: memoryview. + tinyblob_test: memoryview. + blob_test: memoryview. + mediumblob_test: memoryview. + longblob_test: memoryview. + bit_test: memoryview. + date_test: datetime.date. + datetime_test: datetime.datetime. + datetime6_test: datetime.datetime. + timestamp_test: datetime.datetime. + time_test: datetime.timedelta. + json_test: str. + mood: enums.TestMysqlTypesMood. + tag: enums.TestMysqlTypesTag. + """ + async with self._conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + async def insert_one_inner_mysql_type( + self, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, + ) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + table_id: int. + int_test: int | None. + integer_test: int | None. + mediumint_test: int | None. + smallint_test: int | None. + tinyint_test: int | None. + bigint_test: int | None. + int_unsigned_test: int | None. + bigint_unsigned_test: int | None. + year_test: int | None. + tinyint1_test: bool | None. + bool_test: bool | None. + boolean_test: bool | None. + float_test: float | None. + double_test: float | None. + double_precision_test: float | None. + real_test: float | None. + decimal_test: decimal.Decimal | None. + numeric_test: decimal.Decimal | None. + char_test: str | None. + varchar_test: str | None. + tinytext_test: str | None. + text_test: str | None. + mediumtext_test: str | None. + longtext_test: str | None. + binary_test: memoryview | None. + varbinary_test: memoryview | None. + tinyblob_test: memoryview | None. + blob_test: memoryview | None. + mediumblob_test: memoryview | None. + longblob_test: memoryview | None. + bit_test: memoryview | None. + date_test: datetime.date | None. + datetime_test: datetime.datetime | None. + datetime6_test: datetime.datetime | None. + timestamp_test: datetime.datetime | None. + time_test: datetime.timedelta | None. + json_test: str | None. + mood: enums.TestInnerMysqlTypesMood | None. + tag: enums.TestInnerMysqlTypesTag | None. + """ + async with self._conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + async def get_one_mysql_type(self, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestMysqlType` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + async def get_one_inner_mysql_type(self, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + table_id: int. + + Returns: + Result of type `models.TestInnerMysqlType` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + def get_many_mysql_type(self, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(self._conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + def get_many_inner_mysql_type(self, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + table_id: int. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + def get_many_nullable_inner_mysql_type(self, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Args: + table_id: int. + int_test: int | None. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + async def get_one_date(self, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + id_: int. + date_test: datetime.date. + + Returns: + Result of type `datetime.date` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DATE, (id_, date_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_datetime(self, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Args: + id_: int. + datetime_test: datetime.datetime. + + Returns: + Result of type `datetime.datetime` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_time(self, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + id_: int. + time_test: datetime.timedelta. + + Returns: + Result of type `datetime.timedelta` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_TIME, (id_, time_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_bool(self, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + id_: int. + tinyint1_test: bool. + + Returns: + Result of type `bool` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = await cur.fetchone() + if row is None: + return None + return bool(row[0]) + + async def get_one_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Result of type `decimal.Decimal` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_blob(self, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Args: + id_: int. + blob_test: memoryview. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + async def get_one_bit(self, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BIT, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + async def get_one_year(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_YEAR, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_json(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_JSON, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_mood(self, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Args: + id_: int. + mood: enums.TestMysqlTypesMood. + + Returns: + Result of type `enums.TestMysqlTypesMood` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_MOOD, (id_, mood)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + async def get_one_tag(self, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `enums.TestMysqlTypesTag` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_TAG, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + def get_many_date(self, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + id_: int. + date_test: datetime.date. + + Returns: + Helper class of type `QueryResults[datetime.date]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + def get_many_time(self, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + id_: int. + time_test: datetime.timedelta. + + Returns: + Helper class of type `QueryResults[datetime.timedelta]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + def get_many_bool(self, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + id_: int. + tinyint1_test: bool. + + Returns: + Helper class of type `QueryResults[bool]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(self._conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + def get_many_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Helper class of type `QueryResults[decimal.Decimal]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + def get_many_mood(self, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Args: + mood: enums.TestMysqlTypesMood. + + Returns: + Helper class of type `QueryResults[enums.TestMysqlTypesMood]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(self._conn, GET_MANY_MOOD, _decode_hook, mood) + + def list_months(self) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Returns: + Helper class of type `QueryResults[str]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, LIST_MONTHS, operator.itemgetter(0)) + + async def count_mysql_types(self) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(COUNT_MYSQL_TYPES) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def update_varchar_test(self, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Args: + varchar_test: str. + id_: int. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + async with self._conn.cursor() as cur: + return await cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + async def delete_one_mysql_type(self, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + """ + async with self._conn.cursor() as cur: + await cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + async def all_mysql_types_cursor(self) -> asyncmy.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Returns: + The result of type `asyncmy.cursors.Cursor` returned when executing the query. + """ + cur = self._conn.cursor() + await cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + async def insert_exec_last_id(self, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Args: + name: str. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + async def get_exec_last_id_name(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def insert_type_override(self, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Args: + id_: int. + text_test: UserString | None. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + async def get_type_override(self, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestTypeOverride` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + async def get_reserved_arg(self, *, conn: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Args: + conn: str. + + Returns: + Result of type `models.TestReservedArg` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_RESERVED_ARG, (conn,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + async def insert_reserved_arg(self, *, id_: int, conn: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Args: + id_: int. + conn: str. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_RESERVED_ARG, (id_, conn)) + + async def touch_exec_last_id(self, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Args: + name: str. + id_: int. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + async with self._conn.cursor() as cur: + await cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_asyncmy/dataclass/functions/__init__.py b/test/driver_asyncmy/dataclass/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_asyncmy/dataclass/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncmy/dataclass/functions/enums.py b/test/driver_asyncmy/dataclass/functions/enums.py new file mode 100644 index 00000000..873f5d33 --- /dev/null +++ b/test/driver_asyncmy/dataclass/functions/enums.py @@ -0,0 +1,56 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_asyncmy/dataclass/functions/models.py b/test/driver_asyncmy/dataclass/functions/models.py new file mode 100644 index 00000000..3cf2cb38 --- /dev/null +++ b/test/driver_asyncmy/dataclass/functions/models.py @@ -0,0 +1,244 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlType", + "TestMysqlType", + "TestReservedArg", + "TestSlice", + "TestTypeOverride", +) + +import dataclasses +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_asyncmy.dataclass.functions import enums + import collections.abc + import datetime + import decimal + + +@dataclasses.dataclass() +class TestInnerMysqlType: + """Model representing TestInnerMysqlType. + + Attributes: + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +@dataclasses.dataclass() +class TestMysqlType: + """Model representing TestMysqlType. + + Attributes: + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +@dataclasses.dataclass() +class TestReservedArg: + """Model representing TestReservedArg. + + Attributes: + id_: int + conn: str + """ + + id_: int + conn: str + + +@dataclasses.dataclass() +class TestSlice: + """Model representing TestSlice. + + Attributes: + id_: int + name: str + note: str | None + """ + + id_: int + name: str + note: str | None + + +@dataclasses.dataclass() +class TestTypeOverride: + """Model representing TestTypeOverride. + + Attributes: + id_: int + text_test: UserString | None + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_asyncmy/dataclass/functions/queries.py b/test/driver_asyncmy/dataclass/functions/queries.py new file mode 100644 index 00000000..1300b510 --- /dev/null +++ b/test/driver_asyncmy/dataclass/functions/queries.py @@ -0,0 +1,1558 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "all_mysql_types_cursor", + "count_mysql_types", + "delete_one_mysql_type", + "get_exec_last_id_name", + "get_many_bool", + "get_many_date", + "get_many_decimal", + "get_many_inner_mysql_type", + "get_many_mood", + "get_many_mysql_type", + "get_many_nullable_inner_mysql_type", + "get_many_time", + "get_one_bit", + "get_one_blob", + "get_one_bool", + "get_one_date", + "get_one_datetime", + "get_one_decimal", + "get_one_inner_mysql_type", + "get_one_json", + "get_one_mood", + "get_one_mysql_type", + "get_one_tag", + "get_one_time", + "get_one_year", + "get_reserved_arg", + "get_type_override", + "insert_exec_last_id", + "insert_one_inner_mysql_type", + "insert_one_mysql_type", + "insert_reserved_arg", + "insert_type_override", + "list_months", + "touch_exec_last_id", + "update_varchar_test", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + import datetime + import decimal + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.dataclass.functions import enums +from test.driver_asyncmy.dataclass.functions import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `asyncmy.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns: + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopAsyncIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +async def insert_one_mysql_type( + conn: asyncmy.Connection, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, +) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + int_test: int. + integer_test: int. + mediumint_test: int. + smallint_test: int. + tinyint_test: int. + bigint_test: int. + int_unsigned_test: int. + bigint_unsigned_test: int. + year_test: int. + tinyint1_test: bool. + bool_test: bool. + boolean_test: bool. + float_test: float. + double_test: float. + double_precision_test: float. + real_test: float. + decimal_test: decimal.Decimal. + numeric_test: decimal.Decimal. + char_test: str. + varchar_test: str. + tinytext_test: str. + text_test: str. + mediumtext_test: str. + longtext_test: str. + binary_test: memoryview. + varbinary_test: memoryview. + tinyblob_test: memoryview. + blob_test: memoryview. + mediumblob_test: memoryview. + longblob_test: memoryview. + bit_test: memoryview. + date_test: datetime.date. + datetime_test: datetime.datetime. + datetime6_test: datetime.datetime. + timestamp_test: datetime.datetime. + time_test: datetime.timedelta. + json_test: str. + mood: enums.TestMysqlTypesMood. + tag: enums.TestMysqlTypesTag. + """ + async with conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + +async def insert_one_inner_mysql_type( + conn: asyncmy.Connection, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, +) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + table_id: int. + int_test: int | None. + integer_test: int | None. + mediumint_test: int | None. + smallint_test: int | None. + tinyint_test: int | None. + bigint_test: int | None. + int_unsigned_test: int | None. + bigint_unsigned_test: int | None. + year_test: int | None. + tinyint1_test: bool | None. + bool_test: bool | None. + boolean_test: bool | None. + float_test: float | None. + double_test: float | None. + double_precision_test: float | None. + real_test: float | None. + decimal_test: decimal.Decimal | None. + numeric_test: decimal.Decimal | None. + char_test: str | None. + varchar_test: str | None. + tinytext_test: str | None. + text_test: str | None. + mediumtext_test: str | None. + longtext_test: str | None. + binary_test: memoryview | None. + varbinary_test: memoryview | None. + tinyblob_test: memoryview | None. + blob_test: memoryview | None. + mediumblob_test: memoryview | None. + longblob_test: memoryview | None. + bit_test: memoryview | None. + date_test: datetime.date | None. + datetime_test: datetime.datetime | None. + datetime6_test: datetime.datetime | None. + timestamp_test: datetime.datetime | None. + time_test: datetime.timedelta | None. + json_test: str | None. + mood: enums.TestInnerMysqlTypesMood | None. + tag: enums.TestInnerMysqlTypesTag | None. + """ + async with conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + +async def get_one_mysql_type(conn: asyncmy.Connection, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestMysqlType` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + +async def get_one_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + table_id: int. + + Returns: + Result of type `models.TestInnerMysqlType` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + +def get_many_mysql_type(conn: asyncmy.Connection, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + +def get_many_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + table_id: int. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + +def get_many_nullable_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + table_id: int. + int_test: int | None. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + +async def get_one_date(conn: asyncmy.Connection, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + date_test: datetime.date. + + Returns: + Result of type `datetime.date` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DATE, (id_, date_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_datetime(conn: asyncmy.Connection, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + datetime_test: datetime.datetime. + + Returns: + Result of type `datetime.datetime` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_time(conn: asyncmy.Connection, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + time_test: datetime.timedelta. + + Returns: + Result of type `datetime.timedelta` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_TIME, (id_, time_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_bool(conn: asyncmy.Connection, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + tinyint1_test: bool. + + Returns: + Result of type `bool` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = await cur.fetchone() + if row is None: + return None + return bool(row[0]) + + +async def get_one_decimal(conn: asyncmy.Connection, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Result of type `decimal.Decimal` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_blob(conn: asyncmy.Connection, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + blob_test: memoryview. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +async def get_one_bit(conn: asyncmy.Connection, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BIT, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +async def get_one_year(conn: asyncmy.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_YEAR, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_json(conn: asyncmy.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_JSON, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_mood(conn: asyncmy.Connection, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + mood: enums.TestMysqlTypesMood. + + Returns: + Result of type `enums.TestMysqlTypesMood` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_MOOD, (id_, mood)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + +async def get_one_tag(conn: asyncmy.Connection, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `enums.TestMysqlTypesTag` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_TAG, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + +def get_many_date(conn: asyncmy.Connection, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + date_test: datetime.date. + + Returns: + Helper class of type `QueryResults[datetime.date]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + +def get_many_time(conn: asyncmy.Connection, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + time_test: datetime.timedelta. + + Returns: + Helper class of type `QueryResults[datetime.timedelta]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + +def get_many_bool(conn: asyncmy.Connection, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + tinyint1_test: bool. + + Returns: + Helper class of type `QueryResults[bool]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + +def get_many_decimal(conn: asyncmy.Connection, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Helper class of type `QueryResults[decimal.Decimal]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + +def get_many_mood(conn: asyncmy.Connection, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + mood: enums.TestMysqlTypesMood. + + Returns: + Helper class of type `QueryResults[enums.TestMysqlTypesMood]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(conn, GET_MANY_MOOD, _decode_hook, mood) + + +def list_months(conn: asyncmy.Connection) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + Helper class of type `QueryResults[str]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, LIST_MONTHS, operator.itemgetter(0)) + + +async def count_mysql_types(conn: asyncmy.Connection) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(COUNT_MYSQL_TYPES) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def update_varchar_test(conn: asyncmy.Connection, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + varchar_test: str. + id_: int. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + async with conn.cursor() as cur: + return await cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + +async def delete_one_mysql_type(conn: asyncmy.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + """ + async with conn.cursor() as cur: + await cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + +async def all_mysql_types_cursor(conn: asyncmy.Connection) -> asyncmy.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + The result of type `asyncmy.cursors.Cursor` returned when executing the query. + """ + cur = conn.cursor() + await cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + +async def insert_exec_last_id(conn: asyncmy.Connection, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + name: str. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + +async def get_exec_last_id_name(conn: asyncmy.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def insert_type_override(conn: asyncmy.Connection, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + text_test: UserString | None. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + +async def get_type_override(conn: asyncmy.Connection, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestTypeOverride` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + +async def get_reserved_arg(conn: asyncmy.Connection, *, conn_2: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + conn_2: str. + + Returns: + Result of type `models.TestReservedArg` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_RESERVED_ARG, (conn_2,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + +async def insert_reserved_arg(conn: asyncmy.Connection, *, id_: int, conn_2: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + conn_2: str. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_RESERVED_ARG, (id_, conn_2)) + + +async def touch_exec_last_id(conn: asyncmy.Connection, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + name: str. + id_: int. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + async with conn.cursor() as cur: + await cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_asyncmy/dataclass/functions/queries_slice.py b/test/driver_asyncmy/dataclass/functions/queries_slice.py new file mode 100644 index 00000000..07258345 --- /dev/null +++ b/test/driver_asyncmy/dataclass/functions/queries_slice.py @@ -0,0 +1,323 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_slice.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries_slice.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "delete_slice_rows", + "get_first_slice_name", + "get_slice_row_filtered", + "get_slice_rows", + "get_slice_rows_by_name_or_note", + "get_slice_rows_by_name_or_note_filtered", + "get_slice_rows_by_notes", + "insert_slice_row", +) + +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.dataclass.functions import models + + +INSERT_SLICE_ROW: typing.Final[str] = """-- name: InsertSliceRow :exec +INSERT INTO test_slice (id, name, note) VALUES (%s, %s, %s) +""" + +GET_SLICE_ROWS: typing.Final[str] = """-- name: GetSliceRows :many +SELECT id, name, note FROM test_slice WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +GET_SLICE_ROW_FILTERED: typing.Final[str] = """-- name: GetSliceRowFiltered :one +SELECT id, name, note FROM test_slice WHERE name = %s AND id IN (/*SLICE:ids*/%s) AND id != %s LIMIT 1 +""" + +GET_SLICE_ROWS_BY_NOTES: typing.Final[str] = """-- name: GetSliceRowsByNotes :many +SELECT id, name, note FROM test_slice WHERE note IN (/*SLICE:notes*/%s) ORDER BY id +""" + +GET_FIRST_SLICE_NAME: typing.Final[str] = """-- name: GetFirstSliceName :one +SELECT name FROM test_slice WHERE id IN (/*SLICE:ids*/%s) OR name IN (/*SLICE:names*/%s) ORDER BY id LIMIT 1 +""" + +GET_SLICE_ROWS_BY_NAME_OR_NOTE: typing.Final[str] = """-- name: GetSliceRowsByNameOrNote :many +SELECT id, name, note FROM test_slice WHERE name IN (/*SLICE:names*/%s) OR note IN (/*SLICE:names*/%s) ORDER BY id +""" + +GET_SLICE_ROWS_BY_NAME_OR_NOTE_FILTERED: typing.Final[str] = """-- name: GetSliceRowsByNameOrNoteFiltered :many +SELECT id, name, note FROM test_slice WHERE name IN (/*SLICE:names*/%s) AND id != %s OR note IN (/*SLICE:names*/%s) ORDER BY id +""" + +DELETE_SLICE_ROWS: typing.Final[str] = """-- name: DeleteSliceRows :execrows +DELETE FROM test_slice WHERE id IN (/*SLICE:ids*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `asyncmy.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns: + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopAsyncIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +async def insert_slice_row(conn: asyncmy.Connection, *, id_: int, name: str, note: str | None) -> None: + """Execute SQL query with `name: InsertSliceRow :exec`. + + ```sql + INSERT INTO test_slice (id, name, note) VALUES (%s, %s, %s) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + name: str. + note: str | None. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_SLICE_ROW, (id_, name, note)) + + +def get_slice_rows(conn: asyncmy.Connection, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestSlice]: + """Fetch many from the db using the SQL query with `name: GetSliceRows :many`. + + ```sql + SELECT id, name, note FROM test_slice WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + + Returns: + Helper class of type `QueryResults[models.TestSlice]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestSlice: + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + sql = GET_SLICE_ROWS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *ids) + + +async def get_slice_row_filtered(conn: asyncmy.Connection, *, name: str, ids: collections.abc.Sequence[int], id_: int) -> models.TestSlice | None: + """Fetch one from the db using the SQL query with `name: GetSliceRowFiltered :one`. + + ```sql + SELECT id, name, note FROM test_slice WHERE name = %s AND id IN (/*SLICE:ids*/%s) AND id != %s LIMIT 1 + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + name: str. + ids: collections.abc.Sequence[int]. + id_: int. + + Returns: + Result of type `models.TestSlice` fetched from the db. Will be `None` if not found. + """ + sql = GET_SLICE_ROW_FILTERED.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + async with conn.cursor() as cur: + await cur.execute(sql, (name, *ids, id_)) + row = await cur.fetchone() + if row is None: + return None + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + +def get_slice_rows_by_notes(conn: asyncmy.Connection, *, notes: collections.abc.Sequence[str]) -> QueryResults[models.TestSlice]: + """Fetch many from the db using the SQL query with `name: GetSliceRowsByNotes :many`. + + ```sql + SELECT id, name, note FROM test_slice WHERE note IN (/*SLICE:notes*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + notes: collections.abc.Sequence[str]. + + Returns: + Helper class of type `QueryResults[models.TestSlice]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestSlice: + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + sql = GET_SLICE_ROWS_BY_NOTES.replace("/*SLICE:notes*/%s", ",".join(("%s",) * len(notes)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *notes) + + +async def get_first_slice_name(conn: asyncmy.Connection, *, ids: collections.abc.Sequence[int], names: collections.abc.Sequence[str]) -> str | None: + """Fetch one from the db using the SQL query with `name: GetFirstSliceName :one`. + + ```sql + SELECT name FROM test_slice WHERE id IN (/*SLICE:ids*/%s) OR name IN (/*SLICE:names*/%s) ORDER BY id LIMIT 1 + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + names: collections.abc.Sequence[str]. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + sql = GET_FIRST_SLICE_NAME.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + sql = sql.replace("/*SLICE:names*/%s", ",".join(("%s",) * len(names)) or "NULL", 1) + async with conn.cursor() as cur: + await cur.execute(sql, (*ids, *names)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +def get_slice_rows_by_name_or_note(conn: asyncmy.Connection, *, names: collections.abc.Sequence[str]) -> QueryResults[models.TestSlice]: + """Fetch many from the db using the SQL query with `name: GetSliceRowsByNameOrNote :many`. + + ```sql + SELECT id, name, note FROM test_slice WHERE name IN (/*SLICE:names*/%s) OR note IN (/*SLICE:names*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + names: collections.abc.Sequence[str]. + + Returns: + Helper class of type `QueryResults[models.TestSlice]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestSlice: + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + sql = GET_SLICE_ROWS_BY_NAME_OR_NOTE.replace("/*SLICE:names*/%s", ",".join(("%s",) * len(names)) or "NULL") + return QueryResults(conn, sql, _decode_hook, *names, *names) + + +def get_slice_rows_by_name_or_note_filtered(conn: asyncmy.Connection, *, names: collections.abc.Sequence[str], id_: int) -> QueryResults[models.TestSlice]: + """Fetch many from the db using the SQL query with `name: GetSliceRowsByNameOrNoteFiltered :many`. + + ```sql + SELECT id, name, note FROM test_slice WHERE name IN (/*SLICE:names*/%s) AND id != %s OR note IN (/*SLICE:names*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + names: collections.abc.Sequence[str]. + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestSlice]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestSlice: + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + sql = GET_SLICE_ROWS_BY_NAME_OR_NOTE_FILTERED.replace("/*SLICE:names*/%s", ",".join(("%s",) * len(names)) or "NULL") + return QueryResults(conn, sql, _decode_hook, *names, id_, *names) + + +async def delete_slice_rows(conn: asyncmy.Connection, *, ids: collections.abc.Sequence[int]) -> int: + """Execute SQL query with `name: DeleteSliceRows :execrows` and return the number of affected rows. + + ```sql + DELETE FROM test_slice WHERE id IN (/*SLICE:ids*/%s) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + sql = DELETE_SLICE_ROWS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + async with conn.cursor() as cur: + return await cur.execute(sql, (*ids,)) diff --git a/test/driver_asyncmy/dataclass/ruff.toml b/test/driver_asyncmy/dataclass/ruff.toml new file mode 100644 index 00000000..f37cac11 --- /dev/null +++ b/test/driver_asyncmy/dataclass/ruff.toml @@ -0,0 +1,5 @@ +extend="../../../ruff.toml" + + +[lint.pydocstyle] +convention = "google" \ No newline at end of file diff --git a/test/driver_asyncmy/dataclass/test_asyncmy_dataclass_classes.py b/test/driver_asyncmy/dataclass/test_asyncmy_dataclass_classes.py new file mode 100644 index 00000000..9e213454 --- /dev/null +++ b/test/driver_asyncmy/dataclass/test_asyncmy_dataclass_classes.py @@ -0,0 +1,999 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import dataclasses +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import asyncmy +import asyncmy.cursors +import pytest +import pytest_asyncio + +from test.driver_asyncmy import no_row_conn +from test.driver_asyncmy.dataclass.classes import enums +from test.driver_asyncmy.dataclass.classes import models +from test.driver_asyncmy.dataclass.classes import queries + +MODEL_ID = 6000 +NORMALIZATION_ID = 6001 +OVERRIDE_ID = 6100 +OVERRIDE_NULL_ID = 6101 +RESERVED_ARG_ID = 6150 +RESERVED_ARG_VALUE = "dataclass-classes-conn" + + +@pytest.mark.asyncio(loop_scope="session") +class TestAsyncmyDataclassClasses: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MODEL_ID, + int_test=42, + integer_test=-7, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 10, + year_test=2024, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.e, + double_precision_test=1.41421, + real_test=math.pi, + decimal_test=decimal.Decimal("12.3400"), + numeric_test=decimal.Decimal("3.50"), + char_test="ABCDEFGHIJ", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"0123456789abcdef"), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tiny blob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"medium blob"), + longblob_test=memoryview(b"long blob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 5), + datetime_test=datetime.datetime(2026, 1, 5, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 5, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 5, 12, 30, 45), + time_test=datetime.timedelta(hours=13, minutes=14, seconds=15), + json_test=json.dumps({"foo": "bar", "count": 2}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.ALPHA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=None, + bool_test=True, + boolean_test=None, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=None, + tinyblob_test=model.tinyblob_test, + blob_test=None, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=None, + datetime6_test=model.datetime6_test, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=enums.TestInnerMysqlTypesTag.BETA, + ) + + @pytest_asyncio.fixture(scope="class", loop_scope="session") + async def queries_obj(self, asyncmy_conn: asyncmy.Connection) -> queries.Queries: + return queries.Queries(conn=asyncmy_conn) + + @pytest.mark.asyncio(loop_scope="session") + async def test_conn_attr(self, queries_obj: queries.Queries) -> None: + assert isinstance(queries_obj.conn, asyncmy.Connection) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::insert") + async def test_insert( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + await queries_obj.insert_one_mysql_type( + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::inner_insert", depends=["AsyncmyTestDataclassClasses::insert"]) + async def test_inner_insert( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + await queries_obj.insert_one_inner_mysql_type( + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_one", depends=["AsyncmyTestDataclassClasses::inner_insert"]) + async def test_get_one( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mysql_type(id_=model.id_) + + assert result is not None + + assert isinstance(result, models.TestMysqlType) + + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + # MySQL normalizes JSON spacing, so the raw string may differ. + assert json.loads(result.json_test) == json.loads(model.json_test) + assert dataclasses.replace(result, json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_one_none", depends=["AsyncmyTestDataclassClasses::get_one"]) + async def test_get_one_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_mysql_type(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_one_inner", depends=["AsyncmyTestDataclassClasses::get_one_none"]) + async def test_get_one_inner( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = await queries_obj.get_one_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_one_inner_none", depends=["AsyncmyTestDataclassClasses::get_one_inner"]) + async def test_get_one_inner_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_inner_mysql_type(table_id=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_date", depends=["AsyncmyTestDataclassClasses::get_one_inner_none"]) + async def test_get_date( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_date(id_=model.id_, date_test=model.date_test) + + assert result is not None + + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_date_none", depends=["AsyncmyTestDataclassClasses::get_date"]) + async def test_get_date_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_date(id_=0, date_test=model.date_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_datetime", depends=["AsyncmyTestDataclassClasses::get_date_none"]) + async def test_get_datetime( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_datetime(id_=model.id_, datetime_test=model.datetime_test) + + assert result is not None + + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_datetime_none", depends=["AsyncmyTestDataclassClasses::get_datetime"]) + async def test_get_datetime_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_datetime(id_=0, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_time", depends=["AsyncmyTestDataclassClasses::get_datetime_none"]) + async def test_get_time( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_time(id_=model.id_, time_test=model.time_test) + + assert result is not None + + # MySQL time columns arrive as timedelta, not datetime.time. + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_time_none", depends=["AsyncmyTestDataclassClasses::get_time"]) + async def test_get_time_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_time(id_=0, time_test=model.time_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_bool", depends=["AsyncmyTestDataclassClasses::get_time_none"]) + async def test_get_bool( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_bool(id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + + assert isinstance(result, bool) + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_bool_none", depends=["AsyncmyTestDataclassClasses::get_bool"]) + async def test_get_bool_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_bool(id_=0, tinyint1_test=False) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_decimal", depends=["AsyncmyTestDataclassClasses::get_bool_none"]) + async def test_get_decimal( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_decimal(id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + + assert isinstance(result, decimal.Decimal) + # decimal(12,4) always comes back padded to scale. + assert str(result) == "12.3400" + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_decimal_none", depends=["AsyncmyTestDataclassClasses::get_decimal"]) + async def test_get_decimal_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_decimal(id_=0, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_blob", depends=["AsyncmyTestDataclassClasses::get_decimal_none"]) + async def test_get_blob( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_blob(id_=model.id_, blob_test=model.blob_test) + + assert result is not None + + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_blob_none", depends=["AsyncmyTestDataclassClasses::get_blob"]) + async def test_get_blob_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_blob(id_=0, blob_test=memoryview(b"test")) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_bit", depends=["AsyncmyTestDataclassClasses::get_blob_none"]) + async def test_get_bit( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_bit(id_=model.id_) + + assert result is not None + + # bit(8) arrives as a single byte of raw bits. + assert isinstance(result, memoryview) + assert len(result) == 1 + assert bytes(result) == b"\x80" + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_bit_none", depends=["AsyncmyTestDataclassClasses::get_bit"]) + async def test_get_bit_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_bit(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_year", depends=["AsyncmyTestDataclassClasses::get_bit_none"]) + async def test_get_year( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_year(id_=model.id_) + + assert result is not None + + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_year_none", depends=["AsyncmyTestDataclassClasses::get_year"]) + async def test_get_year_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_year(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_json", depends=["AsyncmyTestDataclassClasses::get_year_none"]) + async def test_get_json( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_json(id_=model.id_) + + assert result is not None + + assert isinstance(result, str) + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_json_none", depends=["AsyncmyTestDataclassClasses::get_json"]) + async def test_get_json_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_json(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_mood", depends=["AsyncmyTestDataclassClasses::get_json_none"]) + async def test_get_mood( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mood(id_=model.id_, mood=model.mood) + + assert result is not None + + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_mood_none", depends=["AsyncmyTestDataclassClasses::get_mood"]) + async def test_get_mood_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mood(id_=0, mood=model.mood) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_tag", depends=["AsyncmyTestDataclassClasses::get_mood_none"]) + async def test_get_tag( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_tag(id_=MODEL_ID) + + assert result is not None + + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.ALPHA + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_tag_none", depends=["AsyncmyTestDataclassClasses::get_tag"]) + async def test_get_tag_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_tag(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::value_normalization", depends=["AsyncmyTestDataclassClasses::get_tag_none"]) + async def test_value_normalization( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + # decimal(12,4) pads to scale, char(10) strips trailing spaces and + # binary(16) is right-padded with NUL bytes on return. + await queries_obj.insert_one_mysql_type( + id_=NORMALIZATION_ID, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("3.5"), + char_test="AB ", + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=memoryview(b"abc"), + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + result = await queries_obj.get_one_mysql_type(id_=NORMALIZATION_ID) + assert result is not None + assert str(result.decimal_test) == "12.3400" + assert str(result.numeric_test) == "3.50" + assert result.char_test == "AB" + assert bytes(result.binary_test) == b"abc" + b"\x00" * 13 + await queries_obj.delete_one_mysql_type(id_=NORMALIZATION_ID) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many", depends=["AsyncmyTestDataclassClasses::value_normalization"]) + async def test_get_many(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mysql_type(id_=model.id_) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + + assert dataclasses.replace(results[0], json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_iter", depends=["AsyncmyTestDataclassClasses::get_many"]) + async def test_get_many_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_mysql_type(id_=model.id_): + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert dataclasses.replace(result, json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_inner", depends=["AsyncmyTestDataclassClasses::get_many_iter"]) + async def test_get_many_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestInnerMysqlType) + + assert results[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_inner_iter", depends=["AsyncmyTestDataclassClasses::get_many_inner"]) + async def test_get_many_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::get_many_nullable_inner", + depends=["AsyncmyTestDataclassClasses::get_many_inner_iter"], + ) + async def test_get_many_nullable_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the <=> in the query is NULL-safe equality. + result = queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestInnerMysqlType) + + assert results[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::get_many_nullable_inner_iter", + depends=["AsyncmyTestDataclassClasses::get_many_nullable_inner"], + ) + async def test_get_many_nullable_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::get_many_date", + depends=["AsyncmyTestDataclassClasses::get_many_nullable_inner_iter"], + ) + async def test_get_many_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_date(id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], datetime.date) + + assert results[0] == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_date_iter", depends=["AsyncmyTestDataclassClasses::get_many_date"]) + async def test_get_many_date_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_date(id_=model.id_, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_time", depends=["AsyncmyTestDataclassClasses::get_many_date_iter"]) + async def test_get_many_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_time(id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], datetime.timedelta) + + assert results[0] == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_time_iter", depends=["AsyncmyTestDataclassClasses::get_many_time"]) + async def test_get_many_time_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_time(id_=model.id_, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_bool", depends=["AsyncmyTestDataclassClasses::get_many_time_iter"]) + async def test_get_many_bool(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_bool(id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], bool) + + assert results[0] is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_bool_iter", depends=["AsyncmyTestDataclassClasses::get_many_bool"]) + async def test_get_many_bool_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_bool(id_=model.id_, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_decimal", depends=["AsyncmyTestDataclassClasses::get_many_bool_iter"]) + async def test_get_many_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_decimal(id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], decimal.Decimal) + + assert results[0] == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::get_many_decimal_iter", + depends=["AsyncmyTestDataclassClasses::get_many_decimal"], + ) + async def test_get_many_decimal_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_decimal(id_=model.id_, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_mood", depends=["AsyncmyTestDataclassClasses::get_many_decimal_iter"]) + async def test_get_many_mood(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mood(mood=model.mood) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], enums.TestMysqlTypesMood) + + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::get_many_mood_iter", depends=["AsyncmyTestDataclassClasses::get_many_mood"]) + async def test_get_many_mood_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_mood(mood=model.mood): + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::list_months", depends=["AsyncmyTestDataclassClasses::get_many_mood_iter"]) + async def test_list_months(self, queries_obj: queries.Queries) -> None: + # Parameterless :many with literal percents in the SQL; regression + # test for the percent-doubling bug. + result = queries_obj.list_months() + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert results == ["2026-01"] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::list_months_iter", depends=["AsyncmyTestDataclassClasses::list_months"]) + async def test_list_months_iter(self, queries_obj: queries.Queries) -> None: + months = [month async for month in queries_obj.list_months()] + assert months == ["2026-01"] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::count", depends=["AsyncmyTestDataclassClasses::list_months_iter"]) + async def test_count(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.count_mysql_types() + + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::update_varchar_rows", depends=["AsyncmyTestDataclassClasses::count"]) + async def test_update_varchar_rows(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.update_varchar_test(varchar_test="updated varchar", id_=model.id_) + assert isinstance(result, int) + assert result == 1 + + result = await queries_obj.update_varchar_test(varchar_test="updated varchar", id_=0) + assert result == 0 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::all_types_cursor", depends=["AsyncmyTestDataclassClasses::update_varchar_rows"]) + async def test_all_types_cursor(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + cursor = await queries_obj.all_mysql_types_cursor() + assert isinstance(cursor, asyncmy.cursors.Cursor) + + rows = await cursor.fetchall() + await cursor.close() + # The shared table may carry other files' rows; assert on our own. + assert model.id_ in {row[0] for row in rows} + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::insert_exec_last_id", depends=["AsyncmyTestDataclassClasses::all_types_cursor"]) + async def test_insert_exec_last_id(self, queries_obj: queries.Queries) -> None: + # AUTO_INCREMENT counters persist across runs, so only relative + # assertions are safe. + first_id = await queries_obj.insert_exec_last_id(name="dataclass-classes-first") + assert first_id is not None + assert isinstance(first_id, int) + assert first_id > 0 + + name = await queries_obj.get_exec_last_id_name(id_=first_id) + assert name == "dataclass-classes-first" + + second_id = await queries_obj.insert_exec_last_id(name="dataclass-classes-second") + assert second_id is not None + assert second_id > first_id + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::get_exec_last_id_name_none", + depends=["AsyncmyTestDataclassClasses::insert_exec_last_id"], + ) + async def test_get_exec_last_id_name_none(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.get_exec_last_id_name(id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::delete_mysql_type", + depends=["AsyncmyTestDataclassClasses::get_exec_last_id_name_none"], + ) + async def test_delete_mysql_type(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + await queries_obj.delete_one_mysql_type(id_=model.id_) + + result = await queries_obj.get_one_mysql_type(id_=model.id_) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::insert_type_override", + ) + async def test_insert_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + await queries_obj.insert_type_override(id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::get_type_override", + depends=["AsyncmyTestDataclassClasses::insert_type_override"], + ) + async def test_get_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = await queries_obj.get_type_override(id_=override_model.id_) + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::get_type_override_none", + depends=["AsyncmyTestDataclassClasses::get_type_override"], + ) + async def test_get_type_override_none(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = await queries_obj.get_type_override(id_=override_model.id_ - 1) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::type_override_null_value", + depends=["AsyncmyTestDataclassClasses::get_type_override_none"], + ) + async def test_type_override_null_value(self, queries_obj: queries.Queries) -> None: + # The UserString override sits on a nullable column. + await queries_obj.insert_type_override(id_=OVERRIDE_NULL_ID, text_test=None) + + result = await queries_obj.get_type_override(id_=OVERRIDE_NULL_ID) + assert result is not None + assert result.text_test is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassClasses::insert_reserved_arg") + async def test_insert_reserved_arg(self, queries_obj: queries.Queries) -> None: + # The column is literally named "conn"; on methods no deduplication + # against an implicit connection argument is needed. + await queries_obj.insert_reserved_arg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassClasses::get_reserved_arg", + depends=["AsyncmyTestDataclassClasses::insert_reserved_arg"], + ) + async def test_get_reserved_arg(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.get_reserved_arg(conn=RESERVED_ARG_VALUE) + assert result == models.TestReservedArg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(depends=["AsyncmyTestDataclassClasses::insert_reserved_arg"]) + async def test_get_reserved_arg_not_found(self, queries_obj: queries.Queries) -> None: + assert await queries_obj.get_reserved_arg(conn="missing-reserved-arg-value") is None + + @pytest.mark.asyncio(loop_scope="session") + async def test_one_missing_rows_return_none(self, asyncmy_conn: asyncmy.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. + obj = queries.Queries(conn=asyncmy_conn) + assert await obj.get_one_mysql_type(id_=-1) is None + assert await obj.get_one_inner_mysql_type(table_id=-1) is None + assert await obj.get_one_date(id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert await obj.get_one_datetime(id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert await obj.get_one_time(id_=-1, time_test=datetime.timedelta()) is None + assert await obj.get_one_bool(id_=-1, tinyint1_test=False) is None + assert await obj.get_one_decimal(id_=-1, decimal_test=decimal.Decimal(0)) is None + assert await obj.get_one_blob(id_=-1, blob_test=memoryview(b"")) is None + assert await obj.get_one_bit(id_=-1) is None + assert await obj.get_one_year(id_=-1) is None + assert await obj.get_one_json(id_=-1) is None + assert await obj.get_one_mood(id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert await obj.get_one_tag(id_=-1) is None + assert await obj.get_exec_last_id_name(id_=-1) is None + assert await obj.get_type_override(id_=-1) is None + assert await obj.get_reserved_arg(conn="missing") is None + assert await obj.touch_exec_last_id(name="untouched", id_=-1) is None + + # count(*) always returns a row; its miss branch needs the stub. + stub = typing.cast("asyncmy.Connection", no_row_conn.NoRowConn()) + assert await queries.Queries(conn=stub).count_mysql_types() is None diff --git a/test/driver_asyncmy/dataclass/test_asyncmy_dataclass_functions.py b/test/driver_asyncmy/dataclass/test_asyncmy_dataclass_functions.py new file mode 100644 index 00000000..c383d63d --- /dev/null +++ b/test/driver_asyncmy/dataclass/test_asyncmy_dataclass_functions.py @@ -0,0 +1,1116 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import dataclasses +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import asyncmy +import asyncmy.cursors +import pytest + +from test.driver_asyncmy import no_row_conn +from test.driver_asyncmy.dataclass.functions import enums +from test.driver_asyncmy.dataclass.functions import models +from test.driver_asyncmy.dataclass.functions import queries +from test.driver_asyncmy.dataclass.functions import queries_slice + +MODEL_ID = 6500 +NORMALIZATION_ID = 6501 +OVERRIDE_ID = 6600 +OVERRIDE_NULL_ID = 6601 +RESERVED_ARG_ID = 6650 +RESERVED_ARG_VALUE = "dataclass-functions-conn" +SLICE_ID_BASE = 6900 +SLICE_ROW_COUNT = 4 + + +@pytest.mark.asyncio(loop_scope="session") +class TestAsyncmyDataclassFunctions: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MODEL_ID, + int_test=42, + integer_test=-7, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 10, + year_test=2024, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.e, + double_precision_test=1.41421, + real_test=math.pi, + decimal_test=decimal.Decimal("12.3400"), + numeric_test=decimal.Decimal("3.50"), + char_test="ABCDEFGHIJ", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"0123456789abcdef"), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tiny blob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"medium blob"), + longblob_test=memoryview(b"long blob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 5), + datetime_test=datetime.datetime(2026, 1, 5, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 5, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 5, 12, 30, 45), + time_test=datetime.timedelta(hours=13, minutes=14, seconds=15), + json_test=json.dumps({"foo": "bar", "count": 2}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.ALPHA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=None, + bool_test=True, + boolean_test=None, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=None, + tinyblob_test=model.tinyblob_test, + blob_test=None, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=None, + datetime6_test=model.datetime6_test, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=enums.TestInnerMysqlTypesTag.BETA, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::insert") + async def test_insert( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + await queries.insert_one_mysql_type( + conn=asyncmy_conn, + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::inner_insert", depends=["AsyncmyTestDataclassFunctions::insert"]) + async def test_inner_insert( + self, + asyncmy_conn: asyncmy.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + await queries.insert_one_inner_mysql_type( + conn=asyncmy_conn, + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_one", depends=["AsyncmyTestDataclassFunctions::inner_insert"]) + async def test_get_one( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + + assert isinstance(result, models.TestMysqlType) + + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + # MySQL normalizes JSON spacing, so the raw string may differ. + assert json.loads(result.json_test) == json.loads(model.json_test) + assert dataclasses.replace(result, json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_one_none", depends=["AsyncmyTestDataclassFunctions::get_one"]) + async def test_get_one_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_one_inner", depends=["AsyncmyTestDataclassFunctions::get_one_none"]) + async def test_get_one_inner( + self, + asyncmy_conn: asyncmy.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id) + + assert result is not None + + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_one_inner_none", + depends=["AsyncmyTestDataclassFunctions::get_one_inner"], + ) + async def test_get_one_inner_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_date", depends=["AsyncmyTestDataclassFunctions::get_one_inner_none"]) + async def test_get_date( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_date(conn=asyncmy_conn, id_=model.id_, date_test=model.date_test) + + assert result is not None + + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_date_none", depends=["AsyncmyTestDataclassFunctions::get_date"]) + async def test_get_date_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_date(conn=asyncmy_conn, id_=0, date_test=model.date_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_datetime", depends=["AsyncmyTestDataclassFunctions::get_date_none"]) + async def test_get_datetime( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_datetime(conn=asyncmy_conn, id_=model.id_, datetime_test=model.datetime_test) + + assert result is not None + + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_datetime_none", depends=["AsyncmyTestDataclassFunctions::get_datetime"]) + async def test_get_datetime_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_datetime(conn=asyncmy_conn, id_=0, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_time", depends=["AsyncmyTestDataclassFunctions::get_datetime_none"]) + async def test_get_time( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_time(conn=asyncmy_conn, id_=model.id_, time_test=model.time_test) + + assert result is not None + + # MySQL time columns arrive as timedelta, not datetime.time. + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_time_none", depends=["AsyncmyTestDataclassFunctions::get_time"]) + async def test_get_time_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_time(conn=asyncmy_conn, id_=0, time_test=model.time_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_bool", depends=["AsyncmyTestDataclassFunctions::get_time_none"]) + async def test_get_bool( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_bool(conn=asyncmy_conn, id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + + assert isinstance(result, bool) + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_bool_none", depends=["AsyncmyTestDataclassFunctions::get_bool"]) + async def test_get_bool_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_bool(conn=asyncmy_conn, id_=0, tinyint1_test=False) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_decimal", depends=["AsyncmyTestDataclassFunctions::get_bool_none"]) + async def test_get_decimal( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_decimal(conn=asyncmy_conn, id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + + assert isinstance(result, decimal.Decimal) + # decimal(12,4) always comes back padded to scale. + assert str(result) == "12.3400" + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_decimal_none", depends=["AsyncmyTestDataclassFunctions::get_decimal"]) + async def test_get_decimal_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_decimal(conn=asyncmy_conn, id_=0, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_blob", depends=["AsyncmyTestDataclassFunctions::get_decimal_none"]) + async def test_get_blob( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_blob(conn=asyncmy_conn, id_=model.id_, blob_test=model.blob_test) + + assert result is not None + + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_blob_none", depends=["AsyncmyTestDataclassFunctions::get_blob"]) + async def test_get_blob_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_blob(conn=asyncmy_conn, id_=0, blob_test=memoryview(b"test")) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_bit", depends=["AsyncmyTestDataclassFunctions::get_blob_none"]) + async def test_get_bit( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_bit(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + + # bit(8) arrives as a single byte of raw bits. + assert isinstance(result, memoryview) + assert len(result) == 1 + assert bytes(result) == b"\x80" + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_bit_none", depends=["AsyncmyTestDataclassFunctions::get_bit"]) + async def test_get_bit_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_bit(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_year", depends=["AsyncmyTestDataclassFunctions::get_bit_none"]) + async def test_get_year( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_year(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_year_none", depends=["AsyncmyTestDataclassFunctions::get_year"]) + async def test_get_year_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_year(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_json", depends=["AsyncmyTestDataclassFunctions::get_year_none"]) + async def test_get_json( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_json(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + + assert isinstance(result, str) + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_json_none", depends=["AsyncmyTestDataclassFunctions::get_json"]) + async def test_get_json_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_json(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_mood", depends=["AsyncmyTestDataclassFunctions::get_json_none"]) + async def test_get_mood( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mood(conn=asyncmy_conn, id_=model.id_, mood=model.mood) + + assert result is not None + + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_mood_none", depends=["AsyncmyTestDataclassFunctions::get_mood"]) + async def test_get_mood_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mood(conn=asyncmy_conn, id_=0, mood=model.mood) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_tag", depends=["AsyncmyTestDataclassFunctions::get_mood_none"]) + async def test_get_tag( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_tag(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.ALPHA + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_tag_none", depends=["AsyncmyTestDataclassFunctions::get_tag"]) + async def test_get_tag_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_tag(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::value_normalization", depends=["AsyncmyTestDataclassFunctions::get_tag_none"]) + async def test_value_normalization( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + # decimal(12,4) pads to scale, char(10) strips trailing spaces and + # binary(16) is right-padded with NUL bytes on return. + await queries.insert_one_mysql_type( + conn=asyncmy_conn, + id_=NORMALIZATION_ID, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("3.5"), + char_test="AB ", + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=memoryview(b"abc"), + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=NORMALIZATION_ID) + assert result is not None + assert str(result.decimal_test) == "12.3400" + assert str(result.numeric_test) == "3.50" + assert result.char_test == "AB" + assert bytes(result.binary_test) == b"abc" + b"\x00" * 13 + await queries.delete_one_mysql_type(conn=asyncmy_conn, id_=NORMALIZATION_ID) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many", depends=["AsyncmyTestDataclassFunctions::value_normalization"]) + async def test_get_many(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mysql_type(conn=asyncmy_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + + assert dataclasses.replace(results[0], json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many_iter", depends=["AsyncmyTestDataclassFunctions::get_many"]) + async def test_get_many_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_mysql_type(conn=asyncmy_conn, id_=model.id_): + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert dataclasses.replace(result, json_test=model.json_test) == model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many_inner", depends=["AsyncmyTestDataclassFunctions::get_many_iter"]) + async def test_get_many_inner(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = queries.get_many_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestInnerMysqlType) + + assert results[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_many_inner_iter", + depends=["AsyncmyTestDataclassFunctions::get_many_inner"], + ) + async def test_get_many_inner_iter(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries.get_many_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_many_nullable_inner", + depends=["AsyncmyTestDataclassFunctions::get_many_inner_iter"], + ) + async def test_get_many_nullable_inner(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the <=> in the query is NULL-safe equality. + result = queries.get_many_nullable_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], models.TestInnerMysqlType) + + assert results[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_many_nullable_inner_iter", + depends=["AsyncmyTestDataclassFunctions::get_many_nullable_inner"], + ) + async def test_get_many_nullable_inner_iter(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries.get_many_nullable_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_many_date", + depends=["AsyncmyTestDataclassFunctions::get_many_nullable_inner_iter"], + ) + async def test_get_many_date(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_date(conn=asyncmy_conn, id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], datetime.date) + + assert results[0] == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many_date_iter", depends=["AsyncmyTestDataclassFunctions::get_many_date"]) + async def test_get_many_date_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_date(conn=asyncmy_conn, id_=model.id_, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many_time", depends=["AsyncmyTestDataclassFunctions::get_many_date_iter"]) + async def test_get_many_time(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_time(conn=asyncmy_conn, id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], datetime.timedelta) + + assert results[0] == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many_time_iter", depends=["AsyncmyTestDataclassFunctions::get_many_time"]) + async def test_get_many_time_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_time(conn=asyncmy_conn, id_=model.id_, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many_bool", depends=["AsyncmyTestDataclassFunctions::get_many_time_iter"]) + async def test_get_many_bool(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_bool(conn=asyncmy_conn, id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], bool) + + assert results[0] is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many_bool_iter", depends=["AsyncmyTestDataclassFunctions::get_many_bool"]) + async def test_get_many_bool_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_bool(conn=asyncmy_conn, id_=model.id_, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_many_decimal", + depends=["AsyncmyTestDataclassFunctions::get_many_bool_iter"], + ) + async def test_get_many_decimal(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_decimal(conn=asyncmy_conn, id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], decimal.Decimal) + + assert results[0] == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_many_decimal_iter", + depends=["AsyncmyTestDataclassFunctions::get_many_decimal"], + ) + async def test_get_many_decimal_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_decimal(conn=asyncmy_conn, id_=model.id_, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_many_mood", + depends=["AsyncmyTestDataclassFunctions::get_many_decimal_iter"], + ) + async def test_get_many_mood(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mood(conn=asyncmy_conn, mood=model.mood) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert len(results) == 1 + assert isinstance(results[0], enums.TestMysqlTypesMood) + + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_many_mood_iter", depends=["AsyncmyTestDataclassFunctions::get_many_mood"]) + async def test_get_many_mood_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_mood(conn=asyncmy_conn, mood=model.mood): + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::list_months", depends=["AsyncmyTestDataclassFunctions::get_many_mood_iter"]) + async def test_list_months(self, asyncmy_conn: asyncmy.Connection) -> None: + # Parameterless :many with literal percents in the SQL; regression + # test for the percent-doubling bug. + result = queries.list_months(conn=asyncmy_conn) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = await result + assert results == ["2026-01"] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::list_months_iter", depends=["AsyncmyTestDataclassFunctions::list_months"]) + async def test_list_months_iter(self, asyncmy_conn: asyncmy.Connection) -> None: + months = [month async for month in queries.list_months(conn=asyncmy_conn)] + assert months == ["2026-01"] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::count", depends=["AsyncmyTestDataclassFunctions::list_months_iter"]) + async def test_count(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.count_mysql_types(conn=asyncmy_conn) + + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::update_varchar_rows", depends=["AsyncmyTestDataclassFunctions::count"]) + async def test_update_varchar_rows(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.update_varchar_test(conn=asyncmy_conn, varchar_test="updated varchar", id_=model.id_) + assert isinstance(result, int) + assert result == 1 + + result = await queries.update_varchar_test(conn=asyncmy_conn, varchar_test="updated varchar", id_=0) + assert result == 0 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::all_types_cursor", depends=["AsyncmyTestDataclassFunctions::update_varchar_rows"]) + async def test_all_types_cursor(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + cursor = await queries.all_mysql_types_cursor(conn=asyncmy_conn) + assert isinstance(cursor, asyncmy.cursors.Cursor) + + rows = await cursor.fetchall() + await cursor.close() + # The shared table may carry other files' rows; assert on our own. + assert model.id_ in {row[0] for row in rows} + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::insert_exec_last_id", depends=["AsyncmyTestDataclassFunctions::all_types_cursor"]) + async def test_insert_exec_last_id(self, asyncmy_conn: asyncmy.Connection) -> None: + # AUTO_INCREMENT counters persist across runs, so only relative + # assertions are safe. + first_id = await queries.insert_exec_last_id(conn=asyncmy_conn, name="dataclass-functions-first") + assert first_id is not None + assert isinstance(first_id, int) + assert first_id > 0 + + name = await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=first_id) + assert name == "dataclass-functions-first" + + # A statement that inserts nothing has no last row id: the OK + # packet's 0 maps to the documented None. + assert await queries.touch_exec_last_id(conn=asyncmy_conn, name="untouched", id_=first_id + 1000000) is None + + second_id = await queries.insert_exec_last_id(conn=asyncmy_conn, name="dataclass-functions-second") + assert second_id is not None + assert second_id > first_id + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_exec_last_id_name_none", + depends=["AsyncmyTestDataclassFunctions::insert_exec_last_id"], + ) + async def test_get_exec_last_id_name_none(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=0) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::delete_mysql_type", + depends=["AsyncmyTestDataclassFunctions::get_exec_last_id_name_none"], + ) + async def test_delete_mysql_type(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + await queries.delete_one_mysql_type(conn=asyncmy_conn, id_=model.id_) + + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=model.id_) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::insert_type_override", + ) + async def test_insert_type_override(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + await queries.insert_type_override(conn=asyncmy_conn, id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_type_override", + depends=["AsyncmyTestDataclassFunctions::insert_type_override"], + ) + async def test_get_type_override(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + result = await queries.get_type_override(conn=asyncmy_conn, id_=override_model.id_) + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_type_override_none", + depends=["AsyncmyTestDataclassFunctions::get_type_override"], + ) + async def test_get_type_override_none(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + result = await queries.get_type_override(conn=asyncmy_conn, id_=override_model.id_ - 1) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::type_override_null_value", + depends=["AsyncmyTestDataclassFunctions::get_type_override_none"], + ) + async def test_type_override_null_value(self, asyncmy_conn: asyncmy.Connection) -> None: + # The UserString override sits on a nullable column. + await queries.insert_type_override(conn=asyncmy_conn, id_=OVERRIDE_NULL_ID, text_test=None) + + result = await queries.get_type_override(conn=asyncmy_conn, id_=OVERRIDE_NULL_ID) + assert result is not None + assert result.text_test is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::insert_reserved_arg") + async def test_insert_reserved_arg(self, asyncmy_conn: asyncmy.Connection) -> None: + # The column is literally named "conn"; the generated parameter must + # be deduplicated against the implicit connection argument. + await queries.insert_reserved_arg(conn=asyncmy_conn, id_=RESERVED_ARG_ID, conn_2=RESERVED_ARG_VALUE) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestDataclassFunctions::get_reserved_arg", + depends=["AsyncmyTestDataclassFunctions::insert_reserved_arg"], + ) + async def test_get_reserved_arg(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.get_reserved_arg(conn=asyncmy_conn, conn_2=RESERVED_ARG_VALUE) + assert result == models.TestReservedArg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(depends=["AsyncmyTestDataclassFunctions::insert_reserved_arg"]) + async def test_get_reserved_arg_not_found(self, asyncmy_conn: asyncmy.Connection) -> None: + assert await queries.get_reserved_arg(conn=asyncmy_conn, conn_2="missing-reserved-arg-value") is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::insert_slice_rows") + async def test_insert_slice_rows(self, asyncmy_conn: asyncmy.Connection) -> None: + for offset, (name, note) in enumerate((("a", "x"), ("b", "y"), ("c", None), ("b", "y"))): + await queries_slice.insert_slice_row(conn=asyncmy_conn, id_=SLICE_ID_BASE + offset, name=name, note=note) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_slice_rows", depends=["AsyncmyTestDataclassFunctions::insert_slice_rows"]) + async def test_get_slice_rows(self, asyncmy_conn: asyncmy.Connection) -> None: + result = queries_slice.get_slice_rows(conn=asyncmy_conn, ids=[SLICE_ID_BASE, SLICE_ID_BASE + 2]) + assert isinstance(result, queries_slice.QueryResults) + rows = await result + assert rows == [ + models.TestSlice(id_=SLICE_ID_BASE, name="a", note="x"), + models.TestSlice(id_=SLICE_ID_BASE + 2, name="c", note=None), + ] + assert [row async for row in result] == rows + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_slice_rows_empty_slice", depends=["AsyncmyTestDataclassFunctions::insert_slice_rows"]) + async def test_get_slice_rows_empty_slice(self, asyncmy_conn: asyncmy.Connection) -> None: + # An empty sequence expands the placeholder to NULL: IN (NULL) + # matches no rows instead of raising. + assert await queries_slice.get_slice_rows(conn=asyncmy_conn, ids=[]) == [] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_slice_row_filtered", depends=["AsyncmyTestDataclassFunctions::insert_slice_rows"]) + async def test_get_slice_row_filtered(self, asyncmy_conn: asyncmy.Connection) -> None: + # Plain params surround the slice, so this proves the flattened + # argument tuple binds in SQL text order. + row = await queries_slice.get_slice_row_filtered( + conn=asyncmy_conn, + name="b", + ids=[SLICE_ID_BASE + 1, SLICE_ID_BASE + 3], + id_=SLICE_ID_BASE + 1, + ) + assert row == models.TestSlice(id_=SLICE_ID_BASE + 3, name="b", note="y") + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_slice_row_filtered_not_found", depends=["AsyncmyTestDataclassFunctions::insert_slice_rows"]) + async def test_get_slice_row_filtered_not_found(self, asyncmy_conn: asyncmy.Connection) -> None: + assert await queries_slice.get_slice_row_filtered(conn=asyncmy_conn, name="a", ids=[SLICE_ID_BASE], id_=SLICE_ID_BASE) is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_slice_rows_by_notes", depends=["AsyncmyTestDataclassFunctions::insert_slice_rows"]) + async def test_get_slice_rows_by_notes(self, asyncmy_conn: asyncmy.Connection) -> None: + # The slice targets a nullable column; the parameter is still a plain + # Sequence, and rows whose note is NULL never match. + rows = await queries_slice.get_slice_rows_by_notes(conn=asyncmy_conn, notes=["y"]) + assert rows == [ + models.TestSlice(id_=SLICE_ID_BASE + 1, name="b", note="y"), + models.TestSlice(id_=SLICE_ID_BASE + 3, name="b", note="y"), + ] + assert await queries_slice.get_slice_rows_by_notes(conn=asyncmy_conn, notes=[]) == [] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_slice_rows_by_name_or_note", depends=["AsyncmyTestDataclassFunctions::insert_slice_rows"]) + async def test_get_slice_rows_by_name_or_note(self, asyncmy_conn: asyncmy.Connection) -> None: + # The same slice name is used twice, so every marker occurrence is + # expanded and the sequence is bound once per occurrence. + rows = await queries_slice.get_slice_rows_by_name_or_note(conn=asyncmy_conn, names=["b", "x"]) + assert rows == [ + models.TestSlice(id_=SLICE_ID_BASE, name="a", note="x"), + models.TestSlice(id_=SLICE_ID_BASE + 1, name="b", note="y"), + models.TestSlice(id_=SLICE_ID_BASE + 3, name="b", note="y"), + ] + assert await queries_slice.get_slice_rows_by_name_or_note(conn=asyncmy_conn, names=[]) == [] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_slice_rows_by_name_or_note_filtered", depends=["AsyncmyTestDataclassFunctions::insert_slice_rows"]) + async def test_get_slice_rows_by_name_or_note_filtered(self, asyncmy_conn: asyncmy.Connection) -> None: + # A plain parameter sits between the two uses of the slice, so this + # proves the flattened arguments follow SQL text order. + rows = await queries_slice.get_slice_rows_by_name_or_note_filtered(conn=asyncmy_conn, names=["b", "x"], id_=SLICE_ID_BASE + 1) + assert rows == [ + models.TestSlice(id_=SLICE_ID_BASE, name="a", note="x"), + models.TestSlice(id_=SLICE_ID_BASE + 3, name="b", note="y"), + ] + assert await queries_slice.get_slice_rows_by_name_or_note_filtered(conn=asyncmy_conn, names=[], id_=SLICE_ID_BASE) == [] + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestDataclassFunctions::get_first_slice_name_two_slices", depends=["AsyncmyTestDataclassFunctions::insert_slice_rows"]) + async def test_get_first_slice_name_two_slices(self, asyncmy_conn: asyncmy.Connection) -> None: + name = await queries_slice.get_first_slice_name(conn=asyncmy_conn, ids=[SLICE_ID_BASE + 1], names=["a"]) + assert name == "a" + assert await queries_slice.get_first_slice_name(conn=asyncmy_conn, ids=[], names=[]) is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + depends=[ + "AsyncmyTestDataclassFunctions::get_slice_rows", + "AsyncmyTestDataclassFunctions::get_slice_rows_empty_slice", + "AsyncmyTestDataclassFunctions::get_slice_row_filtered", + "AsyncmyTestDataclassFunctions::get_slice_row_filtered_not_found", + "AsyncmyTestDataclassFunctions::get_slice_rows_by_notes", + "AsyncmyTestDataclassFunctions::get_slice_rows_by_name_or_note", + "AsyncmyTestDataclassFunctions::get_slice_rows_by_name_or_note_filtered", + "AsyncmyTestDataclassFunctions::get_first_slice_name_two_slices", + ] + ) + async def test_delete_slice_rows(self, asyncmy_conn: asyncmy.Connection) -> None: + assert await queries_slice.delete_slice_rows(conn=asyncmy_conn, ids=[]) == 0 + deleted = await queries_slice.delete_slice_rows(conn=asyncmy_conn, ids=[SLICE_ID_BASE + offset for offset in range(SLICE_ROW_COUNT)]) + assert deleted == SLICE_ROW_COUNT + + @pytest.mark.asyncio(loop_scope="session") + async def test_one_missing_rows_return_none(self, asyncmy_conn: asyncmy.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. + assert await queries.get_one_mysql_type(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=-1) is None + assert await queries.get_one_date(conn=asyncmy_conn, id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert await queries.get_one_datetime(conn=asyncmy_conn, id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert await queries.get_one_time(conn=asyncmy_conn, id_=-1, time_test=datetime.timedelta()) is None + assert await queries.get_one_bool(conn=asyncmy_conn, id_=-1, tinyint1_test=False) is None + assert await queries.get_one_decimal(conn=asyncmy_conn, id_=-1, decimal_test=decimal.Decimal(0)) is None + assert await queries.get_one_blob(conn=asyncmy_conn, id_=-1, blob_test=memoryview(b"")) is None + assert await queries.get_one_bit(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_year(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_json(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_mood(conn=asyncmy_conn, id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert await queries.get_one_tag(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_type_override(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_reserved_arg(conn=asyncmy_conn, conn_2="missing") is None + assert await queries.touch_exec_last_id(conn=asyncmy_conn, name="untouched", id_=-1) is None + + # count(*) always returns a row; its miss branch needs the stub. + stub = typing.cast("asyncmy.Connection", no_row_conn.NoRowConn()) + assert await queries.count_mysql_types(conn=stub) is None diff --git a/test/driver_asyncmy/msgspec/__init__.py b/test/driver_asyncmy/msgspec/__init__.py new file mode 100644 index 00000000..0b34101d --- /dev/null +++ b/test/driver_asyncmy/msgspec/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for asyncmy tests.""" diff --git a/test/driver_asyncmy/msgspec/classes/__init__.py b/test/driver_asyncmy/msgspec/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_asyncmy/msgspec/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncmy/msgspec/classes/enums.py b/test/driver_asyncmy/msgspec/classes/enums.py new file mode 100644 index 00000000..873f5d33 --- /dev/null +++ b/test/driver_asyncmy/msgspec/classes/enums.py @@ -0,0 +1,56 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_asyncmy/msgspec/classes/models.py b/test/driver_asyncmy/msgspec/classes/models.py new file mode 100644 index 00000000..ecbd23fa --- /dev/null +++ b/test/driver_asyncmy/msgspec/classes/models.py @@ -0,0 +1,224 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlType", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +import msgspec +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_asyncmy.msgspec.classes import enums + import collections.abc + import datetime + import decimal + + +class TestInnerMysqlType(msgspec.Struct): + """Model representing TestInnerMysqlType. + + Attributes: + table_id -- int + int_test -- int | None + integer_test -- int | None + mediumint_test -- int | None + smallint_test -- int | None + tinyint_test -- int | None + bigint_test -- int | None + int_unsigned_test -- int | None + bigint_unsigned_test -- int | None + year_test -- int | None + tinyint1_test -- bool | None + bool_test -- bool | None + boolean_test -- bool | None + float_test -- float | None + double_test -- float | None + double_precision_test -- float | None + real_test -- float | None + decimal_test -- decimal.Decimal | None + numeric_test -- decimal.Decimal | None + char_test -- str | None + varchar_test -- str | None + tinytext_test -- str | None + text_test -- str | None + mediumtext_test -- str | None + longtext_test -- str | None + binary_test -- memoryview | None + varbinary_test -- memoryview | None + tinyblob_test -- memoryview | None + blob_test -- memoryview | None + mediumblob_test -- memoryview | None + longblob_test -- memoryview | None + bit_test -- memoryview | None + date_test -- datetime.date | None + datetime_test -- datetime.datetime | None + datetime6_test -- datetime.datetime | None + timestamp_test -- datetime.datetime | None + time_test -- datetime.timedelta | None + json_test -- str | None + mood -- enums.TestInnerMysqlTypesMood | None + tag -- enums.TestInnerMysqlTypesTag | None + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +class TestMysqlType(msgspec.Struct): + """Model representing TestMysqlType. + + Attributes: + id_ -- int + int_test -- int + integer_test -- int + mediumint_test -- int + smallint_test -- int + tinyint_test -- int + bigint_test -- int + int_unsigned_test -- int + bigint_unsigned_test -- int + year_test -- int + tinyint1_test -- bool + bool_test -- bool + boolean_test -- bool + float_test -- float + double_test -- float + double_precision_test -- float + real_test -- float + decimal_test -- decimal.Decimal + numeric_test -- decimal.Decimal + char_test -- str + varchar_test -- str + tinytext_test -- str + text_test -- str + mediumtext_test -- str + longtext_test -- str + binary_test -- memoryview + varbinary_test -- memoryview + tinyblob_test -- memoryview + blob_test -- memoryview + mediumblob_test -- memoryview + longblob_test -- memoryview + bit_test -- memoryview + date_test -- datetime.date + datetime_test -- datetime.datetime + datetime6_test -- datetime.datetime + timestamp_test -- datetime.datetime + time_test -- datetime.timedelta + json_test -- str + mood -- enums.TestMysqlTypesMood + tag -- enums.TestMysqlTypesTag + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +class TestReservedArg(msgspec.Struct): + """Model representing TestReservedArg. + + Attributes: + id_ -- int + conn -- str + """ + + id_: int + conn: str + + +class TestTypeOverride(msgspec.Struct): + """Model representing TestTypeOverride. + + Attributes: + id_ -- int + text_test -- UserString | None + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_asyncmy/msgspec/classes/queries.py b/test/driver_asyncmy/msgspec/classes/queries.py new file mode 100644 index 00000000..3b8754d1 --- /dev/null +++ b/test/driver_asyncmy/msgspec/classes/queries.py @@ -0,0 +1,1432 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Queries", + "QueryResults", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + import datetime + import decimal + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.msgspec.classes import enums +from test.driver_asyncmy.msgspec.classes import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Arguments: + conn -- The connection object of type `asyncmy.Connection` used to execute queries. + sql -- The SQL statement that will be executed when fetching/iterating. + decode_hook -- A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args -- Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns: + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopAsyncIteration -- When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +class Queries: + """Queries from file queries.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: asyncmy.Connection) -> None: + """Initialize the instance using the connection. + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute queries. + """ + self._conn = conn + + @property + def conn(self) -> asyncmy.Connection: + """Connection object used to make queries. + + Returns: + asyncmy.Connection -- Connection object used to make queries. + """ + return self._conn + + async def insert_one_mysql_type( + self, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, + ) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Arguments: + id_ -- int. + int_test -- int. + integer_test -- int. + mediumint_test -- int. + smallint_test -- int. + tinyint_test -- int. + bigint_test -- int. + int_unsigned_test -- int. + bigint_unsigned_test -- int. + year_test -- int. + tinyint1_test -- bool. + bool_test -- bool. + boolean_test -- bool. + float_test -- float. + double_test -- float. + double_precision_test -- float. + real_test -- float. + decimal_test -- decimal.Decimal. + numeric_test -- decimal.Decimal. + char_test -- str. + varchar_test -- str. + tinytext_test -- str. + text_test -- str. + mediumtext_test -- str. + longtext_test -- str. + binary_test -- memoryview. + varbinary_test -- memoryview. + tinyblob_test -- memoryview. + blob_test -- memoryview. + mediumblob_test -- memoryview. + longblob_test -- memoryview. + bit_test -- memoryview. + date_test -- datetime.date. + datetime_test -- datetime.datetime. + datetime6_test -- datetime.datetime. + timestamp_test -- datetime.datetime. + time_test -- datetime.timedelta. + json_test -- str. + mood -- enums.TestMysqlTypesMood. + tag -- enums.TestMysqlTypesTag. + """ + async with self._conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + async def insert_one_inner_mysql_type( + self, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, + ) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Arguments: + table_id -- int. + int_test -- int | None. + integer_test -- int | None. + mediumint_test -- int | None. + smallint_test -- int | None. + tinyint_test -- int | None. + bigint_test -- int | None. + int_unsigned_test -- int | None. + bigint_unsigned_test -- int | None. + year_test -- int | None. + tinyint1_test -- bool | None. + bool_test -- bool | None. + boolean_test -- bool | None. + float_test -- float | None. + double_test -- float | None. + double_precision_test -- float | None. + real_test -- float | None. + decimal_test -- decimal.Decimal | None. + numeric_test -- decimal.Decimal | None. + char_test -- str | None. + varchar_test -- str | None. + tinytext_test -- str | None. + text_test -- str | None. + mediumtext_test -- str | None. + longtext_test -- str | None. + binary_test -- memoryview | None. + varbinary_test -- memoryview | None. + tinyblob_test -- memoryview | None. + blob_test -- memoryview | None. + mediumblob_test -- memoryview | None. + longblob_test -- memoryview | None. + bit_test -- memoryview | None. + date_test -- datetime.date | None. + datetime_test -- datetime.datetime | None. + datetime6_test -- datetime.datetime | None. + timestamp_test -- datetime.datetime | None. + time_test -- datetime.timedelta | None. + json_test -- str | None. + mood -- enums.TestInnerMysqlTypesMood | None. + tag -- enums.TestInnerMysqlTypesTag | None. + """ + async with self._conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + async def get_one_mysql_type(self, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + models.TestMysqlType -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + async def get_one_inner_mysql_type(self, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Arguments: + table_id -- int. + + Returns: + models.TestInnerMysqlType -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + def get_many_mysql_type(self, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + QueryResults[models.TestMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(self._conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + def get_many_inner_mysql_type(self, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Arguments: + table_id -- int. + + Returns: + QueryResults[models.TestInnerMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + def get_many_nullable_inner_mysql_type(self, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Arguments: + table_id -- int. + int_test -- int | None. + + Returns: + QueryResults[models.TestInnerMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + async def get_one_date(self, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Arguments: + id_ -- int. + date_test -- datetime.date. + + Returns: + datetime.date -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DATE, (id_, date_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_datetime(self, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Arguments: + id_ -- int. + datetime_test -- datetime.datetime. + + Returns: + datetime.datetime -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_time(self, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Arguments: + id_ -- int. + time_test -- datetime.timedelta. + + Returns: + datetime.timedelta -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_TIME, (id_, time_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_bool(self, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Arguments: + id_ -- int. + tinyint1_test -- bool. + + Returns: + bool -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = await cur.fetchone() + if row is None: + return None + return bool(row[0]) + + async def get_one_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Arguments: + id_ -- int. + decimal_test -- decimal.Decimal. + + Returns: + decimal.Decimal -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_blob(self, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Arguments: + id_ -- int. + blob_test -- memoryview. + + Returns: + memoryview -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + async def get_one_bit(self, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + memoryview -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BIT, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + async def get_one_year(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_YEAR, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_json(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_JSON, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_mood(self, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Arguments: + id_ -- int. + mood -- enums.TestMysqlTypesMood. + + Returns: + enums.TestMysqlTypesMood -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_MOOD, (id_, mood)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + async def get_one_tag(self, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + enums.TestMysqlTypesTag -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_TAG, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + def get_many_date(self, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Arguments: + id_ -- int. + date_test -- datetime.date. + + Returns: + QueryResults[datetime.date] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + def get_many_time(self, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Arguments: + id_ -- int. + time_test -- datetime.timedelta. + + Returns: + QueryResults[datetime.timedelta] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + def get_many_bool(self, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Arguments: + id_ -- int. + tinyint1_test -- bool. + + Returns: + QueryResults[bool] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(self._conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + def get_many_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Arguments: + id_ -- int. + decimal_test -- decimal.Decimal. + + Returns: + QueryResults[decimal.Decimal] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + def get_many_mood(self, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Arguments: + mood -- enums.TestMysqlTypesMood. + + Returns: + QueryResults[enums.TestMysqlTypesMood] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(self._conn, GET_MANY_MOOD, _decode_hook, mood) + + def list_months(self) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Returns: + QueryResults[str] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, LIST_MONTHS, operator.itemgetter(0)) + + async def count_mysql_types(self) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(COUNT_MYSQL_TYPES) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def update_varchar_test(self, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Arguments: + varchar_test -- str. + id_ -- int. + + Returns: + int -- The number of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + async with self._conn.cursor() as cur: + return await cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + async def delete_one_mysql_type(self, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + """ + async with self._conn.cursor() as cur: + await cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + async def all_mysql_types_cursor(self) -> asyncmy.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Returns: + asyncmy.cursors.Cursor -- The result returned when executing the query. + """ + cur = self._conn.cursor() + await cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + async def insert_exec_last_id(self, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Arguments: + name -- str. + + Returns: + int -- The id of the last affected row. Will be `None` if no rows are affected. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + async def get_exec_last_id_name(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def insert_type_override(self, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Arguments: + id_ -- int. + text_test -- UserString | None. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + async def get_type_override(self, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + models.TestTypeOverride -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + async def get_reserved_arg(self, *, conn: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Arguments: + conn -- str. + + Returns: + models.TestReservedArg -- Result fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_RESERVED_ARG, (conn,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + async def insert_reserved_arg(self, *, id_: int, conn: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Arguments: + id_ -- int. + conn -- str. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_RESERVED_ARG, (id_, conn)) + + async def touch_exec_last_id(self, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Arguments: + name -- str. + id_ -- int. + + Returns: + int -- The id of the last affected row. Will be `None` if no rows are affected. + """ + async with self._conn.cursor() as cur: + await cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_asyncmy/msgspec/functions/__init__.py b/test/driver_asyncmy/msgspec/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_asyncmy/msgspec/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncmy/msgspec/functions/enums.py b/test/driver_asyncmy/msgspec/functions/enums.py new file mode 100644 index 00000000..873f5d33 --- /dev/null +++ b/test/driver_asyncmy/msgspec/functions/enums.py @@ -0,0 +1,56 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_asyncmy/msgspec/functions/models.py b/test/driver_asyncmy/msgspec/functions/models.py new file mode 100644 index 00000000..be5358d1 --- /dev/null +++ b/test/driver_asyncmy/msgspec/functions/models.py @@ -0,0 +1,224 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlType", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +import msgspec +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_asyncmy.msgspec.functions import enums + import collections.abc + import datetime + import decimal + + +class TestInnerMysqlType(msgspec.Struct): + """Model representing TestInnerMysqlType. + + Attributes: + table_id -- int + int_test -- int | None + integer_test -- int | None + mediumint_test -- int | None + smallint_test -- int | None + tinyint_test -- int | None + bigint_test -- int | None + int_unsigned_test -- int | None + bigint_unsigned_test -- int | None + year_test -- int | None + tinyint1_test -- bool | None + bool_test -- bool | None + boolean_test -- bool | None + float_test -- float | None + double_test -- float | None + double_precision_test -- float | None + real_test -- float | None + decimal_test -- decimal.Decimal | None + numeric_test -- decimal.Decimal | None + char_test -- str | None + varchar_test -- str | None + tinytext_test -- str | None + text_test -- str | None + mediumtext_test -- str | None + longtext_test -- str | None + binary_test -- memoryview | None + varbinary_test -- memoryview | None + tinyblob_test -- memoryview | None + blob_test -- memoryview | None + mediumblob_test -- memoryview | None + longblob_test -- memoryview | None + bit_test -- memoryview | None + date_test -- datetime.date | None + datetime_test -- datetime.datetime | None + datetime6_test -- datetime.datetime | None + timestamp_test -- datetime.datetime | None + time_test -- datetime.timedelta | None + json_test -- str | None + mood -- enums.TestInnerMysqlTypesMood | None + tag -- enums.TestInnerMysqlTypesTag | None + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +class TestMysqlType(msgspec.Struct): + """Model representing TestMysqlType. + + Attributes: + id_ -- int + int_test -- int + integer_test -- int + mediumint_test -- int + smallint_test -- int + tinyint_test -- int + bigint_test -- int + int_unsigned_test -- int + bigint_unsigned_test -- int + year_test -- int + tinyint1_test -- bool + bool_test -- bool + boolean_test -- bool + float_test -- float + double_test -- float + double_precision_test -- float + real_test -- float + decimal_test -- decimal.Decimal + numeric_test -- decimal.Decimal + char_test -- str + varchar_test -- str + tinytext_test -- str + text_test -- str + mediumtext_test -- str + longtext_test -- str + binary_test -- memoryview + varbinary_test -- memoryview + tinyblob_test -- memoryview + blob_test -- memoryview + mediumblob_test -- memoryview + longblob_test -- memoryview + bit_test -- memoryview + date_test -- datetime.date + datetime_test -- datetime.datetime + datetime6_test -- datetime.datetime + timestamp_test -- datetime.datetime + time_test -- datetime.timedelta + json_test -- str + mood -- enums.TestMysqlTypesMood + tag -- enums.TestMysqlTypesTag + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +class TestReservedArg(msgspec.Struct): + """Model representing TestReservedArg. + + Attributes: + id_ -- int + conn -- str + """ + + id_: int + conn: str + + +class TestTypeOverride(msgspec.Struct): + """Model representing TestTypeOverride. + + Attributes: + id_ -- int + text_test -- UserString | None + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_asyncmy/msgspec/functions/queries.py b/test/driver_asyncmy/msgspec/functions/queries.py new file mode 100644 index 00000000..675aba57 --- /dev/null +++ b/test/driver_asyncmy/msgspec/functions/queries.py @@ -0,0 +1,1519 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "all_mysql_types_cursor", + "count_mysql_types", + "delete_one_mysql_type", + "get_exec_last_id_name", + "get_many_bool", + "get_many_date", + "get_many_decimal", + "get_many_inner_mysql_type", + "get_many_mood", + "get_many_mysql_type", + "get_many_nullable_inner_mysql_type", + "get_many_time", + "get_one_bit", + "get_one_blob", + "get_one_bool", + "get_one_date", + "get_one_datetime", + "get_one_decimal", + "get_one_inner_mysql_type", + "get_one_json", + "get_one_mood", + "get_one_mysql_type", + "get_one_tag", + "get_one_time", + "get_one_year", + "get_reserved_arg", + "get_type_override", + "insert_exec_last_id", + "insert_one_inner_mysql_type", + "insert_one_mysql_type", + "insert_reserved_arg", + "insert_type_override", + "list_months", + "touch_exec_last_id", + "update_varchar_test", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + import datetime + import decimal + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.msgspec.functions import enums +from test.driver_asyncmy.msgspec.functions import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Arguments: + conn -- The connection object of type `asyncmy.Connection` used to execute queries. + sql -- The SQL statement that will be executed when fetching/iterating. + decode_hook -- A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args -- Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns: + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopAsyncIteration -- When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +async def insert_one_mysql_type( + conn: asyncmy.Connection, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, +) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + int_test -- int. + integer_test -- int. + mediumint_test -- int. + smallint_test -- int. + tinyint_test -- int. + bigint_test -- int. + int_unsigned_test -- int. + bigint_unsigned_test -- int. + year_test -- int. + tinyint1_test -- bool. + bool_test -- bool. + boolean_test -- bool. + float_test -- float. + double_test -- float. + double_precision_test -- float. + real_test -- float. + decimal_test -- decimal.Decimal. + numeric_test -- decimal.Decimal. + char_test -- str. + varchar_test -- str. + tinytext_test -- str. + text_test -- str. + mediumtext_test -- str. + longtext_test -- str. + binary_test -- memoryview. + varbinary_test -- memoryview. + tinyblob_test -- memoryview. + blob_test -- memoryview. + mediumblob_test -- memoryview. + longblob_test -- memoryview. + bit_test -- memoryview. + date_test -- datetime.date. + datetime_test -- datetime.datetime. + datetime6_test -- datetime.datetime. + timestamp_test -- datetime.datetime. + time_test -- datetime.timedelta. + json_test -- str. + mood -- enums.TestMysqlTypesMood. + tag -- enums.TestMysqlTypesTag. + """ + async with conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + +async def insert_one_inner_mysql_type( + conn: asyncmy.Connection, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, +) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + table_id -- int. + int_test -- int | None. + integer_test -- int | None. + mediumint_test -- int | None. + smallint_test -- int | None. + tinyint_test -- int | None. + bigint_test -- int | None. + int_unsigned_test -- int | None. + bigint_unsigned_test -- int | None. + year_test -- int | None. + tinyint1_test -- bool | None. + bool_test -- bool | None. + boolean_test -- bool | None. + float_test -- float | None. + double_test -- float | None. + double_precision_test -- float | None. + real_test -- float | None. + decimal_test -- decimal.Decimal | None. + numeric_test -- decimal.Decimal | None. + char_test -- str | None. + varchar_test -- str | None. + tinytext_test -- str | None. + text_test -- str | None. + mediumtext_test -- str | None. + longtext_test -- str | None. + binary_test -- memoryview | None. + varbinary_test -- memoryview | None. + tinyblob_test -- memoryview | None. + blob_test -- memoryview | None. + mediumblob_test -- memoryview | None. + longblob_test -- memoryview | None. + bit_test -- memoryview | None. + date_test -- datetime.date | None. + datetime_test -- datetime.datetime | None. + datetime6_test -- datetime.datetime | None. + timestamp_test -- datetime.datetime | None. + time_test -- datetime.timedelta | None. + json_test -- str | None. + mood -- enums.TestInnerMysqlTypesMood | None. + tag -- enums.TestInnerMysqlTypesTag | None. + """ + async with conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + +async def get_one_mysql_type(conn: asyncmy.Connection, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + + Returns: + models.TestMysqlType -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + +async def get_one_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + table_id -- int. + + Returns: + models.TestInnerMysqlType -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + +def get_many_mysql_type(conn: asyncmy.Connection, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + + Returns: + QueryResults[models.TestMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + +def get_many_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + table_id -- int. + + Returns: + QueryResults[models.TestInnerMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + +def get_many_nullable_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + table_id -- int. + int_test -- int | None. + + Returns: + QueryResults[models.TestInnerMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + +async def get_one_date(conn: asyncmy.Connection, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + date_test -- datetime.date. + + Returns: + datetime.date -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DATE, (id_, date_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_datetime(conn: asyncmy.Connection, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + datetime_test -- datetime.datetime. + + Returns: + datetime.datetime -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_time(conn: asyncmy.Connection, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + time_test -- datetime.timedelta. + + Returns: + datetime.timedelta -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_TIME, (id_, time_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_bool(conn: asyncmy.Connection, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + tinyint1_test -- bool. + + Returns: + bool -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = await cur.fetchone() + if row is None: + return None + return bool(row[0]) + + +async def get_one_decimal(conn: asyncmy.Connection, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + decimal_test -- decimal.Decimal. + + Returns: + decimal.Decimal -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_blob(conn: asyncmy.Connection, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + blob_test -- memoryview. + + Returns: + memoryview -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +async def get_one_bit(conn: asyncmy.Connection, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + + Returns: + memoryview -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BIT, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +async def get_one_year(conn: asyncmy.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_YEAR, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_json(conn: asyncmy.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_JSON, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_mood(conn: asyncmy.Connection, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + mood -- enums.TestMysqlTypesMood. + + Returns: + enums.TestMysqlTypesMood -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_MOOD, (id_, mood)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + +async def get_one_tag(conn: asyncmy.Connection, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + + Returns: + enums.TestMysqlTypesTag -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_TAG, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + +def get_many_date(conn: asyncmy.Connection, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + date_test -- datetime.date. + + Returns: + QueryResults[datetime.date] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + +def get_many_time(conn: asyncmy.Connection, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + time_test -- datetime.timedelta. + + Returns: + QueryResults[datetime.timedelta] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + +def get_many_bool(conn: asyncmy.Connection, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + tinyint1_test -- bool. + + Returns: + QueryResults[bool] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + +def get_many_decimal(conn: asyncmy.Connection, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + decimal_test -- decimal.Decimal. + + Returns: + QueryResults[decimal.Decimal] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + +def get_many_mood(conn: asyncmy.Connection, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + mood -- enums.TestMysqlTypesMood. + + Returns: + QueryResults[enums.TestMysqlTypesMood] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(conn, GET_MANY_MOOD, _decode_hook, mood) + + +def list_months(conn: asyncmy.Connection) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + QueryResults[str] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, LIST_MONTHS, operator.itemgetter(0)) + + +async def count_mysql_types(conn: asyncmy.Connection) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(COUNT_MYSQL_TYPES) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def update_varchar_test(conn: asyncmy.Connection, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + varchar_test -- str. + id_ -- int. + + Returns: + int -- The number of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + async with conn.cursor() as cur: + return await cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + +async def delete_one_mysql_type(conn: asyncmy.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + """ + async with conn.cursor() as cur: + await cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + +async def all_mysql_types_cursor(conn: asyncmy.Connection) -> asyncmy.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + asyncmy.cursors.Cursor -- The result returned when executing the query. + """ + cur = conn.cursor() + await cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + +async def insert_exec_last_id(conn: asyncmy.Connection, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + name -- str. + + Returns: + int -- The id of the last affected row. Will be `None` if no rows are affected. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + +async def get_exec_last_id_name(conn: asyncmy.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def insert_type_override(conn: asyncmy.Connection, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + text_test -- UserString | None. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + +async def get_type_override(conn: asyncmy.Connection, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + + Returns: + models.TestTypeOverride -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + +async def get_reserved_arg(conn: asyncmy.Connection, *, conn_2: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + conn_2 -- str. + + Returns: + models.TestReservedArg -- Result fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_RESERVED_ARG, (conn_2,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + +async def insert_reserved_arg(conn: asyncmy.Connection, *, id_: int, conn_2: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + id_ -- int. + conn_2 -- str. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_RESERVED_ARG, (id_, conn_2)) + + +async def touch_exec_last_id(conn: asyncmy.Connection, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `asyncmy.Connection` used to execute the query. + name -- str. + id_ -- int. + + Returns: + int -- The id of the last affected row. Will be `None` if no rows are affected. + """ + async with conn.cursor() as cur: + await cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_asyncmy/msgspec/ruff.toml b/test/driver_asyncmy/msgspec/ruff.toml new file mode 100644 index 00000000..876ccf0c --- /dev/null +++ b/test/driver_asyncmy/msgspec/ruff.toml @@ -0,0 +1,5 @@ +extend="../../../ruff.toml" + + +[lint.pydocstyle] +convention = "pep257" \ No newline at end of file diff --git a/test/driver_asyncmy/msgspec/test_asyncmy_msgspec_classes.py b/test/driver_asyncmy/msgspec/test_asyncmy_msgspec_classes.py new file mode 100644 index 00000000..f474a3b8 --- /dev/null +++ b/test/driver_asyncmy/msgspec/test_asyncmy_msgspec_classes.py @@ -0,0 +1,883 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import collections.abc +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import asyncmy +import asyncmy.cursors +import msgspec.structs +import pytest +import pytest_asyncio + +from test.driver_asyncmy import no_row_conn +from test.driver_asyncmy.msgspec.classes import enums +from test.driver_asyncmy.msgspec.classes import models +from test.driver_asyncmy.msgspec.classes import queries + +MODEL_ID = 8101 +OVERRIDE_ID = 8201 +OVERRIDE_NONE_ID = 8202 +RESERVED_ID = 8301 +MISSING_ID = 8901 +SUITE_TAG = "asyncmy-msgspec-classes" +EXPECTED_MONTH = "2026-01" +DECIMAL_PADDED = "12.3400" +BINARY_UNPADDED = b"\xaa\xbb\xcc" +BINARY_LENGTH = 16 +BIT_BYTE = b"\x80" + + +@pytest.mark.asyncio(loop_scope="session") +class TestAsyncmyMsgspecClasses: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MODEL_ID, + int_test=42, + integer_test=-1_000_000, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_000_000_000, + bigint_unsigned_test=2**63 + 10, + year_test=2026, + tinyint1_test=True, + bool_test=False, + boolean_test=True, + float_test=3.5, + double_test=math.pi, + double_precision_test=1.41421, + real_test=math.e, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("100.10"), + char_test="CHAR10", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(BINARY_UNPADDED + b"\x00" * (BINARY_LENGTH - len(BINARY_UNPADDED))), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tinyblob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"mediumblob"), + longblob_test=memoryview(b"longblob"), + bit_test=memoryview(BIT_BYTE), + date_test=datetime.date(2026, 1, 2), + datetime_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + datetime6_test=datetime.datetime(2026, 1, 2, 3, 4, 5, 123456), + timestamp_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + time_test=datetime.timedelta(hours=13, minutes=45, seconds=30), + json_test=json.dumps({"foo": "bar", "count": 3}, separators=(",", ":")), + mood=enums.TestMysqlTypesMood.VALUE__HIDDEN, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=None, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=None, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=None, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=None, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=None, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE_24H, + tag=None, + ) + + @pytest_asyncio.fixture(scope="class", loop_scope="session") + async def queries_obj(self, asyncmy_conn: asyncmy.Connection) -> queries.Queries: + return queries.Queries(conn=asyncmy_conn) + + @pytest.mark.asyncio(loop_scope="session") + async def test_conn_attr(self, queries_obj: queries.Queries) -> None: + assert isinstance(queries_obj.conn, asyncmy.Connection) + + @pytest.mark.asyncio(loop_scope="session") + async def test_enum_members(self) -> None: + assert enums.TestMysqlTypesMood.VALUE_24H.value == "24h" + assert enums.TestMysqlTypesMood.VALUE__HIDDEN.value == "_hidden" + assert enums.TestInnerMysqlTypesMood.VALUE_24H.value == "24h" + assert enums.TestMysqlTypesTag.BETA.value == "beta" + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::insert") + async def test_insert( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + await queries_obj.insert_one_mysql_type( + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + # char(10) strips trailing spaces on return; the model holds the stripped value. + char_test=model.char_test + " ", + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + # binary(16) is right-padded with NUL bytes on return; the model holds the padded value. + binary_test=memoryview(BINARY_UNPADDED), + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::inner_insert", depends=["AsyncmyTestMsgspecClasses::insert"]) + async def test_inner_insert( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + await queries_obj.insert_one_inner_mysql_type( + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_one", depends=["AsyncmyTestMsgspecClasses::inner_insert"]) + async def test_get_one( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mysql_type(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert result.tinyint1_test is True + assert result.bool_test is False + assert result.boolean_test is True + assert len(result.binary_test) == BINARY_LENGTH + assert result.char_test == model.char_test + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + assert isinstance(result.time_test, datetime.timedelta) + # MySQL normalizes JSON spacing; compare parsed values, never strings. + assert json.loads(result.json_test) == json.loads(model.json_test) + assert result == msgspec.structs.replace(model, json_test=result.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_one_none", depends=["AsyncmyTestMsgspecClasses::get_one"]) + async def test_get_one_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_mysql_type(id_=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_one_inner", depends=["AsyncmyTestMsgspecClasses::get_one_none"]) + async def test_get_one_inner( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = await queries_obj.get_one_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result.tinyint1_test is None + assert result.bool_test is False + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_one_inner_none", depends=["AsyncmyTestMsgspecClasses::get_one_inner"]) + async def test_get_one_inner_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_inner_mysql_type(table_id=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_date", depends=["AsyncmyTestMsgspecClasses::get_one_inner_none"]) + async def test_get_date( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_date(id_=MODEL_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_date_none", depends=["AsyncmyTestMsgspecClasses::get_date"]) + async def test_get_date_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_date(id_=MISSING_ID, date_test=model.date_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_datetime", depends=["AsyncmyTestMsgspecClasses::get_date_none"]) + async def test_get_datetime( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_datetime(id_=MODEL_ID, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_datetime_none", depends=["AsyncmyTestMsgspecClasses::get_datetime"]) + async def test_get_datetime_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_datetime(id_=MISSING_ID, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_time", depends=["AsyncmyTestMsgspecClasses::get_datetime_none"]) + async def test_get_time( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_time(id_=MODEL_ID, time_test=model.time_test) + + assert result is not None + # MySQL time columns map to datetime.timedelta, not datetime.time. + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_time_none", depends=["AsyncmyTestMsgspecClasses::get_time"]) + async def test_get_time_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_time(id_=MISSING_ID, time_test=model.time_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_bool", depends=["AsyncmyTestMsgspecClasses::get_time_none"]) + async def test_get_bool( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_bool(id_=MODEL_ID, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_bool_none", depends=["AsyncmyTestMsgspecClasses::get_bool"]) + async def test_get_bool_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_bool(id_=MISSING_ID, tinyint1_test=model.tinyint1_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_decimal", depends=["AsyncmyTestMsgspecClasses::get_bool_none"]) + async def test_get_decimal( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_decimal(id_=MODEL_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + # decimal(12,4) comes back padded to scale 4. + assert str(result) == DECIMAL_PADDED + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_decimal_none", depends=["AsyncmyTestMsgspecClasses::get_decimal"]) + async def test_get_decimal_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_decimal(id_=MISSING_ID, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_blob", depends=["AsyncmyTestMsgspecClasses::get_decimal_none"]) + async def test_get_blob( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_blob(id_=MODEL_ID, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_blob_none", depends=["AsyncmyTestMsgspecClasses::get_blob"]) + async def test_get_blob_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_blob(id_=MISSING_ID, blob_test=model.blob_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_bit", depends=["AsyncmyTestMsgspecClasses::get_blob_none"]) + async def test_get_bit( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_bit(id_=MODEL_ID) + + assert result is not None + # bit(8) comes back as a one-byte memoryview. + assert isinstance(result, memoryview) + assert len(result) == 1 + assert bytes(result) == BIT_BYTE + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_year", depends=["AsyncmyTestMsgspecClasses::get_bit"]) + async def test_get_year( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_year(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_json", depends=["AsyncmyTestMsgspecClasses::get_year"]) + async def test_get_json( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_json(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, str) + # MySQL normalizes JSON spacing; compare parsed values, never strings. + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_mood", depends=["AsyncmyTestMsgspecClasses::get_json"]) + async def test_get_mood( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mood(id_=MODEL_ID, mood=model.mood) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE__HIDDEN + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_tag", depends=["AsyncmyTestMsgspecClasses::get_mood"]) + async def test_get_tag( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_tag(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is model.tag + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many", depends=["AsyncmyTestMsgspecClasses::get_tag"]) + async def test_get_many(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_mysql_type(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert len(result) == 1 + assert isinstance(result[0], models.TestMysqlType) + + assert result[0] == msgspec.structs.replace(model, json_test=result[0].json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_iter", depends=["AsyncmyTestMsgspecClasses::get_many"]) + async def test_get_many_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_mysql_type(id_=MODEL_ID): + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert result == msgspec.structs.replace(model, json_test=result.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_inner", depends=["AsyncmyTestMsgspecClasses::get_many_iter"]) + async def test_get_many_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = await queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], models.TestInnerMysqlType) + + assert result[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_inner_iter", depends=["AsyncmyTestMsgspecClasses::get_many_inner"]) + async def test_get_many_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecClasses::get_many_nullable_inner", + depends=["AsyncmyTestMsgspecClasses::get_many_inner_iter"], + ) + async def test_get_many_nullable_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the query matches it via the NULL-safe <=> operator. + result = await queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], models.TestInnerMysqlType) + + assert result[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecClasses::get_many_nullable_inner_iter", + depends=["AsyncmyTestMsgspecClasses::get_many_nullable_inner"], + ) + async def test_get_many_nullable_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecClasses::get_many_date", + depends=["AsyncmyTestMsgspecClasses::get_many_nullable_inner_iter"], + ) + async def test_get_many_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_date(id_=MODEL_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], datetime.date) + + assert result[0] == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_date_iter", depends=["AsyncmyTestMsgspecClasses::get_many_date"]) + async def test_get_many_date_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_date(id_=MODEL_ID, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_time", depends=["AsyncmyTestMsgspecClasses::get_many_date_iter"]) + async def test_get_many_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_time(id_=MODEL_ID, time_test=model.time_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], datetime.timedelta) + + assert result[0] == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_time_iter", depends=["AsyncmyTestMsgspecClasses::get_many_time"]) + async def test_get_many_time_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_time(id_=MODEL_ID, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_bool", depends=["AsyncmyTestMsgspecClasses::get_many_time_iter"]) + async def test_get_many_bool(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_bool(id_=MODEL_ID, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], bool) + + assert result[0] is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_bool_iter", depends=["AsyncmyTestMsgspecClasses::get_many_bool"]) + async def test_get_many_bool_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_bool(id_=MODEL_ID, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_decimal", depends=["AsyncmyTestMsgspecClasses::get_many_bool_iter"]) + async def test_get_many_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_decimal(id_=MODEL_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], decimal.Decimal) + + assert str(result[0]) == DECIMAL_PADDED + assert result[0] == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecClasses::get_many_decimal_iter", + depends=["AsyncmyTestMsgspecClasses::get_many_decimal"], + ) + async def test_get_many_decimal_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_decimal(id_=MODEL_ID, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_mood", depends=["AsyncmyTestMsgspecClasses::get_many_decimal_iter"]) + async def test_get_many_mood(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_mood(mood=model.mood) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + # The query is not id-filtered; other rows may exist, so assert containment. + assert result + for mood in result: + assert isinstance(mood, enums.TestMysqlTypesMood) + assert mood is model.mood + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_many_mood_iter", depends=["AsyncmyTestMsgspecClasses::get_many_mood"]) + async def test_get_many_mood_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + results = [mood async for mood in queries_obj.get_many_mood(mood=model.mood)] + + assert results + for mood in results: + assert isinstance(mood, enums.TestMysqlTypesMood) + assert mood is model.mood + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::list_months", depends=["AsyncmyTestMsgspecClasses::get_many_mood_iter"]) + async def test_list_months(self, queries_obj: queries.Queries) -> None: + # DATE_FORMAT contains literal % characters; regression for the percent-doubling bug. + result = await queries_obj.list_months() + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + for month in result: + assert isinstance(month, str) + assert EXPECTED_MONTH in result + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::list_months_iter", depends=["AsyncmyTestMsgspecClasses::list_months"]) + async def test_list_months_iter(self, queries_obj: queries.Queries) -> None: + results = [month async for month in queries_obj.list_months()] + + assert EXPECTED_MONTH in results + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::count", depends=["AsyncmyTestMsgspecClasses::list_months_iter"]) + async def test_count(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.count_mysql_types() + + assert result is not None + assert isinstance(result, int) + assert result >= 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::update_rows", depends=["AsyncmyTestMsgspecClasses::count"]) + async def test_update_rows(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.update_varchar_test(varchar_test="updated varchar", id_=MODEL_ID) + + assert isinstance(result, int) + assert result == 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::update_rows_none", depends=["AsyncmyTestMsgspecClasses::update_rows"]) + async def test_update_rows_none(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.update_varchar_test(varchar_test="updated varchar", id_=MISSING_ID) + + assert isinstance(result, int) + assert result == 0 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::execresult_cursor", depends=["AsyncmyTestMsgspecClasses::update_rows_none"]) + async def test_execresult_cursor(self, queries_obj: queries.Queries) -> None: + cursor = await queries_obj.all_mysql_types_cursor() + + assert isinstance(cursor, asyncmy.cursors.Cursor) + rows = await cursor.fetchall() + assert any(row[0] == MODEL_ID for row in rows) + await cursor.close() + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::exec_last_id", depends=["AsyncmyTestMsgspecClasses::execresult_cursor"]) + async def test_exec_last_id(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.insert_exec_last_id(name=SUITE_TAG) + + assert result is not None + assert isinstance(result, int) + # AUTO_INCREMENT counters persist across runs; never assert exact ids. + assert result > 0 + + name = await queries_obj.get_exec_last_id_name(id_=result) + assert name == SUITE_TAG + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::insert_reserved_arg", depends=["AsyncmyTestMsgspecClasses::exec_last_id"]) + async def test_insert_reserved_arg(self, queries_obj: queries.Queries) -> None: + await queries_obj.insert_reserved_arg(id_=RESERVED_ID, conn=SUITE_TAG) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::get_reserved_arg", depends=["AsyncmyTestMsgspecClasses::insert_reserved_arg"]) + async def test_get_reserved_arg(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.get_reserved_arg(conn=SUITE_TAG) + + assert result is not None + assert result == models.TestReservedArg(id_=RESERVED_ID, conn=SUITE_TAG) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::delete", depends=["AsyncmyTestMsgspecClasses::get_reserved_arg"]) + async def test_delete(self, queries_obj: queries.Queries) -> None: + await queries_obj.delete_one_mysql_type(id_=MODEL_ID) + + result = await queries_obj.get_one_mysql_type(id_=MODEL_ID) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecClasses::insert_type_override") + async def test_insert_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + await queries_obj.insert_type_override(id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecClasses::get_type_override", + depends=["AsyncmyTestMsgspecClasses::insert_type_override"], + ) + async def test_get_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = await queries_obj.get_type_override(id_=override_model.id_) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecClasses::get_type_override_none_value", + depends=["AsyncmyTestMsgspecClasses::get_type_override"], + ) + async def test_get_type_override_none_value(self, queries_obj: queries.Queries) -> None: + # The UserString override column is nullable; None must round-trip too. + await queries_obj.insert_type_override(id_=OVERRIDE_NONE_ID, text_test=None) + + result = await queries_obj.get_type_override(id_=OVERRIDE_NONE_ID) + assert result is not None + assert result.text_test is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecClasses::get_type_override_missing", + depends=["AsyncmyTestMsgspecClasses::get_type_override_none_value"], + ) + async def test_get_type_override_missing(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.get_type_override(id_=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecClasses::cleanup", + depends=["AsyncmyTestMsgspecClasses::delete", "AsyncmyTestMsgspecClasses::get_type_override_missing"], + ) + async def test_cleanup(self, asyncmy_conn: asyncmy.Connection) -> None: + # Tables without generated delete queries are cleaned directly so the + # fixed ids are free for the next chain. + async with asyncmy_conn.cursor() as cur: + await cur.execute("DELETE FROM test_inner_mysql_types WHERE table_id = %s", (MODEL_ID,)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_type_override WHERE id IN (%s, %s)", (OVERRIDE_ID, OVERRIDE_NONE_ID)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_reserved_args WHERE id = %s", (RESERVED_ID,)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_execlastid WHERE name = %s", (SUITE_TAG,)) # pyright: ignore[reportUnknownMemberType] + + @pytest.mark.asyncio(loop_scope="session") + async def test_one_missing_rows_return_none(self, asyncmy_conn: asyncmy.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. + obj = queries.Queries(conn=asyncmy_conn) + assert await obj.get_one_mysql_type(id_=-1) is None + assert await obj.get_one_inner_mysql_type(table_id=-1) is None + assert await obj.get_one_date(id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert await obj.get_one_datetime(id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert await obj.get_one_time(id_=-1, time_test=datetime.timedelta()) is None + assert await obj.get_one_bool(id_=-1, tinyint1_test=False) is None + assert await obj.get_one_decimal(id_=-1, decimal_test=decimal.Decimal(0)) is None + assert await obj.get_one_blob(id_=-1, blob_test=memoryview(b"")) is None + assert await obj.get_one_bit(id_=-1) is None + assert await obj.get_one_year(id_=-1) is None + assert await obj.get_one_json(id_=-1) is None + assert await obj.get_one_mood(id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert await obj.get_one_tag(id_=-1) is None + assert await obj.get_exec_last_id_name(id_=-1) is None + assert await obj.get_type_override(id_=-1) is None + assert await obj.get_reserved_arg(conn="missing") is None + assert await obj.touch_exec_last_id(name="untouched", id_=-1) is None + + # count(*) always returns a row; its miss branch needs the stub. + stub = typing.cast("asyncmy.Connection", no_row_conn.NoRowConn()) + assert await queries.Queries(conn=stub).count_mysql_types() is None diff --git a/test/driver_asyncmy/msgspec/test_asyncmy_msgspec_functions.py b/test/driver_asyncmy/msgspec/test_asyncmy_msgspec_functions.py new file mode 100644 index 00000000..41e5c12c --- /dev/null +++ b/test/driver_asyncmy/msgspec/test_asyncmy_msgspec_functions.py @@ -0,0 +1,875 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import collections.abc +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import asyncmy +import asyncmy.cursors +import msgspec.structs +import pytest + +from test.driver_asyncmy import no_row_conn +from test.driver_asyncmy.msgspec.functions import enums +from test.driver_asyncmy.msgspec.functions import models +from test.driver_asyncmy.msgspec.functions import queries + +MODEL_ID = 8151 +OVERRIDE_ID = 8251 +OVERRIDE_NONE_ID = 8252 +RESERVED_ID = 8351 +MISSING_ID = 8902 +SUITE_TAG = "asyncmy-msgspec-functions" +EXPECTED_MONTH = "2026-01" +DECIMAL_PADDED = "12.3400" +BINARY_UNPADDED = b"\xaa\xbb\xcc" +BINARY_LENGTH = 16 +BIT_BYTE = b"\x80" + + +@pytest.mark.asyncio(loop_scope="session") +class TestAsyncmyMsgspecFunctions: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MODEL_ID, + int_test=42, + integer_test=-1_000_000, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_000_000_000, + bigint_unsigned_test=2**63 + 10, + year_test=2026, + tinyint1_test=True, + bool_test=False, + boolean_test=True, + float_test=3.5, + double_test=math.pi, + double_precision_test=1.41421, + real_test=math.e, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("100.10"), + char_test="CHAR10", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(BINARY_UNPADDED + b"\x00" * (BINARY_LENGTH - len(BINARY_UNPADDED))), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tinyblob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"mediumblob"), + longblob_test=memoryview(b"longblob"), + bit_test=memoryview(BIT_BYTE), + date_test=datetime.date(2026, 1, 2), + datetime_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + datetime6_test=datetime.datetime(2026, 1, 2, 3, 4, 5, 123456), + timestamp_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + time_test=datetime.timedelta(hours=13, minutes=45, seconds=30), + json_test=json.dumps({"foo": "bar", "count": 3}, separators=(",", ":")), + mood=enums.TestMysqlTypesMood.VALUE__HIDDEN, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=None, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=None, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=None, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=None, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=None, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE_24H, + tag=None, + ) + + @pytest.mark.asyncio(loop_scope="session") + async def test_enum_members(self) -> None: + assert enums.TestMysqlTypesMood.VALUE_24H.value == "24h" + assert enums.TestMysqlTypesMood.VALUE__HIDDEN.value == "_hidden" + assert enums.TestInnerMysqlTypesMood.VALUE_24H.value == "24h" + assert enums.TestMysqlTypesTag.BETA.value == "beta" + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::insert") + async def test_insert( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + await queries.insert_one_mysql_type( + conn=asyncmy_conn, + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + # char(10) strips trailing spaces on return; the model holds the stripped value. + char_test=model.char_test + " ", + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + # binary(16) is right-padded with NUL bytes on return; the model holds the padded value. + binary_test=memoryview(BINARY_UNPADDED), + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::inner_insert", depends=["AsyncmyTestMsgspecFunctions::insert"]) + async def test_inner_insert( + self, + asyncmy_conn: asyncmy.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + await queries.insert_one_inner_mysql_type( + conn=asyncmy_conn, + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_one", depends=["AsyncmyTestMsgspecFunctions::inner_insert"]) + async def test_get_one( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert result.tinyint1_test is True + assert result.bool_test is False + assert result.boolean_test is True + assert len(result.binary_test) == BINARY_LENGTH + assert result.char_test == model.char_test + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + assert isinstance(result.time_test, datetime.timedelta) + # MySQL normalizes JSON spacing; compare parsed values, never strings. + assert json.loads(result.json_test) == json.loads(model.json_test) + assert result == msgspec.structs.replace(model, json_test=result.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_one_none", depends=["AsyncmyTestMsgspecFunctions::get_one"]) + async def test_get_one_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_one_inner", depends=["AsyncmyTestMsgspecFunctions::get_one_none"]) + async def test_get_one_inner( + self, + asyncmy_conn: asyncmy.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result.tinyint1_test is None + assert result.bool_test is False + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_one_inner_none", depends=["AsyncmyTestMsgspecFunctions::get_one_inner"]) + async def test_get_one_inner_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_date", depends=["AsyncmyTestMsgspecFunctions::get_one_inner_none"]) + async def test_get_date( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_date(conn=asyncmy_conn, id_=MODEL_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_date_none", depends=["AsyncmyTestMsgspecFunctions::get_date"]) + async def test_get_date_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_date(conn=asyncmy_conn, id_=MISSING_ID, date_test=model.date_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_datetime", depends=["AsyncmyTestMsgspecFunctions::get_date_none"]) + async def test_get_datetime( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_datetime(conn=asyncmy_conn, id_=MODEL_ID, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_datetime_none", depends=["AsyncmyTestMsgspecFunctions::get_datetime"]) + async def test_get_datetime_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_datetime(conn=asyncmy_conn, id_=MISSING_ID, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_time", depends=["AsyncmyTestMsgspecFunctions::get_datetime_none"]) + async def test_get_time( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_time(conn=asyncmy_conn, id_=MODEL_ID, time_test=model.time_test) + + assert result is not None + # MySQL time columns map to datetime.timedelta, not datetime.time. + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_time_none", depends=["AsyncmyTestMsgspecFunctions::get_time"]) + async def test_get_time_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_time(conn=asyncmy_conn, id_=MISSING_ID, time_test=model.time_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_bool", depends=["AsyncmyTestMsgspecFunctions::get_time_none"]) + async def test_get_bool( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_bool(conn=asyncmy_conn, id_=MODEL_ID, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_bool_none", depends=["AsyncmyTestMsgspecFunctions::get_bool"]) + async def test_get_bool_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_bool(conn=asyncmy_conn, id_=MISSING_ID, tinyint1_test=model.tinyint1_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_decimal", depends=["AsyncmyTestMsgspecFunctions::get_bool_none"]) + async def test_get_decimal( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_decimal(conn=asyncmy_conn, id_=MODEL_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + # decimal(12,4) comes back padded to scale 4. + assert str(result) == DECIMAL_PADDED + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_decimal_none", depends=["AsyncmyTestMsgspecFunctions::get_decimal"]) + async def test_get_decimal_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_decimal(conn=asyncmy_conn, id_=MISSING_ID, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_blob", depends=["AsyncmyTestMsgspecFunctions::get_decimal_none"]) + async def test_get_blob( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_blob(conn=asyncmy_conn, id_=MODEL_ID, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_blob_none", depends=["AsyncmyTestMsgspecFunctions::get_blob"]) + async def test_get_blob_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_blob(conn=asyncmy_conn, id_=MISSING_ID, blob_test=model.blob_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_bit", depends=["AsyncmyTestMsgspecFunctions::get_blob_none"]) + async def test_get_bit( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_bit(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + # bit(8) comes back as a one-byte memoryview. + assert isinstance(result, memoryview) + assert len(result) == 1 + assert bytes(result) == BIT_BYTE + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_year", depends=["AsyncmyTestMsgspecFunctions::get_bit"]) + async def test_get_year( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_year(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_json", depends=["AsyncmyTestMsgspecFunctions::get_year"]) + async def test_get_json( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_json(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, str) + # MySQL normalizes JSON spacing; compare parsed values, never strings. + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_mood", depends=["AsyncmyTestMsgspecFunctions::get_json"]) + async def test_get_mood( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mood(conn=asyncmy_conn, id_=MODEL_ID, mood=model.mood) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE__HIDDEN + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_tag", depends=["AsyncmyTestMsgspecFunctions::get_mood"]) + async def test_get_tag( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_tag(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is model.tag + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many", depends=["AsyncmyTestMsgspecFunctions::get_tag"]) + async def test_get_many(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_mysql_type(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert len(result) == 1 + assert isinstance(result[0], models.TestMysqlType) + + assert result[0] == msgspec.structs.replace(model, json_test=result[0].json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_iter", depends=["AsyncmyTestMsgspecFunctions::get_many"]) + async def test_get_many_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_mysql_type(conn=asyncmy_conn, id_=MODEL_ID): + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert result == msgspec.structs.replace(model, json_test=result.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_inner", depends=["AsyncmyTestMsgspecFunctions::get_many_iter"]) + async def test_get_many_inner(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = await queries.get_many_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], models.TestInnerMysqlType) + + assert result[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_inner_iter", depends=["AsyncmyTestMsgspecFunctions::get_many_inner"]) + async def test_get_many_inner_iter(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries.get_many_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecFunctions::get_many_nullable_inner", + depends=["AsyncmyTestMsgspecFunctions::get_many_inner_iter"], + ) + async def test_get_many_nullable_inner(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the query matches it via the NULL-safe <=> operator. + result = await queries.get_many_nullable_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], models.TestInnerMysqlType) + + assert result[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecFunctions::get_many_nullable_inner_iter", + depends=["AsyncmyTestMsgspecFunctions::get_many_nullable_inner"], + ) + async def test_get_many_nullable_inner_iter(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries.get_many_nullable_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecFunctions::get_many_date", + depends=["AsyncmyTestMsgspecFunctions::get_many_nullable_inner_iter"], + ) + async def test_get_many_date(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_date(conn=asyncmy_conn, id_=MODEL_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], datetime.date) + + assert result[0] == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_date_iter", depends=["AsyncmyTestMsgspecFunctions::get_many_date"]) + async def test_get_many_date_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_date(conn=asyncmy_conn, id_=MODEL_ID, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_time", depends=["AsyncmyTestMsgspecFunctions::get_many_date_iter"]) + async def test_get_many_time(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_time(conn=asyncmy_conn, id_=MODEL_ID, time_test=model.time_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], datetime.timedelta) + + assert result[0] == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_time_iter", depends=["AsyncmyTestMsgspecFunctions::get_many_time"]) + async def test_get_many_time_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_time(conn=asyncmy_conn, id_=MODEL_ID, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_bool", depends=["AsyncmyTestMsgspecFunctions::get_many_time_iter"]) + async def test_get_many_bool(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_bool(conn=asyncmy_conn, id_=MODEL_ID, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], bool) + + assert result[0] is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_bool_iter", depends=["AsyncmyTestMsgspecFunctions::get_many_bool"]) + async def test_get_many_bool_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_bool(conn=asyncmy_conn, id_=MODEL_ID, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_decimal", depends=["AsyncmyTestMsgspecFunctions::get_many_bool_iter"]) + async def test_get_many_decimal(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_decimal(conn=asyncmy_conn, id_=MODEL_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], decimal.Decimal) + + assert str(result[0]) == DECIMAL_PADDED + assert result[0] == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecFunctions::get_many_decimal_iter", + depends=["AsyncmyTestMsgspecFunctions::get_many_decimal"], + ) + async def test_get_many_decimal_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_decimal(conn=asyncmy_conn, id_=MODEL_ID, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_mood", depends=["AsyncmyTestMsgspecFunctions::get_many_decimal_iter"]) + async def test_get_many_mood(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_mood(conn=asyncmy_conn, mood=model.mood) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + # The query is not id-filtered; other rows may exist, so assert containment. + assert result + for mood in result: + assert isinstance(mood, enums.TestMysqlTypesMood) + assert mood is model.mood + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_many_mood_iter", depends=["AsyncmyTestMsgspecFunctions::get_many_mood"]) + async def test_get_many_mood_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + results = [mood async for mood in queries.get_many_mood(conn=asyncmy_conn, mood=model.mood)] + + assert results + for mood in results: + assert isinstance(mood, enums.TestMysqlTypesMood) + assert mood is model.mood + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::list_months", depends=["AsyncmyTestMsgspecFunctions::get_many_mood_iter"]) + async def test_list_months(self, asyncmy_conn: asyncmy.Connection) -> None: + # DATE_FORMAT contains literal % characters; regression for the percent-doubling bug. + result = await queries.list_months(conn=asyncmy_conn) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + for month in result: + assert isinstance(month, str) + assert EXPECTED_MONTH in result + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::list_months_iter", depends=["AsyncmyTestMsgspecFunctions::list_months"]) + async def test_list_months_iter(self, asyncmy_conn: asyncmy.Connection) -> None: + results = [month async for month in queries.list_months(conn=asyncmy_conn)] + + assert EXPECTED_MONTH in results + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::count", depends=["AsyncmyTestMsgspecFunctions::list_months_iter"]) + async def test_count(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.count_mysql_types(conn=asyncmy_conn) + + assert result is not None + assert isinstance(result, int) + assert result >= 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::update_rows", depends=["AsyncmyTestMsgspecFunctions::count"]) + async def test_update_rows(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.update_varchar_test(conn=asyncmy_conn, varchar_test="updated varchar", id_=MODEL_ID) + + assert isinstance(result, int) + assert result == 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::update_rows_none", depends=["AsyncmyTestMsgspecFunctions::update_rows"]) + async def test_update_rows_none(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.update_varchar_test(conn=asyncmy_conn, varchar_test="updated varchar", id_=MISSING_ID) + + assert isinstance(result, int) + assert result == 0 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::execresult_cursor", depends=["AsyncmyTestMsgspecFunctions::update_rows_none"]) + async def test_execresult_cursor(self, asyncmy_conn: asyncmy.Connection) -> None: + cursor = await queries.all_mysql_types_cursor(conn=asyncmy_conn) + + assert isinstance(cursor, asyncmy.cursors.Cursor) + rows = await cursor.fetchall() + assert any(row[0] == MODEL_ID for row in rows) + await cursor.close() + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::exec_last_id", depends=["AsyncmyTestMsgspecFunctions::execresult_cursor"]) + async def test_exec_last_id(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.insert_exec_last_id(conn=asyncmy_conn, name=SUITE_TAG) + + assert result is not None + assert isinstance(result, int) + # AUTO_INCREMENT counters persist across runs; never assert exact ids. + assert result > 0 + + name = await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=result) + assert name == SUITE_TAG + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::insert_reserved_arg", depends=["AsyncmyTestMsgspecFunctions::exec_last_id"]) + async def test_insert_reserved_arg(self, asyncmy_conn: asyncmy.Connection) -> None: + await queries.insert_reserved_arg(conn=asyncmy_conn, id_=RESERVED_ID, conn_2=SUITE_TAG) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::get_reserved_arg", depends=["AsyncmyTestMsgspecFunctions::insert_reserved_arg"]) + async def test_get_reserved_arg(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.get_reserved_arg(conn=asyncmy_conn, conn_2=SUITE_TAG) + + assert result is not None + assert result == models.TestReservedArg(id_=RESERVED_ID, conn=SUITE_TAG) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::delete", depends=["AsyncmyTestMsgspecFunctions::get_reserved_arg"]) + async def test_delete(self, asyncmy_conn: asyncmy.Connection) -> None: + await queries.delete_one_mysql_type(conn=asyncmy_conn, id_=MODEL_ID) + + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=MODEL_ID) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestMsgspecFunctions::insert_type_override") + async def test_insert_type_override(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + await queries.insert_type_override(conn=asyncmy_conn, id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecFunctions::get_type_override", + depends=["AsyncmyTestMsgspecFunctions::insert_type_override"], + ) + async def test_get_type_override(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + result = await queries.get_type_override(conn=asyncmy_conn, id_=override_model.id_) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecFunctions::get_type_override_none_value", + depends=["AsyncmyTestMsgspecFunctions::get_type_override"], + ) + async def test_get_type_override_none_value(self, asyncmy_conn: asyncmy.Connection) -> None: + # The UserString override column is nullable; None must round-trip too. + await queries.insert_type_override(conn=asyncmy_conn, id_=OVERRIDE_NONE_ID, text_test=None) + + result = await queries.get_type_override(conn=asyncmy_conn, id_=OVERRIDE_NONE_ID) + assert result is not None + assert result.text_test is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecFunctions::get_type_override_missing", + depends=["AsyncmyTestMsgspecFunctions::get_type_override_none_value"], + ) + async def test_get_type_override_missing(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.get_type_override(conn=asyncmy_conn, id_=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestMsgspecFunctions::cleanup", + depends=["AsyncmyTestMsgspecFunctions::delete", "AsyncmyTestMsgspecFunctions::get_type_override_missing"], + ) + async def test_cleanup(self, asyncmy_conn: asyncmy.Connection) -> None: + # Tables without generated delete queries are cleaned directly so the + # fixed ids are free for the next chain. + async with asyncmy_conn.cursor() as cur: + await cur.execute("DELETE FROM test_inner_mysql_types WHERE table_id = %s", (MODEL_ID,)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_type_override WHERE id IN (%s, %s)", (OVERRIDE_ID, OVERRIDE_NONE_ID)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_reserved_args WHERE id = %s", (RESERVED_ID,)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_execlastid WHERE name = %s", (SUITE_TAG,)) # pyright: ignore[reportUnknownMemberType] + + @pytest.mark.asyncio(loop_scope="session") + async def test_one_missing_rows_return_none(self, asyncmy_conn: asyncmy.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. + assert await queries.get_one_mysql_type(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=-1) is None + assert await queries.get_one_date(conn=asyncmy_conn, id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert await queries.get_one_datetime(conn=asyncmy_conn, id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert await queries.get_one_time(conn=asyncmy_conn, id_=-1, time_test=datetime.timedelta()) is None + assert await queries.get_one_bool(conn=asyncmy_conn, id_=-1, tinyint1_test=False) is None + assert await queries.get_one_decimal(conn=asyncmy_conn, id_=-1, decimal_test=decimal.Decimal(0)) is None + assert await queries.get_one_blob(conn=asyncmy_conn, id_=-1, blob_test=memoryview(b"")) is None + assert await queries.get_one_bit(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_year(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_json(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_mood(conn=asyncmy_conn, id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert await queries.get_one_tag(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_type_override(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_reserved_arg(conn=asyncmy_conn, conn_2="missing") is None + assert await queries.touch_exec_last_id(conn=asyncmy_conn, name="untouched", id_=-1) is None + + # count(*) always returns a row; its miss branch needs the stub. + stub = typing.cast("asyncmy.Connection", no_row_conn.NoRowConn()) + assert await queries.count_mysql_types(conn=stub) is None diff --git a/test/driver_asyncmy/no_row_conn.py b/test/driver_asyncmy/no_row_conn.py new file mode 100644 index 00000000..600581dd --- /dev/null +++ b/test/driver_asyncmy/no_row_conn.py @@ -0,0 +1,74 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Shared connection stub for exercising the generated not-found branches.""" + +from __future__ import annotations + +import typing + + +class NoRowCursor: + """Cursor stub whose fetchone never finds a row.""" + + async def __aenter__(self) -> typing.Self: + """Return the cursor itself, like a real cursor context manager. + + Returns + ------- + typing.Self + The cursor stub missing every row. + """ + return self + + async def __aexit__(self, *exc_info: object) -> None: + """Do nothing on exit; there is no real cursor to close.""" + + @staticmethod + async def execute(_query: str, _args: object = None) -> int: + """Pretend to execute and affect no rows. + + Returns + ------- + int + Always 0. + """ + return 0 + + @staticmethod + async def fetchone() -> None: + """Return None, exactly like a cursor over an empty result set.""" + + +class NoRowConn: + """Connection stub whose queries never find a row.""" + + # `SELECT count(*)` always returns exactly one row, so the generated + # not-found branch of the count queries needs a connection stub that + # misses. + @staticmethod + def cursor() -> NoRowCursor: + """Return a cursor that finds no row. + + Returns + ------- + NoRowCursor + The cursor stub missing every row. + """ + return NoRowCursor() diff --git a/test/driver_asyncmy/pydantic/__init__.py b/test/driver_asyncmy/pydantic/__init__.py new file mode 100644 index 00000000..0b34101d --- /dev/null +++ b/test/driver_asyncmy/pydantic/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for asyncmy tests.""" diff --git a/test/driver_asyncmy/pydantic/classes/__init__.py b/test/driver_asyncmy/pydantic/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_asyncmy/pydantic/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncmy/pydantic/classes/enums.py b/test/driver_asyncmy/pydantic/classes/enums.py new file mode 100644 index 00000000..873f5d33 --- /dev/null +++ b/test/driver_asyncmy/pydantic/classes/enums.py @@ -0,0 +1,56 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_asyncmy/pydantic/classes/models.py b/test/driver_asyncmy/pydantic/classes/models.py new file mode 100644 index 00000000..d628bbb6 --- /dev/null +++ b/test/driver_asyncmy/pydantic/classes/models.py @@ -0,0 +1,233 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlType", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +from collections import UserString +import datetime +import decimal +import pydantic +import typing + +if typing.TYPE_CHECKING: + import collections.abc + +from test.driver_asyncmy.pydantic.classes import enums + + +class TestInnerMysqlType(pydantic.BaseModel): + """Model representing TestInnerMysqlType. + + Attributes: + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +class TestMysqlType(pydantic.BaseModel): + """Model representing TestMysqlType. + + Attributes: + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +class TestReservedArg(pydantic.BaseModel): + """Model representing TestReservedArg. + + Attributes: + id_: int + conn: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + conn: str + + +class TestTypeOverride(pydantic.BaseModel): + """Model representing TestTypeOverride. + + Attributes: + id_: int + text_test: UserString | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + text_test: UserString | None diff --git a/test/driver_asyncmy/pydantic/classes/queries.py b/test/driver_asyncmy/pydantic/classes/queries.py new file mode 100644 index 00000000..550efeeb --- /dev/null +++ b/test/driver_asyncmy/pydantic/classes/queries.py @@ -0,0 +1,1437 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Queries", + "QueryResults", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + import datetime + import decimal + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.pydantic.classes import enums +from test.driver_asyncmy.pydantic.classes import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `asyncmy.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns: + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopAsyncIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +class Queries: + """Queries from file queries.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: asyncmy.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> asyncmy.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `asyncmy.Connection` used to make queries. + """ + return self._conn + + async def insert_one_mysql_type( + self, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, + ) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + id_: int. + int_test: int. + integer_test: int. + mediumint_test: int. + smallint_test: int. + tinyint_test: int. + bigint_test: int. + int_unsigned_test: int. + bigint_unsigned_test: int. + year_test: int. + tinyint1_test: bool. + bool_test: bool. + boolean_test: bool. + float_test: float. + double_test: float. + double_precision_test: float. + real_test: float. + decimal_test: decimal.Decimal. + numeric_test: decimal.Decimal. + char_test: str. + varchar_test: str. + tinytext_test: str. + text_test: str. + mediumtext_test: str. + longtext_test: str. + binary_test: memoryview. + varbinary_test: memoryview. + tinyblob_test: memoryview. + blob_test: memoryview. + mediumblob_test: memoryview. + longblob_test: memoryview. + bit_test: memoryview. + date_test: datetime.date. + datetime_test: datetime.datetime. + datetime6_test: datetime.datetime. + timestamp_test: datetime.datetime. + time_test: datetime.timedelta. + json_test: str. + mood: enums.TestMysqlTypesMood. + tag: enums.TestMysqlTypesTag. + """ + async with self._conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + async def insert_one_inner_mysql_type( + self, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, + ) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + table_id: int. + int_test: int | None. + integer_test: int | None. + mediumint_test: int | None. + smallint_test: int | None. + tinyint_test: int | None. + bigint_test: int | None. + int_unsigned_test: int | None. + bigint_unsigned_test: int | None. + year_test: int | None. + tinyint1_test: bool | None. + bool_test: bool | None. + boolean_test: bool | None. + float_test: float | None. + double_test: float | None. + double_precision_test: float | None. + real_test: float | None. + decimal_test: decimal.Decimal | None. + numeric_test: decimal.Decimal | None. + char_test: str | None. + varchar_test: str | None. + tinytext_test: str | None. + text_test: str | None. + mediumtext_test: str | None. + longtext_test: str | None. + binary_test: memoryview | None. + varbinary_test: memoryview | None. + tinyblob_test: memoryview | None. + blob_test: memoryview | None. + mediumblob_test: memoryview | None. + longblob_test: memoryview | None. + bit_test: memoryview | None. + date_test: datetime.date | None. + datetime_test: datetime.datetime | None. + datetime6_test: datetime.datetime | None. + timestamp_test: datetime.datetime | None. + time_test: datetime.timedelta | None. + json_test: str | None. + mood: enums.TestInnerMysqlTypesMood | None. + tag: enums.TestInnerMysqlTypesTag | None. + """ + async with self._conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + async def get_one_mysql_type(self, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestMysqlType` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + async def get_one_inner_mysql_type(self, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + table_id: int. + + Returns: + Result of type `models.TestInnerMysqlType` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + def get_many_mysql_type(self, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(self._conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + def get_many_inner_mysql_type(self, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + table_id: int. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + def get_many_nullable_inner_mysql_type(self, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Args: + table_id: int. + int_test: int | None. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + async def get_one_date(self, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + id_: int. + date_test: datetime.date. + + Returns: + Result of type `datetime.date` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DATE, (id_, date_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_datetime(self, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Args: + id_: int. + datetime_test: datetime.datetime. + + Returns: + Result of type `datetime.datetime` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_time(self, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + id_: int. + time_test: datetime.timedelta. + + Returns: + Result of type `datetime.timedelta` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_TIME, (id_, time_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_bool(self, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + id_: int. + tinyint1_test: bool. + + Returns: + Result of type `bool` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = await cur.fetchone() + if row is None: + return None + return bool(row[0]) + + async def get_one_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Result of type `decimal.Decimal` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_blob(self, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Args: + id_: int. + blob_test: memoryview. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + async def get_one_bit(self, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_BIT, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + async def get_one_year(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_YEAR, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_json(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_JSON, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def get_one_mood(self, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Args: + id_: int. + mood: enums.TestMysqlTypesMood. + + Returns: + Result of type `enums.TestMysqlTypesMood` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_MOOD, (id_, mood)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + async def get_one_tag(self, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `enums.TestMysqlTypesTag` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_ONE_TAG, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + def get_many_date(self, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + id_: int. + date_test: datetime.date. + + Returns: + Helper class of type `QueryResults[datetime.date]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + def get_many_time(self, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + id_: int. + time_test: datetime.timedelta. + + Returns: + Helper class of type `QueryResults[datetime.timedelta]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + def get_many_bool(self, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + id_: int. + tinyint1_test: bool. + + Returns: + Helper class of type `QueryResults[bool]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(self._conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + def get_many_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Helper class of type `QueryResults[decimal.Decimal]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + def get_many_mood(self, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Args: + mood: enums.TestMysqlTypesMood. + + Returns: + Helper class of type `QueryResults[enums.TestMysqlTypesMood]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(self._conn, GET_MANY_MOOD, _decode_hook, mood) + + def list_months(self) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Returns: + Helper class of type `QueryResults[str]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, LIST_MONTHS, operator.itemgetter(0)) + + async def count_mysql_types(self) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(COUNT_MYSQL_TYPES) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def update_varchar_test(self, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Args: + varchar_test: str. + id_: int. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + async with self._conn.cursor() as cur: + return await cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + async def delete_one_mysql_type(self, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + """ + async with self._conn.cursor() as cur: + await cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + async def all_mysql_types_cursor(self) -> asyncmy.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Returns: + The result of type `asyncmy.cursors.Cursor` returned when executing the query. + """ + cur = self._conn.cursor() + await cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + async def insert_exec_last_id(self, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Args: + name: str. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + async def get_exec_last_id_name(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + async def insert_type_override(self, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Args: + id_: int. + text_test: UserString | None. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + async def get_type_override(self, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestTypeOverride` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + async def get_reserved_arg(self, *, conn: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Args: + conn: str. + + Returns: + Result of type `models.TestReservedArg` fetched from the db. Will be `None` if not found. + """ + async with self._conn.cursor() as cur: + await cur.execute(GET_RESERVED_ARG, (conn,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + async def insert_reserved_arg(self, *, id_: int, conn: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Args: + id_: int. + conn: str. + """ + async with self._conn.cursor() as cur: + await cur.execute(INSERT_RESERVED_ARG, (id_, conn)) + + async def touch_exec_last_id(self, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Args: + name: str. + id_: int. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + async with self._conn.cursor() as cur: + await cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_asyncmy/pydantic/functions/__init__.py b/test/driver_asyncmy/pydantic/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_asyncmy/pydantic/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncmy/pydantic/functions/enums.py b/test/driver_asyncmy/pydantic/functions/enums.py new file mode 100644 index 00000000..873f5d33 --- /dev/null +++ b/test/driver_asyncmy/pydantic/functions/enums.py @@ -0,0 +1,56 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_asyncmy/pydantic/functions/models.py b/test/driver_asyncmy/pydantic/functions/models.py new file mode 100644 index 00000000..a7cd52a9 --- /dev/null +++ b/test/driver_asyncmy/pydantic/functions/models.py @@ -0,0 +1,233 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestInnerMysqlType", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +from collections import UserString +import datetime +import decimal +import pydantic +import typing + +if typing.TYPE_CHECKING: + import collections.abc + +from test.driver_asyncmy.pydantic.functions import enums + + +class TestInnerMysqlType(pydantic.BaseModel): + """Model representing TestInnerMysqlType. + + Attributes: + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +class TestMysqlType(pydantic.BaseModel): + """Model representing TestMysqlType. + + Attributes: + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +class TestReservedArg(pydantic.BaseModel): + """Model representing TestReservedArg. + + Attributes: + id_: int + conn: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + conn: str + + +class TestTypeOverride(pydantic.BaseModel): + """Model representing TestTypeOverride. + + Attributes: + id_: int + text_test: UserString | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + text_test: UserString | None diff --git a/test/driver_asyncmy/pydantic/functions/queries.py b/test/driver_asyncmy/pydantic/functions/queries.py new file mode 100644 index 00000000..3e431720 --- /dev/null +++ b/test/driver_asyncmy/pydantic/functions/queries.py @@ -0,0 +1,1558 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +# pyright: reportUnknownMemberType=false +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "all_mysql_types_cursor", + "count_mysql_types", + "delete_one_mysql_type", + "get_exec_last_id_name", + "get_many_bool", + "get_many_date", + "get_many_decimal", + "get_many_inner_mysql_type", + "get_many_mood", + "get_many_mysql_type", + "get_many_nullable_inner_mysql_type", + "get_many_time", + "get_one_bit", + "get_one_blob", + "get_one_bool", + "get_one_date", + "get_one_datetime", + "get_one_decimal", + "get_one_inner_mysql_type", + "get_one_json", + "get_one_mood", + "get_one_mysql_type", + "get_one_tag", + "get_one_time", + "get_one_year", + "get_reserved_arg", + "get_type_override", + "insert_exec_last_id", + "insert_one_inner_mysql_type", + "insert_one_mysql_type", + "insert_reserved_arg", + "insert_type_override", + "list_months", + "touch_exec_last_id", + "update_varchar_test", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import asyncmy + import asyncmy.cursors + import collections.abc + import datetime + import decimal + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_asyncmy.pydantic.functions import enums +from test.driver_asyncmy.pydantic.functions import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: asyncmy.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `asyncmy.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: asyncmy.cursors.Cursor | None = None + + def __aiter__(self) -> QueryResults[T]: + """Initialize iteration support for `async for`. + + Returns: + Self as an asynchronous iterator. + """ + return self + + def __await__( + self, + ) -> collections.abc.Generator[None, None, collections.abc.Sequence[T]]: + """Allow `await` on the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + + async def _wrapper() -> collections.abc.Sequence[T]: + cur = self._conn.cursor() + await cur.execute(self._sql, self._args) + result = await cur.fetchall() + await cur.close() + return [self._decode_hook(row) for row in result] + + return _wrapper().__await__() + + async def __anext__(self) -> T: + """Yield the next item in the query result using an asyncmy cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopAsyncIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + await self._cursor.execute(self._sql, self._args) + record = await self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopAsyncIteration + return self._decode_hook(record) + + +async def insert_one_mysql_type( + conn: asyncmy.Connection, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, +) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + int_test: int. + integer_test: int. + mediumint_test: int. + smallint_test: int. + tinyint_test: int. + bigint_test: int. + int_unsigned_test: int. + bigint_unsigned_test: int. + year_test: int. + tinyint1_test: bool. + bool_test: bool. + boolean_test: bool. + float_test: float. + double_test: float. + double_precision_test: float. + real_test: float. + decimal_test: decimal.Decimal. + numeric_test: decimal.Decimal. + char_test: str. + varchar_test: str. + tinytext_test: str. + text_test: str. + mediumtext_test: str. + longtext_test: str. + binary_test: memoryview. + varbinary_test: memoryview. + tinyblob_test: memoryview. + blob_test: memoryview. + mediumblob_test: memoryview. + longblob_test: memoryview. + bit_test: memoryview. + date_test: datetime.date. + datetime_test: datetime.datetime. + datetime6_test: datetime.datetime. + timestamp_test: datetime.datetime. + time_test: datetime.timedelta. + json_test: str. + mood: enums.TestMysqlTypesMood. + tag: enums.TestMysqlTypesTag. + """ + async with conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + +async def insert_one_inner_mysql_type( + conn: asyncmy.Connection, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, +) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + table_id: int. + int_test: int | None. + integer_test: int | None. + mediumint_test: int | None. + smallint_test: int | None. + tinyint_test: int | None. + bigint_test: int | None. + int_unsigned_test: int | None. + bigint_unsigned_test: int | None. + year_test: int | None. + tinyint1_test: bool | None. + bool_test: bool | None. + boolean_test: bool | None. + float_test: float | None. + double_test: float | None. + double_precision_test: float | None. + real_test: float | None. + decimal_test: decimal.Decimal | None. + numeric_test: decimal.Decimal | None. + char_test: str | None. + varchar_test: str | None. + tinytext_test: str | None. + text_test: str | None. + mediumtext_test: str | None. + longtext_test: str | None. + binary_test: memoryview | None. + varbinary_test: memoryview | None. + tinyblob_test: memoryview | None. + blob_test: memoryview | None. + mediumblob_test: memoryview | None. + longblob_test: memoryview | None. + bit_test: memoryview | None. + date_test: datetime.date | None. + datetime_test: datetime.datetime | None. + datetime6_test: datetime.datetime | None. + timestamp_test: datetime.datetime | None. + time_test: datetime.timedelta | None. + json_test: str | None. + mood: enums.TestInnerMysqlTypesMood | None. + tag: enums.TestInnerMysqlTypesTag | None. + """ + async with conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + await cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + +async def get_one_mysql_type(conn: asyncmy.Connection, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestMysqlType` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + +async def get_one_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + table_id: int. + + Returns: + Result of type `models.TestInnerMysqlType` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + +def get_many_mysql_type(conn: asyncmy.Connection, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + +def get_many_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + table_id: int. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + +def get_many_nullable_inner_mysql_type(conn: asyncmy.Connection, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + table_id: int. + int_test: int | None. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + +async def get_one_date(conn: asyncmy.Connection, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + date_test: datetime.date. + + Returns: + Result of type `datetime.date` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DATE, (id_, date_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_datetime(conn: asyncmy.Connection, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + datetime_test: datetime.datetime. + + Returns: + Result of type `datetime.datetime` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_time(conn: asyncmy.Connection, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + time_test: datetime.timedelta. + + Returns: + Result of type `datetime.timedelta` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_TIME, (id_, time_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_bool(conn: asyncmy.Connection, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + tinyint1_test: bool. + + Returns: + Result of type `bool` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = await cur.fetchone() + if row is None: + return None + return bool(row[0]) + + +async def get_one_decimal(conn: asyncmy.Connection, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Result of type `decimal.Decimal` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_blob(conn: asyncmy.Connection, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + blob_test: memoryview. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +async def get_one_bit(conn: asyncmy.Connection, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_BIT, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +async def get_one_year(conn: asyncmy.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_YEAR, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_json(conn: asyncmy.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_JSON, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def get_one_mood(conn: asyncmy.Connection, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + mood: enums.TestMysqlTypesMood. + + Returns: + Result of type `enums.TestMysqlTypesMood` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_MOOD, (id_, mood)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + +async def get_one_tag(conn: asyncmy.Connection, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `enums.TestMysqlTypesTag` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_ONE_TAG, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + +def get_many_date(conn: asyncmy.Connection, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + date_test: datetime.date. + + Returns: + Helper class of type `QueryResults[datetime.date]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + +def get_many_time(conn: asyncmy.Connection, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + time_test: datetime.timedelta. + + Returns: + Helper class of type `QueryResults[datetime.timedelta]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + +def get_many_bool(conn: asyncmy.Connection, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + tinyint1_test: bool. + + Returns: + Helper class of type `QueryResults[bool]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + +def get_many_decimal(conn: asyncmy.Connection, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Helper class of type `QueryResults[decimal.Decimal]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + +def get_many_mood(conn: asyncmy.Connection, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + mood: enums.TestMysqlTypesMood. + + Returns: + Helper class of type `QueryResults[enums.TestMysqlTypesMood]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(conn, GET_MANY_MOOD, _decode_hook, mood) + + +def list_months(conn: asyncmy.Connection) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + Helper class of type `QueryResults[str]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, LIST_MONTHS, operator.itemgetter(0)) + + +async def count_mysql_types(conn: asyncmy.Connection) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(COUNT_MYSQL_TYPES) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def update_varchar_test(conn: asyncmy.Connection, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + varchar_test: str. + id_: int. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + async with conn.cursor() as cur: + return await cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + +async def delete_one_mysql_type(conn: asyncmy.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + """ + async with conn.cursor() as cur: + await cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + +async def all_mysql_types_cursor(conn: asyncmy.Connection) -> asyncmy.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + + Returns: + The result of type `asyncmy.cursors.Cursor` returned when executing the query. + """ + cur = conn.cursor() + await cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + +async def insert_exec_last_id(conn: asyncmy.Connection, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + name: str. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + +async def get_exec_last_id_name(conn: asyncmy.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return row[0] + + +async def insert_type_override(conn: asyncmy.Connection, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + text_test: UserString | None. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + +async def get_type_override(conn: asyncmy.Connection, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestTypeOverride` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + +async def get_reserved_arg(conn: asyncmy.Connection, *, conn_2: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + conn_2: str. + + Returns: + Result of type `models.TestReservedArg` fetched from the db. Will be `None` if not found. + """ + async with conn.cursor() as cur: + await cur.execute(GET_RESERVED_ARG, (conn_2,)) + row = await cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + +async def insert_reserved_arg(conn: asyncmy.Connection, *, id_: int, conn_2: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + id_: int. + conn_2: str. + """ + async with conn.cursor() as cur: + await cur.execute(INSERT_RESERVED_ARG, (id_, conn_2)) + + +async def touch_exec_last_id(conn: asyncmy.Connection, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Args: + conn: + Connection object of type `asyncmy.Connection` used to execute the query. + name: str. + id_: int. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + async with conn.cursor() as cur: + await cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_asyncmy/pydantic/ruff.toml b/test/driver_asyncmy/pydantic/ruff.toml new file mode 100644 index 00000000..ded540a0 --- /dev/null +++ b/test/driver_asyncmy/pydantic/ruff.toml @@ -0,0 +1,9 @@ +extend="../../../ruff.toml" + + +[lint.flake8-type-checking] +runtime-evaluated-base-classes = ["pydantic.BaseModel"] + + +[lint.pydocstyle] +convention = "google" \ No newline at end of file diff --git a/test/driver_asyncmy/pydantic/test_asyncmy_pydantic_classes.py b/test/driver_asyncmy/pydantic/test_asyncmy_pydantic_classes.py new file mode 100644 index 00000000..5a3d402f --- /dev/null +++ b/test/driver_asyncmy/pydantic/test_asyncmy_pydantic_classes.py @@ -0,0 +1,882 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import collections.abc +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import asyncmy +import asyncmy.cursors +import pytest +import pytest_asyncio + +from test.driver_asyncmy import no_row_conn +from test.driver_asyncmy.pydantic.classes import enums +from test.driver_asyncmy.pydantic.classes import models +from test.driver_asyncmy.pydantic.classes import queries + +MODEL_ID = 9101 +OVERRIDE_ID = 9201 +OVERRIDE_NONE_ID = 9202 +RESERVED_ID = 9301 +MISSING_ID = 9901 +SUITE_TAG = "asyncmy-pydantic-classes" +EXPECTED_MONTH = "2026-01" +DECIMAL_PADDED = "12.3400" +BINARY_UNPADDED = b"\xaa\xbb\xcc" +BINARY_LENGTH = 16 +BIT_BYTE = b"\x80" + + +@pytest.mark.asyncio(loop_scope="session") +class TestAsyncmyPydanticClasses: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MODEL_ID, + int_test=42, + integer_test=-1_000_000, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_000_000_000, + bigint_unsigned_test=2**63 + 10, + year_test=2026, + tinyint1_test=True, + bool_test=False, + boolean_test=True, + float_test=3.5, + double_test=math.pi, + double_precision_test=1.41421, + real_test=math.e, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("100.10"), + char_test="CHAR10", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(BINARY_UNPADDED + b"\x00" * (BINARY_LENGTH - len(BINARY_UNPADDED))), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tinyblob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"mediumblob"), + longblob_test=memoryview(b"longblob"), + bit_test=memoryview(BIT_BYTE), + date_test=datetime.date(2026, 1, 2), + datetime_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + datetime6_test=datetime.datetime(2026, 1, 2, 3, 4, 5, 123456), + timestamp_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + time_test=datetime.timedelta(hours=13, minutes=45, seconds=30), + json_test=json.dumps({"foo": "bar", "count": 3}, separators=(",", ":")), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.GAMMA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=None, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=None, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=None, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=None, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=None, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=None, + ) + + @pytest_asyncio.fixture(scope="class", loop_scope="session") + async def queries_obj(self, asyncmy_conn: asyncmy.Connection) -> queries.Queries: + return queries.Queries(conn=asyncmy_conn) + + @pytest.mark.asyncio(loop_scope="session") + async def test_conn_attr(self, queries_obj: queries.Queries) -> None: + assert isinstance(queries_obj.conn, asyncmy.Connection) + + @pytest.mark.asyncio(loop_scope="session") + async def test_enum_members(self) -> None: + assert enums.TestMysqlTypesMood.VALUE_24H.value == "24h" + assert enums.TestMysqlTypesMood.VALUE__HIDDEN.value == "_hidden" + assert enums.TestInnerMysqlTypesMood.VALUE_24H.value == "24h" + assert enums.TestMysqlTypesTag.BETA.value == "beta" + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::insert") + async def test_insert( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + await queries_obj.insert_one_mysql_type( + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + # char(10) strips trailing spaces on return; the model holds the stripped value. + char_test=model.char_test + " ", + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + # binary(16) is right-padded with NUL bytes on return; the model holds the padded value. + binary_test=memoryview(BINARY_UNPADDED), + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::inner_insert", depends=["AsyncmyTestPydanticClasses::insert"]) + async def test_inner_insert( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + await queries_obj.insert_one_inner_mysql_type( + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_one", depends=["AsyncmyTestPydanticClasses::inner_insert"]) + async def test_get_one( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mysql_type(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert result.tinyint1_test is True + assert result.bool_test is False + assert result.boolean_test is True + assert len(result.binary_test) == BINARY_LENGTH + assert result.char_test == model.char_test + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + assert isinstance(result.time_test, datetime.timedelta) + # MySQL normalizes JSON spacing; compare parsed values, never strings. + assert json.loads(result.json_test) == json.loads(model.json_test) + assert result == model.model_copy(update={"json_test": result.json_test}) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_one_none", depends=["AsyncmyTestPydanticClasses::get_one"]) + async def test_get_one_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_mysql_type(id_=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_one_inner", depends=["AsyncmyTestPydanticClasses::get_one_none"]) + async def test_get_one_inner( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = await queries_obj.get_one_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result.tinyint1_test is None + assert result.bool_test is False + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_one_inner_none", depends=["AsyncmyTestPydanticClasses::get_one_inner"]) + async def test_get_one_inner_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_inner_mysql_type(table_id=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_date", depends=["AsyncmyTestPydanticClasses::get_one_inner_none"]) + async def test_get_date( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_date(id_=MODEL_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_date_none", depends=["AsyncmyTestPydanticClasses::get_date"]) + async def test_get_date_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_date(id_=MISSING_ID, date_test=model.date_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_datetime", depends=["AsyncmyTestPydanticClasses::get_date_none"]) + async def test_get_datetime( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_datetime(id_=MODEL_ID, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_datetime_none", depends=["AsyncmyTestPydanticClasses::get_datetime"]) + async def test_get_datetime_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_datetime(id_=MISSING_ID, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_time", depends=["AsyncmyTestPydanticClasses::get_datetime_none"]) + async def test_get_time( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_time(id_=MODEL_ID, time_test=model.time_test) + + assert result is not None + # MySQL time columns map to datetime.timedelta, not datetime.time. + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_time_none", depends=["AsyncmyTestPydanticClasses::get_time"]) + async def test_get_time_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_time(id_=MISSING_ID, time_test=model.time_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_bool", depends=["AsyncmyTestPydanticClasses::get_time_none"]) + async def test_get_bool( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_bool(id_=MODEL_ID, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_bool_none", depends=["AsyncmyTestPydanticClasses::get_bool"]) + async def test_get_bool_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_bool(id_=MISSING_ID, tinyint1_test=model.tinyint1_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_decimal", depends=["AsyncmyTestPydanticClasses::get_bool_none"]) + async def test_get_decimal( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_decimal(id_=MODEL_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + # decimal(12,4) comes back padded to scale 4. + assert str(result) == DECIMAL_PADDED + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_decimal_none", depends=["AsyncmyTestPydanticClasses::get_decimal"]) + async def test_get_decimal_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_decimal(id_=MISSING_ID, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_blob", depends=["AsyncmyTestPydanticClasses::get_decimal_none"]) + async def test_get_blob( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_blob(id_=MODEL_ID, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_blob_none", depends=["AsyncmyTestPydanticClasses::get_blob"]) + async def test_get_blob_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_blob(id_=MISSING_ID, blob_test=model.blob_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_bit", depends=["AsyncmyTestPydanticClasses::get_blob_none"]) + async def test_get_bit( + self, + queries_obj: queries.Queries, + ) -> None: + result = await queries_obj.get_one_bit(id_=MODEL_ID) + + assert result is not None + # bit(8) comes back as a one-byte memoryview. + assert isinstance(result, memoryview) + assert len(result) == 1 + assert bytes(result) == BIT_BYTE + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_year", depends=["AsyncmyTestPydanticClasses::get_bit"]) + async def test_get_year( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_year(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_json", depends=["AsyncmyTestPydanticClasses::get_year"]) + async def test_get_json( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_json(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, str) + # MySQL normalizes JSON spacing; compare parsed values, never strings. + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_mood", depends=["AsyncmyTestPydanticClasses::get_json"]) + async def test_get_mood( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_mood(id_=MODEL_ID, mood=model.mood) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_tag", depends=["AsyncmyTestPydanticClasses::get_mood"]) + async def test_get_tag( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = await queries_obj.get_one_tag(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is model.tag + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many", depends=["AsyncmyTestPydanticClasses::get_tag"]) + async def test_get_many(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_mysql_type(id_=MODEL_ID) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert len(result) == 1 + assert isinstance(result[0], models.TestMysqlType) + + assert result[0] == model.model_copy(update={"json_test": result[0].json_test}) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_iter", depends=["AsyncmyTestPydanticClasses::get_many"]) + async def test_get_many_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_mysql_type(id_=MODEL_ID): + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert result == model.model_copy(update={"json_test": result.json_test}) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_inner", depends=["AsyncmyTestPydanticClasses::get_many_iter"]) + async def test_get_many_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = await queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], models.TestInnerMysqlType) + + assert result[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_inner_iter", depends=["AsyncmyTestPydanticClasses::get_many_inner"]) + async def test_get_many_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticClasses::get_many_nullable_inner", + depends=["AsyncmyTestPydanticClasses::get_many_inner_iter"], + ) + async def test_get_many_nullable_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the query matches it via the NULL-safe <=> operator. + result = await queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], models.TestInnerMysqlType) + + assert result[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticClasses::get_many_nullable_inner_iter", + depends=["AsyncmyTestPydanticClasses::get_many_nullable_inner"], + ) + async def test_get_many_nullable_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticClasses::get_many_date", + depends=["AsyncmyTestPydanticClasses::get_many_nullable_inner_iter"], + ) + async def test_get_many_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_date(id_=MODEL_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], datetime.date) + + assert result[0] == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_date_iter", depends=["AsyncmyTestPydanticClasses::get_many_date"]) + async def test_get_many_date_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_date(id_=MODEL_ID, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_time", depends=["AsyncmyTestPydanticClasses::get_many_date_iter"]) + async def test_get_many_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_time(id_=MODEL_ID, time_test=model.time_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], datetime.timedelta) + + assert result[0] == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_time_iter", depends=["AsyncmyTestPydanticClasses::get_many_time"]) + async def test_get_many_time_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_time(id_=MODEL_ID, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_bool", depends=["AsyncmyTestPydanticClasses::get_many_time_iter"]) + async def test_get_many_bool(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_bool(id_=MODEL_ID, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], bool) + + assert result[0] is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_bool_iter", depends=["AsyncmyTestPydanticClasses::get_many_bool"]) + async def test_get_many_bool_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_bool(id_=MODEL_ID, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_decimal", depends=["AsyncmyTestPydanticClasses::get_many_bool_iter"]) + async def test_get_many_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_decimal(id_=MODEL_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], decimal.Decimal) + + assert str(result[0]) == DECIMAL_PADDED + assert result[0] == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticClasses::get_many_decimal_iter", + depends=["AsyncmyTestPydanticClasses::get_many_decimal"], + ) + async def test_get_many_decimal_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + async for result in queries_obj.get_many_decimal(id_=MODEL_ID, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_mood", depends=["AsyncmyTestPydanticClasses::get_many_decimal_iter"]) + async def test_get_many_mood(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = await queries_obj.get_many_mood(mood=model.mood) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + # The query is not id-filtered; other rows may exist, so assert containment. + assert result + for mood in result: + assert isinstance(mood, enums.TestMysqlTypesMood) + assert mood is model.mood + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_many_mood_iter", depends=["AsyncmyTestPydanticClasses::get_many_mood"]) + async def test_get_many_mood_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + results = [mood async for mood in queries_obj.get_many_mood(mood=model.mood)] + + assert results + for mood in results: + assert isinstance(mood, enums.TestMysqlTypesMood) + assert mood is model.mood + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::list_months", depends=["AsyncmyTestPydanticClasses::get_many_mood_iter"]) + async def test_list_months(self, queries_obj: queries.Queries) -> None: + # DATE_FORMAT contains literal % characters; regression for the percent-doubling bug. + result = await queries_obj.list_months() + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + for month in result: + assert isinstance(month, str) + assert EXPECTED_MONTH in result + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::list_months_iter", depends=["AsyncmyTestPydanticClasses::list_months"]) + async def test_list_months_iter(self, queries_obj: queries.Queries) -> None: + results = [month async for month in queries_obj.list_months()] + + assert EXPECTED_MONTH in results + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::count", depends=["AsyncmyTestPydanticClasses::list_months_iter"]) + async def test_count(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.count_mysql_types() + + assert result is not None + assert isinstance(result, int) + assert result >= 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::update_rows", depends=["AsyncmyTestPydanticClasses::count"]) + async def test_update_rows(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.update_varchar_test(varchar_test="updated varchar", id_=MODEL_ID) + + assert isinstance(result, int) + assert result == 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::update_rows_none", depends=["AsyncmyTestPydanticClasses::update_rows"]) + async def test_update_rows_none(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.update_varchar_test(varchar_test="updated varchar", id_=MISSING_ID) + + assert isinstance(result, int) + assert result == 0 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::execresult_cursor", depends=["AsyncmyTestPydanticClasses::update_rows_none"]) + async def test_execresult_cursor(self, queries_obj: queries.Queries) -> None: + cursor = await queries_obj.all_mysql_types_cursor() + + assert isinstance(cursor, asyncmy.cursors.Cursor) + rows = await cursor.fetchall() + assert any(row[0] == MODEL_ID for row in rows) + await cursor.close() + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::exec_last_id", depends=["AsyncmyTestPydanticClasses::execresult_cursor"]) + async def test_exec_last_id(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.insert_exec_last_id(name=SUITE_TAG) + + assert result is not None + assert isinstance(result, int) + # AUTO_INCREMENT counters persist across runs; never assert exact ids. + assert result > 0 + + name = await queries_obj.get_exec_last_id_name(id_=result) + assert name == SUITE_TAG + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::insert_reserved_arg", depends=["AsyncmyTestPydanticClasses::exec_last_id"]) + async def test_insert_reserved_arg(self, queries_obj: queries.Queries) -> None: + await queries_obj.insert_reserved_arg(id_=RESERVED_ID, conn=SUITE_TAG) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::get_reserved_arg", depends=["AsyncmyTestPydanticClasses::insert_reserved_arg"]) + async def test_get_reserved_arg(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.get_reserved_arg(conn=SUITE_TAG) + + assert result is not None + assert result == models.TestReservedArg(id_=RESERVED_ID, conn=SUITE_TAG) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::delete", depends=["AsyncmyTestPydanticClasses::get_reserved_arg"]) + async def test_delete(self, queries_obj: queries.Queries) -> None: + await queries_obj.delete_one_mysql_type(id_=MODEL_ID) + + result = await queries_obj.get_one_mysql_type(id_=MODEL_ID) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticClasses::insert_type_override") + async def test_insert_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + await queries_obj.insert_type_override(id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticClasses::get_type_override", + depends=["AsyncmyTestPydanticClasses::insert_type_override"], + ) + async def test_get_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = await queries_obj.get_type_override(id_=override_model.id_) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticClasses::get_type_override_none_value", + depends=["AsyncmyTestPydanticClasses::get_type_override"], + ) + async def test_get_type_override_none_value(self, queries_obj: queries.Queries) -> None: + # The UserString override column is nullable; None must round-trip too. + await queries_obj.insert_type_override(id_=OVERRIDE_NONE_ID, text_test=None) + + result = await queries_obj.get_type_override(id_=OVERRIDE_NONE_ID) + assert result is not None + assert result.text_test is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticClasses::get_type_override_missing", + depends=["AsyncmyTestPydanticClasses::get_type_override_none_value"], + ) + async def test_get_type_override_missing(self, queries_obj: queries.Queries) -> None: + result = await queries_obj.get_type_override(id_=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticClasses::cleanup", + depends=["AsyncmyTestPydanticClasses::delete", "AsyncmyTestPydanticClasses::get_type_override_missing"], + ) + async def test_cleanup(self, asyncmy_conn: asyncmy.Connection) -> None: + # Tables without generated delete queries are cleaned directly so the + # fixed ids are free for the next chain. + async with asyncmy_conn.cursor() as cur: + await cur.execute("DELETE FROM test_inner_mysql_types WHERE table_id = %s", (MODEL_ID,)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_type_override WHERE id IN (%s, %s)", (OVERRIDE_ID, OVERRIDE_NONE_ID)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_reserved_args WHERE id = %s", (RESERVED_ID,)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_execlastid WHERE name = %s", (SUITE_TAG,)) # pyright: ignore[reportUnknownMemberType] + + @pytest.mark.asyncio(loop_scope="session") + async def test_one_missing_rows_return_none(self, asyncmy_conn: asyncmy.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. + obj = queries.Queries(conn=asyncmy_conn) + assert await obj.get_one_mysql_type(id_=-1) is None + assert await obj.get_one_inner_mysql_type(table_id=-1) is None + assert await obj.get_one_date(id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert await obj.get_one_datetime(id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert await obj.get_one_time(id_=-1, time_test=datetime.timedelta()) is None + assert await obj.get_one_bool(id_=-1, tinyint1_test=False) is None + assert await obj.get_one_decimal(id_=-1, decimal_test=decimal.Decimal(0)) is None + assert await obj.get_one_blob(id_=-1, blob_test=memoryview(b"")) is None + assert await obj.get_one_bit(id_=-1) is None + assert await obj.get_one_year(id_=-1) is None + assert await obj.get_one_json(id_=-1) is None + assert await obj.get_one_mood(id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert await obj.get_one_tag(id_=-1) is None + assert await obj.get_exec_last_id_name(id_=-1) is None + assert await obj.get_type_override(id_=-1) is None + assert await obj.get_reserved_arg(conn="missing") is None + assert await obj.touch_exec_last_id(name="untouched", id_=-1) is None + + # count(*) always returns a row; its miss branch needs the stub. + stub = typing.cast("asyncmy.Connection", no_row_conn.NoRowConn()) + assert await queries.Queries(conn=stub).count_mysql_types() is None diff --git a/test/driver_asyncmy/pydantic/test_asyncmy_pydantic_functions.py b/test/driver_asyncmy/pydantic/test_asyncmy_pydantic_functions.py new file mode 100644 index 00000000..93322ce1 --- /dev/null +++ b/test/driver_asyncmy/pydantic/test_asyncmy_pydantic_functions.py @@ -0,0 +1,874 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import collections.abc +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import asyncmy +import asyncmy.cursors +import pytest + +from test.driver_asyncmy import no_row_conn +from test.driver_asyncmy.pydantic.functions import enums +from test.driver_asyncmy.pydantic.functions import models +from test.driver_asyncmy.pydantic.functions import queries + +MODEL_ID = 9151 +OVERRIDE_ID = 9251 +OVERRIDE_NONE_ID = 9252 +RESERVED_ID = 9351 +MISSING_ID = 9902 +SUITE_TAG = "asyncmy-pydantic-functions" +EXPECTED_MONTH = "2026-01" +DECIMAL_PADDED = "12.3400" +BINARY_UNPADDED = b"\xaa\xbb\xcc" +BINARY_LENGTH = 16 +BIT_BYTE = b"\x80" + + +@pytest.mark.asyncio(loop_scope="session") +class TestAsyncmyPydanticFunctions: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MODEL_ID, + int_test=42, + integer_test=-1_000_000, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_000_000_000, + bigint_unsigned_test=2**63 + 10, + year_test=2026, + tinyint1_test=True, + bool_test=False, + boolean_test=True, + float_test=3.5, + double_test=math.pi, + double_precision_test=1.41421, + real_test=math.e, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("100.10"), + char_test="CHAR10", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(BINARY_UNPADDED + b"\x00" * (BINARY_LENGTH - len(BINARY_UNPADDED))), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tinyblob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"mediumblob"), + longblob_test=memoryview(b"longblob"), + bit_test=memoryview(BIT_BYTE), + date_test=datetime.date(2026, 1, 2), + datetime_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + datetime6_test=datetime.datetime(2026, 1, 2, 3, 4, 5, 123456), + timestamp_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + time_test=datetime.timedelta(hours=13, minutes=45, seconds=30), + json_test=json.dumps({"foo": "bar", "count": 3}, separators=(",", ":")), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.GAMMA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=None, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=None, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=None, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=None, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=None, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=None, + ) + + @pytest.mark.asyncio(loop_scope="session") + async def test_enum_members(self) -> None: + assert enums.TestMysqlTypesMood.VALUE_24H.value == "24h" + assert enums.TestMysqlTypesMood.VALUE__HIDDEN.value == "_hidden" + assert enums.TestInnerMysqlTypesMood.VALUE_24H.value == "24h" + assert enums.TestMysqlTypesTag.BETA.value == "beta" + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::insert") + async def test_insert( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + await queries.insert_one_mysql_type( + conn=asyncmy_conn, + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + # char(10) strips trailing spaces on return; the model holds the stripped value. + char_test=model.char_test + " ", + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + # binary(16) is right-padded with NUL bytes on return; the model holds the padded value. + binary_test=memoryview(BINARY_UNPADDED), + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::inner_insert", depends=["AsyncmyTestPydanticFunctions::insert"]) + async def test_inner_insert( + self, + asyncmy_conn: asyncmy.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + await queries.insert_one_inner_mysql_type( + conn=asyncmy_conn, + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_one", depends=["AsyncmyTestPydanticFunctions::inner_insert"]) + async def test_get_one( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert result.tinyint1_test is True + assert result.bool_test is False + assert result.boolean_test is True + assert len(result.binary_test) == BINARY_LENGTH + assert result.char_test == model.char_test + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + assert isinstance(result.time_test, datetime.timedelta) + # MySQL normalizes JSON spacing; compare parsed values, never strings. + assert json.loads(result.json_test) == json.loads(model.json_test) + assert result == model.model_copy(update={"json_test": result.json_test}) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_one_none", depends=["AsyncmyTestPydanticFunctions::get_one"]) + async def test_get_one_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_one_inner", depends=["AsyncmyTestPydanticFunctions::get_one_none"]) + async def test_get_one_inner( + self, + asyncmy_conn: asyncmy.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result.tinyint1_test is None + assert result.bool_test is False + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_one_inner_none", depends=["AsyncmyTestPydanticFunctions::get_one_inner"]) + async def test_get_one_inner_none( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_date", depends=["AsyncmyTestPydanticFunctions::get_one_inner_none"]) + async def test_get_date( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_date(conn=asyncmy_conn, id_=MODEL_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_date_none", depends=["AsyncmyTestPydanticFunctions::get_date"]) + async def test_get_date_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_date(conn=asyncmy_conn, id_=MISSING_ID, date_test=model.date_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_datetime", depends=["AsyncmyTestPydanticFunctions::get_date_none"]) + async def test_get_datetime( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_datetime(conn=asyncmy_conn, id_=MODEL_ID, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_datetime_none", depends=["AsyncmyTestPydanticFunctions::get_datetime"]) + async def test_get_datetime_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_datetime(conn=asyncmy_conn, id_=MISSING_ID, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_time", depends=["AsyncmyTestPydanticFunctions::get_datetime_none"]) + async def test_get_time( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_time(conn=asyncmy_conn, id_=MODEL_ID, time_test=model.time_test) + + assert result is not None + # MySQL time columns map to datetime.timedelta, not datetime.time. + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_time_none", depends=["AsyncmyTestPydanticFunctions::get_time"]) + async def test_get_time_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_time(conn=asyncmy_conn, id_=MISSING_ID, time_test=model.time_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_bool", depends=["AsyncmyTestPydanticFunctions::get_time_none"]) + async def test_get_bool( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_bool(conn=asyncmy_conn, id_=MODEL_ID, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_bool_none", depends=["AsyncmyTestPydanticFunctions::get_bool"]) + async def test_get_bool_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_bool(conn=asyncmy_conn, id_=MISSING_ID, tinyint1_test=model.tinyint1_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_decimal", depends=["AsyncmyTestPydanticFunctions::get_bool_none"]) + async def test_get_decimal( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_decimal(conn=asyncmy_conn, id_=MODEL_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + # decimal(12,4) comes back padded to scale 4. + assert str(result) == DECIMAL_PADDED + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_decimal_none", depends=["AsyncmyTestPydanticFunctions::get_decimal"]) + async def test_get_decimal_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_decimal(conn=asyncmy_conn, id_=MISSING_ID, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_blob", depends=["AsyncmyTestPydanticFunctions::get_decimal_none"]) + async def test_get_blob( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_blob(conn=asyncmy_conn, id_=MODEL_ID, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_blob_none", depends=["AsyncmyTestPydanticFunctions::get_blob"]) + async def test_get_blob_none( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_blob(conn=asyncmy_conn, id_=MISSING_ID, blob_test=model.blob_test) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_bit", depends=["AsyncmyTestPydanticFunctions::get_blob_none"]) + async def test_get_bit( + self, + asyncmy_conn: asyncmy.Connection, + ) -> None: + result = await queries.get_one_bit(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + # bit(8) comes back as a one-byte memoryview. + assert isinstance(result, memoryview) + assert len(result) == 1 + assert bytes(result) == BIT_BYTE + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_year", depends=["AsyncmyTestPydanticFunctions::get_bit"]) + async def test_get_year( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_year(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_json", depends=["AsyncmyTestPydanticFunctions::get_year"]) + async def test_get_json( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_json(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, str) + # MySQL normalizes JSON spacing; compare parsed values, never strings. + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_mood", depends=["AsyncmyTestPydanticFunctions::get_json"]) + async def test_get_mood( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_mood(conn=asyncmy_conn, id_=MODEL_ID, mood=model.mood) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_tag", depends=["AsyncmyTestPydanticFunctions::get_mood"]) + async def test_get_tag( + self, + asyncmy_conn: asyncmy.Connection, + model: models.TestMysqlType, + ) -> None: + result = await queries.get_one_tag(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is model.tag + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many", depends=["AsyncmyTestPydanticFunctions::get_tag"]) + async def test_get_many(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_mysql_type(conn=asyncmy_conn, id_=MODEL_ID) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert len(result) == 1 + assert isinstance(result[0], models.TestMysqlType) + + assert result[0] == model.model_copy(update={"json_test": result[0].json_test}) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_iter", depends=["AsyncmyTestPydanticFunctions::get_many"]) + async def test_get_many_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_mysql_type(conn=asyncmy_conn, id_=MODEL_ID): + assert result is not None + assert isinstance(result, models.TestMysqlType) + + assert result == model.model_copy(update={"json_test": result.json_test}) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_inner", depends=["AsyncmyTestPydanticFunctions::get_many_iter"]) + async def test_get_many_inner(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = await queries.get_many_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], models.TestInnerMysqlType) + + assert result[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_inner_iter", depends=["AsyncmyTestPydanticFunctions::get_many_inner"]) + async def test_get_many_inner_iter(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries.get_many_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticFunctions::get_many_nullable_inner", + depends=["AsyncmyTestPydanticFunctions::get_many_inner_iter"], + ) + async def test_get_many_nullable_inner(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the query matches it via the NULL-safe <=> operator. + result = await queries.get_many_nullable_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], models.TestInnerMysqlType) + + assert result[0] == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticFunctions::get_many_nullable_inner_iter", + depends=["AsyncmyTestPydanticFunctions::get_many_nullable_inner"], + ) + async def test_get_many_nullable_inner_iter(self, asyncmy_conn: asyncmy.Connection, inner_model: models.TestInnerMysqlType) -> None: + async for result in queries.get_many_nullable_inner_mysql_type(conn=asyncmy_conn, table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + + assert result == inner_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticFunctions::get_many_date", + depends=["AsyncmyTestPydanticFunctions::get_many_nullable_inner_iter"], + ) + async def test_get_many_date(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_date(conn=asyncmy_conn, id_=MODEL_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], datetime.date) + + assert result[0] == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_date_iter", depends=["AsyncmyTestPydanticFunctions::get_many_date"]) + async def test_get_many_date_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_date(conn=asyncmy_conn, id_=MODEL_ID, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + + assert result == model.date_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_time", depends=["AsyncmyTestPydanticFunctions::get_many_date_iter"]) + async def test_get_many_time(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_time(conn=asyncmy_conn, id_=MODEL_ID, time_test=model.time_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], datetime.timedelta) + + assert result[0] == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_time_iter", depends=["AsyncmyTestPydanticFunctions::get_many_time"]) + async def test_get_many_time_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_time(conn=asyncmy_conn, id_=MODEL_ID, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + + assert result == model.time_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_bool", depends=["AsyncmyTestPydanticFunctions::get_many_time_iter"]) + async def test_get_many_bool(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_bool(conn=asyncmy_conn, id_=MODEL_ID, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], bool) + + assert result[0] is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_bool_iter", depends=["AsyncmyTestPydanticFunctions::get_many_bool"]) + async def test_get_many_bool_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_bool(conn=asyncmy_conn, id_=MODEL_ID, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + + assert result is True + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_decimal", depends=["AsyncmyTestPydanticFunctions::get_many_bool_iter"]) + async def test_get_many_decimal(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_decimal(conn=asyncmy_conn, id_=MODEL_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + assert isinstance(result[0], decimal.Decimal) + + assert str(result[0]) == DECIMAL_PADDED + assert result[0] == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticFunctions::get_many_decimal_iter", + depends=["AsyncmyTestPydanticFunctions::get_many_decimal"], + ) + async def test_get_many_decimal_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + async for result in queries.get_many_decimal(conn=asyncmy_conn, id_=MODEL_ID, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + + assert result == model.decimal_test + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_mood", depends=["AsyncmyTestPydanticFunctions::get_many_decimal_iter"]) + async def test_get_many_mood(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + result = await queries.get_many_mood(conn=asyncmy_conn, mood=model.mood) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + # The query is not id-filtered; other rows may exist, so assert containment. + assert result + for mood in result: + assert isinstance(mood, enums.TestMysqlTypesMood) + assert mood is model.mood + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_many_mood_iter", depends=["AsyncmyTestPydanticFunctions::get_many_mood"]) + async def test_get_many_mood_iter(self, asyncmy_conn: asyncmy.Connection, model: models.TestMysqlType) -> None: + results = [mood async for mood in queries.get_many_mood(conn=asyncmy_conn, mood=model.mood)] + + assert results + for mood in results: + assert isinstance(mood, enums.TestMysqlTypesMood) + assert mood is model.mood + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::list_months", depends=["AsyncmyTestPydanticFunctions::get_many_mood_iter"]) + async def test_list_months(self, asyncmy_conn: asyncmy.Connection) -> None: + # DATE_FORMAT contains literal % characters; regression for the percent-doubling bug. + result = await queries.list_months(conn=asyncmy_conn) + + assert result is not None + assert isinstance(result, collections.abc.Sequence) + for month in result: + assert isinstance(month, str) + assert EXPECTED_MONTH in result + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::list_months_iter", depends=["AsyncmyTestPydanticFunctions::list_months"]) + async def test_list_months_iter(self, asyncmy_conn: asyncmy.Connection) -> None: + results = [month async for month in queries.list_months(conn=asyncmy_conn)] + + assert EXPECTED_MONTH in results + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::count", depends=["AsyncmyTestPydanticFunctions::list_months_iter"]) + async def test_count(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.count_mysql_types(conn=asyncmy_conn) + + assert result is not None + assert isinstance(result, int) + assert result >= 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::update_rows", depends=["AsyncmyTestPydanticFunctions::count"]) + async def test_update_rows(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.update_varchar_test(conn=asyncmy_conn, varchar_test="updated varchar", id_=MODEL_ID) + + assert isinstance(result, int) + assert result == 1 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::update_rows_none", depends=["AsyncmyTestPydanticFunctions::update_rows"]) + async def test_update_rows_none(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.update_varchar_test(conn=asyncmy_conn, varchar_test="updated varchar", id_=MISSING_ID) + + assert isinstance(result, int) + assert result == 0 + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::execresult_cursor", depends=["AsyncmyTestPydanticFunctions::update_rows_none"]) + async def test_execresult_cursor(self, asyncmy_conn: asyncmy.Connection) -> None: + cursor = await queries.all_mysql_types_cursor(conn=asyncmy_conn) + + assert isinstance(cursor, asyncmy.cursors.Cursor) + rows = await cursor.fetchall() + assert any(row[0] == MODEL_ID for row in rows) + await cursor.close() + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::exec_last_id", depends=["AsyncmyTestPydanticFunctions::execresult_cursor"]) + async def test_exec_last_id(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.insert_exec_last_id(conn=asyncmy_conn, name=SUITE_TAG) + + assert result is not None + assert isinstance(result, int) + # AUTO_INCREMENT counters persist across runs; never assert exact ids. + assert result > 0 + + name = await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=result) + assert name == SUITE_TAG + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::insert_reserved_arg", depends=["AsyncmyTestPydanticFunctions::exec_last_id"]) + async def test_insert_reserved_arg(self, asyncmy_conn: asyncmy.Connection) -> None: + await queries.insert_reserved_arg(conn=asyncmy_conn, id_=RESERVED_ID, conn_2=SUITE_TAG) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::get_reserved_arg", depends=["AsyncmyTestPydanticFunctions::insert_reserved_arg"]) + async def test_get_reserved_arg(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.get_reserved_arg(conn=asyncmy_conn, conn_2=SUITE_TAG) + + assert result is not None + assert result == models.TestReservedArg(id_=RESERVED_ID, conn=SUITE_TAG) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::delete", depends=["AsyncmyTestPydanticFunctions::get_reserved_arg"]) + async def test_delete(self, asyncmy_conn: asyncmy.Connection) -> None: + await queries.delete_one_mysql_type(conn=asyncmy_conn, id_=MODEL_ID) + + result = await queries.get_one_mysql_type(conn=asyncmy_conn, id_=MODEL_ID) + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency(name="AsyncmyTestPydanticFunctions::insert_type_override") + async def test_insert_type_override(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + await queries.insert_type_override(conn=asyncmy_conn, id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticFunctions::get_type_override", + depends=["AsyncmyTestPydanticFunctions::insert_type_override"], + ) + async def test_get_type_override(self, asyncmy_conn: asyncmy.Connection, override_model: models.TestTypeOverride) -> None: + result = await queries.get_type_override(conn=asyncmy_conn, id_=override_model.id_) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticFunctions::get_type_override_none_value", + depends=["AsyncmyTestPydanticFunctions::get_type_override"], + ) + async def test_get_type_override_none_value(self, asyncmy_conn: asyncmy.Connection) -> None: + # The UserString override column is nullable; None must round-trip too. + await queries.insert_type_override(conn=asyncmy_conn, id_=OVERRIDE_NONE_ID, text_test=None) + + result = await queries.get_type_override(conn=asyncmy_conn, id_=OVERRIDE_NONE_ID) + assert result is not None + assert result.text_test is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticFunctions::get_type_override_missing", + depends=["AsyncmyTestPydanticFunctions::get_type_override_none_value"], + ) + async def test_get_type_override_missing(self, asyncmy_conn: asyncmy.Connection) -> None: + result = await queries.get_type_override(conn=asyncmy_conn, id_=MISSING_ID) + + assert result is None + + @pytest.mark.asyncio(loop_scope="session") + @pytest.mark.dependency( + name="AsyncmyTestPydanticFunctions::cleanup", + depends=["AsyncmyTestPydanticFunctions::delete", "AsyncmyTestPydanticFunctions::get_type_override_missing"], + ) + async def test_cleanup(self, asyncmy_conn: asyncmy.Connection) -> None: + # Tables without generated delete queries are cleaned directly so the + # fixed ids are free for the next chain. + async with asyncmy_conn.cursor() as cur: + await cur.execute("DELETE FROM test_inner_mysql_types WHERE table_id = %s", (MODEL_ID,)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_type_override WHERE id IN (%s, %s)", (OVERRIDE_ID, OVERRIDE_NONE_ID)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_reserved_args WHERE id = %s", (RESERVED_ID,)) # pyright: ignore[reportUnknownMemberType] + await cur.execute("DELETE FROM test_execlastid WHERE name = %s", (SUITE_TAG,)) # pyright: ignore[reportUnknownMemberType] + + @pytest.mark.asyncio(loop_scope="session") + async def test_one_missing_rows_return_none(self, asyncmy_conn: asyncmy.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. + assert await queries.get_one_mysql_type(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_inner_mysql_type(conn=asyncmy_conn, table_id=-1) is None + assert await queries.get_one_date(conn=asyncmy_conn, id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert await queries.get_one_datetime(conn=asyncmy_conn, id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert await queries.get_one_time(conn=asyncmy_conn, id_=-1, time_test=datetime.timedelta()) is None + assert await queries.get_one_bool(conn=asyncmy_conn, id_=-1, tinyint1_test=False) is None + assert await queries.get_one_decimal(conn=asyncmy_conn, id_=-1, decimal_test=decimal.Decimal(0)) is None + assert await queries.get_one_blob(conn=asyncmy_conn, id_=-1, blob_test=memoryview(b"")) is None + assert await queries.get_one_bit(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_year(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_json(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_one_mood(conn=asyncmy_conn, id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert await queries.get_one_tag(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_exec_last_id_name(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_type_override(conn=asyncmy_conn, id_=-1) is None + assert await queries.get_reserved_arg(conn=asyncmy_conn, conn_2="missing") is None + assert await queries.touch_exec_last_id(conn=asyncmy_conn, name="untouched", id_=-1) is None + + # count(*) always returns a row; its miss branch needs the stub. + stub = typing.cast("asyncmy.Connection", no_row_conn.NoRowConn()) + assert await queries.count_mysql_types(conn=stub) is None diff --git a/test/driver_asyncmy/queries.sql b/test/driver_asyncmy/queries.sql new file mode 100644 index 00000000..182f9bdb --- /dev/null +++ b/test/driver_asyncmy/queries.sql @@ -0,0 +1,146 @@ +-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, + ?, ?, ?, + ?, ?, ?, ?, + ?, ?, + ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ? + ); + +-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, + ?, ?, ?, + ?, ?, ?, ?, + ?, ?, + ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ? + ); + +-- name: GetOneMysqlType :one +SELECT * FROM test_mysql_types WHERE id = ?; + +-- name: GetOneInnerMysqlType :one +SELECT * FROM test_inner_mysql_types WHERE table_id = ?; + +-- name: GetManyMysqlType :many +SELECT * FROM test_mysql_types WHERE id = ?; + +-- name: GetManyInnerMysqlType :many +SELECT * FROM test_inner_mysql_types WHERE table_id = ?; + +-- name: GetManyNullableInnerMysqlType :many +SELECT * FROM test_inner_mysql_types WHERE table_id = ? AND int_test <=> ?; + +-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = ? AND date_test = ?; + +-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = ? AND datetime_test = ?; + +-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = ? AND time_test = ?; + +-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = ? AND tinyint1_test = ?; + +-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = ? AND decimal_test = ?; + +-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = ? AND blob_test = ?; + +-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = ?; + +-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = ?; + +-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = ?; + +-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = ? AND mood = ?; + +-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = ?; + +-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = ? AND date_test = ?; + +-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = ? AND time_test = ?; + +-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = ? AND tinyint1_test = ?; + +-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = ? AND decimal_test = ?; + +-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = ? ORDER BY id; + +-- Parameterless :many with literal percents: QueryResults always passes its +-- args tuple, so the constant must arrive with doubled "%%". +-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%Y-%m') AS month FROM test_mysql_types ORDER BY id; + +-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types; + +-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = ? WHERE id = ?; + +-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = ?; + +-- name: AllMysqlTypesCursor :execresult +SELECT * FROM test_mysql_types; + +-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (?); + +-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = ?; + +-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (?, ?); + +-- name: GetTypeOverride :one +SELECT * FROM test_type_override WHERE id = ?; + +-- name: GetReservedArg :one +SELECT * FROM test_reserved_args WHERE conn = ?; + +-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (?, ?); + +-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = ? WHERE id = ?; diff --git a/test/driver_asyncmy/queries_slice.sql b/test/driver_asyncmy/queries_slice.sql new file mode 100644 index 00000000..46cab028 --- /dev/null +++ b/test/driver_asyncmy/queries_slice.sql @@ -0,0 +1,23 @@ +-- name: InsertSliceRow :exec +INSERT INTO test_slice (id, name, note) VALUES (?, ?, ?); + +-- name: GetSliceRows :many +SELECT * FROM test_slice WHERE id IN (sqlc.slice('ids')) ORDER BY id; + +-- name: GetSliceRowFiltered :one +SELECT * FROM test_slice WHERE name = ? AND id IN (sqlc.slice('ids')) AND id != ? LIMIT 1; + +-- name: GetSliceRowsByNotes :many +SELECT * FROM test_slice WHERE note IN (sqlc.slice('notes')) ORDER BY id; + +-- name: GetFirstSliceName :one +SELECT name FROM test_slice WHERE id IN (sqlc.slice('ids')) OR name IN (sqlc.slice('names')) ORDER BY id LIMIT 1; + +-- name: GetSliceRowsByNameOrNote :many +SELECT * FROM test_slice WHERE name IN (sqlc.slice('names')) OR note IN (sqlc.slice('names')) ORDER BY id; + +-- name: GetSliceRowsByNameOrNoteFiltered :many +SELECT * FROM test_slice WHERE name IN (sqlc.slice('names')) AND id != ? OR note IN (sqlc.slice('names')) ORDER BY id; + +-- name: DeleteSliceRows :execrows +DELETE FROM test_slice WHERE id IN (sqlc.slice('ids')); diff --git a/test/driver_asyncmy/schema.sql b/test/driver_asyncmy/schema.sql new file mode 100644 index 00000000..c59ba0d8 --- /dev/null +++ b/test/driver_asyncmy/schema.sql @@ -0,0 +1,189 @@ +CREATE TABLE IF NOT EXISTS test_mysql_types +( + /* ------------- Integer family ------------- */ + id bigint PRIMARY KEY NOT NULL, + int_test int NOT NULL, + integer_test integer NOT NULL, + mediumint_test mediumint NOT NULL, + smallint_test smallint NOT NULL, + tinyint_test tinyint NOT NULL, -- plain tinyint stays int + bigint_test bigint NOT NULL, + int_unsigned_test int unsigned NOT NULL, + bigint_unsigned_test bigint unsigned NOT NULL, + year_test year NOT NULL, + /* ------------- Boolean (tinyint(1) and its aliases) ------------- */ + tinyint1_test tinyint(1) NOT NULL, + bool_test bool NOT NULL, + boolean_test boolean NOT NULL, + /* ------------- Floating-point ------------- */ + float_test float NOT NULL, + double_test double NOT NULL, + double_precision_test double precision NOT NULL, + real_test real NOT NULL, + /* ------------- Exact numeric (decimal) ------------- */ + decimal_test decimal(12,4) NOT NULL, + numeric_test numeric(10,2) NOT NULL, + /* ------------- Character / text ------------- */ + char_test char(10) NOT NULL, + varchar_test varchar(255) NOT NULL, + tinytext_test tinytext NOT NULL, + text_test text NOT NULL, + mediumtext_test mediumtext NOT NULL, + longtext_test longtext NOT NULL, + /* ------------- Binary ------------- */ + binary_test binary(16) NOT NULL, + varbinary_test varbinary(255) NOT NULL, + tinyblob_test tinyblob NOT NULL, + blob_test blob NOT NULL, + mediumblob_test mediumblob NOT NULL, + longblob_test longblob NOT NULL, + bit_test bit(8) NOT NULL, + /* ------------- Date & time (time maps to timedelta) ------------- */ + date_test date NOT NULL, + datetime_test datetime NOT NULL, + datetime6_test datetime(6) NOT NULL, + timestamp_test timestamp NOT NULL, + time_test time NOT NULL, + /* ------------- JSON (kept as str) ------------- */ + json_test json NOT NULL, + /* ------------- Inline enum and set ------------- */ + -- '24h' and '_hidden' pin the digit- and underscore-leading constant + -- names of the synthesized test_mysql_types_mood enum class. + mood enum('sad','ok','happy','24h','_hidden') NOT NULL, + -- SET columns become StrEnums like enum columns (sqlc materializes + -- both). Only single-valued sets round-trip, see the docs. + tag set('alpha','beta','gamma') NOT NULL +); + +CREATE TABLE IF NOT EXISTS test_inner_mysql_types +( + table_id bigint PRIMARY KEY NOT NULL, + int_test int, + integer_test integer, + mediumint_test mediumint, + smallint_test smallint, + tinyint_test tinyint, + bigint_test bigint, + int_unsigned_test int unsigned, + bigint_unsigned_test bigint unsigned, + year_test year, + tinyint1_test tinyint(1), + bool_test bool, + boolean_test boolean, + float_test float, + double_test double, + double_precision_test double precision, + real_test real, + decimal_test decimal(12,4), + numeric_test numeric(10,2), + char_test char(10), + varchar_test varchar(255), + tinytext_test tinytext, + text_test text, + mediumtext_test mediumtext, + longtext_test longtext, + binary_test binary(16), + varbinary_test varbinary(255), + tinyblob_test tinyblob, + blob_test blob, + mediumblob_test mediumblob, + longblob_test longblob, + bit_test bit(8), + date_test date, + datetime_test datetime, + datetime6_test datetime(6), + timestamp_test timestamp, + time_test time, + json_test json, + mood enum('sad','ok','happy','24h','_hidden'), + tag set('alpha','beta','gamma') +); + +CREATE TABLE IF NOT EXISTS test_type_override +( + id bigint PRIMARY KEY NOT NULL, + text_test text +); + +-- Enum column with a py_type override: the override wins over the +-- synthesized enum class, and parameters convert back through it. +CREATE TABLE IF NOT EXISTS test_enum_override +( + id bigint PRIMARY KEY NOT NULL, + mood_test enum('sad','ok','happy') NOT NULL +); + +-- Uppercase type names and precision variants exercise the SQL-type +-- normalization. The version-comment query in queries_case.sql lives on +-- this table too. +CREATE TABLE IF NOT EXISTS test_case_sensitivity +( + id bigint PRIMARY KEY NOT NULL, + upper_dt DATETIME NOT NULL, + prec_dec DECIMAL(10,2) NOT NULL +); + +-- A column named like the implicit first argument of generated functions. +CREATE TABLE IF NOT EXISTS test_reserved_args +( + id bigint PRIMARY KEY NOT NULL, + conn varchar(64) NOT NULL +); + +-- :execlastid reads cursor.lastrowid from the AUTO_INCREMENT key. serial +-- is the bigint unsigned AUTO_INCREMENT alias. +CREATE TABLE IF NOT EXISTS test_execlastid +( + id serial PRIMARY KEY, + name varchar(64) NOT NULL +); + +-- Plural column name: field names must NOT be singularized (only table +-- names and embed fields are). Ported from PR 164. +CREATE TABLE IF NOT EXISTS test_field_namings +( + id bigint PRIMARY KEY NOT NULL, + outputs json NOT NULL +); + +-- Backtick-quoted identifiers that are not valid Python names (issue 160). +CREATE TABLE IF NOT EXISTS test_invalid_identifiers +( + id bigint PRIMARY KEY NOT NULL, + `3p%` text, + `new notes` text NOT NULL, + `%pct` text +); + +-- Digit-leading table name: the class gets a Model prefix (Model3RdPartyStat). +CREATE TABLE IF NOT EXISTS `3rd_party_stats` +( + id bigint PRIMARY KEY NOT NULL, + total bigint NOT NULL +); + +-- Variable-length IN lists via sqlc.slice: the /*SLICE:name*/ placeholder in +-- the SQL constant is expanded at call time, one "%s" per element. +CREATE TABLE IF NOT EXISTS test_slice +( + id bigint PRIMARY KEY NOT NULL, + name varchar(64) NOT NULL, + note varchar(64) +); + +CREATE TABLE IF NOT EXISTS test_converters +( + id bigint PRIMARY KEY NOT NULL, + prefs json NOT NULL, + maybe_prefs json, + tags text NOT NULL +); + +-- db_type override target: DATETIME must match the normalized type name +-- case-insensitively, through a converter (postgres parity). Isolated in +-- its own module so the main matrix's datetime columns stay untouched. +CREATE TABLE IF NOT EXISTS test_dbtype_override +( + id bigint PRIMARY KEY NOT NULL, + happened_at datetime NOT NULL +); diff --git a/test/driver_asyncmy/sqlc-gen-better-python.wasm b/test/driver_asyncmy/sqlc-gen-better-python.wasm new file mode 100755 index 00000000..dfb69b04 Binary files /dev/null and b/test/driver_asyncmy/sqlc-gen-better-python.wasm differ diff --git a/test/driver_asyncmy/sqlc.yaml b/test/driver_asyncmy/sqlc.yaml new file mode 100644 index 00000000..ed9e6101 --- /dev/null +++ b/test/driver_asyncmy/sqlc.yaml @@ -0,0 +1,200 @@ +version: "2" +plugins: + - name: python + wasm: + url: file://sqlc-gen-better-python.wasm + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 +sql: + - schema: schema.sql + queries: + - queries.sql + engine: mysql + codegen: + - out: /attrs/classes + plugin: python + options: + package: test.driver_asyncmy.attrs.classes + sql_driver: asyncmy + model_type: attrs + emit_classes: true + omit_unused_models: false + emit_init_file: true + docstrings: numpy + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + engine: mysql + codegen: + - out: /attrs/functions + plugin: python + options: + package: test.driver_asyncmy.attrs.functions + sql_driver: asyncmy + model_type: attrs + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: numpy + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + engine: mysql + codegen: + - out: /dataclass/classes + plugin: python + options: + package: test.driver_asyncmy.dataclass.classes + sql_driver: asyncmy + model_type: dataclass + emit_classes: true + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + - queries_slice.sql + engine: mysql + codegen: + - out: /dataclass/functions + plugin: python + options: + package: test.driver_asyncmy.dataclass.functions + sql_driver: asyncmy + model_type: dataclass + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + engine: mysql + codegen: + - out: /msgspec/classes + plugin: python + options: + package: test.driver_asyncmy.msgspec.classes + sql_driver: asyncmy + model_type: msgspec + emit_classes: true + omit_unused_models: true + emit_init_file: true + docstrings: pep257 + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + engine: mysql + codegen: + - out: /msgspec/functions + plugin: python + options: + package: test.driver_asyncmy.msgspec.functions + sql_driver: asyncmy + model_type: msgspec + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: pep257 + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + engine: mysql + codegen: + - out: /pydantic/classes + plugin: python + options: + package: test.driver_asyncmy.pydantic.classes + sql_driver: asyncmy + model_type: pydantic + emit_classes: true + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + engine: mysql + codegen: + - out: /pydantic/functions + plugin: python + options: + package: test.driver_asyncmy.pydantic.functions + sql_driver: asyncmy + model_type: pydantic + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str diff --git a/test/driver_asyncpg/attrs/classes/__init__.py b/test/driver_asyncpg/attrs/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/attrs/classes/__init__.py +++ b/test/driver_asyncpg/attrs/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/attrs/classes/enums.py b/test/driver_asyncpg/attrs/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_asyncpg/attrs/classes/enums.py +++ b/test/driver_asyncpg/attrs/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/attrs/classes/models.py b/test/driver_asyncpg/attrs/classes/models.py index ed7500e6..434bfef7 100644 --- a/test/driver_asyncpg/attrs/classes/models.py +++ b/test/driver_asyncpg/attrs/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/attrs/classes/queries.py b/test/driver_asyncpg/attrs/classes/queries.py index a1bff645..10055cc1 100644 --- a/test/driver_asyncpg/attrs/classes/queries.py +++ b/test/driver_asyncpg/attrs/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_asyncpg/attrs/classes/queries_copy_override.py b/test/driver_asyncpg/attrs/classes/queries_copy_override.py index 6d15bdb5..c21267a1 100644 --- a/test/driver_asyncpg/attrs/classes/queries_copy_override.py +++ b/test/driver_asyncpg/attrs/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_asyncpg/attrs/classes/queries_enum_override.py b/test/driver_asyncpg/attrs/classes/queries_enum_override.py index f888139b..b79e10de 100644 --- a/test/driver_asyncpg/attrs/classes/queries_enum_override.py +++ b/test/driver_asyncpg/attrs/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/attrs/classes/queries_field_namings.py b/test/driver_asyncpg/attrs/classes/queries_field_namings.py index b870049e..29f72caa 100644 --- a/test/driver_asyncpg/attrs/classes/queries_field_namings.py +++ b/test/driver_asyncpg/attrs/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_asyncpg/attrs/classes/queries_invalid_identifiers.py b/test/driver_asyncpg/attrs/classes/queries_invalid_identifiers.py index 3cfb045e..4ba21c01 100644 --- a/test/driver_asyncpg/attrs/classes/queries_invalid_identifiers.py +++ b/test/driver_asyncpg/attrs/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_asyncpg/attrs/functions/__init__.py b/test/driver_asyncpg/attrs/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/attrs/functions/__init__.py +++ b/test/driver_asyncpg/attrs/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/attrs/functions/enums.py b/test/driver_asyncpg/attrs/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_asyncpg/attrs/functions/enums.py +++ b/test/driver_asyncpg/attrs/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/attrs/functions/models.py b/test/driver_asyncpg/attrs/functions/models.py index 967d85bc..1d05016a 100644 --- a/test/driver_asyncpg/attrs/functions/models.py +++ b/test/driver_asyncpg/attrs/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/attrs/functions/queries.py b/test/driver_asyncpg/attrs/functions/queries.py index 22d4afbd..10f281e5 100644 --- a/test/driver_asyncpg/attrs/functions/queries.py +++ b/test/driver_asyncpg/attrs/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_asyncpg/attrs/functions/queries_copy_override.py b/test/driver_asyncpg/attrs/functions/queries_copy_override.py index 9e8bc7aa..9568ed87 100644 --- a/test/driver_asyncpg/attrs/functions/queries_copy_override.py +++ b/test/driver_asyncpg/attrs/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_asyncpg/attrs/functions/queries_enum_override.py b/test/driver_asyncpg/attrs/functions/queries_enum_override.py index df9f6cb5..d301a279 100644 --- a/test/driver_asyncpg/attrs/functions/queries_enum_override.py +++ b/test/driver_asyncpg/attrs/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/attrs/functions/queries_field_namings.py b/test/driver_asyncpg/attrs/functions/queries_field_namings.py index 62caee07..33ca1d20 100644 --- a/test/driver_asyncpg/attrs/functions/queries_field_namings.py +++ b/test/driver_asyncpg/attrs/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_asyncpg/attrs/functions/queries_invalid_identifiers.py b/test/driver_asyncpg/attrs/functions/queries_invalid_identifiers.py index 2b3f1e00..48842727 100644 --- a/test/driver_asyncpg/attrs/functions/queries_invalid_identifiers.py +++ b/test/driver_asyncpg/attrs/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_asyncpg/dataclass/classes/__init__.py b/test/driver_asyncpg/dataclass/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/dataclass/classes/__init__.py +++ b/test/driver_asyncpg/dataclass/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/dataclass/classes/enums.py b/test/driver_asyncpg/dataclass/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_asyncpg/dataclass/classes/enums.py +++ b/test/driver_asyncpg/dataclass/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/dataclass/classes/models.py b/test/driver_asyncpg/dataclass/classes/models.py index cc124e6f..06a286bd 100644 --- a/test/driver_asyncpg/dataclass/classes/models.py +++ b/test/driver_asyncpg/dataclass/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/dataclass/classes/queries.py b/test/driver_asyncpg/dataclass/classes/queries.py index 89b879e1..3a83acb7 100644 --- a/test/driver_asyncpg/dataclass/classes/queries.py +++ b/test/driver_asyncpg/dataclass/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_asyncpg/dataclass/classes/queries_copy_override.py b/test/driver_asyncpg/dataclass/classes/queries_copy_override.py index 8e96f06e..a2680ac8 100644 --- a/test/driver_asyncpg/dataclass/classes/queries_copy_override.py +++ b/test/driver_asyncpg/dataclass/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_asyncpg/dataclass/classes/queries_enum_override.py b/test/driver_asyncpg/dataclass/classes/queries_enum_override.py index fa15f100..e5421a7d 100644 --- a/test/driver_asyncpg/dataclass/classes/queries_enum_override.py +++ b/test/driver_asyncpg/dataclass/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/dataclass/classes/queries_field_namings.py b/test/driver_asyncpg/dataclass/classes/queries_field_namings.py index d9a56c7d..6ee58fd2 100644 --- a/test/driver_asyncpg/dataclass/classes/queries_field_namings.py +++ b/test/driver_asyncpg/dataclass/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_asyncpg/dataclass/classes/queries_invalid_identifiers.py b/test/driver_asyncpg/dataclass/classes/queries_invalid_identifiers.py index 4758689f..5575b7da 100644 --- a/test/driver_asyncpg/dataclass/classes/queries_invalid_identifiers.py +++ b/test/driver_asyncpg/dataclass/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_asyncpg/dataclass/functions/__init__.py b/test/driver_asyncpg/dataclass/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/dataclass/functions/__init__.py +++ b/test/driver_asyncpg/dataclass/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/dataclass/functions/enums.py b/test/driver_asyncpg/dataclass/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_asyncpg/dataclass/functions/enums.py +++ b/test/driver_asyncpg/dataclass/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/dataclass/functions/models.py b/test/driver_asyncpg/dataclass/functions/models.py index f01791aa..1307004a 100644 --- a/test/driver_asyncpg/dataclass/functions/models.py +++ b/test/driver_asyncpg/dataclass/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/dataclass/functions/queries.py b/test/driver_asyncpg/dataclass/functions/queries.py index 0160afb1..b9253ff7 100644 --- a/test/driver_asyncpg/dataclass/functions/queries.py +++ b/test/driver_asyncpg/dataclass/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_asyncpg/dataclass/functions/queries_converters.py b/test/driver_asyncpg/dataclass/functions/queries_converters.py index 1bb497c6..37389e71 100644 --- a/test/driver_asyncpg/dataclass/functions/queries_converters.py +++ b/test/driver_asyncpg/dataclass/functions/queries_converters.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_converters.sql """Module containing queries from file queries_converters.sql.""" diff --git a/test/driver_asyncpg/dataclass/functions/queries_copy_override.py b/test/driver_asyncpg/dataclass/functions/queries_copy_override.py index ef3af6f0..58e41b4d 100644 --- a/test/driver_asyncpg/dataclass/functions/queries_copy_override.py +++ b/test/driver_asyncpg/dataclass/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_asyncpg/dataclass/functions/queries_enum_override.py b/test/driver_asyncpg/dataclass/functions/queries_enum_override.py index fa9c14b4..f4a1f1cc 100644 --- a/test/driver_asyncpg/dataclass/functions/queries_enum_override.py +++ b/test/driver_asyncpg/dataclass/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/dataclass/functions/queries_field_namings.py b/test/driver_asyncpg/dataclass/functions/queries_field_namings.py index 93a572d9..e36c7826 100644 --- a/test/driver_asyncpg/dataclass/functions/queries_field_namings.py +++ b/test/driver_asyncpg/dataclass/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_asyncpg/dataclass/functions/queries_invalid_identifiers.py b/test/driver_asyncpg/dataclass/functions/queries_invalid_identifiers.py index 654f5267..0b34a0a3 100644 --- a/test/driver_asyncpg/dataclass/functions/queries_invalid_identifiers.py +++ b/test/driver_asyncpg/dataclass/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_asyncpg/msgspec/classes/__init__.py b/test/driver_asyncpg/msgspec/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/msgspec/classes/__init__.py +++ b/test/driver_asyncpg/msgspec/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/msgspec/classes/enums.py b/test/driver_asyncpg/msgspec/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_asyncpg/msgspec/classes/enums.py +++ b/test/driver_asyncpg/msgspec/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/msgspec/classes/models.py b/test/driver_asyncpg/msgspec/classes/models.py index b08aea09..60ea6b57 100644 --- a/test/driver_asyncpg/msgspec/classes/models.py +++ b/test/driver_asyncpg/msgspec/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/msgspec/classes/queries.py b/test/driver_asyncpg/msgspec/classes/queries.py index 829f5630..d00af898 100644 --- a/test/driver_asyncpg/msgspec/classes/queries.py +++ b/test/driver_asyncpg/msgspec/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_asyncpg/msgspec/classes/queries_copy_override.py b/test/driver_asyncpg/msgspec/classes/queries_copy_override.py index dcb38119..8f9bb687 100644 --- a/test/driver_asyncpg/msgspec/classes/queries_copy_override.py +++ b/test/driver_asyncpg/msgspec/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_asyncpg/msgspec/classes/queries_enum_override.py b/test/driver_asyncpg/msgspec/classes/queries_enum_override.py index dd154109..7f612d1a 100644 --- a/test/driver_asyncpg/msgspec/classes/queries_enum_override.py +++ b/test/driver_asyncpg/msgspec/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/msgspec/classes/queries_field_namings.py b/test/driver_asyncpg/msgspec/classes/queries_field_namings.py index 6295e591..3f15a580 100644 --- a/test/driver_asyncpg/msgspec/classes/queries_field_namings.py +++ b/test/driver_asyncpg/msgspec/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_asyncpg/msgspec/classes/queries_invalid_identifiers.py b/test/driver_asyncpg/msgspec/classes/queries_invalid_identifiers.py index 37388753..f0671f8d 100644 --- a/test/driver_asyncpg/msgspec/classes/queries_invalid_identifiers.py +++ b/test/driver_asyncpg/msgspec/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_asyncpg/msgspec/functions/__init__.py b/test/driver_asyncpg/msgspec/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/msgspec/functions/__init__.py +++ b/test/driver_asyncpg/msgspec/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/msgspec/functions/enums.py b/test/driver_asyncpg/msgspec/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_asyncpg/msgspec/functions/enums.py +++ b/test/driver_asyncpg/msgspec/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/msgspec/functions/models.py b/test/driver_asyncpg/msgspec/functions/models.py index 410a9888..dabde22d 100644 --- a/test/driver_asyncpg/msgspec/functions/models.py +++ b/test/driver_asyncpg/msgspec/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/msgspec/functions/queries.py b/test/driver_asyncpg/msgspec/functions/queries.py index 05332353..65d0f33e 100644 --- a/test/driver_asyncpg/msgspec/functions/queries.py +++ b/test/driver_asyncpg/msgspec/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_asyncpg/msgspec/functions/queries_copy_override.py b/test/driver_asyncpg/msgspec/functions/queries_copy_override.py index 49bf056e..006ecbc2 100644 --- a/test/driver_asyncpg/msgspec/functions/queries_copy_override.py +++ b/test/driver_asyncpg/msgspec/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_asyncpg/msgspec/functions/queries_enum_override.py b/test/driver_asyncpg/msgspec/functions/queries_enum_override.py index 500be9ed..8cdacfce 100644 --- a/test/driver_asyncpg/msgspec/functions/queries_enum_override.py +++ b/test/driver_asyncpg/msgspec/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/msgspec/functions/queries_field_namings.py b/test/driver_asyncpg/msgspec/functions/queries_field_namings.py index a71ba21a..6f92dcba 100644 --- a/test/driver_asyncpg/msgspec/functions/queries_field_namings.py +++ b/test/driver_asyncpg/msgspec/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_asyncpg/msgspec/functions/queries_invalid_identifiers.py b/test/driver_asyncpg/msgspec/functions/queries_invalid_identifiers.py index 3af106be..1930b171 100644 --- a/test/driver_asyncpg/msgspec/functions/queries_invalid_identifiers.py +++ b/test/driver_asyncpg/msgspec/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_asyncpg/omit_tc/classes/__init__.py b/test/driver_asyncpg/omit_tc/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/omit_tc/classes/__init__.py +++ b/test/driver_asyncpg/omit_tc/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/omit_tc/classes/enums.py b/test/driver_asyncpg/omit_tc/classes/enums.py index 4fd9220a..8dc5439d 100644 --- a/test/driver_asyncpg/omit_tc/classes/enums.py +++ b/test/driver_asyncpg/omit_tc/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/omit_tc/classes/models.py b/test/driver_asyncpg/omit_tc/classes/models.py index 41728279..35c76305 100644 --- a/test/driver_asyncpg/omit_tc/classes/models.py +++ b/test/driver_asyncpg/omit_tc/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/omit_tc/classes/queries_enum_override.py b/test/driver_asyncpg/omit_tc/classes/queries_enum_override.py index 5b17759e..c05e9076 100644 --- a/test/driver_asyncpg/omit_tc/classes/queries_enum_override.py +++ b/test/driver_asyncpg/omit_tc/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/omit_tc/functions/__init__.py b/test/driver_asyncpg/omit_tc/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/omit_tc/functions/__init__.py +++ b/test/driver_asyncpg/omit_tc/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/omit_tc/functions/enums.py b/test/driver_asyncpg/omit_tc/functions/enums.py index 4fd9220a..8dc5439d 100644 --- a/test/driver_asyncpg/omit_tc/functions/enums.py +++ b/test/driver_asyncpg/omit_tc/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/omit_tc/functions/models.py b/test/driver_asyncpg/omit_tc/functions/models.py index 41728279..35c76305 100644 --- a/test/driver_asyncpg/omit_tc/functions/models.py +++ b/test/driver_asyncpg/omit_tc/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/omit_tc/functions/queries_enum_override.py b/test/driver_asyncpg/omit_tc/functions/queries_enum_override.py index 766508ef..0f8bf8df 100644 --- a/test/driver_asyncpg/omit_tc/functions/queries_enum_override.py +++ b/test/driver_asyncpg/omit_tc/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/pydantic/classes/__init__.py b/test/driver_asyncpg/pydantic/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/pydantic/classes/__init__.py +++ b/test/driver_asyncpg/pydantic/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/pydantic/classes/enums.py b/test/driver_asyncpg/pydantic/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_asyncpg/pydantic/classes/enums.py +++ b/test/driver_asyncpg/pydantic/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/pydantic/classes/models.py b/test/driver_asyncpg/pydantic/classes/models.py index 94e2b048..fdad3547 100644 --- a/test/driver_asyncpg/pydantic/classes/models.py +++ b/test/driver_asyncpg/pydantic/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/pydantic/classes/queries.py b/test/driver_asyncpg/pydantic/classes/queries.py index 59b306b0..41c61cbc 100644 --- a/test/driver_asyncpg/pydantic/classes/queries.py +++ b/test/driver_asyncpg/pydantic/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_asyncpg/pydantic/classes/queries_copy_override.py b/test/driver_asyncpg/pydantic/classes/queries_copy_override.py index 900a2eae..015ad8ab 100644 --- a/test/driver_asyncpg/pydantic/classes/queries_copy_override.py +++ b/test/driver_asyncpg/pydantic/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_asyncpg/pydantic/classes/queries_enum_override.py b/test/driver_asyncpg/pydantic/classes/queries_enum_override.py index 61a54794..6fbb09f7 100644 --- a/test/driver_asyncpg/pydantic/classes/queries_enum_override.py +++ b/test/driver_asyncpg/pydantic/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/pydantic/classes/queries_field_namings.py b/test/driver_asyncpg/pydantic/classes/queries_field_namings.py index 7a9a1e31..a431897f 100644 --- a/test/driver_asyncpg/pydantic/classes/queries_field_namings.py +++ b/test/driver_asyncpg/pydantic/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_asyncpg/pydantic/classes/queries_invalid_identifiers.py b/test/driver_asyncpg/pydantic/classes/queries_invalid_identifiers.py index 7c59c0d2..4f9e6662 100644 --- a/test/driver_asyncpg/pydantic/classes/queries_invalid_identifiers.py +++ b/test/driver_asyncpg/pydantic/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_asyncpg/pydantic/functions/__init__.py b/test/driver_asyncpg/pydantic/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_asyncpg/pydantic/functions/__init__.py +++ b/test/driver_asyncpg/pydantic/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_asyncpg/pydantic/functions/enums.py b/test/driver_asyncpg/pydantic/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_asyncpg/pydantic/functions/enums.py +++ b/test/driver_asyncpg/pydantic/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_asyncpg/pydantic/functions/models.py b/test/driver_asyncpg/pydantic/functions/models.py index f2b13eb5..e707b5cd 100644 --- a/test/driver_asyncpg/pydantic/functions/models.py +++ b/test/driver_asyncpg/pydantic/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_asyncpg/pydantic/functions/queries.py b/test/driver_asyncpg/pydantic/functions/queries.py index 18f1f9dc..e0685347 100644 --- a/test/driver_asyncpg/pydantic/functions/queries.py +++ b/test/driver_asyncpg/pydantic/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_asyncpg/pydantic/functions/queries_copy_override.py b/test/driver_asyncpg/pydantic/functions/queries_copy_override.py index be5edfe5..d64666ae 100644 --- a/test/driver_asyncpg/pydantic/functions/queries_copy_override.py +++ b/test/driver_asyncpg/pydantic/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_asyncpg/pydantic/functions/queries_enum_override.py b/test/driver_asyncpg/pydantic/functions/queries_enum_override.py index 6378d907..a0cbe8ef 100644 --- a/test/driver_asyncpg/pydantic/functions/queries_enum_override.py +++ b/test/driver_asyncpg/pydantic/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_asyncpg/pydantic/functions/queries_field_namings.py b/test/driver_asyncpg/pydantic/functions/queries_field_namings.py index 3a9d5f60..d2efe91e 100644 --- a/test/driver_asyncpg/pydantic/functions/queries_field_namings.py +++ b/test/driver_asyncpg/pydantic/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_asyncpg/pydantic/functions/queries_invalid_identifiers.py b/test/driver_asyncpg/pydantic/functions/queries_invalid_identifiers.py index 9f00a315..c9863445 100644 --- a/test/driver_asyncpg/pydantic/functions/queries_invalid_identifiers.py +++ b/test/driver_asyncpg/pydantic/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_asyncpg/sqlc-gen-better-python.wasm b/test/driver_asyncpg/sqlc-gen-better-python.wasm index 7d2fae76..dfb69b04 100644 Binary files a/test/driver_asyncpg/sqlc-gen-better-python.wasm and b/test/driver_asyncpg/sqlc-gen-better-python.wasm differ diff --git a/test/driver_asyncpg/sqlc.yaml b/test/driver_asyncpg/sqlc.yaml index ed98483e..3322ad4c 100644 --- a/test/driver_asyncpg/sqlc.yaml +++ b/test/driver_asyncpg/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: python wasm: url: file://sqlc-gen-better-python.wasm - sha256: eb001e364c1b8088e47bb43d4c9addf51ed7249f97db2fd1052cc14893989bed + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 sql: - schema: schema.sql queries: diff --git a/test/driver_psycopg_async/attrs/classes/__init__.py b/test/driver_psycopg_async/attrs/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/attrs/classes/__init__.py +++ b/test/driver_psycopg_async/attrs/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/attrs/classes/enums.py b/test/driver_psycopg_async/attrs/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_async/attrs/classes/enums.py +++ b/test/driver_psycopg_async/attrs/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/attrs/classes/models.py b/test/driver_psycopg_async/attrs/classes/models.py index 7a038118..53ad3fae 100644 --- a/test/driver_psycopg_async/attrs/classes/models.py +++ b/test/driver_psycopg_async/attrs/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/attrs/classes/queries.py b/test/driver_psycopg_async/attrs/classes/queries.py index e56aa465..c9f2555c 100644 --- a/test/driver_psycopg_async/attrs/classes/queries.py +++ b/test/driver_psycopg_async/attrs/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_async/attrs/classes/queries_copy_override.py b/test/driver_psycopg_async/attrs/classes/queries_copy_override.py index d4380284..77e70724 100644 --- a/test/driver_psycopg_async/attrs/classes/queries_copy_override.py +++ b/test/driver_psycopg_async/attrs/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_async/attrs/classes/queries_enum_override.py b/test/driver_psycopg_async/attrs/classes/queries_enum_override.py index 839fcd65..d8f5b87c 100644 --- a/test/driver_psycopg_async/attrs/classes/queries_enum_override.py +++ b/test/driver_psycopg_async/attrs/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/attrs/classes/queries_field_namings.py b/test/driver_psycopg_async/attrs/classes/queries_field_namings.py index 762bfe3a..0449eda0 100644 --- a/test/driver_psycopg_async/attrs/classes/queries_field_namings.py +++ b/test/driver_psycopg_async/attrs/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_async/attrs/classes/queries_invalid_identifiers.py b/test/driver_psycopg_async/attrs/classes/queries_invalid_identifiers.py index ffe87574..15e50ad6 100644 --- a/test/driver_psycopg_async/attrs/classes/queries_invalid_identifiers.py +++ b/test/driver_psycopg_async/attrs/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_async/attrs/functions/__init__.py b/test/driver_psycopg_async/attrs/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/attrs/functions/__init__.py +++ b/test/driver_psycopg_async/attrs/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/attrs/functions/enums.py b/test/driver_psycopg_async/attrs/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_async/attrs/functions/enums.py +++ b/test/driver_psycopg_async/attrs/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/attrs/functions/models.py b/test/driver_psycopg_async/attrs/functions/models.py index 5e429bb1..8224f3c9 100644 --- a/test/driver_psycopg_async/attrs/functions/models.py +++ b/test/driver_psycopg_async/attrs/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/attrs/functions/queries.py b/test/driver_psycopg_async/attrs/functions/queries.py index f802d3b5..0d40da80 100644 --- a/test/driver_psycopg_async/attrs/functions/queries.py +++ b/test/driver_psycopg_async/attrs/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_async/attrs/functions/queries_copy_override.py b/test/driver_psycopg_async/attrs/functions/queries_copy_override.py index 966a7430..35f20054 100644 --- a/test/driver_psycopg_async/attrs/functions/queries_copy_override.py +++ b/test/driver_psycopg_async/attrs/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_async/attrs/functions/queries_enum_override.py b/test/driver_psycopg_async/attrs/functions/queries_enum_override.py index 2cc130e5..3363efab 100644 --- a/test/driver_psycopg_async/attrs/functions/queries_enum_override.py +++ b/test/driver_psycopg_async/attrs/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/attrs/functions/queries_field_namings.py b/test/driver_psycopg_async/attrs/functions/queries_field_namings.py index bb7693f8..bc58a3d9 100644 --- a/test/driver_psycopg_async/attrs/functions/queries_field_namings.py +++ b/test/driver_psycopg_async/attrs/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_async/attrs/functions/queries_invalid_identifiers.py b/test/driver_psycopg_async/attrs/functions/queries_invalid_identifiers.py index 91cbdfd9..bafea29c 100644 --- a/test/driver_psycopg_async/attrs/functions/queries_invalid_identifiers.py +++ b/test/driver_psycopg_async/attrs/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_async/dataclass/classes/__init__.py b/test/driver_psycopg_async/dataclass/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/dataclass/classes/__init__.py +++ b/test/driver_psycopg_async/dataclass/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/dataclass/classes/enums.py b/test/driver_psycopg_async/dataclass/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_async/dataclass/classes/enums.py +++ b/test/driver_psycopg_async/dataclass/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/dataclass/classes/models.py b/test/driver_psycopg_async/dataclass/classes/models.py index bb9c6162..bdf24190 100644 --- a/test/driver_psycopg_async/dataclass/classes/models.py +++ b/test/driver_psycopg_async/dataclass/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/dataclass/classes/queries.py b/test/driver_psycopg_async/dataclass/classes/queries.py index b0de1fea..558f706c 100644 --- a/test/driver_psycopg_async/dataclass/classes/queries.py +++ b/test/driver_psycopg_async/dataclass/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_async/dataclass/classes/queries_copy_override.py b/test/driver_psycopg_async/dataclass/classes/queries_copy_override.py index 649a1992..5f6dbcee 100644 --- a/test/driver_psycopg_async/dataclass/classes/queries_copy_override.py +++ b/test/driver_psycopg_async/dataclass/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_async/dataclass/classes/queries_enum_override.py b/test/driver_psycopg_async/dataclass/classes/queries_enum_override.py index b2aac076..61a698c7 100644 --- a/test/driver_psycopg_async/dataclass/classes/queries_enum_override.py +++ b/test/driver_psycopg_async/dataclass/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/dataclass/classes/queries_field_namings.py b/test/driver_psycopg_async/dataclass/classes/queries_field_namings.py index 60bc5f09..67bd952b 100644 --- a/test/driver_psycopg_async/dataclass/classes/queries_field_namings.py +++ b/test/driver_psycopg_async/dataclass/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_async/dataclass/classes/queries_invalid_identifiers.py b/test/driver_psycopg_async/dataclass/classes/queries_invalid_identifiers.py index 720136c4..8f51b6af 100644 --- a/test/driver_psycopg_async/dataclass/classes/queries_invalid_identifiers.py +++ b/test/driver_psycopg_async/dataclass/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_async/dataclass/functions/__init__.py b/test/driver_psycopg_async/dataclass/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/dataclass/functions/__init__.py +++ b/test/driver_psycopg_async/dataclass/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/dataclass/functions/enums.py b/test/driver_psycopg_async/dataclass/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_async/dataclass/functions/enums.py +++ b/test/driver_psycopg_async/dataclass/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/dataclass/functions/models.py b/test/driver_psycopg_async/dataclass/functions/models.py index 745efa5f..498108da 100644 --- a/test/driver_psycopg_async/dataclass/functions/models.py +++ b/test/driver_psycopg_async/dataclass/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/dataclass/functions/queries.py b/test/driver_psycopg_async/dataclass/functions/queries.py index 4a88a285..b088bcba 100644 --- a/test/driver_psycopg_async/dataclass/functions/queries.py +++ b/test/driver_psycopg_async/dataclass/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_async/dataclass/functions/queries_converters.py b/test/driver_psycopg_async/dataclass/functions/queries_converters.py index bf096f5c..c7a20786 100644 --- a/test/driver_psycopg_async/dataclass/functions/queries_converters.py +++ b/test/driver_psycopg_async/dataclass/functions/queries_converters.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_converters.sql """Module containing queries from file queries_converters.sql.""" diff --git a/test/driver_psycopg_async/dataclass/functions/queries_copy_override.py b/test/driver_psycopg_async/dataclass/functions/queries_copy_override.py index 2a0ad10b..ed061aee 100644 --- a/test/driver_psycopg_async/dataclass/functions/queries_copy_override.py +++ b/test/driver_psycopg_async/dataclass/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_async/dataclass/functions/queries_enum_override.py b/test/driver_psycopg_async/dataclass/functions/queries_enum_override.py index 7c12135d..3f17fea5 100644 --- a/test/driver_psycopg_async/dataclass/functions/queries_enum_override.py +++ b/test/driver_psycopg_async/dataclass/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/dataclass/functions/queries_field_namings.py b/test/driver_psycopg_async/dataclass/functions/queries_field_namings.py index 40c03767..f7d4a03a 100644 --- a/test/driver_psycopg_async/dataclass/functions/queries_field_namings.py +++ b/test/driver_psycopg_async/dataclass/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_async/dataclass/functions/queries_invalid_identifiers.py b/test/driver_psycopg_async/dataclass/functions/queries_invalid_identifiers.py index 78ab5b95..cdc59f63 100644 --- a/test/driver_psycopg_async/dataclass/functions/queries_invalid_identifiers.py +++ b/test/driver_psycopg_async/dataclass/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_async/msgspec/classes/__init__.py b/test/driver_psycopg_async/msgspec/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/msgspec/classes/__init__.py +++ b/test/driver_psycopg_async/msgspec/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/msgspec/classes/enums.py b/test/driver_psycopg_async/msgspec/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_async/msgspec/classes/enums.py +++ b/test/driver_psycopg_async/msgspec/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/msgspec/classes/models.py b/test/driver_psycopg_async/msgspec/classes/models.py index 66cc9cf2..b41888ca 100644 --- a/test/driver_psycopg_async/msgspec/classes/models.py +++ b/test/driver_psycopg_async/msgspec/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/msgspec/classes/queries.py b/test/driver_psycopg_async/msgspec/classes/queries.py index 7792e803..576c14ae 100644 --- a/test/driver_psycopg_async/msgspec/classes/queries.py +++ b/test/driver_psycopg_async/msgspec/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_async/msgspec/classes/queries_copy_override.py b/test/driver_psycopg_async/msgspec/classes/queries_copy_override.py index 33c3cdd5..de8c9f7d 100644 --- a/test/driver_psycopg_async/msgspec/classes/queries_copy_override.py +++ b/test/driver_psycopg_async/msgspec/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_async/msgspec/classes/queries_enum_override.py b/test/driver_psycopg_async/msgspec/classes/queries_enum_override.py index e996d02b..76a8a571 100644 --- a/test/driver_psycopg_async/msgspec/classes/queries_enum_override.py +++ b/test/driver_psycopg_async/msgspec/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/msgspec/classes/queries_field_namings.py b/test/driver_psycopg_async/msgspec/classes/queries_field_namings.py index b45fc2d1..e8725384 100644 --- a/test/driver_psycopg_async/msgspec/classes/queries_field_namings.py +++ b/test/driver_psycopg_async/msgspec/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_async/msgspec/classes/queries_invalid_identifiers.py b/test/driver_psycopg_async/msgspec/classes/queries_invalid_identifiers.py index a9b18a72..544c292c 100644 --- a/test/driver_psycopg_async/msgspec/classes/queries_invalid_identifiers.py +++ b/test/driver_psycopg_async/msgspec/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_async/msgspec/functions/__init__.py b/test/driver_psycopg_async/msgspec/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/msgspec/functions/__init__.py +++ b/test/driver_psycopg_async/msgspec/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/msgspec/functions/enums.py b/test/driver_psycopg_async/msgspec/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_async/msgspec/functions/enums.py +++ b/test/driver_psycopg_async/msgspec/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/msgspec/functions/models.py b/test/driver_psycopg_async/msgspec/functions/models.py index c067398c..f4ac96c9 100644 --- a/test/driver_psycopg_async/msgspec/functions/models.py +++ b/test/driver_psycopg_async/msgspec/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/msgspec/functions/queries.py b/test/driver_psycopg_async/msgspec/functions/queries.py index 9edc1b5f..0eb709ad 100644 --- a/test/driver_psycopg_async/msgspec/functions/queries.py +++ b/test/driver_psycopg_async/msgspec/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_async/msgspec/functions/queries_copy_override.py b/test/driver_psycopg_async/msgspec/functions/queries_copy_override.py index 9adb3c34..69e7ef07 100644 --- a/test/driver_psycopg_async/msgspec/functions/queries_copy_override.py +++ b/test/driver_psycopg_async/msgspec/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_async/msgspec/functions/queries_enum_override.py b/test/driver_psycopg_async/msgspec/functions/queries_enum_override.py index 5d3fef47..9e104447 100644 --- a/test/driver_psycopg_async/msgspec/functions/queries_enum_override.py +++ b/test/driver_psycopg_async/msgspec/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/msgspec/functions/queries_field_namings.py b/test/driver_psycopg_async/msgspec/functions/queries_field_namings.py index 3572ab83..49552d54 100644 --- a/test/driver_psycopg_async/msgspec/functions/queries_field_namings.py +++ b/test/driver_psycopg_async/msgspec/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_async/msgspec/functions/queries_invalid_identifiers.py b/test/driver_psycopg_async/msgspec/functions/queries_invalid_identifiers.py index 84ac9ee1..c46804f3 100644 --- a/test/driver_psycopg_async/msgspec/functions/queries_invalid_identifiers.py +++ b/test/driver_psycopg_async/msgspec/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_async/omit_tc/classes/__init__.py b/test/driver_psycopg_async/omit_tc/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/omit_tc/classes/__init__.py +++ b/test/driver_psycopg_async/omit_tc/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/omit_tc/classes/enums.py b/test/driver_psycopg_async/omit_tc/classes/enums.py index 4fd9220a..8dc5439d 100644 --- a/test/driver_psycopg_async/omit_tc/classes/enums.py +++ b/test/driver_psycopg_async/omit_tc/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/omit_tc/classes/models.py b/test/driver_psycopg_async/omit_tc/classes/models.py index 41728279..35c76305 100644 --- a/test/driver_psycopg_async/omit_tc/classes/models.py +++ b/test/driver_psycopg_async/omit_tc/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/omit_tc/classes/queries_enum_override.py b/test/driver_psycopg_async/omit_tc/classes/queries_enum_override.py index 76be665c..b51067cb 100644 --- a/test/driver_psycopg_async/omit_tc/classes/queries_enum_override.py +++ b/test/driver_psycopg_async/omit_tc/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/omit_tc/functions/__init__.py b/test/driver_psycopg_async/omit_tc/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/omit_tc/functions/__init__.py +++ b/test/driver_psycopg_async/omit_tc/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/omit_tc/functions/enums.py b/test/driver_psycopg_async/omit_tc/functions/enums.py index 4fd9220a..8dc5439d 100644 --- a/test/driver_psycopg_async/omit_tc/functions/enums.py +++ b/test/driver_psycopg_async/omit_tc/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/omit_tc/functions/models.py b/test/driver_psycopg_async/omit_tc/functions/models.py index 41728279..35c76305 100644 --- a/test/driver_psycopg_async/omit_tc/functions/models.py +++ b/test/driver_psycopg_async/omit_tc/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/omit_tc/functions/queries_enum_override.py b/test/driver_psycopg_async/omit_tc/functions/queries_enum_override.py index 99cf1126..c9f0663a 100644 --- a/test/driver_psycopg_async/omit_tc/functions/queries_enum_override.py +++ b/test/driver_psycopg_async/omit_tc/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/pydantic/classes/__init__.py b/test/driver_psycopg_async/pydantic/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/pydantic/classes/__init__.py +++ b/test/driver_psycopg_async/pydantic/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/pydantic/classes/enums.py b/test/driver_psycopg_async/pydantic/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_async/pydantic/classes/enums.py +++ b/test/driver_psycopg_async/pydantic/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/pydantic/classes/models.py b/test/driver_psycopg_async/pydantic/classes/models.py index b9e4e09d..671cb6dd 100644 --- a/test/driver_psycopg_async/pydantic/classes/models.py +++ b/test/driver_psycopg_async/pydantic/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/pydantic/classes/queries.py b/test/driver_psycopg_async/pydantic/classes/queries.py index 6ea06423..31dcdf02 100644 --- a/test/driver_psycopg_async/pydantic/classes/queries.py +++ b/test/driver_psycopg_async/pydantic/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_async/pydantic/classes/queries_copy_override.py b/test/driver_psycopg_async/pydantic/classes/queries_copy_override.py index 425b86df..4b59a16c 100644 --- a/test/driver_psycopg_async/pydantic/classes/queries_copy_override.py +++ b/test/driver_psycopg_async/pydantic/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_async/pydantic/classes/queries_enum_override.py b/test/driver_psycopg_async/pydantic/classes/queries_enum_override.py index 4e591691..1f712227 100644 --- a/test/driver_psycopg_async/pydantic/classes/queries_enum_override.py +++ b/test/driver_psycopg_async/pydantic/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/pydantic/classes/queries_field_namings.py b/test/driver_psycopg_async/pydantic/classes/queries_field_namings.py index 3310d5f6..456dd65f 100644 --- a/test/driver_psycopg_async/pydantic/classes/queries_field_namings.py +++ b/test/driver_psycopg_async/pydantic/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_async/pydantic/classes/queries_invalid_identifiers.py b/test/driver_psycopg_async/pydantic/classes/queries_invalid_identifiers.py index 91a8a7c4..e37310ca 100644 --- a/test/driver_psycopg_async/pydantic/classes/queries_invalid_identifiers.py +++ b/test/driver_psycopg_async/pydantic/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_async/pydantic/functions/__init__.py b/test/driver_psycopg_async/pydantic/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_async/pydantic/functions/__init__.py +++ b/test/driver_psycopg_async/pydantic/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_async/pydantic/functions/enums.py b/test/driver_psycopg_async/pydantic/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_async/pydantic/functions/enums.py +++ b/test/driver_psycopg_async/pydantic/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/pydantic/functions/models.py b/test/driver_psycopg_async/pydantic/functions/models.py index 1f19e739..204d1ee8 100644 --- a/test/driver_psycopg_async/pydantic/functions/models.py +++ b/test/driver_psycopg_async/pydantic/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_async/pydantic/functions/queries.py b/test/driver_psycopg_async/pydantic/functions/queries.py index 90b20a82..24f23f7f 100644 --- a/test/driver_psycopg_async/pydantic/functions/queries.py +++ b/test/driver_psycopg_async/pydantic/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_async/pydantic/functions/queries_copy_override.py b/test/driver_psycopg_async/pydantic/functions/queries_copy_override.py index 091127c3..4151c236 100644 --- a/test/driver_psycopg_async/pydantic/functions/queries_copy_override.py +++ b/test/driver_psycopg_async/pydantic/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_async/pydantic/functions/queries_enum_override.py b/test/driver_psycopg_async/pydantic/functions/queries_enum_override.py index 260d10b0..159dc7b7 100644 --- a/test/driver_psycopg_async/pydantic/functions/queries_enum_override.py +++ b/test/driver_psycopg_async/pydantic/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_async/pydantic/functions/queries_field_namings.py b/test/driver_psycopg_async/pydantic/functions/queries_field_namings.py index 7ab5cad8..7a51fc66 100644 --- a/test/driver_psycopg_async/pydantic/functions/queries_field_namings.py +++ b/test/driver_psycopg_async/pydantic/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_async/pydantic/functions/queries_invalid_identifiers.py b/test/driver_psycopg_async/pydantic/functions/queries_invalid_identifiers.py index 42e9bcaa..6f7b8d4a 100644 --- a/test/driver_psycopg_async/pydantic/functions/queries_invalid_identifiers.py +++ b/test/driver_psycopg_async/pydantic/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_async/sqlc-gen-better-python.wasm b/test/driver_psycopg_async/sqlc-gen-better-python.wasm index 7d2fae76..dfb69b04 100644 Binary files a/test/driver_psycopg_async/sqlc-gen-better-python.wasm and b/test/driver_psycopg_async/sqlc-gen-better-python.wasm differ diff --git a/test/driver_psycopg_async/sqlc.yaml b/test/driver_psycopg_async/sqlc.yaml index 51840dd6..97b9441c 100644 --- a/test/driver_psycopg_async/sqlc.yaml +++ b/test/driver_psycopg_async/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: python wasm: url: file://sqlc-gen-better-python.wasm - sha256: eb001e364c1b8088e47bb43d4c9addf51ed7249f97db2fd1052cc14893989bed + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 sql: - schema: schema.sql queries: diff --git a/test/driver_psycopg_sync/attrs/classes/__init__.py b/test/driver_psycopg_sync/attrs/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/attrs/classes/__init__.py +++ b/test/driver_psycopg_sync/attrs/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/attrs/classes/enums.py b/test/driver_psycopg_sync/attrs/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_sync/attrs/classes/enums.py +++ b/test/driver_psycopg_sync/attrs/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/attrs/classes/models.py b/test/driver_psycopg_sync/attrs/classes/models.py index 1585b8e4..f3b099b6 100644 --- a/test/driver_psycopg_sync/attrs/classes/models.py +++ b/test/driver_psycopg_sync/attrs/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/attrs/classes/queries.py b/test/driver_psycopg_sync/attrs/classes/queries.py index 1e77da67..cf5d8626 100644 --- a/test/driver_psycopg_sync/attrs/classes/queries.py +++ b/test/driver_psycopg_sync/attrs/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_sync/attrs/classes/queries_copy_override.py b/test/driver_psycopg_sync/attrs/classes/queries_copy_override.py index f4d9edec..3c5744ff 100644 --- a/test/driver_psycopg_sync/attrs/classes/queries_copy_override.py +++ b/test/driver_psycopg_sync/attrs/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_sync/attrs/classes/queries_enum_override.py b/test/driver_psycopg_sync/attrs/classes/queries_enum_override.py index 865ab183..2332e5fb 100644 --- a/test/driver_psycopg_sync/attrs/classes/queries_enum_override.py +++ b/test/driver_psycopg_sync/attrs/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/attrs/classes/queries_field_namings.py b/test/driver_psycopg_sync/attrs/classes/queries_field_namings.py index bfa8727a..66240083 100644 --- a/test/driver_psycopg_sync/attrs/classes/queries_field_namings.py +++ b/test/driver_psycopg_sync/attrs/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_sync/attrs/classes/queries_invalid_identifiers.py b/test/driver_psycopg_sync/attrs/classes/queries_invalid_identifiers.py index 3710983c..c4dbeeb5 100644 --- a/test/driver_psycopg_sync/attrs/classes/queries_invalid_identifiers.py +++ b/test/driver_psycopg_sync/attrs/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_sync/attrs/functions/__init__.py b/test/driver_psycopg_sync/attrs/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/attrs/functions/__init__.py +++ b/test/driver_psycopg_sync/attrs/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/attrs/functions/enums.py b/test/driver_psycopg_sync/attrs/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_sync/attrs/functions/enums.py +++ b/test/driver_psycopg_sync/attrs/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/attrs/functions/models.py b/test/driver_psycopg_sync/attrs/functions/models.py index 304f691b..07e50de3 100644 --- a/test/driver_psycopg_sync/attrs/functions/models.py +++ b/test/driver_psycopg_sync/attrs/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/attrs/functions/queries.py b/test/driver_psycopg_sync/attrs/functions/queries.py index 7f20177f..9b397bc0 100644 --- a/test/driver_psycopg_sync/attrs/functions/queries.py +++ b/test/driver_psycopg_sync/attrs/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_sync/attrs/functions/queries_copy_override.py b/test/driver_psycopg_sync/attrs/functions/queries_copy_override.py index 9fe4bcba..13f8f015 100644 --- a/test/driver_psycopg_sync/attrs/functions/queries_copy_override.py +++ b/test/driver_psycopg_sync/attrs/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_sync/attrs/functions/queries_enum_override.py b/test/driver_psycopg_sync/attrs/functions/queries_enum_override.py index 7dd207a6..f3d59739 100644 --- a/test/driver_psycopg_sync/attrs/functions/queries_enum_override.py +++ b/test/driver_psycopg_sync/attrs/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/attrs/functions/queries_field_namings.py b/test/driver_psycopg_sync/attrs/functions/queries_field_namings.py index aa226d8c..c7be04a8 100644 --- a/test/driver_psycopg_sync/attrs/functions/queries_field_namings.py +++ b/test/driver_psycopg_sync/attrs/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_sync/attrs/functions/queries_invalid_identifiers.py b/test/driver_psycopg_sync/attrs/functions/queries_invalid_identifiers.py index d1078942..51307482 100644 --- a/test/driver_psycopg_sync/attrs/functions/queries_invalid_identifiers.py +++ b/test/driver_psycopg_sync/attrs/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/classes/__init__.py b/test/driver_psycopg_sync/dataclass/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/dataclass/classes/__init__.py +++ b/test/driver_psycopg_sync/dataclass/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/dataclass/classes/enums.py b/test/driver_psycopg_sync/dataclass/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_sync/dataclass/classes/enums.py +++ b/test/driver_psycopg_sync/dataclass/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/dataclass/classes/models.py b/test/driver_psycopg_sync/dataclass/classes/models.py index 5c3a6d67..8791bf80 100644 --- a/test/driver_psycopg_sync/dataclass/classes/models.py +++ b/test/driver_psycopg_sync/dataclass/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/dataclass/classes/queries.py b/test/driver_psycopg_sync/dataclass/classes/queries.py index 3dc9f850..1d2cc718 100644 --- a/test/driver_psycopg_sync/dataclass/classes/queries.py +++ b/test/driver_psycopg_sync/dataclass/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/classes/queries_copy_override.py b/test/driver_psycopg_sync/dataclass/classes/queries_copy_override.py index 279d922f..8ad5fde9 100644 --- a/test/driver_psycopg_sync/dataclass/classes/queries_copy_override.py +++ b/test/driver_psycopg_sync/dataclass/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/classes/queries_enum_override.py b/test/driver_psycopg_sync/dataclass/classes/queries_enum_override.py index b56a3e27..43799d06 100644 --- a/test/driver_psycopg_sync/dataclass/classes/queries_enum_override.py +++ b/test/driver_psycopg_sync/dataclass/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/classes/queries_field_namings.py b/test/driver_psycopg_sync/dataclass/classes/queries_field_namings.py index 33999d38..9010e4e7 100644 --- a/test/driver_psycopg_sync/dataclass/classes/queries_field_namings.py +++ b/test/driver_psycopg_sync/dataclass/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/classes/queries_invalid_identifiers.py b/test/driver_psycopg_sync/dataclass/classes/queries_invalid_identifiers.py index c31196fe..d9edaaf2 100644 --- a/test/driver_psycopg_sync/dataclass/classes/queries_invalid_identifiers.py +++ b/test/driver_psycopg_sync/dataclass/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/functions/__init__.py b/test/driver_psycopg_sync/dataclass/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/dataclass/functions/__init__.py +++ b/test/driver_psycopg_sync/dataclass/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/dataclass/functions/enums.py b/test/driver_psycopg_sync/dataclass/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_sync/dataclass/functions/enums.py +++ b/test/driver_psycopg_sync/dataclass/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/dataclass/functions/models.py b/test/driver_psycopg_sync/dataclass/functions/models.py index d055888e..39fb52e4 100644 --- a/test/driver_psycopg_sync/dataclass/functions/models.py +++ b/test/driver_psycopg_sync/dataclass/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/dataclass/functions/queries.py b/test/driver_psycopg_sync/dataclass/functions/queries.py index b7e91418..af29b411 100644 --- a/test/driver_psycopg_sync/dataclass/functions/queries.py +++ b/test/driver_psycopg_sync/dataclass/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/functions/queries_converters.py b/test/driver_psycopg_sync/dataclass/functions/queries_converters.py index 601d2fd3..a442f550 100644 --- a/test/driver_psycopg_sync/dataclass/functions/queries_converters.py +++ b/test/driver_psycopg_sync/dataclass/functions/queries_converters.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_converters.sql """Module containing queries from file queries_converters.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/functions/queries_copy_override.py b/test/driver_psycopg_sync/dataclass/functions/queries_copy_override.py index 7b28bc22..735862d1 100644 --- a/test/driver_psycopg_sync/dataclass/functions/queries_copy_override.py +++ b/test/driver_psycopg_sync/dataclass/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/functions/queries_enum_override.py b/test/driver_psycopg_sync/dataclass/functions/queries_enum_override.py index ac45b75b..2c8927aa 100644 --- a/test/driver_psycopg_sync/dataclass/functions/queries_enum_override.py +++ b/test/driver_psycopg_sync/dataclass/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/functions/queries_field_namings.py b/test/driver_psycopg_sync/dataclass/functions/queries_field_namings.py index 91f08177..66bdcc1c 100644 --- a/test/driver_psycopg_sync/dataclass/functions/queries_field_namings.py +++ b/test/driver_psycopg_sync/dataclass/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_sync/dataclass/functions/queries_invalid_identifiers.py b/test/driver_psycopg_sync/dataclass/functions/queries_invalid_identifiers.py index dc3aab02..bd1b9fa3 100644 --- a/test/driver_psycopg_sync/dataclass/functions/queries_invalid_identifiers.py +++ b/test/driver_psycopg_sync/dataclass/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/classes/__init__.py b/test/driver_psycopg_sync/msgspec/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/msgspec/classes/__init__.py +++ b/test/driver_psycopg_sync/msgspec/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/msgspec/classes/enums.py b/test/driver_psycopg_sync/msgspec/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_sync/msgspec/classes/enums.py +++ b/test/driver_psycopg_sync/msgspec/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/msgspec/classes/models.py b/test/driver_psycopg_sync/msgspec/classes/models.py index 5d9c1e69..d15994f5 100644 --- a/test/driver_psycopg_sync/msgspec/classes/models.py +++ b/test/driver_psycopg_sync/msgspec/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/msgspec/classes/queries.py b/test/driver_psycopg_sync/msgspec/classes/queries.py index b64ec40c..1251b89c 100644 --- a/test/driver_psycopg_sync/msgspec/classes/queries.py +++ b/test/driver_psycopg_sync/msgspec/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/classes/queries_copy_override.py b/test/driver_psycopg_sync/msgspec/classes/queries_copy_override.py index 400258d4..43c78ffd 100644 --- a/test/driver_psycopg_sync/msgspec/classes/queries_copy_override.py +++ b/test/driver_psycopg_sync/msgspec/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/classes/queries_enum_override.py b/test/driver_psycopg_sync/msgspec/classes/queries_enum_override.py index feac20c7..7aae9241 100644 --- a/test/driver_psycopg_sync/msgspec/classes/queries_enum_override.py +++ b/test/driver_psycopg_sync/msgspec/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/classes/queries_field_namings.py b/test/driver_psycopg_sync/msgspec/classes/queries_field_namings.py index 20bc7f21..c7de87a0 100644 --- a/test/driver_psycopg_sync/msgspec/classes/queries_field_namings.py +++ b/test/driver_psycopg_sync/msgspec/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/classes/queries_invalid_identifiers.py b/test/driver_psycopg_sync/msgspec/classes/queries_invalid_identifiers.py index 3438ef81..de58ef1f 100644 --- a/test/driver_psycopg_sync/msgspec/classes/queries_invalid_identifiers.py +++ b/test/driver_psycopg_sync/msgspec/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/functions/__init__.py b/test/driver_psycopg_sync/msgspec/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/msgspec/functions/__init__.py +++ b/test/driver_psycopg_sync/msgspec/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/msgspec/functions/enums.py b/test/driver_psycopg_sync/msgspec/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_sync/msgspec/functions/enums.py +++ b/test/driver_psycopg_sync/msgspec/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/msgspec/functions/models.py b/test/driver_psycopg_sync/msgspec/functions/models.py index 416a07e6..6cbafd8b 100644 --- a/test/driver_psycopg_sync/msgspec/functions/models.py +++ b/test/driver_psycopg_sync/msgspec/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/msgspec/functions/queries.py b/test/driver_psycopg_sync/msgspec/functions/queries.py index f17cd011..ca8ab0a8 100644 --- a/test/driver_psycopg_sync/msgspec/functions/queries.py +++ b/test/driver_psycopg_sync/msgspec/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/functions/queries_copy_override.py b/test/driver_psycopg_sync/msgspec/functions/queries_copy_override.py index 991e832d..34e9a94b 100644 --- a/test/driver_psycopg_sync/msgspec/functions/queries_copy_override.py +++ b/test/driver_psycopg_sync/msgspec/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/functions/queries_enum_override.py b/test/driver_psycopg_sync/msgspec/functions/queries_enum_override.py index de92ea6c..c1129dd9 100644 --- a/test/driver_psycopg_sync/msgspec/functions/queries_enum_override.py +++ b/test/driver_psycopg_sync/msgspec/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/functions/queries_field_namings.py b/test/driver_psycopg_sync/msgspec/functions/queries_field_namings.py index 027df75f..c9766038 100644 --- a/test/driver_psycopg_sync/msgspec/functions/queries_field_namings.py +++ b/test/driver_psycopg_sync/msgspec/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_sync/msgspec/functions/queries_invalid_identifiers.py b/test/driver_psycopg_sync/msgspec/functions/queries_invalid_identifiers.py index ae05015e..38517c07 100644 --- a/test/driver_psycopg_sync/msgspec/functions/queries_invalid_identifiers.py +++ b/test/driver_psycopg_sync/msgspec/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_sync/omit_tc/classes/__init__.py b/test/driver_psycopg_sync/omit_tc/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/omit_tc/classes/__init__.py +++ b/test/driver_psycopg_sync/omit_tc/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/omit_tc/classes/enums.py b/test/driver_psycopg_sync/omit_tc/classes/enums.py index 4fd9220a..8dc5439d 100644 --- a/test/driver_psycopg_sync/omit_tc/classes/enums.py +++ b/test/driver_psycopg_sync/omit_tc/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/omit_tc/classes/models.py b/test/driver_psycopg_sync/omit_tc/classes/models.py index 41728279..35c76305 100644 --- a/test/driver_psycopg_sync/omit_tc/classes/models.py +++ b/test/driver_psycopg_sync/omit_tc/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/omit_tc/classes/queries_enum_override.py b/test/driver_psycopg_sync/omit_tc/classes/queries_enum_override.py index a2fe593a..7778b80a 100644 --- a/test/driver_psycopg_sync/omit_tc/classes/queries_enum_override.py +++ b/test/driver_psycopg_sync/omit_tc/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/omit_tc/functions/__init__.py b/test/driver_psycopg_sync/omit_tc/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/omit_tc/functions/__init__.py +++ b/test/driver_psycopg_sync/omit_tc/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/omit_tc/functions/enums.py b/test/driver_psycopg_sync/omit_tc/functions/enums.py index 4fd9220a..8dc5439d 100644 --- a/test/driver_psycopg_sync/omit_tc/functions/enums.py +++ b/test/driver_psycopg_sync/omit_tc/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/omit_tc/functions/models.py b/test/driver_psycopg_sync/omit_tc/functions/models.py index 41728279..35c76305 100644 --- a/test/driver_psycopg_sync/omit_tc/functions/models.py +++ b/test/driver_psycopg_sync/omit_tc/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/omit_tc/functions/queries_enum_override.py b/test/driver_psycopg_sync/omit_tc/functions/queries_enum_override.py index 4330ce31..9e8294eb 100644 --- a/test/driver_psycopg_sync/omit_tc/functions/queries_enum_override.py +++ b/test/driver_psycopg_sync/omit_tc/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/classes/__init__.py b/test/driver_psycopg_sync/pydantic/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/pydantic/classes/__init__.py +++ b/test/driver_psycopg_sync/pydantic/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/pydantic/classes/enums.py b/test/driver_psycopg_sync/pydantic/classes/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_sync/pydantic/classes/enums.py +++ b/test/driver_psycopg_sync/pydantic/classes/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/pydantic/classes/models.py b/test/driver_psycopg_sync/pydantic/classes/models.py index 59f90b22..5691f4a2 100644 --- a/test/driver_psycopg_sync/pydantic/classes/models.py +++ b/test/driver_psycopg_sync/pydantic/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/pydantic/classes/queries.py b/test/driver_psycopg_sync/pydantic/classes/queries.py index 090d4b8e..6a535921 100644 --- a/test/driver_psycopg_sync/pydantic/classes/queries.py +++ b/test/driver_psycopg_sync/pydantic/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/classes/queries_copy_override.py b/test/driver_psycopg_sync/pydantic/classes/queries_copy_override.py index fceb3a9d..9e8c6a4d 100644 --- a/test/driver_psycopg_sync/pydantic/classes/queries_copy_override.py +++ b/test/driver_psycopg_sync/pydantic/classes/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/classes/queries_enum_override.py b/test/driver_psycopg_sync/pydantic/classes/queries_enum_override.py index 2a3a7bac..d87df5e8 100644 --- a/test/driver_psycopg_sync/pydantic/classes/queries_enum_override.py +++ b/test/driver_psycopg_sync/pydantic/classes/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/classes/queries_field_namings.py b/test/driver_psycopg_sync/pydantic/classes/queries_field_namings.py index 5c61a3ed..d48d9939 100644 --- a/test/driver_psycopg_sync/pydantic/classes/queries_field_namings.py +++ b/test/driver_psycopg_sync/pydantic/classes/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/classes/queries_invalid_identifiers.py b/test/driver_psycopg_sync/pydantic/classes/queries_invalid_identifiers.py index 0ea69bd8..2e308dc3 100644 --- a/test/driver_psycopg_sync/pydantic/classes/queries_invalid_identifiers.py +++ b/test/driver_psycopg_sync/pydantic/classes/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/functions/__init__.py b/test/driver_psycopg_sync/pydantic/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_psycopg_sync/pydantic/functions/__init__.py +++ b/test/driver_psycopg_sync/pydantic/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_psycopg_sync/pydantic/functions/enums.py b/test/driver_psycopg_sync/pydantic/functions/enums.py index 8e4db82c..0d97e026 100644 --- a/test/driver_psycopg_sync/pydantic/functions/enums.py +++ b/test/driver_psycopg_sync/pydantic/functions/enums.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing enums.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/pydantic/functions/models.py b/test/driver_psycopg_sync/pydantic/functions/models.py index f69857af..a7ffb664 100644 --- a/test/driver_psycopg_sync/pydantic/functions/models.py +++ b/test/driver_psycopg_sync/pydantic/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_psycopg_sync/pydantic/functions/queries.py b/test/driver_psycopg_sync/pydantic/functions/queries.py index e702583d..063fde53 100644 --- a/test/driver_psycopg_sync/pydantic/functions/queries.py +++ b/test/driver_psycopg_sync/pydantic/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/functions/queries_copy_override.py b/test/driver_psycopg_sync/pydantic/functions/queries_copy_override.py index 74368d39..ba1ac8d7 100644 --- a/test/driver_psycopg_sync/pydantic/functions/queries_copy_override.py +++ b/test/driver_psycopg_sync/pydantic/functions/queries_copy_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_copy_override.sql """Module containing queries from file queries_copy_override.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/functions/queries_enum_override.py b/test/driver_psycopg_sync/pydantic/functions/queries_enum_override.py index b1200911..07af456b 100644 --- a/test/driver_psycopg_sync/pydantic/functions/queries_enum_override.py +++ b/test/driver_psycopg_sync/pydantic/functions/queries_enum_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_enum_override.sql """Module containing queries from file queries_enum_override.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/functions/queries_field_namings.py b/test/driver_psycopg_sync/pydantic/functions/queries_field_namings.py index 26cabf5a..381c555e 100644 --- a/test/driver_psycopg_sync/pydantic/functions/queries_field_namings.py +++ b/test/driver_psycopg_sync/pydantic/functions/queries_field_namings.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_field_namings.sql """Module containing queries from file queries_field_namings.sql.""" diff --git a/test/driver_psycopg_sync/pydantic/functions/queries_invalid_identifiers.py b/test/driver_psycopg_sync/pydantic/functions/queries_invalid_identifiers.py index b07b4c1b..00052887 100644 --- a/test/driver_psycopg_sync/pydantic/functions/queries_invalid_identifiers.py +++ b/test/driver_psycopg_sync/pydantic/functions/queries_invalid_identifiers.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_invalid_identifiers.sql """Module containing queries from file queries_invalid_identifiers.sql.""" diff --git a/test/driver_psycopg_sync/sqlc-gen-better-python.wasm b/test/driver_psycopg_sync/sqlc-gen-better-python.wasm index 7d2fae76..dfb69b04 100755 Binary files a/test/driver_psycopg_sync/sqlc-gen-better-python.wasm and b/test/driver_psycopg_sync/sqlc-gen-better-python.wasm differ diff --git a/test/driver_psycopg_sync/sqlc.yaml b/test/driver_psycopg_sync/sqlc.yaml index cf992fd8..40948eba 100644 --- a/test/driver_psycopg_sync/sqlc.yaml +++ b/test/driver_psycopg_sync/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: python wasm: url: file://sqlc-gen-better-python.wasm - sha256: eb001e364c1b8088e47bb43d4c9addf51ed7249f97db2fd1052cc14893989bed + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 sql: - schema: schema.sql queries: diff --git a/test/driver_pymysql/__init__.py b/test/driver_pymysql/__init__.py new file mode 100644 index 00000000..11a9bca5 --- /dev/null +++ b/test/driver_pymysql/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for pymysql tests.""" diff --git a/test/driver_pymysql/attrs/__init__.py b/test/driver_pymysql/attrs/__init__.py new file mode 100644 index 00000000..11a9bca5 --- /dev/null +++ b/test/driver_pymysql/attrs/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for pymysql tests.""" diff --git a/test/driver_pymysql/attrs/classes/__init__.py b/test/driver_pymysql/attrs/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/attrs/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/attrs/classes/enums.py b/test/driver_pymysql/attrs/classes/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_pymysql/attrs/classes/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_pymysql/attrs/classes/models.py b/test/driver_pymysql/attrs/classes/models.py new file mode 100644 index 00000000..235e55ee --- /dev/null +++ b/test/driver_pymysql/attrs/classes/models.py @@ -0,0 +1,392 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestConverter", + "TestDbtypeOverride", + "TestEnumOverride", + "TestExeclastid", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestSlice", + "TestTypeOverride", +) + +import attrs +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_pymysql.attrs.classes import enums + import collections.abc + import datetime + import decimal + + +@attrs.define() +class Model3RdPartyStat: + """Model representing Model3RdPartyStat. + + Attributes + ---------- + id_ : int + total : int + + """ + + id_: int + total: int + + +@attrs.define() +class TestCaseSensitivity: + """Model representing TestCaseSensitivity. + + Attributes + ---------- + id_ : int + upper_dt : datetime.datetime + prec_dec : decimal.Decimal + + """ + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +@attrs.define() +class TestConverter: + """Model representing TestConverter. + + Attributes + ---------- + id_ : int + prefs : str + maybe_prefs : str | None + tags : str + + """ + + id_: int + prefs: str + maybe_prefs: str | None + tags: str + + +@attrs.define() +class TestDbtypeOverride: + """Model representing TestDbtypeOverride. + + Attributes + ---------- + id_ : int + happened_at : datetime.datetime + + """ + + id_: int + happened_at: datetime.datetime + + +@attrs.define() +class TestEnumOverride: + """Model representing TestEnumOverride. + + Attributes + ---------- + id_ : int + mood_test : str + + """ + + id_: int + mood_test: str + + +@attrs.define() +class TestExeclastid: + """Model representing TestExeclastid. + + Attributes + ---------- + id_ : int + name : str + + """ + + id_: int + name: str + + +@attrs.define() +class TestFieldNaming: + """Model representing TestFieldNaming. + + Attributes + ---------- + id_ : int + outputs : str + + """ + + id_: int + outputs: str + + +@attrs.define() +class TestInnerMysqlType: + """Model representing TestInnerMysqlType. + + Attributes + ---------- + table_id : int + int_test : int | None + integer_test : int | None + mediumint_test : int | None + smallint_test : int | None + tinyint_test : int | None + bigint_test : int | None + int_unsigned_test : int | None + bigint_unsigned_test : int | None + year_test : int | None + tinyint1_test : bool | None + bool_test : bool | None + boolean_test : bool | None + float_test : float | None + double_test : float | None + double_precision_test : float | None + real_test : float | None + decimal_test : decimal.Decimal | None + numeric_test : decimal.Decimal | None + char_test : str | None + varchar_test : str | None + tinytext_test : str | None + text_test : str | None + mediumtext_test : str | None + longtext_test : str | None + binary_test : memoryview | None + varbinary_test : memoryview | None + tinyblob_test : memoryview | None + blob_test : memoryview | None + mediumblob_test : memoryview | None + longblob_test : memoryview | None + bit_test : memoryview | None + date_test : datetime.date | None + datetime_test : datetime.datetime | None + datetime6_test : datetime.datetime | None + timestamp_test : datetime.datetime | None + time_test : datetime.timedelta | None + json_test : str | None + mood : enums.TestInnerMysqlTypesMood | None + tag : enums.TestInnerMysqlTypesTag | None + + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +@attrs.define() +class TestInvalidIdentifier: + """Model representing TestInvalidIdentifier. + + Attributes + ---------- + id_ : int + column_3p_ : str | None + new_notes : str + column__pct : str | None + + """ + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +@attrs.define() +class TestMysqlType: + """Model representing TestMysqlType. + + Attributes + ---------- + id_ : int + int_test : int + integer_test : int + mediumint_test : int + smallint_test : int + tinyint_test : int + bigint_test : int + int_unsigned_test : int + bigint_unsigned_test : int + year_test : int + tinyint1_test : bool + bool_test : bool + boolean_test : bool + float_test : float + double_test : float + double_precision_test : float + real_test : float + decimal_test : decimal.Decimal + numeric_test : decimal.Decimal + char_test : str + varchar_test : str + tinytext_test : str + text_test : str + mediumtext_test : str + longtext_test : str + binary_test : memoryview + varbinary_test : memoryview + tinyblob_test : memoryview + blob_test : memoryview + mediumblob_test : memoryview + longblob_test : memoryview + bit_test : memoryview + date_test : datetime.date + datetime_test : datetime.datetime + datetime6_test : datetime.datetime + timestamp_test : datetime.datetime + time_test : datetime.timedelta + json_test : str + mood : enums.TestMysqlTypesMood + tag : enums.TestMysqlTypesTag + + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +@attrs.define() +class TestReservedArg: + """Model representing TestReservedArg. + + Attributes + ---------- + id_ : int + conn : str + + """ + + id_: int + conn: str + + +@attrs.define() +class TestSlice: + """Model representing TestSlice. + + Attributes + ---------- + id_ : int + name : str + note : str | None + + """ + + id_: int + name: str + note: str | None + + +@attrs.define() +class TestTypeOverride: + """Model representing TestTypeOverride. + + Attributes + ---------- + id_ : int + text_test : UserString | None + + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_pymysql/attrs/classes/queries.py b/test/driver_pymysql/attrs/classes/queries.py new file mode 100644 index 00000000..d5de01b2 --- /dev/null +++ b/test/driver_pymysql/attrs/classes/queries.py @@ -0,0 +1,1573 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Queries", + "QueryResults", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.attrs.classes import enums +from test.driver_pymysql.attrs.classes import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db. + + Parameters + ---------- + conn + The connection object of type `pymysql.Connection` used to execute queries. + sql + The SQL statement that will be executed when fetching/iterating. + decode_hook + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args + Arguments that should be sent when executing the sql query. + + """ + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance.""" + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns + ------- + QueryResults[T] + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns + ------- + collections.abc.Sequence[T] + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns + ------- + T + The next decoded result. + + Raises + ------ + StopIteration + When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class Queries: + """Queries from file queries.sql. + + Parameters + ---------- + conn : pymysql.Connection + The connection object used to execute queries. + + """ + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection.""" + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns + ------- + pymysql.Connection + + """ + return self._conn + + def insert_one_mysql_type( + self, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, + ) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Parameters + ---------- + id_ : int + int_test : int + integer_test : int + mediumint_test : int + smallint_test : int + tinyint_test : int + bigint_test : int + int_unsigned_test : int + bigint_unsigned_test : int + year_test : int + tinyint1_test : bool + bool_test : bool + boolean_test : bool + float_test : float + double_test : float + double_precision_test : float + real_test : float + decimal_test : decimal.Decimal + numeric_test : decimal.Decimal + char_test : str + varchar_test : str + tinytext_test : str + text_test : str + mediumtext_test : str + longtext_test : str + binary_test : memoryview + varbinary_test : memoryview + tinyblob_test : memoryview + blob_test : memoryview + mediumblob_test : memoryview + longblob_test : memoryview + bit_test : memoryview + date_test : datetime.date + datetime_test : datetime.datetime + datetime6_test : datetime.datetime + timestamp_test : datetime.datetime + time_test : datetime.timedelta + json_test : str + mood : enums.TestMysqlTypesMood + tag : enums.TestMysqlTypesTag + + """ + with self._conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + def insert_one_inner_mysql_type( + self, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, + ) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Parameters + ---------- + table_id : int + int_test : int | None + integer_test : int | None + mediumint_test : int | None + smallint_test : int | None + tinyint_test : int | None + bigint_test : int | None + int_unsigned_test : int | None + bigint_unsigned_test : int | None + year_test : int | None + tinyint1_test : bool | None + bool_test : bool | None + boolean_test : bool | None + float_test : float | None + double_test : float | None + double_precision_test : float | None + real_test : float | None + decimal_test : decimal.Decimal | None + numeric_test : decimal.Decimal | None + char_test : str | None + varchar_test : str | None + tinytext_test : str | None + text_test : str | None + mediumtext_test : str | None + longtext_test : str | None + binary_test : memoryview | None + varbinary_test : memoryview | None + tinyblob_test : memoryview | None + blob_test : memoryview | None + mediumblob_test : memoryview | None + longblob_test : memoryview | None + bit_test : memoryview | None + date_test : datetime.date | None + datetime_test : datetime.datetime | None + datetime6_test : datetime.datetime | None + timestamp_test : datetime.datetime | None + time_test : datetime.timedelta | None + json_test : str | None + mood : enums.TestInnerMysqlTypesMood | None + tag : enums.TestInnerMysqlTypesTag | None + + """ + with self._conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + def get_one_mysql_type(self, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + models.TestMysqlType + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + def get_one_inner_mysql_type(self, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Parameters + ---------- + table_id : int + + Returns + ------- + models.TestInnerMysqlType + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + def get_many_mysql_type(self, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + QueryResults[models.TestMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(self._conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + def get_many_inner_mysql_type(self, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Parameters + ---------- + table_id : int + + Returns + ------- + QueryResults[models.TestInnerMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + def get_many_nullable_inner_mysql_type(self, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Parameters + ---------- + table_id : int + int_test : int | None + + Returns + ------- + QueryResults[models.TestInnerMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + def get_one_date(self, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Parameters + ---------- + id_ : int + date_test : datetime.date + + Returns + ------- + datetime.date + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DATE, (id_, date_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_datetime(self, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Parameters + ---------- + id_ : int + datetime_test : datetime.datetime + + Returns + ------- + datetime.datetime + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_time(self, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Parameters + ---------- + id_ : int + time_test : datetime.timedelta + + Returns + ------- + datetime.timedelta + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_TIME, (id_, time_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_bool(self, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Parameters + ---------- + id_ : int + tinyint1_test : bool + + Returns + ------- + bool + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = cur.fetchone() + if row is None: + return None + return bool(row[0]) + + def get_one_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Parameters + ---------- + id_ : int + decimal_test : decimal.Decimal + + Returns + ------- + decimal.Decimal + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_blob(self, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Parameters + ---------- + id_ : int + blob_test : memoryview + + Returns + ------- + memoryview + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + def get_one_bit(self, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + memoryview + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BIT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + def get_one_year(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_YEAR, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_json(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_JSON, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_mood(self, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Parameters + ---------- + id_ : int + mood : enums.TestMysqlTypesMood + + Returns + ------- + enums.TestMysqlTypesMood + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_MOOD, (id_, mood)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + def get_one_tag(self, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + enums.TestMysqlTypesTag + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_TAG, (id_,)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + def get_many_date(self, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Parameters + ---------- + id_ : int + date_test : datetime.date + + Returns + ------- + QueryResults[datetime.date] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(self._conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + def get_many_time(self, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Parameters + ---------- + id_ : int + time_test : datetime.timedelta + + Returns + ------- + QueryResults[datetime.timedelta] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(self._conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + def get_many_bool(self, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Parameters + ---------- + id_ : int + tinyint1_test : bool + + Returns + ------- + QueryResults[bool] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(self._conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + def get_many_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Parameters + ---------- + id_ : int + decimal_test : decimal.Decimal + + Returns + ------- + QueryResults[decimal.Decimal] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(self._conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + def get_many_mood(self, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Parameters + ---------- + mood : enums.TestMysqlTypesMood + + Returns + ------- + QueryResults[enums.TestMysqlTypesMood] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(self._conn, GET_MANY_MOOD, _decode_hook, mood) + + def list_months(self) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Returns + ------- + QueryResults[str] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(self._conn, LIST_MONTHS, operator.itemgetter(0)) + + def count_mysql_types(self) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(COUNT_MYSQL_TYPES) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def update_varchar_test(self, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Parameters + ---------- + varchar_test : str + id_ : int + + Returns + ------- + int + The number of affected rows. This will be 0 for queries like `CREATE TABLE`. + + """ + with self._conn.cursor() as cur: + return cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + def delete_one_mysql_type(self, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + """ + with self._conn.cursor() as cur: + cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + def all_mysql_types_cursor(self) -> pymysql.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Returns + ------- + pymysql.cursors.Cursor + The result returned when executing the query. + + """ + cur = self._conn.cursor() + cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + def insert_exec_last_id(self, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Parameters + ---------- + name : str + + Returns + ------- + int + The id of the last affected row. Will be `None` if no rows are affected. + + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + def get_exec_last_id_name(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def insert_type_override(self, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Parameters + ---------- + id_ : int + text_test : UserString | None + + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + def get_type_override(self, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + models.TestTypeOverride + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + def get_reserved_arg(self, *, conn: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Parameters + ---------- + conn : str + + Returns + ------- + models.TestReservedArg + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_RESERVED_ARG, (conn,)) + row = cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + def insert_reserved_arg(self, *, id_: int, conn: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Parameters + ---------- + id_ : int + conn : str + + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_RESERVED_ARG, (id_, conn)) + + def touch_exec_last_id(self, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Parameters + ---------- + name : str + id_ : int + + Returns + ------- + int + The id of the last affected row. Will be `None` if no rows are affected. + + """ + with self._conn.cursor() as cur: + cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_pymysql/attrs/classes/queries_case.py b/test/driver_pymysql/attrs/classes/queries_case.py new file mode 100644 index 00000000..78550318 --- /dev/null +++ b/test/driver_pymysql/attrs/classes/queries_case.py @@ -0,0 +1,126 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_case.sql +"""Module containing queries from file queries_case.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("QueriesCase",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + +from test.driver_pymysql.attrs.classes import models + + +INSERT_CASE_ROW: typing.Final[str] = """-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) +""" + +GET_CASE_ROW: typing.Final[str] = """-- name: GetCaseRow :one +SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s +""" + +COUNT_CASE_ROWS: typing.Final[str] = """-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ +""" + + +class QueriesCase: + """Queries from file queries_case.sql. + + Parameters + ---------- + conn : pymysql.Connection + The connection object used to execute queries. + + """ + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection.""" + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns + ------- + pymysql.Connection + + """ + return self._conn + + def insert_case_row(self, *, id_: int, upper_dt: datetime.datetime, prec_dec: decimal.Decimal) -> None: + """Execute SQL query with `name: InsertCaseRow :exec`. + + ```sql + INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) + ``` + + Parameters + ---------- + id_ : int + upper_dt : datetime.datetime + prec_dec : decimal.Decimal + + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_CASE_ROW, (id_, upper_dt, prec_dec)) + + def get_case_row(self, *, id_: int) -> models.TestCaseSensitivity | None: + """Fetch one from the db using the SQL query with `name: GetCaseRow :one`. + + ```sql + SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + models.TestCaseSensitivity + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_CASE_ROW, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestCaseSensitivity(id_=row[0], upper_dt=row[1], prec_dec=row[2]) + + def count_case_rows(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: CountCaseRows :one`. + + ```sql + SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(COUNT_CASE_ROWS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/attrs/classes/queries_enum_override.py b/test/driver_pymysql/attrs/classes/queries_enum_override.py new file mode 100644 index 00000000..7bfca7e6 --- /dev/null +++ b/test/driver_pymysql/attrs/classes/queries_enum_override.py @@ -0,0 +1,240 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueriesEnumOverride", + "QueryResults", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.attrs.classes import enums +from test.driver_pymysql.attrs.classes import models + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db. + + Parameters + ---------- + conn + The connection object of type `pymysql.Connection` used to execute queries. + sql + The SQL statement that will be executed when fetching/iterating. + decode_hook + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args + Arguments that should be sent when executing the sql query. + + """ + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance.""" + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns + ------- + QueryResults[T] + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns + ------- + collections.abc.Sequence[T] + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns + ------- + T + The next decoded result. + + Raises + ------ + StopIteration + When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class QueriesEnumOverride: + """Queries from file queries_enum_override.sql. + + Parameters + ---------- + conn : pymysql.Connection + The connection object used to execute queries. + + """ + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection.""" + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns + ------- + pymysql.Connection + + """ + return self._conn + + def insert_enum_override(self, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Parameters + ---------- + id_ : int + mood_test : str + + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + def get_enum_override_mood(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + def list_enum_override_by_ids(self, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Parameters + ---------- + ids : collections.abc.Sequence[int] + + Returns + ------- + QueryResults[models.TestEnumOverride] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(self._conn, sql, _decode_hook, *ids) + + def count_enum_override_by_moods(self, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Parameters + ---------- + moods : collections.abc.Sequence[str] + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with self._conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/attrs/classes/queries_field_namings.py b/test/driver_pymysql/attrs/classes/queries_field_namings.py new file mode 100644 index 00000000..d0df628d --- /dev/null +++ b/test/driver_pymysql/attrs/classes/queries_field_namings.py @@ -0,0 +1,156 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_field_namings.sql +"""Module containing queries from file queries_field_namings.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "GetJoinedFieldNamingsRow", + "QueriesFieldNamings", +) + +import attrs +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.attrs.classes import models + + +@attrs.define() +class GetJoinedFieldNamingsRow: + """Model representing GetJoinedFieldNamingsRow. + + Attributes + ---------- + outputs : str + outputs_2 : str + + """ + + outputs: str + outputs_2: str + + +GET_FIELD_NAMING: typing.Final[str] = """-- name: GetFieldNaming :one +SELECT id, outputs +FROM test_field_namings +WHERE id = %s LIMIT 1 +""" + +GET_JOINED_FIELD_NAMINGS: typing.Final[str] = """-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = %s LIMIT 1 +""" + +SET_FIELD_NAMING_OUTPUTS: typing.Final[str] = """-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = %s +WHERE id = %s +""" + + +class QueriesFieldNamings: + """Queries from file queries_field_namings.sql. + + Parameters + ---------- + conn : pymysql.Connection + The connection object used to execute queries. + + """ + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection.""" + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns + ------- + pymysql.Connection + + """ + return self._conn + + def get_field_naming(self, *, id_: int) -> models.TestFieldNaming | None: + """Fetch one from the db using the SQL query with `name: GetFieldNaming :one`. + + ```sql + SELECT id, outputs + FROM test_field_namings + WHERE id = %s LIMIT 1 + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + models.TestFieldNaming + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_FIELD_NAMING, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestFieldNaming(id_=row[0], outputs=row[1]) + + def get_joined_field_namings(self, *, id_: int) -> GetJoinedFieldNamingsRow | None: + """Fetch one from the db using the SQL query with `name: GetJoinedFieldNamings :one`. + + ```sql + SELECT a.outputs, b.outputs + FROM test_field_namings a + JOIN test_field_namings b ON a.id = b.id + WHERE a.id = %s LIMIT 1 + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + GetJoinedFieldNamingsRow + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_JOINED_FIELD_NAMINGS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return GetJoinedFieldNamingsRow(outputs=row[0], outputs_2=row[1]) + + def set_field_naming_outputs(self, *, outputs: str, id_: int) -> None: + """Execute SQL query with `name: SetFieldNamingOutputs :exec`. + + ```sql + UPDATE test_field_namings + SET outputs = %s + WHERE id = %s + ``` + + Parameters + ---------- + outputs : str + id_ : int + + """ + with self._conn.cursor() as cur: + cur.execute(SET_FIELD_NAMING_OUTPUTS, (outputs, id_)) diff --git a/test/driver_pymysql/attrs/classes/queries_invalid_identifiers.py b/test/driver_pymysql/attrs/classes/queries_invalid_identifiers.py new file mode 100644 index 00000000..ce830737 --- /dev/null +++ b/test/driver_pymysql/attrs/classes/queries_invalid_identifiers.py @@ -0,0 +1,144 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_invalid_identifiers.sql +"""Module containing queries from file queries_invalid_identifiers.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("QueriesInvalidIdentifiers",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.attrs.classes import models + + +INSERT_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) +""" + +GET_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: GetInvalidIdentifiers :one +SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s +""" + +INSERT_THIRD_PARTY_STAT: typing.Final[str] = """-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) +""" + +GET_THIRD_PARTY_STAT: typing.Final[str] = """-- name: GetThirdPartyStat :one +SELECT id, total FROM `3rd_party_stats` WHERE id = %s +""" + + +class QueriesInvalidIdentifiers: + """Queries from file queries_invalid_identifiers.sql. + + Parameters + ---------- + conn : pymysql.Connection + The connection object used to execute queries. + + """ + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection.""" + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns + ------- + pymysql.Connection + + """ + return self._conn + + def insert_invalid_identifiers(self, *, id_: int, column_3p_: str | None, new_notes: str) -> None: + """Execute SQL query with `name: InsertInvalidIdentifiers :exec`. + + ```sql + INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) + ``` + + Parameters + ---------- + id_ : int + column_3p_ : str | None + new_notes : str + + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_INVALID_IDENTIFIERS, (id_, column_3p_, new_notes)) + + def get_invalid_identifiers(self, *, id_: int) -> models.TestInvalidIdentifier | None: + """Fetch one from the db using the SQL query with `name: GetInvalidIdentifiers :one`. + + ```sql + SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + models.TestInvalidIdentifier + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_INVALID_IDENTIFIERS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInvalidIdentifier(id_=row[0], column_3p_=row[1], new_notes=row[2], column__pct=row[3]) + + def insert_third_party_stat(self, *, id_: int, total: int) -> None: + """Execute SQL query with `name: InsertThirdPartyStat :exec`. + + ```sql + INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) + ``` + + Parameters + ---------- + id_ : int + total : int + + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_THIRD_PARTY_STAT, (id_, total)) + + def get_third_party_stat(self, *, id_: int) -> models.Model3RdPartyStat | None: + """Fetch one from the db using the SQL query with `name: GetThirdPartyStat :one`. + + ```sql + SELECT id, total FROM `3rd_party_stats` WHERE id = %s + ``` + + Parameters + ---------- + id_ : int + + Returns + ------- + models.Model3RdPartyStat + Result fetched from the db. Will be `None` if not found. + + """ + with self._conn.cursor() as cur: + cur.execute(GET_THIRD_PARTY_STAT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.Model3RdPartyStat(id_=row[0], total=row[1]) diff --git a/test/driver_pymysql/attrs/functions/__init__.py b/test/driver_pymysql/attrs/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/attrs/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/attrs/functions/enums.py b/test/driver_pymysql/attrs/functions/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_pymysql/attrs/functions/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_pymysql/attrs/functions/models.py b/test/driver_pymysql/attrs/functions/models.py new file mode 100644 index 00000000..d4be9c13 --- /dev/null +++ b/test/driver_pymysql/attrs/functions/models.py @@ -0,0 +1,322 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestEnumOverride", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +import attrs +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_pymysql.attrs.functions import enums + import collections.abc + import datetime + import decimal + + +@attrs.define() +class Model3RdPartyStat: + """Model representing Model3RdPartyStat. + + Attributes + ---------- + id_ : int + total : int + + """ + + id_: int + total: int + + +@attrs.define() +class TestCaseSensitivity: + """Model representing TestCaseSensitivity. + + Attributes + ---------- + id_ : int + upper_dt : datetime.datetime + prec_dec : decimal.Decimal + + """ + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +@attrs.define() +class TestEnumOverride: + """Model representing TestEnumOverride. + + Attributes + ---------- + id_ : int + mood_test : str + + """ + + id_: int + mood_test: str + + +@attrs.define() +class TestFieldNaming: + """Model representing TestFieldNaming. + + Attributes + ---------- + id_ : int + outputs : str + + """ + + id_: int + outputs: str + + +@attrs.define() +class TestInnerMysqlType: + """Model representing TestInnerMysqlType. + + Attributes + ---------- + table_id : int + int_test : int | None + integer_test : int | None + mediumint_test : int | None + smallint_test : int | None + tinyint_test : int | None + bigint_test : int | None + int_unsigned_test : int | None + bigint_unsigned_test : int | None + year_test : int | None + tinyint1_test : bool | None + bool_test : bool | None + boolean_test : bool | None + float_test : float | None + double_test : float | None + double_precision_test : float | None + real_test : float | None + decimal_test : decimal.Decimal | None + numeric_test : decimal.Decimal | None + char_test : str | None + varchar_test : str | None + tinytext_test : str | None + text_test : str | None + mediumtext_test : str | None + longtext_test : str | None + binary_test : memoryview | None + varbinary_test : memoryview | None + tinyblob_test : memoryview | None + blob_test : memoryview | None + mediumblob_test : memoryview | None + longblob_test : memoryview | None + bit_test : memoryview | None + date_test : datetime.date | None + datetime_test : datetime.datetime | None + datetime6_test : datetime.datetime | None + timestamp_test : datetime.datetime | None + time_test : datetime.timedelta | None + json_test : str | None + mood : enums.TestInnerMysqlTypesMood | None + tag : enums.TestInnerMysqlTypesTag | None + + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +@attrs.define() +class TestInvalidIdentifier: + """Model representing TestInvalidIdentifier. + + Attributes + ---------- + id_ : int + column_3p_ : str | None + new_notes : str + column__pct : str | None + + """ + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +@attrs.define() +class TestMysqlType: + """Model representing TestMysqlType. + + Attributes + ---------- + id_ : int + int_test : int + integer_test : int + mediumint_test : int + smallint_test : int + tinyint_test : int + bigint_test : int + int_unsigned_test : int + bigint_unsigned_test : int + year_test : int + tinyint1_test : bool + bool_test : bool + boolean_test : bool + float_test : float + double_test : float + double_precision_test : float + real_test : float + decimal_test : decimal.Decimal + numeric_test : decimal.Decimal + char_test : str + varchar_test : str + tinytext_test : str + text_test : str + mediumtext_test : str + longtext_test : str + binary_test : memoryview + varbinary_test : memoryview + tinyblob_test : memoryview + blob_test : memoryview + mediumblob_test : memoryview + longblob_test : memoryview + bit_test : memoryview + date_test : datetime.date + datetime_test : datetime.datetime + datetime6_test : datetime.datetime + timestamp_test : datetime.datetime + time_test : datetime.timedelta + json_test : str + mood : enums.TestMysqlTypesMood + tag : enums.TestMysqlTypesTag + + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +@attrs.define() +class TestReservedArg: + """Model representing TestReservedArg. + + Attributes + ---------- + id_ : int + conn : str + + """ + + id_: int + conn: str + + +@attrs.define() +class TestTypeOverride: + """Model representing TestTypeOverride. + + Attributes + ---------- + id_ : int + text_test : UserString | None + + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_pymysql/attrs/functions/queries.py b/test/driver_pymysql/attrs/functions/queries.py new file mode 100644 index 00000000..fb9d5647 --- /dev/null +++ b/test/driver_pymysql/attrs/functions/queries.py @@ -0,0 +1,1693 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "all_mysql_types_cursor", + "count_mysql_types", + "delete_one_mysql_type", + "get_exec_last_id_name", + "get_many_bool", + "get_many_date", + "get_many_decimal", + "get_many_inner_mysql_type", + "get_many_mood", + "get_many_mysql_type", + "get_many_nullable_inner_mysql_type", + "get_many_time", + "get_one_bit", + "get_one_blob", + "get_one_bool", + "get_one_date", + "get_one_datetime", + "get_one_decimal", + "get_one_inner_mysql_type", + "get_one_json", + "get_one_mood", + "get_one_mysql_type", + "get_one_tag", + "get_one_time", + "get_one_year", + "get_reserved_arg", + "get_type_override", + "insert_exec_last_id", + "insert_one_inner_mysql_type", + "insert_one_mysql_type", + "insert_reserved_arg", + "insert_type_override", + "list_months", + "touch_exec_last_id", + "update_varchar_test", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.attrs.functions import enums +from test.driver_pymysql.attrs.functions import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db. + + Parameters + ---------- + conn + The connection object of type `pymysql.Connection` used to execute queries. + sql + The SQL statement that will be executed when fetching/iterating. + decode_hook + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args + Arguments that should be sent when executing the sql query. + + """ + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance.""" + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns + ------- + QueryResults[T] + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns + ------- + collections.abc.Sequence[T] + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns + ------- + T + The next decoded result. + + Raises + ------ + StopIteration + When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_one_mysql_type( + conn: pymysql.Connection, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, +) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + int_test : int + integer_test : int + mediumint_test : int + smallint_test : int + tinyint_test : int + bigint_test : int + int_unsigned_test : int + bigint_unsigned_test : int + year_test : int + tinyint1_test : bool + bool_test : bool + boolean_test : bool + float_test : float + double_test : float + double_precision_test : float + real_test : float + decimal_test : decimal.Decimal + numeric_test : decimal.Decimal + char_test : str + varchar_test : str + tinytext_test : str + text_test : str + mediumtext_test : str + longtext_test : str + binary_test : memoryview + varbinary_test : memoryview + tinyblob_test : memoryview + blob_test : memoryview + mediumblob_test : memoryview + longblob_test : memoryview + bit_test : memoryview + date_test : datetime.date + datetime_test : datetime.datetime + datetime6_test : datetime.datetime + timestamp_test : datetime.datetime + time_test : datetime.timedelta + json_test : str + mood : enums.TestMysqlTypesMood + tag : enums.TestMysqlTypesTag + + """ + with conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + +def insert_one_inner_mysql_type( + conn: pymysql.Connection, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, +) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + table_id : int + int_test : int | None + integer_test : int | None + mediumint_test : int | None + smallint_test : int | None + tinyint_test : int | None + bigint_test : int | None + int_unsigned_test : int | None + bigint_unsigned_test : int | None + year_test : int | None + tinyint1_test : bool | None + bool_test : bool | None + boolean_test : bool | None + float_test : float | None + double_test : float | None + double_precision_test : float | None + real_test : float | None + decimal_test : decimal.Decimal | None + numeric_test : decimal.Decimal | None + char_test : str | None + varchar_test : str | None + tinytext_test : str | None + text_test : str | None + mediumtext_test : str | None + longtext_test : str | None + binary_test : memoryview | None + varbinary_test : memoryview | None + tinyblob_test : memoryview | None + blob_test : memoryview | None + mediumblob_test : memoryview | None + longblob_test : memoryview | None + bit_test : memoryview | None + date_test : datetime.date | None + datetime_test : datetime.datetime | None + datetime6_test : datetime.datetime | None + timestamp_test : datetime.datetime | None + time_test : datetime.timedelta | None + json_test : str | None + mood : enums.TestInnerMysqlTypesMood | None + tag : enums.TestInnerMysqlTypesTag | None + + """ + with conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + +def get_one_mysql_type(conn: pymysql.Connection, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + models.TestMysqlType + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + +def get_one_inner_mysql_type(conn: pymysql.Connection, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + table_id : int + + Returns + ------- + models.TestInnerMysqlType + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + +def get_many_mysql_type(conn: pymysql.Connection, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + QueryResults[models.TestMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + +def get_many_inner_mysql_type(conn: pymysql.Connection, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + table_id : int + + Returns + ------- + QueryResults[models.TestInnerMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + +def get_many_nullable_inner_mysql_type(conn: pymysql.Connection, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + table_id : int + int_test : int | None + + Returns + ------- + QueryResults[models.TestInnerMysqlType] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + +def get_one_date(conn: pymysql.Connection, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + date_test : datetime.date + + Returns + ------- + datetime.date + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DATE, (id_, date_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_datetime(conn: pymysql.Connection, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + datetime_test : datetime.datetime + + Returns + ------- + datetime.datetime + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_time(conn: pymysql.Connection, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + time_test : datetime.timedelta + + Returns + ------- + datetime.timedelta + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_TIME, (id_, time_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_bool(conn: pymysql.Connection, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + tinyint1_test : bool + + Returns + ------- + bool + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = cur.fetchone() + if row is None: + return None + return bool(row[0]) + + +def get_one_decimal(conn: pymysql.Connection, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + decimal_test : decimal.Decimal + + Returns + ------- + decimal.Decimal + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_blob(conn: pymysql.Connection, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + blob_test : memoryview + + Returns + ------- + memoryview + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +def get_one_bit(conn: pymysql.Connection, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + memoryview + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BIT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +def get_one_year(conn: pymysql.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_YEAR, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_json(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_JSON, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_mood(conn: pymysql.Connection, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + mood : enums.TestMysqlTypesMood + + Returns + ------- + enums.TestMysqlTypesMood + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_MOOD, (id_, mood)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + +def get_one_tag(conn: pymysql.Connection, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + enums.TestMysqlTypesTag + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_TAG, (id_,)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + +def get_many_date(conn: pymysql.Connection, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + date_test : datetime.date + + Returns + ------- + QueryResults[datetime.date] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + +def get_many_time(conn: pymysql.Connection, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + time_test : datetime.timedelta + + Returns + ------- + QueryResults[datetime.timedelta] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + +def get_many_bool(conn: pymysql.Connection, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + tinyint1_test : bool + + Returns + ------- + QueryResults[bool] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + +def get_many_decimal(conn: pymysql.Connection, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + decimal_test : decimal.Decimal + + Returns + ------- + QueryResults[decimal.Decimal] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + +def get_many_mood(conn: pymysql.Connection, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + mood : enums.TestMysqlTypesMood + + Returns + ------- + QueryResults[enums.TestMysqlTypesMood] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(conn, GET_MANY_MOOD, _decode_hook, mood) + + +def list_months(conn: pymysql.Connection) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + + Returns + ------- + QueryResults[str] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + return QueryResults(conn, LIST_MONTHS, operator.itemgetter(0)) + + +def count_mysql_types(conn: pymysql.Connection) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(COUNT_MYSQL_TYPES) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def update_varchar_test(conn: pymysql.Connection, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + varchar_test : str + id_ : int + + Returns + ------- + int + The number of affected rows. This will be 0 for queries like `CREATE TABLE`. + + """ + with conn.cursor() as cur: + return cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + +def delete_one_mysql_type(conn: pymysql.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + """ + with conn.cursor() as cur: + cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + +def all_mysql_types_cursor(conn: pymysql.Connection) -> pymysql.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + + Returns + ------- + pymysql.cursors.Cursor + The result returned when executing the query. + + """ + cur = conn.cursor() + cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + +def insert_exec_last_id(conn: pymysql.Connection, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + name : str + + Returns + ------- + int + The id of the last affected row. Will be `None` if no rows are affected. + + """ + with conn.cursor() as cur: + cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + +def get_exec_last_id_name(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def insert_type_override(conn: pymysql.Connection, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + text_test : UserString | None + + """ + with conn.cursor() as cur: + cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + +def get_type_override(conn: pymysql.Connection, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + models.TestTypeOverride + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + +def get_reserved_arg(conn: pymysql.Connection, *, conn_2: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + conn_2 : str + + Returns + ------- + models.TestReservedArg + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_RESERVED_ARG, (conn_2,)) + row = cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + +def insert_reserved_arg(conn: pymysql.Connection, *, id_: int, conn_2: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + conn_2 : str + + """ + with conn.cursor() as cur: + cur.execute(INSERT_RESERVED_ARG, (id_, conn_2)) + + +def touch_exec_last_id(conn: pymysql.Connection, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + name : str + id_ : int + + Returns + ------- + int + The id of the last affected row. Will be `None` if no rows are affected. + + """ + with conn.cursor() as cur: + cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_pymysql/attrs/functions/queries_case.py b/test/driver_pymysql/attrs/functions/queries_case.py new file mode 100644 index 00000000..a9f6c9e5 --- /dev/null +++ b/test/driver_pymysql/attrs/functions/queries_case.py @@ -0,0 +1,111 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_case.sql +"""Module containing queries from file queries_case.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "count_case_rows", + "get_case_row", + "insert_case_row", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + +from test.driver_pymysql.attrs.functions import models + + +INSERT_CASE_ROW: typing.Final[str] = """-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) +""" + +GET_CASE_ROW: typing.Final[str] = """-- name: GetCaseRow :one +SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s +""" + +COUNT_CASE_ROWS: typing.Final[str] = """-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ +""" + + +def insert_case_row(conn: pymysql.Connection, *, id_: int, upper_dt: datetime.datetime, prec_dec: decimal.Decimal) -> None: + """Execute SQL query with `name: InsertCaseRow :exec`. + + ```sql + INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + upper_dt : datetime.datetime + prec_dec : decimal.Decimal + + """ + with conn.cursor() as cur: + cur.execute(INSERT_CASE_ROW, (id_, upper_dt, prec_dec)) + + +def get_case_row(conn: pymysql.Connection, *, id_: int) -> models.TestCaseSensitivity | None: + """Fetch one from the db using the SQL query with `name: GetCaseRow :one`. + + ```sql + SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + models.TestCaseSensitivity + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_CASE_ROW, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestCaseSensitivity(id_=row[0], upper_dt=row[1], prec_dec=row[2]) + + +def count_case_rows(conn: pymysql.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: CountCaseRows :one`. + + ```sql + SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(COUNT_CASE_ROWS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/attrs/functions/queries_enum_override.py b/test/driver_pymysql/attrs/functions/queries_enum_override.py new file mode 100644 index 00000000..41780800 --- /dev/null +++ b/test/driver_pymysql/attrs/functions/queries_enum_override.py @@ -0,0 +1,227 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "count_enum_override_by_moods", + "get_enum_override_mood", + "insert_enum_override", + "list_enum_override_by_ids", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.attrs.functions import enums +from test.driver_pymysql.attrs.functions import models + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db. + + Parameters + ---------- + conn + The connection object of type `pymysql.Connection` used to execute queries. + sql + The SQL statement that will be executed when fetching/iterating. + decode_hook + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args + Arguments that should be sent when executing the sql query. + + """ + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance.""" + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns + ------- + QueryResults[T] + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns + ------- + collections.abc.Sequence[T] + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns + ------- + T + The next decoded result. + + Raises + ------ + StopIteration + When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_enum_override(conn: pymysql.Connection, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + mood_test : str + + """ + with conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + +def get_enum_override_mood(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + str + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + +def list_enum_override_by_ids(conn: pymysql.Connection, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + ids : collections.abc.Sequence[int] + + Returns + ------- + QueryResults[models.TestEnumOverride] + Helper class that allows both iteration and normal fetching of data from the db. + + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *ids) + + +def count_enum_override_by_moods(conn: pymysql.Connection, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + moods : collections.abc.Sequence[str] + + Returns + ------- + int + Result fetched from the db. Will be `None` if not found. + + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/attrs/functions/queries_field_namings.py b/test/driver_pymysql/attrs/functions/queries_field_namings.py new file mode 100644 index 00000000..2d92c7ca --- /dev/null +++ b/test/driver_pymysql/attrs/functions/queries_field_namings.py @@ -0,0 +1,139 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_field_namings.sql +"""Module containing queries from file queries_field_namings.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "GetJoinedFieldNamingsRow", + "get_field_naming", + "get_joined_field_namings", + "set_field_naming_outputs", +) + +import attrs +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.attrs.functions import models + + +@attrs.define() +class GetJoinedFieldNamingsRow: + """Model representing GetJoinedFieldNamingsRow. + + Attributes + ---------- + outputs : str + outputs_2 : str + + """ + + outputs: str + outputs_2: str + + +GET_FIELD_NAMING: typing.Final[str] = """-- name: GetFieldNaming :one +SELECT id, outputs +FROM test_field_namings +WHERE id = %s LIMIT 1 +""" + +GET_JOINED_FIELD_NAMINGS: typing.Final[str] = """-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = %s LIMIT 1 +""" + +SET_FIELD_NAMING_OUTPUTS: typing.Final[str] = """-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = %s +WHERE id = %s +""" + + +def get_field_naming(conn: pymysql.Connection, *, id_: int) -> models.TestFieldNaming | None: + """Fetch one from the db using the SQL query with `name: GetFieldNaming :one`. + + ```sql + SELECT id, outputs + FROM test_field_namings + WHERE id = %s LIMIT 1 + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + models.TestFieldNaming + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_FIELD_NAMING, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestFieldNaming(id_=row[0], outputs=row[1]) + + +def get_joined_field_namings(conn: pymysql.Connection, *, id_: int) -> GetJoinedFieldNamingsRow | None: + """Fetch one from the db using the SQL query with `name: GetJoinedFieldNamings :one`. + + ```sql + SELECT a.outputs, b.outputs + FROM test_field_namings a + JOIN test_field_namings b ON a.id = b.id + WHERE a.id = %s LIMIT 1 + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + GetJoinedFieldNamingsRow + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_JOINED_FIELD_NAMINGS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return GetJoinedFieldNamingsRow(outputs=row[0], outputs_2=row[1]) + + +def set_field_naming_outputs(conn: pymysql.Connection, *, outputs: str, id_: int) -> None: + """Execute SQL query with `name: SetFieldNamingOutputs :exec`. + + ```sql + UPDATE test_field_namings + SET outputs = %s + WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + outputs : str + id_ : int + + """ + with conn.cursor() as cur: + cur.execute(SET_FIELD_NAMING_OUTPUTS, (outputs, id_)) diff --git a/test/driver_pymysql/attrs/functions/queries_invalid_identifiers.py b/test/driver_pymysql/attrs/functions/queries_invalid_identifiers.py new file mode 100644 index 00000000..85e7710a --- /dev/null +++ b/test/driver_pymysql/attrs/functions/queries_invalid_identifiers.py @@ -0,0 +1,133 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_invalid_identifiers.sql +"""Module containing queries from file queries_invalid_identifiers.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "get_invalid_identifiers", + "get_third_party_stat", + "insert_invalid_identifiers", + "insert_third_party_stat", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.attrs.functions import models + + +INSERT_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) +""" + +GET_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: GetInvalidIdentifiers :one +SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s +""" + +INSERT_THIRD_PARTY_STAT: typing.Final[str] = """-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) +""" + +GET_THIRD_PARTY_STAT: typing.Final[str] = """-- name: GetThirdPartyStat :one +SELECT id, total FROM `3rd_party_stats` WHERE id = %s +""" + + +def insert_invalid_identifiers(conn: pymysql.Connection, *, id_: int, column_3p_: str | None, new_notes: str) -> None: + """Execute SQL query with `name: InsertInvalidIdentifiers :exec`. + + ```sql + INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + column_3p_ : str | None + new_notes : str + + """ + with conn.cursor() as cur: + cur.execute(INSERT_INVALID_IDENTIFIERS, (id_, column_3p_, new_notes)) + + +def get_invalid_identifiers(conn: pymysql.Connection, *, id_: int) -> models.TestInvalidIdentifier | None: + """Fetch one from the db using the SQL query with `name: GetInvalidIdentifiers :one`. + + ```sql + SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + models.TestInvalidIdentifier + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_INVALID_IDENTIFIERS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInvalidIdentifier(id_=row[0], column_3p_=row[1], new_notes=row[2], column__pct=row[3]) + + +def insert_third_party_stat(conn: pymysql.Connection, *, id_: int, total: int) -> None: + """Execute SQL query with `name: InsertThirdPartyStat :exec`. + + ```sql + INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + total : int + + """ + with conn.cursor() as cur: + cur.execute(INSERT_THIRD_PARTY_STAT, (id_, total)) + + +def get_third_party_stat(conn: pymysql.Connection, *, id_: int) -> models.Model3RdPartyStat | None: + """Fetch one from the db using the SQL query with `name: GetThirdPartyStat :one`. + + ```sql + SELECT id, total FROM `3rd_party_stats` WHERE id = %s + ``` + + Parameters + ---------- + conn : pymysql.Connection + Connection object of type `pymysql.Connection` used to execute the query. + id_ : int + + Returns + ------- + models.Model3RdPartyStat + Result fetched from the db. Will be `None` if not found. + + """ + with conn.cursor() as cur: + cur.execute(GET_THIRD_PARTY_STAT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.Model3RdPartyStat(id_=row[0], total=row[1]) diff --git a/test/driver_pymysql/attrs/ruff.toml b/test/driver_pymysql/attrs/ruff.toml new file mode 100644 index 00000000..3047247e --- /dev/null +++ b/test/driver_pymysql/attrs/ruff.toml @@ -0,0 +1,5 @@ +extend="../../../ruff.toml" + + +[lint.pydocstyle] +convention = "numpy" \ No newline at end of file diff --git a/test/driver_pymysql/attrs/test_pymysql_attrs_classes.py b/test/driver_pymysql/attrs/test_pymysql_attrs_classes.py new file mode 100644 index 00000000..aa9a5135 --- /dev/null +++ b/test/driver_pymysql/attrs/test_pymysql_attrs_classes.py @@ -0,0 +1,1104 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import attrs +import pymysql +import pymysql.cursors +import pytest + +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.attrs.classes import enums +from test.driver_pymysql.attrs.classes import models +from test.driver_pymysql.attrs.classes import queries +from test.driver_pymysql.attrs.classes import queries_case +from test.driver_pymysql.attrs.classes import queries_enum_override +from test.driver_pymysql.attrs.classes import queries_field_namings +from test.driver_pymysql.attrs.classes import queries_invalid_identifiers + +# Fixed ids: the MySQL tables are shared by every pymysql/asyncmy suite in the +# session, so each test file owns a distinct id range. This file: 2000-2499. +MAIN_ID = 2000 +TYPE_OVERRIDE_ID = 2100 +TYPE_OVERRIDE_NONE_ID = 2101 +ENUM_OVERRIDE_ID = 2150 +ENUM_OVERRIDE_ID_2 = 2151 +CASE_ID = 2200 +RESERVED_ARG_ID = 2250 +FIELD_NAMING_ID = 2300 +INVALID_IDENTIFIER_ID = 2350 +THIRD_PARTY_ID = 2360 +THIRD_PARTY_TOTAL = 9002 + +CASE_DT = datetime.datetime(2026, 7, 19, 8, 15) +CASE_DEC = decimal.Decimal("12.34") +RESERVED_ARG_VALUE = "pymysql-attrs-classes-conn" +EXEC_LAST_ID_NAME = "pymysql-attrs-classes" +UPDATED_VARCHAR = "updated varchar" +# decimal(12,4) and numeric(10,2) come back padded to their full scale. +DECIMAL_PADDED = "1234.5000" +NUMERIC_PADDED = "87.60" +EXPECTED_MONTH = "2026-01" + + +class TestPymysqlAttrsClasses: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=TYPE_OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MAIN_ID, + int_test=42, + integer_test=43, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 10, + year_test=2026, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.e, + double_precision_test=1.41421, + real_test=math.pi, + decimal_test=decimal.Decimal("1234.5"), + numeric_test=decimal.Decimal("87.6"), + char_test="ABC", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"bin-test".ljust(16, b"\x00")), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tiny blob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"medium blob"), + longblob_test=memoryview(b"long blob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 1), + datetime_test=datetime.datetime(2026, 1, 15, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 15, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 15, 6, 30, 45), + time_test=datetime.timedelta(hours=13, minutes=14, seconds=15), + json_test=json.dumps({"foo": "bar"}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=None, + tinyint1_test=None, + bool_test=None, + boolean_test=None, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=None, + varbinary_test=model.varbinary_test, + tinyblob_test=None, + blob_test=None, + mediumblob_test=None, + longblob_test=model.longblob_test, + bit_test=None, + date_test=None, + datetime_test=None, + datetime6_test=None, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=None, + tag=enums.TestInnerMysqlTypesTag.ALPHA, + ) + + @pytest.fixture(scope="class") + def queries_obj(self, pymysql_conn: pymysql.Connection) -> queries.Queries: + return queries.Queries(conn=pymysql_conn) + + @pytest.fixture(scope="class") + def case_obj(self, pymysql_conn: pymysql.Connection) -> queries_case.QueriesCase: + return queries_case.QueriesCase(conn=pymysql_conn) + + @pytest.fixture(scope="class") + def enum_override_obj(self, pymysql_conn: pymysql.Connection) -> queries_enum_override.QueriesEnumOverride: + return queries_enum_override.QueriesEnumOverride(conn=pymysql_conn) + + @pytest.fixture(scope="class") + def field_namings_obj(self, pymysql_conn: pymysql.Connection) -> queries_field_namings.QueriesFieldNamings: + return queries_field_namings.QueriesFieldNamings(conn=pymysql_conn) + + @pytest.fixture(scope="class") + def invalid_identifiers_obj(self, pymysql_conn: pymysql.Connection) -> queries_invalid_identifiers.QueriesInvalidIdentifiers: + return queries_invalid_identifiers.QueriesInvalidIdentifiers(conn=pymysql_conn) + + def test_conn_attr(self, queries_obj: queries.Queries) -> None: + assert isinstance(queries_obj.conn, pymysql.Connection) + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::insert") + def test_insert( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + queries_obj.insert_one_mysql_type( + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::inner_insert", depends=["PymysqlTestAttrsClasses::insert"]) + def test_inner_insert( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + queries_obj.insert_one_inner_mysql_type( + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_one", depends=["PymysqlTestAttrsClasses::inner_insert"]) + def test_get_one( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_mysql_type(id_=model.id_) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + + # MySQL pads decimals to their declared scale and binary(16) to full + # width, keeps datetime(6) microseconds, and normalizes json spacing. + assert str(result.decimal_test) == DECIMAL_PADDED + assert str(result.numeric_test) == NUMERIC_PADDED + assert bytes(result.binary_test) == b"bin-test".ljust(16, b"\x00") + assert bytes(result.bit_test) == b"\x80" + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + assert isinstance(result.time_test, datetime.timedelta) + assert result.datetime6_test == model.datetime6_test + assert json.loads(result.json_test) == json.loads(model.json_test) + assert attrs.evolve(result, json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_one_none", depends=["PymysqlTestAttrsClasses::get_one"]) + def test_get_one_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_mysql_type(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_one_inner", depends=["PymysqlTestAttrsClasses::get_one_none"]) + def test_get_one_inner( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = queries_obj.get_one_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_one_inner_none", depends=["PymysqlTestAttrsClasses::get_one_inner"]) + def test_get_one_inner_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_inner_mysql_type(table_id=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_date", depends=["PymysqlTestAttrsClasses::get_one_inner_none"]) + def test_get_date( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_date(id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_date_none", depends=["PymysqlTestAttrsClasses::get_date"]) + def test_get_date_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_date(id_=0, date_test=model.date_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_datetime", depends=["PymysqlTestAttrsClasses::get_date_none"]) + def test_get_datetime( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_datetime(id_=model.id_, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_datetime_none", depends=["PymysqlTestAttrsClasses::get_datetime"]) + def test_get_datetime_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_datetime(id_=0, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_time", depends=["PymysqlTestAttrsClasses::get_datetime_none"]) + def test_get_time( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_time(id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_time_none", depends=["PymysqlTestAttrsClasses::get_time"]) + def test_get_time_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_time(id_=0, time_test=model.time_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_bool", depends=["PymysqlTestAttrsClasses::get_time_none"]) + def test_get_bool( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_bool(id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, bool) + assert result is True + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_bool_none", depends=["PymysqlTestAttrsClasses::get_bool"]) + def test_get_bool_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_bool(id_=0, tinyint1_test=False) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_decimal", depends=["PymysqlTestAttrsClasses::get_bool_none"]) + def test_get_decimal( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_decimal(id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + assert result == model.decimal_test + assert str(result) == DECIMAL_PADDED + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_decimal_none", depends=["PymysqlTestAttrsClasses::get_decimal"]) + def test_get_decimal_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_decimal(id_=0, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_blob", depends=["PymysqlTestAttrsClasses::get_decimal_none"]) + def test_get_blob( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_blob(id_=model.id_, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_blob_none", depends=["PymysqlTestAttrsClasses::get_blob"]) + def test_get_blob_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_blob(id_=0, blob_test=model.blob_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_bit", depends=["PymysqlTestAttrsClasses::get_blob_none"]) + def test_get_bit( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_bit(id_=model.id_) + + assert result is not None + assert isinstance(result, memoryview) + assert bytes(result) == b"\x80" + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_bit_none", depends=["PymysqlTestAttrsClasses::get_bit"]) + def test_get_bit_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_bit(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_year", depends=["PymysqlTestAttrsClasses::get_bit_none"]) + def test_get_year( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_year(id_=model.id_) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_year_none", depends=["PymysqlTestAttrsClasses::get_year"]) + def test_get_year_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_year(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_json", depends=["PymysqlTestAttrsClasses::get_year_none"]) + def test_get_json( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_json(id_=model.id_) + + assert result is not None + assert isinstance(result, str) + # MySQL normalizes json spacing; never compare the raw strings. + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_json_none", depends=["PymysqlTestAttrsClasses::get_json"]) + def test_get_json_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_json(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_mood", depends=["PymysqlTestAttrsClasses::get_json_none"]) + def test_get_mood( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_mood(id_=model.id_, mood=model.mood) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_mood_none", depends=["PymysqlTestAttrsClasses::get_mood"]) + def test_get_mood_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_mood(id_=0, mood=model.mood) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_tag", depends=["PymysqlTestAttrsClasses::get_mood_none"]) + def test_get_tag( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_tag(id_=model.id_) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.BETA + assert result == model.tag + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_tag_none", depends=["PymysqlTestAttrsClasses::get_tag"]) + def test_get_tag_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_tag(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many", depends=["PymysqlTestAttrsClasses::get_tag_none"]) + def test_get_many(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mysql_type(id_=model.id_) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + assert json.loads(results[0].json_test) == json.loads(model.json_test) + assert attrs.evolve(results[0], json_test=model.json_test) == model + + results = result() + assert len(results) == 1 + assert attrs.evolve(results[0], json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_iter", depends=["PymysqlTestAttrsClasses::get_many"]) + def test_get_many_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_mysql_type(id_=model.id_): + assert result is not None + assert isinstance(result, models.TestMysqlType) + assert attrs.evolve(result, json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_inner", depends=["PymysqlTestAttrsClasses::get_many_iter"]) + def test_get_many_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], models.TestInnerMysqlType) + assert results[0] == inner_model + + results = result() + assert results[0] == inner_model + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_inner_iter", depends=["PymysqlTestAttrsClasses::get_many_inner"]) + def test_get_many_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + for result in queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_many_nullable_inner", + depends=["PymysqlTestAttrsClasses::get_many_inner_iter"], + ) + def test_get_many_nullable_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the query uses the NULL-safe <=> comparison. + result = queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = result() + assert isinstance(results[0], models.TestInnerMysqlType) + assert results[0] == inner_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_many_nullable_inner_iter", + depends=["PymysqlTestAttrsClasses::get_many_nullable_inner"], + ) + def test_get_many_nullable_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + for result in queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_many_date", + depends=["PymysqlTestAttrsClasses::get_many_nullable_inner_iter"], + ) + def test_get_many_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_date(id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], datetime.date) + assert results[0] == model.date_test + + results = result() + assert results[0] == model.date_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_date_iter", depends=["PymysqlTestAttrsClasses::get_many_date"]) + def test_get_many_date_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_date(id_=model.id_, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_time", depends=["PymysqlTestAttrsClasses::get_many_date_iter"]) + def test_get_many_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_time(id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], datetime.timedelta) + assert results[0] == model.time_test + + results = result() + assert results[0] == model.time_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_time_iter", depends=["PymysqlTestAttrsClasses::get_many_time"]) + def test_get_many_time_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_time(id_=model.id_, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_bool", depends=["PymysqlTestAttrsClasses::get_many_time_iter"]) + def test_get_many_bool(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_bool(id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], bool) + assert results[0] is True + + results = result() + assert results[0] is True + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_bool_iter", depends=["PymysqlTestAttrsClasses::get_many_bool"]) + def test_get_many_bool_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_bool(id_=model.id_, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + assert result is True + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_many_decimal", + depends=["PymysqlTestAttrsClasses::get_many_bool_iter"], + ) + def test_get_many_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_decimal(id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], decimal.Decimal) + assert results[0] == model.decimal_test + assert str(results[0]) == DECIMAL_PADDED + + results = result() + assert results[0] == model.decimal_test + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_many_decimal_iter", + depends=["PymysqlTestAttrsClasses::get_many_decimal"], + ) + def test_get_many_decimal_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_decimal(id_=model.id_, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + assert result == model.decimal_test + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_many_mood", + depends=["PymysqlTestAttrsClasses::get_many_decimal_iter"], + ) + def test_get_many_mood(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mood(mood=model.mood) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert len(results) == 1 + assert isinstance(results[0], enums.TestMysqlTypesMood) + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + results = result() + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_many_mood_iter", depends=["PymysqlTestAttrsClasses::get_many_mood"]) + def test_get_many_mood_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_mood(mood=model.mood): + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::list_months", depends=["PymysqlTestAttrsClasses::get_many_mood_iter"]) + def test_list_months(self, queries_obj: queries.Queries) -> None: + # DATE_FORMAT emits %% in the stored SQL; the empty argument tuple + # still goes through pymysql's %-substitution, halving it back. + result = queries_obj.list_months() + + assert result is not None + assert isinstance(result, queries.QueryResults) + months = result() + assert list(months) == [EXPECTED_MONTH] + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::list_months_iter", depends=["PymysqlTestAttrsClasses::list_months"]) + def test_list_months_iter(self, queries_obj: queries.Queries) -> None: + months = list(queries_obj.list_months()) + assert months == [EXPECTED_MONTH] + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::count", depends=["PymysqlTestAttrsClasses::list_months_iter"]) + def test_count_mysql_types(self, queries_obj: queries.Queries) -> None: + result = queries_obj.count_mysql_types() + + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::all_cursor", depends=["PymysqlTestAttrsClasses::count"]) + def test_all_mysql_types_cursor(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + cur = queries_obj.all_mysql_types_cursor() + + assert isinstance(cur, pymysql.cursors.Cursor) + rows = cur.fetchall() + # The shared table may carry other files' rows; assert on our own. + assert model.id_ in {row[0] for row in rows} + cur.close() + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::update_rows", depends=["PymysqlTestAttrsClasses::all_cursor"]) + def test_update_varchar_rows(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.update_varchar_test(varchar_test=UPDATED_VARCHAR, id_=model.id_) + + assert isinstance(result, int) + assert result == 1 + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::update_rows_noop", depends=["PymysqlTestAttrsClasses::update_rows"]) + def test_update_varchar_rows_noop(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + # Without CLIENT.FOUND_ROWS MySQL reports changed rows, so setting + # the same value again affects nothing. + result = queries_obj.update_varchar_test(varchar_test=UPDATED_VARCHAR, id_=model.id_) + + assert result == 0 + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::insert_last_id", depends=["PymysqlTestAttrsClasses::update_rows_noop"]) + def test_insert_exec_last_id(self, queries_obj: queries.Queries) -> None: + # The AUTO_INCREMENT counter persists across runs; never assert an + # exact id. + new_id = queries_obj.insert_exec_last_id(name=EXEC_LAST_ID_NAME) + + assert new_id is not None + assert isinstance(new_id, int) + assert new_id > 0 + assert queries_obj.get_exec_last_id_name(id_=new_id) == EXEC_LAST_ID_NAME + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::delete", depends=["PymysqlTestAttrsClasses::insert_last_id"]) + def test_delete_mysql_type(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + queries_obj.delete_one_mysql_type(id_=model.id_) + + assert queries_obj.get_one_mysql_type(id_=model.id_) is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::insert_type_override") + def test_insert_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + queries_obj.insert_type_override(id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_type_override", + depends=["PymysqlTestAttrsClasses::insert_type_override"], + ) + def test_get_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = queries_obj.get_type_override(id_=override_model.id_) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_type_override_none_value", + depends=["PymysqlTestAttrsClasses::get_type_override"], + ) + def test_get_type_override_none_value(self, queries_obj: queries.Queries) -> None: + # The override target is nullable: NULL must come back as None + # without passing through UserString. + queries_obj.insert_type_override(id_=TYPE_OVERRIDE_NONE_ID, text_test=None) + result = queries_obj.get_type_override(id_=TYPE_OVERRIDE_NONE_ID) + + assert result is not None + assert result.text_test is None + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_type_override_not_found", + depends=["PymysqlTestAttrsClasses::get_type_override_none_value"], + ) + def test_get_type_override_not_found(self, queries_obj: queries.Queries) -> None: + assert queries_obj.get_type_override(id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::insert_reserved_arg") + def test_insert_reserved_arg(self, queries_obj: queries.Queries) -> None: + # The column is literally named "conn"; on methods the parameter + # keeps its name because self is the only implicit argument. + queries_obj.insert_reserved_arg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_reserved_arg", depends=["PymysqlTestAttrsClasses::insert_reserved_arg"]) + def test_get_reserved_arg(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_reserved_arg(conn=RESERVED_ARG_VALUE) + + assert result == models.TestReservedArg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_reserved_arg_not_found", + depends=["PymysqlTestAttrsClasses::get_reserved_arg"], + ) + def test_get_reserved_arg_not_found(self, queries_obj: queries.Queries) -> None: + assert queries_obj.get_reserved_arg(conn="missing-reserved-arg-value") is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::insert_case_rows") + def test_insert_case_rows(self, case_obj: queries_case.QueriesCase) -> None: + case_obj.insert_case_row(id_=CASE_ID, upper_dt=CASE_DT, prec_dec=CASE_DEC) + case_obj.insert_case_row(id_=CASE_ID + 1, upper_dt=CASE_DT, prec_dec=CASE_DEC) + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_case_row", depends=["PymysqlTestAttrsClasses::insert_case_rows"]) + def test_get_case_row(self, case_obj: queries_case.QueriesCase) -> None: + row = case_obj.get_case_row(id_=CASE_ID) + + assert row is not None + assert isinstance(row.upper_dt, datetime.datetime) + assert row.upper_dt == CASE_DT + assert isinstance(row.prec_dec, decimal.Decimal) + assert row.prec_dec == CASE_DEC + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_case_row_not_found", + depends=["PymysqlTestAttrsClasses::get_case_row"], + ) + def test_get_case_row_not_found(self, case_obj: queries_case.QueriesCase) -> None: + assert case_obj.get_case_row(id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::count_case_rows_filters", + depends=["PymysqlTestAttrsClasses::get_case_row_not_found"], + ) + def test_count_case_rows_filters(self, case_obj: queries_case.QueriesCase) -> None: + # The WHERE clause lives inside an executable /*! version comment; + # raising the threshold by one must drop exactly the first row. + count_ge_first = case_obj.count_case_rows(id_=CASE_ID) + count_ge_second = case_obj.count_case_rows(id_=CASE_ID + 1) + + assert count_ge_first is not None + assert count_ge_second is not None + assert count_ge_first - count_ge_second == 1 + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::insert_enum_override") + def test_insert_enum_override(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + enum_override_obj.insert_enum_override(id_=ENUM_OVERRIDE_ID, mood_test="happy") + enum_override_obj.insert_enum_override(id_=ENUM_OVERRIDE_ID_2, mood_test="sad") + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_enum_override_mood", + depends=["PymysqlTestAttrsClasses::insert_enum_override"], + ) + def test_get_enum_override_mood(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + result = enum_override_obj.get_enum_override_mood(id_=ENUM_OVERRIDE_ID) + + assert result == "happy" + assert isinstance(result, str) + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_enum_override_mood_not_found", + depends=["PymysqlTestAttrsClasses::get_enum_override_mood"], + ) + def test_get_enum_override_mood_not_found(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + assert enum_override_obj.get_enum_override_mood(id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::list_enum_override_by_ids", + depends=["PymysqlTestAttrsClasses::get_enum_override_mood_not_found"], + ) + def test_list_enum_override_by_ids(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + result = enum_override_obj.list_enum_override_by_ids(ids=[ENUM_OVERRIDE_ID, ENUM_OVERRIDE_ID_2]) + + assert isinstance(result, queries_enum_override.QueryResults) + rows = result() + assert rows == [ + models.TestEnumOverride(id_=ENUM_OVERRIDE_ID, mood_test="happy"), + models.TestEnumOverride(id_=ENUM_OVERRIDE_ID_2, mood_test="sad"), + ] + assert list(result) == rows + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::list_enum_override_by_ids_empty", + depends=["PymysqlTestAttrsClasses::list_enum_override_by_ids"], + ) + def test_list_enum_override_by_ids_empty(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + # An empty slice expands the placeholder to NULL: IN (NULL) matches + # no rows instead of raising. + assert enum_override_obj.list_enum_override_by_ids(ids=[])() == [] + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::seed_field_naming") + def test_seed_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + # No generated insert exists for this table; seed it directly. + with pymysql_conn.cursor() as cur: + cur.execute("INSERT INTO test_field_namings (id, outputs) VALUES (%s, %s)", (FIELD_NAMING_ID, json.dumps({"first": 1}))) + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::get_field_naming", depends=["PymysqlTestAttrsClasses::seed_field_naming"]) + def test_get_field_naming(self, field_namings_obj: queries_field_namings.QueriesFieldNamings) -> None: + result = field_namings_obj.get_field_naming(id_=FIELD_NAMING_ID) + + assert result is not None + assert result.id_ == FIELD_NAMING_ID + assert json.loads(result.outputs) == {"first": 1} + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_field_naming_not_found", + depends=["PymysqlTestAttrsClasses::get_field_naming"], + ) + def test_get_field_naming_not_found(self, field_namings_obj: queries_field_namings.QueriesFieldNamings) -> None: + assert field_namings_obj.get_field_naming(id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_joined_field_namings", + depends=["PymysqlTestAttrsClasses::get_field_naming_not_found"], + ) + def test_get_joined_field_namings(self, field_namings_obj: queries_field_namings.QueriesFieldNamings) -> None: + result = field_namings_obj.get_joined_field_namings(id_=FIELD_NAMING_ID) + + assert result is not None + assert isinstance(result, queries_field_namings.GetJoinedFieldNamingsRow) + assert json.loads(result.outputs) == {"first": 1} + assert result.outputs == result.outputs_2 + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::set_field_naming_outputs", + depends=["PymysqlTestAttrsClasses::get_joined_field_namings"], + ) + def test_set_field_naming_outputs(self, field_namings_obj: queries_field_namings.QueriesFieldNamings) -> None: + field_namings_obj.set_field_naming_outputs(outputs=json.dumps({"second": 2}), id_=FIELD_NAMING_ID) + result = field_namings_obj.get_field_naming(id_=FIELD_NAMING_ID) + + assert result is not None + assert json.loads(result.outputs) == {"second": 2} + + @pytest.mark.dependency(depends=["PymysqlTestAttrsClasses::set_field_naming_outputs"]) + def test_delete_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_field_namings WHERE id = %s", (FIELD_NAMING_ID,)) + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::insert_invalid_identifiers") + def test_insert_invalid_identifiers(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + invalid_identifiers_obj.insert_invalid_identifiers(id_=INVALID_IDENTIFIER_ID, column_3p_="3p-value", new_notes="some new notes") + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_invalid_identifiers", + depends=["PymysqlTestAttrsClasses::insert_invalid_identifiers"], + ) + def test_get_invalid_identifiers(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + result = invalid_identifiers_obj.get_invalid_identifiers(id_=INVALID_IDENTIFIER_ID) + + # The insert never sets `%pct`, so it stays NULL. + assert result == models.TestInvalidIdentifier( + id_=INVALID_IDENTIFIER_ID, + column_3p_="3p-value", + new_notes="some new notes", + column__pct=None, + ) + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_invalid_identifiers_not_found", + depends=["PymysqlTestAttrsClasses::get_invalid_identifiers"], + ) + def test_get_invalid_identifiers_not_found(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + assert invalid_identifiers_obj.get_invalid_identifiers(id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestAttrsClasses::insert_third_party_stat") + def test_insert_third_party_stat(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + invalid_identifiers_obj.insert_third_party_stat(id_=THIRD_PARTY_ID, total=THIRD_PARTY_TOTAL) + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_third_party_stat", + depends=["PymysqlTestAttrsClasses::insert_third_party_stat"], + ) + def test_get_third_party_stat(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + result = invalid_identifiers_obj.get_third_party_stat(id_=THIRD_PARTY_ID) + + assert result == models.Model3RdPartyStat(id_=THIRD_PARTY_ID, total=THIRD_PARTY_TOTAL) + + @pytest.mark.dependency( + name="PymysqlTestAttrsClasses::get_third_party_stat_not_found", + depends=["PymysqlTestAttrsClasses::get_third_party_stat"], + ) + def test_get_third_party_stat_not_found(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + assert invalid_identifiers_obj.get_third_party_stat(id_=0) is None + + def test_one_missing_rows_return_none(self, pymysql_conn: pymysql.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. The + # count queries always return a row, so their miss branch needs the + # no-row stub; the sub-module Querier conn properties ride along. + obj = queries.Queries(conn=pymysql_conn) + assert obj.get_one_mysql_type(id_=-1) is None + assert obj.get_one_inner_mysql_type(table_id=-1) is None + assert obj.get_one_date(id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert obj.get_one_datetime(id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert obj.get_one_time(id_=-1, time_test=datetime.timedelta()) is None + assert obj.get_one_bool(id_=-1, tinyint1_test=False) is None + assert obj.get_one_decimal(id_=-1, decimal_test=decimal.Decimal(0)) is None + assert obj.get_one_blob(id_=-1, blob_test=memoryview(b"")) is None + assert obj.get_one_bit(id_=-1) is None + assert obj.get_one_year(id_=-1) is None + assert obj.get_one_json(id_=-1) is None + assert obj.get_one_mood(id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert obj.get_one_tag(id_=-1) is None + assert obj.get_exec_last_id_name(id_=-1) is None + assert obj.get_type_override(id_=-1) is None + assert obj.get_reserved_arg(conn="missing") is None + assert obj.touch_exec_last_id(name="untouched", id_=-1) is None + + case_obj = queries_case.QueriesCase(conn=pymysql_conn) + naming_obj = queries_field_namings.QueriesFieldNamings(conn=pymysql_conn) + invalid_obj = queries_invalid_identifiers.QueriesInvalidIdentifiers(conn=pymysql_conn) + enum_obj = queries_enum_override.QueriesEnumOverride(conn=pymysql_conn) + assert case_obj.conn is pymysql_conn + assert naming_obj.conn is pymysql_conn + assert invalid_obj.conn is pymysql_conn + assert enum_obj.conn is pymysql_conn + assert case_obj.get_case_row(id_=-1) is None + assert naming_obj.get_field_naming(id_=-1) is None + assert naming_obj.get_joined_field_namings(id_=-1) is None + assert invalid_obj.get_invalid_identifiers(id_=-1) is None + assert enum_obj.get_enum_override_mood(id_=-1) is None + assert enum_obj.count_enum_override_by_moods(moods=[]) == 0 + + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert queries.Queries(conn=stub).count_mysql_types() is None + assert queries_case.QueriesCase(conn=stub).count_case_rows(id_=0) is None + assert queries_enum_override.QueriesEnumOverride(conn=stub).count_enum_override_by_moods(moods=[]) is None + + @pytest.mark.dependency(depends=["PymysqlTestAttrsClasses::insert_enum_override"]) + def test_enum_override_cleanup(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_enum_override WHERE id IN (%s, %s)", (ENUM_OVERRIDE_ID, ENUM_OVERRIDE_ID_2)) + assert queries_enum_override.QueriesEnumOverride(conn=pymysql_conn).get_enum_override_mood(id_=ENUM_OVERRIDE_ID) is None diff --git a/test/driver_pymysql/attrs/test_pymysql_attrs_functions.py b/test/driver_pymysql/attrs/test_pymysql_attrs_functions.py new file mode 100644 index 00000000..fac0f903 --- /dev/null +++ b/test/driver_pymysql/attrs/test_pymysql_attrs_functions.py @@ -0,0 +1,1105 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import attrs +import pymysql +import pymysql.cursors +import pytest + +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.attrs.functions import enums +from test.driver_pymysql.attrs.functions import models +from test.driver_pymysql.attrs.functions import queries +from test.driver_pymysql.attrs.functions import queries_case +from test.driver_pymysql.attrs.functions import queries_enum_override +from test.driver_pymysql.attrs.functions import queries_field_namings +from test.driver_pymysql.attrs.functions import queries_invalid_identifiers + +# Fixed ids: the MySQL tables are shared by every pymysql/asyncmy suite in the +# session, so each test file owns a distinct id range. This file: 2500-2999. +MAIN_ID = 2500 +TYPE_OVERRIDE_ID = 2600 +TYPE_OVERRIDE_NONE_ID = 2601 +ENUM_OVERRIDE_ID = 2650 +ENUM_OVERRIDE_ID_2 = 2651 +CASE_ID = 2700 +RESERVED_ARG_ID = 2750 +FIELD_NAMING_ID = 2800 +INVALID_IDENTIFIER_ID = 2850 +THIRD_PARTY_ID = 2860 +THIRD_PARTY_TOTAL = 9003 + +CASE_DT = datetime.datetime(2026, 7, 19, 8, 15) +CASE_DEC = decimal.Decimal("12.34") +RESERVED_ARG_VALUE = "pymysql-attrs-functions-conn" +EXEC_LAST_ID_NAME = "pymysql-attrs-functions" +UPDATED_VARCHAR = "updated varchar" +# decimal(12,4) and numeric(10,2) come back padded to their full scale. +DECIMAL_PADDED = "1234.5000" +NUMERIC_PADDED = "87.60" +EXPECTED_MONTH = "2026-01" + + +class TestPymysqlAttrsFunctions: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=TYPE_OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MAIN_ID, + int_test=42, + integer_test=43, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 10, + year_test=2026, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.e, + double_precision_test=1.41421, + real_test=math.pi, + decimal_test=decimal.Decimal("1234.5"), + numeric_test=decimal.Decimal("87.6"), + char_test="ABC", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"bin-test".ljust(16, b"\x00")), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tiny blob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"medium blob"), + longblob_test=memoryview(b"long blob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 1), + datetime_test=datetime.datetime(2026, 1, 15, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 15, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 15, 6, 30, 45), + time_test=datetime.timedelta(hours=13, minutes=14, seconds=15), + json_test=json.dumps({"foo": "bar"}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=None, + tinyint1_test=None, + bool_test=None, + boolean_test=None, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=None, + varbinary_test=model.varbinary_test, + tinyblob_test=None, + blob_test=None, + mediumblob_test=None, + longblob_test=model.longblob_test, + bit_test=None, + date_test=None, + datetime_test=None, + datetime6_test=None, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=None, + tag=enums.TestInnerMysqlTypesTag.ALPHA, + ) + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::insert") + def test_insert( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + queries.insert_one_mysql_type( + conn=pymysql_conn, + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::inner_insert", depends=["PymysqlTestAttrsFunctions::insert"]) + def test_inner_insert( + self, + pymysql_conn: pymysql.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + queries.insert_one_inner_mysql_type( + conn=pymysql_conn, + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_one", depends=["PymysqlTestAttrsFunctions::inner_insert"]) + def test_get_one( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_mysql_type(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + + # MySQL pads decimals to their declared scale and binary(16) to full + # width, keeps datetime(6) microseconds, and normalizes json spacing. + assert str(result.decimal_test) == DECIMAL_PADDED + assert str(result.numeric_test) == NUMERIC_PADDED + assert bytes(result.binary_test) == b"bin-test".ljust(16, b"\x00") + assert bytes(result.bit_test) == b"\x80" + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + assert isinstance(result.time_test, datetime.timedelta) + assert result.datetime6_test == model.datetime6_test + assert json.loads(result.json_test) == json.loads(model.json_test) + assert attrs.evolve(result, json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_one_none", depends=["PymysqlTestAttrsFunctions::get_one"]) + def test_get_one_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_mysql_type(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_one_inner", depends=["PymysqlTestAttrsFunctions::get_one_none"]) + def test_get_one_inner( + self, + pymysql_conn: pymysql.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_one_inner_none", + depends=["PymysqlTestAttrsFunctions::get_one_inner"], + ) + def test_get_one_inner_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_date", depends=["PymysqlTestAttrsFunctions::get_one_inner_none"]) + def test_get_date( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_date(conn=pymysql_conn, id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_date_none", depends=["PymysqlTestAttrsFunctions::get_date"]) + def test_get_date_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_date(conn=pymysql_conn, id_=0, date_test=model.date_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_datetime", depends=["PymysqlTestAttrsFunctions::get_date_none"]) + def test_get_datetime( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_datetime(conn=pymysql_conn, id_=model.id_, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_datetime_none", depends=["PymysqlTestAttrsFunctions::get_datetime"]) + def test_get_datetime_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_datetime(conn=pymysql_conn, id_=0, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_time", depends=["PymysqlTestAttrsFunctions::get_datetime_none"]) + def test_get_time( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_time(conn=pymysql_conn, id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_time_none", depends=["PymysqlTestAttrsFunctions::get_time"]) + def test_get_time_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_time(conn=pymysql_conn, id_=0, time_test=model.time_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_bool", depends=["PymysqlTestAttrsFunctions::get_time_none"]) + def test_get_bool( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_bool(conn=pymysql_conn, id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, bool) + assert result is True + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_bool_none", depends=["PymysqlTestAttrsFunctions::get_bool"]) + def test_get_bool_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_bool(conn=pymysql_conn, id_=0, tinyint1_test=False) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_decimal", depends=["PymysqlTestAttrsFunctions::get_bool_none"]) + def test_get_decimal( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_decimal(conn=pymysql_conn, id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + assert result == model.decimal_test + assert str(result) == DECIMAL_PADDED + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_decimal_none", depends=["PymysqlTestAttrsFunctions::get_decimal"]) + def test_get_decimal_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_decimal(conn=pymysql_conn, id_=0, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_blob", depends=["PymysqlTestAttrsFunctions::get_decimal_none"]) + def test_get_blob( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_blob(conn=pymysql_conn, id_=model.id_, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_blob_none", depends=["PymysqlTestAttrsFunctions::get_blob"]) + def test_get_blob_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_blob(conn=pymysql_conn, id_=0, blob_test=model.blob_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_bit", depends=["PymysqlTestAttrsFunctions::get_blob_none"]) + def test_get_bit( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_bit(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, memoryview) + assert bytes(result) == b"\x80" + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_bit_none", depends=["PymysqlTestAttrsFunctions::get_bit"]) + def test_get_bit_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_bit(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_year", depends=["PymysqlTestAttrsFunctions::get_bit_none"]) + def test_get_year( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_year(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_year_none", depends=["PymysqlTestAttrsFunctions::get_year"]) + def test_get_year_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_year(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_json", depends=["PymysqlTestAttrsFunctions::get_year_none"]) + def test_get_json( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_json(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, str) + # MySQL normalizes json spacing; never compare the raw strings. + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_json_none", depends=["PymysqlTestAttrsFunctions::get_json"]) + def test_get_json_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_json(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_mood", depends=["PymysqlTestAttrsFunctions::get_json_none"]) + def test_get_mood( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_mood(conn=pymysql_conn, id_=model.id_, mood=model.mood) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_mood_none", depends=["PymysqlTestAttrsFunctions::get_mood"]) + def test_get_mood_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_mood(conn=pymysql_conn, id_=0, mood=model.mood) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_tag", depends=["PymysqlTestAttrsFunctions::get_mood_none"]) + def test_get_tag( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_tag(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.BETA + assert result == model.tag + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_tag_none", depends=["PymysqlTestAttrsFunctions::get_tag"]) + def test_get_tag_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_tag(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_many", depends=["PymysqlTestAttrsFunctions::get_tag_none"]) + def test_get_many(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mysql_type(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + assert json.loads(results[0].json_test) == json.loads(model.json_test) + assert attrs.evolve(results[0], json_test=model.json_test) == model + + results = result() + assert len(results) == 1 + assert attrs.evolve(results[0], json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_many_iter", depends=["PymysqlTestAttrsFunctions::get_many"]) + def test_get_many_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_mysql_type(conn=pymysql_conn, id_=model.id_): + assert result is not None + assert isinstance(result, models.TestMysqlType) + assert attrs.evolve(result, json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_many_inner", depends=["PymysqlTestAttrsFunctions::get_many_iter"]) + def test_get_many_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = queries.get_many_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], models.TestInnerMysqlType) + assert results[0] == inner_model + + results = result() + assert results[0] == inner_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_inner_iter", + depends=["PymysqlTestAttrsFunctions::get_many_inner"], + ) + def test_get_many_inner_iter(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + for result in queries.get_many_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_nullable_inner", + depends=["PymysqlTestAttrsFunctions::get_many_inner_iter"], + ) + def test_get_many_nullable_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the query uses the NULL-safe <=> comparison. + result = queries.get_many_nullable_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = result() + assert isinstance(results[0], models.TestInnerMysqlType) + assert results[0] == inner_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_nullable_inner_iter", + depends=["PymysqlTestAttrsFunctions::get_many_nullable_inner"], + ) + def test_get_many_nullable_inner_iter(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + for result in queries.get_many_nullable_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_date", + depends=["PymysqlTestAttrsFunctions::get_many_nullable_inner_iter"], + ) + def test_get_many_date(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_date(conn=pymysql_conn, id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], datetime.date) + assert results[0] == model.date_test + + results = result() + assert results[0] == model.date_test + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_date_iter", + depends=["PymysqlTestAttrsFunctions::get_many_date"], + ) + def test_get_many_date_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_date(conn=pymysql_conn, id_=model.id_, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_time", + depends=["PymysqlTestAttrsFunctions::get_many_date_iter"], + ) + def test_get_many_time(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_time(conn=pymysql_conn, id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], datetime.timedelta) + assert results[0] == model.time_test + + results = result() + assert results[0] == model.time_test + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_time_iter", + depends=["PymysqlTestAttrsFunctions::get_many_time"], + ) + def test_get_many_time_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_time(conn=pymysql_conn, id_=model.id_, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_bool", + depends=["PymysqlTestAttrsFunctions::get_many_time_iter"], + ) + def test_get_many_bool(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_bool(conn=pymysql_conn, id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], bool) + assert results[0] is True + + results = result() + assert results[0] is True + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_bool_iter", + depends=["PymysqlTestAttrsFunctions::get_many_bool"], + ) + def test_get_many_bool_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_bool(conn=pymysql_conn, id_=model.id_, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + assert result is True + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_decimal", + depends=["PymysqlTestAttrsFunctions::get_many_bool_iter"], + ) + def test_get_many_decimal(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_decimal(conn=pymysql_conn, id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], decimal.Decimal) + assert results[0] == model.decimal_test + assert str(results[0]) == DECIMAL_PADDED + + results = result() + assert results[0] == model.decimal_test + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_decimal_iter", + depends=["PymysqlTestAttrsFunctions::get_many_decimal"], + ) + def test_get_many_decimal_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_decimal(conn=pymysql_conn, id_=model.id_, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + assert result == model.decimal_test + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_mood", + depends=["PymysqlTestAttrsFunctions::get_many_decimal_iter"], + ) + def test_get_many_mood(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mood(conn=pymysql_conn, mood=model.mood) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert len(results) == 1 + assert isinstance(results[0], enums.TestMysqlTypesMood) + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + results = result() + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_many_mood_iter", + depends=["PymysqlTestAttrsFunctions::get_many_mood"], + ) + def test_get_many_mood_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_mood(conn=pymysql_conn, mood=model.mood): + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::list_months", depends=["PymysqlTestAttrsFunctions::get_many_mood_iter"]) + def test_list_months(self, pymysql_conn: pymysql.Connection) -> None: + # DATE_FORMAT emits %% in the stored SQL; the empty argument tuple + # still goes through pymysql's %-substitution, halving it back. + result = queries.list_months(conn=pymysql_conn) + + assert result is not None + assert isinstance(result, queries.QueryResults) + months = result() + assert list(months) == [EXPECTED_MONTH] + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::list_months_iter", depends=["PymysqlTestAttrsFunctions::list_months"]) + def test_list_months_iter(self, pymysql_conn: pymysql.Connection) -> None: + months = list(queries.list_months(conn=pymysql_conn)) + assert months == [EXPECTED_MONTH] + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::count", depends=["PymysqlTestAttrsFunctions::list_months_iter"]) + def test_count_mysql_types(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.count_mysql_types(conn=pymysql_conn) + + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::all_cursor", depends=["PymysqlTestAttrsFunctions::count"]) + def test_all_mysql_types_cursor(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + cur = queries.all_mysql_types_cursor(conn=pymysql_conn) + + assert isinstance(cur, pymysql.cursors.Cursor) + rows = cur.fetchall() + # The shared table may carry other files' rows; assert on our own. + assert model.id_ in {row[0] for row in rows} + cur.close() + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::update_rows", depends=["PymysqlTestAttrsFunctions::all_cursor"]) + def test_update_varchar_rows(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.update_varchar_test(conn=pymysql_conn, varchar_test=UPDATED_VARCHAR, id_=model.id_) + + assert isinstance(result, int) + assert result == 1 + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::update_rows_noop", depends=["PymysqlTestAttrsFunctions::update_rows"]) + def test_update_varchar_rows_noop(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + # Without CLIENT.FOUND_ROWS MySQL reports changed rows, so setting + # the same value again affects nothing. + result = queries.update_varchar_test(conn=pymysql_conn, varchar_test=UPDATED_VARCHAR, id_=model.id_) + + assert result == 0 + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::insert_last_id", depends=["PymysqlTestAttrsFunctions::update_rows_noop"]) + def test_insert_exec_last_id(self, pymysql_conn: pymysql.Connection) -> None: + # The AUTO_INCREMENT counter persists across runs; never assert an + # exact id. + new_id = queries.insert_exec_last_id(conn=pymysql_conn, name=EXEC_LAST_ID_NAME) + + assert new_id is not None + assert isinstance(new_id, int) + assert new_id > 0 + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=new_id) == EXEC_LAST_ID_NAME + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::delete", depends=["PymysqlTestAttrsFunctions::insert_last_id"]) + def test_delete_mysql_type(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + queries.delete_one_mysql_type(conn=pymysql_conn, id_=model.id_) + + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=model.id_) is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::insert_type_override") + def test_insert_type_override(self, pymysql_conn: pymysql.Connection, override_model: models.TestTypeOverride) -> None: + queries.insert_type_override(conn=pymysql_conn, id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_type_override", + depends=["PymysqlTestAttrsFunctions::insert_type_override"], + ) + def test_get_type_override(self, pymysql_conn: pymysql.Connection, override_model: models.TestTypeOverride) -> None: + result = queries.get_type_override(conn=pymysql_conn, id_=override_model.id_) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_type_override_none_value", + depends=["PymysqlTestAttrsFunctions::get_type_override"], + ) + def test_get_type_override_none_value(self, pymysql_conn: pymysql.Connection) -> None: + # The override target is nullable: NULL must come back as None + # without passing through UserString. + queries.insert_type_override(conn=pymysql_conn, id_=TYPE_OVERRIDE_NONE_ID, text_test=None) + result = queries.get_type_override(conn=pymysql_conn, id_=TYPE_OVERRIDE_NONE_ID) + + assert result is not None + assert result.text_test is None + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_type_override_not_found", + depends=["PymysqlTestAttrsFunctions::get_type_override_none_value"], + ) + def test_get_type_override_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries.get_type_override(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::insert_reserved_arg") + def test_insert_reserved_arg(self, pymysql_conn: pymysql.Connection) -> None: + # The column is literally named "conn"; the generated parameter is + # deduplicated against the implicit connection argument. + queries.insert_reserved_arg(conn=pymysql_conn, id_=RESERVED_ARG_ID, conn_2=RESERVED_ARG_VALUE) + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_reserved_arg", + depends=["PymysqlTestAttrsFunctions::insert_reserved_arg"], + ) + def test_get_reserved_arg(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_reserved_arg(conn=pymysql_conn, conn_2=RESERVED_ARG_VALUE) + + assert result == models.TestReservedArg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_reserved_arg_not_found", + depends=["PymysqlTestAttrsFunctions::get_reserved_arg"], + ) + def test_get_reserved_arg_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries.get_reserved_arg(conn=pymysql_conn, conn_2="missing-reserved-arg-value") is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::insert_case_rows") + def test_insert_case_rows(self, pymysql_conn: pymysql.Connection) -> None: + queries_case.insert_case_row(conn=pymysql_conn, id_=CASE_ID, upper_dt=CASE_DT, prec_dec=CASE_DEC) + queries_case.insert_case_row(conn=pymysql_conn, id_=CASE_ID + 1, upper_dt=CASE_DT, prec_dec=CASE_DEC) + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_case_row", depends=["PymysqlTestAttrsFunctions::insert_case_rows"]) + def test_get_case_row(self, pymysql_conn: pymysql.Connection) -> None: + row = queries_case.get_case_row(conn=pymysql_conn, id_=CASE_ID) + + assert row is not None + assert isinstance(row.upper_dt, datetime.datetime) + assert row.upper_dt == CASE_DT + assert isinstance(row.prec_dec, decimal.Decimal) + assert row.prec_dec == CASE_DEC + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_case_row_not_found", + depends=["PymysqlTestAttrsFunctions::get_case_row"], + ) + def test_get_case_row_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_case.get_case_row(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::count_case_rows_filters", + depends=["PymysqlTestAttrsFunctions::get_case_row_not_found"], + ) + def test_count_case_rows_filters(self, pymysql_conn: pymysql.Connection) -> None: + # The WHERE clause lives inside an executable /*! version comment; + # raising the threshold by one must drop exactly the first row. + count_ge_first = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_ID) + count_ge_second = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_ID + 1) + + assert count_ge_first is not None + assert count_ge_second is not None + assert count_ge_first - count_ge_second == 1 + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::insert_enum_override") + def test_insert_enum_override(self, pymysql_conn: pymysql.Connection) -> None: + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=ENUM_OVERRIDE_ID, mood_test="happy") + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=ENUM_OVERRIDE_ID_2, mood_test="sad") + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_enum_override_mood", + depends=["PymysqlTestAttrsFunctions::insert_enum_override"], + ) + def test_get_enum_override_mood(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=ENUM_OVERRIDE_ID) + + assert result == "happy" + assert isinstance(result, str) + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_enum_override_mood_not_found", + depends=["PymysqlTestAttrsFunctions::get_enum_override_mood"], + ) + def test_get_enum_override_mood_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::list_enum_override_by_ids", + depends=["PymysqlTestAttrsFunctions::get_enum_override_mood_not_found"], + ) + def test_list_enum_override_by_ids(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=[ENUM_OVERRIDE_ID, ENUM_OVERRIDE_ID_2]) + + assert isinstance(result, queries_enum_override.QueryResults) + rows = result() + assert rows == [ + models.TestEnumOverride(id_=ENUM_OVERRIDE_ID, mood_test="happy"), + models.TestEnumOverride(id_=ENUM_OVERRIDE_ID_2, mood_test="sad"), + ] + assert list(result) == rows + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::list_enum_override_by_ids_empty", + depends=["PymysqlTestAttrsFunctions::list_enum_override_by_ids"], + ) + def test_list_enum_override_by_ids_empty(self, pymysql_conn: pymysql.Connection) -> None: + # An empty slice expands the placeholder to NULL: IN (NULL) matches + # no rows instead of raising. + assert queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=[])() == [] + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::seed_field_naming") + def test_seed_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + # No generated insert exists for this table; seed it directly. + with pymysql_conn.cursor() as cur: + cur.execute("INSERT INTO test_field_namings (id, outputs) VALUES (%s, %s)", (FIELD_NAMING_ID, json.dumps({"first": 1}))) + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::get_field_naming", depends=["PymysqlTestAttrsFunctions::seed_field_naming"]) + def test_get_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_NAMING_ID) + + assert result is not None + assert result.id_ == FIELD_NAMING_ID + assert json.loads(result.outputs) == {"first": 1} + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_field_naming_not_found", + depends=["PymysqlTestAttrsFunctions::get_field_naming"], + ) + def test_get_field_naming_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_joined_field_namings", + depends=["PymysqlTestAttrsFunctions::get_field_naming_not_found"], + ) + def test_get_joined_field_namings(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_field_namings.get_joined_field_namings(conn=pymysql_conn, id_=FIELD_NAMING_ID) + + assert result is not None + assert isinstance(result, queries_field_namings.GetJoinedFieldNamingsRow) + assert json.loads(result.outputs) == {"first": 1} + assert result.outputs == result.outputs_2 + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::set_field_naming_outputs", + depends=["PymysqlTestAttrsFunctions::get_joined_field_namings"], + ) + def test_set_field_naming_outputs(self, pymysql_conn: pymysql.Connection) -> None: + queries_field_namings.set_field_naming_outputs(conn=pymysql_conn, outputs=json.dumps({"second": 2}), id_=FIELD_NAMING_ID) + result = queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_NAMING_ID) + + assert result is not None + assert json.loads(result.outputs) == {"second": 2} + + @pytest.mark.dependency(depends=["PymysqlTestAttrsFunctions::set_field_naming_outputs"]) + def test_delete_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_field_namings WHERE id = %s", (FIELD_NAMING_ID,)) + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::insert_invalid_identifiers") + def test_insert_invalid_identifiers(self, pymysql_conn: pymysql.Connection) -> None: + queries_invalid_identifiers.insert_invalid_identifiers( + conn=pymysql_conn, + id_=INVALID_IDENTIFIER_ID, + column_3p_="3p-value", + new_notes="some new notes", + ) + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_invalid_identifiers", + depends=["PymysqlTestAttrsFunctions::insert_invalid_identifiers"], + ) + def test_get_invalid_identifiers(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=INVALID_IDENTIFIER_ID) + + # The insert never sets `%pct`, so it stays NULL. + assert result == models.TestInvalidIdentifier( + id_=INVALID_IDENTIFIER_ID, + column_3p_="3p-value", + new_notes="some new notes", + column__pct=None, + ) + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_invalid_identifiers_not_found", + depends=["PymysqlTestAttrsFunctions::get_invalid_identifiers"], + ) + def test_get_invalid_identifiers_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestAttrsFunctions::insert_third_party_stat") + def test_insert_third_party_stat(self, pymysql_conn: pymysql.Connection) -> None: + queries_invalid_identifiers.insert_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID, total=THIRD_PARTY_TOTAL) + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_third_party_stat", + depends=["PymysqlTestAttrsFunctions::insert_third_party_stat"], + ) + def test_get_third_party_stat(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID) + + assert result == models.Model3RdPartyStat(id_=THIRD_PARTY_ID, total=THIRD_PARTY_TOTAL) + + @pytest.mark.dependency( + name="PymysqlTestAttrsFunctions::get_third_party_stat_not_found", + depends=["PymysqlTestAttrsFunctions::get_third_party_stat"], + ) + def test_get_third_party_stat_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=0) is None + + def test_one_missing_rows_return_none(self, pymysql_conn: pymysql.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. The + # count queries always return a row, so their miss branch needs the + # no-row stub. + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=-1) is None + assert queries.get_one_date(conn=pymysql_conn, id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert queries.get_one_datetime(conn=pymysql_conn, id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert queries.get_one_time(conn=pymysql_conn, id_=-1, time_test=datetime.timedelta()) is None + assert queries.get_one_bool(conn=pymysql_conn, id_=-1, tinyint1_test=False) is None + assert queries.get_one_decimal(conn=pymysql_conn, id_=-1, decimal_test=decimal.Decimal(0)) is None + assert queries.get_one_blob(conn=pymysql_conn, id_=-1, blob_test=memoryview(b"")) is None + assert queries.get_one_bit(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_year(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_json(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_mood(conn=pymysql_conn, id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert queries.get_one_tag(conn=pymysql_conn, id_=-1) is None + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=-1) is None + assert queries.get_type_override(conn=pymysql_conn, id_=-1) is None + assert queries.get_reserved_arg(conn=pymysql_conn, conn_2="missing") is None + assert queries.touch_exec_last_id(conn=pymysql_conn, name="untouched", id_=-1) is None + assert queries_case.get_case_row(conn=pymysql_conn, id_=-1) is None + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=-1) is None + assert queries_field_namings.get_joined_field_namings(conn=pymysql_conn, id_=-1) is None + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=-1) is None + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=-1) is None + assert queries_enum_override.count_enum_override_by_moods(conn=pymysql_conn, moods=[]) == 0 + + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert queries.count_mysql_types(conn=stub) is None + assert queries_case.count_case_rows(conn=stub, id_=0) is None + assert queries_enum_override.count_enum_override_by_moods(conn=stub, moods=[]) is None + + @pytest.mark.dependency(depends=["PymysqlTestAttrsFunctions::insert_enum_override"]) + def test_enum_override_cleanup(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_enum_override WHERE id IN (%s, %s)", (ENUM_OVERRIDE_ID, ENUM_OVERRIDE_ID_2)) + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=ENUM_OVERRIDE_ID) is None diff --git a/test/driver_pymysql/dataclass/__init__.py b/test/driver_pymysql/dataclass/__init__.py new file mode 100644 index 00000000..11a9bca5 --- /dev/null +++ b/test/driver_pymysql/dataclass/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for pymysql tests.""" diff --git a/test/driver_pymysql/dataclass/classes/__init__.py b/test/driver_pymysql/dataclass/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/dataclass/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/dataclass/classes/enums.py b/test/driver_pymysql/dataclass/classes/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_pymysql/dataclass/classes/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_pymysql/dataclass/classes/models.py b/test/driver_pymysql/dataclass/classes/models.py new file mode 100644 index 00000000..7bd6aa60 --- /dev/null +++ b/test/driver_pymysql/dataclass/classes/models.py @@ -0,0 +1,304 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestEnumOverride", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +import dataclasses +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_pymysql.dataclass.classes import enums + import collections.abc + import datetime + import decimal + + +@dataclasses.dataclass() +class Model3RdPartyStat: + """Model representing Model3RdPartyStat. + + Attributes: + id_: int + total: int + """ + + id_: int + total: int + + +@dataclasses.dataclass() +class TestCaseSensitivity: + """Model representing TestCaseSensitivity. + + Attributes: + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + """ + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +@dataclasses.dataclass() +class TestEnumOverride: + """Model representing TestEnumOverride. + + Attributes: + id_: int + mood_test: str + """ + + id_: int + mood_test: str + + +@dataclasses.dataclass() +class TestFieldNaming: + """Model representing TestFieldNaming. + + Attributes: + id_: int + outputs: str + """ + + id_: int + outputs: str + + +@dataclasses.dataclass() +class TestInnerMysqlType: + """Model representing TestInnerMysqlType. + + Attributes: + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +@dataclasses.dataclass() +class TestInvalidIdentifier: + """Model representing TestInvalidIdentifier. + + Attributes: + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + """ + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +@dataclasses.dataclass() +class TestMysqlType: + """Model representing TestMysqlType. + + Attributes: + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +@dataclasses.dataclass() +class TestReservedArg: + """Model representing TestReservedArg. + + Attributes: + id_: int + conn: str + """ + + id_: int + conn: str + + +@dataclasses.dataclass() +class TestTypeOverride: + """Model representing TestTypeOverride. + + Attributes: + id_: int + text_test: UserString | None + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_pymysql/dataclass/classes/queries.py b/test/driver_pymysql/dataclass/classes/queries.py new file mode 100644 index 00000000..e935fe89 --- /dev/null +++ b/test/driver_pymysql/dataclass/classes/queries.py @@ -0,0 +1,1432 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Queries", + "QueryResults", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.dataclass.classes import enums +from test.driver_pymysql.dataclass.classes import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class Queries: + """Queries from file queries.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_one_mysql_type( + self, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, + ) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + id_: int. + int_test: int. + integer_test: int. + mediumint_test: int. + smallint_test: int. + tinyint_test: int. + bigint_test: int. + int_unsigned_test: int. + bigint_unsigned_test: int. + year_test: int. + tinyint1_test: bool. + bool_test: bool. + boolean_test: bool. + float_test: float. + double_test: float. + double_precision_test: float. + real_test: float. + decimal_test: decimal.Decimal. + numeric_test: decimal.Decimal. + char_test: str. + varchar_test: str. + tinytext_test: str. + text_test: str. + mediumtext_test: str. + longtext_test: str. + binary_test: memoryview. + varbinary_test: memoryview. + tinyblob_test: memoryview. + blob_test: memoryview. + mediumblob_test: memoryview. + longblob_test: memoryview. + bit_test: memoryview. + date_test: datetime.date. + datetime_test: datetime.datetime. + datetime6_test: datetime.datetime. + timestamp_test: datetime.datetime. + time_test: datetime.timedelta. + json_test: str. + mood: enums.TestMysqlTypesMood. + tag: enums.TestMysqlTypesTag. + """ + with self._conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + def insert_one_inner_mysql_type( + self, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, + ) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + table_id: int. + int_test: int | None. + integer_test: int | None. + mediumint_test: int | None. + smallint_test: int | None. + tinyint_test: int | None. + bigint_test: int | None. + int_unsigned_test: int | None. + bigint_unsigned_test: int | None. + year_test: int | None. + tinyint1_test: bool | None. + bool_test: bool | None. + boolean_test: bool | None. + float_test: float | None. + double_test: float | None. + double_precision_test: float | None. + real_test: float | None. + decimal_test: decimal.Decimal | None. + numeric_test: decimal.Decimal | None. + char_test: str | None. + varchar_test: str | None. + tinytext_test: str | None. + text_test: str | None. + mediumtext_test: str | None. + longtext_test: str | None. + binary_test: memoryview | None. + varbinary_test: memoryview | None. + tinyblob_test: memoryview | None. + blob_test: memoryview | None. + mediumblob_test: memoryview | None. + longblob_test: memoryview | None. + bit_test: memoryview | None. + date_test: datetime.date | None. + datetime_test: datetime.datetime | None. + datetime6_test: datetime.datetime | None. + timestamp_test: datetime.datetime | None. + time_test: datetime.timedelta | None. + json_test: str | None. + mood: enums.TestInnerMysqlTypesMood | None. + tag: enums.TestInnerMysqlTypesTag | None. + """ + with self._conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + def get_one_mysql_type(self, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestMysqlType` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + def get_one_inner_mysql_type(self, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + table_id: int. + + Returns: + Result of type `models.TestInnerMysqlType` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + def get_many_mysql_type(self, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(self._conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + def get_many_inner_mysql_type(self, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + table_id: int. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + def get_many_nullable_inner_mysql_type(self, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Args: + table_id: int. + int_test: int | None. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + def get_one_date(self, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + id_: int. + date_test: datetime.date. + + Returns: + Result of type `datetime.date` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DATE, (id_, date_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_datetime(self, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Args: + id_: int. + datetime_test: datetime.datetime. + + Returns: + Result of type `datetime.datetime` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_time(self, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + id_: int. + time_test: datetime.timedelta. + + Returns: + Result of type `datetime.timedelta` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_TIME, (id_, time_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_bool(self, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + id_: int. + tinyint1_test: bool. + + Returns: + Result of type `bool` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = cur.fetchone() + if row is None: + return None + return bool(row[0]) + + def get_one_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Result of type `decimal.Decimal` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_blob(self, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Args: + id_: int. + blob_test: memoryview. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + def get_one_bit(self, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BIT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + def get_one_year(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_YEAR, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_json(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_JSON, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_mood(self, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Args: + id_: int. + mood: enums.TestMysqlTypesMood. + + Returns: + Result of type `enums.TestMysqlTypesMood` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_MOOD, (id_, mood)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + def get_one_tag(self, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `enums.TestMysqlTypesTag` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_TAG, (id_,)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + def get_many_date(self, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + id_: int. + date_test: datetime.date. + + Returns: + Helper class of type `QueryResults[datetime.date]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + def get_many_time(self, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + id_: int. + time_test: datetime.timedelta. + + Returns: + Helper class of type `QueryResults[datetime.timedelta]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + def get_many_bool(self, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + id_: int. + tinyint1_test: bool. + + Returns: + Helper class of type `QueryResults[bool]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(self._conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + def get_many_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Helper class of type `QueryResults[decimal.Decimal]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + def get_many_mood(self, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Args: + mood: enums.TestMysqlTypesMood. + + Returns: + Helper class of type `QueryResults[enums.TestMysqlTypesMood]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(self._conn, GET_MANY_MOOD, _decode_hook, mood) + + def list_months(self) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Returns: + Helper class of type `QueryResults[str]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, LIST_MONTHS, operator.itemgetter(0)) + + def count_mysql_types(self) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(COUNT_MYSQL_TYPES) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def update_varchar_test(self, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Args: + varchar_test: str. + id_: int. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + with self._conn.cursor() as cur: + return cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + def delete_one_mysql_type(self, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + """ + with self._conn.cursor() as cur: + cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + def all_mysql_types_cursor(self) -> pymysql.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Returns: + The result of type `pymysql.cursors.Cursor` returned when executing the query. + """ + cur = self._conn.cursor() + cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + def insert_exec_last_id(self, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Args: + name: str. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + def get_exec_last_id_name(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def insert_type_override(self, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Args: + id_: int. + text_test: UserString | None. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + def get_type_override(self, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestTypeOverride` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + def get_reserved_arg(self, *, conn: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Args: + conn: str. + + Returns: + Result of type `models.TestReservedArg` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_RESERVED_ARG, (conn,)) + row = cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + def insert_reserved_arg(self, *, id_: int, conn: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Args: + id_: int. + conn: str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_RESERVED_ARG, (id_, conn)) + + def touch_exec_last_id(self, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Args: + name: str. + id_: int. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + with self._conn.cursor() as cur: + cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_pymysql/dataclass/classes/queries_case.py b/test/driver_pymysql/dataclass/classes/queries_case.py new file mode 100644 index 00000000..2f8d3e4d --- /dev/null +++ b/test/driver_pymysql/dataclass/classes/queries_case.py @@ -0,0 +1,112 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_case.sql +"""Module containing queries from file queries_case.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("QueriesCase",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + +from test.driver_pymysql.dataclass.classes import models + + +INSERT_CASE_ROW: typing.Final[str] = """-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) +""" + +GET_CASE_ROW: typing.Final[str] = """-- name: GetCaseRow :one +SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s +""" + +COUNT_CASE_ROWS: typing.Final[str] = """-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ +""" + + +class QueriesCase: + """Queries from file queries_case.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_case_row(self, *, id_: int, upper_dt: datetime.datetime, prec_dec: decimal.Decimal) -> None: + """Execute SQL query with `name: InsertCaseRow :exec`. + + ```sql + INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) + ``` + + Args: + id_: int. + upper_dt: datetime.datetime. + prec_dec: decimal.Decimal. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_CASE_ROW, (id_, upper_dt, prec_dec)) + + def get_case_row(self, *, id_: int) -> models.TestCaseSensitivity | None: + """Fetch one from the db using the SQL query with `name: GetCaseRow :one`. + + ```sql + SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestCaseSensitivity` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_CASE_ROW, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestCaseSensitivity(id_=row[0], upper_dt=row[1], prec_dec=row[2]) + + def count_case_rows(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: CountCaseRows :one`. + + ```sql + SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ + ``` + + Args: + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(COUNT_CASE_ROWS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/dataclass/classes/queries_enum_override.py b/test/driver_pymysql/dataclass/classes/queries_enum_override.py new file mode 100644 index 00000000..13d955b5 --- /dev/null +++ b/test/driver_pymysql/dataclass/classes/queries_enum_override.py @@ -0,0 +1,212 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueriesEnumOverride", + "QueryResults", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.dataclass.classes import enums +from test.driver_pymysql.dataclass.classes import models + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class QueriesEnumOverride: + """Queries from file queries_enum_override.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_enum_override(self, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Args: + id_: int. + mood_test: str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + def get_enum_override_mood(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + def list_enum_override_by_ids(self, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Args: + ids: collections.abc.Sequence[int]. + + Returns: + Helper class of type `QueryResults[models.TestEnumOverride]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(self._conn, sql, _decode_hook, *ids) + + def count_enum_override_by_moods(self, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Args: + moods: collections.abc.Sequence[str]. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with self._conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/dataclass/classes/queries_field_namings.py b/test/driver_pymysql/dataclass/classes/queries_field_namings.py new file mode 100644 index 00000000..808beacc --- /dev/null +++ b/test/driver_pymysql/dataclass/classes/queries_field_namings.py @@ -0,0 +1,140 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_field_namings.sql +"""Module containing queries from file queries_field_namings.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "GetJoinedFieldNamingsRow", + "QueriesFieldNamings", +) + +import dataclasses +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.dataclass.classes import models + + +@dataclasses.dataclass() +class GetJoinedFieldNamingsRow: + """Model representing GetJoinedFieldNamingsRow. + + Attributes: + outputs: str + outputs_2: str + """ + + outputs: str + outputs_2: str + + +GET_FIELD_NAMING: typing.Final[str] = """-- name: GetFieldNaming :one +SELECT id, outputs +FROM test_field_namings +WHERE id = %s LIMIT 1 +""" + +GET_JOINED_FIELD_NAMINGS: typing.Final[str] = """-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = %s LIMIT 1 +""" + +SET_FIELD_NAMING_OUTPUTS: typing.Final[str] = """-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = %s +WHERE id = %s +""" + + +class QueriesFieldNamings: + """Queries from file queries_field_namings.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def get_field_naming(self, *, id_: int) -> models.TestFieldNaming | None: + """Fetch one from the db using the SQL query with `name: GetFieldNaming :one`. + + ```sql + SELECT id, outputs + FROM test_field_namings + WHERE id = %s LIMIT 1 + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestFieldNaming` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_FIELD_NAMING, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestFieldNaming(id_=row[0], outputs=row[1]) + + def get_joined_field_namings(self, *, id_: int) -> GetJoinedFieldNamingsRow | None: + """Fetch one from the db using the SQL query with `name: GetJoinedFieldNamings :one`. + + ```sql + SELECT a.outputs, b.outputs + FROM test_field_namings a + JOIN test_field_namings b ON a.id = b.id + WHERE a.id = %s LIMIT 1 + ``` + + Args: + id_: int. + + Returns: + Result of type `GetJoinedFieldNamingsRow` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_JOINED_FIELD_NAMINGS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return GetJoinedFieldNamingsRow(outputs=row[0], outputs_2=row[1]) + + def set_field_naming_outputs(self, *, outputs: str, id_: int) -> None: + """Execute SQL query with `name: SetFieldNamingOutputs :exec`. + + ```sql + UPDATE test_field_namings + SET outputs = %s + WHERE id = %s + ``` + + Args: + outputs: str. + id_: int. + """ + with self._conn.cursor() as cur: + cur.execute(SET_FIELD_NAMING_OUTPUTS, (outputs, id_)) diff --git a/test/driver_pymysql/dataclass/classes/queries_invalid_identifiers.py b/test/driver_pymysql/dataclass/classes/queries_invalid_identifiers.py new file mode 100644 index 00000000..3e4334c0 --- /dev/null +++ b/test/driver_pymysql/dataclass/classes/queries_invalid_identifiers.py @@ -0,0 +1,128 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_invalid_identifiers.sql +"""Module containing queries from file queries_invalid_identifiers.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("QueriesInvalidIdentifiers",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.dataclass.classes import models + + +INSERT_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) +""" + +GET_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: GetInvalidIdentifiers :one +SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s +""" + +INSERT_THIRD_PARTY_STAT: typing.Final[str] = """-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) +""" + +GET_THIRD_PARTY_STAT: typing.Final[str] = """-- name: GetThirdPartyStat :one +SELECT id, total FROM `3rd_party_stats` WHERE id = %s +""" + + +class QueriesInvalidIdentifiers: + """Queries from file queries_invalid_identifiers.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_invalid_identifiers(self, *, id_: int, column_3p_: str | None, new_notes: str) -> None: + """Execute SQL query with `name: InsertInvalidIdentifiers :exec`. + + ```sql + INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) + ``` + + Args: + id_: int. + column_3p_: str | None. + new_notes: str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_INVALID_IDENTIFIERS, (id_, column_3p_, new_notes)) + + def get_invalid_identifiers(self, *, id_: int) -> models.TestInvalidIdentifier | None: + """Fetch one from the db using the SQL query with `name: GetInvalidIdentifiers :one`. + + ```sql + SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestInvalidIdentifier` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_INVALID_IDENTIFIERS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInvalidIdentifier(id_=row[0], column_3p_=row[1], new_notes=row[2], column__pct=row[3]) + + def insert_third_party_stat(self, *, id_: int, total: int) -> None: + """Execute SQL query with `name: InsertThirdPartyStat :exec`. + + ```sql + INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) + ``` + + Args: + id_: int. + total: int. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_THIRD_PARTY_STAT, (id_, total)) + + def get_third_party_stat(self, *, id_: int) -> models.Model3RdPartyStat | None: + """Fetch one from the db using the SQL query with `name: GetThirdPartyStat :one`. + + ```sql + SELECT id, total FROM `3rd_party_stats` WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.Model3RdPartyStat` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_THIRD_PARTY_STAT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.Model3RdPartyStat(id_=row[0], total=row[1]) diff --git a/test/driver_pymysql/dataclass/functions/__init__.py b/test/driver_pymysql/dataclass/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/dataclass/functions/enums.py b/test/driver_pymysql/dataclass/functions/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_pymysql/dataclass/functions/models.py b/test/driver_pymysql/dataclass/functions/models.py new file mode 100644 index 00000000..ef4164d7 --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/models.py @@ -0,0 +1,339 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestConverter", + "TestEnumOverride", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestSlice", + "TestTypeOverride", +) + +import dataclasses +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.converters import Preferences + from test.driver_pymysql.dataclass.functions import enums + import collections.abc + import datetime + import decimal + + +@dataclasses.dataclass() +class Model3RdPartyStat: + """Model representing Model3RdPartyStat. + + Attributes: + id_: int + total: int + """ + + id_: int + total: int + + +@dataclasses.dataclass() +class TestCaseSensitivity: + """Model representing TestCaseSensitivity. + + Attributes: + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + """ + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +@dataclasses.dataclass() +class TestConverter: + """Model representing TestConverter. + + Attributes: + id_: int + prefs: Preferences + maybe_prefs: Preferences | None + tags: frozenset[str] + """ + + id_: int + prefs: Preferences + maybe_prefs: Preferences | None + tags: frozenset[str] + + +@dataclasses.dataclass() +class TestEnumOverride: + """Model representing TestEnumOverride. + + Attributes: + id_: int + mood_test: str + """ + + id_: int + mood_test: str + + +@dataclasses.dataclass() +class TestFieldNaming: + """Model representing TestFieldNaming. + + Attributes: + id_: int + outputs: str + """ + + id_: int + outputs: str + + +@dataclasses.dataclass() +class TestInnerMysqlType: + """Model representing TestInnerMysqlType. + + Attributes: + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +@dataclasses.dataclass() +class TestInvalidIdentifier: + """Model representing TestInvalidIdentifier. + + Attributes: + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + """ + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +@dataclasses.dataclass() +class TestMysqlType: + """Model representing TestMysqlType. + + Attributes: + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +@dataclasses.dataclass() +class TestReservedArg: + """Model representing TestReservedArg. + + Attributes: + id_: int + conn: str + """ + + id_: int + conn: str + + +@dataclasses.dataclass() +class TestSlice: + """Model representing TestSlice. + + Attributes: + id_: int + name: str + note: str | None + """ + + id_: int + name: str + note: str | None + + +@dataclasses.dataclass() +class TestTypeOverride: + """Model representing TestTypeOverride. + + Attributes: + id_: int + text_test: UserString | None + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_pymysql/dataclass/functions/queries.py b/test/driver_pymysql/dataclass/functions/queries.py new file mode 100644 index 00000000..7f7675e3 --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/queries.py @@ -0,0 +1,1553 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "all_mysql_types_cursor", + "count_mysql_types", + "delete_one_mysql_type", + "get_exec_last_id_name", + "get_many_bool", + "get_many_date", + "get_many_decimal", + "get_many_inner_mysql_type", + "get_many_mood", + "get_many_mysql_type", + "get_many_nullable_inner_mysql_type", + "get_many_time", + "get_one_bit", + "get_one_blob", + "get_one_bool", + "get_one_date", + "get_one_datetime", + "get_one_decimal", + "get_one_inner_mysql_type", + "get_one_json", + "get_one_mood", + "get_one_mysql_type", + "get_one_tag", + "get_one_time", + "get_one_year", + "get_reserved_arg", + "get_type_override", + "insert_exec_last_id", + "insert_one_inner_mysql_type", + "insert_one_mysql_type", + "insert_reserved_arg", + "insert_type_override", + "list_months", + "touch_exec_last_id", + "update_varchar_test", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.dataclass.functions import enums +from test.driver_pymysql.dataclass.functions import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_one_mysql_type( + conn: pymysql.Connection, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, +) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + int_test: int. + integer_test: int. + mediumint_test: int. + smallint_test: int. + tinyint_test: int. + bigint_test: int. + int_unsigned_test: int. + bigint_unsigned_test: int. + year_test: int. + tinyint1_test: bool. + bool_test: bool. + boolean_test: bool. + float_test: float. + double_test: float. + double_precision_test: float. + real_test: float. + decimal_test: decimal.Decimal. + numeric_test: decimal.Decimal. + char_test: str. + varchar_test: str. + tinytext_test: str. + text_test: str. + mediumtext_test: str. + longtext_test: str. + binary_test: memoryview. + varbinary_test: memoryview. + tinyblob_test: memoryview. + blob_test: memoryview. + mediumblob_test: memoryview. + longblob_test: memoryview. + bit_test: memoryview. + date_test: datetime.date. + datetime_test: datetime.datetime. + datetime6_test: datetime.datetime. + timestamp_test: datetime.datetime. + time_test: datetime.timedelta. + json_test: str. + mood: enums.TestMysqlTypesMood. + tag: enums.TestMysqlTypesTag. + """ + with conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + +def insert_one_inner_mysql_type( + conn: pymysql.Connection, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, +) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + table_id: int. + int_test: int | None. + integer_test: int | None. + mediumint_test: int | None. + smallint_test: int | None. + tinyint_test: int | None. + bigint_test: int | None. + int_unsigned_test: int | None. + bigint_unsigned_test: int | None. + year_test: int | None. + tinyint1_test: bool | None. + bool_test: bool | None. + boolean_test: bool | None. + float_test: float | None. + double_test: float | None. + double_precision_test: float | None. + real_test: float | None. + decimal_test: decimal.Decimal | None. + numeric_test: decimal.Decimal | None. + char_test: str | None. + varchar_test: str | None. + tinytext_test: str | None. + text_test: str | None. + mediumtext_test: str | None. + longtext_test: str | None. + binary_test: memoryview | None. + varbinary_test: memoryview | None. + tinyblob_test: memoryview | None. + blob_test: memoryview | None. + mediumblob_test: memoryview | None. + longblob_test: memoryview | None. + bit_test: memoryview | None. + date_test: datetime.date | None. + datetime_test: datetime.datetime | None. + datetime6_test: datetime.datetime | None. + timestamp_test: datetime.datetime | None. + time_test: datetime.timedelta | None. + json_test: str | None. + mood: enums.TestInnerMysqlTypesMood | None. + tag: enums.TestInnerMysqlTypesTag | None. + """ + with conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + +def get_one_mysql_type(conn: pymysql.Connection, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestMysqlType` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + +def get_one_inner_mysql_type(conn: pymysql.Connection, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + table_id: int. + + Returns: + Result of type `models.TestInnerMysqlType` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + +def get_many_mysql_type(conn: pymysql.Connection, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + +def get_many_inner_mysql_type(conn: pymysql.Connection, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + table_id: int. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + +def get_many_nullable_inner_mysql_type(conn: pymysql.Connection, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + table_id: int. + int_test: int | None. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + +def get_one_date(conn: pymysql.Connection, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + date_test: datetime.date. + + Returns: + Result of type `datetime.date` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DATE, (id_, date_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_datetime(conn: pymysql.Connection, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + datetime_test: datetime.datetime. + + Returns: + Result of type `datetime.datetime` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_time(conn: pymysql.Connection, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + time_test: datetime.timedelta. + + Returns: + Result of type `datetime.timedelta` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_TIME, (id_, time_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_bool(conn: pymysql.Connection, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + tinyint1_test: bool. + + Returns: + Result of type `bool` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = cur.fetchone() + if row is None: + return None + return bool(row[0]) + + +def get_one_decimal(conn: pymysql.Connection, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Result of type `decimal.Decimal` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_blob(conn: pymysql.Connection, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + blob_test: memoryview. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +def get_one_bit(conn: pymysql.Connection, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BIT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +def get_one_year(conn: pymysql.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_YEAR, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_json(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_JSON, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_mood(conn: pymysql.Connection, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + mood: enums.TestMysqlTypesMood. + + Returns: + Result of type `enums.TestMysqlTypesMood` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_MOOD, (id_, mood)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + +def get_one_tag(conn: pymysql.Connection, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `enums.TestMysqlTypesTag` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_TAG, (id_,)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + +def get_many_date(conn: pymysql.Connection, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + date_test: datetime.date. + + Returns: + Helper class of type `QueryResults[datetime.date]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + +def get_many_time(conn: pymysql.Connection, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + time_test: datetime.timedelta. + + Returns: + Helper class of type `QueryResults[datetime.timedelta]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + +def get_many_bool(conn: pymysql.Connection, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + tinyint1_test: bool. + + Returns: + Helper class of type `QueryResults[bool]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + +def get_many_decimal(conn: pymysql.Connection, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Helper class of type `QueryResults[decimal.Decimal]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + +def get_many_mood(conn: pymysql.Connection, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + mood: enums.TestMysqlTypesMood. + + Returns: + Helper class of type `QueryResults[enums.TestMysqlTypesMood]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(conn, GET_MANY_MOOD, _decode_hook, mood) + + +def list_months(conn: pymysql.Connection) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + Helper class of type `QueryResults[str]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, LIST_MONTHS, operator.itemgetter(0)) + + +def count_mysql_types(conn: pymysql.Connection) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(COUNT_MYSQL_TYPES) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def update_varchar_test(conn: pymysql.Connection, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + varchar_test: str. + id_: int. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + with conn.cursor() as cur: + return cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + +def delete_one_mysql_type(conn: pymysql.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + """ + with conn.cursor() as cur: + cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + +def all_mysql_types_cursor(conn: pymysql.Connection) -> pymysql.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + The result of type `pymysql.cursors.Cursor` returned when executing the query. + """ + cur = conn.cursor() + cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + +def insert_exec_last_id(conn: pymysql.Connection, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + name: str. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + with conn.cursor() as cur: + cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + +def get_exec_last_id_name(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def insert_type_override(conn: pymysql.Connection, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + text_test: UserString | None. + """ + with conn.cursor() as cur: + cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + +def get_type_override(conn: pymysql.Connection, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestTypeOverride` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + +def get_reserved_arg(conn: pymysql.Connection, *, conn_2: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + conn_2: str. + + Returns: + Result of type `models.TestReservedArg` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_RESERVED_ARG, (conn_2,)) + row = cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + +def insert_reserved_arg(conn: pymysql.Connection, *, id_: int, conn_2: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + conn_2: str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_RESERVED_ARG, (id_, conn_2)) + + +def touch_exec_last_id(conn: pymysql.Connection, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + name: str. + id_: int. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + with conn.cursor() as cur: + cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_pymysql/dataclass/functions/queries_case.py b/test/driver_pymysql/dataclass/functions/queries_case.py new file mode 100644 index 00000000..56971a48 --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/queries_case.py @@ -0,0 +1,101 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_case.sql +"""Module containing queries from file queries_case.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "count_case_rows", + "get_case_row", + "insert_case_row", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + +from test.driver_pymysql.dataclass.functions import models + + +INSERT_CASE_ROW: typing.Final[str] = """-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) +""" + +GET_CASE_ROW: typing.Final[str] = """-- name: GetCaseRow :one +SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s +""" + +COUNT_CASE_ROWS: typing.Final[str] = """-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ +""" + + +def insert_case_row(conn: pymysql.Connection, *, id_: int, upper_dt: datetime.datetime, prec_dec: decimal.Decimal) -> None: + """Execute SQL query with `name: InsertCaseRow :exec`. + + ```sql + INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + upper_dt: datetime.datetime. + prec_dec: decimal.Decimal. + """ + with conn.cursor() as cur: + cur.execute(INSERT_CASE_ROW, (id_, upper_dt, prec_dec)) + + +def get_case_row(conn: pymysql.Connection, *, id_: int) -> models.TestCaseSensitivity | None: + """Fetch one from the db using the SQL query with `name: GetCaseRow :one`. + + ```sql + SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestCaseSensitivity` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_CASE_ROW, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestCaseSensitivity(id_=row[0], upper_dt=row[1], prec_dec=row[2]) + + +def count_case_rows(conn: pymysql.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: CountCaseRows :one`. + + ```sql + SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(COUNT_CASE_ROWS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/dataclass/functions/queries_converters.py b/test/driver_pymysql/dataclass/functions/queries_converters.py new file mode 100644 index 00000000..0e26474e --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/queries_converters.py @@ -0,0 +1,194 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_converters.sql +"""Module containing queries from file queries_converters.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "delete_converted", + "get_converted", + "insert_converted", + "list_converted_by_tags", +) + +import operator +import test.converters +import typing + +if typing.TYPE_CHECKING: + from test.converters import Preferences + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.dataclass.functions import models + + +INSERT_CONVERTED: typing.Final[str] = """-- name: InsertConverted :exec +INSERT INTO test_converters (id, prefs, maybe_prefs, tags) VALUES (%s, %s, %s, %s) +""" + +GET_CONVERTED: typing.Final[str] = """-- name: GetConverted :one +SELECT id, prefs, maybe_prefs, tags FROM test_converters WHERE id = %s +""" + +LIST_CONVERTED_BY_TAGS: typing.Final[str] = """-- name: ListConvertedByTags :many +SELECT id FROM test_converters WHERE tags = %s +""" + +DELETE_CONVERTED: typing.Final[str] = """-- name: DeleteConverted :exec +DELETE FROM test_converters WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_converted(conn: pymysql.Connection, *, id_: int, prefs: Preferences, maybe_prefs: Preferences | None, tags: frozenset[str]) -> None: + """Execute SQL query with `name: InsertConverted :exec`. + + ```sql + INSERT INTO test_converters (id, prefs, maybe_prefs, tags) VALUES (%s, %s, %s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + prefs: Preferences. + maybe_prefs: Preferences | None. + tags: frozenset[str]. + """ + with conn.cursor() as cur: + cur.execute(INSERT_CONVERTED, (id_, test.converters.encode_preferences(prefs), test.converters.encode_preferences(maybe_prefs) if maybe_prefs is not None else None, test.converters.encode_tags(tags))) + + +def get_converted(conn: pymysql.Connection, *, id_: int) -> models.TestConverter | None: + """Fetch one from the db using the SQL query with `name: GetConverted :one`. + + ```sql + SELECT id, prefs, maybe_prefs, tags FROM test_converters WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestConverter` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_CONVERTED, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestConverter(id_=row[0], prefs=test.converters.decode_preferences(row[1]), maybe_prefs=test.converters.decode_preferences(row[2]) if row[2] is not None else None, tags=test.converters.decode_tags(row[3])) + + +def list_converted_by_tags(conn: pymysql.Connection, *, tags: frozenset[str]) -> QueryResults[int]: + """Fetch many from the db using the SQL query with `name: ListConvertedByTags :many`. + + ```sql + SELECT id FROM test_converters WHERE tags = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + tags: frozenset[str]. + + Returns: + Helper class of type `QueryResults[int]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, LIST_CONVERTED_BY_TAGS, operator.itemgetter(0), test.converters.encode_tags(tags)) + + +def delete_converted(conn: pymysql.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteConverted :exec`. + + ```sql + DELETE FROM test_converters WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + """ + with conn.cursor() as cur: + cur.execute(DELETE_CONVERTED, (id_,)) diff --git a/test/driver_pymysql/dataclass/functions/queries_enum_override.py b/test/driver_pymysql/dataclass/functions/queries_enum_override.py new file mode 100644 index 00000000..72459d5f --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/queries_enum_override.py @@ -0,0 +1,203 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "count_enum_override_by_moods", + "get_enum_override_mood", + "insert_enum_override", + "list_enum_override_by_ids", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.dataclass.functions import enums +from test.driver_pymysql.dataclass.functions import models + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_enum_override(conn: pymysql.Connection, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + mood_test: str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + +def get_enum_override_mood(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + +def list_enum_override_by_ids(conn: pymysql.Connection, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + + Returns: + Helper class of type `QueryResults[models.TestEnumOverride]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *ids) + + +def count_enum_override_by_moods(conn: pymysql.Connection, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + moods: collections.abc.Sequence[str]. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/dataclass/functions/queries_field_namings.py b/test/driver_pymysql/dataclass/functions/queries_field_namings.py new file mode 100644 index 00000000..90b08214 --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/queries_field_namings.py @@ -0,0 +1,127 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_field_namings.sql +"""Module containing queries from file queries_field_namings.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "GetJoinedFieldNamingsRow", + "get_field_naming", + "get_joined_field_namings", + "set_field_naming_outputs", +) + +import dataclasses +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.dataclass.functions import models + + +@dataclasses.dataclass() +class GetJoinedFieldNamingsRow: + """Model representing GetJoinedFieldNamingsRow. + + Attributes: + outputs: str + outputs_2: str + """ + + outputs: str + outputs_2: str + + +GET_FIELD_NAMING: typing.Final[str] = """-- name: GetFieldNaming :one +SELECT id, outputs +FROM test_field_namings +WHERE id = %s LIMIT 1 +""" + +GET_JOINED_FIELD_NAMINGS: typing.Final[str] = """-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = %s LIMIT 1 +""" + +SET_FIELD_NAMING_OUTPUTS: typing.Final[str] = """-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = %s +WHERE id = %s +""" + + +def get_field_naming(conn: pymysql.Connection, *, id_: int) -> models.TestFieldNaming | None: + """Fetch one from the db using the SQL query with `name: GetFieldNaming :one`. + + ```sql + SELECT id, outputs + FROM test_field_namings + WHERE id = %s LIMIT 1 + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestFieldNaming` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_FIELD_NAMING, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestFieldNaming(id_=row[0], outputs=row[1]) + + +def get_joined_field_namings(conn: pymysql.Connection, *, id_: int) -> GetJoinedFieldNamingsRow | None: + """Fetch one from the db using the SQL query with `name: GetJoinedFieldNamings :one`. + + ```sql + SELECT a.outputs, b.outputs + FROM test_field_namings a + JOIN test_field_namings b ON a.id = b.id + WHERE a.id = %s LIMIT 1 + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `GetJoinedFieldNamingsRow` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_JOINED_FIELD_NAMINGS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return GetJoinedFieldNamingsRow(outputs=row[0], outputs_2=row[1]) + + +def set_field_naming_outputs(conn: pymysql.Connection, *, outputs: str, id_: int) -> None: + """Execute SQL query with `name: SetFieldNamingOutputs :exec`. + + ```sql + UPDATE test_field_namings + SET outputs = %s + WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + outputs: str. + id_: int. + """ + with conn.cursor() as cur: + cur.execute(SET_FIELD_NAMING_OUTPUTS, (outputs, id_)) diff --git a/test/driver_pymysql/dataclass/functions/queries_invalid_identifiers.py b/test/driver_pymysql/dataclass/functions/queries_invalid_identifiers.py new file mode 100644 index 00000000..b1045eb0 --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/queries_invalid_identifiers.py @@ -0,0 +1,121 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_invalid_identifiers.sql +"""Module containing queries from file queries_invalid_identifiers.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "get_invalid_identifiers", + "get_third_party_stat", + "insert_invalid_identifiers", + "insert_third_party_stat", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.dataclass.functions import models + + +INSERT_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) +""" + +GET_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: GetInvalidIdentifiers :one +SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s +""" + +INSERT_THIRD_PARTY_STAT: typing.Final[str] = """-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) +""" + +GET_THIRD_PARTY_STAT: typing.Final[str] = """-- name: GetThirdPartyStat :one +SELECT id, total FROM `3rd_party_stats` WHERE id = %s +""" + + +def insert_invalid_identifiers(conn: pymysql.Connection, *, id_: int, column_3p_: str | None, new_notes: str) -> None: + """Execute SQL query with `name: InsertInvalidIdentifiers :exec`. + + ```sql + INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + column_3p_: str | None. + new_notes: str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_INVALID_IDENTIFIERS, (id_, column_3p_, new_notes)) + + +def get_invalid_identifiers(conn: pymysql.Connection, *, id_: int) -> models.TestInvalidIdentifier | None: + """Fetch one from the db using the SQL query with `name: GetInvalidIdentifiers :one`. + + ```sql + SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestInvalidIdentifier` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_INVALID_IDENTIFIERS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInvalidIdentifier(id_=row[0], column_3p_=row[1], new_notes=row[2], column__pct=row[3]) + + +def insert_third_party_stat(conn: pymysql.Connection, *, id_: int, total: int) -> None: + """Execute SQL query with `name: InsertThirdPartyStat :exec`. + + ```sql + INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + total: int. + """ + with conn.cursor() as cur: + cur.execute(INSERT_THIRD_PARTY_STAT, (id_, total)) + + +def get_third_party_stat(conn: pymysql.Connection, *, id_: int) -> models.Model3RdPartyStat | None: + """Fetch one from the db using the SQL query with `name: GetThirdPartyStat :one`. + + ```sql + SELECT id, total FROM `3rd_party_stats` WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.Model3RdPartyStat` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_THIRD_PARTY_STAT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.Model3RdPartyStat(id_=row[0], total=row[1]) diff --git a/test/driver_pymysql/dataclass/functions/queries_slice.py b/test/driver_pymysql/dataclass/functions/queries_slice.py new file mode 100644 index 00000000..dab5194f --- /dev/null +++ b/test/driver_pymysql/dataclass/functions/queries_slice.py @@ -0,0 +1,318 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_slice.sql +"""Module containing queries from file queries_slice.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "delete_slice_rows", + "get_first_slice_name", + "get_slice_row_filtered", + "get_slice_rows", + "get_slice_rows_by_name_or_note", + "get_slice_rows_by_name_or_note_filtered", + "get_slice_rows_by_notes", + "insert_slice_row", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.dataclass.functions import models + + +INSERT_SLICE_ROW: typing.Final[str] = """-- name: InsertSliceRow :exec +INSERT INTO test_slice (id, name, note) VALUES (%s, %s, %s) +""" + +GET_SLICE_ROWS: typing.Final[str] = """-- name: GetSliceRows :many +SELECT id, name, note FROM test_slice WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +GET_SLICE_ROW_FILTERED: typing.Final[str] = """-- name: GetSliceRowFiltered :one +SELECT id, name, note FROM test_slice WHERE name = %s AND id IN (/*SLICE:ids*/%s) AND id != %s LIMIT 1 +""" + +GET_SLICE_ROWS_BY_NOTES: typing.Final[str] = """-- name: GetSliceRowsByNotes :many +SELECT id, name, note FROM test_slice WHERE note IN (/*SLICE:notes*/%s) ORDER BY id +""" + +GET_FIRST_SLICE_NAME: typing.Final[str] = """-- name: GetFirstSliceName :one +SELECT name FROM test_slice WHERE id IN (/*SLICE:ids*/%s) OR name IN (/*SLICE:names*/%s) ORDER BY id LIMIT 1 +""" + +GET_SLICE_ROWS_BY_NAME_OR_NOTE: typing.Final[str] = """-- name: GetSliceRowsByNameOrNote :many +SELECT id, name, note FROM test_slice WHERE name IN (/*SLICE:names*/%s) OR note IN (/*SLICE:names*/%s) ORDER BY id +""" + +GET_SLICE_ROWS_BY_NAME_OR_NOTE_FILTERED: typing.Final[str] = """-- name: GetSliceRowsByNameOrNoteFiltered :many +SELECT id, name, note FROM test_slice WHERE name IN (/*SLICE:names*/%s) AND id != %s OR note IN (/*SLICE:names*/%s) ORDER BY id +""" + +DELETE_SLICE_ROWS: typing.Final[str] = """-- name: DeleteSliceRows :execrows +DELETE FROM test_slice WHERE id IN (/*SLICE:ids*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_slice_row(conn: pymysql.Connection, *, id_: int, name: str, note: str | None) -> None: + """Execute SQL query with `name: InsertSliceRow :exec`. + + ```sql + INSERT INTO test_slice (id, name, note) VALUES (%s, %s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + name: str. + note: str | None. + """ + with conn.cursor() as cur: + cur.execute(INSERT_SLICE_ROW, (id_, name, note)) + + +def get_slice_rows(conn: pymysql.Connection, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestSlice]: + """Fetch many from the db using the SQL query with `name: GetSliceRows :many`. + + ```sql + SELECT id, name, note FROM test_slice WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + + Returns: + Helper class of type `QueryResults[models.TestSlice]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestSlice: + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + sql = GET_SLICE_ROWS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *ids) + + +def get_slice_row_filtered(conn: pymysql.Connection, *, name: str, ids: collections.abc.Sequence[int], id_: int) -> models.TestSlice | None: + """Fetch one from the db using the SQL query with `name: GetSliceRowFiltered :one`. + + ```sql + SELECT id, name, note FROM test_slice WHERE name = %s AND id IN (/*SLICE:ids*/%s) AND id != %s LIMIT 1 + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + name: str. + ids: collections.abc.Sequence[int]. + id_: int. + + Returns: + Result of type `models.TestSlice` fetched from the db. Will be `None` if not found. + """ + sql = GET_SLICE_ROW_FILTERED.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + with conn.cursor() as cur: + cur.execute(sql, (name, *ids, id_)) + row = cur.fetchone() + if row is None: + return None + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + +def get_slice_rows_by_notes(conn: pymysql.Connection, *, notes: collections.abc.Sequence[str]) -> QueryResults[models.TestSlice]: + """Fetch many from the db using the SQL query with `name: GetSliceRowsByNotes :many`. + + ```sql + SELECT id, name, note FROM test_slice WHERE note IN (/*SLICE:notes*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + notes: collections.abc.Sequence[str]. + + Returns: + Helper class of type `QueryResults[models.TestSlice]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestSlice: + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + sql = GET_SLICE_ROWS_BY_NOTES.replace("/*SLICE:notes*/%s", ",".join(("%s",) * len(notes)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *notes) + + +def get_first_slice_name(conn: pymysql.Connection, *, ids: collections.abc.Sequence[int], names: collections.abc.Sequence[str]) -> str | None: + """Fetch one from the db using the SQL query with `name: GetFirstSliceName :one`. + + ```sql + SELECT name FROM test_slice WHERE id IN (/*SLICE:ids*/%s) OR name IN (/*SLICE:names*/%s) ORDER BY id LIMIT 1 + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + names: collections.abc.Sequence[str]. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + sql = GET_FIRST_SLICE_NAME.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + sql = sql.replace("/*SLICE:names*/%s", ",".join(("%s",) * len(names)) or "NULL", 1) + with conn.cursor() as cur: + cur.execute(sql, (*ids, *names)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_slice_rows_by_name_or_note(conn: pymysql.Connection, *, names: collections.abc.Sequence[str]) -> QueryResults[models.TestSlice]: + """Fetch many from the db using the SQL query with `name: GetSliceRowsByNameOrNote :many`. + + ```sql + SELECT id, name, note FROM test_slice WHERE name IN (/*SLICE:names*/%s) OR note IN (/*SLICE:names*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + names: collections.abc.Sequence[str]. + + Returns: + Helper class of type `QueryResults[models.TestSlice]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestSlice: + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + sql = GET_SLICE_ROWS_BY_NAME_OR_NOTE.replace("/*SLICE:names*/%s", ",".join(("%s",) * len(names)) or "NULL") + return QueryResults(conn, sql, _decode_hook, *names, *names) + + +def get_slice_rows_by_name_or_note_filtered(conn: pymysql.Connection, *, names: collections.abc.Sequence[str], id_: int) -> QueryResults[models.TestSlice]: + """Fetch many from the db using the SQL query with `name: GetSliceRowsByNameOrNoteFiltered :many`. + + ```sql + SELECT id, name, note FROM test_slice WHERE name IN (/*SLICE:names*/%s) AND id != %s OR note IN (/*SLICE:names*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + names: collections.abc.Sequence[str]. + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestSlice]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestSlice: + return models.TestSlice(id_=row[0], name=row[1], note=row[2]) + + sql = GET_SLICE_ROWS_BY_NAME_OR_NOTE_FILTERED.replace("/*SLICE:names*/%s", ",".join(("%s",) * len(names)) or "NULL") + return QueryResults(conn, sql, _decode_hook, *names, id_, *names) + + +def delete_slice_rows(conn: pymysql.Connection, *, ids: collections.abc.Sequence[int]) -> int: + """Execute SQL query with `name: DeleteSliceRows :execrows` and return the number of affected rows. + + ```sql + DELETE FROM test_slice WHERE id IN (/*SLICE:ids*/%s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + sql = DELETE_SLICE_ROWS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + with conn.cursor() as cur: + return cur.execute(sql, (*ids,)) diff --git a/test/driver_pymysql/dataclass/ruff.toml b/test/driver_pymysql/dataclass/ruff.toml new file mode 100644 index 00000000..f37cac11 --- /dev/null +++ b/test/driver_pymysql/dataclass/ruff.toml @@ -0,0 +1,5 @@ +extend="../../../ruff.toml" + + +[lint.pydocstyle] +convention = "google" \ No newline at end of file diff --git a/test/driver_pymysql/dataclass/test_pymysql_dataclass_classes.py b/test/driver_pymysql/dataclass/test_pymysql_dataclass_classes.py new file mode 100644 index 00000000..2c87efce --- /dev/null +++ b/test/driver_pymysql/dataclass/test_pymysql_dataclass_classes.py @@ -0,0 +1,1125 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import dataclasses +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import pymysql +import pymysql.cursors +import pytest + +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.dataclass.classes import enums +from test.driver_pymysql.dataclass.classes import models +from test.driver_pymysql.dataclass.classes import queries +from test.driver_pymysql.dataclass.classes import queries_case +from test.driver_pymysql.dataclass.classes import queries_enum_override +from test.driver_pymysql.dataclass.classes import queries_field_namings +from test.driver_pymysql.dataclass.classes import queries_invalid_identifiers + +# Fixed ids: the MySQL tables are shared by every pymysql/asyncmy suite in the +# session, so each test file owns a distinct id range. This file: 1000-1499. +MAIN_ID = 1000 +TYPE_OVERRIDE_ID = 1100 +TYPE_OVERRIDE_NONE_ID = 1101 +ENUM_OVERRIDE_ID = 1150 +ENUM_OVERRIDE_ID_2 = 1151 +CASE_ID = 1200 +RESERVED_ARG_ID = 1250 +FIELD_NAMING_ID = 1300 +INVALID_IDENTIFIER_ID = 1350 +THIRD_PARTY_ID = 1360 +THIRD_PARTY_TOTAL = 9000 + +CASE_DT = datetime.datetime(2026, 7, 19, 8, 15) +CASE_DEC = decimal.Decimal("12.34") +RESERVED_ARG_VALUE = "pymysql-dataclass-classes-conn" +EXEC_LAST_ID_NAME = "pymysql-dataclass-classes" +UPDATED_VARCHAR = "updated varchar" +# decimal(12,4) and numeric(10,2) come back padded to their full scale. +DECIMAL_PADDED = "1234.5000" +NUMERIC_PADDED = "87.60" +EXPECTED_MONTH = "2026-01" + + +class TestPymysqlDataclassClasses: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=TYPE_OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MAIN_ID, + int_test=42, + integer_test=43, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 10, + year_test=2026, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.e, + double_precision_test=1.41421, + real_test=math.pi, + decimal_test=decimal.Decimal("1234.5"), + numeric_test=decimal.Decimal("87.6"), + char_test="ABC", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"bin-test".ljust(16, b"\x00")), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tiny blob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"medium blob"), + longblob_test=memoryview(b"long blob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 1), + datetime_test=datetime.datetime(2026, 1, 15, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 15, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 15, 6, 30, 45), + time_test=datetime.timedelta(hours=13, minutes=14, seconds=15), + json_test=json.dumps({"foo": "bar"}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=None, + tinyint1_test=None, + bool_test=None, + boolean_test=None, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=None, + varbinary_test=model.varbinary_test, + tinyblob_test=None, + blob_test=None, + mediumblob_test=None, + longblob_test=model.longblob_test, + bit_test=None, + date_test=None, + datetime_test=None, + datetime6_test=None, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=None, + tag=enums.TestInnerMysqlTypesTag.ALPHA, + ) + + @pytest.fixture(scope="class") + def queries_obj(self, pymysql_conn: pymysql.Connection) -> queries.Queries: + return queries.Queries(conn=pymysql_conn) + + @pytest.fixture(scope="class") + def case_obj(self, pymysql_conn: pymysql.Connection) -> queries_case.QueriesCase: + return queries_case.QueriesCase(conn=pymysql_conn) + + @pytest.fixture(scope="class") + def enum_override_obj(self, pymysql_conn: pymysql.Connection) -> queries_enum_override.QueriesEnumOverride: + return queries_enum_override.QueriesEnumOverride(conn=pymysql_conn) + + @pytest.fixture(scope="class") + def field_namings_obj(self, pymysql_conn: pymysql.Connection) -> queries_field_namings.QueriesFieldNamings: + return queries_field_namings.QueriesFieldNamings(conn=pymysql_conn) + + @pytest.fixture(scope="class") + def invalid_identifiers_obj(self, pymysql_conn: pymysql.Connection) -> queries_invalid_identifiers.QueriesInvalidIdentifiers: + return queries_invalid_identifiers.QueriesInvalidIdentifiers(conn=pymysql_conn) + + def test_conn_attr(self, queries_obj: queries.Queries) -> None: + assert isinstance(queries_obj.conn, pymysql.Connection) + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::insert") + def test_insert( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + queries_obj.insert_one_mysql_type( + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::inner_insert", depends=["PymysqlTestDataclassClasses::insert"]) + def test_inner_insert( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + queries_obj.insert_one_inner_mysql_type( + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_one", depends=["PymysqlTestDataclassClasses::inner_insert"]) + def test_get_one( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_mysql_type(id_=model.id_) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + + # MySQL pads decimals to their declared scale and binary(16) to full + # width, keeps datetime(6) microseconds, and normalizes json spacing. + assert str(result.decimal_test) == DECIMAL_PADDED + assert str(result.numeric_test) == NUMERIC_PADDED + assert bytes(result.binary_test) == b"bin-test".ljust(16, b"\x00") + assert bytes(result.bit_test) == b"\x80" + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + assert isinstance(result.time_test, datetime.timedelta) + assert result.datetime6_test == model.datetime6_test + assert json.loads(result.json_test) == json.loads(model.json_test) + assert dataclasses.replace(result, json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_one_none", depends=["PymysqlTestDataclassClasses::get_one"]) + def test_get_one_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_mysql_type(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_one_inner", depends=["PymysqlTestDataclassClasses::get_one_none"]) + def test_get_one_inner( + self, + queries_obj: queries.Queries, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = queries_obj.get_one_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_one_inner_none", depends=["PymysqlTestDataclassClasses::get_one_inner"]) + def test_get_one_inner_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_inner_mysql_type(table_id=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_date", depends=["PymysqlTestDataclassClasses::get_one_inner_none"]) + def test_get_date( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_date(id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_date_none", depends=["PymysqlTestDataclassClasses::get_date"]) + def test_get_date_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_date(id_=0, date_test=model.date_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_datetime", depends=["PymysqlTestDataclassClasses::get_date_none"]) + def test_get_datetime( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_datetime(id_=model.id_, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_datetime_none", depends=["PymysqlTestDataclassClasses::get_datetime"]) + def test_get_datetime_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_datetime(id_=0, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_time", depends=["PymysqlTestDataclassClasses::get_datetime_none"]) + def test_get_time( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_time(id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_time_none", depends=["PymysqlTestDataclassClasses::get_time"]) + def test_get_time_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_time(id_=0, time_test=model.time_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_bool", depends=["PymysqlTestDataclassClasses::get_time_none"]) + def test_get_bool( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_bool(id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, bool) + assert result is True + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_bool_none", depends=["PymysqlTestDataclassClasses::get_bool"]) + def test_get_bool_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_bool(id_=0, tinyint1_test=False) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_decimal", depends=["PymysqlTestDataclassClasses::get_bool_none"]) + def test_get_decimal( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_decimal(id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + assert result == model.decimal_test + assert str(result) == DECIMAL_PADDED + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_decimal_none", depends=["PymysqlTestDataclassClasses::get_decimal"]) + def test_get_decimal_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_decimal(id_=0, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_blob", depends=["PymysqlTestDataclassClasses::get_decimal_none"]) + def test_get_blob( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_blob(id_=model.id_, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_blob_none", depends=["PymysqlTestDataclassClasses::get_blob"]) + def test_get_blob_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_blob(id_=0, blob_test=model.blob_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_bit", depends=["PymysqlTestDataclassClasses::get_blob_none"]) + def test_get_bit( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_bit(id_=model.id_) + + assert result is not None + assert isinstance(result, memoryview) + assert bytes(result) == b"\x80" + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_bit_none", depends=["PymysqlTestDataclassClasses::get_bit"]) + def test_get_bit_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_bit(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_year", depends=["PymysqlTestDataclassClasses::get_bit_none"]) + def test_get_year( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_year(id_=model.id_) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_year_none", depends=["PymysqlTestDataclassClasses::get_year"]) + def test_get_year_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_year(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_json", depends=["PymysqlTestDataclassClasses::get_year_none"]) + def test_get_json( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_json(id_=model.id_) + + assert result is not None + assert isinstance(result, str) + # MySQL normalizes json spacing; never compare the raw strings. + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_json_none", depends=["PymysqlTestDataclassClasses::get_json"]) + def test_get_json_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_json(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_mood", depends=["PymysqlTestDataclassClasses::get_json_none"]) + def test_get_mood( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_mood(id_=model.id_, mood=model.mood) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_mood_none", depends=["PymysqlTestDataclassClasses::get_mood"]) + def test_get_mood_none( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_mood(id_=0, mood=model.mood) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_tag", depends=["PymysqlTestDataclassClasses::get_mood_none"]) + def test_get_tag( + self, + queries_obj: queries.Queries, + model: models.TestMysqlType, + ) -> None: + result = queries_obj.get_one_tag(id_=model.id_) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.BETA + assert result == model.tag + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_tag_none", depends=["PymysqlTestDataclassClasses::get_tag"]) + def test_get_tag_none( + self, + queries_obj: queries.Queries, + ) -> None: + result = queries_obj.get_one_tag(id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many", depends=["PymysqlTestDataclassClasses::get_tag_none"]) + def test_get_many(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mysql_type(id_=model.id_) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + assert json.loads(results[0].json_test) == json.loads(model.json_test) + assert dataclasses.replace(results[0], json_test=model.json_test) == model + + results = result() + assert len(results) == 1 + assert dataclasses.replace(results[0], json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_iter", depends=["PymysqlTestDataclassClasses::get_many"]) + def test_get_many_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_mysql_type(id_=model.id_): + assert result is not None + assert isinstance(result, models.TestMysqlType) + assert dataclasses.replace(result, json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_inner", depends=["PymysqlTestDataclassClasses::get_many_iter"]) + def test_get_many_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], models.TestInnerMysqlType) + assert results[0] == inner_model + + results = result() + assert results[0] == inner_model + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_inner_iter", depends=["PymysqlTestDataclassClasses::get_many_inner"]) + def test_get_many_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + for result in queries_obj.get_many_inner_mysql_type(table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_many_nullable_inner", + depends=["PymysqlTestDataclassClasses::get_many_inner_iter"], + ) + def test_get_many_nullable_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the query uses the NULL-safe <=> comparison. + result = queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = result() + assert isinstance(results[0], models.TestInnerMysqlType) + assert results[0] == inner_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_many_nullable_inner_iter", + depends=["PymysqlTestDataclassClasses::get_many_nullable_inner"], + ) + def test_get_many_nullable_inner_iter(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + for result in queries_obj.get_many_nullable_inner_mysql_type(table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_many_date", + depends=["PymysqlTestDataclassClasses::get_many_nullable_inner_iter"], + ) + def test_get_many_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_date(id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], datetime.date) + assert results[0] == model.date_test + + results = result() + assert results[0] == model.date_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_date_iter", depends=["PymysqlTestDataclassClasses::get_many_date"]) + def test_get_many_date_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_date(id_=model.id_, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_time", depends=["PymysqlTestDataclassClasses::get_many_date_iter"]) + def test_get_many_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_time(id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], datetime.timedelta) + assert results[0] == model.time_test + + results = result() + assert results[0] == model.time_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_time_iter", depends=["PymysqlTestDataclassClasses::get_many_time"]) + def test_get_many_time_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_time(id_=model.id_, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_bool", depends=["PymysqlTestDataclassClasses::get_many_time_iter"]) + def test_get_many_bool(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_bool(id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], bool) + assert results[0] is True + + results = result() + assert results[0] is True + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_bool_iter", depends=["PymysqlTestDataclassClasses::get_many_bool"]) + def test_get_many_bool_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_bool(id_=model.id_, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + assert result is True + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_many_decimal", + depends=["PymysqlTestDataclassClasses::get_many_bool_iter"], + ) + def test_get_many_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_decimal(id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], decimal.Decimal) + assert results[0] == model.decimal_test + assert str(results[0]) == DECIMAL_PADDED + + results = result() + assert results[0] == model.decimal_test + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_many_decimal_iter", + depends=["PymysqlTestDataclassClasses::get_many_decimal"], + ) + def test_get_many_decimal_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_decimal(id_=model.id_, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + assert result == model.decimal_test + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_many_mood", + depends=["PymysqlTestDataclassClasses::get_many_decimal_iter"], + ) + def test_get_many_mood(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mood(mood=model.mood) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert len(results) == 1 + assert isinstance(results[0], enums.TestMysqlTypesMood) + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + results = result() + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_many_mood_iter", depends=["PymysqlTestDataclassClasses::get_many_mood"]) + def test_get_many_mood_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_mood(mood=model.mood): + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::list_months", depends=["PymysqlTestDataclassClasses::get_many_mood_iter"]) + def test_list_months(self, queries_obj: queries.Queries) -> None: + # DATE_FORMAT emits %% in the stored SQL; the empty argument tuple + # still goes through pymysql's %-substitution, halving it back. + result = queries_obj.list_months() + + assert result is not None + assert isinstance(result, queries.QueryResults) + months = result() + assert list(months) == [EXPECTED_MONTH] + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::list_months_iter", depends=["PymysqlTestDataclassClasses::list_months"]) + def test_list_months_iter(self, queries_obj: queries.Queries) -> None: + months = list(queries_obj.list_months()) + assert months == [EXPECTED_MONTH] + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::count", depends=["PymysqlTestDataclassClasses::list_months_iter"]) + def test_count_mysql_types(self, queries_obj: queries.Queries) -> None: + result = queries_obj.count_mysql_types() + + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::all_cursor", depends=["PymysqlTestDataclassClasses::count"]) + def test_all_mysql_types_cursor(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + cur = queries_obj.all_mysql_types_cursor() + + assert isinstance(cur, pymysql.cursors.Cursor) + rows = cur.fetchall() + # The shared table may carry other files' rows; assert on our own. + assert model.id_ in {row[0] for row in rows} + cur.close() + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::update_rows", depends=["PymysqlTestDataclassClasses::all_cursor"]) + def test_update_varchar_rows(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.update_varchar_test(varchar_test=UPDATED_VARCHAR, id_=model.id_) + + assert isinstance(result, int) + assert result == 1 + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::update_rows_noop", depends=["PymysqlTestDataclassClasses::update_rows"]) + def test_update_varchar_rows_noop(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + # Without CLIENT.FOUND_ROWS MySQL reports changed rows, so setting + # the same value again affects nothing. + result = queries_obj.update_varchar_test(varchar_test=UPDATED_VARCHAR, id_=model.id_) + + assert result == 0 + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::insert_last_id", depends=["PymysqlTestDataclassClasses::update_rows_noop"]) + def test_insert_exec_last_id(self, queries_obj: queries.Queries) -> None: + # The AUTO_INCREMENT counter persists across runs; never assert an + # exact id. + new_id = queries_obj.insert_exec_last_id(name=EXEC_LAST_ID_NAME) + + assert new_id is not None + assert isinstance(new_id, int) + assert new_id > 0 + assert queries_obj.get_exec_last_id_name(id_=new_id) == EXEC_LAST_ID_NAME + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::delete", depends=["PymysqlTestDataclassClasses::insert_last_id"]) + def test_delete_mysql_type(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + queries_obj.delete_one_mysql_type(id_=model.id_) + + assert queries_obj.get_one_mysql_type(id_=model.id_) is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::insert_type_override") + def test_insert_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + queries_obj.insert_type_override(id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_type_override", + depends=["PymysqlTestDataclassClasses::insert_type_override"], + ) + def test_get_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = queries_obj.get_type_override(id_=override_model.id_) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_type_override_none_value", + depends=["PymysqlTestDataclassClasses::get_type_override"], + ) + def test_get_type_override_none_value(self, queries_obj: queries.Queries) -> None: + # The override target is nullable: NULL must come back as None + # without passing through UserString. + queries_obj.insert_type_override(id_=TYPE_OVERRIDE_NONE_ID, text_test=None) + result = queries_obj.get_type_override(id_=TYPE_OVERRIDE_NONE_ID) + + assert result is not None + assert result.text_test is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_type_override_not_found", + depends=["PymysqlTestDataclassClasses::get_type_override_none_value"], + ) + def test_get_type_override_not_found(self, queries_obj: queries.Queries) -> None: + assert queries_obj.get_type_override(id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::insert_reserved_arg") + def test_insert_reserved_arg(self, queries_obj: queries.Queries) -> None: + # The column is literally named "conn"; on methods the parameter + # keeps its name because self is the only implicit argument. + queries_obj.insert_reserved_arg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_reserved_arg", depends=["PymysqlTestDataclassClasses::insert_reserved_arg"]) + def test_get_reserved_arg(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_reserved_arg(conn=RESERVED_ARG_VALUE) + + assert result == models.TestReservedArg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_reserved_arg_not_found", + depends=["PymysqlTestDataclassClasses::get_reserved_arg"], + ) + def test_get_reserved_arg_not_found(self, queries_obj: queries.Queries) -> None: + assert queries_obj.get_reserved_arg(conn="missing-reserved-arg-value") is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::insert_case_rows") + def test_insert_case_rows(self, case_obj: queries_case.QueriesCase) -> None: + case_obj.insert_case_row(id_=CASE_ID, upper_dt=CASE_DT, prec_dec=CASE_DEC) + case_obj.insert_case_row(id_=CASE_ID + 1, upper_dt=CASE_DT, prec_dec=CASE_DEC) + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_case_row", depends=["PymysqlTestDataclassClasses::insert_case_rows"]) + def test_get_case_row(self, case_obj: queries_case.QueriesCase) -> None: + row = case_obj.get_case_row(id_=CASE_ID) + + assert row is not None + assert isinstance(row.upper_dt, datetime.datetime) + assert row.upper_dt == CASE_DT + assert isinstance(row.prec_dec, decimal.Decimal) + assert row.prec_dec == CASE_DEC + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_case_row_not_found", + depends=["PymysqlTestDataclassClasses::get_case_row"], + ) + def test_get_case_row_not_found(self, case_obj: queries_case.QueriesCase) -> None: + assert case_obj.get_case_row(id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::count_case_rows_filters", + depends=["PymysqlTestDataclassClasses::get_case_row_not_found"], + ) + def test_count_case_rows_filters(self, case_obj: queries_case.QueriesCase) -> None: + # The WHERE clause lives inside an executable /*! version comment; + # raising the threshold by one must drop exactly the first row. + count_ge_first = case_obj.count_case_rows(id_=CASE_ID) + count_ge_second = case_obj.count_case_rows(id_=CASE_ID + 1) + + assert count_ge_first is not None + assert count_ge_second is not None + assert count_ge_first - count_ge_second == 1 + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::insert_enum_override") + def test_insert_enum_override(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + enum_override_obj.insert_enum_override(id_=ENUM_OVERRIDE_ID, mood_test="happy") + enum_override_obj.insert_enum_override(id_=ENUM_OVERRIDE_ID_2, mood_test="sad") + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::count_enum_override_by_moods", + depends=["PymysqlTestDataclassClasses::insert_enum_override"], + ) + def test_count_enum_override_by_moods(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + # Each slice element converts back through the enum class before + # binding; an invalid member raises before any SQL runs. Counts are + # relative: the shared table carries other files' rows too. + both = enum_override_obj.count_enum_override_by_moods(moods=("happy", "sad")) + happy = enum_override_obj.count_enum_override_by_moods(moods=["happy"]) + sad = enum_override_obj.count_enum_override_by_moods(moods=["sad"]) + assert both is not None + assert happy is not None + assert sad is not None + assert happy >= 1 + assert sad >= 1 + assert both == happy + sad + assert enum_override_obj.count_enum_override_by_moods(moods=[]) == 0 + with pytest.raises(ValueError, match="not-a-mood"): + enum_override_obj.count_enum_override_by_moods(moods=["not-a-mood"]) + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_enum_override_mood", + depends=["PymysqlTestDataclassClasses::insert_enum_override"], + ) + def test_get_enum_override_mood(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + result = enum_override_obj.get_enum_override_mood(id_=ENUM_OVERRIDE_ID) + + assert result == "happy" + assert isinstance(result, str) + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_enum_override_mood_not_found", + depends=["PymysqlTestDataclassClasses::get_enum_override_mood"], + ) + def test_get_enum_override_mood_not_found(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + assert enum_override_obj.get_enum_override_mood(id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::list_enum_override_by_ids", + depends=["PymysqlTestDataclassClasses::get_enum_override_mood_not_found"], + ) + def test_list_enum_override_by_ids(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + result = enum_override_obj.list_enum_override_by_ids(ids=[ENUM_OVERRIDE_ID, ENUM_OVERRIDE_ID_2]) + + assert isinstance(result, queries_enum_override.QueryResults) + rows = result() + assert rows == [ + models.TestEnumOverride(id_=ENUM_OVERRIDE_ID, mood_test="happy"), + models.TestEnumOverride(id_=ENUM_OVERRIDE_ID_2, mood_test="sad"), + ] + assert list(result) == rows + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::list_enum_override_by_ids_empty", + depends=["PymysqlTestDataclassClasses::list_enum_override_by_ids"], + ) + def test_list_enum_override_by_ids_empty(self, enum_override_obj: queries_enum_override.QueriesEnumOverride) -> None: + # An empty slice expands the placeholder to NULL: IN (NULL) matches + # no rows instead of raising. + assert enum_override_obj.list_enum_override_by_ids(ids=[])() == [] + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::seed_field_naming") + def test_seed_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + # No generated insert exists for this table; seed it directly. + with pymysql_conn.cursor() as cur: + cur.execute("INSERT INTO test_field_namings (id, outputs) VALUES (%s, %s)", (FIELD_NAMING_ID, json.dumps({"first": 1}))) + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::get_field_naming", depends=["PymysqlTestDataclassClasses::seed_field_naming"]) + def test_get_field_naming(self, field_namings_obj: queries_field_namings.QueriesFieldNamings) -> None: + result = field_namings_obj.get_field_naming(id_=FIELD_NAMING_ID) + + assert result is not None + assert result.id_ == FIELD_NAMING_ID + assert json.loads(result.outputs) == {"first": 1} + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_field_naming_not_found", + depends=["PymysqlTestDataclassClasses::get_field_naming"], + ) + def test_get_field_naming_not_found(self, field_namings_obj: queries_field_namings.QueriesFieldNamings) -> None: + assert field_namings_obj.get_field_naming(id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_joined_field_namings", + depends=["PymysqlTestDataclassClasses::get_field_naming_not_found"], + ) + def test_get_joined_field_namings(self, field_namings_obj: queries_field_namings.QueriesFieldNamings) -> None: + result = field_namings_obj.get_joined_field_namings(id_=FIELD_NAMING_ID) + + assert result is not None + assert isinstance(result, queries_field_namings.GetJoinedFieldNamingsRow) + assert json.loads(result.outputs) == {"first": 1} + assert result.outputs == result.outputs_2 + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::set_field_naming_outputs", + depends=["PymysqlTestDataclassClasses::get_joined_field_namings"], + ) + def test_set_field_naming_outputs(self, field_namings_obj: queries_field_namings.QueriesFieldNamings) -> None: + field_namings_obj.set_field_naming_outputs(outputs=json.dumps({"second": 2}), id_=FIELD_NAMING_ID) + result = field_namings_obj.get_field_naming(id_=FIELD_NAMING_ID) + + assert result is not None + assert json.loads(result.outputs) == {"second": 2} + + @pytest.mark.dependency(depends=["PymysqlTestDataclassClasses::set_field_naming_outputs"]) + def test_delete_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_field_namings WHERE id = %s", (FIELD_NAMING_ID,)) + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::insert_invalid_identifiers") + def test_insert_invalid_identifiers(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + invalid_identifiers_obj.insert_invalid_identifiers(id_=INVALID_IDENTIFIER_ID, column_3p_="3p-value", new_notes="some new notes") + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_invalid_identifiers", + depends=["PymysqlTestDataclassClasses::insert_invalid_identifiers"], + ) + def test_get_invalid_identifiers(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + result = invalid_identifiers_obj.get_invalid_identifiers(id_=INVALID_IDENTIFIER_ID) + + # The insert never sets `%pct`, so it stays NULL. + assert result == models.TestInvalidIdentifier( + id_=INVALID_IDENTIFIER_ID, + column_3p_="3p-value", + new_notes="some new notes", + column__pct=None, + ) + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_invalid_identifiers_not_found", + depends=["PymysqlTestDataclassClasses::get_invalid_identifiers"], + ) + def test_get_invalid_identifiers_not_found(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + assert invalid_identifiers_obj.get_invalid_identifiers(id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestDataclassClasses::insert_third_party_stat") + def test_insert_third_party_stat(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + invalid_identifiers_obj.insert_third_party_stat(id_=THIRD_PARTY_ID, total=THIRD_PARTY_TOTAL) + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_third_party_stat", + depends=["PymysqlTestDataclassClasses::insert_third_party_stat"], + ) + def test_get_third_party_stat(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + result = invalid_identifiers_obj.get_third_party_stat(id_=THIRD_PARTY_ID) + + assert result == models.Model3RdPartyStat(id_=THIRD_PARTY_ID, total=THIRD_PARTY_TOTAL) + + @pytest.mark.dependency( + name="PymysqlTestDataclassClasses::get_third_party_stat_not_found", + depends=["PymysqlTestDataclassClasses::get_third_party_stat"], + ) + def test_get_third_party_stat_not_found(self, invalid_identifiers_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + assert invalid_identifiers_obj.get_third_party_stat(id_=0) is None + + def test_one_missing_rows_return_none(self, pymysql_conn: pymysql.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. The + # count queries always return a row, so their miss branch needs the + # no-row stub; the sub-module Querier conn properties ride along. + obj = queries.Queries(conn=pymysql_conn) + assert obj.get_one_mysql_type(id_=-1) is None + assert obj.get_one_inner_mysql_type(table_id=-1) is None + assert obj.get_one_date(id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert obj.get_one_datetime(id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert obj.get_one_time(id_=-1, time_test=datetime.timedelta()) is None + assert obj.get_one_bool(id_=-1, tinyint1_test=False) is None + assert obj.get_one_decimal(id_=-1, decimal_test=decimal.Decimal(0)) is None + assert obj.get_one_blob(id_=-1, blob_test=memoryview(b"")) is None + assert obj.get_one_bit(id_=-1) is None + assert obj.get_one_year(id_=-1) is None + assert obj.get_one_json(id_=-1) is None + assert obj.get_one_mood(id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert obj.get_one_tag(id_=-1) is None + assert obj.get_exec_last_id_name(id_=-1) is None + assert obj.get_type_override(id_=-1) is None + assert obj.get_reserved_arg(conn="missing") is None + assert obj.touch_exec_last_id(name="untouched", id_=-1) is None + + case_obj = queries_case.QueriesCase(conn=pymysql_conn) + naming_obj = queries_field_namings.QueriesFieldNamings(conn=pymysql_conn) + invalid_obj = queries_invalid_identifiers.QueriesInvalidIdentifiers(conn=pymysql_conn) + enum_obj = queries_enum_override.QueriesEnumOverride(conn=pymysql_conn) + assert case_obj.conn is pymysql_conn + assert naming_obj.conn is pymysql_conn + assert invalid_obj.conn is pymysql_conn + assert enum_obj.conn is pymysql_conn + assert case_obj.get_case_row(id_=-1) is None + assert naming_obj.get_field_naming(id_=-1) is None + assert naming_obj.get_joined_field_namings(id_=-1) is None + assert invalid_obj.get_invalid_identifiers(id_=-1) is None + assert enum_obj.get_enum_override_mood(id_=-1) is None + assert enum_obj.count_enum_override_by_moods(moods=[]) == 0 + + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert queries.Queries(conn=stub).count_mysql_types() is None + assert queries_case.QueriesCase(conn=stub).count_case_rows(id_=0) is None + assert queries_enum_override.QueriesEnumOverride(conn=stub).count_enum_override_by_moods(moods=[]) is None + + @pytest.mark.dependency(depends=["PymysqlTestDataclassClasses::insert_enum_override"]) + def test_enum_override_cleanup(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_enum_override WHERE id IN (%s, %s)", (ENUM_OVERRIDE_ID, ENUM_OVERRIDE_ID_2)) + assert queries_enum_override.QueriesEnumOverride(conn=pymysql_conn).get_enum_override_mood(id_=ENUM_OVERRIDE_ID) is None diff --git a/test/driver_pymysql/dataclass/test_pymysql_dataclass_functions.py b/test/driver_pymysql/dataclass/test_pymysql_dataclass_functions.py new file mode 100644 index 00000000..568a6b14 --- /dev/null +++ b/test/driver_pymysql/dataclass/test_pymysql_dataclass_functions.py @@ -0,0 +1,1301 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import dataclasses +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import pymysql +import pymysql.cursors +import pytest + +from test.converters import Preferences +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.dataclass.functions import enums +from test.driver_pymysql.dataclass.functions import models +from test.driver_pymysql.dataclass.functions import queries +from test.driver_pymysql.dataclass.functions import queries_case +from test.driver_pymysql.dataclass.functions import queries_converters +from test.driver_pymysql.dataclass.functions import queries_enum_override +from test.driver_pymysql.dataclass.functions import queries_field_namings +from test.driver_pymysql.dataclass.functions import queries_invalid_identifiers +from test.driver_pymysql.dataclass.functions import queries_slice + +# Fixed ids: the MySQL tables are shared by every pymysql/asyncmy suite in the +# session, so each test file owns a distinct id range. This file: 1500-1999. +MAIN_ID = 1500 +TYPE_OVERRIDE_ID = 1600 +TYPE_OVERRIDE_NONE_ID = 1601 +ENUM_OVERRIDE_ID = 1650 +ENUM_OVERRIDE_ID_2 = 1651 +CASE_ID = 1700 +RESERVED_ARG_ID = 1750 +FIELD_NAMING_ID = 1800 +INVALID_IDENTIFIER_ID = 1850 +THIRD_PARTY_ID = 1860 +THIRD_PARTY_TOTAL = 9001 +SLICE_ID_BASE = 1900 +SLICE_ROW_COUNT = 4 +CONVERTER_ID = 1950 +CONVERTER_ID_2 = 1951 + +CASE_DT = datetime.datetime(2026, 7, 19, 8, 15) +CASE_DEC = decimal.Decimal("12.34") +RESERVED_ARG_VALUE = "pymysql-dataclass-functions-conn" +EXEC_LAST_ID_NAME = "pymysql-dataclass-functions" +UPDATED_VARCHAR = "updated varchar" +# decimal(12,4) and numeric(10,2) come back padded to their full scale. +DECIMAL_PADDED = "1234.5000" +NUMERIC_PADDED = "87.60" +EXPECTED_MONTH = "2026-01" + + +class TestPymysqlDataclassFunctions: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=TYPE_OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=MAIN_ID, + int_test=42, + integer_test=43, + mediumint_test=8_388_607, + smallint_test=32_767, + tinyint_test=127, + bigint_test=9_007_199_254_740_991, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 10, + year_test=2026, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.e, + double_precision_test=1.41421, + real_test=math.pi, + decimal_test=decimal.Decimal("1234.5"), + numeric_test=decimal.Decimal("87.6"), + char_test="ABC", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"bin-test".ljust(16, b"\x00")), + varbinary_test=memoryview(b"\x00\x01\x02hello"), + tinyblob_test=memoryview(b"tiny blob"), + blob_test=memoryview(b"\x00\x01\x02blob"), + mediumblob_test=memoryview(b"medium blob"), + longblob_test=memoryview(b"long blob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 1), + datetime_test=datetime.datetime(2026, 1, 15, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 15, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 15, 6, 30, 45), + time_test=datetime.timedelta(hours=13, minutes=14, seconds=15), + json_test=json.dumps({"foo": "bar"}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=None, + tinyint1_test=None, + bool_test=None, + boolean_test=None, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=None, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=None, + varbinary_test=model.varbinary_test, + tinyblob_test=None, + blob_test=None, + mediumblob_test=None, + longblob_test=model.longblob_test, + bit_test=None, + date_test=None, + datetime_test=None, + datetime6_test=None, + timestamp_test=None, + time_test=model.time_test, + json_test=None, + mood=None, + tag=enums.TestInnerMysqlTypesTag.ALPHA, + ) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert") + def test_insert( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + queries.insert_one_mysql_type( + conn=pymysql_conn, + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::inner_insert", depends=["PymysqlTestDataclassFunctions::insert"]) + def test_inner_insert( + self, + pymysql_conn: pymysql.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + queries.insert_one_inner_mysql_type( + conn=pymysql_conn, + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_one", depends=["PymysqlTestDataclassFunctions::inner_insert"]) + def test_get_one( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_mysql_type(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + + # MySQL pads decimals to their declared scale and binary(16) to full + # width, keeps datetime(6) microseconds, and normalizes json spacing. + assert str(result.decimal_test) == DECIMAL_PADDED + assert str(result.numeric_test) == NUMERIC_PADDED + assert bytes(result.binary_test) == b"bin-test".ljust(16, b"\x00") + assert bytes(result.bit_test) == b"\x80" + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + assert isinstance(result.time_test, datetime.timedelta) + assert result.datetime6_test == model.datetime6_test + assert json.loads(result.json_test) == json.loads(model.json_test) + assert dataclasses.replace(result, json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_one_none", depends=["PymysqlTestDataclassFunctions::get_one"]) + def test_get_one_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_mysql_type(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_one_inner", depends=["PymysqlTestDataclassFunctions::get_one_none"]) + def test_get_one_inner( + self, + pymysql_conn: pymysql.Connection, + inner_model: models.TestInnerMysqlType, + ) -> None: + result = queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_one_inner_none", + depends=["PymysqlTestDataclassFunctions::get_one_inner"], + ) + def test_get_one_inner_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_date", depends=["PymysqlTestDataclassFunctions::get_one_inner_none"]) + def test_get_date( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_date(conn=pymysql_conn, id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_date_none", depends=["PymysqlTestDataclassFunctions::get_date"]) + def test_get_date_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_date(conn=pymysql_conn, id_=0, date_test=model.date_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_datetime", depends=["PymysqlTestDataclassFunctions::get_date_none"]) + def test_get_datetime( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_datetime(conn=pymysql_conn, id_=model.id_, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_datetime_none", depends=["PymysqlTestDataclassFunctions::get_datetime"]) + def test_get_datetime_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_datetime(conn=pymysql_conn, id_=0, datetime_test=model.datetime_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_time", depends=["PymysqlTestDataclassFunctions::get_datetime_none"]) + def test_get_time( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_time(conn=pymysql_conn, id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_time_none", depends=["PymysqlTestDataclassFunctions::get_time"]) + def test_get_time_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_time(conn=pymysql_conn, id_=0, time_test=model.time_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_bool", depends=["PymysqlTestDataclassFunctions::get_time_none"]) + def test_get_bool( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_bool(conn=pymysql_conn, id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, bool) + assert result is True + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_bool_none", depends=["PymysqlTestDataclassFunctions::get_bool"]) + def test_get_bool_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_bool(conn=pymysql_conn, id_=0, tinyint1_test=False) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_decimal", depends=["PymysqlTestDataclassFunctions::get_bool_none"]) + def test_get_decimal( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_decimal(conn=pymysql_conn, id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + assert result == model.decimal_test + assert str(result) == DECIMAL_PADDED + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_decimal_none", depends=["PymysqlTestDataclassFunctions::get_decimal"]) + def test_get_decimal_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_decimal(conn=pymysql_conn, id_=0, decimal_test=model.decimal_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_blob", depends=["PymysqlTestDataclassFunctions::get_decimal_none"]) + def test_get_blob( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_blob(conn=pymysql_conn, id_=model.id_, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_blob_none", depends=["PymysqlTestDataclassFunctions::get_blob"]) + def test_get_blob_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_blob(conn=pymysql_conn, id_=0, blob_test=model.blob_test) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_bit", depends=["PymysqlTestDataclassFunctions::get_blob_none"]) + def test_get_bit( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_bit(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, memoryview) + assert bytes(result) == b"\x80" + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_bit_none", depends=["PymysqlTestDataclassFunctions::get_bit"]) + def test_get_bit_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_bit(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_year", depends=["PymysqlTestDataclassFunctions::get_bit_none"]) + def test_get_year( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_year(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_year_none", depends=["PymysqlTestDataclassFunctions::get_year"]) + def test_get_year_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_year(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_json", depends=["PymysqlTestDataclassFunctions::get_year_none"]) + def test_get_json( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_json(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, str) + # MySQL normalizes json spacing; never compare the raw strings. + assert json.loads(result) == json.loads(model.json_test) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_json_none", depends=["PymysqlTestDataclassFunctions::get_json"]) + def test_get_json_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_json(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_mood", depends=["PymysqlTestDataclassFunctions::get_json_none"]) + def test_get_mood( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_mood(conn=pymysql_conn, id_=model.id_, mood=model.mood) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_mood_none", depends=["PymysqlTestDataclassFunctions::get_mood"]) + def test_get_mood_none( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_mood(conn=pymysql_conn, id_=0, mood=model.mood) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_tag", depends=["PymysqlTestDataclassFunctions::get_mood_none"]) + def test_get_tag( + self, + pymysql_conn: pymysql.Connection, + model: models.TestMysqlType, + ) -> None: + result = queries.get_one_tag(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.BETA + assert result == model.tag + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_tag_none", depends=["PymysqlTestDataclassFunctions::get_tag"]) + def test_get_tag_none( + self, + pymysql_conn: pymysql.Connection, + ) -> None: + result = queries.get_one_tag(conn=pymysql_conn, id_=0) + + assert result is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_many", depends=["PymysqlTestDataclassFunctions::get_tag_none"]) + def test_get_many(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mysql_type(conn=pymysql_conn, id_=model.id_) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + assert json.loads(results[0].json_test) == json.loads(model.json_test) + assert dataclasses.replace(results[0], json_test=model.json_test) == model + + results = result() + assert len(results) == 1 + assert dataclasses.replace(results[0], json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_many_iter", depends=["PymysqlTestDataclassFunctions::get_many"]) + def test_get_many_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_mysql_type(conn=pymysql_conn, id_=model.id_): + assert result is not None + assert isinstance(result, models.TestMysqlType) + assert dataclasses.replace(result, json_test=model.json_test) == model + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_many_inner", depends=["PymysqlTestDataclassFunctions::get_many_iter"]) + def test_get_many_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = queries.get_many_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], models.TestInnerMysqlType) + assert results[0] == inner_model + + results = result() + assert results[0] == inner_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_inner_iter", + depends=["PymysqlTestDataclassFunctions::get_many_inner"], + ) + def test_get_many_inner_iter(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + for result in queries.get_many_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_nullable_inner", + depends=["PymysqlTestDataclassFunctions::get_many_inner_iter"], + ) + def test_get_many_nullable_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + # int_test is None; the query uses the NULL-safe <=> comparison. + result = queries.get_many_nullable_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id, int_test=inner_model.int_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = result() + assert isinstance(results[0], models.TestInnerMysqlType) + assert results[0] == inner_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_nullable_inner_iter", + depends=["PymysqlTestDataclassFunctions::get_many_nullable_inner"], + ) + def test_get_many_nullable_inner_iter(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + for result in queries.get_many_nullable_inner_mysql_type(conn=pymysql_conn, table_id=inner_model.table_id, int_test=inner_model.int_test): + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_date", + depends=["PymysqlTestDataclassFunctions::get_many_nullable_inner_iter"], + ) + def test_get_many_date(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_date(conn=pymysql_conn, id_=model.id_, date_test=model.date_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], datetime.date) + assert results[0] == model.date_test + + results = result() + assert results[0] == model.date_test + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_date_iter", + depends=["PymysqlTestDataclassFunctions::get_many_date"], + ) + def test_get_many_date_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_date(conn=pymysql_conn, id_=model.id_, date_test=model.date_test): + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_time", + depends=["PymysqlTestDataclassFunctions::get_many_date_iter"], + ) + def test_get_many_time(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_time(conn=pymysql_conn, id_=model.id_, time_test=model.time_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], datetime.timedelta) + assert results[0] == model.time_test + + results = result() + assert results[0] == model.time_test + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_time_iter", + depends=["PymysqlTestDataclassFunctions::get_many_time"], + ) + def test_get_many_time_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_time(conn=pymysql_conn, id_=model.id_, time_test=model.time_test): + assert result is not None + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_bool", + depends=["PymysqlTestDataclassFunctions::get_many_time_iter"], + ) + def test_get_many_bool(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_bool(conn=pymysql_conn, id_=model.id_, tinyint1_test=model.tinyint1_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], bool) + assert results[0] is True + + results = result() + assert results[0] is True + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_bool_iter", + depends=["PymysqlTestDataclassFunctions::get_many_bool"], + ) + def test_get_many_bool_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_bool(conn=pymysql_conn, id_=model.id_, tinyint1_test=model.tinyint1_test): + assert result is not None + assert isinstance(result, bool) + assert result is True + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_decimal", + depends=["PymysqlTestDataclassFunctions::get_many_bool_iter"], + ) + def test_get_many_decimal(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_decimal(conn=pymysql_conn, id_=model.id_, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert isinstance(results[0], decimal.Decimal) + assert results[0] == model.decimal_test + assert str(results[0]) == DECIMAL_PADDED + + results = result() + assert results[0] == model.decimal_test + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_decimal_iter", + depends=["PymysqlTestDataclassFunctions::get_many_decimal"], + ) + def test_get_many_decimal_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_decimal(conn=pymysql_conn, id_=model.id_, decimal_test=model.decimal_test): + assert result is not None + assert isinstance(result, decimal.Decimal) + assert result == model.decimal_test + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_mood", + depends=["PymysqlTestDataclassFunctions::get_many_decimal_iter"], + ) + def test_get_many_mood(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mood(conn=pymysql_conn, mood=model.mood) + + assert result is not None + assert isinstance(result, queries.QueryResults) + results = list(result) + assert len(results) == 1 + assert isinstance(results[0], enums.TestMysqlTypesMood) + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + results = result() + assert results[0] is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_many_mood_iter", + depends=["PymysqlTestDataclassFunctions::get_many_mood"], + ) + def test_get_many_mood_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_mood(conn=pymysql_conn, mood=model.mood): + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::list_months", depends=["PymysqlTestDataclassFunctions::get_many_mood_iter"]) + def test_list_months(self, pymysql_conn: pymysql.Connection) -> None: + # DATE_FORMAT emits %% in the stored SQL; the empty argument tuple + # still goes through pymysql's %-substitution, halving it back. + result = queries.list_months(conn=pymysql_conn) + + assert result is not None + assert isinstance(result, queries.QueryResults) + months = result() + assert list(months) == [EXPECTED_MONTH] + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::list_months_iter", depends=["PymysqlTestDataclassFunctions::list_months"]) + def test_list_months_iter(self, pymysql_conn: pymysql.Connection) -> None: + months = list(queries.list_months(conn=pymysql_conn)) + assert months == [EXPECTED_MONTH] + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::count", depends=["PymysqlTestDataclassFunctions::list_months_iter"]) + def test_count_mysql_types(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.count_mysql_types(conn=pymysql_conn) + + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::all_cursor", depends=["PymysqlTestDataclassFunctions::count"]) + def test_all_mysql_types_cursor(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + cur = queries.all_mysql_types_cursor(conn=pymysql_conn) + + assert isinstance(cur, pymysql.cursors.Cursor) + rows = cur.fetchall() + # The shared table may carry other files' rows; assert on our own. + assert model.id_ in {row[0] for row in rows} + cur.close() + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::update_rows", depends=["PymysqlTestDataclassFunctions::all_cursor"]) + def test_update_varchar_rows(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.update_varchar_test(conn=pymysql_conn, varchar_test=UPDATED_VARCHAR, id_=model.id_) + + assert isinstance(result, int) + assert result == 1 + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::update_rows_noop", depends=["PymysqlTestDataclassFunctions::update_rows"]) + def test_update_varchar_rows_noop(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + # Without CLIENT.FOUND_ROWS MySQL reports changed rows, so setting + # the same value again affects nothing. + result = queries.update_varchar_test(conn=pymysql_conn, varchar_test=UPDATED_VARCHAR, id_=model.id_) + + assert result == 0 + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_last_id", depends=["PymysqlTestDataclassFunctions::update_rows_noop"]) + def test_insert_exec_last_id(self, pymysql_conn: pymysql.Connection) -> None: + # The AUTO_INCREMENT counter persists across runs; never assert an + # exact id. + new_id = queries.insert_exec_last_id(conn=pymysql_conn, name=EXEC_LAST_ID_NAME) + + assert new_id is not None + assert isinstance(new_id, int) + assert new_id > 0 + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=new_id) == EXEC_LAST_ID_NAME + + # A statement that inserts nothing has no last row id: the OK + # packet's 0 maps to the documented None. + assert queries.touch_exec_last_id(conn=pymysql_conn, name="untouched", id_=new_id + 1000000) is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::delete", depends=["PymysqlTestDataclassFunctions::insert_last_id"]) + def test_delete_mysql_type(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + queries.delete_one_mysql_type(conn=pymysql_conn, id_=model.id_) + + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=model.id_) is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_type_override") + def test_insert_type_override(self, pymysql_conn: pymysql.Connection, override_model: models.TestTypeOverride) -> None: + queries.insert_type_override(conn=pymysql_conn, id_=override_model.id_, text_test=override_model.text_test) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_type_override", + depends=["PymysqlTestDataclassFunctions::insert_type_override"], + ) + def test_get_type_override(self, pymysql_conn: pymysql.Connection, override_model: models.TestTypeOverride) -> None: + result = queries.get_type_override(conn=pymysql_conn, id_=override_model.id_) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_type_override_none_value", + depends=["PymysqlTestDataclassFunctions::get_type_override"], + ) + def test_get_type_override_none_value(self, pymysql_conn: pymysql.Connection) -> None: + # The override target is nullable: NULL must come back as None + # without passing through UserString. + queries.insert_type_override(conn=pymysql_conn, id_=TYPE_OVERRIDE_NONE_ID, text_test=None) + result = queries.get_type_override(conn=pymysql_conn, id_=TYPE_OVERRIDE_NONE_ID) + + assert result is not None + assert result.text_test is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_type_override_not_found", + depends=["PymysqlTestDataclassFunctions::get_type_override_none_value"], + ) + def test_get_type_override_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries.get_type_override(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_reserved_arg") + def test_insert_reserved_arg(self, pymysql_conn: pymysql.Connection) -> None: + # The column is literally named "conn"; the generated parameter is + # deduplicated against the implicit connection argument. + queries.insert_reserved_arg(conn=pymysql_conn, id_=RESERVED_ARG_ID, conn_2=RESERVED_ARG_VALUE) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_reserved_arg", + depends=["PymysqlTestDataclassFunctions::insert_reserved_arg"], + ) + def test_get_reserved_arg(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_reserved_arg(conn=pymysql_conn, conn_2=RESERVED_ARG_VALUE) + + assert result == models.TestReservedArg(id_=RESERVED_ARG_ID, conn=RESERVED_ARG_VALUE) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_reserved_arg_not_found", + depends=["PymysqlTestDataclassFunctions::get_reserved_arg"], + ) + def test_get_reserved_arg_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries.get_reserved_arg(conn=pymysql_conn, conn_2="missing-reserved-arg-value") is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_case_rows") + def test_insert_case_rows(self, pymysql_conn: pymysql.Connection) -> None: + queries_case.insert_case_row(conn=pymysql_conn, id_=CASE_ID, upper_dt=CASE_DT, prec_dec=CASE_DEC) + queries_case.insert_case_row(conn=pymysql_conn, id_=CASE_ID + 1, upper_dt=CASE_DT, prec_dec=CASE_DEC) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_case_row", depends=["PymysqlTestDataclassFunctions::insert_case_rows"]) + def test_get_case_row(self, pymysql_conn: pymysql.Connection) -> None: + row = queries_case.get_case_row(conn=pymysql_conn, id_=CASE_ID) + + assert row is not None + assert isinstance(row.upper_dt, datetime.datetime) + assert row.upper_dt == CASE_DT + assert isinstance(row.prec_dec, decimal.Decimal) + assert row.prec_dec == CASE_DEC + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_case_row_not_found", + depends=["PymysqlTestDataclassFunctions::get_case_row"], + ) + def test_get_case_row_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_case.get_case_row(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::count_case_rows_filters", + depends=["PymysqlTestDataclassFunctions::get_case_row_not_found"], + ) + def test_count_case_rows_filters(self, pymysql_conn: pymysql.Connection) -> None: + # The WHERE clause lives inside an executable /*! version comment; + # raising the threshold by one must drop exactly the first row. + count_ge_first = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_ID) + count_ge_second = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_ID + 1) + + assert count_ge_first is not None + assert count_ge_second is not None + assert count_ge_first - count_ge_second == 1 + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_enum_override") + def test_insert_enum_override(self, pymysql_conn: pymysql.Connection) -> None: + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=ENUM_OVERRIDE_ID, mood_test="happy") + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=ENUM_OVERRIDE_ID_2, mood_test="sad") + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_enum_override_mood", + depends=["PymysqlTestDataclassFunctions::insert_enum_override"], + ) + def test_get_enum_override_mood(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=ENUM_OVERRIDE_ID) + + assert result == "happy" + assert isinstance(result, str) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_enum_override_mood_not_found", + depends=["PymysqlTestDataclassFunctions::get_enum_override_mood"], + ) + def test_get_enum_override_mood_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::list_enum_override_by_ids", + depends=["PymysqlTestDataclassFunctions::get_enum_override_mood_not_found"], + ) + def test_list_enum_override_by_ids(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=[ENUM_OVERRIDE_ID, ENUM_OVERRIDE_ID_2]) + + assert isinstance(result, queries_enum_override.QueryResults) + rows = result() + assert rows == [ + models.TestEnumOverride(id_=ENUM_OVERRIDE_ID, mood_test="happy"), + models.TestEnumOverride(id_=ENUM_OVERRIDE_ID_2, mood_test="sad"), + ] + assert list(result) == rows + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::list_enum_override_by_ids_empty", + depends=["PymysqlTestDataclassFunctions::list_enum_override_by_ids"], + ) + def test_list_enum_override_by_ids_empty(self, pymysql_conn: pymysql.Connection) -> None: + # An empty slice expands the placeholder to NULL: IN (NULL) matches + # no rows instead of raising. + assert queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=[])() == [] + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::seed_field_naming") + def test_seed_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + # No generated insert exists for this table; seed it directly. + with pymysql_conn.cursor() as cur: + cur.execute("INSERT INTO test_field_namings (id, outputs) VALUES (%s, %s)", (FIELD_NAMING_ID, json.dumps({"first": 1}))) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_field_naming", depends=["PymysqlTestDataclassFunctions::seed_field_naming"]) + def test_get_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_NAMING_ID) + + assert result is not None + assert result.id_ == FIELD_NAMING_ID + assert json.loads(result.outputs) == {"first": 1} + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_field_naming_not_found", + depends=["PymysqlTestDataclassFunctions::get_field_naming"], + ) + def test_get_field_naming_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_joined_field_namings", + depends=["PymysqlTestDataclassFunctions::get_field_naming_not_found"], + ) + def test_get_joined_field_namings(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_field_namings.get_joined_field_namings(conn=pymysql_conn, id_=FIELD_NAMING_ID) + + assert result is not None + assert isinstance(result, queries_field_namings.GetJoinedFieldNamingsRow) + assert json.loads(result.outputs) == {"first": 1} + assert result.outputs == result.outputs_2 + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::set_field_naming_outputs", + depends=["PymysqlTestDataclassFunctions::get_joined_field_namings"], + ) + def test_set_field_naming_outputs(self, pymysql_conn: pymysql.Connection) -> None: + queries_field_namings.set_field_naming_outputs(conn=pymysql_conn, outputs=json.dumps({"second": 2}), id_=FIELD_NAMING_ID) + result = queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_NAMING_ID) + + assert result is not None + assert json.loads(result.outputs) == {"second": 2} + + @pytest.mark.dependency(depends=["PymysqlTestDataclassFunctions::set_field_naming_outputs"]) + def test_delete_field_naming(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_field_namings WHERE id = %s", (FIELD_NAMING_ID,)) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_invalid_identifiers") + def test_insert_invalid_identifiers(self, pymysql_conn: pymysql.Connection) -> None: + queries_invalid_identifiers.insert_invalid_identifiers( + conn=pymysql_conn, + id_=INVALID_IDENTIFIER_ID, + column_3p_="3p-value", + new_notes="some new notes", + ) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_invalid_identifiers", + depends=["PymysqlTestDataclassFunctions::insert_invalid_identifiers"], + ) + def test_get_invalid_identifiers(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=INVALID_IDENTIFIER_ID) + + # The insert never sets `%pct`, so it stays NULL. + assert result == models.TestInvalidIdentifier( + id_=INVALID_IDENTIFIER_ID, + column_3p_="3p-value", + new_notes="some new notes", + column__pct=None, + ) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_invalid_identifiers_not_found", + depends=["PymysqlTestDataclassFunctions::get_invalid_identifiers"], + ) + def test_get_invalid_identifiers_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_third_party_stat") + def test_insert_third_party_stat(self, pymysql_conn: pymysql.Connection) -> None: + queries_invalid_identifiers.insert_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID, total=THIRD_PARTY_TOTAL) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_third_party_stat", + depends=["PymysqlTestDataclassFunctions::insert_third_party_stat"], + ) + def test_get_third_party_stat(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID) + + assert result == models.Model3RdPartyStat(id_=THIRD_PARTY_ID, total=THIRD_PARTY_TOTAL) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_third_party_stat_not_found", + depends=["PymysqlTestDataclassFunctions::get_third_party_stat"], + ) + def test_get_third_party_stat_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_slice_rows") + def test_insert_slice_rows(self, pymysql_conn: pymysql.Connection) -> None: + for offset, (name, note) in enumerate((("a", "x"), ("b", "y"), ("c", None), ("b", "y"))): + queries_slice.insert_slice_row(conn=pymysql_conn, id_=SLICE_ID_BASE + offset, name=name, note=note) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_slice_rows", depends=["PymysqlTestDataclassFunctions::insert_slice_rows"]) + def test_get_slice_rows(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_slice.get_slice_rows(conn=pymysql_conn, ids=[SLICE_ID_BASE, SLICE_ID_BASE + 2]) + assert isinstance(result, queries_slice.QueryResults) + rows = result() + assert rows == [ + models.TestSlice(id_=SLICE_ID_BASE, name="a", note="x"), + models.TestSlice(id_=SLICE_ID_BASE + 2, name="c", note=None), + ] + assert list(result) == rows + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_slice_rows_empty_slice", + depends=["PymysqlTestDataclassFunctions::insert_slice_rows"], + ) + def test_get_slice_rows_empty_slice(self, pymysql_conn: pymysql.Connection) -> None: + # An empty sequence expands the placeholder to NULL: IN (NULL) + # matches no rows instead of raising. + assert queries_slice.get_slice_rows(conn=pymysql_conn, ids=[])() == [] + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_slice_row_filtered", + depends=["PymysqlTestDataclassFunctions::insert_slice_rows"], + ) + def test_get_slice_row_filtered(self, pymysql_conn: pymysql.Connection) -> None: + # Plain params surround the slice, so this proves the flattened + # argument tuple binds in SQL text order. + row = queries_slice.get_slice_row_filtered( + conn=pymysql_conn, + name="b", + ids=[SLICE_ID_BASE + 1, SLICE_ID_BASE + 3], + id_=SLICE_ID_BASE + 1, + ) + assert row == models.TestSlice(id_=SLICE_ID_BASE + 3, name="b", note="y") + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_slice_row_filtered_not_found", + depends=["PymysqlTestDataclassFunctions::insert_slice_rows"], + ) + def test_get_slice_row_filtered_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_slice.get_slice_row_filtered(conn=pymysql_conn, name="a", ids=[SLICE_ID_BASE], id_=SLICE_ID_BASE) is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_slice_rows_by_notes", + depends=["PymysqlTestDataclassFunctions::insert_slice_rows"], + ) + def test_get_slice_rows_by_notes(self, pymysql_conn: pymysql.Connection) -> None: + # The slice targets a nullable column; the parameter is still a plain + # Sequence, and rows whose note is NULL never match. + rows = queries_slice.get_slice_rows_by_notes(conn=pymysql_conn, notes=["y"])() + assert rows == [ + models.TestSlice(id_=SLICE_ID_BASE + 1, name="b", note="y"), + models.TestSlice(id_=SLICE_ID_BASE + 3, name="b", note="y"), + ] + assert queries_slice.get_slice_rows_by_notes(conn=pymysql_conn, notes=[])() == [] + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_slice_rows_by_name_or_note", + depends=["PymysqlTestDataclassFunctions::insert_slice_rows"], + ) + def test_get_slice_rows_by_name_or_note(self, pymysql_conn: pymysql.Connection) -> None: + # The same slice name is used twice but the function takes ONE + # parameter; every marker occurrence is expanded and the sequence is + # bound once per occurrence. + rows = queries_slice.get_slice_rows_by_name_or_note(conn=pymysql_conn, names=["b", "x"])() + assert rows == [ + models.TestSlice(id_=SLICE_ID_BASE, name="a", note="x"), + models.TestSlice(id_=SLICE_ID_BASE + 1, name="b", note="y"), + models.TestSlice(id_=SLICE_ID_BASE + 3, name="b", note="y"), + ] + assert queries_slice.get_slice_rows_by_name_or_note(conn=pymysql_conn, names=[])() == [] + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_slice_rows_by_name_or_note_filtered", + depends=["PymysqlTestDataclassFunctions::insert_slice_rows"], + ) + def test_get_slice_rows_by_name_or_note_filtered(self, pymysql_conn: pymysql.Connection) -> None: + # A plain parameter sits between the two uses of the slice, so this + # proves the flattened arguments follow SQL text order. + rows = queries_slice.get_slice_rows_by_name_or_note_filtered(conn=pymysql_conn, names=["b", "x"], id_=SLICE_ID_BASE + 1)() + assert rows == [ + models.TestSlice(id_=SLICE_ID_BASE, name="a", note="x"), + models.TestSlice(id_=SLICE_ID_BASE + 3, name="b", note="y"), + ] + assert queries_slice.get_slice_rows_by_name_or_note_filtered(conn=pymysql_conn, names=[], id_=SLICE_ID_BASE)() == [] + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_first_slice_name_two_slices", + depends=["PymysqlTestDataclassFunctions::insert_slice_rows"], + ) + def test_get_first_slice_name_two_slices(self, pymysql_conn: pymysql.Connection) -> None: + name = queries_slice.get_first_slice_name(conn=pymysql_conn, ids=[SLICE_ID_BASE + 1], names=["a"]) + assert name == "a" + assert queries_slice.get_first_slice_name(conn=pymysql_conn, ids=[], names=[]) is None + + @pytest.mark.dependency( + depends=[ + "PymysqlTestDataclassFunctions::get_slice_rows", + "PymysqlTestDataclassFunctions::get_slice_rows_empty_slice", + "PymysqlTestDataclassFunctions::get_slice_row_filtered", + "PymysqlTestDataclassFunctions::get_slice_row_filtered_not_found", + "PymysqlTestDataclassFunctions::get_slice_rows_by_notes", + "PymysqlTestDataclassFunctions::get_slice_rows_by_name_or_note", + "PymysqlTestDataclassFunctions::get_slice_rows_by_name_or_note_filtered", + "PymysqlTestDataclassFunctions::get_first_slice_name_two_slices", + ] + ) + def test_delete_slice_rows(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_slice.delete_slice_rows(conn=pymysql_conn, ids=[]) == 0 + deleted = queries_slice.delete_slice_rows(conn=pymysql_conn, ids=[SLICE_ID_BASE + offset for offset in range(SLICE_ROW_COUNT)]) + assert deleted == SLICE_ROW_COUNT + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::insert_converted") + def test_insert_converted(self, pymysql_conn: pymysql.Connection) -> None: + queries_converters.insert_converted( + conn=pymysql_conn, + id_=CONVERTER_ID, + prefs=Preferences(theme="dark", notifications=True), + maybe_prefs=None, + tags=frozenset({"alpha", "beta"}), + ) + queries_converters.insert_converted( + conn=pymysql_conn, + id_=CONVERTER_ID_2, + prefs=Preferences(theme="light", notifications=False), + maybe_prefs=Preferences(theme="light", notifications=True), + tags=frozenset({"gamma"}), + ) + + @pytest.mark.dependency(name="PymysqlTestDataclassFunctions::get_converted", depends=["PymysqlTestDataclassFunctions::insert_converted"]) + def test_get_converted(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_converters.get_converted(conn=pymysql_conn, id_=CONVERTER_ID) + + assert result is not None + assert result.prefs == Preferences(theme="dark", notifications=True) + # NULL never reaches the decoder; it comes back as plain None. + assert result.maybe_prefs is None + assert result.tags == frozenset({"alpha", "beta"}) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_converted_with_prefs", + depends=["PymysqlTestDataclassFunctions::get_converted"], + ) + def test_get_converted_with_prefs(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_converters.get_converted(conn=pymysql_conn, id_=CONVERTER_ID_2) + + assert result == models.TestConverter( + id_=CONVERTER_ID_2, + prefs=Preferences(theme="light", notifications=False), + maybe_prefs=Preferences(theme="light", notifications=True), + tags=frozenset({"gamma"}), + ) + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::get_converted_not_found", + depends=["PymysqlTestDataclassFunctions::get_converted_with_prefs"], + ) + def test_get_converted_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert queries_converters.get_converted(conn=pymysql_conn, id_=0) is None + + @pytest.mark.dependency( + name="PymysqlTestDataclassFunctions::list_converted_by_tags", + depends=["PymysqlTestDataclassFunctions::get_converted_not_found"], + ) + def test_list_converted_by_tags(self, pymysql_conn: pymysql.Connection) -> None: + # The parameter converts through encode_tags before hitting the db. + result = queries_converters.list_converted_by_tags(conn=pymysql_conn, tags=frozenset({"alpha", "beta"})) + + assert isinstance(result, queries_converters.QueryResults) + assert result() == [CONVERTER_ID] + assert list(result) == [CONVERTER_ID] + + @pytest.mark.dependency(depends=["PymysqlTestDataclassFunctions::list_converted_by_tags"]) + def test_delete_converted(self, pymysql_conn: pymysql.Connection) -> None: + queries_converters.delete_converted(conn=pymysql_conn, id_=CONVERTER_ID) + queries_converters.delete_converted(conn=pymysql_conn, id_=CONVERTER_ID_2) + + assert queries_converters.get_converted(conn=pymysql_conn, id_=CONVERTER_ID) is None + assert queries_converters.get_converted(conn=pymysql_conn, id_=CONVERTER_ID_2) is None + + def test_one_missing_rows_return_none(self, pymysql_conn: pymysql.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. The + # count queries always return a row, so their miss branch needs the + # no-row stub. + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=-1) is None + assert queries.get_one_date(conn=pymysql_conn, id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert queries.get_one_datetime(conn=pymysql_conn, id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert queries.get_one_time(conn=pymysql_conn, id_=-1, time_test=datetime.timedelta()) is None + assert queries.get_one_bool(conn=pymysql_conn, id_=-1, tinyint1_test=False) is None + assert queries.get_one_decimal(conn=pymysql_conn, id_=-1, decimal_test=decimal.Decimal(0)) is None + assert queries.get_one_blob(conn=pymysql_conn, id_=-1, blob_test=memoryview(b"")) is None + assert queries.get_one_bit(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_year(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_json(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_mood(conn=pymysql_conn, id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert queries.get_one_tag(conn=pymysql_conn, id_=-1) is None + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=-1) is None + assert queries.get_type_override(conn=pymysql_conn, id_=-1) is None + assert queries.get_reserved_arg(conn=pymysql_conn, conn_2="missing") is None + assert queries.touch_exec_last_id(conn=pymysql_conn, name="untouched", id_=-1) is None + assert queries_case.get_case_row(conn=pymysql_conn, id_=-1) is None + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=-1) is None + assert queries_field_namings.get_joined_field_namings(conn=pymysql_conn, id_=-1) is None + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=-1) is None + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=-1) is None + assert queries_enum_override.count_enum_override_by_moods(conn=pymysql_conn, moods=[]) == 0 + + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert queries.count_mysql_types(conn=stub) is None + assert queries_case.count_case_rows(conn=stub, id_=0) is None + assert queries_enum_override.count_enum_override_by_moods(conn=stub, moods=[]) is None + + @pytest.mark.dependency(depends=["PymysqlTestDataclassFunctions::insert_enum_override"]) + def test_enum_override_cleanup(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_enum_override WHERE id IN (%s, %s)", (ENUM_OVERRIDE_ID, ENUM_OVERRIDE_ID_2)) + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=ENUM_OVERRIDE_ID) is None diff --git a/test/driver_pymysql/dbtype/__init__.py b/test/driver_pymysql/dbtype/__init__.py new file mode 100644 index 00000000..09b4d663 --- /dev/null +++ b/test/driver_pymysql/dbtype/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for pymysql db_type override tests.""" diff --git a/test/driver_pymysql/dbtype/functions/__init__.py b/test/driver_pymysql/dbtype/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/dbtype/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/dbtype/functions/models.py b/test/driver_pymysql/dbtype/functions/models.py new file mode 100644 index 00000000..45e6db10 --- /dev/null +++ b/test/driver_pymysql/dbtype/functions/models.py @@ -0,0 +1,28 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("TestDbtypeOverride",) + +import dataclasses +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +@dataclasses.dataclass() +class TestDbtypeOverride: + """Model representing TestDbtypeOverride. + + Attributes: + id_: int + happened_at: str + """ + + id_: int + happened_at: str diff --git a/test/driver_pymysql/dbtype/functions/queries_dbtype_override.py b/test/driver_pymysql/dbtype/functions/queries_dbtype_override.py new file mode 100644 index 00000000..d18b75d4 --- /dev/null +++ b/test/driver_pymysql/dbtype/functions/queries_dbtype_override.py @@ -0,0 +1,71 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_dbtype_override.sql +"""Module containing queries from file queries_dbtype_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "get_dbtype_override", + "insert_dbtype_override", +) + +import test.converters +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.dbtype.functions import models + + +INSERT_DBTYPE_OVERRIDE: typing.Final[str] = """-- name: InsertDbtypeOverride :exec +INSERT INTO test_dbtype_override (id, happened_at) VALUES (%s, %s) +""" + +GET_DBTYPE_OVERRIDE: typing.Final[str] = """-- name: GetDbtypeOverride :one +SELECT id, happened_at FROM test_dbtype_override WHERE id = %s +""" + + +def insert_dbtype_override(conn: pymysql.Connection, *, id_: int, happened_at: str) -> None: + """Execute SQL query with `name: InsertDbtypeOverride :exec`. + + ```sql + INSERT INTO test_dbtype_override (id, happened_at) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + happened_at: str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_DBTYPE_OVERRIDE, (id_, test.converters.encode_stamp(happened_at))) + + +def get_dbtype_override(conn: pymysql.Connection, *, id_: int) -> models.TestDbtypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetDbtypeOverride :one`. + + ```sql + SELECT id, happened_at FROM test_dbtype_override WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestDbtypeOverride` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_DBTYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestDbtypeOverride(id_=row[0], happened_at=test.converters.decode_stamp(row[1])) diff --git a/test/driver_pymysql/dbtype/ruff.toml b/test/driver_pymysql/dbtype/ruff.toml new file mode 100644 index 00000000..f37cac11 --- /dev/null +++ b/test/driver_pymysql/dbtype/ruff.toml @@ -0,0 +1,5 @@ +extend="../../../ruff.toml" + + +[lint.pydocstyle] +convention = "google" \ No newline at end of file diff --git a/test/driver_pymysql/dbtype/test_pymysql_dbtype_override.py b/test/driver_pymysql/dbtype/test_pymysql_dbtype_override.py new file mode 100644 index 00000000..c5807d40 --- /dev/null +++ b/test/driver_pymysql/dbtype/test_pymysql_dbtype_override.py @@ -0,0 +1,64 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Runtime tests for the DATETIME db_type override of the pymysql driver.""" + +from __future__ import annotations + +import datetime +import typing + +import pytest + +from test.driver_pymysql.dbtype.functions import models +from test.driver_pymysql.dbtype.functions import queries_dbtype_override + +if typing.TYPE_CHECKING: + import pymysql + +DBTYPE_ID: typing.Final = 5100 +STAMP: typing.Final = "2026-01-02T03:04:05" + + +class TestPymysqlDbtypeOverride: + @pytest.mark.dependency(name="PymysqlDbtypeOverride::insert") + def test_insert_dbtype_override(self, pymysql_conn: pymysql.Connection) -> None: + # The str parameter converts to a datetime through the stamp + # converter before binding. + queries_dbtype_override.insert_dbtype_override(conn=pymysql_conn, id_=DBTYPE_ID, happened_at=STAMP) + + @pytest.mark.dependency(name="PymysqlDbtypeOverride::get", depends=["PymysqlDbtypeOverride::insert"]) + def test_get_dbtype_override(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_dbtype_override.get_dbtype_override(conn=pymysql_conn, id_=DBTYPE_ID) + + assert result is not None + assert result == models.TestDbtypeOverride(id_=DBTYPE_ID, happened_at=STAMP) + assert isinstance(result.happened_at, str) + # The database stored a real datetime, not the string. + with pymysql_conn.cursor() as cur: + cur.execute("SELECT happened_at FROM test_dbtype_override WHERE id = %s", (DBTYPE_ID,)) + row = cur.fetchone() + assert row is not None + assert row[0] == datetime.datetime.fromisoformat(STAMP) + + @pytest.mark.dependency(depends=["PymysqlDbtypeOverride::get"]) + def test_delete_dbtype_override(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_dbtype_override WHERE id = %s", (DBTYPE_ID,)) + assert queries_dbtype_override.get_dbtype_override(conn=pymysql_conn, id_=DBTYPE_ID) is None diff --git a/test/driver_pymysql/msgspec/__init__.py b/test/driver_pymysql/msgspec/__init__.py new file mode 100644 index 00000000..11a9bca5 --- /dev/null +++ b/test/driver_pymysql/msgspec/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for pymysql tests.""" diff --git a/test/driver_pymysql/msgspec/classes/__init__.py b/test/driver_pymysql/msgspec/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/msgspec/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/msgspec/classes/enums.py b/test/driver_pymysql/msgspec/classes/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_pymysql/msgspec/classes/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_pymysql/msgspec/classes/models.py b/test/driver_pymysql/msgspec/classes/models.py new file mode 100644 index 00000000..f37fc6f8 --- /dev/null +++ b/test/driver_pymysql/msgspec/classes/models.py @@ -0,0 +1,295 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestEnumOverride", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +import msgspec +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_pymysql.msgspec.classes import enums + import collections.abc + import datetime + import decimal + + +class Model3RdPartyStat(msgspec.Struct): + """Model representing Model3RdPartyStat. + + Attributes: + id_ -- int + total -- int + """ + + id_: int + total: int + + +class TestCaseSensitivity(msgspec.Struct): + """Model representing TestCaseSensitivity. + + Attributes: + id_ -- int + upper_dt -- datetime.datetime + prec_dec -- decimal.Decimal + """ + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +class TestEnumOverride(msgspec.Struct): + """Model representing TestEnumOverride. + + Attributes: + id_ -- int + mood_test -- str + """ + + id_: int + mood_test: str + + +class TestFieldNaming(msgspec.Struct): + """Model representing TestFieldNaming. + + Attributes: + id_ -- int + outputs -- str + """ + + id_: int + outputs: str + + +class TestInnerMysqlType(msgspec.Struct): + """Model representing TestInnerMysqlType. + + Attributes: + table_id -- int + int_test -- int | None + integer_test -- int | None + mediumint_test -- int | None + smallint_test -- int | None + tinyint_test -- int | None + bigint_test -- int | None + int_unsigned_test -- int | None + bigint_unsigned_test -- int | None + year_test -- int | None + tinyint1_test -- bool | None + bool_test -- bool | None + boolean_test -- bool | None + float_test -- float | None + double_test -- float | None + double_precision_test -- float | None + real_test -- float | None + decimal_test -- decimal.Decimal | None + numeric_test -- decimal.Decimal | None + char_test -- str | None + varchar_test -- str | None + tinytext_test -- str | None + text_test -- str | None + mediumtext_test -- str | None + longtext_test -- str | None + binary_test -- memoryview | None + varbinary_test -- memoryview | None + tinyblob_test -- memoryview | None + blob_test -- memoryview | None + mediumblob_test -- memoryview | None + longblob_test -- memoryview | None + bit_test -- memoryview | None + date_test -- datetime.date | None + datetime_test -- datetime.datetime | None + datetime6_test -- datetime.datetime | None + timestamp_test -- datetime.datetime | None + time_test -- datetime.timedelta | None + json_test -- str | None + mood -- enums.TestInnerMysqlTypesMood | None + tag -- enums.TestInnerMysqlTypesTag | None + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +class TestInvalidIdentifier(msgspec.Struct): + """Model representing TestInvalidIdentifier. + + Attributes: + id_ -- int + column_3p_ -- str | None + new_notes -- str + column__pct -- str | None + """ + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +class TestMysqlType(msgspec.Struct): + """Model representing TestMysqlType. + + Attributes: + id_ -- int + int_test -- int + integer_test -- int + mediumint_test -- int + smallint_test -- int + tinyint_test -- int + bigint_test -- int + int_unsigned_test -- int + bigint_unsigned_test -- int + year_test -- int + tinyint1_test -- bool + bool_test -- bool + boolean_test -- bool + float_test -- float + double_test -- float + double_precision_test -- float + real_test -- float + decimal_test -- decimal.Decimal + numeric_test -- decimal.Decimal + char_test -- str + varchar_test -- str + tinytext_test -- str + text_test -- str + mediumtext_test -- str + longtext_test -- str + binary_test -- memoryview + varbinary_test -- memoryview + tinyblob_test -- memoryview + blob_test -- memoryview + mediumblob_test -- memoryview + longblob_test -- memoryview + bit_test -- memoryview + date_test -- datetime.date + datetime_test -- datetime.datetime + datetime6_test -- datetime.datetime + timestamp_test -- datetime.datetime + time_test -- datetime.timedelta + json_test -- str + mood -- enums.TestMysqlTypesMood + tag -- enums.TestMysqlTypesTag + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +class TestReservedArg(msgspec.Struct): + """Model representing TestReservedArg. + + Attributes: + id_ -- int + conn -- str + """ + + id_: int + conn: str + + +class TestTypeOverride(msgspec.Struct): + """Model representing TestTypeOverride. + + Attributes: + id_ -- int + text_test -- UserString | None + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_pymysql/msgspec/classes/queries.py b/test/driver_pymysql/msgspec/classes/queries.py new file mode 100644 index 00000000..256a924d --- /dev/null +++ b/test/driver_pymysql/msgspec/classes/queries.py @@ -0,0 +1,1427 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Queries", + "QueryResults", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.msgspec.classes import enums +from test.driver_pymysql.msgspec.classes import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Arguments: + conn -- The connection object of type `pymysql.Connection` used to execute queries. + sql -- The SQL statement that will be executed when fetching/iterating. + decode_hook -- A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args -- Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration -- When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class Queries: + """Queries from file queries.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute queries. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + pymysql.Connection -- Connection object used to make queries. + """ + return self._conn + + def insert_one_mysql_type( + self, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, + ) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Arguments: + id_ -- int. + int_test -- int. + integer_test -- int. + mediumint_test -- int. + smallint_test -- int. + tinyint_test -- int. + bigint_test -- int. + int_unsigned_test -- int. + bigint_unsigned_test -- int. + year_test -- int. + tinyint1_test -- bool. + bool_test -- bool. + boolean_test -- bool. + float_test -- float. + double_test -- float. + double_precision_test -- float. + real_test -- float. + decimal_test -- decimal.Decimal. + numeric_test -- decimal.Decimal. + char_test -- str. + varchar_test -- str. + tinytext_test -- str. + text_test -- str. + mediumtext_test -- str. + longtext_test -- str. + binary_test -- memoryview. + varbinary_test -- memoryview. + tinyblob_test -- memoryview. + blob_test -- memoryview. + mediumblob_test -- memoryview. + longblob_test -- memoryview. + bit_test -- memoryview. + date_test -- datetime.date. + datetime_test -- datetime.datetime. + datetime6_test -- datetime.datetime. + timestamp_test -- datetime.datetime. + time_test -- datetime.timedelta. + json_test -- str. + mood -- enums.TestMysqlTypesMood. + tag -- enums.TestMysqlTypesTag. + """ + with self._conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + def insert_one_inner_mysql_type( + self, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, + ) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Arguments: + table_id -- int. + int_test -- int | None. + integer_test -- int | None. + mediumint_test -- int | None. + smallint_test -- int | None. + tinyint_test -- int | None. + bigint_test -- int | None. + int_unsigned_test -- int | None. + bigint_unsigned_test -- int | None. + year_test -- int | None. + tinyint1_test -- bool | None. + bool_test -- bool | None. + boolean_test -- bool | None. + float_test -- float | None. + double_test -- float | None. + double_precision_test -- float | None. + real_test -- float | None. + decimal_test -- decimal.Decimal | None. + numeric_test -- decimal.Decimal | None. + char_test -- str | None. + varchar_test -- str | None. + tinytext_test -- str | None. + text_test -- str | None. + mediumtext_test -- str | None. + longtext_test -- str | None. + binary_test -- memoryview | None. + varbinary_test -- memoryview | None. + tinyblob_test -- memoryview | None. + blob_test -- memoryview | None. + mediumblob_test -- memoryview | None. + longblob_test -- memoryview | None. + bit_test -- memoryview | None. + date_test -- datetime.date | None. + datetime_test -- datetime.datetime | None. + datetime6_test -- datetime.datetime | None. + timestamp_test -- datetime.datetime | None. + time_test -- datetime.timedelta | None. + json_test -- str | None. + mood -- enums.TestInnerMysqlTypesMood | None. + tag -- enums.TestInnerMysqlTypesTag | None. + """ + with self._conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + def get_one_mysql_type(self, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + models.TestMysqlType -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + def get_one_inner_mysql_type(self, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Arguments: + table_id -- int. + + Returns: + models.TestInnerMysqlType -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + def get_many_mysql_type(self, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + QueryResults[models.TestMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(self._conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + def get_many_inner_mysql_type(self, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Arguments: + table_id -- int. + + Returns: + QueryResults[models.TestInnerMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + def get_many_nullable_inner_mysql_type(self, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Arguments: + table_id -- int. + int_test -- int | None. + + Returns: + QueryResults[models.TestInnerMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + def get_one_date(self, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Arguments: + id_ -- int. + date_test -- datetime.date. + + Returns: + datetime.date -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DATE, (id_, date_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_datetime(self, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Arguments: + id_ -- int. + datetime_test -- datetime.datetime. + + Returns: + datetime.datetime -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_time(self, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Arguments: + id_ -- int. + time_test -- datetime.timedelta. + + Returns: + datetime.timedelta -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_TIME, (id_, time_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_bool(self, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Arguments: + id_ -- int. + tinyint1_test -- bool. + + Returns: + bool -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = cur.fetchone() + if row is None: + return None + return bool(row[0]) + + def get_one_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Arguments: + id_ -- int. + decimal_test -- decimal.Decimal. + + Returns: + decimal.Decimal -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_blob(self, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Arguments: + id_ -- int. + blob_test -- memoryview. + + Returns: + memoryview -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + def get_one_bit(self, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + memoryview -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BIT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + def get_one_year(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_YEAR, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_json(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_JSON, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_mood(self, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Arguments: + id_ -- int. + mood -- enums.TestMysqlTypesMood. + + Returns: + enums.TestMysqlTypesMood -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_MOOD, (id_, mood)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + def get_one_tag(self, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + enums.TestMysqlTypesTag -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_TAG, (id_,)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + def get_many_date(self, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Arguments: + id_ -- int. + date_test -- datetime.date. + + Returns: + QueryResults[datetime.date] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + def get_many_time(self, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Arguments: + id_ -- int. + time_test -- datetime.timedelta. + + Returns: + QueryResults[datetime.timedelta] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + def get_many_bool(self, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Arguments: + id_ -- int. + tinyint1_test -- bool. + + Returns: + QueryResults[bool] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(self._conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + def get_many_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Arguments: + id_ -- int. + decimal_test -- decimal.Decimal. + + Returns: + QueryResults[decimal.Decimal] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + def get_many_mood(self, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Arguments: + mood -- enums.TestMysqlTypesMood. + + Returns: + QueryResults[enums.TestMysqlTypesMood] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(self._conn, GET_MANY_MOOD, _decode_hook, mood) + + def list_months(self) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Returns: + QueryResults[str] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, LIST_MONTHS, operator.itemgetter(0)) + + def count_mysql_types(self) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(COUNT_MYSQL_TYPES) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def update_varchar_test(self, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Arguments: + varchar_test -- str. + id_ -- int. + + Returns: + int -- The number of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + with self._conn.cursor() as cur: + return cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + def delete_one_mysql_type(self, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + id_ -- int. + """ + with self._conn.cursor() as cur: + cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + def all_mysql_types_cursor(self) -> pymysql.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Returns: + pymysql.cursors.Cursor -- The result returned when executing the query. + """ + cur = self._conn.cursor() + cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + def insert_exec_last_id(self, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Arguments: + name -- str. + + Returns: + int -- The id of the last affected row. Will be `None` if no rows are affected. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + def get_exec_last_id_name(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def insert_type_override(self, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Arguments: + id_ -- int. + text_test -- UserString | None. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + def get_type_override(self, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + models.TestTypeOverride -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + def get_reserved_arg(self, *, conn: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Arguments: + conn -- str. + + Returns: + models.TestReservedArg -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_RESERVED_ARG, (conn,)) + row = cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + def insert_reserved_arg(self, *, id_: int, conn: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Arguments: + id_ -- int. + conn -- str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_RESERVED_ARG, (id_, conn)) + + def touch_exec_last_id(self, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Arguments: + name -- str. + id_ -- int. + + Returns: + int -- The id of the last affected row. Will be `None` if no rows are affected. + """ + with self._conn.cursor() as cur: + cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_pymysql/msgspec/classes/queries_case.py b/test/driver_pymysql/msgspec/classes/queries_case.py new file mode 100644 index 00000000..f6dc0345 --- /dev/null +++ b/test/driver_pymysql/msgspec/classes/queries_case.py @@ -0,0 +1,111 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_case.sql +"""Module containing queries from file queries_case.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("QueriesCase",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + +from test.driver_pymysql.msgspec.classes import models + + +INSERT_CASE_ROW: typing.Final[str] = """-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) +""" + +GET_CASE_ROW: typing.Final[str] = """-- name: GetCaseRow :one +SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s +""" + +COUNT_CASE_ROWS: typing.Final[str] = """-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ +""" + + +class QueriesCase: + """Queries from file queries_case.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute queries. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + pymysql.Connection -- Connection object used to make queries. + """ + return self._conn + + def insert_case_row(self, *, id_: int, upper_dt: datetime.datetime, prec_dec: decimal.Decimal) -> None: + """Execute SQL query with `name: InsertCaseRow :exec`. + + ```sql + INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) + ``` + + Arguments: + id_ -- int. + upper_dt -- datetime.datetime. + prec_dec -- decimal.Decimal. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_CASE_ROW, (id_, upper_dt, prec_dec)) + + def get_case_row(self, *, id_: int) -> models.TestCaseSensitivity | None: + """Fetch one from the db using the SQL query with `name: GetCaseRow :one`. + + ```sql + SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + models.TestCaseSensitivity -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_CASE_ROW, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestCaseSensitivity(id_=row[0], upper_dt=row[1], prec_dec=row[2]) + + def count_case_rows(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: CountCaseRows :one`. + + ```sql + SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ + ``` + + Arguments: + id_ -- int. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(COUNT_CASE_ROWS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/msgspec/classes/queries_enum_override.py b/test/driver_pymysql/msgspec/classes/queries_enum_override.py new file mode 100644 index 00000000..6b3845f1 --- /dev/null +++ b/test/driver_pymysql/msgspec/classes/queries_enum_override.py @@ -0,0 +1,207 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueriesEnumOverride", + "QueryResults", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.msgspec.classes import enums +from test.driver_pymysql.msgspec.classes import models + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Arguments: + conn -- The connection object of type `pymysql.Connection` used to execute queries. + sql -- The SQL statement that will be executed when fetching/iterating. + decode_hook -- A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args -- Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration -- When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class QueriesEnumOverride: + """Queries from file queries_enum_override.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute queries. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + pymysql.Connection -- Connection object used to make queries. + """ + return self._conn + + def insert_enum_override(self, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Arguments: + id_ -- int. + mood_test -- str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + def get_enum_override_mood(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + def list_enum_override_by_ids(self, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Arguments: + ids -- collections.abc.Sequence[int]. + + Returns: + QueryResults[models.TestEnumOverride] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(self._conn, sql, _decode_hook, *ids) + + def count_enum_override_by_moods(self, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Arguments: + moods -- collections.abc.Sequence[str]. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with self._conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/msgspec/classes/queries_field_namings.py b/test/driver_pymysql/msgspec/classes/queries_field_namings.py new file mode 100644 index 00000000..f897b596 --- /dev/null +++ b/test/driver_pymysql/msgspec/classes/queries_field_namings.py @@ -0,0 +1,138 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_field_namings.sql +"""Module containing queries from file queries_field_namings.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "GetJoinedFieldNamingsRow", + "QueriesFieldNamings", +) + +import msgspec +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.msgspec.classes import models + + +class GetJoinedFieldNamingsRow(msgspec.Struct): + """Model representing GetJoinedFieldNamingsRow. + + Attributes: + outputs -- str + outputs_2 -- str + """ + + outputs: str + outputs_2: str + + +GET_FIELD_NAMING: typing.Final[str] = """-- name: GetFieldNaming :one +SELECT id, outputs +FROM test_field_namings +WHERE id = %s LIMIT 1 +""" + +GET_JOINED_FIELD_NAMINGS: typing.Final[str] = """-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = %s LIMIT 1 +""" + +SET_FIELD_NAMING_OUTPUTS: typing.Final[str] = """-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = %s +WHERE id = %s +""" + + +class QueriesFieldNamings: + """Queries from file queries_field_namings.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute queries. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + pymysql.Connection -- Connection object used to make queries. + """ + return self._conn + + def get_field_naming(self, *, id_: int) -> models.TestFieldNaming | None: + """Fetch one from the db using the SQL query with `name: GetFieldNaming :one`. + + ```sql + SELECT id, outputs + FROM test_field_namings + WHERE id = %s LIMIT 1 + ``` + + Arguments: + id_ -- int. + + Returns: + models.TestFieldNaming -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_FIELD_NAMING, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestFieldNaming(id_=row[0], outputs=row[1]) + + def get_joined_field_namings(self, *, id_: int) -> GetJoinedFieldNamingsRow | None: + """Fetch one from the db using the SQL query with `name: GetJoinedFieldNamings :one`. + + ```sql + SELECT a.outputs, b.outputs + FROM test_field_namings a + JOIN test_field_namings b ON a.id = b.id + WHERE a.id = %s LIMIT 1 + ``` + + Arguments: + id_ -- int. + + Returns: + GetJoinedFieldNamingsRow -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_JOINED_FIELD_NAMINGS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return GetJoinedFieldNamingsRow(outputs=row[0], outputs_2=row[1]) + + def set_field_naming_outputs(self, *, outputs: str, id_: int) -> None: + """Execute SQL query with `name: SetFieldNamingOutputs :exec`. + + ```sql + UPDATE test_field_namings + SET outputs = %s + WHERE id = %s + ``` + + Arguments: + outputs -- str. + id_ -- int. + """ + with self._conn.cursor() as cur: + cur.execute(SET_FIELD_NAMING_OUTPUTS, (outputs, id_)) diff --git a/test/driver_pymysql/msgspec/classes/queries_invalid_identifiers.py b/test/driver_pymysql/msgspec/classes/queries_invalid_identifiers.py new file mode 100644 index 00000000..fe4d893a --- /dev/null +++ b/test/driver_pymysql/msgspec/classes/queries_invalid_identifiers.py @@ -0,0 +1,127 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_invalid_identifiers.sql +"""Module containing queries from file queries_invalid_identifiers.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("QueriesInvalidIdentifiers",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.msgspec.classes import models + + +INSERT_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) +""" + +GET_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: GetInvalidIdentifiers :one +SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s +""" + +INSERT_THIRD_PARTY_STAT: typing.Final[str] = """-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) +""" + +GET_THIRD_PARTY_STAT: typing.Final[str] = """-- name: GetThirdPartyStat :one +SELECT id, total FROM `3rd_party_stats` WHERE id = %s +""" + + +class QueriesInvalidIdentifiers: + """Queries from file queries_invalid_identifiers.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute queries. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + pymysql.Connection -- Connection object used to make queries. + """ + return self._conn + + def insert_invalid_identifiers(self, *, id_: int, column_3p_: str | None, new_notes: str) -> None: + """Execute SQL query with `name: InsertInvalidIdentifiers :exec`. + + ```sql + INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) + ``` + + Arguments: + id_ -- int. + column_3p_ -- str | None. + new_notes -- str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_INVALID_IDENTIFIERS, (id_, column_3p_, new_notes)) + + def get_invalid_identifiers(self, *, id_: int) -> models.TestInvalidIdentifier | None: + """Fetch one from the db using the SQL query with `name: GetInvalidIdentifiers :one`. + + ```sql + SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + models.TestInvalidIdentifier -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_INVALID_IDENTIFIERS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInvalidIdentifier(id_=row[0], column_3p_=row[1], new_notes=row[2], column__pct=row[3]) + + def insert_third_party_stat(self, *, id_: int, total: int) -> None: + """Execute SQL query with `name: InsertThirdPartyStat :exec`. + + ```sql + INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) + ``` + + Arguments: + id_ -- int. + total -- int. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_THIRD_PARTY_STAT, (id_, total)) + + def get_third_party_stat(self, *, id_: int) -> models.Model3RdPartyStat | None: + """Fetch one from the db using the SQL query with `name: GetThirdPartyStat :one`. + + ```sql + SELECT id, total FROM `3rd_party_stats` WHERE id = %s + ``` + + Arguments: + id_ -- int. + + Returns: + models.Model3RdPartyStat -- Result fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_THIRD_PARTY_STAT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.Model3RdPartyStat(id_=row[0], total=row[1]) diff --git a/test/driver_pymysql/msgspec/functions/__init__.py b/test/driver_pymysql/msgspec/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/msgspec/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/msgspec/functions/enums.py b/test/driver_pymysql/msgspec/functions/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_pymysql/msgspec/functions/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_pymysql/msgspec/functions/models.py b/test/driver_pymysql/msgspec/functions/models.py new file mode 100644 index 00000000..d73753f9 --- /dev/null +++ b/test/driver_pymysql/msgspec/functions/models.py @@ -0,0 +1,295 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestEnumOverride", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +import msgspec +import typing + +if typing.TYPE_CHECKING: + from collections import UserString + from test.driver_pymysql.msgspec.functions import enums + import collections.abc + import datetime + import decimal + + +class Model3RdPartyStat(msgspec.Struct): + """Model representing Model3RdPartyStat. + + Attributes: + id_ -- int + total -- int + """ + + id_: int + total: int + + +class TestCaseSensitivity(msgspec.Struct): + """Model representing TestCaseSensitivity. + + Attributes: + id_ -- int + upper_dt -- datetime.datetime + prec_dec -- decimal.Decimal + """ + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +class TestEnumOverride(msgspec.Struct): + """Model representing TestEnumOverride. + + Attributes: + id_ -- int + mood_test -- str + """ + + id_: int + mood_test: str + + +class TestFieldNaming(msgspec.Struct): + """Model representing TestFieldNaming. + + Attributes: + id_ -- int + outputs -- str + """ + + id_: int + outputs: str + + +class TestInnerMysqlType(msgspec.Struct): + """Model representing TestInnerMysqlType. + + Attributes: + table_id -- int + int_test -- int | None + integer_test -- int | None + mediumint_test -- int | None + smallint_test -- int | None + tinyint_test -- int | None + bigint_test -- int | None + int_unsigned_test -- int | None + bigint_unsigned_test -- int | None + year_test -- int | None + tinyint1_test -- bool | None + bool_test -- bool | None + boolean_test -- bool | None + float_test -- float | None + double_test -- float | None + double_precision_test -- float | None + real_test -- float | None + decimal_test -- decimal.Decimal | None + numeric_test -- decimal.Decimal | None + char_test -- str | None + varchar_test -- str | None + tinytext_test -- str | None + text_test -- str | None + mediumtext_test -- str | None + longtext_test -- str | None + binary_test -- memoryview | None + varbinary_test -- memoryview | None + tinyblob_test -- memoryview | None + blob_test -- memoryview | None + mediumblob_test -- memoryview | None + longblob_test -- memoryview | None + bit_test -- memoryview | None + date_test -- datetime.date | None + datetime_test -- datetime.datetime | None + datetime6_test -- datetime.datetime | None + timestamp_test -- datetime.datetime | None + time_test -- datetime.timedelta | None + json_test -- str | None + mood -- enums.TestInnerMysqlTypesMood | None + tag -- enums.TestInnerMysqlTypesTag | None + """ + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +class TestInvalidIdentifier(msgspec.Struct): + """Model representing TestInvalidIdentifier. + + Attributes: + id_ -- int + column_3p_ -- str | None + new_notes -- str + column__pct -- str | None + """ + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +class TestMysqlType(msgspec.Struct): + """Model representing TestMysqlType. + + Attributes: + id_ -- int + int_test -- int + integer_test -- int + mediumint_test -- int + smallint_test -- int + tinyint_test -- int + bigint_test -- int + int_unsigned_test -- int + bigint_unsigned_test -- int + year_test -- int + tinyint1_test -- bool + bool_test -- bool + boolean_test -- bool + float_test -- float + double_test -- float + double_precision_test -- float + real_test -- float + decimal_test -- decimal.Decimal + numeric_test -- decimal.Decimal + char_test -- str + varchar_test -- str + tinytext_test -- str + text_test -- str + mediumtext_test -- str + longtext_test -- str + binary_test -- memoryview + varbinary_test -- memoryview + tinyblob_test -- memoryview + blob_test -- memoryview + mediumblob_test -- memoryview + longblob_test -- memoryview + bit_test -- memoryview + date_test -- datetime.date + datetime_test -- datetime.datetime + datetime6_test -- datetime.datetime + timestamp_test -- datetime.datetime + time_test -- datetime.timedelta + json_test -- str + mood -- enums.TestMysqlTypesMood + tag -- enums.TestMysqlTypesTag + """ + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +class TestReservedArg(msgspec.Struct): + """Model representing TestReservedArg. + + Attributes: + id_ -- int + conn -- str + """ + + id_: int + conn: str + + +class TestTypeOverride(msgspec.Struct): + """Model representing TestTypeOverride. + + Attributes: + id_ -- int + text_test -- UserString | None + """ + + id_: int + text_test: UserString | None diff --git a/test/driver_pymysql/msgspec/functions/queries.py b/test/driver_pymysql/msgspec/functions/queries.py new file mode 100644 index 00000000..117c12ad --- /dev/null +++ b/test/driver_pymysql/msgspec/functions/queries.py @@ -0,0 +1,1514 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "all_mysql_types_cursor", + "count_mysql_types", + "delete_one_mysql_type", + "get_exec_last_id_name", + "get_many_bool", + "get_many_date", + "get_many_decimal", + "get_many_inner_mysql_type", + "get_many_mood", + "get_many_mysql_type", + "get_many_nullable_inner_mysql_type", + "get_many_time", + "get_one_bit", + "get_one_blob", + "get_one_bool", + "get_one_date", + "get_one_datetime", + "get_one_decimal", + "get_one_inner_mysql_type", + "get_one_json", + "get_one_mood", + "get_one_mysql_type", + "get_one_tag", + "get_one_time", + "get_one_year", + "get_reserved_arg", + "get_type_override", + "insert_exec_last_id", + "insert_one_inner_mysql_type", + "insert_one_mysql_type", + "insert_reserved_arg", + "insert_type_override", + "list_months", + "touch_exec_last_id", + "update_varchar_test", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.msgspec.functions import enums +from test.driver_pymysql.msgspec.functions import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Arguments: + conn -- The connection object of type `pymysql.Connection` used to execute queries. + sql -- The SQL statement that will be executed when fetching/iterating. + decode_hook -- A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args -- Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration -- When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_one_mysql_type( + conn: pymysql.Connection, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, +) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + int_test -- int. + integer_test -- int. + mediumint_test -- int. + smallint_test -- int. + tinyint_test -- int. + bigint_test -- int. + int_unsigned_test -- int. + bigint_unsigned_test -- int. + year_test -- int. + tinyint1_test -- bool. + bool_test -- bool. + boolean_test -- bool. + float_test -- float. + double_test -- float. + double_precision_test -- float. + real_test -- float. + decimal_test -- decimal.Decimal. + numeric_test -- decimal.Decimal. + char_test -- str. + varchar_test -- str. + tinytext_test -- str. + text_test -- str. + mediumtext_test -- str. + longtext_test -- str. + binary_test -- memoryview. + varbinary_test -- memoryview. + tinyblob_test -- memoryview. + blob_test -- memoryview. + mediumblob_test -- memoryview. + longblob_test -- memoryview. + bit_test -- memoryview. + date_test -- datetime.date. + datetime_test -- datetime.datetime. + datetime6_test -- datetime.datetime. + timestamp_test -- datetime.datetime. + time_test -- datetime.timedelta. + json_test -- str. + mood -- enums.TestMysqlTypesMood. + tag -- enums.TestMysqlTypesTag. + """ + with conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + +def insert_one_inner_mysql_type( + conn: pymysql.Connection, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, +) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + table_id -- int. + int_test -- int | None. + integer_test -- int | None. + mediumint_test -- int | None. + smallint_test -- int | None. + tinyint_test -- int | None. + bigint_test -- int | None. + int_unsigned_test -- int | None. + bigint_unsigned_test -- int | None. + year_test -- int | None. + tinyint1_test -- bool | None. + bool_test -- bool | None. + boolean_test -- bool | None. + float_test -- float | None. + double_test -- float | None. + double_precision_test -- float | None. + real_test -- float | None. + decimal_test -- decimal.Decimal | None. + numeric_test -- decimal.Decimal | None. + char_test -- str | None. + varchar_test -- str | None. + tinytext_test -- str | None. + text_test -- str | None. + mediumtext_test -- str | None. + longtext_test -- str | None. + binary_test -- memoryview | None. + varbinary_test -- memoryview | None. + tinyblob_test -- memoryview | None. + blob_test -- memoryview | None. + mediumblob_test -- memoryview | None. + longblob_test -- memoryview | None. + bit_test -- memoryview | None. + date_test -- datetime.date | None. + datetime_test -- datetime.datetime | None. + datetime6_test -- datetime.datetime | None. + timestamp_test -- datetime.datetime | None. + time_test -- datetime.timedelta | None. + json_test -- str | None. + mood -- enums.TestInnerMysqlTypesMood | None. + tag -- enums.TestInnerMysqlTypesTag | None. + """ + with conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + +def get_one_mysql_type(conn: pymysql.Connection, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + models.TestMysqlType -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + +def get_one_inner_mysql_type(conn: pymysql.Connection, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + table_id -- int. + + Returns: + models.TestInnerMysqlType -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + +def get_many_mysql_type(conn: pymysql.Connection, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + QueryResults[models.TestMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + +def get_many_inner_mysql_type(conn: pymysql.Connection, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + table_id -- int. + + Returns: + QueryResults[models.TestInnerMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + +def get_many_nullable_inner_mysql_type(conn: pymysql.Connection, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + table_id -- int. + int_test -- int | None. + + Returns: + QueryResults[models.TestInnerMysqlType] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + +def get_one_date(conn: pymysql.Connection, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + date_test -- datetime.date. + + Returns: + datetime.date -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DATE, (id_, date_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_datetime(conn: pymysql.Connection, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + datetime_test -- datetime.datetime. + + Returns: + datetime.datetime -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_time(conn: pymysql.Connection, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + time_test -- datetime.timedelta. + + Returns: + datetime.timedelta -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_TIME, (id_, time_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_bool(conn: pymysql.Connection, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + tinyint1_test -- bool. + + Returns: + bool -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = cur.fetchone() + if row is None: + return None + return bool(row[0]) + + +def get_one_decimal(conn: pymysql.Connection, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + decimal_test -- decimal.Decimal. + + Returns: + decimal.Decimal -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_blob(conn: pymysql.Connection, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + blob_test -- memoryview. + + Returns: + memoryview -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +def get_one_bit(conn: pymysql.Connection, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + memoryview -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BIT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +def get_one_year(conn: pymysql.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_YEAR, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_json(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_JSON, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_mood(conn: pymysql.Connection, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + mood -- enums.TestMysqlTypesMood. + + Returns: + enums.TestMysqlTypesMood -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_MOOD, (id_, mood)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + +def get_one_tag(conn: pymysql.Connection, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + enums.TestMysqlTypesTag -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_TAG, (id_,)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + +def get_many_date(conn: pymysql.Connection, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + date_test -- datetime.date. + + Returns: + QueryResults[datetime.date] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + +def get_many_time(conn: pymysql.Connection, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + time_test -- datetime.timedelta. + + Returns: + QueryResults[datetime.timedelta] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + +def get_many_bool(conn: pymysql.Connection, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + tinyint1_test -- bool. + + Returns: + QueryResults[bool] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + +def get_many_decimal(conn: pymysql.Connection, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + decimal_test -- decimal.Decimal. + + Returns: + QueryResults[decimal.Decimal] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + +def get_many_mood(conn: pymysql.Connection, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + mood -- enums.TestMysqlTypesMood. + + Returns: + QueryResults[enums.TestMysqlTypesMood] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(conn, GET_MANY_MOOD, _decode_hook, mood) + + +def list_months(conn: pymysql.Connection) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + QueryResults[str] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, LIST_MONTHS, operator.itemgetter(0)) + + +def count_mysql_types(conn: pymysql.Connection) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(COUNT_MYSQL_TYPES) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def update_varchar_test(conn: pymysql.Connection, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + varchar_test -- str. + id_ -- int. + + Returns: + int -- The number of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + with conn.cursor() as cur: + return cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + +def delete_one_mysql_type(conn: pymysql.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + """ + with conn.cursor() as cur: + cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + +def all_mysql_types_cursor(conn: pymysql.Connection) -> pymysql.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + pymysql.cursors.Cursor -- The result returned when executing the query. + """ + cur = conn.cursor() + cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + +def insert_exec_last_id(conn: pymysql.Connection, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + name -- str. + + Returns: + int -- The id of the last affected row. Will be `None` if no rows are affected. + """ + with conn.cursor() as cur: + cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + +def get_exec_last_id_name(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def insert_type_override(conn: pymysql.Connection, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + text_test -- UserString | None. + """ + with conn.cursor() as cur: + cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + +def get_type_override(conn: pymysql.Connection, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + models.TestTypeOverride -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + +def get_reserved_arg(conn: pymysql.Connection, *, conn_2: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + conn_2 -- str. + + Returns: + models.TestReservedArg -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_RESERVED_ARG, (conn_2,)) + row = cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + +def insert_reserved_arg(conn: pymysql.Connection, *, id_: int, conn_2: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + conn_2 -- str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_RESERVED_ARG, (id_, conn_2)) + + +def touch_exec_last_id(conn: pymysql.Connection, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + name -- str. + id_ -- int. + + Returns: + int -- The id of the last affected row. Will be `None` if no rows are affected. + """ + with conn.cursor() as cur: + cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_pymysql/msgspec/functions/queries_case.py b/test/driver_pymysql/msgspec/functions/queries_case.py new file mode 100644 index 00000000..8ae25979 --- /dev/null +++ b/test/driver_pymysql/msgspec/functions/queries_case.py @@ -0,0 +1,98 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_case.sql +"""Module containing queries from file queries_case.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "count_case_rows", + "get_case_row", + "insert_case_row", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + +from test.driver_pymysql.msgspec.functions import models + + +INSERT_CASE_ROW: typing.Final[str] = """-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) +""" + +GET_CASE_ROW: typing.Final[str] = """-- name: GetCaseRow :one +SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s +""" + +COUNT_CASE_ROWS: typing.Final[str] = """-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ +""" + + +def insert_case_row(conn: pymysql.Connection, *, id_: int, upper_dt: datetime.datetime, prec_dec: decimal.Decimal) -> None: + """Execute SQL query with `name: InsertCaseRow :exec`. + + ```sql + INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + upper_dt -- datetime.datetime. + prec_dec -- decimal.Decimal. + """ + with conn.cursor() as cur: + cur.execute(INSERT_CASE_ROW, (id_, upper_dt, prec_dec)) + + +def get_case_row(conn: pymysql.Connection, *, id_: int) -> models.TestCaseSensitivity | None: + """Fetch one from the db using the SQL query with `name: GetCaseRow :one`. + + ```sql + SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + models.TestCaseSensitivity -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_CASE_ROW, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestCaseSensitivity(id_=row[0], upper_dt=row[1], prec_dec=row[2]) + + +def count_case_rows(conn: pymysql.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: CountCaseRows :one`. + + ```sql + SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(COUNT_CASE_ROWS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/msgspec/functions/queries_enum_override.py b/test/driver_pymysql/msgspec/functions/queries_enum_override.py new file mode 100644 index 00000000..7d31f337 --- /dev/null +++ b/test/driver_pymysql/msgspec/functions/queries_enum_override.py @@ -0,0 +1,195 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "count_enum_override_by_moods", + "get_enum_override_mood", + "insert_enum_override", + "list_enum_override_by_ids", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.msgspec.functions import enums +from test.driver_pymysql.msgspec.functions import models + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Arguments: + conn -- The connection object of type `pymysql.Connection` used to execute queries. + sql -- The SQL statement that will be executed when fetching/iterating. + decode_hook -- A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args -- Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration -- When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_enum_override(conn: pymysql.Connection, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + mood_test -- str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + +def get_enum_override_mood(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + str -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + +def list_enum_override_by_ids(conn: pymysql.Connection, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + ids -- collections.abc.Sequence[int]. + + Returns: + QueryResults[models.TestEnumOverride] -- Helper class that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *ids) + + +def count_enum_override_by_moods(conn: pymysql.Connection, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + moods -- collections.abc.Sequence[str]. + + Returns: + int -- Result fetched from the db. Will be `None` if not found. + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/msgspec/functions/queries_field_namings.py b/test/driver_pymysql/msgspec/functions/queries_field_namings.py new file mode 100644 index 00000000..65f0f28d --- /dev/null +++ b/test/driver_pymysql/msgspec/functions/queries_field_namings.py @@ -0,0 +1,123 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_field_namings.sql +"""Module containing queries from file queries_field_namings.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "GetJoinedFieldNamingsRow", + "get_field_naming", + "get_joined_field_namings", + "set_field_naming_outputs", +) + +import msgspec +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.msgspec.functions import models + + +class GetJoinedFieldNamingsRow(msgspec.Struct): + """Model representing GetJoinedFieldNamingsRow. + + Attributes: + outputs -- str + outputs_2 -- str + """ + + outputs: str + outputs_2: str + + +GET_FIELD_NAMING: typing.Final[str] = """-- name: GetFieldNaming :one +SELECT id, outputs +FROM test_field_namings +WHERE id = %s LIMIT 1 +""" + +GET_JOINED_FIELD_NAMINGS: typing.Final[str] = """-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = %s LIMIT 1 +""" + +SET_FIELD_NAMING_OUTPUTS: typing.Final[str] = """-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = %s +WHERE id = %s +""" + + +def get_field_naming(conn: pymysql.Connection, *, id_: int) -> models.TestFieldNaming | None: + """Fetch one from the db using the SQL query with `name: GetFieldNaming :one`. + + ```sql + SELECT id, outputs + FROM test_field_namings + WHERE id = %s LIMIT 1 + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + models.TestFieldNaming -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_FIELD_NAMING, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestFieldNaming(id_=row[0], outputs=row[1]) + + +def get_joined_field_namings(conn: pymysql.Connection, *, id_: int) -> GetJoinedFieldNamingsRow | None: + """Fetch one from the db using the SQL query with `name: GetJoinedFieldNamings :one`. + + ```sql + SELECT a.outputs, b.outputs + FROM test_field_namings a + JOIN test_field_namings b ON a.id = b.id + WHERE a.id = %s LIMIT 1 + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + GetJoinedFieldNamingsRow -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_JOINED_FIELD_NAMINGS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return GetJoinedFieldNamingsRow(outputs=row[0], outputs_2=row[1]) + + +def set_field_naming_outputs(conn: pymysql.Connection, *, outputs: str, id_: int) -> None: + """Execute SQL query with `name: SetFieldNamingOutputs :exec`. + + ```sql + UPDATE test_field_namings + SET outputs = %s + WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + outputs -- str. + id_ -- int. + """ + with conn.cursor() as cur: + cur.execute(SET_FIELD_NAMING_OUTPUTS, (outputs, id_)) diff --git a/test/driver_pymysql/msgspec/functions/queries_invalid_identifiers.py b/test/driver_pymysql/msgspec/functions/queries_invalid_identifiers.py new file mode 100644 index 00000000..a843cea2 --- /dev/null +++ b/test/driver_pymysql/msgspec/functions/queries_invalid_identifiers.py @@ -0,0 +1,117 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_invalid_identifiers.sql +"""Module containing queries from file queries_invalid_identifiers.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "get_invalid_identifiers", + "get_third_party_stat", + "insert_invalid_identifiers", + "insert_third_party_stat", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.msgspec.functions import models + + +INSERT_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) +""" + +GET_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: GetInvalidIdentifiers :one +SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s +""" + +INSERT_THIRD_PARTY_STAT: typing.Final[str] = """-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) +""" + +GET_THIRD_PARTY_STAT: typing.Final[str] = """-- name: GetThirdPartyStat :one +SELECT id, total FROM `3rd_party_stats` WHERE id = %s +""" + + +def insert_invalid_identifiers(conn: pymysql.Connection, *, id_: int, column_3p_: str | None, new_notes: str) -> None: + """Execute SQL query with `name: InsertInvalidIdentifiers :exec`. + + ```sql + INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + column_3p_ -- str | None. + new_notes -- str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_INVALID_IDENTIFIERS, (id_, column_3p_, new_notes)) + + +def get_invalid_identifiers(conn: pymysql.Connection, *, id_: int) -> models.TestInvalidIdentifier | None: + """Fetch one from the db using the SQL query with `name: GetInvalidIdentifiers :one`. + + ```sql + SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + models.TestInvalidIdentifier -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_INVALID_IDENTIFIERS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInvalidIdentifier(id_=row[0], column_3p_=row[1], new_notes=row[2], column__pct=row[3]) + + +def insert_third_party_stat(conn: pymysql.Connection, *, id_: int, total: int) -> None: + """Execute SQL query with `name: InsertThirdPartyStat :exec`. + + ```sql + INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + total -- int. + """ + with conn.cursor() as cur: + cur.execute(INSERT_THIRD_PARTY_STAT, (id_, total)) + + +def get_third_party_stat(conn: pymysql.Connection, *, id_: int) -> models.Model3RdPartyStat | None: + """Fetch one from the db using the SQL query with `name: GetThirdPartyStat :one`. + + ```sql + SELECT id, total FROM `3rd_party_stats` WHERE id = %s + ``` + + Arguments: + conn -- Connection object of type `pymysql.Connection` used to execute the query. + id_ -- int. + + Returns: + models.Model3RdPartyStat -- Result fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_THIRD_PARTY_STAT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.Model3RdPartyStat(id_=row[0], total=row[1]) diff --git a/test/driver_pymysql/msgspec/ruff.toml b/test/driver_pymysql/msgspec/ruff.toml new file mode 100644 index 00000000..876ccf0c --- /dev/null +++ b/test/driver_pymysql/msgspec/ruff.toml @@ -0,0 +1,5 @@ +extend="../../../ruff.toml" + + +[lint.pydocstyle] +convention = "pep257" \ No newline at end of file diff --git a/test/driver_pymysql/msgspec/test_pymysql_msgspec_classes.py b/test/driver_pymysql/msgspec/test_pymysql_msgspec_classes.py new file mode 100644 index 00000000..0d0a70ed --- /dev/null +++ b/test/driver_pymysql/msgspec/test_pymysql_msgspec_classes.py @@ -0,0 +1,769 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import msgspec +import pymysql +import pytest + +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.msgspec.classes import enums +from test.driver_pymysql.msgspec.classes import models +from test.driver_pymysql.msgspec.classes import queries +from test.driver_pymysql.msgspec.classes import queries_case +from test.driver_pymysql.msgspec.classes import queries_enum_override +from test.driver_pymysql.msgspec.classes import queries_field_namings +from test.driver_pymysql.msgspec.classes import queries_invalid_identifiers + +# Ids fixed and unique across the pymysql suites (msgspec owns 3000-3999); +# every chain deletes its rows at the end so reruns start clean. +TYPE_ID: typing.Final = 3000 +OVERRIDE_ID: typing.Final = 3010 +OVERRIDE_NONE_ID: typing.Final = 3011 +RESERVED_ID: typing.Final = 3020 +CASE_IDS: typing.Final = (3030, 3031) +ENUM_IDS: typing.Final = (3040, 3041) +FIELD_ID: typing.Final = 3050 +INVALID_ID: typing.Final = 3060 +THIRD_PARTY_ID: typing.Final = 3061 +MISSING_ID: typing.Final = 3999 +RESERVED_CONN: typing.Final = "msgspec-classes-conn" +EXEC_LAST_ID_NAME: typing.Final = "msgspec-classes-lastid" +CASE_DT: typing.Final = datetime.datetime(2026, 7, 19, 8, 15) +CASE_DEC: typing.Final = decimal.Decimal("12.34") + + +def _without_json(row: models.TestMysqlType) -> models.TestMysqlType: + # MySQL normalizes JSON spacing, so json_test never compares as a string. + return msgspec.structs.replace(row, json_test="") + + +class TestPymysqlMsgspecClasses: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=TYPE_ID, + int_test=42, + integer_test=-42, + mediumint_test=8_388_607, + smallint_test=-32_768, + tinyint_test=-128, + bigint_test=9_223_372_036_854_775_807, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 11, + year_test=2026, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.pi, + double_precision_test=math.e, + real_test=1.5, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("99.99"), + char_test="ABCDEFGHIJ", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"0123456789abcdef"), + varbinary_test=memoryview(b"\x00\x01varbinary"), + tinyblob_test=memoryview(b"tinyblob"), + blob_test=memoryview(b"\x00\x01\x02hello"), + mediumblob_test=memoryview(b"mediumblob"), + longblob_test=memoryview(b"longblob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 15), + datetime_test=datetime.datetime(2026, 1, 15, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 15, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + time_test=datetime.timedelta(hours=1, minutes=2, seconds=3), + json_test=json.dumps({"foo": "bar"}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=7, + mediumint_test=None, + smallint_test=3, + tinyint_test=127, + bigint_test=None, + int_unsigned_test=None, + bigint_unsigned_test=2**63 + 42, + year_test=1901, + tinyint1_test=False, + bool_test=None, + boolean_test=True, + float_test=None, + double_test=0.25, + double_precision_test=None, + real_test=None, + decimal_test=decimal.Decimal("0.5000"), + numeric_test=None, + char_test=None, + varchar_test="inner varchar", + tinytext_test=None, + text_test=None, + mediumtext_test=None, + longtext_test=None, + binary_test=None, + varbinary_test=memoryview(b"inner"), + tinyblob_test=None, + blob_test=None, + mediumblob_test=None, + longblob_test=None, + bit_test=memoryview(b"\x01"), + date_test=None, + datetime_test=None, + datetime6_test=None, + timestamp_test=None, + time_test=datetime.timedelta(hours=8, minutes=30), + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=None, + ) + + @pytest.fixture(scope="session") + def queries_obj(self, pymysql_conn: pymysql.Connection) -> queries.Queries: + return queries.Queries(conn=pymysql_conn) + + @pytest.fixture(scope="session") + def case_obj(self, pymysql_conn: pymysql.Connection) -> queries_case.QueriesCase: + return queries_case.QueriesCase(conn=pymysql_conn) + + @pytest.fixture(scope="session") + def enum_obj(self, pymysql_conn: pymysql.Connection) -> queries_enum_override.QueriesEnumOverride: + return queries_enum_override.QueriesEnumOverride(conn=pymysql_conn) + + @pytest.fixture(scope="session") + def field_obj(self, pymysql_conn: pymysql.Connection) -> queries_field_namings.QueriesFieldNamings: + return queries_field_namings.QueriesFieldNamings(conn=pymysql_conn) + + @pytest.fixture(scope="session") + def invalid_obj(self, pymysql_conn: pymysql.Connection) -> queries_invalid_identifiers.QueriesInvalidIdentifiers: + return queries_invalid_identifiers.QueriesInvalidIdentifiers(conn=pymysql_conn) + + def test_conn_attr(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + assert isinstance(queries_obj.conn, pymysql.Connection) + assert queries_obj.conn is pymysql_conn + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::insert") + def test_insert(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + queries_obj.insert_one_mysql_type( + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::inner_insert", depends=["PymysqlMsgspecClasses::insert"]) + def test_inner_insert(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + queries_obj.insert_one_inner_mysql_type( + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_one", depends=["PymysqlMsgspecClasses::inner_insert"]) + def test_get_one(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_mysql_type(id_=TYPE_ID) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + assert json.loads(result.json_test) == json.loads(model.json_test) + assert _without_json(result) == _without_json(model) + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + # plain datetime drops microseconds, datetime(6) keeps them + assert result.datetime_test.microsecond == 0 + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + assert result.bigint_unsigned_test == 2**63 + 11 + assert result.mood is enums.TestMysqlTypesMood.VALUE_24H + assert result.tag is enums.TestMysqlTypesTag.BETA + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_one_none", depends=["PymysqlMsgspecClasses::get_one"]) + def test_get_one_none(self, queries_obj: queries.Queries) -> None: + assert queries_obj.get_one_mysql_type(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_one_inner", depends=["PymysqlMsgspecClasses::get_one_none"]) + def test_get_one_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = queries_obj.get_one_inner_mysql_type(table_id=TYPE_ID) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + assert result.tinyint1_test is False + assert result.boolean_test is True + assert result.json_test is None + assert result.mood is enums.TestInnerMysqlTypesMood.VALUE__HIDDEN + assert result.tag is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_one_inner_none", depends=["PymysqlMsgspecClasses::get_one_inner"]) + def test_get_one_inner_none(self, queries_obj: queries.Queries) -> None: + assert queries_obj.get_one_inner_mysql_type(table_id=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_date", depends=["PymysqlMsgspecClasses::get_one_inner_none"]) + def test_get_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_date(id_=TYPE_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + assert queries_obj.get_one_date(id_=MISSING_ID, date_test=model.date_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_datetime", depends=["PymysqlMsgspecClasses::get_date"]) + def test_get_datetime(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_datetime(id_=TYPE_ID, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + assert queries_obj.get_one_datetime(id_=MISSING_ID, datetime_test=model.datetime_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_time", depends=["PymysqlMsgspecClasses::get_datetime"]) + def test_get_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_time(id_=TYPE_ID, time_test=model.time_test) + + assert result is not None + # MySQL time maps to timedelta, not datetime.time + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + assert queries_obj.get_one_time(id_=MISSING_ID, time_test=model.time_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_bool", depends=["PymysqlMsgspecClasses::get_time"]) + def test_get_bool(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_bool(id_=TYPE_ID, tinyint1_test=True) + + assert result is True + assert queries_obj.get_one_bool(id_=MISSING_ID, tinyint1_test=True) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_decimal", depends=["PymysqlMsgspecClasses::get_bool"]) + def test_get_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_decimal(id_=TYPE_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + # decimal(12,4) comes back padded to scale 4 + assert result == decimal.Decimal("12.3400") + assert str(result) == "12.3400" + assert queries_obj.get_one_decimal(id_=MISSING_ID, decimal_test=model.decimal_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_blob", depends=["PymysqlMsgspecClasses::get_decimal"]) + def test_get_blob(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_blob(id_=TYPE_ID, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + assert queries_obj.get_one_blob(id_=MISSING_ID, blob_test=model.blob_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_bit", depends=["PymysqlMsgspecClasses::get_blob"]) + def test_get_bit(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_bit(id_=TYPE_ID) + + assert result is not None + # bit(8) comes back as a single byte + assert isinstance(result, memoryview) + assert bytes(result) == b"\x80" + assert queries_obj.get_one_bit(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_year", depends=["PymysqlMsgspecClasses::get_bit"]) + def test_get_year(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_year(id_=TYPE_ID) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + assert queries_obj.get_one_year(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_json", depends=["PymysqlMsgspecClasses::get_year"]) + def test_get_json(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_json(id_=TYPE_ID) + + assert result is not None + assert isinstance(result, str) + assert json.loads(result) == {"foo": "bar"} + assert queries_obj.get_one_json(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_mood", depends=["PymysqlMsgspecClasses::get_json"]) + def test_get_mood(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_mood(id_=TYPE_ID, mood=enums.TestMysqlTypesMood.VALUE_24H) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + assert result == "24h" + assert queries_obj.get_one_mood(id_=MISSING_ID, mood=enums.TestMysqlTypesMood.VALUE_24H) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_tag", depends=["PymysqlMsgspecClasses::get_mood"]) + def test_get_tag(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_tag(id_=TYPE_ID) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.BETA + assert queries_obj.get_one_tag(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many", depends=["PymysqlMsgspecClasses::get_tag"]) + def test_get_many(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mysql_type(id_=TYPE_ID) + + assert isinstance(result, queries.QueryResults) + results = result() + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + assert _without_json(results[0]) == _without_json(model) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many_iter", depends=["PymysqlMsgspecClasses::get_many"]) + def test_get_many_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_mysql_type(id_=TYPE_ID): + assert isinstance(result, models.TestMysqlType) + assert _without_json(result) == _without_json(model) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many_inner", depends=["PymysqlMsgspecClasses::get_many_iter"]) + def test_get_many_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = queries_obj.get_many_inner_mysql_type(table_id=TYPE_ID) + + assert isinstance(result, queries.QueryResults) + results = result() + assert list(results) == [inner_model] + for row in queries_obj.get_many_inner_mysql_type(table_id=TYPE_ID): + assert isinstance(row, models.TestInnerMysqlType) + assert row == inner_model + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many_nullable_inner", depends=["PymysqlMsgspecClasses::get_many_inner"]) + def test_get_many_nullable_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + # int_test is compared with <=>, so None matches the NULL row. + result = queries_obj.get_many_nullable_inner_mysql_type(table_id=TYPE_ID, int_test=None) + + results = result() + assert list(results) == [inner_model] + assert list(queries_obj.get_many_nullable_inner_mysql_type(table_id=TYPE_ID, int_test=0)()) == [] + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many_date", depends=["PymysqlMsgspecClasses::get_many_nullable_inner"]) + def test_get_many_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_date(id_=TYPE_ID, date_test=model.date_test) + + assert isinstance(result, queries.QueryResults) + assert list(result()) == [model.date_test] + assert list(queries_obj.get_many_date(id_=TYPE_ID, date_test=model.date_test)) == [model.date_test] + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many_time", depends=["PymysqlMsgspecClasses::get_many_date"]) + def test_get_many_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_time(id_=TYPE_ID, time_test=model.time_test) + + assert list(result()) == [model.time_test] + assert list(queries_obj.get_many_time(id_=TYPE_ID, time_test=model.time_test)) == [model.time_test] + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many_bool", depends=["PymysqlMsgspecClasses::get_many_time"]) + def test_get_many_bool(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_many_bool(id_=TYPE_ID, tinyint1_test=True) + + results = result() + assert len(results) == 1 + assert results[0] is True + for row in queries_obj.get_many_bool(id_=TYPE_ID, tinyint1_test=True): + assert row is True + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many_decimal", depends=["PymysqlMsgspecClasses::get_many_bool"]) + def test_get_many_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_decimal(id_=TYPE_ID, decimal_test=model.decimal_test) + + assert list(result()) == [decimal.Decimal("12.3400")] + for row in queries_obj.get_many_decimal(id_=TYPE_ID, decimal_test=model.decimal_test): + assert str(row) == "12.3400" + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_many_mood", depends=["PymysqlMsgspecClasses::get_many_decimal"]) + def test_get_many_mood(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_many_mood(mood=enums.TestMysqlTypesMood.VALUE_24H) + + assert list(result()) == [enums.TestMysqlTypesMood.VALUE_24H] + assert list(queries_obj.get_many_mood(mood=enums.TestMysqlTypesMood.VALUE_24H)) == [enums.TestMysqlTypesMood.VALUE_24H] + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::list_months", depends=["PymysqlMsgspecClasses::get_many_mood"]) + def test_list_months(self, queries_obj: queries.Queries) -> None: + # Regression for the percent-doubling bug: the parameterless :many + # query contains literal % signs in DATE_FORMAT. + result = queries_obj.list_months() + + assert list(result()) == ["2026-01"] + assert list(queries_obj.list_months()) == ["2026-01"] + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::count", depends=["PymysqlMsgspecClasses::list_months"]) + def test_count(self, queries_obj: queries.Queries) -> None: + # The shared table may carry other files' rows; only a lower bound is safe. + count = queries_obj.count_mysql_types() + assert count is not None + assert count >= 1 + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::update_varchar", depends=["PymysqlMsgspecClasses::count"]) + def test_update_varchar(self, queries_obj: queries.Queries) -> None: + result = queries_obj.update_varchar_test(varchar_test="updated varchar", id_=TYPE_ID) + + assert isinstance(result, int) + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + assert queries_obj.update_varchar_test(varchar_test="updated varchar", id_=MISSING_ID) == 0 + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::all_cursor", depends=["PymysqlMsgspecClasses::update_varchar"]) + def test_all_cursor(self, queries_obj: queries.Queries) -> None: + cursor = queries_obj.all_mysql_types_cursor() + + assert isinstance(cursor, pymysql.cursors.Cursor) + rows = cursor.fetchall() + cursor.close() + # The shared table may carry other files' rows; assert on our own. + assert TYPE_ID in {row[0] for row in rows} + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::delete", depends=["PymysqlMsgspecClasses::all_cursor"]) + def test_delete(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + queries_obj.delete_one_mysql_type(id_=TYPE_ID) + + assert queries_obj.get_one_mysql_type(id_=TYPE_ID) is None + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_inner_mysql_types WHERE table_id = %s", (TYPE_ID,)) + assert queries_obj.get_one_inner_mysql_type(table_id=TYPE_ID) is None + + def test_exec_last_id(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + # The AUTO_INCREMENT counter persists across runs, so only > 0 holds. + last_id = queries_obj.insert_exec_last_id(name=EXEC_LAST_ID_NAME) + + assert isinstance(last_id, int) + assert last_id > 0 + assert queries_obj.get_exec_last_id_name(id_=last_id) == EXEC_LAST_ID_NAME + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_execlastid WHERE id = %s", (last_id,)) + assert queries_obj.get_exec_last_id_name(id_=last_id) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::insert_type_override") + def test_insert_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + queries_obj.insert_type_override(id_=override_model.id_, text_test=override_model.text_test) + queries_obj.insert_type_override(id_=OVERRIDE_NONE_ID, text_test=None) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::get_type_override", depends=["PymysqlMsgspecClasses::insert_type_override"]) + def test_get_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = queries_obj.get_type_override(id_=OVERRIDE_ID) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + none_result = queries_obj.get_type_override(id_=OVERRIDE_NONE_ID) + assert none_result is not None + assert none_result.text_test is None + assert queries_obj.get_type_override(id_=MISSING_ID) is None + + @pytest.mark.dependency(depends=["PymysqlMsgspecClasses::get_type_override"]) + def test_delete_type_override(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_type_override WHERE id IN (%s, %s)", (OVERRIDE_ID, OVERRIDE_NONE_ID)) + assert queries_obj.get_type_override(id_=OVERRIDE_ID) is None + + def test_reserved_arg(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + queries_obj.insert_reserved_arg(id_=RESERVED_ID, conn=RESERVED_CONN) + + result = queries_obj.get_reserved_arg(conn=RESERVED_CONN) + assert result == models.TestReservedArg(id_=RESERVED_ID, conn=RESERVED_CONN) + assert queries_obj.get_reserved_arg(conn="msgspec-classes-missing") is None + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_reserved_args WHERE id = %s", (RESERVED_ID,)) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::case_insert") + def test_case_insert(self, case_obj: queries_case.QueriesCase) -> None: + case_obj.insert_case_row(id_=CASE_IDS[0], upper_dt=CASE_DT, prec_dec=CASE_DEC) + case_obj.insert_case_row(id_=CASE_IDS[1], upper_dt=CASE_DT, prec_dec=CASE_DEC) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::case_get", depends=["PymysqlMsgspecClasses::case_insert"]) + def test_case_get(self, case_obj: queries_case.QueriesCase) -> None: + result = case_obj.get_case_row(id_=CASE_IDS[0]) + + assert result is not None + assert result == models.TestCaseSensitivity(id_=CASE_IDS[0], upper_dt=CASE_DT, prec_dec=CASE_DEC) + assert case_obj.get_case_row(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::case_count", depends=["PymysqlMsgspecClasses::case_get"]) + def test_case_count(self, case_obj: queries_case.QueriesCase) -> None: + # The WHERE clause lives inside an executable /*! version comment; if + # MySQL ignored it both counts would be 2. + # Range-scoped asserts: the shared table may carry other files' rows, + # so counts outside [CASE_IDS[0], CASE_IDS[1]] must cancel out. + beyond = case_obj.count_case_rows(id_=CASE_IDS[1] + 1) + high = case_obj.count_case_rows(id_=CASE_IDS[1]) + low = case_obj.count_case_rows(id_=CASE_IDS[0]) + assert beyond is not None + assert high is not None + assert low is not None + assert high - beyond == 1 + assert low - beyond == len(CASE_IDS) + + @pytest.mark.dependency(depends=["PymysqlMsgspecClasses::case_count"]) + def test_case_delete(self, case_obj: queries_case.QueriesCase, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_case_sensitivity WHERE id IN (%s, %s)", CASE_IDS) + beyond = case_obj.count_case_rows(id_=CASE_IDS[1] + 1) + low = case_obj.count_case_rows(id_=CASE_IDS[0]) + assert beyond is not None + assert low is not None + assert low - beyond == 0 + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::enum_insert") + def test_enum_override_insert(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + # The overridden parameter is a plain str; the generated code converts + # it back through enums.TestEnumOverrideMoodTest. + enum_obj.insert_enum_override(id_=ENUM_IDS[0], mood_test="happy") + enum_obj.insert_enum_override(id_=ENUM_IDS[1], mood_test="sad") + with pytest.raises(ValueError, match="angry"): + enum_obj.insert_enum_override(id_=MISSING_ID, mood_test="angry") + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::enum_get", depends=["PymysqlMsgspecClasses::enum_insert"]) + def test_enum_override_get(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + mood = enum_obj.get_enum_override_mood(id_=ENUM_IDS[0]) + + assert mood is not None + assert isinstance(mood, str) + assert mood == "happy" + assert enum_obj.get_enum_override_mood(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::enum_list", depends=["PymysqlMsgspecClasses::enum_insert"]) + def test_enum_override_list(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + rows = enum_obj.list_enum_override_by_ids(ids=list(ENUM_IDS))() + + assert all(isinstance(row, models.TestEnumOverride) for row in rows) + assert {row.id_: row.mood_test for row in rows} == {ENUM_IDS[0]: "happy", ENUM_IDS[1]: "sad"} + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::enum_iterate", depends=["PymysqlMsgspecClasses::enum_insert"]) + def test_enum_override_iterate(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + seen: dict[int, str] = {} + for row in enum_obj.list_enum_override_by_ids(ids=list(ENUM_IDS)): + assert isinstance(row, models.TestEnumOverride) + seen[row.id_] = row.mood_test + assert seen == {ENUM_IDS[0]: "happy", ENUM_IDS[1]: "sad"} + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::enum_empty", depends=["PymysqlMsgspecClasses::enum_insert"]) + def test_enum_override_empty_slice(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + assert list(enum_obj.list_enum_override_by_ids(ids=[])()) == [] + assert list(enum_obj.list_enum_override_by_ids(ids=[])) == [] + + @pytest.mark.dependency(depends=["PymysqlMsgspecClasses::enum_empty"]) + def test_enum_override_delete(self, enum_obj: queries_enum_override.QueriesEnumOverride, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_enum_override WHERE id IN (%s, %s)", ENUM_IDS) + assert enum_obj.get_enum_override_mood(id_=ENUM_IDS[0]) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::field_insert") + def test_field_naming_insert(self, pymysql_conn: pymysql.Connection) -> None: + # There is no generated insert for this table. + with pymysql_conn.cursor() as cur: + cur.execute("INSERT INTO test_field_namings (id, outputs) VALUES (%s, %s)", (FIELD_ID, json.dumps(["first", "second"]))) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::field_get", depends=["PymysqlMsgspecClasses::field_insert"]) + def test_field_naming_get(self, field_obj: queries_field_namings.QueriesFieldNamings) -> None: + result = field_obj.get_field_naming(id_=FIELD_ID) + + assert result is not None + assert isinstance(result, models.TestFieldNaming) + assert result.id_ == FIELD_ID + assert json.loads(result.outputs) == ["first", "second"] + assert field_obj.get_field_naming(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::field_joined", depends=["PymysqlMsgspecClasses::field_get"]) + def test_field_naming_joined(self, field_obj: queries_field_namings.QueriesFieldNamings) -> None: + result = field_obj.get_joined_field_namings(id_=FIELD_ID) + + assert result is not None + assert isinstance(result, queries_field_namings.GetJoinedFieldNamingsRow) + assert json.loads(result.outputs) == ["first", "second"] + assert json.loads(result.outputs_2) == ["first", "second"] + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::field_set", depends=["PymysqlMsgspecClasses::field_joined"]) + def test_field_naming_set(self, field_obj: queries_field_namings.QueriesFieldNamings) -> None: + field_obj.set_field_naming_outputs(outputs=json.dumps({"count": 2}), id_=FIELD_ID) + + result = field_obj.get_field_naming(id_=FIELD_ID) + assert result is not None + assert json.loads(result.outputs) == {"count": 2} + + @pytest.mark.dependency(depends=["PymysqlMsgspecClasses::field_set"]) + def test_field_naming_delete(self, field_obj: queries_field_namings.QueriesFieldNamings, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_field_namings WHERE id = %s", (FIELD_ID,)) + assert field_obj.get_field_naming(id_=FIELD_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::invalid_insert") + def test_invalid_identifiers_insert(self, invalid_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + invalid_obj.insert_invalid_identifiers(id_=INVALID_ID, column_3p_="3p value", new_notes="note value") + invalid_obj.insert_third_party_stat(id_=THIRD_PARTY_ID, total=987) + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::invalid_get", depends=["PymysqlMsgspecClasses::invalid_insert"]) + def test_invalid_identifiers_get(self, invalid_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + result = invalid_obj.get_invalid_identifiers(id_=INVALID_ID) + + assert result == models.TestInvalidIdentifier(id_=INVALID_ID, column_3p_="3p value", new_notes="note value", column__pct=None) + assert invalid_obj.get_invalid_identifiers(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecClasses::third_party_get", depends=["PymysqlMsgspecClasses::invalid_insert"]) + def test_third_party_stat_get(self, invalid_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + result = invalid_obj.get_third_party_stat(id_=THIRD_PARTY_ID) + + assert result == models.Model3RdPartyStat(id_=THIRD_PARTY_ID, total=987) + assert invalid_obj.get_third_party_stat(id_=MISSING_ID) is None + + @pytest.mark.dependency(depends=["PymysqlMsgspecClasses::invalid_get", "PymysqlMsgspecClasses::third_party_get"]) + def test_invalid_identifiers_delete(self, invalid_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_invalid_identifiers WHERE id = %s", (INVALID_ID,)) + cur.execute("DELETE FROM `3rd_party_stats` WHERE id = %s", (THIRD_PARTY_ID,)) + assert invalid_obj.get_invalid_identifiers(id_=INVALID_ID) is None + assert invalid_obj.get_third_party_stat(id_=THIRD_PARTY_ID) is None + + def test_one_missing_rows_return_none(self, pymysql_conn: pymysql.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. The + # count queries always return a row, so their miss branch needs the + # no-row stub; the sub-module Querier conn properties ride along. + obj = queries.Queries(conn=pymysql_conn) + assert obj.get_one_mysql_type(id_=-1) is None + assert obj.get_one_inner_mysql_type(table_id=-1) is None + assert obj.get_one_date(id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert obj.get_one_datetime(id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert obj.get_one_time(id_=-1, time_test=datetime.timedelta()) is None + assert obj.get_one_bool(id_=-1, tinyint1_test=False) is None + assert obj.get_one_decimal(id_=-1, decimal_test=decimal.Decimal(0)) is None + assert obj.get_one_blob(id_=-1, blob_test=memoryview(b"")) is None + assert obj.get_one_bit(id_=-1) is None + assert obj.get_one_year(id_=-1) is None + assert obj.get_one_json(id_=-1) is None + assert obj.get_one_mood(id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert obj.get_one_tag(id_=-1) is None + assert obj.get_exec_last_id_name(id_=-1) is None + assert obj.get_type_override(id_=-1) is None + assert obj.get_reserved_arg(conn="missing") is None + assert obj.touch_exec_last_id(name="untouched", id_=-1) is None + + case_obj = queries_case.QueriesCase(conn=pymysql_conn) + naming_obj = queries_field_namings.QueriesFieldNamings(conn=pymysql_conn) + invalid_obj = queries_invalid_identifiers.QueriesInvalidIdentifiers(conn=pymysql_conn) + enum_obj = queries_enum_override.QueriesEnumOverride(conn=pymysql_conn) + assert case_obj.conn is pymysql_conn + assert naming_obj.conn is pymysql_conn + assert invalid_obj.conn is pymysql_conn + assert enum_obj.conn is pymysql_conn + assert case_obj.get_case_row(id_=-1) is None + assert naming_obj.get_field_naming(id_=-1) is None + assert naming_obj.get_joined_field_namings(id_=-1) is None + assert invalid_obj.get_invalid_identifiers(id_=-1) is None + assert enum_obj.get_enum_override_mood(id_=-1) is None + assert enum_obj.count_enum_override_by_moods(moods=[]) == 0 + + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert queries.Queries(conn=stub).count_mysql_types() is None + assert queries_case.QueriesCase(conn=stub).count_case_rows(id_=0) is None + assert queries_enum_override.QueriesEnumOverride(conn=stub).count_enum_override_by_moods(moods=[]) is None diff --git a/test/driver_pymysql/msgspec/test_pymysql_msgspec_functions.py b/test/driver_pymysql/msgspec/test_pymysql_msgspec_functions.py new file mode 100644 index 00000000..bf0785c8 --- /dev/null +++ b/test/driver_pymysql/msgspec/test_pymysql_msgspec_functions.py @@ -0,0 +1,739 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import msgspec +import pymysql +import pytest + +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.msgspec.functions import enums +from test.driver_pymysql.msgspec.functions import models +from test.driver_pymysql.msgspec.functions import queries +from test.driver_pymysql.msgspec.functions import queries_case +from test.driver_pymysql.msgspec.functions import queries_enum_override +from test.driver_pymysql.msgspec.functions import queries_field_namings +from test.driver_pymysql.msgspec.functions import queries_invalid_identifiers + +# Ids fixed and unique across the pymysql suites (msgspec owns 3000-3999, +# this file uses the 35xx block); every chain deletes its rows at the end. +TYPE_ID: typing.Final = 3500 +OVERRIDE_ID: typing.Final = 3510 +OVERRIDE_NONE_ID: typing.Final = 3511 +RESERVED_ID: typing.Final = 3520 +CASE_IDS: typing.Final = (3530, 3531) +ENUM_IDS: typing.Final = (3540, 3541) +FIELD_ID: typing.Final = 3550 +INVALID_ID: typing.Final = 3560 +THIRD_PARTY_ID: typing.Final = 3561 +MISSING_ID: typing.Final = 3999 +RESERVED_CONN: typing.Final = "msgspec-functions-conn" +EXEC_LAST_ID_NAME: typing.Final = "msgspec-functions-lastid" +CASE_DT: typing.Final = datetime.datetime(2026, 7, 19, 8, 15) +CASE_DEC: typing.Final = decimal.Decimal("12.34") + + +def _without_json(row: models.TestMysqlType) -> models.TestMysqlType: + # MySQL normalizes JSON spacing, so json_test never compares as a string. + return msgspec.structs.replace(row, json_test="") + + +class TestPymysqlMsgspecFunctions: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=TYPE_ID, + int_test=42, + integer_test=-42, + mediumint_test=8_388_607, + smallint_test=-32_768, + tinyint_test=-128, + bigint_test=9_223_372_036_854_775_807, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 11, + year_test=2026, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.pi, + double_precision_test=math.e, + real_test=1.5, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("99.99"), + char_test="ABCDEFGHIJ", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"0123456789abcdef"), + varbinary_test=memoryview(b"\x00\x01varbinary"), + tinyblob_test=memoryview(b"tinyblob"), + blob_test=memoryview(b"\x00\x01\x02hello"), + mediumblob_test=memoryview(b"mediumblob"), + longblob_test=memoryview(b"longblob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 15), + datetime_test=datetime.datetime(2026, 1, 15, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 15, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + time_test=datetime.timedelta(hours=1, minutes=2, seconds=3), + json_test=json.dumps({"foo": "bar"}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=7, + mediumint_test=None, + smallint_test=3, + tinyint_test=127, + bigint_test=None, + int_unsigned_test=None, + bigint_unsigned_test=2**63 + 42, + year_test=1901, + tinyint1_test=False, + bool_test=None, + boolean_test=True, + float_test=None, + double_test=0.25, + double_precision_test=None, + real_test=None, + decimal_test=decimal.Decimal("0.5000"), + numeric_test=None, + char_test=None, + varchar_test="inner varchar", + tinytext_test=None, + text_test=None, + mediumtext_test=None, + longtext_test=None, + binary_test=None, + varbinary_test=memoryview(b"inner"), + tinyblob_test=None, + blob_test=None, + mediumblob_test=None, + longblob_test=None, + bit_test=memoryview(b"\x01"), + date_test=None, + datetime_test=None, + datetime6_test=None, + timestamp_test=None, + time_test=datetime.timedelta(hours=8, minutes=30), + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=None, + ) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::insert") + def test_insert(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + queries.insert_one_mysql_type( + conn=pymysql_conn, + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::inner_insert", depends=["PymysqlMsgspecFunctions::insert"]) + def test_inner_insert(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + queries.insert_one_inner_mysql_type( + conn=pymysql_conn, + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_one", depends=["PymysqlMsgspecFunctions::inner_insert"]) + def test_get_one(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_mysql_type(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + assert json.loads(result.json_test) == json.loads(model.json_test) + assert _without_json(result) == _without_json(model) + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + # plain datetime drops microseconds, datetime(6) keeps them + assert result.datetime_test.microsecond == 0 + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + assert result.bigint_unsigned_test == 2**63 + 11 + assert result.mood is enums.TestMysqlTypesMood.VALUE_24H + assert result.tag is enums.TestMysqlTypesTag.BETA + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_one_none", depends=["PymysqlMsgspecFunctions::get_one"]) + def test_get_one_none(self, pymysql_conn: pymysql.Connection) -> None: + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_one_inner", depends=["PymysqlMsgspecFunctions::get_one_none"]) + def test_get_one_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + assert result.tinyint1_test is False + assert result.boolean_test is True + assert result.json_test is None + assert result.mood is enums.TestInnerMysqlTypesMood.VALUE__HIDDEN + assert result.tag is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_one_inner_none", depends=["PymysqlMsgspecFunctions::get_one_inner"]) + def test_get_one_inner_none(self, pymysql_conn: pymysql.Connection) -> None: + assert queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_date", depends=["PymysqlMsgspecFunctions::get_one_inner_none"]) + def test_get_date(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_date(conn=pymysql_conn, id_=TYPE_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + assert queries.get_one_date(conn=pymysql_conn, id_=MISSING_ID, date_test=model.date_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_datetime", depends=["PymysqlMsgspecFunctions::get_date"]) + def test_get_datetime(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_datetime(conn=pymysql_conn, id_=TYPE_ID, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + assert queries.get_one_datetime(conn=pymysql_conn, id_=MISSING_ID, datetime_test=model.datetime_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_time", depends=["PymysqlMsgspecFunctions::get_datetime"]) + def test_get_time(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_time(conn=pymysql_conn, id_=TYPE_ID, time_test=model.time_test) + + assert result is not None + # MySQL time maps to timedelta, not datetime.time + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + assert queries.get_one_time(conn=pymysql_conn, id_=MISSING_ID, time_test=model.time_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_bool", depends=["PymysqlMsgspecFunctions::get_time"]) + def test_get_bool(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_bool(conn=pymysql_conn, id_=TYPE_ID, tinyint1_test=True) + + assert result is True + assert queries.get_one_bool(conn=pymysql_conn, id_=MISSING_ID, tinyint1_test=True) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_decimal", depends=["PymysqlMsgspecFunctions::get_bool"]) + def test_get_decimal(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_decimal(conn=pymysql_conn, id_=TYPE_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + # decimal(12,4) comes back padded to scale 4 + assert result == decimal.Decimal("12.3400") + assert str(result) == "12.3400" + assert queries.get_one_decimal(conn=pymysql_conn, id_=MISSING_ID, decimal_test=model.decimal_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_blob", depends=["PymysqlMsgspecFunctions::get_decimal"]) + def test_get_blob(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_blob(conn=pymysql_conn, id_=TYPE_ID, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + assert queries.get_one_blob(conn=pymysql_conn, id_=MISSING_ID, blob_test=model.blob_test) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_bit", depends=["PymysqlMsgspecFunctions::get_blob"]) + def test_get_bit(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_bit(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + # bit(8) comes back as a single byte + assert isinstance(result, memoryview) + assert bytes(result) == b"\x80" + assert queries.get_one_bit(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_year", depends=["PymysqlMsgspecFunctions::get_bit"]) + def test_get_year(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_year(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + assert queries.get_one_year(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_json", depends=["PymysqlMsgspecFunctions::get_year"]) + def test_get_json(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_json(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + assert isinstance(result, str) + assert json.loads(result) == {"foo": "bar"} + assert queries.get_one_json(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_mood", depends=["PymysqlMsgspecFunctions::get_json"]) + def test_get_mood(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_mood(conn=pymysql_conn, id_=TYPE_ID, mood=enums.TestMysqlTypesMood.VALUE_24H) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + assert result == "24h" + assert queries.get_one_mood(conn=pymysql_conn, id_=MISSING_ID, mood=enums.TestMysqlTypesMood.VALUE_24H) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_tag", depends=["PymysqlMsgspecFunctions::get_mood"]) + def test_get_tag(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_tag(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.BETA + assert queries.get_one_tag(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many", depends=["PymysqlMsgspecFunctions::get_tag"]) + def test_get_many(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mysql_type(conn=pymysql_conn, id_=TYPE_ID) + + assert isinstance(result, queries.QueryResults) + results = result() + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + assert _without_json(results[0]) == _without_json(model) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many_iter", depends=["PymysqlMsgspecFunctions::get_many"]) + def test_get_many_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_mysql_type(conn=pymysql_conn, id_=TYPE_ID): + assert isinstance(result, models.TestMysqlType) + assert _without_json(result) == _without_json(model) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many_inner", depends=["PymysqlMsgspecFunctions::get_many_iter"]) + def test_get_many_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = queries.get_many_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID) + + assert isinstance(result, queries.QueryResults) + results = result() + assert list(results) == [inner_model] + for row in queries.get_many_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID): + assert isinstance(row, models.TestInnerMysqlType) + assert row == inner_model + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many_nullable_inner", depends=["PymysqlMsgspecFunctions::get_many_inner"]) + def test_get_many_nullable_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + # int_test is compared with <=>, so None matches the NULL row. + result = queries.get_many_nullable_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID, int_test=None) + + results = result() + assert list(results) == [inner_model] + assert list(queries.get_many_nullable_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID, int_test=0)()) == [] + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many_date", depends=["PymysqlMsgspecFunctions::get_many_nullable_inner"]) + def test_get_many_date(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_date(conn=pymysql_conn, id_=TYPE_ID, date_test=model.date_test) + + assert isinstance(result, queries.QueryResults) + assert list(result()) == [model.date_test] + assert list(queries.get_many_date(conn=pymysql_conn, id_=TYPE_ID, date_test=model.date_test)) == [model.date_test] + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many_time", depends=["PymysqlMsgspecFunctions::get_many_date"]) + def test_get_many_time(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_time(conn=pymysql_conn, id_=TYPE_ID, time_test=model.time_test) + + assert list(result()) == [model.time_test] + assert list(queries.get_many_time(conn=pymysql_conn, id_=TYPE_ID, time_test=model.time_test)) == [model.time_test] + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many_bool", depends=["PymysqlMsgspecFunctions::get_many_time"]) + def test_get_many_bool(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_many_bool(conn=pymysql_conn, id_=TYPE_ID, tinyint1_test=True) + + results = result() + assert len(results) == 1 + assert results[0] is True + for row in queries.get_many_bool(conn=pymysql_conn, id_=TYPE_ID, tinyint1_test=True): + assert row is True + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many_decimal", depends=["PymysqlMsgspecFunctions::get_many_bool"]) + def test_get_many_decimal(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_decimal(conn=pymysql_conn, id_=TYPE_ID, decimal_test=model.decimal_test) + + assert list(result()) == [decimal.Decimal("12.3400")] + for row in queries.get_many_decimal(conn=pymysql_conn, id_=TYPE_ID, decimal_test=model.decimal_test): + assert str(row) == "12.3400" + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_many_mood", depends=["PymysqlMsgspecFunctions::get_many_decimal"]) + def test_get_many_mood(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_many_mood(conn=pymysql_conn, mood=enums.TestMysqlTypesMood.VALUE_24H) + + assert list(result()) == [enums.TestMysqlTypesMood.VALUE_24H] + assert list(queries.get_many_mood(conn=pymysql_conn, mood=enums.TestMysqlTypesMood.VALUE_24H)) == [enums.TestMysqlTypesMood.VALUE_24H] + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::list_months", depends=["PymysqlMsgspecFunctions::get_many_mood"]) + def test_list_months(self, pymysql_conn: pymysql.Connection) -> None: + # Regression for the percent-doubling bug: the parameterless :many + # query contains literal % signs in DATE_FORMAT. + result = queries.list_months(conn=pymysql_conn) + + assert list(result()) == ["2026-01"] + assert list(queries.list_months(conn=pymysql_conn)) == ["2026-01"] + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::count", depends=["PymysqlMsgspecFunctions::list_months"]) + def test_count(self, pymysql_conn: pymysql.Connection) -> None: + # The shared table may carry other files' rows; only a lower bound is safe. + count = queries.count_mysql_types(conn=pymysql_conn) + assert count is not None + assert count >= 1 + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::update_varchar", depends=["PymysqlMsgspecFunctions::count"]) + def test_update_varchar(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.update_varchar_test(conn=pymysql_conn, varchar_test="updated varchar", id_=TYPE_ID) + + assert isinstance(result, int) + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + assert queries.update_varchar_test(conn=pymysql_conn, varchar_test="updated varchar", id_=MISSING_ID) == 0 + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::all_cursor", depends=["PymysqlMsgspecFunctions::update_varchar"]) + def test_all_cursor(self, pymysql_conn: pymysql.Connection) -> None: + cursor = queries.all_mysql_types_cursor(conn=pymysql_conn) + + assert isinstance(cursor, pymysql.cursors.Cursor) + rows = cursor.fetchall() + cursor.close() + # The shared table may carry other files' rows; assert on our own. + assert TYPE_ID in {row[0] for row in rows} + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::delete", depends=["PymysqlMsgspecFunctions::all_cursor"]) + def test_delete(self, pymysql_conn: pymysql.Connection) -> None: + queries.delete_one_mysql_type(conn=pymysql_conn, id_=TYPE_ID) + + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=TYPE_ID) is None + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_inner_mysql_types WHERE table_id = %s", (TYPE_ID,)) + assert queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID) is None + + def test_exec_last_id(self, pymysql_conn: pymysql.Connection) -> None: + # The AUTO_INCREMENT counter persists across runs, so only > 0 holds. + last_id = queries.insert_exec_last_id(conn=pymysql_conn, name=EXEC_LAST_ID_NAME) + + assert isinstance(last_id, int) + assert last_id > 0 + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=last_id) == EXEC_LAST_ID_NAME + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_execlastid WHERE id = %s", (last_id,)) + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=last_id) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::insert_type_override") + def test_insert_type_override(self, pymysql_conn: pymysql.Connection, override_model: models.TestTypeOverride) -> None: + queries.insert_type_override(conn=pymysql_conn, id_=override_model.id_, text_test=override_model.text_test) + queries.insert_type_override(conn=pymysql_conn, id_=OVERRIDE_NONE_ID, text_test=None) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::get_type_override", depends=["PymysqlMsgspecFunctions::insert_type_override"]) + def test_get_type_override(self, pymysql_conn: pymysql.Connection, override_model: models.TestTypeOverride) -> None: + result = queries.get_type_override(conn=pymysql_conn, id_=OVERRIDE_ID) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + none_result = queries.get_type_override(conn=pymysql_conn, id_=OVERRIDE_NONE_ID) + assert none_result is not None + assert none_result.text_test is None + assert queries.get_type_override(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(depends=["PymysqlMsgspecFunctions::get_type_override"]) + def test_delete_type_override(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_type_override WHERE id IN (%s, %s)", (OVERRIDE_ID, OVERRIDE_NONE_ID)) + assert queries.get_type_override(conn=pymysql_conn, id_=OVERRIDE_ID) is None + + def test_reserved_arg(self, pymysql_conn: pymysql.Connection) -> None: + # The column is named conn, which collides with the connection + # argument and is deduped to conn_2. + queries.insert_reserved_arg(conn=pymysql_conn, id_=RESERVED_ID, conn_2=RESERVED_CONN) + + result = queries.get_reserved_arg(conn=pymysql_conn, conn_2=RESERVED_CONN) + assert result == models.TestReservedArg(id_=RESERVED_ID, conn=RESERVED_CONN) + assert queries.get_reserved_arg(conn=pymysql_conn, conn_2="msgspec-functions-missing") is None + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_reserved_args WHERE id = %s", (RESERVED_ID,)) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::case_insert") + def test_case_insert(self, pymysql_conn: pymysql.Connection) -> None: + queries_case.insert_case_row(conn=pymysql_conn, id_=CASE_IDS[0], upper_dt=CASE_DT, prec_dec=CASE_DEC) + queries_case.insert_case_row(conn=pymysql_conn, id_=CASE_IDS[1], upper_dt=CASE_DT, prec_dec=CASE_DEC) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::case_get", depends=["PymysqlMsgspecFunctions::case_insert"]) + def test_case_get(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_case.get_case_row(conn=pymysql_conn, id_=CASE_IDS[0]) + + assert result is not None + assert result == models.TestCaseSensitivity(id_=CASE_IDS[0], upper_dt=CASE_DT, prec_dec=CASE_DEC) + assert queries_case.get_case_row(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::case_count", depends=["PymysqlMsgspecFunctions::case_get"]) + def test_case_count(self, pymysql_conn: pymysql.Connection) -> None: + # The WHERE clause lives inside an executable /*! version comment; if + # MySQL ignored it both counts would be 2. + # Range-scoped asserts: the shared table may carry other files' rows, + # so counts outside [CASE_IDS[0], CASE_IDS[1]] must cancel out. + beyond = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[1] + 1) + high = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[1]) + low = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[0]) + assert beyond is not None + assert high is not None + assert low is not None + assert high - beyond == 1 + assert low - beyond == len(CASE_IDS) + + @pytest.mark.dependency(depends=["PymysqlMsgspecFunctions::case_count"]) + def test_case_delete(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_case_sensitivity WHERE id IN (%s, %s)", CASE_IDS) + beyond = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[1] + 1) + low = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[0]) + assert beyond is not None + assert low is not None + assert low - beyond == 0 + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::enum_insert") + def test_enum_override_insert(self, pymysql_conn: pymysql.Connection) -> None: + # The overridden parameter is a plain str; the generated code converts + # it back through enums.TestEnumOverrideMoodTest. + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=ENUM_IDS[0], mood_test="happy") + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=ENUM_IDS[1], mood_test="sad") + with pytest.raises(ValueError, match="angry"): + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=MISSING_ID, mood_test="angry") + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::enum_get", depends=["PymysqlMsgspecFunctions::enum_insert"]) + def test_enum_override_get(self, pymysql_conn: pymysql.Connection) -> None: + mood = queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=ENUM_IDS[0]) + + assert mood is not None + assert isinstance(mood, str) + assert mood == "happy" + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::enum_list", depends=["PymysqlMsgspecFunctions::enum_insert"]) + def test_enum_override_list(self, pymysql_conn: pymysql.Connection) -> None: + rows = queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=list(ENUM_IDS))() + + assert all(isinstance(row, models.TestEnumOverride) for row in rows) + assert {row.id_: row.mood_test for row in rows} == {ENUM_IDS[0]: "happy", ENUM_IDS[1]: "sad"} + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::enum_iterate", depends=["PymysqlMsgspecFunctions::enum_insert"]) + def test_enum_override_iterate(self, pymysql_conn: pymysql.Connection) -> None: + seen: dict[int, str] = {} + for row in queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=list(ENUM_IDS)): + assert isinstance(row, models.TestEnumOverride) + seen[row.id_] = row.mood_test + assert seen == {ENUM_IDS[0]: "happy", ENUM_IDS[1]: "sad"} + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::enum_empty", depends=["PymysqlMsgspecFunctions::enum_insert"]) + def test_enum_override_empty_slice(self, pymysql_conn: pymysql.Connection) -> None: + assert list(queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=[])()) == [] + assert list(queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=[])) == [] + + @pytest.mark.dependency(depends=["PymysqlMsgspecFunctions::enum_empty"]) + def test_enum_override_delete(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_enum_override WHERE id IN (%s, %s)", ENUM_IDS) + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=ENUM_IDS[0]) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::field_insert") + def test_field_naming_insert(self, pymysql_conn: pymysql.Connection) -> None: + # There is no generated insert for this table. + with pymysql_conn.cursor() as cur: + cur.execute("INSERT INTO test_field_namings (id, outputs) VALUES (%s, %s)", (FIELD_ID, json.dumps(["first", "second"]))) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::field_get", depends=["PymysqlMsgspecFunctions::field_insert"]) + def test_field_naming_get(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_ID) + + assert result is not None + assert isinstance(result, models.TestFieldNaming) + assert result.id_ == FIELD_ID + assert json.loads(result.outputs) == ["first", "second"] + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::field_joined", depends=["PymysqlMsgspecFunctions::field_get"]) + def test_field_naming_joined(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_field_namings.get_joined_field_namings(conn=pymysql_conn, id_=FIELD_ID) + + assert result is not None + assert isinstance(result, queries_field_namings.GetJoinedFieldNamingsRow) + assert json.loads(result.outputs) == ["first", "second"] + assert json.loads(result.outputs_2) == ["first", "second"] + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::field_set", depends=["PymysqlMsgspecFunctions::field_joined"]) + def test_field_naming_set(self, pymysql_conn: pymysql.Connection) -> None: + queries_field_namings.set_field_naming_outputs(conn=pymysql_conn, outputs=json.dumps({"count": 2}), id_=FIELD_ID) + + result = queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_ID) + assert result is not None + assert json.loads(result.outputs) == {"count": 2} + + @pytest.mark.dependency(depends=["PymysqlMsgspecFunctions::field_set"]) + def test_field_naming_delete(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_field_namings WHERE id = %s", (FIELD_ID,)) + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::invalid_insert") + def test_invalid_identifiers_insert(self, pymysql_conn: pymysql.Connection) -> None: + queries_invalid_identifiers.insert_invalid_identifiers(conn=pymysql_conn, id_=INVALID_ID, column_3p_="3p value", new_notes="note value") + queries_invalid_identifiers.insert_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID, total=987) + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::invalid_get", depends=["PymysqlMsgspecFunctions::invalid_insert"]) + def test_invalid_identifiers_get(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=INVALID_ID) + + assert result == models.TestInvalidIdentifier(id_=INVALID_ID, column_3p_="3p value", new_notes="note value", column__pct=None) + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlMsgspecFunctions::third_party_get", depends=["PymysqlMsgspecFunctions::invalid_insert"]) + def test_third_party_stat_get(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID) + + assert result == models.Model3RdPartyStat(id_=THIRD_PARTY_ID, total=987) + assert queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(depends=["PymysqlMsgspecFunctions::invalid_get", "PymysqlMsgspecFunctions::third_party_get"]) + def test_invalid_identifiers_delete(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_invalid_identifiers WHERE id = %s", (INVALID_ID,)) + cur.execute("DELETE FROM `3rd_party_stats` WHERE id = %s", (THIRD_PARTY_ID,)) + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=INVALID_ID) is None + assert queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID) is None + + def test_one_missing_rows_return_none(self, pymysql_conn: pymysql.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. The + # count queries always return a row, so their miss branch needs the + # no-row stub. + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=-1) is None + assert queries.get_one_date(conn=pymysql_conn, id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert queries.get_one_datetime(conn=pymysql_conn, id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert queries.get_one_time(conn=pymysql_conn, id_=-1, time_test=datetime.timedelta()) is None + assert queries.get_one_bool(conn=pymysql_conn, id_=-1, tinyint1_test=False) is None + assert queries.get_one_decimal(conn=pymysql_conn, id_=-1, decimal_test=decimal.Decimal(0)) is None + assert queries.get_one_blob(conn=pymysql_conn, id_=-1, blob_test=memoryview(b"")) is None + assert queries.get_one_bit(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_year(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_json(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_mood(conn=pymysql_conn, id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert queries.get_one_tag(conn=pymysql_conn, id_=-1) is None + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=-1) is None + assert queries.get_type_override(conn=pymysql_conn, id_=-1) is None + assert queries.get_reserved_arg(conn=pymysql_conn, conn_2="missing") is None + assert queries.touch_exec_last_id(conn=pymysql_conn, name="untouched", id_=-1) is None + assert queries_case.get_case_row(conn=pymysql_conn, id_=-1) is None + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=-1) is None + assert queries_field_namings.get_joined_field_namings(conn=pymysql_conn, id_=-1) is None + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=-1) is None + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=-1) is None + assert queries_enum_override.count_enum_override_by_moods(conn=pymysql_conn, moods=[]) == 0 + + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert queries.count_mysql_types(conn=stub) is None + assert queries_case.count_case_rows(conn=stub, id_=0) is None + assert queries_enum_override.count_enum_override_by_moods(conn=stub, moods=[]) is None diff --git a/test/driver_pymysql/no_row_conn.py b/test/driver_pymysql/no_row_conn.py new file mode 100644 index 00000000..3e18fe58 --- /dev/null +++ b/test/driver_pymysql/no_row_conn.py @@ -0,0 +1,74 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Shared connection stub for exercising the generated not-found branches.""" + +from __future__ import annotations + +import typing + + +class NoRowCursor: + """Cursor stub whose fetchone never finds a row.""" + + def __enter__(self) -> typing.Self: + """Return the cursor itself, like a real cursor context manager. + + Returns + ------- + typing.Self + The cursor stub missing every row. + """ + return self + + def __exit__(self, *exc_info: object) -> None: + """Do nothing on exit; there is no real cursor to close.""" + + @staticmethod + def execute(_query: str, _args: object = None) -> int: + """Pretend to execute and affect no rows. + + Returns + ------- + int + Always 0. + """ + return 0 + + @staticmethod + def fetchone() -> None: + """Return None, exactly like a cursor over an empty result set.""" + + +class NoRowConn: + """Connection stub whose queries never find a row.""" + + # `SELECT count(*)` always returns exactly one row, so the generated + # not-found branch of the count queries needs a connection stub that + # misses. + @staticmethod + def cursor() -> NoRowCursor: + """Return a cursor that finds no row. + + Returns + ------- + NoRowCursor + The cursor stub missing every row. + """ + return NoRowCursor() diff --git a/test/driver_pymysql/omit_tc/__init__.py b/test/driver_pymysql/omit_tc/__init__.py new file mode 100644 index 00000000..a6c572cd --- /dev/null +++ b/test/driver_pymysql/omit_tc/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for pymysql omit_typechecking_block tests.""" diff --git a/test/driver_pymysql/omit_tc/classes/__init__.py b/test/driver_pymysql/omit_tc/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/omit_tc/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/omit_tc/classes/enums.py b/test/driver_pymysql/omit_tc/classes/enums.py new file mode 100644 index 00000000..9df21084 --- /dev/null +++ b/test/driver_pymysql/omit_tc/classes/enums.py @@ -0,0 +1,20 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("TestEnumOverrideMoodTest",) + +import enum +import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" diff --git a/test/driver_pymysql/omit_tc/classes/models.py b/test/driver_pymysql/omit_tc/classes/models.py new file mode 100644 index 00000000..35c76305 --- /dev/null +++ b/test/driver_pymysql/omit_tc/classes/models.py @@ -0,0 +1,25 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("TestEnumOverride",) + +import dataclasses +import collections.abc + + +@dataclasses.dataclass() +class TestEnumOverride: + """Model representing TestEnumOverride. + + Attributes: + id_: int + mood_test: str + """ + + id_: int + mood_test: str diff --git a/test/driver_pymysql/omit_tc/classes/queries_enum_override.py b/test/driver_pymysql/omit_tc/classes/queries_enum_override.py new file mode 100644 index 00000000..4fcdf19b --- /dev/null +++ b/test/driver_pymysql/omit_tc/classes/queries_enum_override.py @@ -0,0 +1,211 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueriesEnumOverride", + "QueryResults", +) + +import typing +import collections.abc +import pymysql +import pymysql.cursors + + +from test.driver_pymysql.omit_tc.classes import enums +from test.driver_pymysql.omit_tc.classes import models + +type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class QueriesEnumOverride: + """Queries from file queries_enum_override.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_enum_override(self, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Args: + id_: int. + mood_test: str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + def get_enum_override_mood(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + def list_enum_override_by_ids(self, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Args: + ids: collections.abc.Sequence[int]. + + Returns: + Helper class of type `QueryResults[models.TestEnumOverride]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(self._conn, sql, _decode_hook, *ids) + + def count_enum_override_by_moods(self, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Args: + moods: collections.abc.Sequence[str]. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with self._conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/omit_tc/functions/__init__.py b/test/driver_pymysql/omit_tc/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/omit_tc/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/omit_tc/functions/enums.py b/test/driver_pymysql/omit_tc/functions/enums.py new file mode 100644 index 00000000..9df21084 --- /dev/null +++ b/test/driver_pymysql/omit_tc/functions/enums.py @@ -0,0 +1,20 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("TestEnumOverrideMoodTest",) + +import enum +import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" diff --git a/test/driver_pymysql/omit_tc/functions/models.py b/test/driver_pymysql/omit_tc/functions/models.py new file mode 100644 index 00000000..35c76305 --- /dev/null +++ b/test/driver_pymysql/omit_tc/functions/models.py @@ -0,0 +1,25 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("TestEnumOverride",) + +import dataclasses +import collections.abc + + +@dataclasses.dataclass() +class TestEnumOverride: + """Model representing TestEnumOverride. + + Attributes: + id_: int + mood_test: str + """ + + id_: int + mood_test: str diff --git a/test/driver_pymysql/omit_tc/functions/queries_enum_override.py b/test/driver_pymysql/omit_tc/functions/queries_enum_override.py new file mode 100644 index 00000000..061142d9 --- /dev/null +++ b/test/driver_pymysql/omit_tc/functions/queries_enum_override.py @@ -0,0 +1,202 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "count_enum_override_by_moods", + "get_enum_override_mood", + "insert_enum_override", + "list_enum_override_by_ids", +) + +import typing +import collections.abc +import pymysql +import pymysql.cursors + + +from test.driver_pymysql.omit_tc.functions import enums +from test.driver_pymysql.omit_tc.functions import models + +type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_enum_override(conn: pymysql.Connection, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + mood_test: str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + +def get_enum_override_mood(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + +def list_enum_override_by_ids(conn: pymysql.Connection, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + + Returns: + Helper class of type `QueryResults[models.TestEnumOverride]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *ids) + + +def count_enum_override_by_moods(conn: pymysql.Connection, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + moods: collections.abc.Sequence[str]. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/omit_tc/ruff.toml b/test/driver_pymysql/omit_tc/ruff.toml new file mode 100644 index 00000000..0fa548a4 --- /dev/null +++ b/test/driver_pymysql/omit_tc/ruff.toml @@ -0,0 +1,5 @@ +extend="../../../ruff.toml" + + +[lint.pydocstyle] +convention = "google" diff --git a/test/driver_pymysql/omit_tc/test_omit_typechecking_import.py b/test/driver_pymysql/omit_tc/test_omit_typechecking_import.py new file mode 100644 index 00000000..cfd83f68 --- /dev/null +++ b/test/driver_pymysql/omit_tc/test_omit_typechecking_import.py @@ -0,0 +1,35 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""With omit_typechecking_block the annotation imports execute at module level. + +Importing the generated modules is the whole test: the QueryResultsArgsType +alias executes at module level, which the lazy PEP 695 alias form must keep +safe. +""" + +from __future__ import annotations + +from test.driver_pymysql.omit_tc.classes import queries_enum_override as classes_module +from test.driver_pymysql.omit_tc.functions import queries_enum_override as functions_module + + +def test_omit_typechecking_modules_import_at_runtime() -> None: + assert classes_module.INSERT_ENUM_OVERRIDE + assert functions_module.INSERT_ENUM_OVERRIDE diff --git a/test/driver_pymysql/omit_tc/test_omit_typechecking_runtime.py b/test/driver_pymysql/omit_tc/test_omit_typechecking_runtime.py new file mode 100644 index 00000000..651b173a --- /dev/null +++ b/test/driver_pymysql/omit_tc/test_omit_typechecking_runtime.py @@ -0,0 +1,176 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Runtime coverage for the pymysql omit_typechecking_block query modules. + +The generated code must behave exactly like the regular variants even though +all imports and type aliases execute at module level. These tests exercise +the query functions and the QueryResults helper (both the call path and the +cursor-based for path) of the classes and functions packages. +""" + +from __future__ import annotations + +import typing + +import pytest + +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.omit_tc.classes import models as classes_models +from test.driver_pymysql.omit_tc.classes import queries_enum_override as classes_queries +from test.driver_pymysql.omit_tc.functions import models as functions_models +from test.driver_pymysql.omit_tc.functions import queries_enum_override as functions_queries + +if typing.TYPE_CHECKING: + import pymysql + +# Ids reserved for this file (omit_tc owns 5000-5099); all suites share one +# database sequentially, so every enum_override chain uses unique ids and +# deletes its rows at the end. +CLASSES_IDS: typing.Final[tuple[int, int]] = (5000, 5001) +FUNCTIONS_IDS: typing.Final[tuple[int, int]] = (5010, 5011) +MISSING_ID: typing.Final[int] = 5099 + + +class TestOmitTcClasses: + @pytest.fixture(scope="session") + def queries_obj(self, pymysql_conn: pymysql.Connection) -> classes_queries.QueriesEnumOverride: + return classes_queries.QueriesEnumOverride(conn=pymysql_conn) + + @pytest.mark.dependency(name="TestOmitTcClasses::insert_enum_override") + def test_insert_enum_override(self, queries_obj: classes_queries.QueriesEnumOverride) -> None: + # The overridden parameter is a plain str; the generated code converts + # it back to enums.TestEnumOverrideMoodTest before it reaches the + # driver. + queries_obj.insert_enum_override(id_=CLASSES_IDS[0], mood_test="happy") + queries_obj.insert_enum_override(id_=CLASSES_IDS[1], mood_test="sad") + with pytest.raises(ValueError, match="angry"): + queries_obj.insert_enum_override(id_=MISSING_ID, mood_test="angry") + + @pytest.mark.dependency(name="TestOmitTcClasses::get_enum_override", depends=["TestOmitTcClasses::insert_enum_override"]) + def test_get_enum_override_mood(self, queries_obj: classes_queries.QueriesEnumOverride) -> None: + mood = queries_obj.get_enum_override_mood(id_=CLASSES_IDS[0]) + assert mood is not None + assert isinstance(mood, str) + assert mood == "happy" + + def test_get_enum_override_mood_not_found(self, queries_obj: classes_queries.QueriesEnumOverride) -> None: + assert queries_obj.get_enum_override_mood(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="TestOmitTcClasses::list_enum_override", depends=["TestOmitTcClasses::insert_enum_override"]) + def test_list_enum_override_by_ids(self, queries_obj: classes_queries.QueriesEnumOverride) -> None: + # Calling the QueryResults object fetches all rows in one go. + rows = queries_obj.list_enum_override_by_ids(ids=list(CLASSES_IDS))() + assert all(isinstance(row, classes_models.TestEnumOverride) for row in rows) + assert {row.id_: row.mood_test for row in rows} == {CLASSES_IDS[0]: "happy", CLASSES_IDS[1]: "sad"} + + @pytest.mark.dependency(name="TestOmitTcClasses::iterate_enum_override", depends=["TestOmitTcClasses::insert_enum_override"]) + def test_iterate_enum_override_by_ids( + self, + queries_obj: classes_queries.QueriesEnumOverride, + pymysql_conn: pymysql.Connection, + ) -> None: + assert queries_obj.conn is pymysql_conn + results = queries_obj.list_enum_override_by_ids(ids=list(CLASSES_IDS)) + seen: dict[int, str] = {} + # Exercise the cursor-based for path. + for row in results: + assert isinstance(row, classes_models.TestEnumOverride) + seen[row.id_] = row.mood_test + assert seen == {CLASSES_IDS[0]: "happy", CLASSES_IDS[1]: "sad"} + + @pytest.mark.dependency(name="TestOmitTcClasses::empty_enum_override", depends=["TestOmitTcClasses::insert_enum_override"]) + def test_list_enum_override_by_ids_empty(self, queries_obj: classes_queries.QueriesEnumOverride) -> None: + # An empty slice expands to IN (NULL), which matches no rows. + assert list(queries_obj.list_enum_override_by_ids(ids=[])()) == [] + assert list(queries_obj.list_enum_override_by_ids(ids=[])) == [] + + @pytest.mark.dependency(depends=["TestOmitTcClasses::insert_enum_override"]) + def test_count_enum_override_by_moods(self, queries_obj: classes_queries.QueriesEnumOverride) -> None: + # An empty slice expands to IN (NULL); count(*) still returns a row. + assert queries_obj.count_enum_override_by_moods(moods=[]) == 0 + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert classes_queries.QueriesEnumOverride(conn=stub).count_enum_override_by_moods(moods=[]) is None + + @pytest.mark.dependency(depends=["TestOmitTcClasses::insert_enum_override"]) + def test_delete_enum_override(self, pymysql_conn: pymysql.Connection) -> None: + # Remove the rows so later suites against the shared database start + # clean. + with pymysql_conn.cursor() as cur: + for row_id in CLASSES_IDS: + cur.execute("DELETE FROM test_enum_override WHERE id = %s", (row_id,)) + + +class TestOmitTcFunctions: + @pytest.mark.dependency(name="TestOmitTcFunctions::insert_enum_override") + def test_insert_enum_override(self, pymysql_conn: pymysql.Connection) -> None: + # The overridden parameter is a plain str; the generated code converts + # it back to enums.TestEnumOverrideMoodTest before it reaches the + # driver. + functions_queries.insert_enum_override(conn=pymysql_conn, id_=FUNCTIONS_IDS[0], mood_test="happy") + functions_queries.insert_enum_override(conn=pymysql_conn, id_=FUNCTIONS_IDS[1], mood_test="sad") + with pytest.raises(ValueError, match="angry"): + functions_queries.insert_enum_override(conn=pymysql_conn, id_=MISSING_ID, mood_test="angry") + + @pytest.mark.dependency(name="TestOmitTcFunctions::get_enum_override", depends=["TestOmitTcFunctions::insert_enum_override"]) + def test_get_enum_override_mood(self, pymysql_conn: pymysql.Connection) -> None: + mood = functions_queries.get_enum_override_mood(conn=pymysql_conn, id_=FUNCTIONS_IDS[0]) + assert mood is not None + assert isinstance(mood, str) + assert mood == "happy" + + def test_get_enum_override_mood_not_found(self, pymysql_conn: pymysql.Connection) -> None: + assert functions_queries.get_enum_override_mood(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="TestOmitTcFunctions::list_enum_override", depends=["TestOmitTcFunctions::insert_enum_override"]) + def test_count_enum_override_by_moods(self, pymysql_conn: pymysql.Connection) -> None: + # An empty slice expands to IN (NULL); count(*) still returns a row. + assert functions_queries.count_enum_override_by_moods(conn=pymysql_conn, moods=[]) == 0 + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert functions_queries.count_enum_override_by_moods(conn=stub, moods=[]) is None + + def test_list_enum_override_by_ids(self, pymysql_conn: pymysql.Connection) -> None: + # Calling the QueryResults object fetches all rows in one go. + rows = functions_queries.list_enum_override_by_ids(conn=pymysql_conn, ids=list(FUNCTIONS_IDS))() + assert all(isinstance(row, functions_models.TestEnumOverride) for row in rows) + assert {row.id_: row.mood_test for row in rows} == {FUNCTIONS_IDS[0]: "happy", FUNCTIONS_IDS[1]: "sad"} + + @pytest.mark.dependency(name="TestOmitTcFunctions::iterate_enum_override", depends=["TestOmitTcFunctions::insert_enum_override"]) + def test_iterate_enum_override_by_ids(self, pymysql_conn: pymysql.Connection) -> None: + results = functions_queries.list_enum_override_by_ids(conn=pymysql_conn, ids=list(FUNCTIONS_IDS)) + seen: dict[int, str] = {} + # Exercise the cursor-based for path. + for row in results: + assert isinstance(row, functions_models.TestEnumOverride) + seen[row.id_] = row.mood_test + assert seen == {FUNCTIONS_IDS[0]: "happy", FUNCTIONS_IDS[1]: "sad"} + + @pytest.mark.dependency(name="TestOmitTcFunctions::empty_enum_override", depends=["TestOmitTcFunctions::insert_enum_override"]) + def test_list_enum_override_by_ids_empty(self, pymysql_conn: pymysql.Connection) -> None: + # An empty slice expands to IN (NULL), which matches no rows. + assert list(functions_queries.list_enum_override_by_ids(conn=pymysql_conn, ids=[])()) == [] + assert list(functions_queries.list_enum_override_by_ids(conn=pymysql_conn, ids=[])) == [] + + @pytest.mark.dependency(depends=["TestOmitTcFunctions::insert_enum_override"]) + def test_delete_enum_override(self, pymysql_conn: pymysql.Connection) -> None: + # Remove the rows so later suites against the shared database start + # clean. + with pymysql_conn.cursor() as cur: + for row_id in FUNCTIONS_IDS: + cur.execute("DELETE FROM test_enum_override WHERE id = %s", (row_id,)) diff --git a/test/driver_pymysql/pydantic/__init__.py b/test/driver_pymysql/pydantic/__init__.py new file mode 100644 index 00000000..11a9bca5 --- /dev/null +++ b/test/driver_pymysql/pydantic/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Package to allow importing for pymysql tests.""" diff --git a/test/driver_pymysql/pydantic/classes/__init__.py b/test/driver_pymysql/pydantic/classes/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/pydantic/classes/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/pydantic/classes/enums.py b/test/driver_pymysql/pydantic/classes/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_pymysql/pydantic/classes/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_pymysql/pydantic/classes/models.py b/test/driver_pymysql/pydantic/classes/models.py new file mode 100644 index 00000000..3de2b428 --- /dev/null +++ b/test/driver_pymysql/pydantic/classes/models.py @@ -0,0 +1,314 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestEnumOverride", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +from collections import UserString +import datetime +import decimal +import pydantic +import typing + +if typing.TYPE_CHECKING: + import collections.abc + +from test.driver_pymysql.pydantic.classes import enums + + +class Model3RdPartyStat(pydantic.BaseModel): + """Model representing Model3RdPartyStat. + + Attributes: + id_: int + total: int + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + total: int + + +class TestCaseSensitivity(pydantic.BaseModel): + """Model representing TestCaseSensitivity. + + Attributes: + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +class TestEnumOverride(pydantic.BaseModel): + """Model representing TestEnumOverride. + + Attributes: + id_: int + mood_test: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + mood_test: str + + +class TestFieldNaming(pydantic.BaseModel): + """Model representing TestFieldNaming. + + Attributes: + id_: int + outputs: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + outputs: str + + +class TestInnerMysqlType(pydantic.BaseModel): + """Model representing TestInnerMysqlType. + + Attributes: + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +class TestInvalidIdentifier(pydantic.BaseModel): + """Model representing TestInvalidIdentifier. + + Attributes: + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +class TestMysqlType(pydantic.BaseModel): + """Model representing TestMysqlType. + + Attributes: + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +class TestReservedArg(pydantic.BaseModel): + """Model representing TestReservedArg. + + Attributes: + id_: int + conn: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + conn: str + + +class TestTypeOverride(pydantic.BaseModel): + """Model representing TestTypeOverride. + + Attributes: + id_: int + text_test: UserString | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + text_test: UserString | None diff --git a/test/driver_pymysql/pydantic/classes/queries.py b/test/driver_pymysql/pydantic/classes/queries.py new file mode 100644 index 00000000..ac94ef7b --- /dev/null +++ b/test/driver_pymysql/pydantic/classes/queries.py @@ -0,0 +1,1432 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Queries", + "QueryResults", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.pydantic.classes import enums +from test.driver_pymysql.pydantic.classes import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class Queries: + """Queries from file queries.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_one_mysql_type( + self, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, + ) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + id_: int. + int_test: int. + integer_test: int. + mediumint_test: int. + smallint_test: int. + tinyint_test: int. + bigint_test: int. + int_unsigned_test: int. + bigint_unsigned_test: int. + year_test: int. + tinyint1_test: bool. + bool_test: bool. + boolean_test: bool. + float_test: float. + double_test: float. + double_precision_test: float. + real_test: float. + decimal_test: decimal.Decimal. + numeric_test: decimal.Decimal. + char_test: str. + varchar_test: str. + tinytext_test: str. + text_test: str. + mediumtext_test: str. + longtext_test: str. + binary_test: memoryview. + varbinary_test: memoryview. + tinyblob_test: memoryview. + blob_test: memoryview. + mediumblob_test: memoryview. + longblob_test: memoryview. + bit_test: memoryview. + date_test: datetime.date. + datetime_test: datetime.datetime. + datetime6_test: datetime.datetime. + timestamp_test: datetime.datetime. + time_test: datetime.timedelta. + json_test: str. + mood: enums.TestMysqlTypesMood. + tag: enums.TestMysqlTypesTag. + """ + with self._conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + def insert_one_inner_mysql_type( + self, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, + ) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + table_id: int. + int_test: int | None. + integer_test: int | None. + mediumint_test: int | None. + smallint_test: int | None. + tinyint_test: int | None. + bigint_test: int | None. + int_unsigned_test: int | None. + bigint_unsigned_test: int | None. + year_test: int | None. + tinyint1_test: bool | None. + bool_test: bool | None. + boolean_test: bool | None. + float_test: float | None. + double_test: float | None. + double_precision_test: float | None. + real_test: float | None. + decimal_test: decimal.Decimal | None. + numeric_test: decimal.Decimal | None. + char_test: str | None. + varchar_test: str | None. + tinytext_test: str | None. + text_test: str | None. + mediumtext_test: str | None. + longtext_test: str | None. + binary_test: memoryview | None. + varbinary_test: memoryview | None. + tinyblob_test: memoryview | None. + blob_test: memoryview | None. + mediumblob_test: memoryview | None. + longblob_test: memoryview | None. + bit_test: memoryview | None. + date_test: datetime.date | None. + datetime_test: datetime.datetime | None. + datetime6_test: datetime.datetime | None. + timestamp_test: datetime.datetime | None. + time_test: datetime.timedelta | None. + json_test: str | None. + mood: enums.TestInnerMysqlTypesMood | None. + tag: enums.TestInnerMysqlTypesTag | None. + """ + with self._conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + def get_one_mysql_type(self, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestMysqlType` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + def get_one_inner_mysql_type(self, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + table_id: int. + + Returns: + Result of type `models.TestInnerMysqlType` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + def get_many_mysql_type(self, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(self._conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + def get_many_inner_mysql_type(self, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + table_id: int. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + def get_many_nullable_inner_mysql_type(self, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Args: + table_id: int. + int_test: int | None. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(self._conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + def get_one_date(self, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + id_: int. + date_test: datetime.date. + + Returns: + Result of type `datetime.date` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DATE, (id_, date_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_datetime(self, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Args: + id_: int. + datetime_test: datetime.datetime. + + Returns: + Result of type `datetime.datetime` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_time(self, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + id_: int. + time_test: datetime.timedelta. + + Returns: + Result of type `datetime.timedelta` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_TIME, (id_, time_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_bool(self, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + id_: int. + tinyint1_test: bool. + + Returns: + Result of type `bool` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = cur.fetchone() + if row is None: + return None + return bool(row[0]) + + def get_one_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Result of type `decimal.Decimal` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_blob(self, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Args: + id_: int. + blob_test: memoryview. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + def get_one_bit(self, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_BIT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + def get_one_year(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_YEAR, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_json(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_JSON, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def get_one_mood(self, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Args: + id_: int. + mood: enums.TestMysqlTypesMood. + + Returns: + Result of type `enums.TestMysqlTypesMood` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_MOOD, (id_, mood)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + def get_one_tag(self, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `enums.TestMysqlTypesTag` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ONE_TAG, (id_,)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + def get_many_date(self, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + id_: int. + date_test: datetime.date. + + Returns: + Helper class of type `QueryResults[datetime.date]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + def get_many_time(self, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + id_: int. + time_test: datetime.timedelta. + + Returns: + Helper class of type `QueryResults[datetime.timedelta]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + def get_many_bool(self, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + id_: int. + tinyint1_test: bool. + + Returns: + Helper class of type `QueryResults[bool]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(self._conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + def get_many_decimal(self, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Helper class of type `QueryResults[decimal.Decimal]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + def get_many_mood(self, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Args: + mood: enums.TestMysqlTypesMood. + + Returns: + Helper class of type `QueryResults[enums.TestMysqlTypesMood]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(self._conn, GET_MANY_MOOD, _decode_hook, mood) + + def list_months(self) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Returns: + Helper class of type `QueryResults[str]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(self._conn, LIST_MONTHS, operator.itemgetter(0)) + + def count_mysql_types(self) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(COUNT_MYSQL_TYPES) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def update_varchar_test(self, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Args: + varchar_test: str. + id_: int. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + with self._conn.cursor() as cur: + return cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + def delete_one_mysql_type(self, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Args: + id_: int. + """ + with self._conn.cursor() as cur: + cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + def all_mysql_types_cursor(self) -> pymysql.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Returns: + The result of type `pymysql.cursors.Cursor` returned when executing the query. + """ + cur = self._conn.cursor() + cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + def insert_exec_last_id(self, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Args: + name: str. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + def get_exec_last_id_name(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + def insert_type_override(self, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Args: + id_: int. + text_test: UserString | None. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + def get_type_override(self, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestTypeOverride` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + def get_reserved_arg(self, *, conn: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Args: + conn: str. + + Returns: + Result of type `models.TestReservedArg` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_RESERVED_ARG, (conn,)) + row = cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + def insert_reserved_arg(self, *, id_: int, conn: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Args: + id_: int. + conn: str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_RESERVED_ARG, (id_, conn)) + + def touch_exec_last_id(self, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Args: + name: str. + id_: int. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + with self._conn.cursor() as cur: + cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_pymysql/pydantic/classes/queries_case.py b/test/driver_pymysql/pydantic/classes/queries_case.py new file mode 100644 index 00000000..78efc94c --- /dev/null +++ b/test/driver_pymysql/pydantic/classes/queries_case.py @@ -0,0 +1,112 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_case.sql +"""Module containing queries from file queries_case.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("QueriesCase",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + +from test.driver_pymysql.pydantic.classes import models + + +INSERT_CASE_ROW: typing.Final[str] = """-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) +""" + +GET_CASE_ROW: typing.Final[str] = """-- name: GetCaseRow :one +SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s +""" + +COUNT_CASE_ROWS: typing.Final[str] = """-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ +""" + + +class QueriesCase: + """Queries from file queries_case.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_case_row(self, *, id_: int, upper_dt: datetime.datetime, prec_dec: decimal.Decimal) -> None: + """Execute SQL query with `name: InsertCaseRow :exec`. + + ```sql + INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) + ``` + + Args: + id_: int. + upper_dt: datetime.datetime. + prec_dec: decimal.Decimal. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_CASE_ROW, (id_, upper_dt, prec_dec)) + + def get_case_row(self, *, id_: int) -> models.TestCaseSensitivity | None: + """Fetch one from the db using the SQL query with `name: GetCaseRow :one`. + + ```sql + SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestCaseSensitivity` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_CASE_ROW, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestCaseSensitivity(id_=row[0], upper_dt=row[1], prec_dec=row[2]) + + def count_case_rows(self, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: CountCaseRows :one`. + + ```sql + SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ + ``` + + Args: + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(COUNT_CASE_ROWS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/pydantic/classes/queries_enum_override.py b/test/driver_pymysql/pydantic/classes/queries_enum_override.py new file mode 100644 index 00000000..5072dabd --- /dev/null +++ b/test/driver_pymysql/pydantic/classes/queries_enum_override.py @@ -0,0 +1,212 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueriesEnumOverride", + "QueryResults", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.pydantic.classes import enums +from test.driver_pymysql.pydantic.classes import models + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +class QueriesEnumOverride: + """Queries from file queries_enum_override.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_enum_override(self, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Args: + id_: int. + mood_test: str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + def get_enum_override_mood(self, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + def list_enum_override_by_ids(self, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Args: + ids: collections.abc.Sequence[int]. + + Returns: + Helper class of type `QueryResults[models.TestEnumOverride]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(self._conn, sql, _decode_hook, *ids) + + def count_enum_override_by_moods(self, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Args: + moods: collections.abc.Sequence[str]. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with self._conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/pydantic/classes/queries_field_namings.py b/test/driver_pymysql/pydantic/classes/queries_field_namings.py new file mode 100644 index 00000000..7416b7f1 --- /dev/null +++ b/test/driver_pymysql/pydantic/classes/queries_field_namings.py @@ -0,0 +1,141 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_field_namings.sql +"""Module containing queries from file queries_field_namings.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "GetJoinedFieldNamingsRow", + "QueriesFieldNamings", +) + +import pydantic +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.pydantic.classes import models + + +class GetJoinedFieldNamingsRow(pydantic.BaseModel): + """Model representing GetJoinedFieldNamingsRow. + + Attributes: + outputs: str + outputs_2: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + outputs: str + outputs_2: str + + +GET_FIELD_NAMING: typing.Final[str] = """-- name: GetFieldNaming :one +SELECT id, outputs +FROM test_field_namings +WHERE id = %s LIMIT 1 +""" + +GET_JOINED_FIELD_NAMINGS: typing.Final[str] = """-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = %s LIMIT 1 +""" + +SET_FIELD_NAMING_OUTPUTS: typing.Final[str] = """-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = %s +WHERE id = %s +""" + + +class QueriesFieldNamings: + """Queries from file queries_field_namings.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def get_field_naming(self, *, id_: int) -> models.TestFieldNaming | None: + """Fetch one from the db using the SQL query with `name: GetFieldNaming :one`. + + ```sql + SELECT id, outputs + FROM test_field_namings + WHERE id = %s LIMIT 1 + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestFieldNaming` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_FIELD_NAMING, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestFieldNaming(id_=row[0], outputs=row[1]) + + def get_joined_field_namings(self, *, id_: int) -> GetJoinedFieldNamingsRow | None: + """Fetch one from the db using the SQL query with `name: GetJoinedFieldNamings :one`. + + ```sql + SELECT a.outputs, b.outputs + FROM test_field_namings a + JOIN test_field_namings b ON a.id = b.id + WHERE a.id = %s LIMIT 1 + ``` + + Args: + id_: int. + + Returns: + Result of type `GetJoinedFieldNamingsRow` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_JOINED_FIELD_NAMINGS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return GetJoinedFieldNamingsRow(outputs=row[0], outputs_2=row[1]) + + def set_field_naming_outputs(self, *, outputs: str, id_: int) -> None: + """Execute SQL query with `name: SetFieldNamingOutputs :exec`. + + ```sql + UPDATE test_field_namings + SET outputs = %s + WHERE id = %s + ``` + + Args: + outputs: str. + id_: int. + """ + with self._conn.cursor() as cur: + cur.execute(SET_FIELD_NAMING_OUTPUTS, (outputs, id_)) diff --git a/test/driver_pymysql/pydantic/classes/queries_invalid_identifiers.py b/test/driver_pymysql/pydantic/classes/queries_invalid_identifiers.py new file mode 100644 index 00000000..a652c28b --- /dev/null +++ b/test/driver_pymysql/pydantic/classes/queries_invalid_identifiers.py @@ -0,0 +1,128 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_invalid_identifiers.sql +"""Module containing queries from file queries_invalid_identifiers.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ("QueriesInvalidIdentifiers",) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.pydantic.classes import models + + +INSERT_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) +""" + +GET_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: GetInvalidIdentifiers :one +SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s +""" + +INSERT_THIRD_PARTY_STAT: typing.Final[str] = """-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) +""" + +GET_THIRD_PARTY_STAT: typing.Final[str] = """-- name: GetThirdPartyStat :one +SELECT id, total FROM `3rd_party_stats` WHERE id = %s +""" + + +class QueriesInvalidIdentifiers: + """Queries from file queries_invalid_identifiers.sql.""" + + __slots__ = ("_conn",) + + def __init__(self, conn: pymysql.Connection) -> None: + """Initialize the instance using the connection. + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + """ + self._conn = conn + + @property + def conn(self) -> pymysql.Connection: + """Connection object used to make queries. + + Returns: + Connection object of type `pymysql.Connection` used to make queries. + """ + return self._conn + + def insert_invalid_identifiers(self, *, id_: int, column_3p_: str | None, new_notes: str) -> None: + """Execute SQL query with `name: InsertInvalidIdentifiers :exec`. + + ```sql + INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) + ``` + + Args: + id_: int. + column_3p_: str | None. + new_notes: str. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_INVALID_IDENTIFIERS, (id_, column_3p_, new_notes)) + + def get_invalid_identifiers(self, *, id_: int) -> models.TestInvalidIdentifier | None: + """Fetch one from the db using the SQL query with `name: GetInvalidIdentifiers :one`. + + ```sql + SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.TestInvalidIdentifier` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_INVALID_IDENTIFIERS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInvalidIdentifier(id_=row[0], column_3p_=row[1], new_notes=row[2], column__pct=row[3]) + + def insert_third_party_stat(self, *, id_: int, total: int) -> None: + """Execute SQL query with `name: InsertThirdPartyStat :exec`. + + ```sql + INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) + ``` + + Args: + id_: int. + total: int. + """ + with self._conn.cursor() as cur: + cur.execute(INSERT_THIRD_PARTY_STAT, (id_, total)) + + def get_third_party_stat(self, *, id_: int) -> models.Model3RdPartyStat | None: + """Fetch one from the db using the SQL query with `name: GetThirdPartyStat :one`. + + ```sql + SELECT id, total FROM `3rd_party_stats` WHERE id = %s + ``` + + Args: + id_: int. + + Returns: + Result of type `models.Model3RdPartyStat` fetched from the db. Will be `None` if not found. + """ + with self._conn.cursor() as cur: + cur.execute(GET_THIRD_PARTY_STAT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.Model3RdPartyStat(id_=row[0], total=row[1]) diff --git a/test/driver_pymysql/pydantic/functions/__init__.py b/test/driver_pymysql/pydantic/functions/__init__.py new file mode 100644 index 00000000..9d3275af --- /dev/null +++ b/test/driver_pymysql/pydantic/functions/__init__.py @@ -0,0 +1,5 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_pymysql/pydantic/functions/enums.py b/test/driver_pymysql/pydantic/functions/enums.py new file mode 100644 index 00000000..80b8677a --- /dev/null +++ b/test/driver_pymysql/pydantic/functions/enums.py @@ -0,0 +1,65 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing enums.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "TestEnumOverrideMoodTest", + "TestInnerMysqlTypesMood", + "TestInnerMysqlTypesTag", + "TestMysqlTypesMood", + "TestMysqlTypesTag", +) + +import enum +import typing + +if typing.TYPE_CHECKING: + import collections.abc + + +class TestEnumOverrideMoodTest(enum.StrEnum): + """Enum representing TestEnumOverrideMoodTest.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + + +class TestInnerMysqlTypesMood(enum.StrEnum): + """Enum representing TestInnerMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestInnerMysqlTypesTag(enum.StrEnum): + """Enum representing TestInnerMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" + + +class TestMysqlTypesMood(enum.StrEnum): + """Enum representing TestMysqlTypesMood.""" + + SAD = "sad" + OK = "ok" + HAPPY = "happy" + VALUE_24H = "24h" + VALUE__HIDDEN = "_hidden" + + +class TestMysqlTypesTag(enum.StrEnum): + """Enum representing TestMysqlTypesTag.""" + + ALPHA = "alpha" + BETA = "beta" + GAMMA = "gamma" diff --git a/test/driver_pymysql/pydantic/functions/models.py b/test/driver_pymysql/pydantic/functions/models.py new file mode 100644 index 00000000..c261eec8 --- /dev/null +++ b/test/driver_pymysql/pydantic/functions/models.py @@ -0,0 +1,314 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +"""Module containing models.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "Model3RdPartyStat", + "TestCaseSensitivity", + "TestEnumOverride", + "TestFieldNaming", + "TestInnerMysqlType", + "TestInvalidIdentifier", + "TestMysqlType", + "TestReservedArg", + "TestTypeOverride", +) + +from collections import UserString +import datetime +import decimal +import pydantic +import typing + +if typing.TYPE_CHECKING: + import collections.abc + +from test.driver_pymysql.pydantic.functions import enums + + +class Model3RdPartyStat(pydantic.BaseModel): + """Model representing Model3RdPartyStat. + + Attributes: + id_: int + total: int + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + total: int + + +class TestCaseSensitivity(pydantic.BaseModel): + """Model representing TestCaseSensitivity. + + Attributes: + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + upper_dt: datetime.datetime + prec_dec: decimal.Decimal + + +class TestEnumOverride(pydantic.BaseModel): + """Model representing TestEnumOverride. + + Attributes: + id_: int + mood_test: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + mood_test: str + + +class TestFieldNaming(pydantic.BaseModel): + """Model representing TestFieldNaming. + + Attributes: + id_: int + outputs: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + outputs: str + + +class TestInnerMysqlType(pydantic.BaseModel): + """Model representing TestInnerMysqlType. + + Attributes: + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + table_id: int + int_test: int | None + integer_test: int | None + mediumint_test: int | None + smallint_test: int | None + tinyint_test: int | None + bigint_test: int | None + int_unsigned_test: int | None + bigint_unsigned_test: int | None + year_test: int | None + tinyint1_test: bool | None + bool_test: bool | None + boolean_test: bool | None + float_test: float | None + double_test: float | None + double_precision_test: float | None + real_test: float | None + decimal_test: decimal.Decimal | None + numeric_test: decimal.Decimal | None + char_test: str | None + varchar_test: str | None + tinytext_test: str | None + text_test: str | None + mediumtext_test: str | None + longtext_test: str | None + binary_test: memoryview | None + varbinary_test: memoryview | None + tinyblob_test: memoryview | None + blob_test: memoryview | None + mediumblob_test: memoryview | None + longblob_test: memoryview | None + bit_test: memoryview | None + date_test: datetime.date | None + datetime_test: datetime.datetime | None + datetime6_test: datetime.datetime | None + timestamp_test: datetime.datetime | None + time_test: datetime.timedelta | None + json_test: str | None + mood: enums.TestInnerMysqlTypesMood | None + tag: enums.TestInnerMysqlTypesTag | None + + +class TestInvalidIdentifier(pydantic.BaseModel): + """Model representing TestInvalidIdentifier. + + Attributes: + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + column_3p_: str | None + new_notes: str + column__pct: str | None + + +class TestMysqlType(pydantic.BaseModel): + """Model representing TestMysqlType. + + Attributes: + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + int_test: int + integer_test: int + mediumint_test: int + smallint_test: int + tinyint_test: int + bigint_test: int + int_unsigned_test: int + bigint_unsigned_test: int + year_test: int + tinyint1_test: bool + bool_test: bool + boolean_test: bool + float_test: float + double_test: float + double_precision_test: float + real_test: float + decimal_test: decimal.Decimal + numeric_test: decimal.Decimal + char_test: str + varchar_test: str + tinytext_test: str + text_test: str + mediumtext_test: str + longtext_test: str + binary_test: memoryview + varbinary_test: memoryview + tinyblob_test: memoryview + blob_test: memoryview + mediumblob_test: memoryview + longblob_test: memoryview + bit_test: memoryview + date_test: datetime.date + datetime_test: datetime.datetime + datetime6_test: datetime.datetime + timestamp_test: datetime.datetime + time_test: datetime.timedelta + json_test: str + mood: enums.TestMysqlTypesMood + tag: enums.TestMysqlTypesTag + + +class TestReservedArg(pydantic.BaseModel): + """Model representing TestReservedArg. + + Attributes: + id_: int + conn: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + conn: str + + +class TestTypeOverride(pydantic.BaseModel): + """Model representing TestTypeOverride. + + Attributes: + id_: int + text_test: UserString | None + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + id_: int + text_test: UserString | None diff --git a/test/driver_pymysql/pydantic/functions/queries.py b/test/driver_pymysql/pydantic/functions/queries.py new file mode 100644 index 00000000..1d3c6bd3 --- /dev/null +++ b/test/driver_pymysql/pydantic/functions/queries.py @@ -0,0 +1,1553 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries.sql +"""Module containing queries from file queries.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "all_mysql_types_cursor", + "count_mysql_types", + "delete_one_mysql_type", + "get_exec_last_id_name", + "get_many_bool", + "get_many_date", + "get_many_decimal", + "get_many_inner_mysql_type", + "get_many_mood", + "get_many_mysql_type", + "get_many_nullable_inner_mysql_type", + "get_many_time", + "get_one_bit", + "get_one_blob", + "get_one_bool", + "get_one_date", + "get_one_datetime", + "get_one_decimal", + "get_one_inner_mysql_type", + "get_one_json", + "get_one_mood", + "get_one_mysql_type", + "get_one_tag", + "get_one_time", + "get_one_year", + "get_reserved_arg", + "get_type_override", + "insert_exec_last_id", + "insert_one_inner_mysql_type", + "insert_one_mysql_type", + "insert_reserved_arg", + "insert_type_override", + "list_months", + "touch_exec_last_id", + "update_varchar_test", +) + +from collections import UserString +import operator +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | decimal.Decimal | datetime.date | datetime.time | datetime.datetime | datetime.timedelta | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.pydantic.functions import enums +from test.driver_pymysql.pydantic.functions import models + + +INSERT_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +INSERT_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) +""" + +GET_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneMysqlType :one +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetOneInnerMysqlType :one +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyMysqlType :many +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s +""" + +GET_MANY_NULLABLE_INNER_MYSQL_TYPE: typing.Final[str] = """-- name: GetManyNullableInnerMysqlType :many +SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s +""" + +GET_ONE_DATE: typing.Final[str] = """-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_ONE_DATETIME: typing.Final[str] = """-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s +""" + +GET_ONE_TIME: typing.Final[str] = """-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_ONE_BOOL: typing.Final[str] = """-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_ONE_DECIMAL: typing.Final[str] = """-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_ONE_BLOB: typing.Final[str] = """-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s +""" + +GET_ONE_BIT: typing.Final[str] = """-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_YEAR: typing.Final[str] = """-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_JSON: typing.Final[str] = """-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = %s +""" + +GET_ONE_MOOD: typing.Final[str] = """-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s +""" + +GET_ONE_TAG: typing.Final[str] = """-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = %s +""" + +GET_MANY_DATE: typing.Final[str] = """-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s +""" + +GET_MANY_TIME: typing.Final[str] = """-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s +""" + +GET_MANY_BOOL: typing.Final[str] = """-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s +""" + +GET_MANY_DECIMAL: typing.Final[str] = """-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s +""" + +GET_MANY_MOOD: typing.Final[str] = """-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id +""" + +LIST_MONTHS: typing.Final[str] = """-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id +""" + +COUNT_MYSQL_TYPES: typing.Final[str] = """-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types +""" + +UPDATE_VARCHAR_TEST: typing.Final[str] = """-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s +""" + +DELETE_ONE_MYSQL_TYPE: typing.Final[str] = """-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = %s +""" + +ALL_MYSQL_TYPES_CURSOR: typing.Final[str] = """-- name: AllMysqlTypesCursor :execresult +SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types +""" + +INSERT_EXEC_LAST_ID: typing.Final[str] = """-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (%s) +""" + +GET_EXEC_LAST_ID_NAME: typing.Final[str] = """-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = %s +""" + +INSERT_TYPE_OVERRIDE: typing.Final[str] = """-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) +""" + +GET_TYPE_OVERRIDE: typing.Final[str] = """-- name: GetTypeOverride :one +SELECT id, text_test FROM test_type_override WHERE id = %s +""" + +GET_RESERVED_ARG: typing.Final[str] = """-- name: GetReservedArg :one +SELECT id, conn FROM test_reserved_args WHERE conn = %s +""" + +INSERT_RESERVED_ARG: typing.Final[str] = """-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) +""" + +TOUCH_EXEC_LAST_ID: typing.Final[str] = """-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = %s WHERE id = %s +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_one_mysql_type( + conn: pymysql.Connection, + *, + id_: int, + int_test: int, + integer_test: int, + mediumint_test: int, + smallint_test: int, + tinyint_test: int, + bigint_test: int, + int_unsigned_test: int, + bigint_unsigned_test: int, + year_test: int, + tinyint1_test: bool, + bool_test: bool, + boolean_test: bool, + float_test: float, + double_test: float, + double_precision_test: float, + real_test: float, + decimal_test: decimal.Decimal, + numeric_test: decimal.Decimal, + char_test: str, + varchar_test: str, + tinytext_test: str, + text_test: str, + mediumtext_test: str, + longtext_test: str, + binary_test: memoryview, + varbinary_test: memoryview, + tinyblob_test: memoryview, + blob_test: memoryview, + mediumblob_test: memoryview, + longblob_test: memoryview, + bit_test: memoryview, + date_test: datetime.date, + datetime_test: datetime.datetime, + datetime6_test: datetime.datetime, + timestamp_test: datetime.datetime, + time_test: datetime.timedelta, + json_test: str, + mood: enums.TestMysqlTypesMood, + tag: enums.TestMysqlTypesTag, +) -> None: + """Execute SQL query with `name: InsertOneMysqlType :exec`. + + ```sql + INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + int_test: int. + integer_test: int. + mediumint_test: int. + smallint_test: int. + tinyint_test: int. + bigint_test: int. + int_unsigned_test: int. + bigint_unsigned_test: int. + year_test: int. + tinyint1_test: bool. + bool_test: bool. + boolean_test: bool. + float_test: float. + double_test: float. + double_precision_test: float. + real_test: float. + decimal_test: decimal.Decimal. + numeric_test: decimal.Decimal. + char_test: str. + varchar_test: str. + tinytext_test: str. + text_test: str. + mediumtext_test: str. + longtext_test: str. + binary_test: memoryview. + varbinary_test: memoryview. + tinyblob_test: memoryview. + blob_test: memoryview. + mediumblob_test: memoryview. + longblob_test: memoryview. + bit_test: memoryview. + date_test: datetime.date. + datetime_test: datetime.datetime. + datetime6_test: datetime.datetime. + timestamp_test: datetime.datetime. + time_test: datetime.timedelta. + json_test: str. + mood: enums.TestMysqlTypesMood. + tag: enums.TestMysqlTypesTag. + """ + with conn.cursor() as cur: + sql_args = ( + id_, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test), + bytes(varbinary_test), + bytes(tinyblob_test), + bytes(blob_test), + bytes(mediumblob_test), + bytes(longblob_test), + bytes(bit_test), + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_MYSQL_TYPE, sql_args) + + +def insert_one_inner_mysql_type( + conn: pymysql.Connection, + *, + table_id: int, + int_test: int | None, + integer_test: int | None, + mediumint_test: int | None, + smallint_test: int | None, + tinyint_test: int | None, + bigint_test: int | None, + int_unsigned_test: int | None, + bigint_unsigned_test: int | None, + year_test: int | None, + tinyint1_test: bool | None, + bool_test: bool | None, + boolean_test: bool | None, + float_test: float | None, + double_test: float | None, + double_precision_test: float | None, + real_test: float | None, + decimal_test: decimal.Decimal | None, + numeric_test: decimal.Decimal | None, + char_test: str | None, + varchar_test: str | None, + tinytext_test: str | None, + text_test: str | None, + mediumtext_test: str | None, + longtext_test: str | None, + binary_test: memoryview | None, + varbinary_test: memoryview | None, + tinyblob_test: memoryview | None, + blob_test: memoryview | None, + mediumblob_test: memoryview | None, + longblob_test: memoryview | None, + bit_test: memoryview | None, + date_test: datetime.date | None, + datetime_test: datetime.datetime | None, + datetime6_test: datetime.datetime | None, + timestamp_test: datetime.datetime | None, + time_test: datetime.timedelta | None, + json_test: str | None, + mood: enums.TestInnerMysqlTypesMood | None, + tag: enums.TestInnerMysqlTypesTag | None, +) -> None: + """Execute SQL query with `name: InsertOneInnerMysqlType :exec`. + + ```sql + INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag + ) VALUES ( + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s, + %s, %s, %s, %s, + %s, %s, + %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, %s, %s, + %s, %s, %s, %s, %s, + %s, %s, %s + ) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + table_id: int. + int_test: int | None. + integer_test: int | None. + mediumint_test: int | None. + smallint_test: int | None. + tinyint_test: int | None. + bigint_test: int | None. + int_unsigned_test: int | None. + bigint_unsigned_test: int | None. + year_test: int | None. + tinyint1_test: bool | None. + bool_test: bool | None. + boolean_test: bool | None. + float_test: float | None. + double_test: float | None. + double_precision_test: float | None. + real_test: float | None. + decimal_test: decimal.Decimal | None. + numeric_test: decimal.Decimal | None. + char_test: str | None. + varchar_test: str | None. + tinytext_test: str | None. + text_test: str | None. + mediumtext_test: str | None. + longtext_test: str | None. + binary_test: memoryview | None. + varbinary_test: memoryview | None. + tinyblob_test: memoryview | None. + blob_test: memoryview | None. + mediumblob_test: memoryview | None. + longblob_test: memoryview | None. + bit_test: memoryview | None. + date_test: datetime.date | None. + datetime_test: datetime.datetime | None. + datetime6_test: datetime.datetime | None. + timestamp_test: datetime.datetime | None. + time_test: datetime.timedelta | None. + json_test: str | None. + mood: enums.TestInnerMysqlTypesMood | None. + tag: enums.TestInnerMysqlTypesTag | None. + """ + with conn.cursor() as cur: + sql_args = ( + table_id, + int_test, + integer_test, + mediumint_test, + smallint_test, + tinyint_test, + bigint_test, + int_unsigned_test, + bigint_unsigned_test, + year_test, + tinyint1_test, + bool_test, + boolean_test, + float_test, + double_test, + double_precision_test, + real_test, + decimal_test, + numeric_test, + char_test, + varchar_test, + tinytext_test, + text_test, + mediumtext_test, + longtext_test, + bytes(binary_test) if binary_test is not None else None, + bytes(varbinary_test) if varbinary_test is not None else None, + bytes(tinyblob_test) if tinyblob_test is not None else None, + bytes(blob_test) if blob_test is not None else None, + bytes(mediumblob_test) if mediumblob_test is not None else None, + bytes(longblob_test) if longblob_test is not None else None, + bytes(bit_test) if bit_test is not None else None, + date_test, + datetime_test, + datetime6_test, + timestamp_test, + time_test, + json_test, + mood, + tag, + ) + cur.execute(INSERT_ONE_INNER_MYSQL_TYPE, sql_args) + + +def get_one_mysql_type(conn: pymysql.Connection, *, id_: int) -> models.TestMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneMysqlType :one`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestMysqlType` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_MYSQL_TYPE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + +def get_one_inner_mysql_type(conn: pymysql.Connection, *, table_id: int) -> models.TestInnerMysqlType | None: + """Fetch one from the db using the SQL query with `name: GetOneInnerMysqlType :one`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + table_id: int. + + Returns: + Result of type `models.TestInnerMysqlType` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_INNER_MYSQL_TYPE, (table_id,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + +def get_many_mysql_type(conn: pymysql.Connection, *, id_: int) -> QueryResults[models.TestMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyMysqlType :many`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Helper class of type `QueryResults[models.TestMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestMysqlType: + return models.TestMysqlType( + id_=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]), + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]), + bool_test=bool(row[11]), + boolean_test=bool(row[12]), + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]), + varbinary_test=memoryview(row[26]), + tinyblob_test=memoryview(row[27]), + blob_test=memoryview(row[28]), + mediumblob_test=memoryview(row[29]), + longblob_test=memoryview(row[30]), + bit_test=memoryview(row[31]), + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestMysqlTypesMood(row[38]), + tag=enums.TestMysqlTypesTag(row[39]), + ) + + return QueryResults(conn, GET_MANY_MYSQL_TYPE, _decode_hook, id_) + + +def get_many_inner_mysql_type(conn: pymysql.Connection, *, table_id: int) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + table_id: int. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_INNER_MYSQL_TYPE, _decode_hook, table_id) + + +def get_many_nullable_inner_mysql_type(conn: pymysql.Connection, *, table_id: int, int_test: int | None) -> QueryResults[models.TestInnerMysqlType]: + """Fetch many from the db using the SQL query with `name: GetManyNullableInnerMysqlType :many`. + + ```sql + SELECT table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_inner_mysql_types WHERE table_id = %s AND int_test <=> %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + table_id: int. + int_test: int | None. + + Returns: + Helper class of type `QueryResults[models.TestInnerMysqlType]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=row[0], + int_test=row[1], + integer_test=row[2], + mediumint_test=row[3], + smallint_test=row[4], + tinyint_test=int(row[5]) if row[5] is not None else None, + bigint_test=row[6], + int_unsigned_test=row[7], + bigint_unsigned_test=row[8], + year_test=row[9], + tinyint1_test=bool(row[10]) if row[10] is not None else None, + bool_test=bool(row[11]) if row[11] is not None else None, + boolean_test=bool(row[12]) if row[12] is not None else None, + float_test=row[13], + double_test=row[14], + double_precision_test=row[15], + real_test=row[16], + decimal_test=row[17], + numeric_test=row[18], + char_test=row[19], + varchar_test=row[20], + tinytext_test=row[21], + text_test=row[22], + mediumtext_test=row[23], + longtext_test=row[24], + binary_test=memoryview(row[25]) if row[25] is not None else None, + varbinary_test=memoryview(row[26]) if row[26] is not None else None, + tinyblob_test=memoryview(row[27]) if row[27] is not None else None, + blob_test=memoryview(row[28]) if row[28] is not None else None, + mediumblob_test=memoryview(row[29]) if row[29] is not None else None, + longblob_test=memoryview(row[30]) if row[30] is not None else None, + bit_test=memoryview(row[31]) if row[31] is not None else None, + date_test=row[32], + datetime_test=row[33], + datetime6_test=row[34], + timestamp_test=row[35], + time_test=row[36], + json_test=row[37], + mood=enums.TestInnerMysqlTypesMood(row[38]) if row[38] is not None else None, + tag=enums.TestInnerMysqlTypesTag(row[39]) if row[39] is not None else None, + ) + + return QueryResults(conn, GET_MANY_NULLABLE_INNER_MYSQL_TYPE, _decode_hook, table_id, int_test) + + +def get_one_date(conn: pymysql.Connection, *, id_: int, date_test: datetime.date) -> datetime.date | None: + """Fetch one from the db using the SQL query with `name: GetOneDate :one`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + date_test: datetime.date. + + Returns: + Result of type `datetime.date` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DATE, (id_, date_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_datetime(conn: pymysql.Connection, *, id_: int, datetime_test: datetime.datetime) -> datetime.datetime | None: + """Fetch one from the db using the SQL query with `name: GetOneDatetime :one`. + + ```sql + SELECT datetime_test FROM test_mysql_types WHERE id = %s AND datetime_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + datetime_test: datetime.datetime. + + Returns: + Result of type `datetime.datetime` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DATETIME, (id_, datetime_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_time(conn: pymysql.Connection, *, id_: int, time_test: datetime.timedelta) -> datetime.timedelta | None: + """Fetch one from the db using the SQL query with `name: GetOneTime :one`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + time_test: datetime.timedelta. + + Returns: + Result of type `datetime.timedelta` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_TIME, (id_, time_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_bool(conn: pymysql.Connection, *, id_: int, tinyint1_test: bool) -> bool | None: + """Fetch one from the db using the SQL query with `name: GetOneBool :one`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + tinyint1_test: bool. + + Returns: + Result of type `bool` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BOOL, (id_, tinyint1_test)) + row = cur.fetchone() + if row is None: + return None + return bool(row[0]) + + +def get_one_decimal(conn: pymysql.Connection, *, id_: int, decimal_test: decimal.Decimal) -> decimal.Decimal | None: + """Fetch one from the db using the SQL query with `name: GetOneDecimal :one`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Result of type `decimal.Decimal` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_DECIMAL, (id_, decimal_test)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_blob(conn: pymysql.Connection, *, id_: int, blob_test: memoryview) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBlob :one`. + + ```sql + SELECT blob_test FROM test_mysql_types WHERE id = %s AND blob_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + blob_test: memoryview. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BLOB, (id_, bytes(blob_test))) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +def get_one_bit(conn: pymysql.Connection, *, id_: int) -> memoryview | None: + """Fetch one from the db using the SQL query with `name: GetOneBit :one`. + + ```sql + SELECT bit_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `memoryview` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_BIT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return memoryview(row[0]) + + +def get_one_year(conn: pymysql.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: GetOneYear :one`. + + ```sql + SELECT year_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_YEAR, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_json(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetOneJson :one`. + + ```sql + SELECT json_test FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_JSON, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def get_one_mood(conn: pymysql.Connection, *, id_: int, mood: enums.TestMysqlTypesMood) -> enums.TestMysqlTypesMood | None: + """Fetch one from the db using the SQL query with `name: GetOneMood :one`. + + ```sql + SELECT mood FROM test_mysql_types WHERE id = %s AND mood = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + mood: enums.TestMysqlTypesMood. + + Returns: + Result of type `enums.TestMysqlTypesMood` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_MOOD, (id_, mood)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesMood(row[0]) + + +def get_one_tag(conn: pymysql.Connection, *, id_: int) -> enums.TestMysqlTypesTag | None: + """Fetch one from the db using the SQL query with `name: GetOneTag :one`. + + ```sql + SELECT tag FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `enums.TestMysqlTypesTag` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ONE_TAG, (id_,)) + row = cur.fetchone() + if row is None: + return None + return enums.TestMysqlTypesTag(row[0]) + + +def get_many_date(conn: pymysql.Connection, *, id_: int, date_test: datetime.date) -> QueryResults[datetime.date]: + """Fetch many from the db using the SQL query with `name: GetManyDate :many`. + + ```sql + SELECT date_test FROM test_mysql_types WHERE id = %s AND date_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + date_test: datetime.date. + + Returns: + Helper class of type `QueryResults[datetime.date]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DATE, operator.itemgetter(0), id_, date_test) + + +def get_many_time(conn: pymysql.Connection, *, id_: int, time_test: datetime.timedelta) -> QueryResults[datetime.timedelta]: + """Fetch many from the db using the SQL query with `name: GetManyTime :many`. + + ```sql + SELECT time_test FROM test_mysql_types WHERE id = %s AND time_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + time_test: datetime.timedelta. + + Returns: + Helper class of type `QueryResults[datetime.timedelta]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_TIME, operator.itemgetter(0), id_, time_test) + + +def get_many_bool(conn: pymysql.Connection, *, id_: int, tinyint1_test: bool) -> QueryResults[bool]: + """Fetch many from the db using the SQL query with `name: GetManyBool :many`. + + ```sql + SELECT tinyint1_test FROM test_mysql_types WHERE id = %s AND tinyint1_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + tinyint1_test: bool. + + Returns: + Helper class of type `QueryResults[bool]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> bool: + return bool(row[0]) + + return QueryResults(conn, GET_MANY_BOOL, _decode_hook, id_, tinyint1_test) + + +def get_many_decimal(conn: pymysql.Connection, *, id_: int, decimal_test: decimal.Decimal) -> QueryResults[decimal.Decimal]: + """Fetch many from the db using the SQL query with `name: GetManyDecimal :many`. + + ```sql + SELECT decimal_test FROM test_mysql_types WHERE id = %s AND decimal_test = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + decimal_test: decimal.Decimal. + + Returns: + Helper class of type `QueryResults[decimal.Decimal]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, GET_MANY_DECIMAL, operator.itemgetter(0), id_, decimal_test) + + +def get_many_mood(conn: pymysql.Connection, *, mood: enums.TestMysqlTypesMood) -> QueryResults[enums.TestMysqlTypesMood]: + """Fetch many from the db using the SQL query with `name: GetManyMood :many`. + + ```sql + SELECT mood FROM test_mysql_types WHERE mood = %s ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + mood: enums.TestMysqlTypesMood. + + Returns: + Helper class of type `QueryResults[enums.TestMysqlTypesMood]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> enums.TestMysqlTypesMood: + return enums.TestMysqlTypesMood(row[0]) + + return QueryResults(conn, GET_MANY_MOOD, _decode_hook, mood) + + +def list_months(conn: pymysql.Connection) -> QueryResults[str]: + """Fetch many from the db using the SQL query with `name: ListMonths :many`. + + ```sql + SELECT DATE_FORMAT(datetime_test, '%%Y-%%m') AS month FROM test_mysql_types ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + Helper class of type `QueryResults[str]` that allows both iteration and normal fetching of data from the db. + """ + return QueryResults(conn, LIST_MONTHS, operator.itemgetter(0)) + + +def count_mysql_types(conn: pymysql.Connection) -> int | None: + """Fetch one from the db using the SQL query with `name: CountMysqlTypes :one`. + + ```sql + SELECT count(*) FROM test_mysql_types + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(COUNT_MYSQL_TYPES) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def update_varchar_test(conn: pymysql.Connection, *, varchar_test: str, id_: int) -> int: + """Execute SQL query with `name: UpdateVarcharTest :execrows` and return the number of affected rows. + + ```sql + UPDATE test_mysql_types SET varchar_test = %s WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + varchar_test: str. + id_: int. + + Returns: + The number (`int`) of affected rows. This will be 0 for queries like `CREATE TABLE`. + """ + with conn.cursor() as cur: + return cur.execute(UPDATE_VARCHAR_TEST, (varchar_test, id_)) + + +def delete_one_mysql_type(conn: pymysql.Connection, *, id_: int) -> None: + """Execute SQL query with `name: DeleteOneMysqlType :exec`. + + ```sql + DELETE FROM test_mysql_types WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + """ + with conn.cursor() as cur: + cur.execute(DELETE_ONE_MYSQL_TYPE, (id_,)) + + +def all_mysql_types_cursor(conn: pymysql.Connection) -> pymysql.cursors.Cursor: + """Execute and return the result of SQL query with `name: AllMysqlTypesCursor :execresult`. + + ```sql + SELECT id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, int_unsigned_test, bigint_unsigned_test, year_test, tinyint1_test, bool_test, boolean_test, float_test, double_test, double_precision_test, real_test, decimal_test, numeric_test, char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, date_test, datetime_test, datetime6_test, timestamp_test, time_test, json_test, mood, tag FROM test_mysql_types + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + + Returns: + The result of type `pymysql.cursors.Cursor` returned when executing the query. + """ + cur = conn.cursor() + cur.execute(ALL_MYSQL_TYPES_CURSOR) + return cur + + +def insert_exec_last_id(conn: pymysql.Connection, *, name: str) -> int | None: + """Execute SQL query with `name: InsertExecLastId :execlastid` and return the id of the last affected row. + + ```sql + INSERT INTO test_execlastid (name) VALUES (%s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + name: str. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + with conn.cursor() as cur: + cur.execute(INSERT_EXEC_LAST_ID, (name,)) + return cur.lastrowid or None + + +def get_exec_last_id_name(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetExecLastIdName :one`. + + ```sql + SELECT name FROM test_execlastid WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_EXEC_LAST_ID_NAME, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] + + +def insert_type_override(conn: pymysql.Connection, *, id_: int, text_test: UserString | None) -> None: + """Execute SQL query with `name: InsertTypeOverride :exec`. + + ```sql + INSERT INTO test_type_override (id, text_test) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + text_test: UserString | None. + """ + with conn.cursor() as cur: + cur.execute(INSERT_TYPE_OVERRIDE, (id_, str(text_test) if text_test is not None else None)) + + +def get_type_override(conn: pymysql.Connection, *, id_: int) -> models.TestTypeOverride | None: + """Fetch one from the db using the SQL query with `name: GetTypeOverride :one`. + + ```sql + SELECT id, text_test FROM test_type_override WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestTypeOverride` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_TYPE_OVERRIDE, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestTypeOverride(id_=row[0], text_test=UserString(row[1]) if row[1] is not None else None) + + +def get_reserved_arg(conn: pymysql.Connection, *, conn_2: str) -> models.TestReservedArg | None: + """Fetch one from the db using the SQL query with `name: GetReservedArg :one`. + + ```sql + SELECT id, conn FROM test_reserved_args WHERE conn = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + conn_2: str. + + Returns: + Result of type `models.TestReservedArg` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_RESERVED_ARG, (conn_2,)) + row = cur.fetchone() + if row is None: + return None + return models.TestReservedArg(id_=row[0], conn=row[1]) + + +def insert_reserved_arg(conn: pymysql.Connection, *, id_: int, conn_2: str) -> None: + """Execute SQL query with `name: InsertReservedArg :exec`. + + ```sql + INSERT INTO test_reserved_args (id, conn) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + conn_2: str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_RESERVED_ARG, (id_, conn_2)) + + +def touch_exec_last_id(conn: pymysql.Connection, *, name: str, id_: int) -> int | None: + """Execute SQL query with `name: TouchExecLastId :execlastid` and return the id of the last affected row. + + ```sql + UPDATE test_execlastid SET name = %s WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + name: str. + id_: int. + + Returns: + The id (`int`) of the last affected row. Will be `None` if no rows are affected. + """ + with conn.cursor() as cur: + cur.execute(TOUCH_EXEC_LAST_ID, (name, id_)) + return cur.lastrowid or None diff --git a/test/driver_pymysql/pydantic/functions/queries_case.py b/test/driver_pymysql/pydantic/functions/queries_case.py new file mode 100644 index 00000000..1f844191 --- /dev/null +++ b/test/driver_pymysql/pydantic/functions/queries_case.py @@ -0,0 +1,101 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_case.sql +"""Module containing queries from file queries_case.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "count_case_rows", + "get_case_row", + "insert_case_row", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import datetime + import decimal + import pymysql + +from test.driver_pymysql.pydantic.functions import models + + +INSERT_CASE_ROW: typing.Final[str] = """-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) +""" + +GET_CASE_ROW: typing.Final[str] = """-- name: GetCaseRow :one +SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s +""" + +COUNT_CASE_ROWS: typing.Final[str] = """-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ +""" + + +def insert_case_row(conn: pymysql.Connection, *, id_: int, upper_dt: datetime.datetime, prec_dec: decimal.Decimal) -> None: + """Execute SQL query with `name: InsertCaseRow :exec`. + + ```sql + INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (%s, %s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + upper_dt: datetime.datetime. + prec_dec: decimal.Decimal. + """ + with conn.cursor() as cur: + cur.execute(INSERT_CASE_ROW, (id_, upper_dt, prec_dec)) + + +def get_case_row(conn: pymysql.Connection, *, id_: int) -> models.TestCaseSensitivity | None: + """Fetch one from the db using the SQL query with `name: GetCaseRow :one`. + + ```sql + SELECT id, upper_dt, prec_dec FROM test_case_sensitivity WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestCaseSensitivity` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_CASE_ROW, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestCaseSensitivity(id_=row[0], upper_dt=row[1], prec_dec=row[2]) + + +def count_case_rows(conn: pymysql.Connection, *, id_: int) -> int | None: + """Fetch one from the db using the SQL query with `name: CountCaseRows :one`. + + ```sql + SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= %s */ + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(COUNT_CASE_ROWS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/pydantic/functions/queries_enum_override.py b/test/driver_pymysql/pydantic/functions/queries_enum_override.py new file mode 100644 index 00000000..1bdb3c2f --- /dev/null +++ b/test/driver_pymysql/pydantic/functions/queries_enum_override.py @@ -0,0 +1,203 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_enum_override.sql +"""Module containing queries from file queries_enum_override.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "QueryResults", + "count_enum_override_by_moods", + "get_enum_override_mood", + "insert_enum_override", + "list_enum_override_by_ids", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + import pymysql.cursors + + type QueryResultsArgsType = int | float | str | memoryview | bytes | collections.abc.Sequence[QueryResultsArgsType] | None + +from test.driver_pymysql.pydantic.functions import enums +from test.driver_pymysql.pydantic.functions import models + + +INSERT_ENUM_OVERRIDE: typing.Final[str] = """-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) +""" + +GET_ENUM_OVERRIDE_MOOD: typing.Final[str] = """-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = %s +""" + +LIST_ENUM_OVERRIDE_BY_IDS: typing.Final[str] = """-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id +""" + +COUNT_ENUM_OVERRIDE_BY_MOODS: typing.Final[str] = """-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) +""" + + +class QueryResults[T]: + """Helper class that allows both iteration and normal fetching of data from the db.""" + + __slots__ = ("_args", "_conn", "_cursor", "_decode_hook", "_sql") + + def __init__( + self, + conn: pymysql.Connection, + sql: str, + decode_hook: collections.abc.Callable[[tuple[typing.Any, ...]], T], + *args: QueryResultsArgsType, + ) -> None: + """Initialize the QueryResults instance. + + Args: + conn: + The connection object of type `pymysql.Connection` used to execute queries. + sql: + The SQL statement that will be executed when fetching/iterating. + decode_hook: + A callback that turns an `tuple[typing.Any, ...]` object into `T` that will be returned. + *args: + Arguments that should be sent when executing the sql query. + """ + self._conn = conn + self._sql = sql + self._decode_hook = decode_hook + self._args = args + self._cursor: pymysql.cursors.Cursor | None = None + + def __iter__(self) -> QueryResults[T]: + """Initialize iteration support. + + Returns: + Self as an iterator. + """ + return self + + def __call__( + self, + ) -> collections.abc.Sequence[T]: + """Allow calling the object to return all rows as a fully decoded sequence. + + Returns: + A sequence of decoded objects of type `T`. + """ + cur = self._conn.cursor() + cur.execute(self._sql, self._args) + result = cur.fetchall() + cur.close() + return [self._decode_hook(row) for row in result] + + def __next__(self) -> T: + """Yield the next item in the query result using a pymysql cursor. + + Returns: + The next decoded result of type `T`. + + Raises: + StopIteration: When no more records are available. + """ + if self._cursor is None: + self._cursor = self._conn.cursor() + self._cursor.execute(self._sql, self._args) + record = self._cursor.fetchone() + if record is None: + self._cursor = None + raise StopIteration + return self._decode_hook(record) + + +def insert_enum_override(conn: pymysql.Connection, *, id_: int, mood_test: str) -> None: + """Execute SQL query with `name: InsertEnumOverride :exec`. + + ```sql + INSERT INTO test_enum_override (id, mood_test) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + mood_test: str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_ENUM_OVERRIDE, (id_, enums.TestEnumOverrideMoodTest(mood_test))) + + +def get_enum_override_mood(conn: pymysql.Connection, *, id_: int) -> str | None: + """Fetch one from the db using the SQL query with `name: GetEnumOverrideMood :one`. + + ```sql + SELECT mood_test FROM test_enum_override WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `str` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_ENUM_OVERRIDE_MOOD, (id_,)) + row = cur.fetchone() + if row is None: + return None + return str(row[0]) + + +def list_enum_override_by_ids(conn: pymysql.Connection, *, ids: collections.abc.Sequence[int]) -> QueryResults[models.TestEnumOverride]: + """Fetch many from the db using the SQL query with `name: ListEnumOverrideByIds :many`. + + ```sql + SELECT id, mood_test FROM test_enum_override WHERE id IN (/*SLICE:ids*/%s) ORDER BY id + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + ids: collections.abc.Sequence[int]. + + Returns: + Helper class of type `QueryResults[models.TestEnumOverride]` that allows both iteration and normal fetching of data from the db. + """ + + def _decode_hook(row: tuple[typing.Any, ...]) -> models.TestEnumOverride: + return models.TestEnumOverride(id_=row[0], mood_test=str(row[1])) + + sql = LIST_ENUM_OVERRIDE_BY_IDS.replace("/*SLICE:ids*/%s", ",".join(("%s",) * len(ids)) or "NULL", 1) + return QueryResults(conn, sql, _decode_hook, *ids) + + +def count_enum_override_by_moods(conn: pymysql.Connection, *, moods: collections.abc.Sequence[str]) -> int | None: + """Fetch one from the db using the SQL query with `name: CountEnumOverrideByMoods :one`. + + ```sql + SELECT count(*) FROM test_enum_override WHERE mood_test IN (/*SLICE:moods*/%s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + moods: collections.abc.Sequence[str]. + + Returns: + Result of type `int` fetched from the db. Will be `None` if not found. + """ + sql = COUNT_ENUM_OVERRIDE_BY_MOODS.replace("/*SLICE:moods*/%s", ",".join(("%s",) * len(moods)) or "NULL", 1) + with conn.cursor() as cur: + cur.execute(sql, (*[enums.TestEnumOverrideMoodTest(v) for v in moods],)) + row = cur.fetchone() + if row is None: + return None + return row[0] diff --git a/test/driver_pymysql/pydantic/functions/queries_field_namings.py b/test/driver_pymysql/pydantic/functions/queries_field_namings.py new file mode 100644 index 00000000..9c9d3f80 --- /dev/null +++ b/test/driver_pymysql/pydantic/functions/queries_field_namings.py @@ -0,0 +1,128 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_field_namings.sql +"""Module containing queries from file queries_field_namings.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "GetJoinedFieldNamingsRow", + "get_field_naming", + "get_joined_field_namings", + "set_field_naming_outputs", +) + +import pydantic +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.pydantic.functions import models + + +class GetJoinedFieldNamingsRow(pydantic.BaseModel): + """Model representing GetJoinedFieldNamingsRow. + + Attributes: + outputs: str + outputs_2: str + """ + + model_config = pydantic.ConfigDict(arbitrary_types_allowed=True) + + outputs: str + outputs_2: str + + +GET_FIELD_NAMING: typing.Final[str] = """-- name: GetFieldNaming :one +SELECT id, outputs +FROM test_field_namings +WHERE id = %s LIMIT 1 +""" + +GET_JOINED_FIELD_NAMINGS: typing.Final[str] = """-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = %s LIMIT 1 +""" + +SET_FIELD_NAMING_OUTPUTS: typing.Final[str] = """-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = %s +WHERE id = %s +""" + + +def get_field_naming(conn: pymysql.Connection, *, id_: int) -> models.TestFieldNaming | None: + """Fetch one from the db using the SQL query with `name: GetFieldNaming :one`. + + ```sql + SELECT id, outputs + FROM test_field_namings + WHERE id = %s LIMIT 1 + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestFieldNaming` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_FIELD_NAMING, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestFieldNaming(id_=row[0], outputs=row[1]) + + +def get_joined_field_namings(conn: pymysql.Connection, *, id_: int) -> GetJoinedFieldNamingsRow | None: + """Fetch one from the db using the SQL query with `name: GetJoinedFieldNamings :one`. + + ```sql + SELECT a.outputs, b.outputs + FROM test_field_namings a + JOIN test_field_namings b ON a.id = b.id + WHERE a.id = %s LIMIT 1 + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `GetJoinedFieldNamingsRow` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_JOINED_FIELD_NAMINGS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return GetJoinedFieldNamingsRow(outputs=row[0], outputs_2=row[1]) + + +def set_field_naming_outputs(conn: pymysql.Connection, *, outputs: str, id_: int) -> None: + """Execute SQL query with `name: SetFieldNamingOutputs :exec`. + + ```sql + UPDATE test_field_namings + SET outputs = %s + WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + outputs: str. + id_: int. + """ + with conn.cursor() as cur: + cur.execute(SET_FIELD_NAMING_OUTPUTS, (outputs, id_)) diff --git a/test/driver_pymysql/pydantic/functions/queries_invalid_identifiers.py b/test/driver_pymysql/pydantic/functions/queries_invalid_identifiers.py new file mode 100644 index 00000000..f979be99 --- /dev/null +++ b/test/driver_pymysql/pydantic/functions/queries_invalid_identifiers.py @@ -0,0 +1,121 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.31.1 +# sqlc-gen-better-python v0.8.0 +# source file: queries_invalid_identifiers.sql +"""Module containing queries from file queries_invalid_identifiers.sql.""" + +from __future__ import annotations + +__all__: collections.abc.Sequence[str] = ( + "get_invalid_identifiers", + "get_third_party_stat", + "insert_invalid_identifiers", + "insert_third_party_stat", +) + +import typing + +if typing.TYPE_CHECKING: + import collections.abc + import pymysql + +from test.driver_pymysql.pydantic.functions import models + + +INSERT_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) +""" + +GET_INVALID_IDENTIFIERS: typing.Final[str] = """-- name: GetInvalidIdentifiers :one +SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s +""" + +INSERT_THIRD_PARTY_STAT: typing.Final[str] = """-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) +""" + +GET_THIRD_PARTY_STAT: typing.Final[str] = """-- name: GetThirdPartyStat :one +SELECT id, total FROM `3rd_party_stats` WHERE id = %s +""" + + +def insert_invalid_identifiers(conn: pymysql.Connection, *, id_: int, column_3p_: str | None, new_notes: str) -> None: + """Execute SQL query with `name: InsertInvalidIdentifiers :exec`. + + ```sql + INSERT INTO test_invalid_identifiers (id, `3p%%`, `new notes`) VALUES (%s, %s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + column_3p_: str | None. + new_notes: str. + """ + with conn.cursor() as cur: + cur.execute(INSERT_INVALID_IDENTIFIERS, (id_, column_3p_, new_notes)) + + +def get_invalid_identifiers(conn: pymysql.Connection, *, id_: int) -> models.TestInvalidIdentifier | None: + """Fetch one from the db using the SQL query with `name: GetInvalidIdentifiers :one`. + + ```sql + SELECT id, `3p%%`, `new notes`, `%%pct` FROM test_invalid_identifiers WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.TestInvalidIdentifier` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_INVALID_IDENTIFIERS, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.TestInvalidIdentifier(id_=row[0], column_3p_=row[1], new_notes=row[2], column__pct=row[3]) + + +def insert_third_party_stat(conn: pymysql.Connection, *, id_: int, total: int) -> None: + """Execute SQL query with `name: InsertThirdPartyStat :exec`. + + ```sql + INSERT INTO `3rd_party_stats` (id, total) VALUES (%s, %s) + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + total: int. + """ + with conn.cursor() as cur: + cur.execute(INSERT_THIRD_PARTY_STAT, (id_, total)) + + +def get_third_party_stat(conn: pymysql.Connection, *, id_: int) -> models.Model3RdPartyStat | None: + """Fetch one from the db using the SQL query with `name: GetThirdPartyStat :one`. + + ```sql + SELECT id, total FROM `3rd_party_stats` WHERE id = %s + ``` + + Args: + conn: + Connection object of type `pymysql.Connection` used to execute the query. + id_: int. + + Returns: + Result of type `models.Model3RdPartyStat` fetched from the db. Will be `None` if not found. + """ + with conn.cursor() as cur: + cur.execute(GET_THIRD_PARTY_STAT, (id_,)) + row = cur.fetchone() + if row is None: + return None + return models.Model3RdPartyStat(id_=row[0], total=row[1]) diff --git a/test/driver_pymysql/pydantic/ruff.toml b/test/driver_pymysql/pydantic/ruff.toml new file mode 100644 index 00000000..ded540a0 --- /dev/null +++ b/test/driver_pymysql/pydantic/ruff.toml @@ -0,0 +1,9 @@ +extend="../../../ruff.toml" + + +[lint.flake8-type-checking] +runtime-evaluated-base-classes = ["pydantic.BaseModel"] + + +[lint.pydocstyle] +convention = "google" \ No newline at end of file diff --git a/test/driver_pymysql/pydantic/test_pymysql_pydantic_classes.py b/test/driver_pymysql/pydantic/test_pymysql_pydantic_classes.py new file mode 100644 index 00000000..aabec2a7 --- /dev/null +++ b/test/driver_pymysql/pydantic/test_pymysql_pydantic_classes.py @@ -0,0 +1,769 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import pymysql +import pymysql.cursors +import pytest + +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.pydantic.classes import enums +from test.driver_pymysql.pydantic.classes import models +from test.driver_pymysql.pydantic.classes import queries +from test.driver_pymysql.pydantic.classes import queries_case +from test.driver_pymysql.pydantic.classes import queries_enum_override +from test.driver_pymysql.pydantic.classes import queries_field_namings +from test.driver_pymysql.pydantic.classes import queries_invalid_identifiers + +# Ids fixed and unique across the pymysql suites (pydantic owns 4000-4999); +# every chain deletes its rows at the end so reruns start clean. +TYPE_ID: typing.Final = 4000 +OVERRIDE_ID: typing.Final = 4010 +OVERRIDE_NONE_ID: typing.Final = 4011 +RESERVED_ID: typing.Final = 4020 +CASE_IDS: typing.Final = (4030, 4031) +ENUM_IDS: typing.Final = (4040, 4041) +FIELD_ID: typing.Final = 4050 +INVALID_ID: typing.Final = 4060 +THIRD_PARTY_ID: typing.Final = 4061 +MISSING_ID: typing.Final = 4999 +RESERVED_CONN: typing.Final = "pydantic-classes-conn" +EXEC_LAST_ID_NAME: typing.Final = "pydantic-classes-lastid" +CASE_DT: typing.Final = datetime.datetime(2026, 7, 19, 8, 15) +CASE_DEC: typing.Final = decimal.Decimal("12.34") + + +def _without_json(row: models.TestMysqlType) -> models.TestMysqlType: + # MySQL normalizes JSON spacing, so json_test never compares as a string. + return row.model_copy(update={"json_test": ""}) + + +class TestPymysqlPydanticClasses: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=TYPE_ID, + int_test=42, + integer_test=-42, + mediumint_test=8_388_607, + smallint_test=-32_768, + tinyint_test=-128, + bigint_test=9_223_372_036_854_775_807, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 11, + year_test=2026, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.pi, + double_precision_test=math.e, + real_test=1.5, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("99.99"), + char_test="ABCDEFGHIJ", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"0123456789abcdef"), + varbinary_test=memoryview(b"\x00\x01varbinary"), + tinyblob_test=memoryview(b"tinyblob"), + blob_test=memoryview(b"\x00\x01\x02hello"), + mediumblob_test=memoryview(b"mediumblob"), + longblob_test=memoryview(b"longblob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 15), + datetime_test=datetime.datetime(2026, 1, 15, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 15, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + time_test=datetime.timedelta(hours=1, minutes=2, seconds=3), + json_test=json.dumps({"foo": "bar"}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=7, + mediumint_test=None, + smallint_test=3, + tinyint_test=127, + bigint_test=None, + int_unsigned_test=None, + bigint_unsigned_test=2**63 + 42, + year_test=1901, + tinyint1_test=False, + bool_test=None, + boolean_test=True, + float_test=None, + double_test=0.25, + double_precision_test=None, + real_test=None, + decimal_test=decimal.Decimal("0.5000"), + numeric_test=None, + char_test=None, + varchar_test="inner varchar", + tinytext_test=None, + text_test=None, + mediumtext_test=None, + longtext_test=None, + binary_test=None, + varbinary_test=memoryview(b"inner"), + tinyblob_test=None, + blob_test=None, + mediumblob_test=None, + longblob_test=None, + bit_test=memoryview(b"\x01"), + date_test=None, + datetime_test=None, + datetime6_test=None, + timestamp_test=None, + time_test=datetime.timedelta(hours=8, minutes=30), + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=None, + ) + + @pytest.fixture(scope="session") + def queries_obj(self, pymysql_conn: pymysql.Connection) -> queries.Queries: + return queries.Queries(conn=pymysql_conn) + + @pytest.fixture(scope="session") + def case_obj(self, pymysql_conn: pymysql.Connection) -> queries_case.QueriesCase: + return queries_case.QueriesCase(conn=pymysql_conn) + + @pytest.fixture(scope="session") + def enum_obj(self, pymysql_conn: pymysql.Connection) -> queries_enum_override.QueriesEnumOverride: + return queries_enum_override.QueriesEnumOverride(conn=pymysql_conn) + + @pytest.fixture(scope="session") + def field_obj(self, pymysql_conn: pymysql.Connection) -> queries_field_namings.QueriesFieldNamings: + return queries_field_namings.QueriesFieldNamings(conn=pymysql_conn) + + @pytest.fixture(scope="session") + def invalid_obj(self, pymysql_conn: pymysql.Connection) -> queries_invalid_identifiers.QueriesInvalidIdentifiers: + return queries_invalid_identifiers.QueriesInvalidIdentifiers(conn=pymysql_conn) + + def test_conn_attr(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + assert isinstance(queries_obj.conn, pymysql.Connection) + assert queries_obj.conn is pymysql_conn + + @pytest.mark.dependency(name="PymysqlPydanticClasses::insert") + def test_insert(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + queries_obj.insert_one_mysql_type( + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::inner_insert", depends=["PymysqlPydanticClasses::insert"]) + def test_inner_insert(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + queries_obj.insert_one_inner_mysql_type( + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_one", depends=["PymysqlPydanticClasses::inner_insert"]) + def test_get_one(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_mysql_type(id_=TYPE_ID) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + assert json.loads(result.json_test) == json.loads(model.json_test) + assert _without_json(result) == _without_json(model) + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + # plain datetime drops microseconds, datetime(6) keeps them + assert result.datetime_test.microsecond == 0 + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + assert result.bigint_unsigned_test == 2**63 + 11 + assert result.mood is enums.TestMysqlTypesMood.VALUE_24H + assert result.tag is enums.TestMysqlTypesTag.BETA + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_one_none", depends=["PymysqlPydanticClasses::get_one"]) + def test_get_one_none(self, queries_obj: queries.Queries) -> None: + assert queries_obj.get_one_mysql_type(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_one_inner", depends=["PymysqlPydanticClasses::get_one_none"]) + def test_get_one_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = queries_obj.get_one_inner_mysql_type(table_id=TYPE_ID) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + assert result.tinyint1_test is False + assert result.boolean_test is True + assert result.json_test is None + assert result.mood is enums.TestInnerMysqlTypesMood.VALUE__HIDDEN + assert result.tag is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_one_inner_none", depends=["PymysqlPydanticClasses::get_one_inner"]) + def test_get_one_inner_none(self, queries_obj: queries.Queries) -> None: + assert queries_obj.get_one_inner_mysql_type(table_id=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_date", depends=["PymysqlPydanticClasses::get_one_inner_none"]) + def test_get_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_date(id_=TYPE_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + assert queries_obj.get_one_date(id_=MISSING_ID, date_test=model.date_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_datetime", depends=["PymysqlPydanticClasses::get_date"]) + def test_get_datetime(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_datetime(id_=TYPE_ID, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + assert queries_obj.get_one_datetime(id_=MISSING_ID, datetime_test=model.datetime_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_time", depends=["PymysqlPydanticClasses::get_datetime"]) + def test_get_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_time(id_=TYPE_ID, time_test=model.time_test) + + assert result is not None + # MySQL time maps to timedelta, not datetime.time + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + assert queries_obj.get_one_time(id_=MISSING_ID, time_test=model.time_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_bool", depends=["PymysqlPydanticClasses::get_time"]) + def test_get_bool(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_bool(id_=TYPE_ID, tinyint1_test=True) + + assert result is True + assert queries_obj.get_one_bool(id_=MISSING_ID, tinyint1_test=True) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_decimal", depends=["PymysqlPydanticClasses::get_bool"]) + def test_get_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_decimal(id_=TYPE_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + # decimal(12,4) comes back padded to scale 4 + assert result == decimal.Decimal("12.3400") + assert str(result) == "12.3400" + assert queries_obj.get_one_decimal(id_=MISSING_ID, decimal_test=model.decimal_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_blob", depends=["PymysqlPydanticClasses::get_decimal"]) + def test_get_blob(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_blob(id_=TYPE_ID, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + assert queries_obj.get_one_blob(id_=MISSING_ID, blob_test=model.blob_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_bit", depends=["PymysqlPydanticClasses::get_blob"]) + def test_get_bit(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_bit(id_=TYPE_ID) + + assert result is not None + # bit(8) comes back as a single byte + assert isinstance(result, memoryview) + assert bytes(result) == b"\x80" + assert queries_obj.get_one_bit(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_year", depends=["PymysqlPydanticClasses::get_bit"]) + def test_get_year(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_one_year(id_=TYPE_ID) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + assert queries_obj.get_one_year(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_json", depends=["PymysqlPydanticClasses::get_year"]) + def test_get_json(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_json(id_=TYPE_ID) + + assert result is not None + assert isinstance(result, str) + assert json.loads(result) == {"foo": "bar"} + assert queries_obj.get_one_json(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_mood", depends=["PymysqlPydanticClasses::get_json"]) + def test_get_mood(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_mood(id_=TYPE_ID, mood=enums.TestMysqlTypesMood.VALUE_24H) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + assert result == "24h" + assert queries_obj.get_one_mood(id_=MISSING_ID, mood=enums.TestMysqlTypesMood.VALUE_24H) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_tag", depends=["PymysqlPydanticClasses::get_mood"]) + def test_get_tag(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_one_tag(id_=TYPE_ID) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.BETA + assert queries_obj.get_one_tag(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many", depends=["PymysqlPydanticClasses::get_tag"]) + def test_get_many(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_mysql_type(id_=TYPE_ID) + + assert isinstance(result, queries.QueryResults) + results = result() + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + assert _without_json(results[0]) == _without_json(model) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many_iter", depends=["PymysqlPydanticClasses::get_many"]) + def test_get_many_iter(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + for result in queries_obj.get_many_mysql_type(id_=TYPE_ID): + assert isinstance(result, models.TestMysqlType) + assert _without_json(result) == _without_json(model) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many_inner", depends=["PymysqlPydanticClasses::get_many_iter"]) + def test_get_many_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + result = queries_obj.get_many_inner_mysql_type(table_id=TYPE_ID) + + assert isinstance(result, queries.QueryResults) + results = result() + assert list(results) == [inner_model] + for row in queries_obj.get_many_inner_mysql_type(table_id=TYPE_ID): + assert isinstance(row, models.TestInnerMysqlType) + assert row == inner_model + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many_nullable_inner", depends=["PymysqlPydanticClasses::get_many_inner"]) + def test_get_many_nullable_inner(self, queries_obj: queries.Queries, inner_model: models.TestInnerMysqlType) -> None: + # int_test is compared with <=>, so None matches the NULL row. + result = queries_obj.get_many_nullable_inner_mysql_type(table_id=TYPE_ID, int_test=None) + + results = result() + assert list(results) == [inner_model] + assert list(queries_obj.get_many_nullable_inner_mysql_type(table_id=TYPE_ID, int_test=0)()) == [] + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many_date", depends=["PymysqlPydanticClasses::get_many_nullable_inner"]) + def test_get_many_date(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_date(id_=TYPE_ID, date_test=model.date_test) + + assert isinstance(result, queries.QueryResults) + assert list(result()) == [model.date_test] + assert list(queries_obj.get_many_date(id_=TYPE_ID, date_test=model.date_test)) == [model.date_test] + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many_time", depends=["PymysqlPydanticClasses::get_many_date"]) + def test_get_many_time(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_time(id_=TYPE_ID, time_test=model.time_test) + + assert list(result()) == [model.time_test] + assert list(queries_obj.get_many_time(id_=TYPE_ID, time_test=model.time_test)) == [model.time_test] + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many_bool", depends=["PymysqlPydanticClasses::get_many_time"]) + def test_get_many_bool(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_many_bool(id_=TYPE_ID, tinyint1_test=True) + + results = result() + assert len(results) == 1 + assert results[0] is True + for row in queries_obj.get_many_bool(id_=TYPE_ID, tinyint1_test=True): + assert row is True + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many_decimal", depends=["PymysqlPydanticClasses::get_many_bool"]) + def test_get_many_decimal(self, queries_obj: queries.Queries, model: models.TestMysqlType) -> None: + result = queries_obj.get_many_decimal(id_=TYPE_ID, decimal_test=model.decimal_test) + + assert list(result()) == [decimal.Decimal("12.3400")] + for row in queries_obj.get_many_decimal(id_=TYPE_ID, decimal_test=model.decimal_test): + assert str(row) == "12.3400" + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_many_mood", depends=["PymysqlPydanticClasses::get_many_decimal"]) + def test_get_many_mood(self, queries_obj: queries.Queries) -> None: + result = queries_obj.get_many_mood(mood=enums.TestMysqlTypesMood.VALUE_24H) + + assert list(result()) == [enums.TestMysqlTypesMood.VALUE_24H] + assert list(queries_obj.get_many_mood(mood=enums.TestMysqlTypesMood.VALUE_24H)) == [enums.TestMysqlTypesMood.VALUE_24H] + + @pytest.mark.dependency(name="PymysqlPydanticClasses::list_months", depends=["PymysqlPydanticClasses::get_many_mood"]) + def test_list_months(self, queries_obj: queries.Queries) -> None: + # Regression for the percent-doubling bug: the parameterless :many + # query contains literal % signs in DATE_FORMAT. + result = queries_obj.list_months() + + assert list(result()) == ["2026-01"] + assert list(queries_obj.list_months()) == ["2026-01"] + + @pytest.mark.dependency(name="PymysqlPydanticClasses::count", depends=["PymysqlPydanticClasses::list_months"]) + def test_count(self, queries_obj: queries.Queries) -> None: + # The shared table may carry other files' rows; only a lower bound is safe. + count = queries_obj.count_mysql_types() + assert count is not None + assert count >= 1 + + @pytest.mark.dependency(name="PymysqlPydanticClasses::update_varchar", depends=["PymysqlPydanticClasses::count"]) + def test_update_varchar(self, queries_obj: queries.Queries) -> None: + result = queries_obj.update_varchar_test(varchar_test="updated varchar", id_=TYPE_ID) + + assert isinstance(result, int) + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + assert queries_obj.update_varchar_test(varchar_test="updated varchar", id_=MISSING_ID) == 0 + + @pytest.mark.dependency(name="PymysqlPydanticClasses::all_cursor", depends=["PymysqlPydanticClasses::update_varchar"]) + def test_all_cursor(self, queries_obj: queries.Queries) -> None: + cursor = queries_obj.all_mysql_types_cursor() + + assert isinstance(cursor, pymysql.cursors.Cursor) + rows = cursor.fetchall() + cursor.close() + # The shared table may carry other files' rows; assert on our own. + assert TYPE_ID in {row[0] for row in rows} + + @pytest.mark.dependency(name="PymysqlPydanticClasses::delete", depends=["PymysqlPydanticClasses::all_cursor"]) + def test_delete(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + queries_obj.delete_one_mysql_type(id_=TYPE_ID) + + assert queries_obj.get_one_mysql_type(id_=TYPE_ID) is None + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_inner_mysql_types WHERE table_id = %s", (TYPE_ID,)) + assert queries_obj.get_one_inner_mysql_type(table_id=TYPE_ID) is None + + def test_exec_last_id(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + # The AUTO_INCREMENT counter persists across runs, so only > 0 holds. + last_id = queries_obj.insert_exec_last_id(name=EXEC_LAST_ID_NAME) + + assert isinstance(last_id, int) + assert last_id > 0 + assert queries_obj.get_exec_last_id_name(id_=last_id) == EXEC_LAST_ID_NAME + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_execlastid WHERE id = %s", (last_id,)) + assert queries_obj.get_exec_last_id_name(id_=last_id) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::insert_type_override") + def test_insert_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + queries_obj.insert_type_override(id_=override_model.id_, text_test=override_model.text_test) + queries_obj.insert_type_override(id_=OVERRIDE_NONE_ID, text_test=None) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::get_type_override", depends=["PymysqlPydanticClasses::insert_type_override"]) + def test_get_type_override(self, queries_obj: queries.Queries, override_model: models.TestTypeOverride) -> None: + result = queries_obj.get_type_override(id_=OVERRIDE_ID) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + none_result = queries_obj.get_type_override(id_=OVERRIDE_NONE_ID) + assert none_result is not None + assert none_result.text_test is None + assert queries_obj.get_type_override(id_=MISSING_ID) is None + + @pytest.mark.dependency(depends=["PymysqlPydanticClasses::get_type_override"]) + def test_delete_type_override(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_type_override WHERE id IN (%s, %s)", (OVERRIDE_ID, OVERRIDE_NONE_ID)) + assert queries_obj.get_type_override(id_=OVERRIDE_ID) is None + + def test_reserved_arg(self, queries_obj: queries.Queries, pymysql_conn: pymysql.Connection) -> None: + queries_obj.insert_reserved_arg(id_=RESERVED_ID, conn=RESERVED_CONN) + + result = queries_obj.get_reserved_arg(conn=RESERVED_CONN) + assert result == models.TestReservedArg(id_=RESERVED_ID, conn=RESERVED_CONN) + assert queries_obj.get_reserved_arg(conn="pydantic-classes-missing") is None + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_reserved_args WHERE id = %s", (RESERVED_ID,)) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::case_insert") + def test_case_insert(self, case_obj: queries_case.QueriesCase) -> None: + case_obj.insert_case_row(id_=CASE_IDS[0], upper_dt=CASE_DT, prec_dec=CASE_DEC) + case_obj.insert_case_row(id_=CASE_IDS[1], upper_dt=CASE_DT, prec_dec=CASE_DEC) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::case_get", depends=["PymysqlPydanticClasses::case_insert"]) + def test_case_get(self, case_obj: queries_case.QueriesCase) -> None: + result = case_obj.get_case_row(id_=CASE_IDS[0]) + + assert result is not None + assert result == models.TestCaseSensitivity(id_=CASE_IDS[0], upper_dt=CASE_DT, prec_dec=CASE_DEC) + assert case_obj.get_case_row(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::case_count", depends=["PymysqlPydanticClasses::case_get"]) + def test_case_count(self, case_obj: queries_case.QueriesCase) -> None: + # The WHERE clause lives inside an executable /*! version comment; if + # MySQL ignored it both counts would be 2. + # Range-scoped asserts: the shared table may carry other files' rows, + # so counts outside [CASE_IDS[0], CASE_IDS[1]] must cancel out. + beyond = case_obj.count_case_rows(id_=CASE_IDS[1] + 1) + high = case_obj.count_case_rows(id_=CASE_IDS[1]) + low = case_obj.count_case_rows(id_=CASE_IDS[0]) + assert beyond is not None + assert high is not None + assert low is not None + assert high - beyond == 1 + assert low - beyond == len(CASE_IDS) + + @pytest.mark.dependency(depends=["PymysqlPydanticClasses::case_count"]) + def test_case_delete(self, case_obj: queries_case.QueriesCase, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_case_sensitivity WHERE id IN (%s, %s)", CASE_IDS) + beyond = case_obj.count_case_rows(id_=CASE_IDS[1] + 1) + low = case_obj.count_case_rows(id_=CASE_IDS[0]) + assert beyond is not None + assert low is not None + assert low - beyond == 0 + + @pytest.mark.dependency(name="PymysqlPydanticClasses::enum_insert") + def test_enum_override_insert(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + # The overridden parameter is a plain str; the generated code converts + # it back through enums.TestEnumOverrideMoodTest. + enum_obj.insert_enum_override(id_=ENUM_IDS[0], mood_test="happy") + enum_obj.insert_enum_override(id_=ENUM_IDS[1], mood_test="sad") + with pytest.raises(ValueError, match="angry"): + enum_obj.insert_enum_override(id_=MISSING_ID, mood_test="angry") + + @pytest.mark.dependency(name="PymysqlPydanticClasses::enum_get", depends=["PymysqlPydanticClasses::enum_insert"]) + def test_enum_override_get(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + mood = enum_obj.get_enum_override_mood(id_=ENUM_IDS[0]) + + assert mood is not None + assert isinstance(mood, str) + assert mood == "happy" + assert enum_obj.get_enum_override_mood(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::enum_list", depends=["PymysqlPydanticClasses::enum_insert"]) + def test_enum_override_list(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + rows = enum_obj.list_enum_override_by_ids(ids=list(ENUM_IDS))() + + assert all(isinstance(row, models.TestEnumOverride) for row in rows) + assert {row.id_: row.mood_test for row in rows} == {ENUM_IDS[0]: "happy", ENUM_IDS[1]: "sad"} + + @pytest.mark.dependency(name="PymysqlPydanticClasses::enum_iterate", depends=["PymysqlPydanticClasses::enum_insert"]) + def test_enum_override_iterate(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + seen: dict[int, str] = {} + for row in enum_obj.list_enum_override_by_ids(ids=list(ENUM_IDS)): + assert isinstance(row, models.TestEnumOverride) + seen[row.id_] = row.mood_test + assert seen == {ENUM_IDS[0]: "happy", ENUM_IDS[1]: "sad"} + + @pytest.mark.dependency(name="PymysqlPydanticClasses::enum_empty", depends=["PymysqlPydanticClasses::enum_insert"]) + def test_enum_override_empty_slice(self, enum_obj: queries_enum_override.QueriesEnumOverride) -> None: + assert list(enum_obj.list_enum_override_by_ids(ids=[])()) == [] + assert list(enum_obj.list_enum_override_by_ids(ids=[])) == [] + + @pytest.mark.dependency(depends=["PymysqlPydanticClasses::enum_empty"]) + def test_enum_override_delete(self, enum_obj: queries_enum_override.QueriesEnumOverride, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_enum_override WHERE id IN (%s, %s)", ENUM_IDS) + assert enum_obj.get_enum_override_mood(id_=ENUM_IDS[0]) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::field_insert") + def test_field_naming_insert(self, pymysql_conn: pymysql.Connection) -> None: + # There is no generated insert for this table. + with pymysql_conn.cursor() as cur: + cur.execute("INSERT INTO test_field_namings (id, outputs) VALUES (%s, %s)", (FIELD_ID, json.dumps(["first", "second"]))) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::field_get", depends=["PymysqlPydanticClasses::field_insert"]) + def test_field_naming_get(self, field_obj: queries_field_namings.QueriesFieldNamings) -> None: + result = field_obj.get_field_naming(id_=FIELD_ID) + + assert result is not None + assert isinstance(result, models.TestFieldNaming) + assert result.id_ == FIELD_ID + assert json.loads(result.outputs) == ["first", "second"] + assert field_obj.get_field_naming(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::field_joined", depends=["PymysqlPydanticClasses::field_get"]) + def test_field_naming_joined(self, field_obj: queries_field_namings.QueriesFieldNamings) -> None: + result = field_obj.get_joined_field_namings(id_=FIELD_ID) + + assert result is not None + assert isinstance(result, queries_field_namings.GetJoinedFieldNamingsRow) + assert json.loads(result.outputs) == ["first", "second"] + assert json.loads(result.outputs_2) == ["first", "second"] + + @pytest.mark.dependency(name="PymysqlPydanticClasses::field_set", depends=["PymysqlPydanticClasses::field_joined"]) + def test_field_naming_set(self, field_obj: queries_field_namings.QueriesFieldNamings) -> None: + field_obj.set_field_naming_outputs(outputs=json.dumps({"count": 2}), id_=FIELD_ID) + + result = field_obj.get_field_naming(id_=FIELD_ID) + assert result is not None + assert json.loads(result.outputs) == {"count": 2} + + @pytest.mark.dependency(depends=["PymysqlPydanticClasses::field_set"]) + def test_field_naming_delete(self, field_obj: queries_field_namings.QueriesFieldNamings, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_field_namings WHERE id = %s", (FIELD_ID,)) + assert field_obj.get_field_naming(id_=FIELD_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::invalid_insert") + def test_invalid_identifiers_insert(self, invalid_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + invalid_obj.insert_invalid_identifiers(id_=INVALID_ID, column_3p_="3p value", new_notes="note value") + invalid_obj.insert_third_party_stat(id_=THIRD_PARTY_ID, total=987) + + @pytest.mark.dependency(name="PymysqlPydanticClasses::invalid_get", depends=["PymysqlPydanticClasses::invalid_insert"]) + def test_invalid_identifiers_get(self, invalid_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + result = invalid_obj.get_invalid_identifiers(id_=INVALID_ID) + + assert result == models.TestInvalidIdentifier(id_=INVALID_ID, column_3p_="3p value", new_notes="note value", column__pct=None) + assert invalid_obj.get_invalid_identifiers(id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticClasses::third_party_get", depends=["PymysqlPydanticClasses::invalid_insert"]) + def test_third_party_stat_get(self, invalid_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers) -> None: + result = invalid_obj.get_third_party_stat(id_=THIRD_PARTY_ID) + + assert result == models.Model3RdPartyStat(id_=THIRD_PARTY_ID, total=987) + assert invalid_obj.get_third_party_stat(id_=MISSING_ID) is None + + @pytest.mark.dependency(depends=["PymysqlPydanticClasses::invalid_get", "PymysqlPydanticClasses::third_party_get"]) + def test_invalid_identifiers_delete(self, invalid_obj: queries_invalid_identifiers.QueriesInvalidIdentifiers, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_invalid_identifiers WHERE id = %s", (INVALID_ID,)) + cur.execute("DELETE FROM `3rd_party_stats` WHERE id = %s", (THIRD_PARTY_ID,)) + assert invalid_obj.get_invalid_identifiers(id_=INVALID_ID) is None + assert invalid_obj.get_third_party_stat(id_=THIRD_PARTY_ID) is None + + def test_one_missing_rows_return_none(self, pymysql_conn: pymysql.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. The + # count queries always return a row, so their miss branch needs the + # no-row stub; the sub-module Querier conn properties ride along. + obj = queries.Queries(conn=pymysql_conn) + assert obj.get_one_mysql_type(id_=-1) is None + assert obj.get_one_inner_mysql_type(table_id=-1) is None + assert obj.get_one_date(id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert obj.get_one_datetime(id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert obj.get_one_time(id_=-1, time_test=datetime.timedelta()) is None + assert obj.get_one_bool(id_=-1, tinyint1_test=False) is None + assert obj.get_one_decimal(id_=-1, decimal_test=decimal.Decimal(0)) is None + assert obj.get_one_blob(id_=-1, blob_test=memoryview(b"")) is None + assert obj.get_one_bit(id_=-1) is None + assert obj.get_one_year(id_=-1) is None + assert obj.get_one_json(id_=-1) is None + assert obj.get_one_mood(id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert obj.get_one_tag(id_=-1) is None + assert obj.get_exec_last_id_name(id_=-1) is None + assert obj.get_type_override(id_=-1) is None + assert obj.get_reserved_arg(conn="missing") is None + assert obj.touch_exec_last_id(name="untouched", id_=-1) is None + + case_obj = queries_case.QueriesCase(conn=pymysql_conn) + naming_obj = queries_field_namings.QueriesFieldNamings(conn=pymysql_conn) + invalid_obj = queries_invalid_identifiers.QueriesInvalidIdentifiers(conn=pymysql_conn) + enum_obj = queries_enum_override.QueriesEnumOverride(conn=pymysql_conn) + assert case_obj.conn is pymysql_conn + assert naming_obj.conn is pymysql_conn + assert invalid_obj.conn is pymysql_conn + assert enum_obj.conn is pymysql_conn + assert case_obj.get_case_row(id_=-1) is None + assert naming_obj.get_field_naming(id_=-1) is None + assert naming_obj.get_joined_field_namings(id_=-1) is None + assert invalid_obj.get_invalid_identifiers(id_=-1) is None + assert enum_obj.get_enum_override_mood(id_=-1) is None + assert enum_obj.count_enum_override_by_moods(moods=[]) == 0 + + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert queries.Queries(conn=stub).count_mysql_types() is None + assert queries_case.QueriesCase(conn=stub).count_case_rows(id_=0) is None + assert queries_enum_override.QueriesEnumOverride(conn=stub).count_enum_override_by_moods(moods=[]) is None diff --git a/test/driver_pymysql/pydantic/test_pymysql_pydantic_functions.py b/test/driver_pymysql/pydantic/test_pymysql_pydantic_functions.py new file mode 100644 index 00000000..c8c79708 --- /dev/null +++ b/test/driver_pymysql/pydantic/test_pymysql_pydantic_functions.py @@ -0,0 +1,739 @@ +# Copyright (c) 2025-present Rayakame + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import annotations + +import datetime +import decimal +import json +import math +import typing +from collections import UserString + +import pymysql +import pymysql.cursors +import pytest + +from test.driver_pymysql import no_row_conn +from test.driver_pymysql.pydantic.functions import enums +from test.driver_pymysql.pydantic.functions import models +from test.driver_pymysql.pydantic.functions import queries +from test.driver_pymysql.pydantic.functions import queries_case +from test.driver_pymysql.pydantic.functions import queries_enum_override +from test.driver_pymysql.pydantic.functions import queries_field_namings +from test.driver_pymysql.pydantic.functions import queries_invalid_identifiers + +# Ids fixed and unique across the pymysql suites (pydantic owns 4000-4999, +# this file uses the 45xx block); every chain deletes its rows at the end. +TYPE_ID: typing.Final = 4500 +OVERRIDE_ID: typing.Final = 4510 +OVERRIDE_NONE_ID: typing.Final = 4511 +RESERVED_ID: typing.Final = 4520 +CASE_IDS: typing.Final = (4530, 4531) +ENUM_IDS: typing.Final = (4540, 4541) +FIELD_ID: typing.Final = 4550 +INVALID_ID: typing.Final = 4560 +THIRD_PARTY_ID: typing.Final = 4561 +MISSING_ID: typing.Final = 4999 +RESERVED_CONN: typing.Final = "pydantic-functions-conn" +EXEC_LAST_ID_NAME: typing.Final = "pydantic-functions-lastid" +CASE_DT: typing.Final = datetime.datetime(2026, 7, 19, 8, 15) +CASE_DEC: typing.Final = decimal.Decimal("12.34") + + +def _without_json(row: models.TestMysqlType) -> models.TestMysqlType: + # MySQL normalizes JSON spacing, so json_test never compares as a string. + return row.model_copy(update={"json_test": ""}) + + +class TestPymysqlPydanticFunctions: + @pytest.fixture(scope="session") + def override_model(self) -> models.TestTypeOverride: + return models.TestTypeOverride(id_=OVERRIDE_ID, text_test=UserString("Test")) + + @pytest.fixture(scope="session") + def model(self) -> models.TestMysqlType: + return models.TestMysqlType( + id_=TYPE_ID, + int_test=42, + integer_test=-42, + mediumint_test=8_388_607, + smallint_test=-32_768, + tinyint_test=-128, + bigint_test=9_223_372_036_854_775_807, + int_unsigned_test=4_294_967_295, + bigint_unsigned_test=2**63 + 11, + year_test=2026, + tinyint1_test=True, + bool_test=True, + boolean_test=False, + float_test=2.5, + double_test=math.pi, + double_precision_test=math.e, + real_test=1.5, + decimal_test=decimal.Decimal("12.34"), + numeric_test=decimal.Decimal("99.99"), + char_test="ABCDEFGHIJ", + varchar_test="Hello varchar", + tinytext_test="tiny text", + text_test="Some text", + mediumtext_test="medium text", + longtext_test="long text", + binary_test=memoryview(b"0123456789abcdef"), + varbinary_test=memoryview(b"\x00\x01varbinary"), + tinyblob_test=memoryview(b"tinyblob"), + blob_test=memoryview(b"\x00\x01\x02hello"), + mediumblob_test=memoryview(b"mediumblob"), + longblob_test=memoryview(b"longblob"), + bit_test=memoryview(b"\x80"), + date_test=datetime.date(2026, 1, 15), + datetime_test=datetime.datetime(2026, 1, 15, 12, 30, 45), + datetime6_test=datetime.datetime(2026, 1, 15, 12, 30, 45, 123456), + timestamp_test=datetime.datetime(2026, 1, 2, 3, 4, 5), + time_test=datetime.timedelta(hours=1, minutes=2, seconds=3), + json_test=json.dumps({"foo": "bar"}), + mood=enums.TestMysqlTypesMood.VALUE_24H, + tag=enums.TestMysqlTypesTag.BETA, + ) + + @pytest.fixture(scope="session") + def inner_model(self, model: models.TestMysqlType) -> models.TestInnerMysqlType: + return models.TestInnerMysqlType( + table_id=model.id_, + int_test=None, + integer_test=7, + mediumint_test=None, + smallint_test=3, + tinyint_test=127, + bigint_test=None, + int_unsigned_test=None, + bigint_unsigned_test=2**63 + 42, + year_test=1901, + tinyint1_test=False, + bool_test=None, + boolean_test=True, + float_test=None, + double_test=0.25, + double_precision_test=None, + real_test=None, + decimal_test=decimal.Decimal("0.5000"), + numeric_test=None, + char_test=None, + varchar_test="inner varchar", + tinytext_test=None, + text_test=None, + mediumtext_test=None, + longtext_test=None, + binary_test=None, + varbinary_test=memoryview(b"inner"), + tinyblob_test=None, + blob_test=None, + mediumblob_test=None, + longblob_test=None, + bit_test=memoryview(b"\x01"), + date_test=None, + datetime_test=None, + datetime6_test=None, + timestamp_test=None, + time_test=datetime.timedelta(hours=8, minutes=30), + json_test=None, + mood=enums.TestInnerMysqlTypesMood.VALUE__HIDDEN, + tag=None, + ) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::insert") + def test_insert(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + queries.insert_one_mysql_type( + conn=pymysql_conn, + id_=model.id_, + int_test=model.int_test, + integer_test=model.integer_test, + mediumint_test=model.mediumint_test, + smallint_test=model.smallint_test, + tinyint_test=model.tinyint_test, + bigint_test=model.bigint_test, + int_unsigned_test=model.int_unsigned_test, + bigint_unsigned_test=model.bigint_unsigned_test, + year_test=model.year_test, + tinyint1_test=model.tinyint1_test, + bool_test=model.bool_test, + boolean_test=model.boolean_test, + float_test=model.float_test, + double_test=model.double_test, + double_precision_test=model.double_precision_test, + real_test=model.real_test, + decimal_test=model.decimal_test, + numeric_test=model.numeric_test, + char_test=model.char_test, + varchar_test=model.varchar_test, + tinytext_test=model.tinytext_test, + text_test=model.text_test, + mediumtext_test=model.mediumtext_test, + longtext_test=model.longtext_test, + binary_test=model.binary_test, + varbinary_test=model.varbinary_test, + tinyblob_test=model.tinyblob_test, + blob_test=model.blob_test, + mediumblob_test=model.mediumblob_test, + longblob_test=model.longblob_test, + bit_test=model.bit_test, + date_test=model.date_test, + datetime_test=model.datetime_test, + datetime6_test=model.datetime6_test, + timestamp_test=model.timestamp_test, + time_test=model.time_test, + json_test=model.json_test, + mood=model.mood, + tag=model.tag, + ) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::inner_insert", depends=["PymysqlPydanticFunctions::insert"]) + def test_inner_insert(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + queries.insert_one_inner_mysql_type( + conn=pymysql_conn, + table_id=inner_model.table_id, + int_test=inner_model.int_test, + integer_test=inner_model.integer_test, + mediumint_test=inner_model.mediumint_test, + smallint_test=inner_model.smallint_test, + tinyint_test=inner_model.tinyint_test, + bigint_test=inner_model.bigint_test, + int_unsigned_test=inner_model.int_unsigned_test, + bigint_unsigned_test=inner_model.bigint_unsigned_test, + year_test=inner_model.year_test, + tinyint1_test=inner_model.tinyint1_test, + bool_test=inner_model.bool_test, + boolean_test=inner_model.boolean_test, + float_test=inner_model.float_test, + double_test=inner_model.double_test, + double_precision_test=inner_model.double_precision_test, + real_test=inner_model.real_test, + decimal_test=inner_model.decimal_test, + numeric_test=inner_model.numeric_test, + char_test=inner_model.char_test, + varchar_test=inner_model.varchar_test, + tinytext_test=inner_model.tinytext_test, + text_test=inner_model.text_test, + mediumtext_test=inner_model.mediumtext_test, + longtext_test=inner_model.longtext_test, + binary_test=inner_model.binary_test, + varbinary_test=inner_model.varbinary_test, + tinyblob_test=inner_model.tinyblob_test, + blob_test=inner_model.blob_test, + mediumblob_test=inner_model.mediumblob_test, + longblob_test=inner_model.longblob_test, + bit_test=inner_model.bit_test, + date_test=inner_model.date_test, + datetime_test=inner_model.datetime_test, + datetime6_test=inner_model.datetime6_test, + timestamp_test=inner_model.timestamp_test, + time_test=inner_model.time_test, + json_test=inner_model.json_test, + mood=inner_model.mood, + tag=inner_model.tag, + ) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_one", depends=["PymysqlPydanticFunctions::inner_insert"]) + def test_get_one(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_mysql_type(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + assert isinstance(result, models.TestMysqlType) + assert json.loads(result.json_test) == json.loads(model.json_test) + assert _without_json(result) == _without_json(model) + assert result.tinyint1_test is True + assert result.bool_test is True + assert result.boolean_test is False + # plain datetime drops microseconds, datetime(6) keeps them + assert result.datetime_test.microsecond == 0 + assert result.datetime6_test.microsecond == model.datetime6_test.microsecond + assert result.bigint_unsigned_test == 2**63 + 11 + assert result.mood is enums.TestMysqlTypesMood.VALUE_24H + assert result.tag is enums.TestMysqlTypesTag.BETA + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_one_none", depends=["PymysqlPydanticFunctions::get_one"]) + def test_get_one_none(self, pymysql_conn: pymysql.Connection) -> None: + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_one_inner", depends=["PymysqlPydanticFunctions::get_one_none"]) + def test_get_one_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID) + + assert result is not None + assert isinstance(result, models.TestInnerMysqlType) + assert result == inner_model + assert result.tinyint1_test is False + assert result.boolean_test is True + assert result.json_test is None + assert result.mood is enums.TestInnerMysqlTypesMood.VALUE__HIDDEN + assert result.tag is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_one_inner_none", depends=["PymysqlPydanticFunctions::get_one_inner"]) + def test_get_one_inner_none(self, pymysql_conn: pymysql.Connection) -> None: + assert queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_date", depends=["PymysqlPydanticFunctions::get_one_inner_none"]) + def test_get_date(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_date(conn=pymysql_conn, id_=TYPE_ID, date_test=model.date_test) + + assert result is not None + assert isinstance(result, datetime.date) + assert result == model.date_test + assert queries.get_one_date(conn=pymysql_conn, id_=MISSING_ID, date_test=model.date_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_datetime", depends=["PymysqlPydanticFunctions::get_date"]) + def test_get_datetime(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_datetime(conn=pymysql_conn, id_=TYPE_ID, datetime_test=model.datetime_test) + + assert result is not None + assert isinstance(result, datetime.datetime) + assert result == model.datetime_test + assert queries.get_one_datetime(conn=pymysql_conn, id_=MISSING_ID, datetime_test=model.datetime_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_time", depends=["PymysqlPydanticFunctions::get_datetime"]) + def test_get_time(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_time(conn=pymysql_conn, id_=TYPE_ID, time_test=model.time_test) + + assert result is not None + # MySQL time maps to timedelta, not datetime.time + assert isinstance(result, datetime.timedelta) + assert result == model.time_test + assert queries.get_one_time(conn=pymysql_conn, id_=MISSING_ID, time_test=model.time_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_bool", depends=["PymysqlPydanticFunctions::get_time"]) + def test_get_bool(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_bool(conn=pymysql_conn, id_=TYPE_ID, tinyint1_test=True) + + assert result is True + assert queries.get_one_bool(conn=pymysql_conn, id_=MISSING_ID, tinyint1_test=True) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_decimal", depends=["PymysqlPydanticFunctions::get_bool"]) + def test_get_decimal(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_decimal(conn=pymysql_conn, id_=TYPE_ID, decimal_test=model.decimal_test) + + assert result is not None + assert isinstance(result, decimal.Decimal) + # decimal(12,4) comes back padded to scale 4 + assert result == decimal.Decimal("12.3400") + assert str(result) == "12.3400" + assert queries.get_one_decimal(conn=pymysql_conn, id_=MISSING_ID, decimal_test=model.decimal_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_blob", depends=["PymysqlPydanticFunctions::get_decimal"]) + def test_get_blob(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_blob(conn=pymysql_conn, id_=TYPE_ID, blob_test=model.blob_test) + + assert result is not None + assert isinstance(result, memoryview) + assert result == model.blob_test + assert queries.get_one_blob(conn=pymysql_conn, id_=MISSING_ID, blob_test=model.blob_test) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_bit", depends=["PymysqlPydanticFunctions::get_blob"]) + def test_get_bit(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_bit(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + # bit(8) comes back as a single byte + assert isinstance(result, memoryview) + assert bytes(result) == b"\x80" + assert queries.get_one_bit(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_year", depends=["PymysqlPydanticFunctions::get_bit"]) + def test_get_year(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_one_year(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + assert isinstance(result, int) + assert result == model.year_test + assert queries.get_one_year(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_json", depends=["PymysqlPydanticFunctions::get_year"]) + def test_get_json(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_json(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + assert isinstance(result, str) + assert json.loads(result) == {"foo": "bar"} + assert queries.get_one_json(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_mood", depends=["PymysqlPydanticFunctions::get_json"]) + def test_get_mood(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_mood(conn=pymysql_conn, id_=TYPE_ID, mood=enums.TestMysqlTypesMood.VALUE_24H) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesMood) + assert result is enums.TestMysqlTypesMood.VALUE_24H + assert result == "24h" + assert queries.get_one_mood(conn=pymysql_conn, id_=MISSING_ID, mood=enums.TestMysqlTypesMood.VALUE_24H) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_tag", depends=["PymysqlPydanticFunctions::get_mood"]) + def test_get_tag(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_one_tag(conn=pymysql_conn, id_=TYPE_ID) + + assert result is not None + assert isinstance(result, enums.TestMysqlTypesTag) + assert result is enums.TestMysqlTypesTag.BETA + assert queries.get_one_tag(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many", depends=["PymysqlPydanticFunctions::get_tag"]) + def test_get_many(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_mysql_type(conn=pymysql_conn, id_=TYPE_ID) + + assert isinstance(result, queries.QueryResults) + results = result() + assert len(results) == 1 + assert isinstance(results[0], models.TestMysqlType) + assert _without_json(results[0]) == _without_json(model) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many_iter", depends=["PymysqlPydanticFunctions::get_many"]) + def test_get_many_iter(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + for result in queries.get_many_mysql_type(conn=pymysql_conn, id_=TYPE_ID): + assert isinstance(result, models.TestMysqlType) + assert _without_json(result) == _without_json(model) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many_inner", depends=["PymysqlPydanticFunctions::get_many_iter"]) + def test_get_many_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + result = queries.get_many_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID) + + assert isinstance(result, queries.QueryResults) + results = result() + assert list(results) == [inner_model] + for row in queries.get_many_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID): + assert isinstance(row, models.TestInnerMysqlType) + assert row == inner_model + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many_nullable_inner", depends=["PymysqlPydanticFunctions::get_many_inner"]) + def test_get_many_nullable_inner(self, pymysql_conn: pymysql.Connection, inner_model: models.TestInnerMysqlType) -> None: + # int_test is compared with <=>, so None matches the NULL row. + result = queries.get_many_nullable_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID, int_test=None) + + results = result() + assert list(results) == [inner_model] + assert list(queries.get_many_nullable_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID, int_test=0)()) == [] + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many_date", depends=["PymysqlPydanticFunctions::get_many_nullable_inner"]) + def test_get_many_date(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_date(conn=pymysql_conn, id_=TYPE_ID, date_test=model.date_test) + + assert isinstance(result, queries.QueryResults) + assert list(result()) == [model.date_test] + assert list(queries.get_many_date(conn=pymysql_conn, id_=TYPE_ID, date_test=model.date_test)) == [model.date_test] + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many_time", depends=["PymysqlPydanticFunctions::get_many_date"]) + def test_get_many_time(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_time(conn=pymysql_conn, id_=TYPE_ID, time_test=model.time_test) + + assert list(result()) == [model.time_test] + assert list(queries.get_many_time(conn=pymysql_conn, id_=TYPE_ID, time_test=model.time_test)) == [model.time_test] + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many_bool", depends=["PymysqlPydanticFunctions::get_many_time"]) + def test_get_many_bool(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_many_bool(conn=pymysql_conn, id_=TYPE_ID, tinyint1_test=True) + + results = result() + assert len(results) == 1 + assert results[0] is True + for row in queries.get_many_bool(conn=pymysql_conn, id_=TYPE_ID, tinyint1_test=True): + assert row is True + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many_decimal", depends=["PymysqlPydanticFunctions::get_many_bool"]) + def test_get_many_decimal(self, pymysql_conn: pymysql.Connection, model: models.TestMysqlType) -> None: + result = queries.get_many_decimal(conn=pymysql_conn, id_=TYPE_ID, decimal_test=model.decimal_test) + + assert list(result()) == [decimal.Decimal("12.3400")] + for row in queries.get_many_decimal(conn=pymysql_conn, id_=TYPE_ID, decimal_test=model.decimal_test): + assert str(row) == "12.3400" + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_many_mood", depends=["PymysqlPydanticFunctions::get_many_decimal"]) + def test_get_many_mood(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.get_many_mood(conn=pymysql_conn, mood=enums.TestMysqlTypesMood.VALUE_24H) + + assert list(result()) == [enums.TestMysqlTypesMood.VALUE_24H] + assert list(queries.get_many_mood(conn=pymysql_conn, mood=enums.TestMysqlTypesMood.VALUE_24H)) == [enums.TestMysqlTypesMood.VALUE_24H] + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::list_months", depends=["PymysqlPydanticFunctions::get_many_mood"]) + def test_list_months(self, pymysql_conn: pymysql.Connection) -> None: + # Regression for the percent-doubling bug: the parameterless :many + # query contains literal % signs in DATE_FORMAT. + result = queries.list_months(conn=pymysql_conn) + + assert list(result()) == ["2026-01"] + assert list(queries.list_months(conn=pymysql_conn)) == ["2026-01"] + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::count", depends=["PymysqlPydanticFunctions::list_months"]) + def test_count(self, pymysql_conn: pymysql.Connection) -> None: + # The shared table may carry other files' rows; only a lower bound is safe. + count = queries.count_mysql_types(conn=pymysql_conn) + assert count is not None + assert count >= 1 + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::update_varchar", depends=["PymysqlPydanticFunctions::count"]) + def test_update_varchar(self, pymysql_conn: pymysql.Connection) -> None: + result = queries.update_varchar_test(conn=pymysql_conn, varchar_test="updated varchar", id_=TYPE_ID) + + assert isinstance(result, int) + # The shared table may carry other files' rows; only a lower bound is safe. + assert result is not None + assert result >= 1 + assert queries.update_varchar_test(conn=pymysql_conn, varchar_test="updated varchar", id_=MISSING_ID) == 0 + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::all_cursor", depends=["PymysqlPydanticFunctions::update_varchar"]) + def test_all_cursor(self, pymysql_conn: pymysql.Connection) -> None: + cursor = queries.all_mysql_types_cursor(conn=pymysql_conn) + + assert isinstance(cursor, pymysql.cursors.Cursor) + rows = cursor.fetchall() + cursor.close() + # The shared table may carry other files' rows; assert on our own. + assert TYPE_ID in {row[0] for row in rows} + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::delete", depends=["PymysqlPydanticFunctions::all_cursor"]) + def test_delete(self, pymysql_conn: pymysql.Connection) -> None: + queries.delete_one_mysql_type(conn=pymysql_conn, id_=TYPE_ID) + + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=TYPE_ID) is None + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_inner_mysql_types WHERE table_id = %s", (TYPE_ID,)) + assert queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=TYPE_ID) is None + + def test_exec_last_id(self, pymysql_conn: pymysql.Connection) -> None: + # The AUTO_INCREMENT counter persists across runs, so only > 0 holds. + last_id = queries.insert_exec_last_id(conn=pymysql_conn, name=EXEC_LAST_ID_NAME) + + assert isinstance(last_id, int) + assert last_id > 0 + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=last_id) == EXEC_LAST_ID_NAME + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_execlastid WHERE id = %s", (last_id,)) + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=last_id) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::insert_type_override") + def test_insert_type_override(self, pymysql_conn: pymysql.Connection, override_model: models.TestTypeOverride) -> None: + queries.insert_type_override(conn=pymysql_conn, id_=override_model.id_, text_test=override_model.text_test) + queries.insert_type_override(conn=pymysql_conn, id_=OVERRIDE_NONE_ID, text_test=None) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::get_type_override", depends=["PymysqlPydanticFunctions::insert_type_override"]) + def test_get_type_override(self, pymysql_conn: pymysql.Connection, override_model: models.TestTypeOverride) -> None: + result = queries.get_type_override(conn=pymysql_conn, id_=OVERRIDE_ID) + + assert result is not None + assert isinstance(result.text_test, UserString) + assert result == override_model + + none_result = queries.get_type_override(conn=pymysql_conn, id_=OVERRIDE_NONE_ID) + assert none_result is not None + assert none_result.text_test is None + assert queries.get_type_override(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(depends=["PymysqlPydanticFunctions::get_type_override"]) + def test_delete_type_override(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_type_override WHERE id IN (%s, %s)", (OVERRIDE_ID, OVERRIDE_NONE_ID)) + assert queries.get_type_override(conn=pymysql_conn, id_=OVERRIDE_ID) is None + + def test_reserved_arg(self, pymysql_conn: pymysql.Connection) -> None: + # The column is named conn, which collides with the connection + # argument and is deduped to conn_2. + queries.insert_reserved_arg(conn=pymysql_conn, id_=RESERVED_ID, conn_2=RESERVED_CONN) + + result = queries.get_reserved_arg(conn=pymysql_conn, conn_2=RESERVED_CONN) + assert result == models.TestReservedArg(id_=RESERVED_ID, conn=RESERVED_CONN) + assert queries.get_reserved_arg(conn=pymysql_conn, conn_2="pydantic-functions-missing") is None + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_reserved_args WHERE id = %s", (RESERVED_ID,)) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::case_insert") + def test_case_insert(self, pymysql_conn: pymysql.Connection) -> None: + queries_case.insert_case_row(conn=pymysql_conn, id_=CASE_IDS[0], upper_dt=CASE_DT, prec_dec=CASE_DEC) + queries_case.insert_case_row(conn=pymysql_conn, id_=CASE_IDS[1], upper_dt=CASE_DT, prec_dec=CASE_DEC) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::case_get", depends=["PymysqlPydanticFunctions::case_insert"]) + def test_case_get(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_case.get_case_row(conn=pymysql_conn, id_=CASE_IDS[0]) + + assert result is not None + assert result == models.TestCaseSensitivity(id_=CASE_IDS[0], upper_dt=CASE_DT, prec_dec=CASE_DEC) + assert queries_case.get_case_row(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::case_count", depends=["PymysqlPydanticFunctions::case_get"]) + def test_case_count(self, pymysql_conn: pymysql.Connection) -> None: + # The WHERE clause lives inside an executable /*! version comment; if + # MySQL ignored it both counts would be 2. + # Range-scoped asserts: the shared table may carry other files' rows, + # so counts outside [CASE_IDS[0], CASE_IDS[1]] must cancel out. + beyond = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[1] + 1) + high = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[1]) + low = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[0]) + assert beyond is not None + assert high is not None + assert low is not None + assert high - beyond == 1 + assert low - beyond == len(CASE_IDS) + + @pytest.mark.dependency(depends=["PymysqlPydanticFunctions::case_count"]) + def test_case_delete(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_case_sensitivity WHERE id IN (%s, %s)", CASE_IDS) + beyond = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[1] + 1) + low = queries_case.count_case_rows(conn=pymysql_conn, id_=CASE_IDS[0]) + assert beyond is not None + assert low is not None + assert low - beyond == 0 + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::enum_insert") + def test_enum_override_insert(self, pymysql_conn: pymysql.Connection) -> None: + # The overridden parameter is a plain str; the generated code converts + # it back through enums.TestEnumOverrideMoodTest. + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=ENUM_IDS[0], mood_test="happy") + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=ENUM_IDS[1], mood_test="sad") + with pytest.raises(ValueError, match="angry"): + queries_enum_override.insert_enum_override(conn=pymysql_conn, id_=MISSING_ID, mood_test="angry") + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::enum_get", depends=["PymysqlPydanticFunctions::enum_insert"]) + def test_enum_override_get(self, pymysql_conn: pymysql.Connection) -> None: + mood = queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=ENUM_IDS[0]) + + assert mood is not None + assert isinstance(mood, str) + assert mood == "happy" + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::enum_list", depends=["PymysqlPydanticFunctions::enum_insert"]) + def test_enum_override_list(self, pymysql_conn: pymysql.Connection) -> None: + rows = queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=list(ENUM_IDS))() + + assert all(isinstance(row, models.TestEnumOverride) for row in rows) + assert {row.id_: row.mood_test for row in rows} == {ENUM_IDS[0]: "happy", ENUM_IDS[1]: "sad"} + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::enum_iterate", depends=["PymysqlPydanticFunctions::enum_insert"]) + def test_enum_override_iterate(self, pymysql_conn: pymysql.Connection) -> None: + seen: dict[int, str] = {} + for row in queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=list(ENUM_IDS)): + assert isinstance(row, models.TestEnumOverride) + seen[row.id_] = row.mood_test + assert seen == {ENUM_IDS[0]: "happy", ENUM_IDS[1]: "sad"} + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::enum_empty", depends=["PymysqlPydanticFunctions::enum_insert"]) + def test_enum_override_empty_slice(self, pymysql_conn: pymysql.Connection) -> None: + assert list(queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=[])()) == [] + assert list(queries_enum_override.list_enum_override_by_ids(conn=pymysql_conn, ids=[])) == [] + + @pytest.mark.dependency(depends=["PymysqlPydanticFunctions::enum_empty"]) + def test_enum_override_delete(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_enum_override WHERE id IN (%s, %s)", ENUM_IDS) + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=ENUM_IDS[0]) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::field_insert") + def test_field_naming_insert(self, pymysql_conn: pymysql.Connection) -> None: + # There is no generated insert for this table. + with pymysql_conn.cursor() as cur: + cur.execute("INSERT INTO test_field_namings (id, outputs) VALUES (%s, %s)", (FIELD_ID, json.dumps(["first", "second"]))) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::field_get", depends=["PymysqlPydanticFunctions::field_insert"]) + def test_field_naming_get(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_ID) + + assert result is not None + assert isinstance(result, models.TestFieldNaming) + assert result.id_ == FIELD_ID + assert json.loads(result.outputs) == ["first", "second"] + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::field_joined", depends=["PymysqlPydanticFunctions::field_get"]) + def test_field_naming_joined(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_field_namings.get_joined_field_namings(conn=pymysql_conn, id_=FIELD_ID) + + assert result is not None + assert isinstance(result, queries_field_namings.GetJoinedFieldNamingsRow) + assert json.loads(result.outputs) == ["first", "second"] + assert json.loads(result.outputs_2) == ["first", "second"] + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::field_set", depends=["PymysqlPydanticFunctions::field_joined"]) + def test_field_naming_set(self, pymysql_conn: pymysql.Connection) -> None: + queries_field_namings.set_field_naming_outputs(conn=pymysql_conn, outputs=json.dumps({"count": 2}), id_=FIELD_ID) + + result = queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_ID) + assert result is not None + assert json.loads(result.outputs) == {"count": 2} + + @pytest.mark.dependency(depends=["PymysqlPydanticFunctions::field_set"]) + def test_field_naming_delete(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_field_namings WHERE id = %s", (FIELD_ID,)) + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=FIELD_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::invalid_insert") + def test_invalid_identifiers_insert(self, pymysql_conn: pymysql.Connection) -> None: + queries_invalid_identifiers.insert_invalid_identifiers(conn=pymysql_conn, id_=INVALID_ID, column_3p_="3p value", new_notes="note value") + queries_invalid_identifiers.insert_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID, total=987) + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::invalid_get", depends=["PymysqlPydanticFunctions::invalid_insert"]) + def test_invalid_identifiers_get(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=INVALID_ID) + + assert result == models.TestInvalidIdentifier(id_=INVALID_ID, column_3p_="3p value", new_notes="note value", column__pct=None) + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(name="PymysqlPydanticFunctions::third_party_get", depends=["PymysqlPydanticFunctions::invalid_insert"]) + def test_third_party_stat_get(self, pymysql_conn: pymysql.Connection) -> None: + result = queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID) + + assert result == models.Model3RdPartyStat(id_=THIRD_PARTY_ID, total=987) + assert queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=MISSING_ID) is None + + @pytest.mark.dependency(depends=["PymysqlPydanticFunctions::invalid_get", "PymysqlPydanticFunctions::third_party_get"]) + def test_invalid_identifiers_delete(self, pymysql_conn: pymysql.Connection) -> None: + with pymysql_conn.cursor() as cur: + cur.execute("DELETE FROM test_invalid_identifiers WHERE id = %s", (INVALID_ID,)) + cur.execute("DELETE FROM `3rd_party_stats` WHERE id = %s", (THIRD_PARTY_ID,)) + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=INVALID_ID) is None + assert queries_invalid_identifiers.get_third_party_stat(conn=pymysql_conn, id_=THIRD_PARTY_ID) is None + + def test_one_missing_rows_return_none(self, pymysql_conn: pymysql.Connection) -> None: + # Every :one not-found branch, plus the no-insert :execlastid. The + # count queries always return a row, so their miss branch needs the + # no-row stub. + assert queries.get_one_mysql_type(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_inner_mysql_type(conn=pymysql_conn, table_id=-1) is None + assert queries.get_one_date(conn=pymysql_conn, id_=-1, date_test=datetime.date(1970, 1, 1)) is None + assert queries.get_one_datetime(conn=pymysql_conn, id_=-1, datetime_test=datetime.datetime(1970, 1, 1)) is None + assert queries.get_one_time(conn=pymysql_conn, id_=-1, time_test=datetime.timedelta()) is None + assert queries.get_one_bool(conn=pymysql_conn, id_=-1, tinyint1_test=False) is None + assert queries.get_one_decimal(conn=pymysql_conn, id_=-1, decimal_test=decimal.Decimal(0)) is None + assert queries.get_one_blob(conn=pymysql_conn, id_=-1, blob_test=memoryview(b"")) is None + assert queries.get_one_bit(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_year(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_json(conn=pymysql_conn, id_=-1) is None + assert queries.get_one_mood(conn=pymysql_conn, id_=-1, mood=enums.TestMysqlTypesMood.SAD) is None + assert queries.get_one_tag(conn=pymysql_conn, id_=-1) is None + assert queries.get_exec_last_id_name(conn=pymysql_conn, id_=-1) is None + assert queries.get_type_override(conn=pymysql_conn, id_=-1) is None + assert queries.get_reserved_arg(conn=pymysql_conn, conn_2="missing") is None + assert queries.touch_exec_last_id(conn=pymysql_conn, name="untouched", id_=-1) is None + assert queries_case.get_case_row(conn=pymysql_conn, id_=-1) is None + assert queries_field_namings.get_field_naming(conn=pymysql_conn, id_=-1) is None + assert queries_field_namings.get_joined_field_namings(conn=pymysql_conn, id_=-1) is None + assert queries_invalid_identifiers.get_invalid_identifiers(conn=pymysql_conn, id_=-1) is None + assert queries_enum_override.get_enum_override_mood(conn=pymysql_conn, id_=-1) is None + assert queries_enum_override.count_enum_override_by_moods(conn=pymysql_conn, moods=[]) == 0 + + stub = typing.cast("pymysql.Connection", no_row_conn.NoRowConn()) + assert queries.count_mysql_types(conn=stub) is None + assert queries_case.count_case_rows(conn=stub, id_=0) is None + assert queries_enum_override.count_enum_override_by_moods(conn=stub, moods=[]) is None diff --git a/test/driver_pymysql/queries.sql b/test/driver_pymysql/queries.sql new file mode 100644 index 00000000..182f9bdb --- /dev/null +++ b/test/driver_pymysql/queries.sql @@ -0,0 +1,146 @@ +-- name: InsertOneMysqlType :exec +INSERT INTO test_mysql_types ( + id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, + ?, ?, ?, + ?, ?, ?, ?, + ?, ?, + ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ? + ); + +-- name: InsertOneInnerMysqlType :exec +INSERT INTO test_inner_mysql_types ( + table_id, int_test, integer_test, mediumint_test, smallint_test, tinyint_test, bigint_test, + int_unsigned_test, bigint_unsigned_test, year_test, + tinyint1_test, bool_test, boolean_test, + float_test, double_test, double_precision_test, real_test, + decimal_test, numeric_test, + char_test, varchar_test, tinytext_test, text_test, mediumtext_test, longtext_test, + binary_test, varbinary_test, tinyblob_test, blob_test, mediumblob_test, longblob_test, bit_test, + date_test, datetime_test, datetime6_test, timestamp_test, time_test, + json_test, mood, tag +) VALUES ( + ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, + ?, ?, ?, + ?, ?, ?, ?, + ?, ?, + ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ? + ); + +-- name: GetOneMysqlType :one +SELECT * FROM test_mysql_types WHERE id = ?; + +-- name: GetOneInnerMysqlType :one +SELECT * FROM test_inner_mysql_types WHERE table_id = ?; + +-- name: GetManyMysqlType :many +SELECT * FROM test_mysql_types WHERE id = ?; + +-- name: GetManyInnerMysqlType :many +SELECT * FROM test_inner_mysql_types WHERE table_id = ?; + +-- name: GetManyNullableInnerMysqlType :many +SELECT * FROM test_inner_mysql_types WHERE table_id = ? AND int_test <=> ?; + +-- name: GetOneDate :one +SELECT date_test FROM test_mysql_types WHERE id = ? AND date_test = ?; + +-- name: GetOneDatetime :one +SELECT datetime_test FROM test_mysql_types WHERE id = ? AND datetime_test = ?; + +-- name: GetOneTime :one +SELECT time_test FROM test_mysql_types WHERE id = ? AND time_test = ?; + +-- name: GetOneBool :one +SELECT tinyint1_test FROM test_mysql_types WHERE id = ? AND tinyint1_test = ?; + +-- name: GetOneDecimal :one +SELECT decimal_test FROM test_mysql_types WHERE id = ? AND decimal_test = ?; + +-- name: GetOneBlob :one +SELECT blob_test FROM test_mysql_types WHERE id = ? AND blob_test = ?; + +-- name: GetOneBit :one +SELECT bit_test FROM test_mysql_types WHERE id = ?; + +-- name: GetOneYear :one +SELECT year_test FROM test_mysql_types WHERE id = ?; + +-- name: GetOneJson :one +SELECT json_test FROM test_mysql_types WHERE id = ?; + +-- name: GetOneMood :one +SELECT mood FROM test_mysql_types WHERE id = ? AND mood = ?; + +-- name: GetOneTag :one +SELECT tag FROM test_mysql_types WHERE id = ?; + +-- name: GetManyDate :many +SELECT date_test FROM test_mysql_types WHERE id = ? AND date_test = ?; + +-- name: GetManyTime :many +SELECT time_test FROM test_mysql_types WHERE id = ? AND time_test = ?; + +-- name: GetManyBool :many +SELECT tinyint1_test FROM test_mysql_types WHERE id = ? AND tinyint1_test = ?; + +-- name: GetManyDecimal :many +SELECT decimal_test FROM test_mysql_types WHERE id = ? AND decimal_test = ?; + +-- name: GetManyMood :many +SELECT mood FROM test_mysql_types WHERE mood = ? ORDER BY id; + +-- Parameterless :many with literal percents: QueryResults always passes its +-- args tuple, so the constant must arrive with doubled "%%". +-- name: ListMonths :many +SELECT DATE_FORMAT(datetime_test, '%Y-%m') AS month FROM test_mysql_types ORDER BY id; + +-- name: CountMysqlTypes :one +SELECT count(*) FROM test_mysql_types; + +-- name: UpdateVarcharTest :execrows +UPDATE test_mysql_types SET varchar_test = ? WHERE id = ?; + +-- name: DeleteOneMysqlType :exec +DELETE FROM test_mysql_types WHERE id = ?; + +-- name: AllMysqlTypesCursor :execresult +SELECT * FROM test_mysql_types; + +-- name: InsertExecLastId :execlastid +INSERT INTO test_execlastid (name) VALUES (?); + +-- name: GetExecLastIdName :one +SELECT name FROM test_execlastid WHERE id = ?; + +-- name: InsertTypeOverride :exec +INSERT INTO test_type_override (id, text_test) VALUES (?, ?); + +-- name: GetTypeOverride :one +SELECT * FROM test_type_override WHERE id = ?; + +-- name: GetReservedArg :one +SELECT * FROM test_reserved_args WHERE conn = ?; + +-- name: InsertReservedArg :exec +INSERT INTO test_reserved_args (id, conn) VALUES (?, ?); + +-- name: TouchExecLastId :execlastid +UPDATE test_execlastid SET name = ? WHERE id = ?; diff --git a/test/driver_pymysql/queries_case.sql b/test/driver_pymysql/queries_case.sql new file mode 100644 index 00000000..72626368 --- /dev/null +++ b/test/driver_pymysql/queries_case.sql @@ -0,0 +1,10 @@ +-- name: InsertCaseRow :exec +INSERT INTO test_case_sensitivity (id, upper_dt, prec_dec) VALUES (?, ?, ?); + +-- name: GetCaseRow :one +SELECT * FROM test_case_sensitivity WHERE id = ?; + +-- Placeholder inside an executable /*! version comment: the body is live +-- SQL to both MySQL and sqlc, so the ? must become a real %s. +-- name: CountCaseRows :one +SELECT count(*) FROM test_case_sensitivity /*! WHERE id >= ? */; diff --git a/test/driver_pymysql/queries_converters.sql b/test/driver_pymysql/queries_converters.sql new file mode 100644 index 00000000..62dcea4c --- /dev/null +++ b/test/driver_pymysql/queries_converters.sql @@ -0,0 +1,11 @@ +-- name: InsertConverted :exec +INSERT INTO test_converters (id, prefs, maybe_prefs, tags) VALUES (?, ?, ?, ?); + +-- name: GetConverted :one +SELECT * FROM test_converters WHERE id = ?; + +-- name: ListConvertedByTags :many +SELECT id FROM test_converters WHERE tags = ?; + +-- name: DeleteConverted :exec +DELETE FROM test_converters WHERE id = ?; diff --git a/test/driver_pymysql/queries_dbtype_override.sql b/test/driver_pymysql/queries_dbtype_override.sql new file mode 100644 index 00000000..96e4d6dd --- /dev/null +++ b/test/driver_pymysql/queries_dbtype_override.sql @@ -0,0 +1,5 @@ +-- name: InsertDbtypeOverride :exec +INSERT INTO test_dbtype_override (id, happened_at) VALUES (?, ?); + +-- name: GetDbtypeOverride :one +SELECT * FROM test_dbtype_override WHERE id = ?; diff --git a/test/driver_pymysql/queries_enum_override.sql b/test/driver_pymysql/queries_enum_override.sql new file mode 100644 index 00000000..6883a1d8 --- /dev/null +++ b/test/driver_pymysql/queries_enum_override.sql @@ -0,0 +1,13 @@ +-- name: InsertEnumOverride :exec +INSERT INTO test_enum_override (id, mood_test) VALUES (?, ?); + +-- name: GetEnumOverrideMood :one +SELECT mood_test FROM test_enum_override WHERE id = ?; + +-- name: ListEnumOverrideByIds :many +SELECT id, mood_test FROM test_enum_override WHERE id IN (sqlc.slice('ids')) ORDER BY id; + +-- Element-wise slice conversion: each element converts back through the +-- overridden column's enum class before binding. +-- name: CountEnumOverrideByMoods :one +SELECT count(*) FROM test_enum_override WHERE mood_test IN (sqlc.slice('moods')); diff --git a/test/driver_pymysql/queries_field_namings.sql b/test/driver_pymysql/queries_field_namings.sql new file mode 100644 index 00000000..530af0c0 --- /dev/null +++ b/test/driver_pymysql/queries_field_namings.sql @@ -0,0 +1,15 @@ +-- name: GetFieldNaming :one +SELECT * +FROM test_field_namings +WHERE id = ? LIMIT 1; + +-- name: GetJoinedFieldNamings :one +SELECT a.outputs, b.outputs +FROM test_field_namings a +JOIN test_field_namings b ON a.id = b.id +WHERE a.id = ? LIMIT 1; + +-- name: SetFieldNamingOutputs :exec +UPDATE test_field_namings +SET outputs = ? +WHERE id = ?; diff --git a/test/driver_pymysql/queries_invalid_identifiers.sql b/test/driver_pymysql/queries_invalid_identifiers.sql new file mode 100644 index 00000000..8f175316 --- /dev/null +++ b/test/driver_pymysql/queries_invalid_identifiers.sql @@ -0,0 +1,13 @@ +-- name: InsertInvalidIdentifiers :exec +INSERT INTO test_invalid_identifiers (id, `3p%`, `new notes`) VALUES (?, ?, ?); + +-- sqlc's star expansion drops the backtick quoting on MySQL, so the +-- columns are listed explicitly. +-- name: GetInvalidIdentifiers :one +SELECT id, `3p%`, `new notes`, `%pct` FROM test_invalid_identifiers WHERE id = ?; + +-- name: InsertThirdPartyStat :exec +INSERT INTO `3rd_party_stats` (id, total) VALUES (?, ?); + +-- name: GetThirdPartyStat :one +SELECT * FROM `3rd_party_stats` WHERE id = ?; diff --git a/test/driver_pymysql/queries_slice.sql b/test/driver_pymysql/queries_slice.sql new file mode 100644 index 00000000..46cab028 --- /dev/null +++ b/test/driver_pymysql/queries_slice.sql @@ -0,0 +1,23 @@ +-- name: InsertSliceRow :exec +INSERT INTO test_slice (id, name, note) VALUES (?, ?, ?); + +-- name: GetSliceRows :many +SELECT * FROM test_slice WHERE id IN (sqlc.slice('ids')) ORDER BY id; + +-- name: GetSliceRowFiltered :one +SELECT * FROM test_slice WHERE name = ? AND id IN (sqlc.slice('ids')) AND id != ? LIMIT 1; + +-- name: GetSliceRowsByNotes :many +SELECT * FROM test_slice WHERE note IN (sqlc.slice('notes')) ORDER BY id; + +-- name: GetFirstSliceName :one +SELECT name FROM test_slice WHERE id IN (sqlc.slice('ids')) OR name IN (sqlc.slice('names')) ORDER BY id LIMIT 1; + +-- name: GetSliceRowsByNameOrNote :many +SELECT * FROM test_slice WHERE name IN (sqlc.slice('names')) OR note IN (sqlc.slice('names')) ORDER BY id; + +-- name: GetSliceRowsByNameOrNoteFiltered :many +SELECT * FROM test_slice WHERE name IN (sqlc.slice('names')) AND id != ? OR note IN (sqlc.slice('names')) ORDER BY id; + +-- name: DeleteSliceRows :execrows +DELETE FROM test_slice WHERE id IN (sqlc.slice('ids')); diff --git a/test/driver_pymysql/schema.sql b/test/driver_pymysql/schema.sql new file mode 100644 index 00000000..c59ba0d8 --- /dev/null +++ b/test/driver_pymysql/schema.sql @@ -0,0 +1,189 @@ +CREATE TABLE IF NOT EXISTS test_mysql_types +( + /* ------------- Integer family ------------- */ + id bigint PRIMARY KEY NOT NULL, + int_test int NOT NULL, + integer_test integer NOT NULL, + mediumint_test mediumint NOT NULL, + smallint_test smallint NOT NULL, + tinyint_test tinyint NOT NULL, -- plain tinyint stays int + bigint_test bigint NOT NULL, + int_unsigned_test int unsigned NOT NULL, + bigint_unsigned_test bigint unsigned NOT NULL, + year_test year NOT NULL, + /* ------------- Boolean (tinyint(1) and its aliases) ------------- */ + tinyint1_test tinyint(1) NOT NULL, + bool_test bool NOT NULL, + boolean_test boolean NOT NULL, + /* ------------- Floating-point ------------- */ + float_test float NOT NULL, + double_test double NOT NULL, + double_precision_test double precision NOT NULL, + real_test real NOT NULL, + /* ------------- Exact numeric (decimal) ------------- */ + decimal_test decimal(12,4) NOT NULL, + numeric_test numeric(10,2) NOT NULL, + /* ------------- Character / text ------------- */ + char_test char(10) NOT NULL, + varchar_test varchar(255) NOT NULL, + tinytext_test tinytext NOT NULL, + text_test text NOT NULL, + mediumtext_test mediumtext NOT NULL, + longtext_test longtext NOT NULL, + /* ------------- Binary ------------- */ + binary_test binary(16) NOT NULL, + varbinary_test varbinary(255) NOT NULL, + tinyblob_test tinyblob NOT NULL, + blob_test blob NOT NULL, + mediumblob_test mediumblob NOT NULL, + longblob_test longblob NOT NULL, + bit_test bit(8) NOT NULL, + /* ------------- Date & time (time maps to timedelta) ------------- */ + date_test date NOT NULL, + datetime_test datetime NOT NULL, + datetime6_test datetime(6) NOT NULL, + timestamp_test timestamp NOT NULL, + time_test time NOT NULL, + /* ------------- JSON (kept as str) ------------- */ + json_test json NOT NULL, + /* ------------- Inline enum and set ------------- */ + -- '24h' and '_hidden' pin the digit- and underscore-leading constant + -- names of the synthesized test_mysql_types_mood enum class. + mood enum('sad','ok','happy','24h','_hidden') NOT NULL, + -- SET columns become StrEnums like enum columns (sqlc materializes + -- both). Only single-valued sets round-trip, see the docs. + tag set('alpha','beta','gamma') NOT NULL +); + +CREATE TABLE IF NOT EXISTS test_inner_mysql_types +( + table_id bigint PRIMARY KEY NOT NULL, + int_test int, + integer_test integer, + mediumint_test mediumint, + smallint_test smallint, + tinyint_test tinyint, + bigint_test bigint, + int_unsigned_test int unsigned, + bigint_unsigned_test bigint unsigned, + year_test year, + tinyint1_test tinyint(1), + bool_test bool, + boolean_test boolean, + float_test float, + double_test double, + double_precision_test double precision, + real_test real, + decimal_test decimal(12,4), + numeric_test numeric(10,2), + char_test char(10), + varchar_test varchar(255), + tinytext_test tinytext, + text_test text, + mediumtext_test mediumtext, + longtext_test longtext, + binary_test binary(16), + varbinary_test varbinary(255), + tinyblob_test tinyblob, + blob_test blob, + mediumblob_test mediumblob, + longblob_test longblob, + bit_test bit(8), + date_test date, + datetime_test datetime, + datetime6_test datetime(6), + timestamp_test timestamp, + time_test time, + json_test json, + mood enum('sad','ok','happy','24h','_hidden'), + tag set('alpha','beta','gamma') +); + +CREATE TABLE IF NOT EXISTS test_type_override +( + id bigint PRIMARY KEY NOT NULL, + text_test text +); + +-- Enum column with a py_type override: the override wins over the +-- synthesized enum class, and parameters convert back through it. +CREATE TABLE IF NOT EXISTS test_enum_override +( + id bigint PRIMARY KEY NOT NULL, + mood_test enum('sad','ok','happy') NOT NULL +); + +-- Uppercase type names and precision variants exercise the SQL-type +-- normalization. The version-comment query in queries_case.sql lives on +-- this table too. +CREATE TABLE IF NOT EXISTS test_case_sensitivity +( + id bigint PRIMARY KEY NOT NULL, + upper_dt DATETIME NOT NULL, + prec_dec DECIMAL(10,2) NOT NULL +); + +-- A column named like the implicit first argument of generated functions. +CREATE TABLE IF NOT EXISTS test_reserved_args +( + id bigint PRIMARY KEY NOT NULL, + conn varchar(64) NOT NULL +); + +-- :execlastid reads cursor.lastrowid from the AUTO_INCREMENT key. serial +-- is the bigint unsigned AUTO_INCREMENT alias. +CREATE TABLE IF NOT EXISTS test_execlastid +( + id serial PRIMARY KEY, + name varchar(64) NOT NULL +); + +-- Plural column name: field names must NOT be singularized (only table +-- names and embed fields are). Ported from PR 164. +CREATE TABLE IF NOT EXISTS test_field_namings +( + id bigint PRIMARY KEY NOT NULL, + outputs json NOT NULL +); + +-- Backtick-quoted identifiers that are not valid Python names (issue 160). +CREATE TABLE IF NOT EXISTS test_invalid_identifiers +( + id bigint PRIMARY KEY NOT NULL, + `3p%` text, + `new notes` text NOT NULL, + `%pct` text +); + +-- Digit-leading table name: the class gets a Model prefix (Model3RdPartyStat). +CREATE TABLE IF NOT EXISTS `3rd_party_stats` +( + id bigint PRIMARY KEY NOT NULL, + total bigint NOT NULL +); + +-- Variable-length IN lists via sqlc.slice: the /*SLICE:name*/ placeholder in +-- the SQL constant is expanded at call time, one "%s" per element. +CREATE TABLE IF NOT EXISTS test_slice +( + id bigint PRIMARY KEY NOT NULL, + name varchar(64) NOT NULL, + note varchar(64) +); + +CREATE TABLE IF NOT EXISTS test_converters +( + id bigint PRIMARY KEY NOT NULL, + prefs json NOT NULL, + maybe_prefs json, + tags text NOT NULL +); + +-- db_type override target: DATETIME must match the normalized type name +-- case-insensitively, through a converter (postgres parity). Isolated in +-- its own module so the main matrix's datetime columns stay untouched. +CREATE TABLE IF NOT EXISTS test_dbtype_override +( + id bigint PRIMARY KEY NOT NULL, + happened_at datetime NOT NULL +); diff --git a/test/driver_pymysql/sqlc-gen-better-python.wasm b/test/driver_pymysql/sqlc-gen-better-python.wasm new file mode 100755 index 00000000..dfb69b04 Binary files /dev/null and b/test/driver_pymysql/sqlc-gen-better-python.wasm differ diff --git a/test/driver_pymysql/sqlc.yaml b/test/driver_pymysql/sqlc.yaml new file mode 100644 index 00000000..5d567327 --- /dev/null +++ b/test/driver_pymysql/sqlc.yaml @@ -0,0 +1,320 @@ +version: "2" +plugins: + - name: python + wasm: + url: file://sqlc-gen-better-python.wasm + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 +sql: + - schema: schema.sql + queries: + - queries.sql + - queries_case.sql + - queries_enum_override.sql + - queries_field_namings.sql + - queries_invalid_identifiers.sql + engine: mysql + codegen: + - out: /attrs/classes + plugin: python + options: + package: test.driver_pymysql.attrs.classes + sql_driver: pymysql + model_type: attrs + emit_classes: true + omit_unused_models: false + emit_init_file: true + docstrings: numpy + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + - queries_case.sql + - queries_enum_override.sql + - queries_field_namings.sql + - queries_invalid_identifiers.sql + engine: mysql + codegen: + - out: /attrs/functions + plugin: python + options: + package: test.driver_pymysql.attrs.functions + sql_driver: pymysql + model_type: attrs + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: numpy + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + - queries_case.sql + - queries_enum_override.sql + - queries_field_namings.sql + - queries_invalid_identifiers.sql + engine: mysql + codegen: + - out: /dataclass/classes + plugin: python + options: + package: test.driver_pymysql.dataclass.classes + sql_driver: pymysql + model_type: dataclass + emit_classes: true + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + - queries_case.sql + - queries_enum_override.sql + - queries_field_namings.sql + - queries_invalid_identifiers.sql + - queries_converters.sql + - queries_slice.sql + engine: mysql + codegen: + - out: /dataclass/functions + plugin: python + options: + package: test.driver_pymysql.dataclass.functions + sql_driver: pymysql + model_type: dataclass + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - column: test_converters.prefs + converter: prefs + - column: test_converters.maybe_prefs + converter: prefs + - column: test_converters.tags + converter: tags + converters: + - name: prefs + py_type: + import: test.converters + package: Preferences + type: Preferences + to_db: test.converters.encode_preferences + from_db: test.converters.decode_preferences + - name: tags + py_type: + type: frozenset[str] + to_db: test.converters.encode_tags + from_db: test.converters.decode_tags + - schema: schema.sql + queries: + - queries.sql + - queries_case.sql + - queries_enum_override.sql + - queries_field_namings.sql + - queries_invalid_identifiers.sql + engine: mysql + codegen: + - out: /msgspec/classes + plugin: python + options: + package: test.driver_pymysql.msgspec.classes + sql_driver: pymysql + model_type: msgspec + emit_classes: true + omit_unused_models: true + emit_init_file: true + docstrings: pep257 + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + - queries_case.sql + - queries_enum_override.sql + - queries_field_namings.sql + - queries_invalid_identifiers.sql + engine: mysql + codegen: + - out: /msgspec/functions + plugin: python + options: + package: test.driver_pymysql.msgspec.functions + sql_driver: pymysql + model_type: msgspec + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: pep257 + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + - queries_case.sql + - queries_enum_override.sql + - queries_field_namings.sql + - queries_invalid_identifiers.sql + engine: mysql + codegen: + - out: /pydantic/classes + plugin: python + options: + package: test.driver_pymysql.pydantic.classes + sql_driver: pymysql + model_type: pydantic + emit_classes: true + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: + - queries.sql + - queries_case.sql + - queries_enum_override.sql + - queries_field_namings.sql + - queries_invalid_identifiers.sql + engine: mysql + codegen: + - out: /pydantic/functions + plugin: python + options: + package: test.driver_pymysql.pydantic.functions + sql_driver: pymysql + model_type: pydantic + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - column: test_type_override.text_test + py_type: + import: collections + package: UserString + type: UserString + - column: test_enum_override.mood_test + py_type: + type: str + # db_type override coverage: DATETIME (uppercase on purpose) must match + # the normalized type name case-insensitively, converting through the + # stamp converter pair. Isolated module so the matrix datetimes stay + # untouched. + - schema: schema.sql + queries: queries_dbtype_override.sql + engine: mysql + codegen: + - out: /dbtype/functions + plugin: python + options: + package: test.driver_pymysql.dbtype.functions + sql_driver: pymysql + model_type: dataclass + emit_classes: false + omit_unused_models: true + emit_init_file: true + docstrings: google + overrides: + - db_type: DATETIME + converter: stamp + converters: + - name: stamp + py_type: + type: str + to_db: test.converters.encode_stamp + from_db: test.converters.decode_stamp + # omit_typechecking_block coverage: the PEP 695 aliases and hoisted + # annotation imports must be runtime-safe at module level, pinned on the + # enum-override module. + - schema: schema.sql + queries: queries_enum_override.sql + engine: mysql + codegen: + - out: /omit_tc/classes + plugin: python + options: + package: test.driver_pymysql.omit_tc.classes + sql_driver: pymysql + model_type: dataclass + emit_classes: true + omit_unused_models: true + emit_init_file: true + omit_typechecking_block: true + docstrings: google + overrides: + - column: test_enum_override.mood_test + py_type: + type: str + - schema: schema.sql + queries: queries_enum_override.sql + engine: mysql + codegen: + - out: /omit_tc/functions + plugin: python + options: + package: test.driver_pymysql.omit_tc.functions + sql_driver: pymysql + model_type: dataclass + emit_classes: false + omit_unused_models: true + emit_init_file: true + omit_typechecking_block: true + docstrings: google + overrides: + - column: test_enum_override.mood_test + py_type: + type: str diff --git a/test/driver_sqlite3/attrs/classes/__init__.py b/test/driver_sqlite3/attrs/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_sqlite3/attrs/classes/__init__.py +++ b/test/driver_sqlite3/attrs/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_sqlite3/attrs/classes/models.py b/test/driver_sqlite3/attrs/classes/models.py index 7e7db66c..254f60c7 100644 --- a/test/driver_sqlite3/attrs/classes/models.py +++ b/test/driver_sqlite3/attrs/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_sqlite3/attrs/classes/queries.py b/test/driver_sqlite3/attrs/classes/queries.py index 79a6cd8a..334c9c9e 100644 --- a/test/driver_sqlite3/attrs/classes/queries.py +++ b/test/driver_sqlite3/attrs/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_sqlite3/attrs/classes/queries_case.py b/test/driver_sqlite3/attrs/classes/queries_case.py index c2b69c6b..93f16682 100644 --- a/test/driver_sqlite3/attrs/classes/queries_case.py +++ b/test/driver_sqlite3/attrs/classes/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_sqlite3/attrs/classes/queries_override_adapter.py b/test/driver_sqlite3/attrs/classes/queries_override_adapter.py index 0b401c2e..03a7dd58 100644 --- a/test/driver_sqlite3/attrs/classes/queries_override_adapter.py +++ b/test/driver_sqlite3/attrs/classes/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_sqlite3/attrs/classes/queries_override_converter.py b/test/driver_sqlite3/attrs/classes/queries_override_converter.py index e92981f2..6670535b 100644 --- a/test/driver_sqlite3/attrs/classes/queries_override_converter.py +++ b/test/driver_sqlite3/attrs/classes/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_sqlite3/attrs/classes/queries_unknown_override.py b/test/driver_sqlite3/attrs/classes/queries_unknown_override.py index c723a283..911cd46e 100644 --- a/test/driver_sqlite3/attrs/classes/queries_unknown_override.py +++ b/test/driver_sqlite3/attrs/classes/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_sqlite3/attrs/functions/__init__.py b/test/driver_sqlite3/attrs/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_sqlite3/attrs/functions/__init__.py +++ b/test/driver_sqlite3/attrs/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_sqlite3/attrs/functions/models.py b/test/driver_sqlite3/attrs/functions/models.py index 9f2e2abb..73abe31d 100644 --- a/test/driver_sqlite3/attrs/functions/models.py +++ b/test/driver_sqlite3/attrs/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_sqlite3/attrs/functions/queries.py b/test/driver_sqlite3/attrs/functions/queries.py index 4eb1a1ef..53add09a 100644 --- a/test/driver_sqlite3/attrs/functions/queries.py +++ b/test/driver_sqlite3/attrs/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_sqlite3/attrs/functions/queries_case.py b/test/driver_sqlite3/attrs/functions/queries_case.py index 0fdb9271..2254c31d 100644 --- a/test/driver_sqlite3/attrs/functions/queries_case.py +++ b/test/driver_sqlite3/attrs/functions/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_sqlite3/attrs/functions/queries_override_adapter.py b/test/driver_sqlite3/attrs/functions/queries_override_adapter.py index 495e86df..c8748cc7 100644 --- a/test/driver_sqlite3/attrs/functions/queries_override_adapter.py +++ b/test/driver_sqlite3/attrs/functions/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_sqlite3/attrs/functions/queries_override_converter.py b/test/driver_sqlite3/attrs/functions/queries_override_converter.py index b06a7785..3f3e5a4e 100644 --- a/test/driver_sqlite3/attrs/functions/queries_override_converter.py +++ b/test/driver_sqlite3/attrs/functions/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_sqlite3/attrs/functions/queries_unknown_override.py b/test/driver_sqlite3/attrs/functions/queries_unknown_override.py index a258798a..8f8fff24 100644 --- a/test/driver_sqlite3/attrs/functions/queries_unknown_override.py +++ b/test/driver_sqlite3/attrs/functions/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_sqlite3/dataclass/classes/__init__.py b/test/driver_sqlite3/dataclass/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_sqlite3/dataclass/classes/__init__.py +++ b/test/driver_sqlite3/dataclass/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_sqlite3/dataclass/classes/models.py b/test/driver_sqlite3/dataclass/classes/models.py index dc3b902c..aba98a13 100644 --- a/test/driver_sqlite3/dataclass/classes/models.py +++ b/test/driver_sqlite3/dataclass/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_sqlite3/dataclass/classes/queries.py b/test/driver_sqlite3/dataclass/classes/queries.py index 2056ff10..63fa91e0 100644 --- a/test/driver_sqlite3/dataclass/classes/queries.py +++ b/test/driver_sqlite3/dataclass/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_sqlite3/dataclass/classes/queries_case.py b/test/driver_sqlite3/dataclass/classes/queries_case.py index 449ae771..a3c00cb5 100644 --- a/test/driver_sqlite3/dataclass/classes/queries_case.py +++ b/test/driver_sqlite3/dataclass/classes/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_sqlite3/dataclass/classes/queries_override_adapter.py b/test/driver_sqlite3/dataclass/classes/queries_override_adapter.py index 1dd16d35..cac8b6fb 100644 --- a/test/driver_sqlite3/dataclass/classes/queries_override_adapter.py +++ b/test/driver_sqlite3/dataclass/classes/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_sqlite3/dataclass/classes/queries_override_converter.py b/test/driver_sqlite3/dataclass/classes/queries_override_converter.py index 0e9a93de..149695d0 100644 --- a/test/driver_sqlite3/dataclass/classes/queries_override_converter.py +++ b/test/driver_sqlite3/dataclass/classes/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_sqlite3/dataclass/classes/queries_unknown_override.py b/test/driver_sqlite3/dataclass/classes/queries_unknown_override.py index d2bab358..3bd0121b 100644 --- a/test/driver_sqlite3/dataclass/classes/queries_unknown_override.py +++ b/test/driver_sqlite3/dataclass/classes/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_sqlite3/dataclass/functions/__init__.py b/test/driver_sqlite3/dataclass/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_sqlite3/dataclass/functions/__init__.py +++ b/test/driver_sqlite3/dataclass/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_sqlite3/dataclass/functions/models.py b/test/driver_sqlite3/dataclass/functions/models.py index edba6f59..03436fa9 100644 --- a/test/driver_sqlite3/dataclass/functions/models.py +++ b/test/driver_sqlite3/dataclass/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_sqlite3/dataclass/functions/queries.py b/test/driver_sqlite3/dataclass/functions/queries.py index 397d618a..f1635d26 100644 --- a/test/driver_sqlite3/dataclass/functions/queries.py +++ b/test/driver_sqlite3/dataclass/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_sqlite3/dataclass/functions/queries_any_param.py b/test/driver_sqlite3/dataclass/functions/queries_any_param.py index 22cffcd4..60c4f991 100644 --- a/test/driver_sqlite3/dataclass/functions/queries_any_param.py +++ b/test/driver_sqlite3/dataclass/functions/queries_any_param.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_any_param.sql """Module containing queries from file queries_any_param.sql.""" diff --git a/test/driver_sqlite3/dataclass/functions/queries_case.py b/test/driver_sqlite3/dataclass/functions/queries_case.py index 132680b9..c1f1a234 100644 --- a/test/driver_sqlite3/dataclass/functions/queries_case.py +++ b/test/driver_sqlite3/dataclass/functions/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_sqlite3/dataclass/functions/queries_override_adapter.py b/test/driver_sqlite3/dataclass/functions/queries_override_adapter.py index e526d428..c72b4523 100644 --- a/test/driver_sqlite3/dataclass/functions/queries_override_adapter.py +++ b/test/driver_sqlite3/dataclass/functions/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_sqlite3/dataclass/functions/queries_override_converter.py b/test/driver_sqlite3/dataclass/functions/queries_override_converter.py index 4b665357..2103fe23 100644 --- a/test/driver_sqlite3/dataclass/functions/queries_override_converter.py +++ b/test/driver_sqlite3/dataclass/functions/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_sqlite3/dataclass/functions/queries_slice.py b/test/driver_sqlite3/dataclass/functions/queries_slice.py index eba52d0d..766eafb0 100644 --- a/test/driver_sqlite3/dataclass/functions/queries_slice.py +++ b/test/driver_sqlite3/dataclass/functions/queries_slice.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_slice.sql """Module containing queries from file queries_slice.sql.""" diff --git a/test/driver_sqlite3/dataclass/functions/queries_unknown_override.py b/test/driver_sqlite3/dataclass/functions/queries_unknown_override.py index 9026f626..f364e800 100644 --- a/test/driver_sqlite3/dataclass/functions/queries_unknown_override.py +++ b/test/driver_sqlite3/dataclass/functions/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_sqlite3/msgspec/classes/__init__.py b/test/driver_sqlite3/msgspec/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_sqlite3/msgspec/classes/__init__.py +++ b/test/driver_sqlite3/msgspec/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_sqlite3/msgspec/classes/models.py b/test/driver_sqlite3/msgspec/classes/models.py index eabe2ef3..219c0288 100644 --- a/test/driver_sqlite3/msgspec/classes/models.py +++ b/test/driver_sqlite3/msgspec/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_sqlite3/msgspec/classes/queries.py b/test/driver_sqlite3/msgspec/classes/queries.py index e91247a1..6c716ace 100644 --- a/test/driver_sqlite3/msgspec/classes/queries.py +++ b/test/driver_sqlite3/msgspec/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_sqlite3/msgspec/classes/queries_case.py b/test/driver_sqlite3/msgspec/classes/queries_case.py index 10017aa2..c17c1938 100644 --- a/test/driver_sqlite3/msgspec/classes/queries_case.py +++ b/test/driver_sqlite3/msgspec/classes/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_sqlite3/msgspec/classes/queries_override_adapter.py b/test/driver_sqlite3/msgspec/classes/queries_override_adapter.py index 3a69b3e4..f1d30b36 100644 --- a/test/driver_sqlite3/msgspec/classes/queries_override_adapter.py +++ b/test/driver_sqlite3/msgspec/classes/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_sqlite3/msgspec/classes/queries_override_converter.py b/test/driver_sqlite3/msgspec/classes/queries_override_converter.py index 2a0dd269..ba596d6f 100644 --- a/test/driver_sqlite3/msgspec/classes/queries_override_converter.py +++ b/test/driver_sqlite3/msgspec/classes/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_sqlite3/msgspec/classes/queries_unknown_override.py b/test/driver_sqlite3/msgspec/classes/queries_unknown_override.py index ad38b494..a95eb6ea 100644 --- a/test/driver_sqlite3/msgspec/classes/queries_unknown_override.py +++ b/test/driver_sqlite3/msgspec/classes/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_sqlite3/msgspec/functions/__init__.py b/test/driver_sqlite3/msgspec/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_sqlite3/msgspec/functions/__init__.py +++ b/test/driver_sqlite3/msgspec/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_sqlite3/msgspec/functions/models.py b/test/driver_sqlite3/msgspec/functions/models.py index eabe2ef3..219c0288 100644 --- a/test/driver_sqlite3/msgspec/functions/models.py +++ b/test/driver_sqlite3/msgspec/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_sqlite3/msgspec/functions/queries.py b/test/driver_sqlite3/msgspec/functions/queries.py index ee07b21d..3e8d3517 100644 --- a/test/driver_sqlite3/msgspec/functions/queries.py +++ b/test/driver_sqlite3/msgspec/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_sqlite3/msgspec/functions/queries_case.py b/test/driver_sqlite3/msgspec/functions/queries_case.py index 969f7216..5adadb73 100644 --- a/test/driver_sqlite3/msgspec/functions/queries_case.py +++ b/test/driver_sqlite3/msgspec/functions/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_sqlite3/msgspec/functions/queries_override_adapter.py b/test/driver_sqlite3/msgspec/functions/queries_override_adapter.py index 6b83bb14..9a35a12a 100644 --- a/test/driver_sqlite3/msgspec/functions/queries_override_adapter.py +++ b/test/driver_sqlite3/msgspec/functions/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_sqlite3/msgspec/functions/queries_override_converter.py b/test/driver_sqlite3/msgspec/functions/queries_override_converter.py index d081cf15..2ae01b84 100644 --- a/test/driver_sqlite3/msgspec/functions/queries_override_converter.py +++ b/test/driver_sqlite3/msgspec/functions/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_sqlite3/msgspec/functions/queries_unknown_override.py b/test/driver_sqlite3/msgspec/functions/queries_unknown_override.py index de86364a..3258f95f 100644 --- a/test/driver_sqlite3/msgspec/functions/queries_unknown_override.py +++ b/test/driver_sqlite3/msgspec/functions/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_sqlite3/pydantic/classes/__init__.py b/test/driver_sqlite3/pydantic/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_sqlite3/pydantic/classes/__init__.py +++ b/test/driver_sqlite3/pydantic/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_sqlite3/pydantic/classes/models.py b/test/driver_sqlite3/pydantic/classes/models.py index 3bb851b3..ccd04019 100644 --- a/test/driver_sqlite3/pydantic/classes/models.py +++ b/test/driver_sqlite3/pydantic/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_sqlite3/pydantic/classes/queries.py b/test/driver_sqlite3/pydantic/classes/queries.py index b59c984e..205c32e7 100644 --- a/test/driver_sqlite3/pydantic/classes/queries.py +++ b/test/driver_sqlite3/pydantic/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_sqlite3/pydantic/classes/queries_case.py b/test/driver_sqlite3/pydantic/classes/queries_case.py index 54fd532f..bcd9fa2b 100644 --- a/test/driver_sqlite3/pydantic/classes/queries_case.py +++ b/test/driver_sqlite3/pydantic/classes/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_sqlite3/pydantic/classes/queries_override_adapter.py b/test/driver_sqlite3/pydantic/classes/queries_override_adapter.py index 1dd16d35..cac8b6fb 100644 --- a/test/driver_sqlite3/pydantic/classes/queries_override_adapter.py +++ b/test/driver_sqlite3/pydantic/classes/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_sqlite3/pydantic/classes/queries_override_converter.py b/test/driver_sqlite3/pydantic/classes/queries_override_converter.py index 0e9a93de..149695d0 100644 --- a/test/driver_sqlite3/pydantic/classes/queries_override_converter.py +++ b/test/driver_sqlite3/pydantic/classes/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_sqlite3/pydantic/classes/queries_unknown_override.py b/test/driver_sqlite3/pydantic/classes/queries_unknown_override.py index d2bab358..3bd0121b 100644 --- a/test/driver_sqlite3/pydantic/classes/queries_unknown_override.py +++ b/test/driver_sqlite3/pydantic/classes/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_sqlite3/pydantic/functions/__init__.py b/test/driver_sqlite3/pydantic/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_sqlite3/pydantic/functions/__init__.py +++ b/test/driver_sqlite3/pydantic/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_sqlite3/pydantic/functions/models.py b/test/driver_sqlite3/pydantic/functions/models.py index 3bb851b3..ccd04019 100644 --- a/test/driver_sqlite3/pydantic/functions/models.py +++ b/test/driver_sqlite3/pydantic/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_sqlite3/pydantic/functions/queries.py b/test/driver_sqlite3/pydantic/functions/queries.py index 13daca24..8ca3fcbc 100644 --- a/test/driver_sqlite3/pydantic/functions/queries.py +++ b/test/driver_sqlite3/pydantic/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_sqlite3/pydantic/functions/queries_case.py b/test/driver_sqlite3/pydantic/functions/queries_case.py index 2ac7ef95..62c9aad7 100644 --- a/test/driver_sqlite3/pydantic/functions/queries_case.py +++ b/test/driver_sqlite3/pydantic/functions/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_sqlite3/pydantic/functions/queries_override_adapter.py b/test/driver_sqlite3/pydantic/functions/queries_override_adapter.py index e526d428..c72b4523 100644 --- a/test/driver_sqlite3/pydantic/functions/queries_override_adapter.py +++ b/test/driver_sqlite3/pydantic/functions/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_sqlite3/pydantic/functions/queries_override_converter.py b/test/driver_sqlite3/pydantic/functions/queries_override_converter.py index 4b665357..2103fe23 100644 --- a/test/driver_sqlite3/pydantic/functions/queries_override_converter.py +++ b/test/driver_sqlite3/pydantic/functions/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_sqlite3/pydantic/functions/queries_unknown_override.py b/test/driver_sqlite3/pydantic/functions/queries_unknown_override.py index 9026f626..f364e800 100644 --- a/test/driver_sqlite3/pydantic/functions/queries_unknown_override.py +++ b/test/driver_sqlite3/pydantic/functions/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_sqlite3/sqlc-gen-better-python.wasm b/test/driver_sqlite3/sqlc-gen-better-python.wasm index 7d2fae76..dfb69b04 100644 Binary files a/test/driver_sqlite3/sqlc-gen-better-python.wasm and b/test/driver_sqlite3/sqlc-gen-better-python.wasm differ diff --git a/test/driver_sqlite3/sqlc.yaml b/test/driver_sqlite3/sqlc.yaml index 8328a89a..cb3a2f48 100644 --- a/test/driver_sqlite3/sqlc.yaml +++ b/test/driver_sqlite3/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: python wasm: url: file://sqlc-gen-better-python.wasm - sha256: eb001e364c1b8088e47bb43d4c9addf51ed7249f97db2fd1052cc14893989bed + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 sql: - schema: schema.sql queries: diff --git a/test/driver_turso_async/attrs/classes/__init__.py b/test/driver_turso_async/attrs/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_async/attrs/classes/__init__.py +++ b/test/driver_turso_async/attrs/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_async/attrs/classes/models.py b/test/driver_turso_async/attrs/classes/models.py index 9f2e2abb..73abe31d 100644 --- a/test/driver_turso_async/attrs/classes/models.py +++ b/test/driver_turso_async/attrs/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_async/attrs/classes/queries.py b/test/driver_turso_async/attrs/classes/queries.py index 20bcaba4..416e8c16 100644 --- a/test/driver_turso_async/attrs/classes/queries.py +++ b/test/driver_turso_async/attrs/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_async/attrs/functions/__init__.py b/test/driver_turso_async/attrs/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_async/attrs/functions/__init__.py +++ b/test/driver_turso_async/attrs/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_async/attrs/functions/models.py b/test/driver_turso_async/attrs/functions/models.py index 9f2e2abb..73abe31d 100644 --- a/test/driver_turso_async/attrs/functions/models.py +++ b/test/driver_turso_async/attrs/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_async/attrs/functions/queries.py b/test/driver_turso_async/attrs/functions/queries.py index 7ee5b923..96280ed4 100644 --- a/test/driver_turso_async/attrs/functions/queries.py +++ b/test/driver_turso_async/attrs/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_async/dataclass/classes/__init__.py b/test/driver_turso_async/dataclass/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_async/dataclass/classes/__init__.py +++ b/test/driver_turso_async/dataclass/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_async/dataclass/classes/models.py b/test/driver_turso_async/dataclass/classes/models.py index dc3b902c..aba98a13 100644 --- a/test/driver_turso_async/dataclass/classes/models.py +++ b/test/driver_turso_async/dataclass/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_async/dataclass/classes/queries.py b/test/driver_turso_async/dataclass/classes/queries.py index 6c5f8bee..50eca82e 100644 --- a/test/driver_turso_async/dataclass/classes/queries.py +++ b/test/driver_turso_async/dataclass/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_async/dataclass/functions/__init__.py b/test/driver_turso_async/dataclass/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_async/dataclass/functions/__init__.py +++ b/test/driver_turso_async/dataclass/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_async/dataclass/functions/models.py b/test/driver_turso_async/dataclass/functions/models.py index edba6f59..03436fa9 100644 --- a/test/driver_turso_async/dataclass/functions/models.py +++ b/test/driver_turso_async/dataclass/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_async/dataclass/functions/queries.py b/test/driver_turso_async/dataclass/functions/queries.py index 587dc3c3..ab100217 100644 --- a/test/driver_turso_async/dataclass/functions/queries.py +++ b/test/driver_turso_async/dataclass/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_async/dataclass/functions/queries_slice.py b/test/driver_turso_async/dataclass/functions/queries_slice.py index bdaaf657..e7db66d4 100644 --- a/test/driver_turso_async/dataclass/functions/queries_slice.py +++ b/test/driver_turso_async/dataclass/functions/queries_slice.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_slice.sql """Module containing queries from file queries_slice.sql.""" diff --git a/test/driver_turso_async/msgspec/classes/__init__.py b/test/driver_turso_async/msgspec/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_async/msgspec/classes/__init__.py +++ b/test/driver_turso_async/msgspec/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_async/msgspec/classes/models.py b/test/driver_turso_async/msgspec/classes/models.py index eabe2ef3..219c0288 100644 --- a/test/driver_turso_async/msgspec/classes/models.py +++ b/test/driver_turso_async/msgspec/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_async/msgspec/classes/queries.py b/test/driver_turso_async/msgspec/classes/queries.py index a8459cd0..caf7c15e 100644 --- a/test/driver_turso_async/msgspec/classes/queries.py +++ b/test/driver_turso_async/msgspec/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_async/msgspec/functions/__init__.py b/test/driver_turso_async/msgspec/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_async/msgspec/functions/__init__.py +++ b/test/driver_turso_async/msgspec/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_async/msgspec/functions/models.py b/test/driver_turso_async/msgspec/functions/models.py index eabe2ef3..219c0288 100644 --- a/test/driver_turso_async/msgspec/functions/models.py +++ b/test/driver_turso_async/msgspec/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_async/msgspec/functions/queries.py b/test/driver_turso_async/msgspec/functions/queries.py index 13b4531c..4aba73d7 100644 --- a/test/driver_turso_async/msgspec/functions/queries.py +++ b/test/driver_turso_async/msgspec/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_async/pydantic/classes/__init__.py b/test/driver_turso_async/pydantic/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_async/pydantic/classes/__init__.py +++ b/test/driver_turso_async/pydantic/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_async/pydantic/classes/models.py b/test/driver_turso_async/pydantic/classes/models.py index 3bb851b3..ccd04019 100644 --- a/test/driver_turso_async/pydantic/classes/models.py +++ b/test/driver_turso_async/pydantic/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_async/pydantic/classes/queries.py b/test/driver_turso_async/pydantic/classes/queries.py index 60029768..905cf29a 100644 --- a/test/driver_turso_async/pydantic/classes/queries.py +++ b/test/driver_turso_async/pydantic/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_async/pydantic/functions/__init__.py b/test/driver_turso_async/pydantic/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_async/pydantic/functions/__init__.py +++ b/test/driver_turso_async/pydantic/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_async/pydantic/functions/models.py b/test/driver_turso_async/pydantic/functions/models.py index 3bb851b3..ccd04019 100644 --- a/test/driver_turso_async/pydantic/functions/models.py +++ b/test/driver_turso_async/pydantic/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_async/pydantic/functions/queries.py b/test/driver_turso_async/pydantic/functions/queries.py index e5d6b09d..01ded2e8 100644 --- a/test/driver_turso_async/pydantic/functions/queries.py +++ b/test/driver_turso_async/pydantic/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_async/sqlc-gen-better-python.wasm b/test/driver_turso_async/sqlc-gen-better-python.wasm index 7d2fae76..dfb69b04 100755 Binary files a/test/driver_turso_async/sqlc-gen-better-python.wasm and b/test/driver_turso_async/sqlc-gen-better-python.wasm differ diff --git a/test/driver_turso_async/sqlc.yaml b/test/driver_turso_async/sqlc.yaml index 467710f3..2510838b 100644 --- a/test/driver_turso_async/sqlc.yaml +++ b/test/driver_turso_async/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: python wasm: url: file://sqlc-gen-better-python.wasm - sha256: eb001e364c1b8088e47bb43d4c9addf51ed7249f97db2fd1052cc14893989bed + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 sql: - schema: schema.sql queries: queries.sql diff --git a/test/driver_turso_sync/attrs/classes/__init__.py b/test/driver_turso_sync/attrs/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_sync/attrs/classes/__init__.py +++ b/test/driver_turso_sync/attrs/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_sync/attrs/classes/models.py b/test/driver_turso_sync/attrs/classes/models.py index 7e7db66c..254f60c7 100644 --- a/test/driver_turso_sync/attrs/classes/models.py +++ b/test/driver_turso_sync/attrs/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_sync/attrs/classes/queries.py b/test/driver_turso_sync/attrs/classes/queries.py index 2a3f97c6..703ce315 100644 --- a/test/driver_turso_sync/attrs/classes/queries.py +++ b/test/driver_turso_sync/attrs/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_sync/attrs/classes/queries_case.py b/test/driver_turso_sync/attrs/classes/queries_case.py index 1ce790b8..ce9737a6 100644 --- a/test/driver_turso_sync/attrs/classes/queries_case.py +++ b/test/driver_turso_sync/attrs/classes/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_turso_sync/attrs/classes/queries_override_adapter.py b/test/driver_turso_sync/attrs/classes/queries_override_adapter.py index 55864f73..be96cf74 100644 --- a/test/driver_turso_sync/attrs/classes/queries_override_adapter.py +++ b/test/driver_turso_sync/attrs/classes/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_turso_sync/attrs/classes/queries_override_converter.py b/test/driver_turso_sync/attrs/classes/queries_override_converter.py index 0e3eb393..63fc2eb3 100644 --- a/test/driver_turso_sync/attrs/classes/queries_override_converter.py +++ b/test/driver_turso_sync/attrs/classes/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_turso_sync/attrs/classes/queries_unknown_override.py b/test/driver_turso_sync/attrs/classes/queries_unknown_override.py index 53105c07..69da6c0f 100644 --- a/test/driver_turso_sync/attrs/classes/queries_unknown_override.py +++ b/test/driver_turso_sync/attrs/classes/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_turso_sync/attrs/functions/__init__.py b/test/driver_turso_sync/attrs/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_sync/attrs/functions/__init__.py +++ b/test/driver_turso_sync/attrs/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_sync/attrs/functions/models.py b/test/driver_turso_sync/attrs/functions/models.py index 9f2e2abb..73abe31d 100644 --- a/test/driver_turso_sync/attrs/functions/models.py +++ b/test/driver_turso_sync/attrs/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_sync/attrs/functions/queries.py b/test/driver_turso_sync/attrs/functions/queries.py index 30880eb2..0d178eac 100644 --- a/test/driver_turso_sync/attrs/functions/queries.py +++ b/test/driver_turso_sync/attrs/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_sync/attrs/functions/queries_case.py b/test/driver_turso_sync/attrs/functions/queries_case.py index 42c04b68..a0cd45ff 100644 --- a/test/driver_turso_sync/attrs/functions/queries_case.py +++ b/test/driver_turso_sync/attrs/functions/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_turso_sync/attrs/functions/queries_override_adapter.py b/test/driver_turso_sync/attrs/functions/queries_override_adapter.py index 82eb9246..0fb70c65 100644 --- a/test/driver_turso_sync/attrs/functions/queries_override_adapter.py +++ b/test/driver_turso_sync/attrs/functions/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_turso_sync/attrs/functions/queries_override_converter.py b/test/driver_turso_sync/attrs/functions/queries_override_converter.py index b80dc09e..31777ee7 100644 --- a/test/driver_turso_sync/attrs/functions/queries_override_converter.py +++ b/test/driver_turso_sync/attrs/functions/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_turso_sync/attrs/functions/queries_unknown_override.py b/test/driver_turso_sync/attrs/functions/queries_unknown_override.py index ac928f45..d60468b5 100644 --- a/test/driver_turso_sync/attrs/functions/queries_unknown_override.py +++ b/test/driver_turso_sync/attrs/functions/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_turso_sync/dataclass/classes/__init__.py b/test/driver_turso_sync/dataclass/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_sync/dataclass/classes/__init__.py +++ b/test/driver_turso_sync/dataclass/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_sync/dataclass/classes/models.py b/test/driver_turso_sync/dataclass/classes/models.py index dc3b902c..aba98a13 100644 --- a/test/driver_turso_sync/dataclass/classes/models.py +++ b/test/driver_turso_sync/dataclass/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_sync/dataclass/classes/queries.py b/test/driver_turso_sync/dataclass/classes/queries.py index 30d8e014..79cade48 100644 --- a/test/driver_turso_sync/dataclass/classes/queries.py +++ b/test/driver_turso_sync/dataclass/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_sync/dataclass/classes/queries_case.py b/test/driver_turso_sync/dataclass/classes/queries_case.py index ea00c84a..7b8aeb18 100644 --- a/test/driver_turso_sync/dataclass/classes/queries_case.py +++ b/test/driver_turso_sync/dataclass/classes/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_turso_sync/dataclass/classes/queries_override_adapter.py b/test/driver_turso_sync/dataclass/classes/queries_override_adapter.py index 99450f58..05213293 100644 --- a/test/driver_turso_sync/dataclass/classes/queries_override_adapter.py +++ b/test/driver_turso_sync/dataclass/classes/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_turso_sync/dataclass/classes/queries_override_converter.py b/test/driver_turso_sync/dataclass/classes/queries_override_converter.py index 6ad303a2..0afb6bfa 100644 --- a/test/driver_turso_sync/dataclass/classes/queries_override_converter.py +++ b/test/driver_turso_sync/dataclass/classes/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_turso_sync/dataclass/classes/queries_unknown_override.py b/test/driver_turso_sync/dataclass/classes/queries_unknown_override.py index 71505e2f..a13948c1 100644 --- a/test/driver_turso_sync/dataclass/classes/queries_unknown_override.py +++ b/test/driver_turso_sync/dataclass/classes/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_turso_sync/dataclass/functions/__init__.py b/test/driver_turso_sync/dataclass/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_sync/dataclass/functions/__init__.py +++ b/test/driver_turso_sync/dataclass/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_sync/dataclass/functions/models.py b/test/driver_turso_sync/dataclass/functions/models.py index edba6f59..03436fa9 100644 --- a/test/driver_turso_sync/dataclass/functions/models.py +++ b/test/driver_turso_sync/dataclass/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_sync/dataclass/functions/queries.py b/test/driver_turso_sync/dataclass/functions/queries.py index 08d1632e..fed63e19 100644 --- a/test/driver_turso_sync/dataclass/functions/queries.py +++ b/test/driver_turso_sync/dataclass/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_sync/dataclass/functions/queries_case.py b/test/driver_turso_sync/dataclass/functions/queries_case.py index 5563a744..3b0ce15e 100644 --- a/test/driver_turso_sync/dataclass/functions/queries_case.py +++ b/test/driver_turso_sync/dataclass/functions/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_turso_sync/dataclass/functions/queries_override_adapter.py b/test/driver_turso_sync/dataclass/functions/queries_override_adapter.py index 926ea807..bf99eeda 100644 --- a/test/driver_turso_sync/dataclass/functions/queries_override_adapter.py +++ b/test/driver_turso_sync/dataclass/functions/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_turso_sync/dataclass/functions/queries_override_converter.py b/test/driver_turso_sync/dataclass/functions/queries_override_converter.py index a3364c03..a199d981 100644 --- a/test/driver_turso_sync/dataclass/functions/queries_override_converter.py +++ b/test/driver_turso_sync/dataclass/functions/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_turso_sync/dataclass/functions/queries_slice.py b/test/driver_turso_sync/dataclass/functions/queries_slice.py index d5766703..ca27c048 100644 --- a/test/driver_turso_sync/dataclass/functions/queries_slice.py +++ b/test/driver_turso_sync/dataclass/functions/queries_slice.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_slice.sql """Module containing queries from file queries_slice.sql.""" diff --git a/test/driver_turso_sync/dataclass/functions/queries_unknown_override.py b/test/driver_turso_sync/dataclass/functions/queries_unknown_override.py index 456c2ac8..d86c6cb2 100644 --- a/test/driver_turso_sync/dataclass/functions/queries_unknown_override.py +++ b/test/driver_turso_sync/dataclass/functions/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_turso_sync/msgspec/classes/__init__.py b/test/driver_turso_sync/msgspec/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_sync/msgspec/classes/__init__.py +++ b/test/driver_turso_sync/msgspec/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_sync/msgspec/classes/models.py b/test/driver_turso_sync/msgspec/classes/models.py index eabe2ef3..219c0288 100644 --- a/test/driver_turso_sync/msgspec/classes/models.py +++ b/test/driver_turso_sync/msgspec/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_sync/msgspec/classes/queries.py b/test/driver_turso_sync/msgspec/classes/queries.py index 2fe04054..ed618bf2 100644 --- a/test/driver_turso_sync/msgspec/classes/queries.py +++ b/test/driver_turso_sync/msgspec/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_sync/msgspec/classes/queries_case.py b/test/driver_turso_sync/msgspec/classes/queries_case.py index 3741ff79..03ba59b8 100644 --- a/test/driver_turso_sync/msgspec/classes/queries_case.py +++ b/test/driver_turso_sync/msgspec/classes/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_turso_sync/msgspec/classes/queries_override_adapter.py b/test/driver_turso_sync/msgspec/classes/queries_override_adapter.py index 203ebfb7..5435bdbd 100644 --- a/test/driver_turso_sync/msgspec/classes/queries_override_adapter.py +++ b/test/driver_turso_sync/msgspec/classes/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_turso_sync/msgspec/classes/queries_override_converter.py b/test/driver_turso_sync/msgspec/classes/queries_override_converter.py index 063e3fc3..ebcfd923 100644 --- a/test/driver_turso_sync/msgspec/classes/queries_override_converter.py +++ b/test/driver_turso_sync/msgspec/classes/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_turso_sync/msgspec/classes/queries_unknown_override.py b/test/driver_turso_sync/msgspec/classes/queries_unknown_override.py index 33f62560..0e3db9f6 100644 --- a/test/driver_turso_sync/msgspec/classes/queries_unknown_override.py +++ b/test/driver_turso_sync/msgspec/classes/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_turso_sync/msgspec/functions/__init__.py b/test/driver_turso_sync/msgspec/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_sync/msgspec/functions/__init__.py +++ b/test/driver_turso_sync/msgspec/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_sync/msgspec/functions/models.py b/test/driver_turso_sync/msgspec/functions/models.py index eabe2ef3..219c0288 100644 --- a/test/driver_turso_sync/msgspec/functions/models.py +++ b/test/driver_turso_sync/msgspec/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_sync/msgspec/functions/queries.py b/test/driver_turso_sync/msgspec/functions/queries.py index 1721f6f6..f52d0ef8 100644 --- a/test/driver_turso_sync/msgspec/functions/queries.py +++ b/test/driver_turso_sync/msgspec/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_sync/msgspec/functions/queries_case.py b/test/driver_turso_sync/msgspec/functions/queries_case.py index 3a32ffa1..acfef326 100644 --- a/test/driver_turso_sync/msgspec/functions/queries_case.py +++ b/test/driver_turso_sync/msgspec/functions/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_turso_sync/msgspec/functions/queries_override_adapter.py b/test/driver_turso_sync/msgspec/functions/queries_override_adapter.py index 468d9ca5..bef78bf5 100644 --- a/test/driver_turso_sync/msgspec/functions/queries_override_adapter.py +++ b/test/driver_turso_sync/msgspec/functions/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_turso_sync/msgspec/functions/queries_override_converter.py b/test/driver_turso_sync/msgspec/functions/queries_override_converter.py index 4ac9a9f3..b44fb1fc 100644 --- a/test/driver_turso_sync/msgspec/functions/queries_override_converter.py +++ b/test/driver_turso_sync/msgspec/functions/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_turso_sync/msgspec/functions/queries_unknown_override.py b/test/driver_turso_sync/msgspec/functions/queries_unknown_override.py index cf652648..a0f05d08 100644 --- a/test/driver_turso_sync/msgspec/functions/queries_unknown_override.py +++ b/test/driver_turso_sync/msgspec/functions/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_turso_sync/pydantic/classes/__init__.py b/test/driver_turso_sync/pydantic/classes/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_sync/pydantic/classes/__init__.py +++ b/test/driver_turso_sync/pydantic/classes/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_sync/pydantic/classes/models.py b/test/driver_turso_sync/pydantic/classes/models.py index 3bb851b3..ccd04019 100644 --- a/test/driver_turso_sync/pydantic/classes/models.py +++ b/test/driver_turso_sync/pydantic/classes/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_sync/pydantic/classes/queries.py b/test/driver_turso_sync/pydantic/classes/queries.py index 9d6f0543..f73092ba 100644 --- a/test/driver_turso_sync/pydantic/classes/queries.py +++ b/test/driver_turso_sync/pydantic/classes/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_sync/pydantic/classes/queries_case.py b/test/driver_turso_sync/pydantic/classes/queries_case.py index 4038886e..d29faff2 100644 --- a/test/driver_turso_sync/pydantic/classes/queries_case.py +++ b/test/driver_turso_sync/pydantic/classes/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_turso_sync/pydantic/classes/queries_override_adapter.py b/test/driver_turso_sync/pydantic/classes/queries_override_adapter.py index 99450f58..05213293 100644 --- a/test/driver_turso_sync/pydantic/classes/queries_override_adapter.py +++ b/test/driver_turso_sync/pydantic/classes/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_turso_sync/pydantic/classes/queries_override_converter.py b/test/driver_turso_sync/pydantic/classes/queries_override_converter.py index 6ad303a2..0afb6bfa 100644 --- a/test/driver_turso_sync/pydantic/classes/queries_override_converter.py +++ b/test/driver_turso_sync/pydantic/classes/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_turso_sync/pydantic/classes/queries_unknown_override.py b/test/driver_turso_sync/pydantic/classes/queries_unknown_override.py index 71505e2f..a13948c1 100644 --- a/test/driver_turso_sync/pydantic/classes/queries_unknown_override.py +++ b/test/driver_turso_sync/pydantic/classes/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_turso_sync/pydantic/functions/__init__.py b/test/driver_turso_sync/pydantic/functions/__init__.py index b7bd3b7c..9d3275af 100644 --- a/test/driver_turso_sync/pydantic/functions/__init__.py +++ b/test/driver_turso_sync/pydantic/functions/__init__.py @@ -1,5 +1,5 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Package containing queries and models automatically generated using sqlc-gen-better-python.""" diff --git a/test/driver_turso_sync/pydantic/functions/models.py b/test/driver_turso_sync/pydantic/functions/models.py index 3bb851b3..ccd04019 100644 --- a/test/driver_turso_sync/pydantic/functions/models.py +++ b/test/driver_turso_sync/pydantic/functions/models.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 """Module containing models.""" from __future__ import annotations diff --git a/test/driver_turso_sync/pydantic/functions/queries.py b/test/driver_turso_sync/pydantic/functions/queries.py index 3cbd3448..dcb071f0 100644 --- a/test/driver_turso_sync/pydantic/functions/queries.py +++ b/test/driver_turso_sync/pydantic/functions/queries.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries.sql """Module containing queries from file queries.sql.""" diff --git a/test/driver_turso_sync/pydantic/functions/queries_case.py b/test/driver_turso_sync/pydantic/functions/queries_case.py index d86421a7..02b3b28a 100644 --- a/test/driver_turso_sync/pydantic/functions/queries_case.py +++ b/test/driver_turso_sync/pydantic/functions/queries_case.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_case.sql """Module containing queries from file queries_case.sql.""" diff --git a/test/driver_turso_sync/pydantic/functions/queries_override_adapter.py b/test/driver_turso_sync/pydantic/functions/queries_override_adapter.py index 926ea807..bf99eeda 100644 --- a/test/driver_turso_sync/pydantic/functions/queries_override_adapter.py +++ b/test/driver_turso_sync/pydantic/functions/queries_override_adapter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_adapter.sql """Module containing queries from file queries_override_adapter.sql.""" diff --git a/test/driver_turso_sync/pydantic/functions/queries_override_converter.py b/test/driver_turso_sync/pydantic/functions/queries_override_converter.py index a3364c03..a199d981 100644 --- a/test/driver_turso_sync/pydantic/functions/queries_override_converter.py +++ b/test/driver_turso_sync/pydantic/functions/queries_override_converter.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_override_converter.sql """Module containing queries from file queries_override_converter.sql.""" diff --git a/test/driver_turso_sync/pydantic/functions/queries_unknown_override.py b/test/driver_turso_sync/pydantic/functions/queries_unknown_override.py index 456c2ac8..d86c6cb2 100644 --- a/test/driver_turso_sync/pydantic/functions/queries_unknown_override.py +++ b/test/driver_turso_sync/pydantic/functions/queries_unknown_override.py @@ -1,7 +1,7 @@ # Code generated by sqlc. DO NOT EDIT. # versions: # sqlc v1.31.1 -# sqlc-gen-better-python v0.7.0 +# sqlc-gen-better-python v0.8.0 # source file: queries_unknown_override.sql """Module containing queries from file queries_unknown_override.sql.""" diff --git a/test/driver_turso_sync/sqlc-gen-better-python.wasm b/test/driver_turso_sync/sqlc-gen-better-python.wasm index 7d2fae76..dfb69b04 100755 Binary files a/test/driver_turso_sync/sqlc-gen-better-python.wasm and b/test/driver_turso_sync/sqlc-gen-better-python.wasm differ diff --git a/test/driver_turso_sync/sqlc.yaml b/test/driver_turso_sync/sqlc.yaml index 5635a00e..1a716da9 100644 --- a/test/driver_turso_sync/sqlc.yaml +++ b/test/driver_turso_sync/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: python wasm: url: file://sqlc-gen-better-python.wasm - sha256: eb001e364c1b8088e47bb43d4c9addf51ed7249f97db2fd1052cc14893989bed + sha256: 81efcdb423ecc55ecf2ab065d3f3f70ca3068ba43f8eff0cf507a3b3a4ccb863 sql: - schema: schema.sql queries: diff --git a/uv.lock b/uv.lock index 03e27e39..334a71e5 100644 --- a/uv.lock +++ b/uv.lock @@ -29,6 +29,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/bd/551ee6af426af84ca33e02622be722925c196608e9127d731ef17c47f06e/argcomplete-3.7.2-py3-none-any.whl", hash = "sha256:6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19", size = 43294, upload-time = "2026-08-06T04:53:20.246Z" }, ] +[[package]] +name = "asyncmy" +version = "0.2.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/23/4a77a7776d161e29ff5607d2f33605c9cd4f6be9f9e9cf19c25ee0d5ec1e/asyncmy-0.2.14.tar.gz", hash = "sha256:d058195574cc889f3f773686f7e17d71693641f9ac0386ae59ae623532a841ff", size = 92913, upload-time = "2026-08-12T05:14:06.617Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/c8/7b18cf514d2ee509381e6e72fd1818c5d52e53f2eb4b7ce4e3fb59cc11a0/asyncmy-0.2.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c4d3a7982a7a97dcbc9f895f2e9846edd401dd882c5fc24e6587e6b06215f75", size = 2056901, upload-time = "2026-08-12T05:12:31.054Z" }, + { url = "https://files.pythonhosted.org/packages/eb/54/e9f7a0c67c933c406d703d31016d5b0e1c872602ac36d1cfaaf0a2ab5104/asyncmy-0.2.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9405dbd5daeed8878a770b9c068201cb52cd0eac1bc1c453e73ad4b5bdb79916", size = 2034676, upload-time = "2026-08-12T05:12:32.445Z" }, + { url = "https://files.pythonhosted.org/packages/1e/21/8f1213ee2567ad7fe5ff51f4bb764ef528ccbaae11a0d57f66f8a4ad1bcd/asyncmy-0.2.14-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07d9ae2805fdc53cdd38dc4a1e7fb01a35dfcdf6c15664f05c81e7a2703b90b9", size = 6233135, upload-time = "2026-08-12T05:12:34.282Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3e/315a16d189ba13be874cf63d3e7b88f472b5750da908269ab5a22d8d5e7f/asyncmy-0.2.14-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd8e23d2eab3d9249a206f2446e2896ef99893d03ccda900ba5e17018fd44136", size = 6294746, upload-time = "2026-08-12T05:12:36.265Z" }, + { url = "https://files.pythonhosted.org/packages/9b/02/5c6a018b1377a4a5ebd1f6cf3b58e325fd35c1c83f1be50041eb1a45f40b/asyncmy-0.2.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:91c4188713db7ab3840e854fe812b1bbffa773073315d0f614884ddcbcc0519a", size = 6024724, upload-time = "2026-08-12T05:12:38.1Z" }, + { url = "https://files.pythonhosted.org/packages/37/95/bb9ea684700d66972767b9a4bef85cc5d0816f2ca45dd48fa86940a5bf88/asyncmy-0.2.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a20a8f063d279b96f9ed2d391d7ade82bb3ecda375e21d388c110f979e8cc478", size = 6156140, upload-time = "2026-08-12T05:12:40.47Z" }, + { url = "https://files.pythonhosted.org/packages/2f/dd/90e169494e0998ecd57f55a4b39ee6aeb470d8299503d283903b1286e79a/asyncmy-0.2.14-cp312-cp312-win32.whl", hash = "sha256:63b3f5f052a9b4cbd837a2a867f954f6e437979a7c74a663ac39f911f9da45ce", size = 1846950, upload-time = "2026-08-12T05:12:42.062Z" }, + { url = "https://files.pythonhosted.org/packages/ac/7f/50a56182750805bb08ef7af712138ff5c31a3cf297b4c757b1133f00d259/asyncmy-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:999546ded3238150b62d62454f9ee1203368db7673095993f2132b907798ec41", size = 1943255, upload-time = "2026-08-12T05:12:43.647Z" }, + { url = "https://files.pythonhosted.org/packages/19/02/2955b846d1241ed03884e384f8fe301d4cdf65294774acad1b64ecd8985e/asyncmy-0.2.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d073fd93612fecfb02218e8a190a0e2f8cac764a24d95752c335244c83b2b9e4", size = 2052132, upload-time = "2026-08-12T05:12:45.273Z" }, + { url = "https://files.pythonhosted.org/packages/17/b2/95698fcaff9a23464f594c506ad1d4a7f7fc5c4d8b6af47c26f604661b68/asyncmy-0.2.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0551a8470b84f9114d359a0bb8e24584d00a42c379a80ae943d338da8b5f5b7", size = 2029223, upload-time = "2026-08-12T05:12:46.724Z" }, + { url = "https://files.pythonhosted.org/packages/56/c7/a375c219f6bffd4bfd3bb1ead40120127b44ca5ef48f40e61b29bb1e755a/asyncmy-0.2.14-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b4ed3cb126fa9615c98aa4cfe9f22a75af08ab06253aabedd6c293a9544e9c", size = 6186238, upload-time = "2026-08-12T05:12:48.418Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fa/c501774450db4a18aac1f5d0d47d96fdff54b4836616efd6c8ccf1415e56/asyncmy-0.2.14-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a9f6cad22be74180bb9ae9569c54bf72e72bf8dad8ec6d5d66e835e6d579651", size = 6236477, upload-time = "2026-08-12T05:12:50.476Z" }, + { url = "https://files.pythonhosted.org/packages/79/75/2e69a287a4d3dcdfd783eb5ac736253ce457d73d39ce21c276b834a1dff2/asyncmy-0.2.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f88d48947ce41ffe4e0488fa80b9d8b745c422a61133e07d250413974e709d3", size = 5971721, upload-time = "2026-08-12T05:12:52.986Z" }, + { url = "https://files.pythonhosted.org/packages/d2/c0/f0a151ee093f8829859b2957934f1654ded7e97d3b8fb8584ed2b6ba246f/asyncmy-0.2.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bc0403d1b7625557f966ae16b9798d72d20a6293d6d0989118b8f21261adcb02", size = 6115410, upload-time = "2026-08-12T05:12:54.887Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a9/a62afa69effe2f6407699c0dee22e242469be3fcf2231c0846eb89f9abe9/asyncmy-0.2.14-cp313-cp313-win32.whl", hash = "sha256:969570b5ea070662fc178cd84e58b2d3de791499a5275f2e5f2a2fb31a829666", size = 1844813, upload-time = "2026-08-12T05:12:57.017Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ff/6380c67ea2dd61902ea0704511defd1d15aa68f8965611c4f23e85850588/asyncmy-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:fa1d887afa1b5deabad254a864bbfb9e0818810e522cd1037efc1b14823c5007", size = 1940657, upload-time = "2026-08-12T05:12:58.566Z" }, + { url = "https://files.pythonhosted.org/packages/8d/e4/a67bd7df92f587702eaf8d9a031574fe15acc075e14dffa0d400c89619b6/asyncmy-0.2.14-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:23d6cdfbab90c8b0e5da7499b17422de0008d881179650d15d6b3a7fd7f7321e", size = 2807764, upload-time = "2026-08-12T05:13:00.175Z" }, + { url = "https://files.pythonhosted.org/packages/d9/40/c7bcb17220a59709dfd6a2002661276e4d8ad2623b0ffb590a661b7e69b5/asyncmy-0.2.14-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1c1c56750503b4a98737124e5e6f98a077349f897430c612e7b71994fdfa1fb1", size = 2772816, upload-time = "2026-08-12T05:13:01.635Z" }, + { url = "https://files.pythonhosted.org/packages/07/4b/8565b440e7e580454aa2fc9fa4b8b4db8131e86502af846192d04d5cfde0/asyncmy-0.2.14-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f258d918994ff6c39d01fec17a53de950aa521e70fffe2f9bca8e6c09efe4a06", size = 11243257, upload-time = "2026-08-12T05:13:03.964Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a1/4564fe3f8ef28a86e8cbc5f6a59349155aadf0345641bae6b0013e8f8e78/asyncmy-0.2.14-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00e6b2d37da51e10cd9057df18455d7de6acb03b04bdce1d973b7bbf9efb8356", size = 10962335, upload-time = "2026-08-12T05:13:06.678Z" }, + { url = "https://files.pythonhosted.org/packages/68/4d/131d88e9be4d5d86e5ac038041e2f091f86d9f2eb23ef31efc28fec356bb/asyncmy-0.2.14-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b4c4b52d74e97b323d989d430d115f8d42b6c357ef9f6013790014965b3c2188", size = 10693083, upload-time = "2026-08-12T05:13:09.613Z" }, + { url = "https://files.pythonhosted.org/packages/e7/95/1eeae27be8ac5102d6c7113e757639562d5f46dd33bcbf75d7460a7753ac/asyncmy-0.2.14-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d31e61fac2520319894af6951614e6d82f5c0a0207ab02ae2408ffc69583b669", size = 10700413, upload-time = "2026-08-12T05:13:12.314Z" }, + { url = "https://files.pythonhosted.org/packages/0c/72/65bda5a44d48c540483bcfda961e472fd3d36d41778db77a06ecafa8042d/asyncmy-0.2.14-cp313-cp313t-win32.whl", hash = "sha256:d23f172c101542b5bc93c19dd49ca683eea3211884696c64af89370241e26591", size = 2380351, upload-time = "2026-08-12T05:13:14.361Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/55dd20ec5910e62757fa48248285bbb124a29e2be9e62b27bce7bcffd39b/asyncmy-0.2.14-cp313-cp313t-win_amd64.whl", hash = "sha256:fb0c5ae02f9e5f360cb645fa02f613c0136ac7488ee4677bb05197546b4a6af1", size = 2568577, upload-time = "2026-08-12T05:13:16.342Z" }, + { url = "https://files.pythonhosted.org/packages/6f/89/733f2cb87224e318c573fd05854ae8b9d21de722dafbc9bf6902e7e131e5/asyncmy-0.2.14-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:74b803d60b00ea476d13912756c2b26f5cc4d70501d1d1c1437f0515bddf8f65", size = 2075378, upload-time = "2026-08-12T05:13:17.742Z" }, + { url = "https://files.pythonhosted.org/packages/cb/81/341f29110611b0f42ad7f266966df26f2975159425d94aa22627e494c5e8/asyncmy-0.2.14-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2d2d98de92eb22d702fd5fa6dc9a39ca20b1de49e5a71437f8e5b13b04a5b7ec", size = 2056634, upload-time = "2026-08-12T05:13:19.265Z" }, + { url = "https://files.pythonhosted.org/packages/bf/6d/904eb0c5a0eb0230b9d06a43e5f3fd26db3f9685d835bfa9e948d4dba26d/asyncmy-0.2.14-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e83bc138424ff4cce01fd5ef9eb624cc98c417105a85375a67e8bbabdb426a55", size = 6207597, upload-time = "2026-08-12T05:13:21.657Z" }, + { url = "https://files.pythonhosted.org/packages/fe/50/abbd49ab1d7e4ce88cf4dc777624d7e5778279acefd78e605dde71da6922/asyncmy-0.2.14-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d116cd2b5d5715c8cda7c2af1238bf9b36dca210558b0fa309ae9c587369335", size = 6192810, upload-time = "2026-08-12T05:13:23.632Z" }, + { url = "https://files.pythonhosted.org/packages/b5/0d/db165a9d359627c57ba49ad8494bf2a24c4affe96e4bc4322b98fa6179e5/asyncmy-0.2.14-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46ba9453332bf45e580122ad9bb69657fbb50dbb9684b76ad10d83498372311", size = 6006568, upload-time = "2026-08-12T05:13:25.716Z" }, + { url = "https://files.pythonhosted.org/packages/4a/f7/95910a5c49239b9c186e9eaee899b56c89e8a29ff91c89fc319952c6b33a/asyncmy-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9062b7fdd0e14c32e1fac2114fd72a2d4a4677506a6d1a8a522917ec5c4cf0e0", size = 6087245, upload-time = "2026-08-12T05:13:27.855Z" }, + { url = "https://files.pythonhosted.org/packages/3e/1b/6f990217f27e7490dbf31120e67bd8fcf746e7045ef592fd0ab507e0a7d9/asyncmy-0.2.14-cp314-cp314-win32.whl", hash = "sha256:e4755698751e6f04632abf48ee0d9b8882fb369ae21cb14de6c152d0dc1019c7", size = 1861936, upload-time = "2026-08-12T05:13:29.605Z" }, + { url = "https://files.pythonhosted.org/packages/fa/dd/aff1b47247bde3b638124ffbde4c0f3b1f8728dd0ba24c51bdc3f4b93b1b/asyncmy-0.2.14-cp314-cp314-win_amd64.whl", hash = "sha256:5e5210c013d15c6c01d384d7b1607678f3118121a3ce78c69c4e7ab564c41d8f", size = 1961901, upload-time = "2026-08-12T05:13:31.602Z" }, + { url = "https://files.pythonhosted.org/packages/aa/89/6e6979f8c014ea445facd52168bf4a2871886e4e7c73d856c99ec65f60ce/asyncmy-0.2.14-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5af65cfe97d33efc6697ebc056c714f09eb741555c9fc6d73ce0d9651618484d", size = 2835467, upload-time = "2026-08-12T05:13:33.112Z" }, + { url = "https://files.pythonhosted.org/packages/60/5e/fe7ab0c4398f99e5397566cea513c91db5d739166080c72b445e51a82495/asyncmy-0.2.14-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f1ea30352fa047f7000cbf56eda605bb8fad5d331bbbf41956d459e00e5e148", size = 2803718, upload-time = "2026-08-12T05:13:34.867Z" }, + { url = "https://files.pythonhosted.org/packages/c4/15/668855756814f6551d023ae07d90a77047eee4f9d5aa55ba8e90ddcb0dc0/asyncmy-0.2.14-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d36816da461bb2d6d5ac910cb07ab83527c0b94cc98753c88b070ebecec77cd0", size = 11269601, upload-time = "2026-08-12T05:13:37.321Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ff/9de0a1ed33dc368d38793ef760003e8cd92b062b9a5c243002f8068752b5/asyncmy-0.2.14-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0b5b2287c873eda34a449e752eaadc1f23f67e41e74ec01c30e207873ceb8b6", size = 10929177, upload-time = "2026-08-12T05:13:40.73Z" }, + { url = "https://files.pythonhosted.org/packages/e5/24/2359601008327dff0f09ec507db85830469e690d0582e15c2d4f69289821/asyncmy-0.2.14-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:568c3f75403043431a4ccf5f7de5148753d4d3f1d23a2ab76e5bedad270163d1", size = 10731726, upload-time = "2026-08-12T05:13:43.724Z" }, + { url = "https://files.pythonhosted.org/packages/18/4c/ca564c154040c284e8233233660e5f02359b48be20b4b1ce245a71f0d16e/asyncmy-0.2.14-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62e86e132d4f3b429015c81efd6ed0dfed43cd6820d7c3dd4599cb9bff0d8c44", size = 10684340, upload-time = "2026-08-12T05:13:47.181Z" }, + { url = "https://files.pythonhosted.org/packages/1f/1e/c8b459576b2211f06ce940de89ec188912e13d60e7e9bd18bf92c0143210/asyncmy-0.2.14-cp314-cp314t-win32.whl", hash = "sha256:3ef392a9c7e6d9821a3f265880dab960737aa8e3eccb74a529bca86914a7b712", size = 2413208, upload-time = "2026-08-12T05:13:49.192Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e7/690e3d13935cedb5238556624ea33b25f08cf34e4596cfea0814175c0296/asyncmy-0.2.14-cp314-cp314t-win_amd64.whl", hash = "sha256:b086030b0f647c622c675c090a377fdc6ce94421dc71efa745613df175fd4bff", size = 2609526, upload-time = "2026-08-12T05:13:50.921Z" }, +] + [[package]] name = "asyncpg" version = "0.31.0" @@ -91,6 +139,67 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "cffi" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" }, + { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" }, + { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" }, + { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, + { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, + { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, + { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" }, + { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" }, + { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" }, + { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" }, + { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" }, + { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" }, + { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" }, + { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" }, + { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" }, +] + [[package]] name = "ciso8601" version = "2.3.3" @@ -213,6 +322,56 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b4/d9/e70c286c979378f061d8266e279b686ab0b0b688e1fe0af864684f23a77d/coverage-7.15.4-py3-none-any.whl", hash = "sha256:964730a1e9de9c0cf11be6a1a3c79ce419c34882842abd256086ba4698705e84", size = 214332, upload-time = "2026-08-06T13:50:22.192Z" }, ] +[[package]] +name = "cryptography" +version = "50.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/5c/59086b4aac5e879d38ddbcf74e4be7ade89cebc3eb199a55da998c3bb46a/cryptography-50.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03", size = 4001252, upload-time = "2026-07-31T14:23:33.331Z" }, + { url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" }, + { url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" }, + { url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" }, + { url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" }, + { url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" }, + { url = "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" }, + { url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" }, + { url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" }, + { url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" }, + { url = "https://files.pythonhosted.org/packages/32/2e/c9db68a0c4bfa28e310707527c0ee3a2bd254104d2e02e68f368e197aa4c/cryptography-50.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30", size = 3840395, upload-time = "2026-07-31T14:23:56.677Z" }, + { url = "https://files.pythonhosted.org/packages/c3/fb/951032a3bf22a5697c83183fb6294a4843772947a70e616c57b3ff5f522e/cryptography-50.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41", size = 3989258, upload-time = "2026-07-31T14:23:58.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" }, + { url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" }, + { url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" }, + { url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" }, + { url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" }, + { url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" }, + { url = "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" }, + { url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" }, + { url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" }, + { url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" }, + { url = "https://files.pythonhosted.org/packages/74/9a/02ffe35b2853d121689871eb5dce862092562b3a1ed5cc98f1aaed441506/cryptography-50.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269", size = 3816291, upload-time = "2026-07-31T14:24:22.125Z" }, + { url = "https://files.pythonhosted.org/packages/03/37/73d005be173aff344af30e9fd2a576575cb2391a7101d9cd3842e1fa8cce/cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07", size = 4036009, upload-time = "2026-07-31T14:24:24.122Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" }, + { url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" }, + { url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" }, + { url = "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" }, + { url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" }, + { url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" }, + { url = "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" }, + { url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" }, + { url = "https://files.pythonhosted.org/packages/57/30/4a22984d4f1bdfb8c054f07a92bc176b97a3134cc1d6c4b3bffb1f3688b4/cryptography-50.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba", size = 3874135, upload-time = "2026-07-31T14:24:50.085Z" }, +] + [[package]] name = "dependency-groups" version = "1.3.1" @@ -413,6 +572,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/e6/5fff07a70d1f945ed90ae131c3bd76cab32beff7c58c6db15ad5820b6d1f/psycopg_binary-3.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8", size = 3666849, upload-time = "2026-05-01T23:31:51.165Z" }, ] +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + [[package]] name = "pydantic" version = "2.13.4" @@ -512,6 +680,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pymysql" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/bc/1c6a92f385940f727daeecf3bacaf186e03875dff57197801046c583bcf0/pymysql-1.2.0.tar.gz", hash = "sha256:6c7b17ca686988104d7426c27895b455cdeea3e9d3ceb1270f0c3704fead8c33", size = 49021, upload-time = "2026-05-19T08:26:22.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/bd/2534e130295c8cfd4f0a2e31623baab7502278f1e97bcfe61db75656a77f/pymysql-1.2.0-py3-none-any.whl", hash = "sha256:62169ce6d5510f08e140c5e7990ee884a9764024e4a9a27b2cc11f1099322ae0", size = 45716, upload-time = "2026-05-19T08:26:20.974Z" }, +] + [[package]] name = "pyright" version = "1.1.411" @@ -645,12 +822,15 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "aiosqlite" }, + { name = "asyncmy" }, { name = "asyncpg" }, { name = "attrs" }, { name = "ciso8601" }, + { name = "cryptography" }, { name = "msgspec" }, { name = "psycopg", extra = ["binary"] }, { name = "pydantic" }, + { name = "pymysql" }, { name = "pyturso" }, ] @@ -671,6 +851,7 @@ dev-complete = [ { name = "pytest-cov" }, { name = "pytest-dependency" }, { name = "ruff" }, + { name = "types-pymysql" }, ] pyright = [ { name = "asyncpg-stubs" }, @@ -680,6 +861,7 @@ pyright = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-dependency" }, + { name = "types-pymysql" }, ] pytest = [ { name = "coverage" }, @@ -695,12 +877,15 @@ ruff = [ [package.metadata] requires-dist = [ { name = "aiosqlite", specifier = ">=0.21.0" }, + { name = "asyncmy", specifier = ">=0.2.14" }, { name = "asyncpg", specifier = ">=0.31.0" }, { name = "attrs", specifier = ">=25.3.0" }, { name = "ciso8601", specifier = ">=2.3.2" }, + { name = "cryptography", specifier = ">=45.0.0" }, { name = "msgspec", specifier = ">=0.19.0" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.2" }, { name = "pydantic", specifier = ">=2.9.0" }, + { name = "pymysql", specifier = ">=1.2.0" }, { name = "pyturso", specifier = ">=0.7.0" }, ] @@ -717,6 +902,7 @@ dev-complete = [ { name = "pytest-cov", specifier = ">=6.1.1" }, { name = "pytest-dependency", specifier = ">=0.6.0" }, { name = "ruff", specifier = ">=0.11.9" }, + { name = "types-pymysql", specifier = ">=1.2.0.20260807" }, ] pyright = [ { name = "asyncpg-stubs", specifier = ">=0.30.1" }, @@ -726,6 +912,7 @@ pyright = [ { name = "pytest-asyncio", specifier = ">=0.26.0" }, { name = "pytest-cov", specifier = ">=6.1.1" }, { name = "pytest-dependency", specifier = ">=0.6.0" }, + { name = "types-pymysql", specifier = ">=1.2.0.20260807" }, ] pytest = [ { name = "coverage", extras = ["toml"], specifier = ">=7.8.0" }, @@ -736,6 +923,15 @@ pytest = [ ] ruff = [{ name = "ruff", specifier = ">=0.11.9" }] +[[package]] +name = "types-pymysql" +version = "1.2.0.20260807" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/71/f2d9cf554a59396c9dcf7cd902d1c187b6d4762dc7650e25a02c4ca2cf9c/types_pymysql-1.2.0.20260807.tar.gz", hash = "sha256:7fd4a7767925dbb0fd3e73fd488184fcd98b7612f8066cda280453b17864264f", size = 22768, upload-time = "2026-08-07T04:17:29.272Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/83/37e485e7ad3404aa018b6740d759b74665669c61c9c154f81d890b2ccd3a/types_pymysql-1.2.0.20260807-py3-none-any.whl", hash = "sha256:2a722284451649e1c2564655cba4963c0661851d04cc10195b7c002fab8e55f6", size = 23327, upload-time = "2026-08-07T04:17:28.318Z" }, +] + [[package]] name = "typing-extensions" version = "4.16.0"