adamc@866
|
1 (* Copyright (c) 2008-2009, Adam Chlipala
|
adamc@866
|
2 * All rights reserved.
|
adamc@866
|
3 *
|
adamc@866
|
4 * Redistribution and use in source and binary forms, with or without
|
adamc@866
|
5 * modification, are permitted provided that the following conditions are met:
|
adamc@866
|
6 *
|
adamc@866
|
7 * - Redistributions of source code must retain the above copyright notice,
|
adamc@866
|
8 * this list of conditions and the following disclaimer.
|
adamc@866
|
9 * - Redistributions in binary form must reproduce the above copyright notice,
|
adamc@866
|
10 * this list of conditions and the following disclaimer in the documentation
|
adamc@866
|
11 * and/or other materials provided with the distribution.
|
adamc@866
|
12 * - The names of contributors may not be used to endorse or promote products
|
adamc@866
|
13 * derived from this software without specific prior written permission.
|
adamc@866
|
14 *
|
adamc@866
|
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
adamc@866
|
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
adamc@866
|
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
adamc@866
|
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
adamc@866
|
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
adamc@866
|
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
adamc@866
|
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
adamc@866
|
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
adamc@866
|
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
adamc@866
|
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
adamc@866
|
25 * POSSIBILITY OF SUCH DAMAGE.
|
adamc@866
|
26 *)
|
adamc@866
|
27
|
adamc@866
|
28 structure Postgres :> POSTGRES = struct
|
adamc@866
|
29
|
adamc@866
|
30 open Settings
|
adamc@866
|
31 open Print.PD
|
adamc@866
|
32 open Print
|
adamc@866
|
33
|
adamc@866
|
34 fun init (dbstring, ss) =
|
adamc@866
|
35 box [if #persistent (currentProtocol ()) then
|
adamc@866
|
36 box [string "static void uw_db_prepare(uw_context ctx) {",
|
adamc@866
|
37 newline,
|
adamc@866
|
38 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@866
|
39 newline,
|
adamc@866
|
40 string "PGresult *res;",
|
adamc@866
|
41 newline,
|
adamc@866
|
42 newline,
|
adamc@866
|
43
|
adamc@866
|
44 p_list_sepi newline (fn i => fn (s, n) =>
|
adamc@866
|
45 box [string "res = PQprepare(conn, \"uw",
|
adamc@866
|
46 string (Int.toString i),
|
adamc@866
|
47 string "\", \"",
|
adamc@866
|
48 string (String.toString s),
|
adamc@866
|
49 string "\", ",
|
adamc@866
|
50 string (Int.toString n),
|
adamc@866
|
51 string ", NULL);",
|
adamc@866
|
52 newline,
|
adamc@866
|
53 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@866
|
54 newline,
|
adamc@866
|
55 box [string "char msg[1024];",
|
adamc@866
|
56 newline,
|
adamc@866
|
57 string "strncpy(msg, PQerrorMessage(conn), 1024);",
|
adamc@866
|
58 newline,
|
adamc@866
|
59 string "msg[1023] = 0;",
|
adamc@866
|
60 newline,
|
adamc@866
|
61 string "PQclear(res);",
|
adamc@866
|
62 newline,
|
adamc@866
|
63 string "PQfinish(conn);",
|
adamc@866
|
64 newline,
|
adamc@866
|
65 string "uw_error(ctx, FATAL, \"Unable to create prepared statement:\\n",
|
adamc@866
|
66 string (String.toString s),
|
adamc@866
|
67 string "\\n%s\", msg);",
|
adamc@866
|
68 newline],
|
adamc@866
|
69 string "}",
|
adamc@866
|
70 newline,
|
adamc@866
|
71 string "PQclear(res);",
|
adamc@866
|
72 newline])
|
adamc@866
|
73 ss,
|
adamc@866
|
74
|
adamc@866
|
75 string "}",
|
adamc@866
|
76 newline,
|
adamc@866
|
77 newline,
|
adamc@866
|
78
|
adamc@866
|
79 string "void uw_db_close(uw_context ctx) {",
|
adamc@866
|
80 newline,
|
adamc@866
|
81 string "PQfinish(uw_get_db(ctx));",
|
adamc@866
|
82 newline,
|
adamc@866
|
83 string "}",
|
adamc@866
|
84 newline,
|
adamc@866
|
85 newline,
|
adamc@866
|
86
|
adamc@866
|
87 string "int uw_db_begin(uw_context ctx) {",
|
adamc@866
|
88 newline,
|
adamc@866
|
89 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@866
|
90 newline,
|
adamc@866
|
91 string "PGresult *res = PQexec(conn, \"BEGIN ISOLATION LEVEL SERIALIZABLE\");",
|
adamc@866
|
92 newline,
|
adamc@866
|
93 newline,
|
adamc@866
|
94 string "if (res == NULL) return 1;",
|
adamc@866
|
95 newline,
|
adamc@866
|
96 newline,
|
adamc@866
|
97 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@866
|
98 box [string "PQclear(res);",
|
adamc@866
|
99 newline,
|
adamc@866
|
100 string "return 1;",
|
adamc@866
|
101 newline],
|
adamc@866
|
102 string "}",
|
adamc@866
|
103 newline,
|
adamc@866
|
104 string "return 0;",
|
adamc@866
|
105 newline,
|
adamc@866
|
106 string "}",
|
adamc@866
|
107 newline,
|
adamc@866
|
108 newline,
|
adamc@866
|
109
|
adamc@866
|
110 string "int uw_db_commit(uw_context ctx) {",
|
adamc@866
|
111 newline,
|
adamc@866
|
112 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@866
|
113 newline,
|
adamc@866
|
114 string "PGresult *res = PQexec(conn, \"COMMIT\");",
|
adamc@866
|
115 newline,
|
adamc@866
|
116 newline,
|
adamc@866
|
117 string "if (res == NULL) return 1;",
|
adamc@866
|
118 newline,
|
adamc@866
|
119 newline,
|
adamc@866
|
120 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@866
|
121 box [string "PQclear(res);",
|
adamc@866
|
122 newline,
|
adamc@866
|
123 string "return 1;",
|
adamc@866
|
124 newline],
|
adamc@866
|
125 string "}",
|
adamc@866
|
126 newline,
|
adamc@866
|
127 string "return 0;",
|
adamc@866
|
128 newline,
|
adamc@866
|
129 string "}",
|
adamc@866
|
130 newline,
|
adamc@866
|
131 newline,
|
adamc@866
|
132
|
adamc@866
|
133 string "int uw_db_rollback(uw_context ctx) {",
|
adamc@866
|
134 newline,
|
adamc@866
|
135 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@866
|
136 newline,
|
adamc@866
|
137 string "PGresult *res = PQexec(conn, \"ROLLBACK\");",
|
adamc@866
|
138 newline,
|
adamc@866
|
139 newline,
|
adamc@866
|
140 string "if (res == NULL) return 1;",
|
adamc@866
|
141 newline,
|
adamc@866
|
142 newline,
|
adamc@866
|
143 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@866
|
144 box [string "PQclear(res);",
|
adamc@866
|
145 newline,
|
adamc@866
|
146 string "return 1;",
|
adamc@866
|
147 newline],
|
adamc@866
|
148 string "}",
|
adamc@866
|
149 newline,
|
adamc@866
|
150 string "return 0;",
|
adamc@866
|
151 newline,
|
adamc@866
|
152 string "}",
|
adamc@866
|
153 newline,
|
adamc@866
|
154 newline]
|
adamc@866
|
155 else
|
adamc@866
|
156 string "static void uw_db_prepare(uw_context ctx) { }",
|
adamc@866
|
157 newline,
|
adamc@866
|
158 newline,
|
adamc@866
|
159
|
adamc@866
|
160 string "void uw_db_init(uw_context ctx) {",
|
adamc@866
|
161 newline,
|
adamc@866
|
162 string "PGconn *conn = PQconnectdb(\"",
|
adamc@866
|
163 string (String.toString dbstring),
|
adamc@866
|
164 string "\");",
|
adamc@866
|
165 newline,
|
adamc@866
|
166 string "if (conn == NULL) uw_error(ctx, FATAL, ",
|
adamc@866
|
167 string "\"libpq can't allocate a connection.\");",
|
adamc@866
|
168 newline,
|
adamc@866
|
169 string "if (PQstatus(conn) != CONNECTION_OK) {",
|
adamc@866
|
170 newline,
|
adamc@866
|
171 box [string "char msg[1024];",
|
adamc@866
|
172 newline,
|
adamc@866
|
173 string "strncpy(msg, PQerrorMessage(conn), 1024);",
|
adamc@866
|
174 newline,
|
adamc@866
|
175 string "msg[1023] = 0;",
|
adamc@866
|
176 newline,
|
adamc@866
|
177 string "PQfinish(conn);",
|
adamc@866
|
178 newline,
|
adamc@866
|
179 string "uw_error(ctx, BOUNDED_RETRY, ",
|
adamc@866
|
180 string "\"Connection to Postgres server failed: %s\", msg);"],
|
adamc@866
|
181 newline,
|
adamc@866
|
182 string "}",
|
adamc@866
|
183 newline,
|
adamc@866
|
184 string "uw_set_db(ctx, conn);",
|
adamc@866
|
185 newline,
|
adamc@866
|
186 string "uw_db_validate(ctx);",
|
adamc@866
|
187 newline,
|
adamc@866
|
188 string "uw_db_prepare(ctx);",
|
adamc@866
|
189 newline,
|
adamc@866
|
190 string "}"]
|
adamc@866
|
191
|
adamc@867
|
192 fun p_getcol {wontLeakStrings, col = i, typ = t} =
|
adamc@867
|
193 let
|
adamc@867
|
194 fun p_unsql t e eLen =
|
adamc@867
|
195 case t of
|
adamc@867
|
196 Int => box [string "uw_Basis_stringToInt_error(ctx, ", e, string ")"]
|
adamc@867
|
197 | Float => box [string "uw_Basis_stringToFloat_error(ctx, ", e, string ")"]
|
adamc@867
|
198 | String =>
|
adamc@867
|
199 if wontLeakStrings then
|
adamc@867
|
200 e
|
adamc@867
|
201 else
|
adamc@867
|
202 box [string "uw_strdup(ctx, ", e, string ")"]
|
adamc@867
|
203 | Bool => box [string "uw_Basis_stringToBool_error(ctx, ", e, string ")"]
|
adamc@867
|
204 | Time => box [string "uw_Basis_stringToTime_error(ctx, ", e, string ")"]
|
adamc@867
|
205 | Blob => box [string "uw_Basis_stringToBlob_error(ctx, ",
|
adamc@867
|
206 e,
|
adamc@867
|
207 string ", ",
|
adamc@867
|
208 eLen,
|
adamc@867
|
209 string ")"]
|
adamc@867
|
210 | Channel => box [string "uw_Basis_stringToChannel_error(ctx, ", e, string ")"]
|
adamc@867
|
211 | Client => box [string "uw_Basis_stringToClient_error(ctx, ", e, string ")"]
|
adamc@867
|
212
|
adamc@867
|
213 | Nullable _ => raise Fail "Postgres: Recursive Nullable"
|
adamc@867
|
214
|
adamc@867
|
215 fun getter t =
|
adamc@867
|
216 case t of
|
adamc@867
|
217 Nullable t =>
|
adamc@867
|
218 box [string "(PQgetisnull(res, i, ",
|
adamc@867
|
219 string (Int.toString i),
|
adamc@867
|
220 string ") ? NULL : ",
|
adamc@867
|
221 case t of
|
adamc@867
|
222 String => getter t
|
adamc@867
|
223 | _ => box [string "({",
|
adamc@867
|
224 newline,
|
adamc@867
|
225 p_sql_type t,
|
adamc@867
|
226 space,
|
adamc@867
|
227 string "*tmp = uw_malloc(ctx, sizeof(",
|
adamc@867
|
228 p_sql_type t,
|
adamc@867
|
229 string "));",
|
adamc@867
|
230 newline,
|
adamc@867
|
231 string "*tmp = ",
|
adamc@867
|
232 getter t,
|
adamc@867
|
233 string ";",
|
adamc@867
|
234 newline,
|
adamc@867
|
235 string "tmp;",
|
adamc@867
|
236 newline,
|
adamc@867
|
237 string "})"],
|
adamc@867
|
238 string ")"]
|
adamc@867
|
239 | _ =>
|
adamc@867
|
240 box [string "(PQgetisnull(res, i, ",
|
adamc@867
|
241 string (Int.toString i),
|
adamc@867
|
242 string ") ? ",
|
adamc@867
|
243 box [string "({",
|
adamc@867
|
244 p_sql_type t,
|
adamc@867
|
245 space,
|
adamc@867
|
246 string "tmp;",
|
adamc@867
|
247 newline,
|
adamc@867
|
248 string "uw_error(ctx, FATAL, \"Unexpectedly NULL field #",
|
adamc@867
|
249 string (Int.toString i),
|
adamc@867
|
250 string "\");",
|
adamc@867
|
251 newline,
|
adamc@867
|
252 string "tmp;",
|
adamc@867
|
253 newline,
|
adamc@867
|
254 string "})"],
|
adamc@867
|
255 string " : ",
|
adamc@867
|
256 p_unsql t
|
adamc@867
|
257 (box [string "PQgetvalue(res, i, ",
|
adamc@867
|
258 string (Int.toString i),
|
adamc@867
|
259 string ")"])
|
adamc@867
|
260 (box [string "PQgetlength(res, i, ",
|
adamc@867
|
261 string (Int.toString i),
|
adamc@867
|
262 string ")"]),
|
adamc@867
|
263 string ")"]
|
adamc@867
|
264 in
|
adamc@867
|
265 getter t
|
adamc@867
|
266 end
|
adamc@867
|
267
|
adamc@867
|
268 fun queryCommon {loc, query, numCols, doCols} =
|
adamc@867
|
269 box [string "int n, i;",
|
adamc@867
|
270 newline,
|
adamc@867
|
271 newline,
|
adamc@867
|
272
|
adamc@867
|
273 string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
|
adamc@867
|
274 newline,
|
adamc@867
|
275 newline,
|
adamc@867
|
276
|
adamc@867
|
277 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
|
adamc@867
|
278 newline,
|
adamc@867
|
279 box [string "PQclear(res);",
|
adamc@867
|
280 newline,
|
adamc@867
|
281 string "uw_error(ctx, FATAL, \"",
|
adamc@867
|
282 string (ErrorMsg.spanToString loc),
|
adamc@867
|
283 string ": Query failed:\\n%s\\n%s\", ",
|
adamc@867
|
284 query,
|
adamc@867
|
285 string ", PQerrorMessage(conn));",
|
adamc@867
|
286 newline],
|
adamc@867
|
287 string "}",
|
adamc@867
|
288 newline,
|
adamc@867
|
289 newline,
|
adamc@867
|
290
|
adamc@867
|
291 string "if (PQnfields(res) != ",
|
adamc@867
|
292 string (Int.toString numCols),
|
adamc@867
|
293 string ") {",
|
adamc@867
|
294 newline,
|
adamc@867
|
295 box [string "int nf = PQnfields(res);",
|
adamc@867
|
296 newline,
|
adamc@867
|
297 string "PQclear(res);",
|
adamc@867
|
298 newline,
|
adamc@867
|
299 string "uw_error(ctx, FATAL, \"",
|
adamc@867
|
300 string (ErrorMsg.spanToString loc),
|
adamc@867
|
301 string ": Query returned %d columns instead of ",
|
adamc@867
|
302 string (Int.toString numCols),
|
adamc@867
|
303 string ":\\n%s\\n%s\", nf, ",
|
adamc@867
|
304 query,
|
adamc@867
|
305 string ", PQerrorMessage(conn));",
|
adamc@867
|
306 newline],
|
adamc@867
|
307 string "}",
|
adamc@867
|
308 newline,
|
adamc@867
|
309 newline,
|
adamc@867
|
310
|
adamc@867
|
311 string "uw_end_region(ctx);",
|
adamc@867
|
312 newline,
|
adamc@867
|
313 string "uw_push_cleanup(ctx, (void (*)(void *))PQclear, res);",
|
adamc@867
|
314 newline,
|
adamc@867
|
315 string "n = PQntuples(res);",
|
adamc@867
|
316 newline,
|
adamc@867
|
317 string "for (i = 0; i < n; ++i) {",
|
adamc@867
|
318 newline,
|
adamc@867
|
319 doCols p_getcol,
|
adamc@867
|
320 string "}",
|
adamc@867
|
321 newline,
|
adamc@867
|
322 newline,
|
adamc@867
|
323 string "uw_pop_cleanup(ctx);",
|
adamc@867
|
324 newline]
|
adamc@867
|
325
|
adamc@867
|
326 fun query {loc, numCols, doCols} =
|
adamc@867
|
327 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@867
|
328 newline,
|
adamc@867
|
329 string "PGresult *res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);",
|
adamc@867
|
330 newline,
|
adamc@867
|
331 newline,
|
adamc@867
|
332 queryCommon {loc = loc, numCols = numCols, doCols = doCols, query = string "query"}]
|
adamc@867
|
333
|
adamc@867
|
334 fun p_ensql t e =
|
adamc@867
|
335 case t of
|
adamc@867
|
336 Int => box [string "uw_Basis_attrifyInt(ctx, ", e, string ")"]
|
adamc@867
|
337 | Float => box [string "uw_Basis_attrifyFloat(ctx, ", e, string ")"]
|
adamc@867
|
338 | String => e
|
adamc@867
|
339 | Bool => box [string "(", e, string " ? \"TRUE\" : \"FALSE\")"]
|
adamc@867
|
340 | Time => box [string "uw_Basis_attrifyTime(ctx, ", e, string ")"]
|
adamc@867
|
341 | Blob => box [e, string ".data"]
|
adamc@867
|
342 | Channel => box [string "uw_Basis_attrifyChannel(ctx, ", e, string ")"]
|
adamc@867
|
343 | Client => box [string "uw_Basis_attrifyClient(ctx, ", e, string ")"]
|
adamc@867
|
344 | Nullable String => e
|
adamc@867
|
345 | Nullable t => box [string "(",
|
adamc@867
|
346 e,
|
adamc@867
|
347 string " == NULL ? NULL : ",
|
adamc@867
|
348 p_ensql t (box [string "(*", e, string ")"]),
|
adamc@867
|
349 string ")"]
|
adamc@867
|
350
|
adamc@867
|
351 fun queryPrepared {loc, id, query, inputs, numCols, doCols} =
|
adamc@867
|
352 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@867
|
353 newline,
|
adamc@867
|
354 string "const int paramFormats[] = { ",
|
adamc@867
|
355 p_list_sep (box [string ",", space])
|
adamc@867
|
356 (fn t => if isBlob t then string "1" else string "0") inputs,
|
adamc@867
|
357 string " };",
|
adamc@867
|
358 newline,
|
adamc@867
|
359 string "const int paramLengths[] = { ",
|
adamc@867
|
360 p_list_sepi (box [string ",", space])
|
adamc@867
|
361 (fn i => fn Blob => string ("arg" ^ Int.toString (i + 1) ^ ".size")
|
adamc@867
|
362 | Nullable Blob => string ("arg" ^ Int.toString (i + 1)
|
adamc@867
|
363 ^ "?arg" ^ Int.toString (i + 1) ^ "->size:0")
|
adamc@867
|
364 | _ => string "0") inputs,
|
adamc@867
|
365 string " };",
|
adamc@867
|
366 newline,
|
adamc@867
|
367 string "const char *paramValues[] = { ",
|
adamc@867
|
368 p_list_sepi (box [string ",", space])
|
adamc@867
|
369 (fn i => fn t => p_ensql t (box [string "arg",
|
adamc@867
|
370 string (Int.toString (i + 1))]))
|
adamc@867
|
371 inputs,
|
adamc@867
|
372 string " };",
|
adamc@867
|
373 newline,
|
adamc@867
|
374 newline,
|
adamc@867
|
375 string "PGresult *res = ",
|
adamc@867
|
376 if #persistent (Settings.currentProtocol ()) then
|
adamc@867
|
377 box [string "PQexecPrepared(conn, \"uw",
|
adamc@867
|
378 string (Int.toString id),
|
adamc@867
|
379 string "\", ",
|
adamc@867
|
380 string (Int.toString (length inputs)),
|
adamc@867
|
381 string ", paramValues, paramLengths, paramFormats, 0);"]
|
adamc@867
|
382 else
|
adamc@867
|
383 box [string "PQexecParams(conn, \"",
|
adamc@867
|
384 string (String.toString query),
|
adamc@867
|
385 string "\", ",
|
adamc@867
|
386 string (Int.toString (length inputs)),
|
adamc@867
|
387 string ", NULL, paramValues, paramLengths, paramFormats, 0);"],
|
adamc@867
|
388 newline,
|
adamc@867
|
389 newline,
|
adamc@867
|
390 queryCommon {loc = loc, numCols = numCols, doCols = doCols, query = box [string "\"",
|
adamc@867
|
391 string (String.toString query),
|
adamc@867
|
392 string "\""]}]
|
adamc@867
|
393
|
adamc@868
|
394 fun dmlCommon {loc, dml} =
|
adamc@868
|
395 box [string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating DML result.\");",
|
adamc@868
|
396 newline,
|
adamc@868
|
397 newline,
|
adamc@868
|
398
|
adamc@868
|
399 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@868
|
400 newline,
|
adamc@868
|
401 box [string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {",
|
adamc@868
|
402 box [newline,
|
adamc@868
|
403 string "PQclear(res);",
|
adamc@868
|
404 newline,
|
adamc@868
|
405 string "uw_error(ctx, UNLIMITED_RETRY, \"Serialization failure\");",
|
adamc@868
|
406 newline],
|
adamc@868
|
407 string "}",
|
adamc@868
|
408 newline,
|
adamc@868
|
409 string "PQclear(res);",
|
adamc@868
|
410 newline,
|
adamc@868
|
411 string "uw_error(ctx, FATAL, \"",
|
adamc@868
|
412 string (ErrorMsg.spanToString loc),
|
adamc@868
|
413 string ": DML failed:\\n%s\\n%s\", ",
|
adamc@868
|
414 dml,
|
adamc@868
|
415 string ", PQerrorMessage(conn));",
|
adamc@868
|
416 newline],
|
adamc@868
|
417 string "}",
|
adamc@868
|
418 newline,
|
adamc@868
|
419 newline,
|
adamc@868
|
420
|
adamc@868
|
421 string "PQclear(res);",
|
adamc@868
|
422 newline]
|
adamc@868
|
423
|
adamc@868
|
424 fun dml loc =
|
adamc@868
|
425 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@868
|
426 newline,
|
adamc@868
|
427 string "PGresult *res = PQexecParams(conn, dml, 0, NULL, NULL, NULL, NULL, 0);",
|
adamc@868
|
428 newline,
|
adamc@868
|
429 newline,
|
adamc@868
|
430 dmlCommon {loc = loc, dml = string "dml"}]
|
adamc@868
|
431
|
adamc@868
|
432 fun dmlPrepared {loc, id, dml, inputs} =
|
adamc@868
|
433 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@868
|
434 newline,
|
adamc@868
|
435 string "const int paramFormats[] = { ",
|
adamc@868
|
436 p_list_sep (box [string ",", space])
|
adamc@868
|
437 (fn t => if isBlob t then string "1" else string "0") inputs,
|
adamc@868
|
438 string " };",
|
adamc@868
|
439 newline,
|
adamc@868
|
440 string "const int paramLengths[] = { ",
|
adamc@868
|
441 p_list_sepi (box [string ",", space])
|
adamc@868
|
442 (fn i => fn Blob => string ("arg" ^ Int.toString (i + 1) ^ ".size")
|
adamc@868
|
443 | Nullable Blob => string ("arg" ^ Int.toString (i + 1)
|
adamc@868
|
444 ^ "?arg" ^ Int.toString (i + 1) ^ "->size:0")
|
adamc@868
|
445 | _ => string "0") inputs,
|
adamc@868
|
446 string " };",
|
adamc@868
|
447 newline,
|
adamc@868
|
448 string "const char *paramValues[] = { ",
|
adamc@868
|
449 p_list_sepi (box [string ",", space])
|
adamc@868
|
450 (fn i => fn t => p_ensql t (box [string "arg",
|
adamc@868
|
451 string (Int.toString (i + 1))]))
|
adamc@868
|
452 inputs,
|
adamc@868
|
453 string " };",
|
adamc@868
|
454 newline,
|
adamc@868
|
455 newline,
|
adamc@868
|
456 string "PGresult *res = ",
|
adamc@868
|
457 if #persistent (Settings.currentProtocol ()) then
|
adamc@868
|
458 box [string "PQexecPrepared(conn, \"uw",
|
adamc@868
|
459 string (Int.toString id),
|
adamc@868
|
460 string "\", ",
|
adamc@868
|
461 string (Int.toString (length inputs)),
|
adamc@868
|
462 string ", paramValues, paramLengths, paramFormats, 0);"]
|
adamc@868
|
463 else
|
adamc@868
|
464 box [string "PQexecParams(conn, \"",
|
adamc@868
|
465 string (String.toString dml),
|
adamc@868
|
466 string "\", ",
|
adamc@868
|
467 string (Int.toString (length inputs)),
|
adamc@868
|
468 string ", NULL, paramValues, paramLengths, paramFormats, 0);"],
|
adamc@868
|
469 newline,
|
adamc@868
|
470 newline,
|
adamc@868
|
471 dmlCommon {loc = loc, dml = box [string "\"",
|
adamc@868
|
472 string (String.toString dml),
|
adamc@868
|
473 string "\""]}]
|
adamc@868
|
474
|
adamc@869
|
475 fun nextvalCommon {loc, query} =
|
adamc@869
|
476 box [string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating nextval result.\");",
|
adamc@869
|
477 newline,
|
adamc@869
|
478 newline,
|
adamc@869
|
479
|
adamc@869
|
480 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
|
adamc@869
|
481 newline,
|
adamc@869
|
482 box [string "PQclear(res);",
|
adamc@869
|
483 newline,
|
adamc@869
|
484 string "uw_error(ctx, FATAL, \"",
|
adamc@869
|
485 string (ErrorMsg.spanToString loc),
|
adamc@869
|
486 string ": Query failed:\\n%s\\n%s\", ",
|
adamc@869
|
487 query,
|
adamc@869
|
488 string ", PQerrorMessage(conn));",
|
adamc@869
|
489 newline],
|
adamc@869
|
490 string "}",
|
adamc@869
|
491 newline,
|
adamc@869
|
492 newline,
|
adamc@869
|
493
|
adamc@869
|
494 string "uw_end_region(ctx);",
|
adamc@869
|
495 newline,
|
adamc@869
|
496 string "n = PQntuples(res);",
|
adamc@869
|
497 newline,
|
adamc@869
|
498 string "if (n != 1) {",
|
adamc@869
|
499 newline,
|
adamc@869
|
500 box [string "PQclear(res);",
|
adamc@869
|
501 newline,
|
adamc@869
|
502 string "uw_error(ctx, FATAL, \"",
|
adamc@869
|
503 string (ErrorMsg.spanToString loc),
|
adamc@869
|
504 string ": Wrong number of result rows:\\n%s\\n%s\", ",
|
adamc@869
|
505 query,
|
adamc@869
|
506 string ", PQerrorMessage(conn));",
|
adamc@869
|
507 newline],
|
adamc@869
|
508 string "}",
|
adamc@869
|
509 newline,
|
adamc@869
|
510 newline,
|
adamc@869
|
511
|
adamc@869
|
512 string "n = uw_Basis_stringToInt_error(ctx, PQgetvalue(res, 0, 0));",
|
adamc@869
|
513 newline,
|
adamc@869
|
514 string "PQclear(res);",
|
adamc@869
|
515 newline]
|
adamc@869
|
516
|
adamc@869
|
517 fun nextval loc =
|
adamc@869
|
518 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@869
|
519 newline,
|
adamc@869
|
520 string "PGresult *res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);",
|
adamc@869
|
521 newline,
|
adamc@869
|
522 newline,
|
adamc@869
|
523 nextvalCommon {loc = loc, query = string "query"}]
|
adamc@869
|
524
|
adamc@869
|
525 fun nextvalPrepared {loc, id, query} =
|
adamc@869
|
526 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@869
|
527 newline,
|
adamc@869
|
528 newline,
|
adamc@869
|
529 string "PGresult *res = ",
|
adamc@869
|
530 if #persistent (Settings.currentProtocol ()) then
|
adamc@869
|
531 box [string "PQexecPrepared(conn, \"uw",
|
adamc@869
|
532 string (Int.toString id),
|
adamc@869
|
533 string "\", 0, NULL, NULL, NULL, 0);"]
|
adamc@869
|
534 else
|
adamc@869
|
535 box [string "PQexecParams(conn, \"",
|
adamc@869
|
536 string (String.toString query),
|
adamc@869
|
537 string "\", 0, NULL, NULL, NULL, NULL, 0);"],
|
adamc@869
|
538 newline,
|
adamc@869
|
539 newline,
|
adamc@869
|
540 nextvalCommon {loc = loc, query = box [string "\"",
|
adamc@869
|
541 string (String.toString query),
|
adamc@869
|
542 string "\""]}]
|
adamc@869
|
543
|
adamc@866
|
544 val () = addDbms {name = "postgres",
|
adamc@866
|
545 header = "postgresql/libpq-fe.h",
|
adamc@866
|
546 link = "-lpq",
|
adamc@866
|
547 global_init = box [string "void uw_client_init() { }",
|
adamc@866
|
548 newline],
|
adamc@867
|
549 init = init,
|
adamc@867
|
550 query = query,
|
adamc@868
|
551 queryPrepared = queryPrepared,
|
adamc@868
|
552 dml = dml,
|
adamc@869
|
553 dmlPrepared = dmlPrepared,
|
adamc@869
|
554 nextval = nextval,
|
adamc@869
|
555 nextvalPrepared = nextvalPrepared}
|
adamc@866
|
556 val () = setDbms "postgres"
|
adamc@866
|
557
|
adamc@866
|
558 end
|