Skip to content
Feature support

Feature support

Which sqlc features and query commands the plugin supports.

Macros

Every sqlc macro is supported (sqlc.arg, sqlc.narg, sqlc.embed, sqlc.slice).

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.

Query commands

The supported query annotations depend on the driver:

Commandaiosqlitesqlite3asyncpgpsycopg_asyncpsycopg_syncasyncmypymysqlturso_asyncturso_sync
:oneyesyesyesyesyesyesyesyesyes
:manyyesyesyesyesyesyesyesyesyes
:execyesyesyesyesyesyesyesyesyes
:execresultyesyesyesyesyesyesyesyesyes
:execrowsyesyesyesyesyesyesyesyesyes
:execlastidyesyesnononoyesyesyesyes
:copyfromnonoyesyesyesnononono

See Writing queries for what each command generates.

  • :execlastid relies on a last-inserted-row id, which PostgreSQL does not provide - use a RETURNING clause with :one instead.
  • On the turso drivers :execlastid returns None for UPDATE/DELETE 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 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.

Prepared queries

Looking for sqlc’s emit_prepared_queries? There is none, on purpose: every stable driver already prepares statements automatically. What differs is when a query is prepared and which knob controls it:

  • asyncpg prepares every query it runs and keeps it in a per-connection LRU statement cache (100 entries by default). Tune it at connect time:

    conn = await asyncpg.connect(
        dsn,
        statement_cache_size=200,  # default 100; 0 disables the cache
    )
  • psycopg (both flavors) prepares a query server-side once it has been executed more than prepare_threshold times on the connection - with the default of 5, the sixth execution is the first prepared one. Set it to 0 to prepare from the first execution, or None to never prepare:

    conn = await psycopg.AsyncConnection.connect(dsn, prepare_threshold=0)  # psycopg_async
    conn = psycopg.connect(dsn, prepare_threshold=0)  # psycopg_sync
  • sqlite3 / aiosqlite expose no explicit prepare API, but the sqlite3 module compiles each statement once and reuses it through an internal per-connection cache (128 entries by default). Raise it with the cached_statements argument of connect() if you have more distinct queries than that.

  • 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.

Behind PgBouncer in transaction-pooling mode, server-side prepared statements belong to a connection you do not control. Disable them there: statement_cache_size=0 for asyncpg, prepare_threshold=None for psycopg.

Not supported

  • :batch* commands (:batchexec, :batchmany, :batchone) are not supported and likely never will be.
  • 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).