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@870
|
34 val ident = String.translate (fn #"'" => "PRIME"
|
adamc@870
|
35 | ch => str ch)
|
adamc@870
|
36
|
adamc@870
|
37 fun p_sql_type_base t =
|
adamc@870
|
38 case t of
|
adamc@870
|
39 Int => "int8"
|
adamc@870
|
40 | Float => "float8"
|
adamc@870
|
41 | String => "text"
|
adamc@870
|
42 | Bool => "bool"
|
adamc@870
|
43 | Time => "timestamp"
|
adamc@870
|
44 | Blob => "bytea"
|
adamc@870
|
45 | Channel => "int8"
|
adamc@870
|
46 | Client => "int4"
|
adamc@870
|
47 | Nullable t => p_sql_type_base t
|
adamc@870
|
48
|
adamc@870
|
49 fun init {dbstring, prepared = ss, tables, sequences} =
|
adamc@866
|
50 box [if #persistent (currentProtocol ()) then
|
adamc@870
|
51 box [string "static void uw_db_validate(uw_context ctx) {",
|
adamc@870
|
52 newline,
|
adamc@870
|
53 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@870
|
54 newline,
|
adamc@870
|
55 string "PGresult *res;",
|
adamc@870
|
56 newline,
|
adamc@870
|
57 newline,
|
adamc@870
|
58 p_list_sep newline
|
adamc@870
|
59 (fn (s, xts) =>
|
adamc@870
|
60 let
|
adamc@870
|
61 val sl = CharVector.map Char.toLower s
|
adamc@870
|
62
|
adamc@870
|
63 val q = "SELECT COUNT(*) FROM pg_class WHERE relname = '"
|
adamc@870
|
64 ^ sl ^ "'"
|
adamc@870
|
65
|
adamc@870
|
66 val q' = String.concat ["SELECT COUNT(*) FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '",
|
adamc@870
|
67 sl,
|
adamc@870
|
68 "') AND (",
|
adamc@870
|
69 String.concatWith " OR "
|
adamc@870
|
70 (map (fn (x, t) =>
|
adamc@870
|
71 String.concat ["(attname = 'uw_",
|
adamc@870
|
72 CharVector.map
|
adamc@870
|
73 Char.toLower (ident x),
|
adamc@870
|
74 "' AND atttypid = (SELECT oid FROM pg_type",
|
adamc@870
|
75 " WHERE typname = '",
|
adamc@870
|
76 p_sql_type_base t,
|
adamc@870
|
77 "') AND attnotnull = ",
|
adamc@870
|
78 if isNotNull t then
|
adamc@870
|
79 "TRUE"
|
adamc@870
|
80 else
|
adamc@870
|
81 "FALSE",
|
adamc@870
|
82 ")"]) xts),
|
adamc@870
|
83 ")"]
|
adamc@870
|
84
|
adamc@870
|
85 val q'' = String.concat ["SELECT COUNT(*) FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '",
|
adamc@870
|
86 sl,
|
adamc@870
|
87 "') AND attname LIKE 'uw_%'"]
|
adamc@870
|
88 in
|
adamc@870
|
89 box [string "res = PQexec(conn, \"",
|
adamc@870
|
90 string q,
|
adamc@870
|
91 string "\");",
|
adamc@870
|
92 newline,
|
adamc@870
|
93 newline,
|
adamc@870
|
94 string "if (res == NULL) {",
|
adamc@870
|
95 newline,
|
adamc@870
|
96 box [string "PQfinish(conn);",
|
adamc@870
|
97 newline,
|
adamc@870
|
98 string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
|
adamc@870
|
99 newline],
|
adamc@870
|
100 string "}",
|
adamc@870
|
101 newline,
|
adamc@870
|
102 newline,
|
adamc@870
|
103 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
|
adamc@870
|
104 newline,
|
adamc@870
|
105 box [string "char msg[1024];",
|
adamc@870
|
106 newline,
|
adamc@870
|
107 string "strncpy(msg, PQerrorMessage(conn), 1024);",
|
adamc@870
|
108 newline,
|
adamc@870
|
109 string "msg[1023] = 0;",
|
adamc@870
|
110 newline,
|
adamc@870
|
111 string "PQclear(res);",
|
adamc@870
|
112 newline,
|
adamc@870
|
113 string "PQfinish(conn);",
|
adamc@870
|
114 newline,
|
adamc@870
|
115 string "uw_error(ctx, FATAL, \"Query failed:\\n",
|
adamc@870
|
116 string q,
|
adamc@870
|
117 string "\\n%s\", msg);",
|
adamc@870
|
118 newline],
|
adamc@870
|
119 string "}",
|
adamc@870
|
120 newline,
|
adamc@870
|
121 newline,
|
adamc@870
|
122 string "if (strcmp(PQgetvalue(res, 0, 0), \"1\")) {",
|
adamc@870
|
123 newline,
|
adamc@870
|
124 box [string "PQclear(res);",
|
adamc@870
|
125 newline,
|
adamc@870
|
126 string "PQfinish(conn);",
|
adamc@870
|
127 newline,
|
adamc@870
|
128 string "uw_error(ctx, FATAL, \"Table '",
|
adamc@870
|
129 string s,
|
adamc@870
|
130 string "' does not exist.\");",
|
adamc@870
|
131 newline],
|
adamc@870
|
132 string "}",
|
adamc@870
|
133 newline,
|
adamc@870
|
134 newline,
|
adamc@870
|
135 string "PQclear(res);",
|
adamc@870
|
136 newline,
|
adamc@870
|
137
|
adamc@870
|
138 string "res = PQexec(conn, \"",
|
adamc@870
|
139 string q',
|
adamc@870
|
140 string "\");",
|
adamc@870
|
141 newline,
|
adamc@870
|
142 newline,
|
adamc@870
|
143 string "if (res == NULL) {",
|
adamc@870
|
144 newline,
|
adamc@870
|
145 box [string "PQfinish(conn);",
|
adamc@870
|
146 newline,
|
adamc@870
|
147 string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
|
adamc@870
|
148 newline],
|
adamc@870
|
149 string "}",
|
adamc@870
|
150 newline,
|
adamc@870
|
151 newline,
|
adamc@870
|
152 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
|
adamc@870
|
153 newline,
|
adamc@870
|
154 box [string "char msg[1024];",
|
adamc@870
|
155 newline,
|
adamc@870
|
156 string "strncpy(msg, PQerrorMessage(conn), 1024);",
|
adamc@870
|
157 newline,
|
adamc@870
|
158 string "msg[1023] = 0;",
|
adamc@870
|
159 newline,
|
adamc@870
|
160 string "PQclear(res);",
|
adamc@870
|
161 newline,
|
adamc@870
|
162 string "PQfinish(conn);",
|
adamc@870
|
163 newline,
|
adamc@870
|
164 string "uw_error(ctx, FATAL, \"Query failed:\\n",
|
adamc@870
|
165 string q',
|
adamc@870
|
166 string "\\n%s\", msg);",
|
adamc@870
|
167 newline],
|
adamc@870
|
168 string "}",
|
adamc@870
|
169 newline,
|
adamc@870
|
170 newline,
|
adamc@870
|
171 string "if (strcmp(PQgetvalue(res, 0, 0), \"",
|
adamc@870
|
172 string (Int.toString (length xts)),
|
adamc@870
|
173 string "\")) {",
|
adamc@870
|
174 newline,
|
adamc@870
|
175 box [string "PQclear(res);",
|
adamc@870
|
176 newline,
|
adamc@870
|
177 string "PQfinish(conn);",
|
adamc@870
|
178 newline,
|
adamc@870
|
179 string "uw_error(ctx, FATAL, \"Table '",
|
adamc@870
|
180 string s,
|
adamc@870
|
181 string "' has the wrong column types.\");",
|
adamc@870
|
182 newline],
|
adamc@870
|
183 string "}",
|
adamc@870
|
184 newline,
|
adamc@870
|
185 newline,
|
adamc@870
|
186 string "PQclear(res);",
|
adamc@870
|
187 newline,
|
adamc@870
|
188 newline,
|
adamc@870
|
189
|
adamc@870
|
190 string "res = PQexec(conn, \"",
|
adamc@870
|
191 string q'',
|
adamc@870
|
192 string "\");",
|
adamc@870
|
193 newline,
|
adamc@870
|
194 newline,
|
adamc@870
|
195 string "if (res == NULL) {",
|
adamc@870
|
196 newline,
|
adamc@870
|
197 box [string "PQfinish(conn);",
|
adamc@870
|
198 newline,
|
adamc@870
|
199 string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
|
adamc@870
|
200 newline],
|
adamc@870
|
201 string "}",
|
adamc@870
|
202 newline,
|
adamc@870
|
203 newline,
|
adamc@870
|
204 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
|
adamc@870
|
205 newline,
|
adamc@870
|
206 box [string "char msg[1024];",
|
adamc@870
|
207 newline,
|
adamc@870
|
208 string "strncpy(msg, PQerrorMessage(conn), 1024);",
|
adamc@870
|
209 newline,
|
adamc@870
|
210 string "msg[1023] = 0;",
|
adamc@870
|
211 newline,
|
adamc@870
|
212 string "PQclear(res);",
|
adamc@870
|
213 newline,
|
adamc@870
|
214 string "PQfinish(conn);",
|
adamc@870
|
215 newline,
|
adamc@870
|
216 string "uw_error(ctx, FATAL, \"Query failed:\\n",
|
adamc@870
|
217 string q'',
|
adamc@870
|
218 string "\\n%s\", msg);",
|
adamc@870
|
219 newline],
|
adamc@870
|
220 string "}",
|
adamc@870
|
221 newline,
|
adamc@870
|
222 newline,
|
adamc@870
|
223 string "if (strcmp(PQgetvalue(res, 0, 0), \"",
|
adamc@870
|
224 string (Int.toString (length xts)),
|
adamc@870
|
225 string "\")) {",
|
adamc@870
|
226 newline,
|
adamc@870
|
227 box [string "PQclear(res);",
|
adamc@870
|
228 newline,
|
adamc@870
|
229 string "PQfinish(conn);",
|
adamc@870
|
230 newline,
|
adamc@870
|
231 string "uw_error(ctx, FATAL, \"Table '",
|
adamc@870
|
232 string s,
|
adamc@870
|
233 string "' has extra columns.\");",
|
adamc@870
|
234 newline],
|
adamc@870
|
235 string "}",
|
adamc@870
|
236 newline,
|
adamc@870
|
237 newline,
|
adamc@870
|
238 string "PQclear(res);",
|
adamc@870
|
239 newline]
|
adamc@870
|
240 end) tables,
|
adamc@870
|
241
|
adamc@870
|
242 p_list_sep newline
|
adamc@870
|
243 (fn s =>
|
adamc@870
|
244 let
|
adamc@870
|
245 val sl = CharVector.map Char.toLower s
|
adamc@870
|
246
|
adamc@870
|
247 val q = "SELECT COUNT(*) FROM pg_class WHERE relname = '"
|
adamc@870
|
248 ^ sl ^ "' AND relkind = 'S'"
|
adamc@870
|
249 in
|
adamc@870
|
250 box [string "res = PQexec(conn, \"",
|
adamc@870
|
251 string q,
|
adamc@870
|
252 string "\");",
|
adamc@870
|
253 newline,
|
adamc@870
|
254 newline,
|
adamc@870
|
255 string "if (res == NULL) {",
|
adamc@870
|
256 newline,
|
adamc@870
|
257 box [string "PQfinish(conn);",
|
adamc@870
|
258 newline,
|
adamc@870
|
259 string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
|
adamc@870
|
260 newline],
|
adamc@870
|
261 string "}",
|
adamc@870
|
262 newline,
|
adamc@870
|
263 newline,
|
adamc@870
|
264 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
|
adamc@870
|
265 newline,
|
adamc@870
|
266 box [string "char msg[1024];",
|
adamc@870
|
267 newline,
|
adamc@870
|
268 string "strncpy(msg, PQerrorMessage(conn), 1024);",
|
adamc@870
|
269 newline,
|
adamc@870
|
270 string "msg[1023] = 0;",
|
adamc@870
|
271 newline,
|
adamc@870
|
272 string "PQclear(res);",
|
adamc@870
|
273 newline,
|
adamc@870
|
274 string "PQfinish(conn);",
|
adamc@870
|
275 newline,
|
adamc@870
|
276 string "uw_error(ctx, FATAL, \"Query failed:\\n",
|
adamc@870
|
277 string q,
|
adamc@870
|
278 string "\\n%s\", msg);",
|
adamc@870
|
279 newline],
|
adamc@870
|
280 string "}",
|
adamc@870
|
281 newline,
|
adamc@870
|
282 newline,
|
adamc@870
|
283 string "if (strcmp(PQgetvalue(res, 0, 0), \"1\")) {",
|
adamc@870
|
284 newline,
|
adamc@870
|
285 box [string "PQclear(res);",
|
adamc@870
|
286 newline,
|
adamc@870
|
287 string "PQfinish(conn);",
|
adamc@870
|
288 newline,
|
adamc@870
|
289 string "uw_error(ctx, FATAL, \"Sequence '",
|
adamc@870
|
290 string s,
|
adamc@870
|
291 string "' does not exist.\");",
|
adamc@870
|
292 newline],
|
adamc@870
|
293 string "}",
|
adamc@870
|
294 newline,
|
adamc@870
|
295 newline,
|
adamc@870
|
296 string "PQclear(res);",
|
adamc@870
|
297 newline]
|
adamc@870
|
298 end) sequences,
|
adamc@870
|
299
|
adamc@870
|
300 string "}",
|
adamc@870
|
301
|
adamc@870
|
302 string "static void uw_db_prepare(uw_context ctx) {",
|
adamc@866
|
303 newline,
|
adamc@866
|
304 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@866
|
305 newline,
|
adamc@866
|
306 string "PGresult *res;",
|
adamc@866
|
307 newline,
|
adamc@866
|
308 newline,
|
adamc@866
|
309
|
adamc@866
|
310 p_list_sepi newline (fn i => fn (s, n) =>
|
adamc@866
|
311 box [string "res = PQprepare(conn, \"uw",
|
adamc@866
|
312 string (Int.toString i),
|
adamc@866
|
313 string "\", \"",
|
adamc@866
|
314 string (String.toString s),
|
adamc@866
|
315 string "\", ",
|
adamc@866
|
316 string (Int.toString n),
|
adamc@866
|
317 string ", NULL);",
|
adamc@866
|
318 newline,
|
adamc@866
|
319 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@866
|
320 newline,
|
adamc@866
|
321 box [string "char msg[1024];",
|
adamc@866
|
322 newline,
|
adamc@866
|
323 string "strncpy(msg, PQerrorMessage(conn), 1024);",
|
adamc@866
|
324 newline,
|
adamc@866
|
325 string "msg[1023] = 0;",
|
adamc@866
|
326 newline,
|
adamc@866
|
327 string "PQclear(res);",
|
adamc@866
|
328 newline,
|
adamc@866
|
329 string "PQfinish(conn);",
|
adamc@866
|
330 newline,
|
adamc@866
|
331 string "uw_error(ctx, FATAL, \"Unable to create prepared statement:\\n",
|
adamc@866
|
332 string (String.toString s),
|
adamc@866
|
333 string "\\n%s\", msg);",
|
adamc@866
|
334 newline],
|
adamc@866
|
335 string "}",
|
adamc@866
|
336 newline,
|
adamc@866
|
337 string "PQclear(res);",
|
adamc@866
|
338 newline])
|
adamc@866
|
339 ss,
|
adamc@866
|
340
|
adamc@866
|
341 string "}",
|
adamc@866
|
342 newline,
|
adamc@866
|
343 newline,
|
adamc@866
|
344
|
adamc@866
|
345 string "void uw_db_close(uw_context ctx) {",
|
adamc@866
|
346 newline,
|
adamc@866
|
347 string "PQfinish(uw_get_db(ctx));",
|
adamc@866
|
348 newline,
|
adamc@866
|
349 string "}",
|
adamc@866
|
350 newline,
|
adamc@866
|
351 newline,
|
adamc@866
|
352
|
adamc@866
|
353 string "int uw_db_begin(uw_context ctx) {",
|
adamc@866
|
354 newline,
|
adamc@866
|
355 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@866
|
356 newline,
|
adamc@866
|
357 string "PGresult *res = PQexec(conn, \"BEGIN ISOLATION LEVEL SERIALIZABLE\");",
|
adamc@866
|
358 newline,
|
adamc@866
|
359 newline,
|
adamc@866
|
360 string "if (res == NULL) return 1;",
|
adamc@866
|
361 newline,
|
adamc@866
|
362 newline,
|
adamc@866
|
363 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@866
|
364 box [string "PQclear(res);",
|
adamc@866
|
365 newline,
|
adamc@866
|
366 string "return 1;",
|
adamc@866
|
367 newline],
|
adamc@866
|
368 string "}",
|
adamc@866
|
369 newline,
|
adamc@866
|
370 string "return 0;",
|
adamc@866
|
371 newline,
|
adamc@866
|
372 string "}",
|
adamc@866
|
373 newline,
|
adamc@866
|
374 newline,
|
adamc@866
|
375
|
adamc@866
|
376 string "int uw_db_commit(uw_context ctx) {",
|
adamc@866
|
377 newline,
|
adamc@866
|
378 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@866
|
379 newline,
|
adamc@866
|
380 string "PGresult *res = PQexec(conn, \"COMMIT\");",
|
adamc@866
|
381 newline,
|
adamc@866
|
382 newline,
|
adamc@866
|
383 string "if (res == NULL) return 1;",
|
adamc@866
|
384 newline,
|
adamc@866
|
385 newline,
|
adamc@866
|
386 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@866
|
387 box [string "PQclear(res);",
|
adamc@866
|
388 newline,
|
adamc@866
|
389 string "return 1;",
|
adamc@866
|
390 newline],
|
adamc@866
|
391 string "}",
|
adamc@866
|
392 newline,
|
adamc@866
|
393 string "return 0;",
|
adamc@866
|
394 newline,
|
adamc@866
|
395 string "}",
|
adamc@866
|
396 newline,
|
adamc@866
|
397 newline,
|
adamc@866
|
398
|
adamc@866
|
399 string "int uw_db_rollback(uw_context ctx) {",
|
adamc@866
|
400 newline,
|
adamc@866
|
401 string "PGconn *conn = uw_get_db(ctx);",
|
adamc@866
|
402 newline,
|
adamc@866
|
403 string "PGresult *res = PQexec(conn, \"ROLLBACK\");",
|
adamc@866
|
404 newline,
|
adamc@866
|
405 newline,
|
adamc@866
|
406 string "if (res == NULL) return 1;",
|
adamc@866
|
407 newline,
|
adamc@866
|
408 newline,
|
adamc@866
|
409 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@866
|
410 box [string "PQclear(res);",
|
adamc@866
|
411 newline,
|
adamc@866
|
412 string "return 1;",
|
adamc@866
|
413 newline],
|
adamc@866
|
414 string "}",
|
adamc@866
|
415 newline,
|
adamc@866
|
416 string "return 0;",
|
adamc@866
|
417 newline,
|
adamc@866
|
418 string "}",
|
adamc@866
|
419 newline,
|
adamc@866
|
420 newline]
|
adamc@866
|
421 else
|
adamc@870
|
422 box [string "static void uw_db_validate(uw_context ctx) { }",
|
adamc@870
|
423 newline,
|
adamc@870
|
424 string "static void uw_db_prepare(uw_context ctx) { }"],
|
adamc@870
|
425
|
adamc@866
|
426 newline,
|
adamc@866
|
427 newline,
|
adamc@866
|
428
|
adamc@866
|
429 string "void uw_db_init(uw_context ctx) {",
|
adamc@866
|
430 newline,
|
adamc@866
|
431 string "PGconn *conn = PQconnectdb(\"",
|
adamc@866
|
432 string (String.toString dbstring),
|
adamc@866
|
433 string "\");",
|
adamc@866
|
434 newline,
|
adamc@866
|
435 string "if (conn == NULL) uw_error(ctx, FATAL, ",
|
adamc@866
|
436 string "\"libpq can't allocate a connection.\");",
|
adamc@866
|
437 newline,
|
adamc@866
|
438 string "if (PQstatus(conn) != CONNECTION_OK) {",
|
adamc@866
|
439 newline,
|
adamc@866
|
440 box [string "char msg[1024];",
|
adamc@866
|
441 newline,
|
adamc@866
|
442 string "strncpy(msg, PQerrorMessage(conn), 1024);",
|
adamc@866
|
443 newline,
|
adamc@866
|
444 string "msg[1023] = 0;",
|
adamc@866
|
445 newline,
|
adamc@866
|
446 string "PQfinish(conn);",
|
adamc@866
|
447 newline,
|
adamc@866
|
448 string "uw_error(ctx, BOUNDED_RETRY, ",
|
adamc@866
|
449 string "\"Connection to Postgres server failed: %s\", msg);"],
|
adamc@866
|
450 newline,
|
adamc@866
|
451 string "}",
|
adamc@866
|
452 newline,
|
adamc@866
|
453 string "uw_set_db(ctx, conn);",
|
adamc@866
|
454 newline,
|
adamc@866
|
455 string "uw_db_validate(ctx);",
|
adamc@866
|
456 newline,
|
adamc@866
|
457 string "uw_db_prepare(ctx);",
|
adamc@866
|
458 newline,
|
adamc@866
|
459 string "}"]
|
adamc@866
|
460
|
adamc@867
|
461 fun p_getcol {wontLeakStrings, col = i, typ = t} =
|
adamc@867
|
462 let
|
adamc@867
|
463 fun p_unsql t e eLen =
|
adamc@867
|
464 case t of
|
adamc@867
|
465 Int => box [string "uw_Basis_stringToInt_error(ctx, ", e, string ")"]
|
adamc@867
|
466 | Float => box [string "uw_Basis_stringToFloat_error(ctx, ", e, string ")"]
|
adamc@867
|
467 | String =>
|
adamc@867
|
468 if wontLeakStrings then
|
adamc@867
|
469 e
|
adamc@867
|
470 else
|
adamc@867
|
471 box [string "uw_strdup(ctx, ", e, string ")"]
|
adamc@867
|
472 | Bool => box [string "uw_Basis_stringToBool_error(ctx, ", e, string ")"]
|
adamc@867
|
473 | Time => box [string "uw_Basis_stringToTime_error(ctx, ", e, string ")"]
|
adamc@867
|
474 | Blob => box [string "uw_Basis_stringToBlob_error(ctx, ",
|
adamc@867
|
475 e,
|
adamc@867
|
476 string ", ",
|
adamc@867
|
477 eLen,
|
adamc@867
|
478 string ")"]
|
adamc@867
|
479 | Channel => box [string "uw_Basis_stringToChannel_error(ctx, ", e, string ")"]
|
adamc@867
|
480 | Client => box [string "uw_Basis_stringToClient_error(ctx, ", e, string ")"]
|
adamc@867
|
481
|
adamc@867
|
482 | Nullable _ => raise Fail "Postgres: Recursive Nullable"
|
adamc@867
|
483
|
adamc@867
|
484 fun getter t =
|
adamc@867
|
485 case t of
|
adamc@867
|
486 Nullable t =>
|
adamc@867
|
487 box [string "(PQgetisnull(res, i, ",
|
adamc@867
|
488 string (Int.toString i),
|
adamc@867
|
489 string ") ? NULL : ",
|
adamc@867
|
490 case t of
|
adamc@867
|
491 String => getter t
|
adamc@867
|
492 | _ => box [string "({",
|
adamc@867
|
493 newline,
|
adamc@870
|
494 string (p_sql_type t),
|
adamc@867
|
495 space,
|
adamc@867
|
496 string "*tmp = uw_malloc(ctx, sizeof(",
|
adamc@870
|
497 string (p_sql_type t),
|
adamc@867
|
498 string "));",
|
adamc@867
|
499 newline,
|
adamc@867
|
500 string "*tmp = ",
|
adamc@867
|
501 getter t,
|
adamc@867
|
502 string ";",
|
adamc@867
|
503 newline,
|
adamc@867
|
504 string "tmp;",
|
adamc@867
|
505 newline,
|
adamc@867
|
506 string "})"],
|
adamc@867
|
507 string ")"]
|
adamc@867
|
508 | _ =>
|
adamc@867
|
509 box [string "(PQgetisnull(res, i, ",
|
adamc@867
|
510 string (Int.toString i),
|
adamc@867
|
511 string ") ? ",
|
adamc@867
|
512 box [string "({",
|
adamc@870
|
513 string (p_sql_type t),
|
adamc@867
|
514 space,
|
adamc@867
|
515 string "tmp;",
|
adamc@867
|
516 newline,
|
adamc@867
|
517 string "uw_error(ctx, FATAL, \"Unexpectedly NULL field #",
|
adamc@867
|
518 string (Int.toString i),
|
adamc@867
|
519 string "\");",
|
adamc@867
|
520 newline,
|
adamc@867
|
521 string "tmp;",
|
adamc@867
|
522 newline,
|
adamc@867
|
523 string "})"],
|
adamc@867
|
524 string " : ",
|
adamc@867
|
525 p_unsql t
|
adamc@867
|
526 (box [string "PQgetvalue(res, i, ",
|
adamc@867
|
527 string (Int.toString i),
|
adamc@867
|
528 string ")"])
|
adamc@867
|
529 (box [string "PQgetlength(res, i, ",
|
adamc@867
|
530 string (Int.toString i),
|
adamc@867
|
531 string ")"]),
|
adamc@867
|
532 string ")"]
|
adamc@867
|
533 in
|
adamc@867
|
534 getter t
|
adamc@867
|
535 end
|
adamc@867
|
536
|
adamc@867
|
537 fun queryCommon {loc, query, numCols, doCols} =
|
adamc@867
|
538 box [string "int n, i;",
|
adamc@867
|
539 newline,
|
adamc@867
|
540 newline,
|
adamc@867
|
541
|
adamc@867
|
542 string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
|
adamc@867
|
543 newline,
|
adamc@867
|
544 newline,
|
adamc@867
|
545
|
adamc@867
|
546 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
|
adamc@867
|
547 newline,
|
adamc@867
|
548 box [string "PQclear(res);",
|
adamc@867
|
549 newline,
|
adamc@867
|
550 string "uw_error(ctx, FATAL, \"",
|
adamc@867
|
551 string (ErrorMsg.spanToString loc),
|
adamc@867
|
552 string ": Query failed:\\n%s\\n%s\", ",
|
adamc@867
|
553 query,
|
adamc@867
|
554 string ", PQerrorMessage(conn));",
|
adamc@867
|
555 newline],
|
adamc@867
|
556 string "}",
|
adamc@867
|
557 newline,
|
adamc@867
|
558 newline,
|
adamc@867
|
559
|
adamc@867
|
560 string "if (PQnfields(res) != ",
|
adamc@867
|
561 string (Int.toString numCols),
|
adamc@867
|
562 string ") {",
|
adamc@867
|
563 newline,
|
adamc@867
|
564 box [string "int nf = PQnfields(res);",
|
adamc@867
|
565 newline,
|
adamc@867
|
566 string "PQclear(res);",
|
adamc@867
|
567 newline,
|
adamc@867
|
568 string "uw_error(ctx, FATAL, \"",
|
adamc@867
|
569 string (ErrorMsg.spanToString loc),
|
adamc@867
|
570 string ": Query returned %d columns instead of ",
|
adamc@867
|
571 string (Int.toString numCols),
|
adamc@867
|
572 string ":\\n%s\\n%s\", nf, ",
|
adamc@867
|
573 query,
|
adamc@867
|
574 string ", PQerrorMessage(conn));",
|
adamc@867
|
575 newline],
|
adamc@867
|
576 string "}",
|
adamc@867
|
577 newline,
|
adamc@867
|
578 newline,
|
adamc@867
|
579
|
adamc@867
|
580 string "uw_end_region(ctx);",
|
adamc@867
|
581 newline,
|
adamc@867
|
582 string "uw_push_cleanup(ctx, (void (*)(void *))PQclear, res);",
|
adamc@867
|
583 newline,
|
adamc@867
|
584 string "n = PQntuples(res);",
|
adamc@867
|
585 newline,
|
adamc@867
|
586 string "for (i = 0; i < n; ++i) {",
|
adamc@867
|
587 newline,
|
adamc@867
|
588 doCols p_getcol,
|
adamc@867
|
589 string "}",
|
adamc@867
|
590 newline,
|
adamc@867
|
591 newline,
|
adamc@867
|
592 string "uw_pop_cleanup(ctx);",
|
adamc@867
|
593 newline]
|
adamc@867
|
594
|
adamc@867
|
595 fun query {loc, numCols, doCols} =
|
adamc@867
|
596 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@867
|
597 newline,
|
adamc@867
|
598 string "PGresult *res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);",
|
adamc@867
|
599 newline,
|
adamc@867
|
600 newline,
|
adamc@867
|
601 queryCommon {loc = loc, numCols = numCols, doCols = doCols, query = string "query"}]
|
adamc@867
|
602
|
adamc@867
|
603 fun p_ensql t e =
|
adamc@867
|
604 case t of
|
adamc@867
|
605 Int => box [string "uw_Basis_attrifyInt(ctx, ", e, string ")"]
|
adamc@867
|
606 | Float => box [string "uw_Basis_attrifyFloat(ctx, ", e, string ")"]
|
adamc@867
|
607 | String => e
|
adamc@867
|
608 | Bool => box [string "(", e, string " ? \"TRUE\" : \"FALSE\")"]
|
adamc@867
|
609 | Time => box [string "uw_Basis_attrifyTime(ctx, ", e, string ")"]
|
adamc@867
|
610 | Blob => box [e, string ".data"]
|
adamc@867
|
611 | Channel => box [string "uw_Basis_attrifyChannel(ctx, ", e, string ")"]
|
adamc@867
|
612 | Client => box [string "uw_Basis_attrifyClient(ctx, ", e, string ")"]
|
adamc@867
|
613 | Nullable String => e
|
adamc@867
|
614 | Nullable t => box [string "(",
|
adamc@867
|
615 e,
|
adamc@867
|
616 string " == NULL ? NULL : ",
|
adamc@867
|
617 p_ensql t (box [string "(*", e, string ")"]),
|
adamc@867
|
618 string ")"]
|
adamc@867
|
619
|
adamc@867
|
620 fun queryPrepared {loc, id, query, inputs, numCols, doCols} =
|
adamc@867
|
621 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@867
|
622 newline,
|
adamc@867
|
623 string "const int paramFormats[] = { ",
|
adamc@867
|
624 p_list_sep (box [string ",", space])
|
adamc@867
|
625 (fn t => if isBlob t then string "1" else string "0") inputs,
|
adamc@867
|
626 string " };",
|
adamc@867
|
627 newline,
|
adamc@867
|
628 string "const int paramLengths[] = { ",
|
adamc@867
|
629 p_list_sepi (box [string ",", space])
|
adamc@867
|
630 (fn i => fn Blob => string ("arg" ^ Int.toString (i + 1) ^ ".size")
|
adamc@867
|
631 | Nullable Blob => string ("arg" ^ Int.toString (i + 1)
|
adamc@867
|
632 ^ "?arg" ^ Int.toString (i + 1) ^ "->size:0")
|
adamc@867
|
633 | _ => string "0") inputs,
|
adamc@867
|
634 string " };",
|
adamc@867
|
635 newline,
|
adamc@867
|
636 string "const char *paramValues[] = { ",
|
adamc@867
|
637 p_list_sepi (box [string ",", space])
|
adamc@867
|
638 (fn i => fn t => p_ensql t (box [string "arg",
|
adamc@867
|
639 string (Int.toString (i + 1))]))
|
adamc@867
|
640 inputs,
|
adamc@867
|
641 string " };",
|
adamc@867
|
642 newline,
|
adamc@867
|
643 newline,
|
adamc@867
|
644 string "PGresult *res = ",
|
adamc@867
|
645 if #persistent (Settings.currentProtocol ()) then
|
adamc@867
|
646 box [string "PQexecPrepared(conn, \"uw",
|
adamc@867
|
647 string (Int.toString id),
|
adamc@867
|
648 string "\", ",
|
adamc@867
|
649 string (Int.toString (length inputs)),
|
adamc@867
|
650 string ", paramValues, paramLengths, paramFormats, 0);"]
|
adamc@867
|
651 else
|
adamc@867
|
652 box [string "PQexecParams(conn, \"",
|
adamc@867
|
653 string (String.toString query),
|
adamc@867
|
654 string "\", ",
|
adamc@867
|
655 string (Int.toString (length inputs)),
|
adamc@867
|
656 string ", NULL, paramValues, paramLengths, paramFormats, 0);"],
|
adamc@867
|
657 newline,
|
adamc@867
|
658 newline,
|
adamc@867
|
659 queryCommon {loc = loc, numCols = numCols, doCols = doCols, query = box [string "\"",
|
adamc@867
|
660 string (String.toString query),
|
adamc@867
|
661 string "\""]}]
|
adamc@867
|
662
|
adamc@868
|
663 fun dmlCommon {loc, dml} =
|
adamc@868
|
664 box [string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating DML result.\");",
|
adamc@868
|
665 newline,
|
adamc@868
|
666 newline,
|
adamc@868
|
667
|
adamc@868
|
668 string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
|
adamc@868
|
669 newline,
|
adamc@868
|
670 box [string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {",
|
adamc@868
|
671 box [newline,
|
adamc@868
|
672 string "PQclear(res);",
|
adamc@868
|
673 newline,
|
adamc@868
|
674 string "uw_error(ctx, UNLIMITED_RETRY, \"Serialization failure\");",
|
adamc@868
|
675 newline],
|
adamc@868
|
676 string "}",
|
adamc@868
|
677 newline,
|
adamc@868
|
678 string "PQclear(res);",
|
adamc@868
|
679 newline,
|
adamc@868
|
680 string "uw_error(ctx, FATAL, \"",
|
adamc@868
|
681 string (ErrorMsg.spanToString loc),
|
adamc@868
|
682 string ": DML failed:\\n%s\\n%s\", ",
|
adamc@868
|
683 dml,
|
adamc@868
|
684 string ", PQerrorMessage(conn));",
|
adamc@868
|
685 newline],
|
adamc@868
|
686 string "}",
|
adamc@868
|
687 newline,
|
adamc@868
|
688 newline,
|
adamc@868
|
689
|
adamc@868
|
690 string "PQclear(res);",
|
adamc@868
|
691 newline]
|
adamc@868
|
692
|
adamc@868
|
693 fun dml loc =
|
adamc@868
|
694 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@868
|
695 newline,
|
adamc@868
|
696 string "PGresult *res = PQexecParams(conn, dml, 0, NULL, NULL, NULL, NULL, 0);",
|
adamc@868
|
697 newline,
|
adamc@868
|
698 newline,
|
adamc@868
|
699 dmlCommon {loc = loc, dml = string "dml"}]
|
adamc@868
|
700
|
adamc@868
|
701 fun dmlPrepared {loc, id, dml, inputs} =
|
adamc@868
|
702 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@868
|
703 newline,
|
adamc@868
|
704 string "const int paramFormats[] = { ",
|
adamc@868
|
705 p_list_sep (box [string ",", space])
|
adamc@868
|
706 (fn t => if isBlob t then string "1" else string "0") inputs,
|
adamc@868
|
707 string " };",
|
adamc@868
|
708 newline,
|
adamc@868
|
709 string "const int paramLengths[] = { ",
|
adamc@868
|
710 p_list_sepi (box [string ",", space])
|
adamc@868
|
711 (fn i => fn Blob => string ("arg" ^ Int.toString (i + 1) ^ ".size")
|
adamc@868
|
712 | Nullable Blob => string ("arg" ^ Int.toString (i + 1)
|
adamc@868
|
713 ^ "?arg" ^ Int.toString (i + 1) ^ "->size:0")
|
adamc@868
|
714 | _ => string "0") inputs,
|
adamc@868
|
715 string " };",
|
adamc@868
|
716 newline,
|
adamc@868
|
717 string "const char *paramValues[] = { ",
|
adamc@868
|
718 p_list_sepi (box [string ",", space])
|
adamc@868
|
719 (fn i => fn t => p_ensql t (box [string "arg",
|
adamc@868
|
720 string (Int.toString (i + 1))]))
|
adamc@868
|
721 inputs,
|
adamc@868
|
722 string " };",
|
adamc@868
|
723 newline,
|
adamc@868
|
724 newline,
|
adamc@868
|
725 string "PGresult *res = ",
|
adamc@868
|
726 if #persistent (Settings.currentProtocol ()) then
|
adamc@868
|
727 box [string "PQexecPrepared(conn, \"uw",
|
adamc@868
|
728 string (Int.toString id),
|
adamc@868
|
729 string "\", ",
|
adamc@868
|
730 string (Int.toString (length inputs)),
|
adamc@868
|
731 string ", paramValues, paramLengths, paramFormats, 0);"]
|
adamc@868
|
732 else
|
adamc@868
|
733 box [string "PQexecParams(conn, \"",
|
adamc@868
|
734 string (String.toString dml),
|
adamc@868
|
735 string "\", ",
|
adamc@868
|
736 string (Int.toString (length inputs)),
|
adamc@868
|
737 string ", NULL, paramValues, paramLengths, paramFormats, 0);"],
|
adamc@868
|
738 newline,
|
adamc@868
|
739 newline,
|
adamc@868
|
740 dmlCommon {loc = loc, dml = box [string "\"",
|
adamc@868
|
741 string (String.toString dml),
|
adamc@868
|
742 string "\""]}]
|
adamc@868
|
743
|
adamc@869
|
744 fun nextvalCommon {loc, query} =
|
adamc@869
|
745 box [string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating nextval result.\");",
|
adamc@869
|
746 newline,
|
adamc@869
|
747 newline,
|
adamc@869
|
748
|
adamc@869
|
749 string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
|
adamc@869
|
750 newline,
|
adamc@869
|
751 box [string "PQclear(res);",
|
adamc@869
|
752 newline,
|
adamc@869
|
753 string "uw_error(ctx, FATAL, \"",
|
adamc@869
|
754 string (ErrorMsg.spanToString loc),
|
adamc@869
|
755 string ": Query failed:\\n%s\\n%s\", ",
|
adamc@869
|
756 query,
|
adamc@869
|
757 string ", PQerrorMessage(conn));",
|
adamc@869
|
758 newline],
|
adamc@869
|
759 string "}",
|
adamc@869
|
760 newline,
|
adamc@869
|
761 newline,
|
adamc@869
|
762
|
adamc@869
|
763 string "uw_end_region(ctx);",
|
adamc@869
|
764 newline,
|
adamc@869
|
765 string "n = PQntuples(res);",
|
adamc@869
|
766 newline,
|
adamc@869
|
767 string "if (n != 1) {",
|
adamc@869
|
768 newline,
|
adamc@869
|
769 box [string "PQclear(res);",
|
adamc@869
|
770 newline,
|
adamc@869
|
771 string "uw_error(ctx, FATAL, \"",
|
adamc@869
|
772 string (ErrorMsg.spanToString loc),
|
adamc@869
|
773 string ": Wrong number of result rows:\\n%s\\n%s\", ",
|
adamc@869
|
774 query,
|
adamc@869
|
775 string ", PQerrorMessage(conn));",
|
adamc@869
|
776 newline],
|
adamc@869
|
777 string "}",
|
adamc@869
|
778 newline,
|
adamc@869
|
779 newline,
|
adamc@869
|
780
|
adamc@869
|
781 string "n = uw_Basis_stringToInt_error(ctx, PQgetvalue(res, 0, 0));",
|
adamc@869
|
782 newline,
|
adamc@869
|
783 string "PQclear(res);",
|
adamc@869
|
784 newline]
|
adamc@869
|
785
|
adamc@869
|
786 fun nextval loc =
|
adamc@869
|
787 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@869
|
788 newline,
|
adamc@869
|
789 string "PGresult *res = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);",
|
adamc@869
|
790 newline,
|
adamc@869
|
791 newline,
|
adamc@869
|
792 nextvalCommon {loc = loc, query = string "query"}]
|
adamc@869
|
793
|
adamc@869
|
794 fun nextvalPrepared {loc, id, query} =
|
adamc@869
|
795 box [string "PGconn *conn = uw_get_db(ctx);",
|
adamc@869
|
796 newline,
|
adamc@869
|
797 newline,
|
adamc@869
|
798 string "PGresult *res = ",
|
adamc@869
|
799 if #persistent (Settings.currentProtocol ()) then
|
adamc@869
|
800 box [string "PQexecPrepared(conn, \"uw",
|
adamc@869
|
801 string (Int.toString id),
|
adamc@869
|
802 string "\", 0, NULL, NULL, NULL, 0);"]
|
adamc@869
|
803 else
|
adamc@869
|
804 box [string "PQexecParams(conn, \"",
|
adamc@869
|
805 string (String.toString query),
|
adamc@869
|
806 string "\", 0, NULL, NULL, NULL, NULL, 0);"],
|
adamc@869
|
807 newline,
|
adamc@869
|
808 newline,
|
adamc@869
|
809 nextvalCommon {loc = loc, query = box [string "\"",
|
adamc@869
|
810 string (String.toString query),
|
adamc@869
|
811 string "\""]}]
|
adamc@869
|
812
|
adamc@866
|
813 val () = addDbms {name = "postgres",
|
adamc@866
|
814 header = "postgresql/libpq-fe.h",
|
adamc@866
|
815 link = "-lpq",
|
adamc@866
|
816 global_init = box [string "void uw_client_init() { }",
|
adamc@866
|
817 newline],
|
adamc@867
|
818 init = init,
|
adamc@867
|
819 query = query,
|
adamc@868
|
820 queryPrepared = queryPrepared,
|
adamc@868
|
821 dml = dml,
|
adamc@869
|
822 dmlPrepared = dmlPrepared,
|
adamc@869
|
823 nextval = nextval,
|
adamc@869
|
824 nextvalPrepared = nextvalPrepared}
|
adamc@866
|
825 val () = setDbms "postgres"
|
adamc@866
|
826
|
adamc@866
|
827 end
|