v / vlib / db / pg / db.v
207 lines · 176 sloc · 6.49 KB · 2b04a6ecbf16697b4a6ae2e1e02d3a381967e8f0
Raw
1module pg
2
3import io
4import time
5
6// DB is a thread-safe handle to a PostgreSQL database, backed by a pool of
7// `Conn` objects. It mirrors Go's `database/sql.DB` design: methods on `DB`
8// transparently acquire a conn for the call, then release it back to the
9// pool. For operations that must run on the same physical connection
10// (LISTEN/NOTIFY, session-scoped prepared statements, manual transactions),
11// use `db.conn()` to pin a conn or `db.begin()` to start a transaction.
12pub struct DB {
13mut:
14 pool &Pool = unsafe { nil }
15}
16
17// connect creates a new pool and opens an initial connection to verify the
18// config works. The returned `&DB` is safe to share between threads.
19pub fn connect(config Config, pcfg PoolConfig) !&DB {
20 return connect_with_conninfo(config.conninfo()!, pcfg)!
21}
22
23// connect_with_conninfo is the conninfo-string variant of `connect`.
24pub fn connect_with_conninfo(conninfo string, pcfg PoolConfig) !&DB {
25 mut db := &DB{
26 pool: new_pool(conninfo, pcfg)
27 }
28 // Fail fast if the conninfo is wrong, rather than at first query.
29 probe := db.pool.acquire()!
30 db.pool.release(probe)
31 return db
32}
33
34// close shuts down the pool and tears down all idle connections.
35// In-flight conns will be closed when released.
36pub fn (mut db DB) close() ! {
37 if isnil(db.pool) {
38 return
39 }
40 db.pool.close()
41}
42
43// stats returns a snapshot of the pool state.
44pub fn (mut db DB) stats() PoolStats {
45 return db.pool.stats()
46}
47
48// set_max_open_conns caps the total number of open connections.
49// A value of 0 means unlimited (the default, like Go).
50pub fn (mut db DB) set_max_open_conns(n int) {
51 db.pool.set_max_open(n)
52}
53
54// set_max_idle_conns caps the number of idle connections kept warm.
55pub fn (mut db DB) set_max_idle_conns(n int) {
56 db.pool.set_max_idle(n)
57}
58
59// set_conn_max_lifetime sets the maximum amount of time a conn may be reused.
60// A value of zero means conns are reused indefinitely.
61pub fn (mut db DB) set_conn_max_lifetime(d time.Duration) {
62 db.pool.set_conn_max_lifetime(d)
63}
64
65// conn checks a conn out of the pool. The caller is responsible for calling
66// `conn.close()` when done; failing to do so leaks the conn. Use this when
67// you need session-bound operations like LISTEN/NOTIFY.
68pub fn (mut db DB) conn() !&Conn {
69 return db.pool.acquire()
70}
71
72// validate borrows a conn from the pool and checks it is alive.
73pub fn (mut db DB) validate() !bool {
74 mut c := db.pool.acquire()!
75 defer { c.close() or {} }
76 return c.validate()
77}
78
79// reset is a no-op kept for ORM compatibility.
80pub fn (mut db DB) reset() ! {
81}
82
83// ---- exec/query helpers (acquire-use-release) ----
84
85// exec runs `query` on a pooled conn and returns the rows.
86pub fn (mut db DB) exec(query string) ![]Row {
87 mut c := db.pool.acquire()!
88 defer { c.close() or {} }
89 return c.exec(query)
90}
91
92// exec_no_null runs `query` and returns rows with no nullable fields.
93pub fn (mut db DB) exec_no_null(query string) ![]RowNoNull {
94 mut c := db.pool.acquire()!
95 defer { c.close() or {} }
96 return c.exec_no_null(query)
97}
98
99// exec_result runs `query` and returns a `Result` (rows + column index).
100pub fn (mut db DB) exec_result(query string) !Result {
101 mut c := db.pool.acquire()!
102 defer { c.close() or {} }
103 return c.exec_result(query)
104}
105
106// exec_one runs `query` and returns its first row.
107pub fn (mut db DB) exec_one(query string) !Row {
108 mut c := db.pool.acquire()!
109 defer { c.close() or {} }
110 return c.exec_one(query)
111}
112
113// exec_param_many runs `query` with the given parameters.
114pub fn (mut db DB) exec_param_many(query string, params []string) ![]Row {
115 mut c := db.pool.acquire()!
116 defer { c.close() or {} }
117 return c.exec_param_many(query, params)
118}
119
120// exec_param_many_result runs `query` with parameters and returns a `Result`.
121pub fn (mut db DB) exec_param_many_result(query string, params []string) !Result {
122 mut c := db.pool.acquire()!
123 defer { c.close() or {} }
124 return c.exec_param_many_result(query, params)
125}
126
127// exec_param runs `query` with a single `$1` parameter.
128pub fn (mut db DB) exec_param(query string, param string) ![]Row {
129 return db.exec_param_many(query, [param])
130}
131
132// exec_param2 runs `query` with two parameters (`$1`, `$2`).
133pub fn (mut db DB) exec_param2(query string, param string, param2 string) ![]Row {
134 return db.exec_param_many(query, [param, param2])
135}
136
137// q_int runs `query` and returns the first column of the first row as int.
138pub fn (mut db DB) q_int(query string) !int {
139 mut c := db.pool.acquire()!
140 defer { c.close() or {} }
141 return c.q_int(query)
142}
143
144// q_string runs `query` and returns the first column of the first row as string.
145pub fn (mut db DB) q_string(query string) !string {
146 mut c := db.pool.acquire()!
147 defer { c.close() or {} }
148 return c.q_string(query)
149}
150
151// q_strings runs `query` and returns the full row set (alias of `exec`).
152pub fn (mut db DB) q_strings(query string) ![]Row {
153 return db.exec(query)
154}
155
156// copy_expert runs a COPY command on a pooled conn.
157pub fn (mut db DB) copy_expert(query string, mut file io.ReaderWriter) !int {
158 mut c := db.pool.acquire()!
159 defer { c.close() or {} }
160 return c.copy_expert(query, mut file)
161}
162
163// ---- prepared statements ----
164//
165// NOTE: prepared statements are session-scoped. Calling `prepare` on `DB`
166// only registers the statement on the conn that happened to serve the call.
167// Use `db.conn()` to pin a conn for prepare+exec_prepared cycles.
168
169// prepare registers a prepared statement on a transient conn.
170// For repeated use, pin a conn via `db.conn()`.
171pub fn (mut db DB) prepare(name string, query string, num_params int) ! {
172 mut c := db.pool.acquire()!
173 defer { c.close() or {} }
174 return c.prepare(name, query, num_params)
175}
176
177// exec_prepared runs a previously-prepared statement.
178pub fn (mut db DB) exec_prepared(name string, params []string) ![]Row {
179 mut c := db.pool.acquire()!
180 defer { c.close() or {} }
181 return c.exec_prepared(name, params)
182}
183
184// exec_prepared_result runs a previously-prepared statement and returns a `Result`.
185pub fn (mut db DB) exec_prepared_result(name string, params []string) !Result {
186 mut c := db.pool.acquire()!
187 defer { c.close() or {} }
188 return c.exec_prepared_result(name, params)
189}
190
191// ---- transactions ----
192
193// begin starts a new transaction and returns a `Tx` that pins a conn from
194// the pool. The conn is released when `Tx.commit()` or `Tx.rollback()` is
195// called. The default isolation level is REPEATABLE READ (matching the old
196// single-conn API); pass `PQTransactionParam{ transaction_level: ... }` to
197// override.
198pub fn (mut db DB) begin(param PQTransactionParam) !&Tx {
199 mut c := db.pool.acquire()!
200 c.begin_on_conn(param) or {
201 c.close() or {}
202 return err
203 }
204 return &Tx{
205 conn: c
206 }
207}
208