Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 67 additions & 2 deletions async_postgres/pg_client.nim
Original file line number Diff line number Diff line change
@@ -1,3 +1,57 @@
## Query execution API.
##
## Choosing between extended- and simple-protocol entry points
## ===========================================================
##
## ``exec`` / ``query`` use the **extended query protocol** (Parse / Bind /
## Describe / Execute). They are the default choice for application queries:
##
## - Exactly one statement per call.
## - Typed parameters via ``seq[PgParam]`` or ``openArray[PgParamInline]`` —
## values are bound out-of-band, so no string escaping is required.
## - Reuses server-side prepared statements across calls with identical SQL
## text (bounded by ``stmtCacheCapacity``); the statement is parsed once
## and rebound on subsequent calls.
## - Result rows may use the binary wire format when ``resultFormat =
## rfBinary`` is passed, or on paths that build per-column format codes
## via ``buildResultFormats``. The default ``rfAuto`` returns text rows.
##
## ``simpleExec`` / ``simpleQuery`` use the **simple query protocol** (a single
## ``Query`` message, text-only rows). Prefer them only when the extended
## protocol cannot express what you need:
##
## - **No parameters.** The SQL string is sent verbatim — only use with
## trusted input, or quote identifiers/literals yourself (e.g. via
## ``quoteIdentifier``).
## - **No prepared statement reuse.** Each call re-parses on the server;
## appropriate for one-off session commands (``BEGIN``, ``SET``,
## ``VACUUM`` …) where a cached statement would be wasted. For
## ``LISTEN`` / ``UNLISTEN`` / ``NOTIFY`` prefer the dedicated ``listen``,
## ``unlisten``, and ``notify`` helpers — they quote the channel name for
## you.
## - ``simpleQuery`` accepts multiple ``;``-separated statements and returns
## one ``QueryResult`` per statement — the one case the extended protocol
## cannot cover in a single round trip.
## - ``simpleExec`` expects a side-effect command; the returned tag is the
## **last** ``CommandComplete`` seen, so multi-statement input is accepted
## but per-statement results are not surfaced — use ``simpleQuery`` when
## you need them.
##
## Quick reference
## ---------------
##
## =========================== ========= ============ =========== ==============
## API Protocol Multi-stmt Parameters Plan cache
## =========================== ========= ============ =========== ==============
## ``query`` / ``exec`` extended no yes yes
## ``simpleQuery`` simple yes no no
## ``simpleExec`` simple last-wins no no
## =========================== ========= ============ =========== ==============
##
## Timeout behaviour is shared by all four: when a ``timeout`` is exceeded the
## connection is marked ``csClosed`` (the protocol may be mid-exchange) and a
## pooled connection is discarded on release.

import std/[options, tables, macros]

import async_backend, pg_protocol, pg_connection, pg_types
Expand Down Expand Up @@ -363,7 +417,13 @@ proc exec*(
params: seq[PgParam] = @[],
timeout: Duration = ZeroDuration,
): Future[CommandResult] {.async.} =
## Execute a statement with typed parameters.
## Execute a statement with typed parameters via the extended query protocol.
##
## Single statement only; the plan is cached per-connection. Use
## ``simpleExec`` for parameter-less session commands (``BEGIN``, ``SET``,
## ``VACUUM``, ``LISTEN`` …) or ``simpleQuery`` when you need multi-statement
## execution in one round trip.
##
## On timeout the connection is marked closed (protocol desync) and cannot be
## reused; pooled connections are discarded automatically.
var tag: string
Expand Down Expand Up @@ -991,7 +1051,12 @@ proc query*(
resultFormat: ResultFormat = rfAuto,
timeout: Duration = ZeroDuration,
): Future[QueryResult] {.async.} =
## Execute a query with typed parameters.
## Execute a query with typed parameters via the extended query protocol.
##
## Single statement only; the plan is cached per-connection. Use
## ``simpleQuery`` when you need multiple ``;``-separated statements to run
## in one round trip (no parameters, text-only rows).
##
## On timeout the connection is marked closed (protocol desync) and cannot be
## reused; pooled connections are discarded automatically.
var qr: QueryResult
Expand Down
31 changes: 27 additions & 4 deletions async_postgres/pg_connection.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1547,8 +1547,18 @@ proc quoteIdentifier*(s: string): string =
"\"" & s.replace("\"", "\"\"") & "\""

proc simpleQuery*(conn: PgConnection, sql: string): Future[seq[QueryResult]] {.async.} =
## Execute one or more SQL statements via simple query protocol.
## Returns one `QueryResult` per statement. Supports multiple statements separated by semicolons.
## Execute one or more SQL statements via the **simple query protocol**.
##
## Returns one ``QueryResult`` per statement; supports multiple statements
## separated by ``;`` in a single round trip — this is the main reason to
## choose ``simpleQuery`` over ``query``.
##
## No parameters are supported (the SQL string is sent verbatim — only use
## trusted input) and rows are always in the text wire format. No
## server-side plan cache entry is created.
##
## For single-statement parameterised reads, prefer ``query``; for
## parameter-less commands without rows, prefer ``simpleExec``.
conn.checkReady()

var results: seq[QueryResult]
Expand Down Expand Up @@ -1718,8 +1728,21 @@ proc invalidateOnTimeout*(conn: PgConnection, reason: string) =
proc simpleExec*(
conn: PgConnection, sql: string, timeout: Duration = ZeroDuration
): Future[CommandResult] {.async.} =
## Execute a SQL statement via simple query protocol, returning the command result.
## Lighter than `exec` for parameter-less commands (no Parse/Bind/Describe overhead).
## Execute a side-effect SQL command via the **simple query protocol**,
## returning the final command tag.
##
## Lighter than ``exec`` for parameter-less commands — one ``Query`` message,
## no Parse/Bind/Describe round trip and no plan cache entry. Intended for
## session-level commands such as ``BEGIN``, ``SET``, ``VACUUM``,
## ``LISTEN``, ``NOTIFY``.
##
## The SQL string is sent verbatim (no parameters) — only use trusted input,
## or quote interpolated identifiers yourself via ``quoteIdentifier``.
##
## Multiple ``;``-separated statements are accepted, but only the **last**
## command tag is returned; use ``simpleQuery`` if you need per-statement
## results. For parameterised writes, prefer ``exec``.
##
## On timeout, the connection is marked csClosed (protocol out of sync).
var tag: string
withConnTracing(
Expand Down
9 changes: 6 additions & 3 deletions async_postgres/pg_pool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,9 @@ proc queryColumn*(
result.add(row.getStr(0))

proc simpleQuery*(pool: PgPool, sql: string): Future[seq[QueryResult]] {.async.} =
## Execute one or more SQL statements via simple query protocol using a pooled connection.
## Execute one or more SQL statements via the simple query protocol using a
## pooled connection. See ``PgConnection.simpleQuery`` for semantics —
## multi-statement, no parameters, no plan cache.
let conn = await pool.acquire()
try:
return await conn.simpleQuery(sql)
Expand All @@ -903,8 +905,9 @@ proc simpleQuery*(pool: PgPool, sql: string): Future[seq[QueryResult]] {.async.}
proc simpleExec*(
pool: PgPool, sql: string, timeout: Duration = ZeroDuration
): Future[CommandResult] {.async.} =
## Execute a SQL statement via simple query protocol using a pooled connection.
## Returns the command result.
## Execute a side-effect SQL command via the simple query protocol using a
## pooled connection. See ``PgConnection.simpleExec`` for semantics — no
## parameters, no plan cache, last command tag returned.
let conn = await pool.acquire()
try:
return await conn.simpleExec(sql, timeout)
Expand Down