-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathasync_postgres.nim
More file actions
267 lines (262 loc) · 10.8 KB
/
Copy pathasync_postgres.nim
File metadata and controls
267 lines (262 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
## Async PostgreSQL client for Nim.
##
## Implements the PostgreSQL wire protocol v3 with full support for the
## extended query protocol, connection pooling, SSL/TLS, and binary format
## optimization.
##
## Async Backend
## =============
## Select at compile time with ``-d:asyncBackend=asyncdispatch`` (default) or
## ``-d:asyncBackend=chronos``.
##
## Quick Start
## ===========
##
## .. code-block:: nim
## import pkg/async_postgres
##
## proc main() {.async.} =
## let conn = await connect("postgresql://myuser:mypass@127.0.0.1:5432/mydb")
## defer: await conn.close()
##
## # Insert with typed parameters
## let name = "Alice"
## let age = 30'i32
## let cr = await conn.exec(sql"INSERT INTO users (name, age) VALUES ({name}, {age})")
## echo "Inserted: ", cr.affectedRows
##
## # Query multiple rows
## let minAge = 25'i32
## let row = await conn.query(sql"SELECT id, name, age FROM users WHERE age > {minAge}")
## for r in row:
## echo r.getStr("name"), " age=", r.getInt("age")
##
## # Query a single value
## let count = await conn.queryValueOrDefault("SELECT count(*) FROM users", default = "0")
## echo "Total users: ", count
##
## waitFor main()
##
## Choosing a query API
## ====================
## Four parameterised entry points are provided. They differ in how parameters
## are supplied and in the amount of per-call allocation:
##
## 1. `sql"..."` macro — compile-time ``{expr}`` interpolation
## ----------------------------------------------------------------------
## Most readable. ``{expr}`` placeholders are rewritten to ``$1, $2, …`` at
## compile time and the expressions are collected into a ``seq[PgParam]``.
## Works with `query`, `exec`, `pool.query`, `cluster.withReadConnection`, etc.
##
## .. code-block:: nim
## let name = "Alice"
## await conn.query(sql"SELECT id FROM users WHERE name = {name}")
##
## - Pros: concise; compile-time placeholder rewriting; SQL injection-safe.
## - Cons: still allocates a ``seq[PgParam]`` per call; ``{expr}`` must be a
## compile-time-visible expression (not an ``openArray`` spread).
## - Use when: SQL is a literal and ergonomics matter more than zero-alloc.
##
## 2. `query`/`exec` with an explicit ``seq[PgParam]``
## ----------------------------------------------------------------------
## The baseline runtime API. Use when the SQL or the parameter list is
## constructed dynamically (e.g. conditional ``WHERE`` clauses).
##
## .. code-block:: nim
## var params: seq[PgParam]
## params.add name.toPgParam
## params.add age.toPgParam
## await conn.query("SELECT id FROM users WHERE name = $1 AND age > $2", params)
##
## The ``pgParams(a, b, c)`` macro builds a ``seq[PgParam]`` in one call. A
## second overload takes ``seq[PgParamInline]`` — constructed manually as
## ``@[toPgParamInline(a), toPgParamInline(b)]`` — which avoids per-parameter
## heap allocations for scalar types.
##
## 3. `queryDirect`/`execDirect` — zero-allocation macros
## ----------------------------------------------------------------------
## Encodes parameters directly into the connection's send buffer at compile
## time; no intermediate ``seq[PgParam]`` or ``seq[byte]`` is built.
##
## .. code-block:: nim
## let qr = await conn.queryDirect("SELECT name FROM users WHERE id = $1", myId)
##
## - Pros: no per-call allocations for the parameter path; same statement
## cache semantics as `query`.
## - Cons: SQL must be a string literal/compile-time constant; arguments are
## positional (``$1, $2, …``), no ``{expr}`` sugar.
## - Use when: the call site is on a hot path and params are scalars.
##
## 4. `simpleQuery`/`simpleExec` — simple query protocol
## ----------------------------------------------------------------------
## Parameter-less, text-only, single round trip. Allows multiple
## ``;``-separated statements and session-only commands that the extended
## protocol rejects (``SET``, ``LISTEN``, ``VACUUM``, …).
##
## .. code-block:: nim
## discard await conn.simpleExec("SET search_path TO myschema, public")
##
## Quick decision table
## --------------------
## ========================= ===================================================
## Situation Prefer
## ========================= ===================================================
## Literal SQL, readability ``sql"..."`` macro
## Dynamic SQL or params ``query(sql, params)`` / ``exec``
## Hot path, scalar params ``queryDirect`` / ``execDirect``
## ``SET`` / multi-statement ``simpleQuery`` / ``simpleExec``
## ========================= ===================================================
##
## ``sql"..."``, ``query``/``exec``, and ``queryDirect``/``execDirect`` share
## the per-connection prepared-statement cache; ``simpleQuery``/``simpleExec``
## use the simple protocol and are not cached. A ``timeout`` parameter is
## accepted by ``query``/``exec``, ``queryDirect``/``execDirect``, and
## ``simpleQuery``/``simpleExec``; on timeout the connection is marked
## closed because the wire protocol desynchronises.
##
## Modules
## =======
## - `pg_connection <async_postgres/pg_connection.html>`_ — Connection management, DSN parsing, SSL, LISTEN/NOTIFY
## - `pg_client <async_postgres/pg_client.html>`_ — Query execution, prepared statements, cursors, pipelines, transactions, COPY, zero-alloc macros (``queryDirect``/``execDirect``)
## - `pg_pool <async_postgres/pg_pool.html>`_ — Connection pooling with health checks and maintenance
## - `pg_pool_cluster <async_postgres/pg_pool_cluster.html>`_ — Read replica pool cluster with automatic query routing
## - `pg_types <async_postgres/pg_types.html>`_ — Type conversions (``toPgParam``, row accessors, arrays, ranges, composites, enums)
## - `pg_protocol <async_postgres/pg_protocol.html>`_ — Wire protocol encoding/decoding
## - `pg_auth <async_postgres/pg_auth.html>`_ — MD5 and SCRAM-SHA-256 authentication
## - `pg_largeobject <async_postgres/pg_largeobject.html>`_ — Large Object API for streaming binary data
## - `pg_advisory_lock <async_postgres/pg_advisory_lock.html>`_ — Advisory lock API (session/transaction, exclusive/shared)
## - `pg_replication <async_postgres/pg_replication.html>`_ — Logical replication streaming with pgoutput decoder
## - `async_backend <async_postgres/async_backend.html>`_ — Async framework abstraction (asyncdispatch / chronos)
import
async_postgres/[
async_backend, pg_protocol, pg_auth, pg_types, pg_connection, pg_client, pg_pool,
pg_pool_cluster, pg_largeobject, pg_advisory_lock, pg_sql, pg_replication,
]
# `pg_types`/`pg_connection`/`pg_client` whitelist themselves; the other
# modules expose only their public API surface.
export pg_types, pg_connection, pg_client
export pg_pool_cluster, pg_largeobject, pg_advisory_lock, pg_sql, pg_replication
export pg_auth
# `pg_pool` — public pool API (internal gauges/helpers stay in the module).
export pg_pool.PoolConfig
export pg_pool.PoolMetrics
export pg_pool.PooledConnHandle
export pg_pool.PgPool
export pg_pool.initPoolConfig
export pg_pool.idleCount
export pg_pool.activeCount
export pg_pool.size
export pg_pool.isClosed
export pg_pool.metrics
export pg_pool.resetSession
export pg_pool.newPool
export pg_pool.release
export pg_pool.resetSessionAndRelease
export pg_pool.acquire
export pg_pool.runAndRelease
export pg_pool.withConnection
export pg_pool.exec
export pg_pool.query
export pg_pool.queryEach
export pg_pool.queryRow
export pg_pool.queryRowOpt
export pg_pool.queryValue
export pg_pool.queryValueOpt
export pg_pool.queryValueOrDefault
export pg_pool.queryExists
export pg_pool.queryColumn
export pg_pool.simpleQuery
export pg_pool.simpleExec
export pg_pool.execInTransaction
export pg_pool.queryInTransaction
export pg_pool.notify
export pg_pool.withTransaction
export pg_pool.withTransactionRetry
export pg_pool.withTransactionDeadline
export pg_pool.withTransactionRetryDeadline
export pg_pool.withPipeline
export pg_pool.close
# `async_backend` is exported wholesale; it also re-exports the selected
# backend (asyncdispatch / chronos), supplying `async`, `waitFor`, etc.
export async_backend
# `pg_protocol` — the wire protocol codec and entry points. The inbound
# decoders and leaf encoders stay internal; the send-buffer helpers are
# re-exported because the `addParseDirect`/`addBindDirect` macros and advanced
# call sites resolve them in the caller's scope.
export pg_protocol.FrontendMessageKind
export pg_protocol.BackendMessageKind
export pg_protocol.DescribeKind
export pg_protocol.TransactionStatus
export pg_protocol.FieldDescription
export pg_protocol.CopyFormat
export pg_protocol.BackendMessage
export pg_protocol.ParseState
export pg_protocol.ParseResult
export pg_protocol.RowData
export pg_protocol.Row
export pg_protocol.syncMsg
export pg_protocol.flushMsg
export pg_protocol.copyDoneMsg
export pg_protocol.BinarySafeOids
export pg_protocol.maxInt32Len
export pg_protocol.DefaultMaxBackendMessageLen
export pg_protocol.MaxNegotiateProtocolOptions
export pg_protocol.MaxErrorOrNoticeFields
export pg_protocol.MaxSaslMechanisms
export pg_protocol.initRow
export pg_protocol.data
export pg_protocol.rowIdx
export pg_protocol.isBinarySafeOid
export pg_protocol.addInt16
export pg_protocol.addInt32
export pg_protocol.addCount16
export pg_protocol.addLen32
export pg_protocol.addCString
export pg_protocol.patchMsgLen
export pg_protocol.encodeStartup
export pg_protocol.encodeSSLRequest
export pg_protocol.encodePassword
export pg_protocol.encodeSASLInitialResponse
export pg_protocol.encodeSASLResponse
export pg_protocol.encodeQuery
export pg_protocol.addParse
export pg_protocol.addBind
export pg_protocol.addBindRaw
export pg_protocol.addDescribe
export pg_protocol.addExecute
export pg_protocol.addClose
export pg_protocol.addSync
export pg_protocol.addFlush
export pg_protocol.addCopyDone
export pg_protocol.encodeParse
export pg_protocol.encodeBind
export pg_protocol.encodeDescribe
export pg_protocol.encodeExecute
export pg_protocol.encodeClose
export pg_protocol.encodeSync
export pg_protocol.encodeFlush
export pg_protocol.encodeTerminate
export pg_protocol.encodeCancelRequest
export pg_protocol.encodeCopyData
export pg_protocol.encodeCopyDone
export pg_protocol.encodeCopyFail
export pg_protocol.newRowData
export pg_protocol.reuseRowData
export pg_protocol.clone
export pg_protocol.buildResultFormats
export pg_protocol.parseDataRowInto
export pg_protocol.parseBackendMessage
export pg_protocol.formatError
export pg_protocol.addCopyBinaryHeader
export pg_protocol.addCopyBinaryTrailer
export pg_protocol.addCopyTupleStart
export pg_protocol.addCopyFieldNull
export pg_protocol.addCopyFieldInt16
export pg_protocol.addCopyFieldInt32
export pg_protocol.addCopyFieldInt64
export pg_protocol.addCopyFieldFloat64
export pg_protocol.addCopyFieldFloat32
export pg_protocol.addCopyFieldBool
export pg_protocol.addCopyFieldText
export pg_protocol.addCopyFieldString
export pg_protocol.encodeStandbyStatusUpdate