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 MySQL :> MYSQL = struct
|
adamc@866
|
29
|
adamc@866
|
30 open Settings
|
adamc@866
|
31 open Print.PD
|
adamc@866
|
32 open Print
|
adamc@866
|
33
|
adamc@873
|
34 fun p_sql_type t =
|
adamc@873
|
35 case t of
|
adamc@873
|
36 Int => "bigint"
|
adamc@873
|
37 | Float => "double"
|
adamc@873
|
38 | String => "longtext"
|
adamc@873
|
39 | Bool => "bool"
|
adamc@873
|
40 | Time => "timestamp"
|
adamc@873
|
41 | Blob => "longblob"
|
adamc@873
|
42 | Channel => "bigint"
|
adamc@873
|
43 | Client => "int"
|
adamc@873
|
44 | Nullable t => p_sql_type t
|
adamc@873
|
45
|
adamc@873
|
46 fun p_buffer_type t =
|
adamc@873
|
47 case t of
|
adamc@873
|
48 Int => "MYSQL_TYPE_LONGLONG"
|
adamc@873
|
49 | Float => "MYSQL_TYPE_DOUBLE"
|
adamc@873
|
50 | String => "MYSQL_TYPE_STRING"
|
adamc@873
|
51 | Bool => "MYSQL_TYPE_LONG"
|
adamc@873
|
52 | Time => "MYSQL_TYPE_TIME"
|
adamc@873
|
53 | Blob => "MYSQL_TYPE_BLOB"
|
adamc@873
|
54 | Channel => "MYSQL_TYPE_LONGLONG"
|
adamc@873
|
55 | Client => "MYSQL_TYPE_LONG"
|
adamc@873
|
56 | Nullable t => p_buffer_type t
|
adamc@873
|
57
|
adamc@872
|
58 fun init {dbstring, prepared = ss, tables, views, sequences} =
|
adamc@866
|
59 let
|
adamc@866
|
60 val host = ref NONE
|
adamc@866
|
61 val user = ref NONE
|
adamc@866
|
62 val passwd = ref NONE
|
adamc@866
|
63 val db = ref NONE
|
adamc@866
|
64 val port = ref NONE
|
adamc@866
|
65 val unix_socket = ref NONE
|
adamc@866
|
66
|
adamc@866
|
67 fun stringOf r = case !r of
|
adamc@866
|
68 NONE => string "NULL"
|
adamc@866
|
69 | SOME s => box [string "\"",
|
adamc@866
|
70 string (String.toString s),
|
adamc@866
|
71 string "\""]
|
adamc@866
|
72 in
|
adamc@866
|
73 app (fn s =>
|
adamc@866
|
74 case String.fields (fn ch => ch = #"=") s of
|
adamc@866
|
75 [name, value] =>
|
adamc@866
|
76 (case name of
|
adamc@866
|
77 "host" =>
|
adamc@866
|
78 if size value > 0 andalso String.sub (value, 0) = #"/" then
|
adamc@866
|
79 unix_socket := SOME value
|
adamc@866
|
80 else
|
adamc@866
|
81 host := SOME value
|
adamc@866
|
82 | "hostaddr" => host := SOME value
|
adamc@866
|
83 | "port" => port := Int.fromString value
|
adamc@866
|
84 | "dbname" => db := SOME value
|
adamc@866
|
85 | "user" => user := SOME value
|
adamc@866
|
86 | "password" => passwd := SOME value
|
adamc@866
|
87 | _ => ())
|
adamc@866
|
88 | _ => ()) (String.tokens Char.isSpace dbstring);
|
adamc@866
|
89
|
adamc@866
|
90 box [string "typedef struct {",
|
adamc@866
|
91 newline,
|
adamc@866
|
92 box [string "MYSQL *conn;",
|
adamc@866
|
93 newline,
|
adamc@866
|
94 p_list_sepi (box [])
|
adamc@866
|
95 (fn i => fn _ =>
|
adamc@866
|
96 box [string "MYSQL_STMT *p",
|
adamc@866
|
97 string (Int.toString i),
|
adamc@866
|
98 string ";",
|
adamc@866
|
99 newline])
|
adamc@866
|
100 ss],
|
adamc@866
|
101 string "} uw_conn;",
|
adamc@866
|
102 newline,
|
adamc@866
|
103 newline,
|
adamc@866
|
104
|
adamc@866
|
105 if #persistent (currentProtocol ()) then
|
adamc@866
|
106 box [string "static void uw_db_prepare(uw_context ctx) {",
|
adamc@866
|
107 newline,
|
adamc@866
|
108 string "uw_conn *conn = uw_get_db(ctx);",
|
adamc@866
|
109 newline,
|
adamc@866
|
110 string "MYSQL_STMT *stmt;",
|
adamc@866
|
111 newline,
|
adamc@866
|
112 newline,
|
adamc@866
|
113
|
adamc@866
|
114 p_list_sepi newline (fn i => fn (s, n) =>
|
adamc@866
|
115 let
|
adamc@866
|
116 fun uhoh this s args =
|
adamc@866
|
117 box [p_list_sepi (box [])
|
adamc@866
|
118 (fn j => fn () =>
|
adamc@866
|
119 box [string
|
adamc@866
|
120 "mysql_stmt_close(conn->p",
|
adamc@866
|
121 string (Int.toString j),
|
adamc@866
|
122 string ");",
|
adamc@866
|
123 newline])
|
adamc@866
|
124 (List.tabulate (i, fn _ => ())),
|
adamc@866
|
125 box (if this then
|
adamc@866
|
126 [string
|
adamc@866
|
127 "mysql_stmt_close(conn->p",
|
adamc@866
|
128 string (Int.toString i),
|
adamc@866
|
129 string ");",
|
adamc@866
|
130 newline]
|
adamc@866
|
131 else
|
adamc@866
|
132 []),
|
adamc@866
|
133 string "mysql_close(conn->conn);",
|
adamc@866
|
134 newline,
|
adamc@866
|
135 string "uw_error(ctx, FATAL, \"",
|
adamc@866
|
136 string s,
|
adamc@866
|
137 string "\"",
|
adamc@866
|
138 p_list_sep (box []) (fn s => box [string ", ",
|
adamc@866
|
139 string s]) args,
|
adamc@866
|
140 string ");",
|
adamc@866
|
141 newline]
|
adamc@866
|
142 in
|
adamc@866
|
143 box [string "stmt = mysql_stmt_init(conn->conn);",
|
adamc@866
|
144 newline,
|
adamc@866
|
145 string "if (stmt == NULL) {",
|
adamc@866
|
146 newline,
|
adamc@866
|
147 uhoh false "Out of memory allocating prepared statement" [],
|
adamc@866
|
148 string "}",
|
adamc@866
|
149 newline,
|
adamc@866
|
150
|
adamc@866
|
151 string "if (mysql_stmt_prepare(stmt, \"",
|
adamc@866
|
152 string (String.toString s),
|
adamc@866
|
153 string "\", ",
|
adamc@866
|
154 string (Int.toString (size s)),
|
adamc@866
|
155 string ")) {",
|
adamc@866
|
156 newline,
|
adamc@866
|
157 box [string "char msg[1024];",
|
adamc@866
|
158 newline,
|
adamc@866
|
159 string "strncpy(msg, mysql_stmt_error(stmt), 1024);",
|
adamc@866
|
160 newline,
|
adamc@866
|
161 string "msg[1023] = 0;",
|
adamc@866
|
162 newline,
|
adamc@866
|
163 uhoh true "Error preparing statement: %s" ["msg"]],
|
adamc@866
|
164 string "}",
|
adamc@873
|
165 newline,
|
adamc@873
|
166 string "conn->p",
|
adamc@873
|
167 string (Int.toString i),
|
adamc@873
|
168 string " = stmt;",
|
adamc@866
|
169 newline]
|
adamc@866
|
170 end)
|
adamc@866
|
171 ss,
|
adamc@866
|
172
|
adamc@866
|
173 string "}"]
|
adamc@866
|
174 else
|
adamc@866
|
175 string "static void uw_db_prepare(uw_context ctx) { }",
|
adamc@866
|
176 newline,
|
adamc@866
|
177 newline,
|
adamc@866
|
178
|
adamc@866
|
179 string "void uw_db_init(uw_context ctx) {",
|
adamc@866
|
180 newline,
|
adamc@866
|
181 string "MYSQL *mysql = mysql_init(NULL);",
|
adamc@866
|
182 newline,
|
adamc@866
|
183 string "uw_conn *conn;",
|
adamc@866
|
184 newline,
|
adamc@866
|
185 string "if (mysql == NULL) uw_error(ctx, FATAL, ",
|
adamc@866
|
186 string "\"libmysqlclient can't allocate a connection.\");",
|
adamc@866
|
187 newline,
|
adamc@866
|
188 string "if (mysql_real_connect(mysql, ",
|
adamc@866
|
189 stringOf host,
|
adamc@866
|
190 string ", ",
|
adamc@866
|
191 stringOf user,
|
adamc@866
|
192 string ", ",
|
adamc@866
|
193 stringOf passwd,
|
adamc@866
|
194 string ", ",
|
adamc@866
|
195 stringOf db,
|
adamc@866
|
196 string ", ",
|
adamc@866
|
197 case !port of
|
adamc@866
|
198 NONE => string "0"
|
adamc@866
|
199 | SOME n => string (Int.toString n),
|
adamc@866
|
200 string ", ",
|
adamc@866
|
201 stringOf unix_socket,
|
adamc@866
|
202 string ", 0)) {",
|
adamc@866
|
203 newline,
|
adamc@866
|
204 box [string "char msg[1024];",
|
adamc@866
|
205 newline,
|
adamc@866
|
206 string "strncpy(msg, mysql_error(mysql), 1024);",
|
adamc@866
|
207 newline,
|
adamc@866
|
208 string "msg[1023] = 0;",
|
adamc@866
|
209 newline,
|
adamc@866
|
210 string "mysql_close(mysql);",
|
adamc@866
|
211 newline,
|
adamc@866
|
212 string "uw_error(ctx, BOUNDED_RETRY, ",
|
adamc@866
|
213 string "\"Connection to MySQL server failed: %s\", msg);"],
|
adamc@866
|
214 newline,
|
adamc@866
|
215 string "}",
|
adamc@866
|
216 newline,
|
adamc@867
|
217 string "conn = calloc(1, sizeof(conn));",
|
adamc@866
|
218 newline,
|
adamc@866
|
219 string "conn->conn = mysql;",
|
adamc@866
|
220 newline,
|
adamc@866
|
221 string "uw_set_db(ctx, conn);",
|
adamc@866
|
222 newline,
|
adamc@866
|
223 string "uw_db_validate(ctx);",
|
adamc@866
|
224 newline,
|
adamc@866
|
225 string "uw_db_prepare(ctx);",
|
adamc@866
|
226 newline,
|
adamc@866
|
227 string "}",
|
adamc@866
|
228 newline,
|
adamc@866
|
229 newline,
|
adamc@866
|
230
|
adamc@866
|
231 string "void uw_db_close(uw_context ctx) {",
|
adamc@866
|
232 newline,
|
adamc@866
|
233 string "uw_conn *conn = uw_get_db(ctx);",
|
adamc@866
|
234 newline,
|
adamc@866
|
235 p_list_sepi (box [])
|
adamc@866
|
236 (fn i => fn _ =>
|
adamc@866
|
237 box [string "if (conn->p",
|
adamc@866
|
238 string (Int.toString i),
|
adamc@866
|
239 string ") mysql_stmt_close(conn->p",
|
adamc@866
|
240 string (Int.toString i),
|
adamc@866
|
241 string ");",
|
adamc@866
|
242 newline])
|
adamc@866
|
243 ss,
|
adamc@866
|
244 string "mysql_close(conn->conn);",
|
adamc@866
|
245 newline,
|
adamc@866
|
246 string "}",
|
adamc@866
|
247 newline,
|
adamc@866
|
248 newline,
|
adamc@866
|
249
|
adamc@866
|
250 string "int uw_db_begin(uw_context ctx) {",
|
adamc@866
|
251 newline,
|
adamc@866
|
252 string "uw_conn *conn = uw_get_db(ctx);",
|
adamc@866
|
253 newline,
|
adamc@866
|
254 newline,
|
adamc@866
|
255 string "return mysql_query(conn->conn, \"SET TRANSACTION ISOLATION LEVEL SERIALIZABLE\")",
|
adamc@866
|
256 newline,
|
adamc@866
|
257 string " || mysql_query(conn->conn, \"BEGIN\");",
|
adamc@866
|
258 newline,
|
adamc@866
|
259 string "}",
|
adamc@866
|
260 newline,
|
adamc@866
|
261 newline,
|
adamc@866
|
262
|
adamc@866
|
263 string "int uw_db_commit(uw_context ctx) {",
|
adamc@866
|
264 newline,
|
adamc@866
|
265 string "uw_conn *conn = uw_get_db(ctx);",
|
adamc@866
|
266 newline,
|
adamc@866
|
267 string "return mysql_commit(conn->conn);",
|
adamc@866
|
268 newline,
|
adamc@866
|
269 string "}",
|
adamc@866
|
270 newline,
|
adamc@866
|
271 newline,
|
adamc@866
|
272
|
adamc@866
|
273 string "int uw_db_rollback(uw_context ctx) {",
|
adamc@866
|
274 newline,
|
adamc@866
|
275 string "uw_conn *conn = uw_get_db(ctx);",
|
adamc@866
|
276 newline,
|
adamc@866
|
277 string "return mysql_rollback(conn->conn);",
|
adamc@866
|
278 newline,
|
adamc@866
|
279 string "}",
|
adamc@866
|
280 newline,
|
adamc@866
|
281 newline]
|
adamc@866
|
282 end
|
adamc@866
|
283
|
adamc@873
|
284 fun p_getcol {wontLeakStrings = _, col = i, typ = t} =
|
adamc@873
|
285 let
|
adamc@873
|
286 fun getter t =
|
adamc@873
|
287 case t of
|
adamc@873
|
288 String => box [string "({",
|
adamc@873
|
289 newline,
|
adamc@873
|
290 string "uw_Basis_string s = uw_malloc(ctx, length",
|
adamc@873
|
291 string (Int.toString i),
|
adamc@873
|
292 string " + 1);",
|
adamc@873
|
293 newline,
|
adamc@873
|
294 string "out[",
|
adamc@873
|
295 string (Int.toString i),
|
adamc@873
|
296 string "].buffer = s;",
|
adamc@873
|
297 newline,
|
adamc@873
|
298 string "out[",
|
adamc@873
|
299 string (Int.toString i),
|
adamc@873
|
300 string "].buffer_length = length",
|
adamc@873
|
301 string (Int.toString i),
|
adamc@873
|
302 string " + 1;",
|
adamc@873
|
303 newline,
|
adamc@873
|
304 string "mysql_stmt_fetch_column(stmt, &out[",
|
adamc@873
|
305 string (Int.toString i),
|
adamc@873
|
306 string "], ",
|
adamc@873
|
307 string (Int.toString i),
|
adamc@873
|
308 string ", 0);",
|
adamc@873
|
309 newline,
|
adamc@873
|
310 string "s[length",
|
adamc@873
|
311 string (Int.toString i),
|
adamc@873
|
312 string "] = 0;",
|
adamc@873
|
313 newline,
|
adamc@873
|
314 string "s;",
|
adamc@873
|
315 newline,
|
adamc@873
|
316 string "})"]
|
adamc@873
|
317 | Blob => box [string "({",
|
adamc@873
|
318 newline,
|
adamc@873
|
319 string "uw_Basis_blob b = {length",
|
adamc@873
|
320 string (Int.toString i),
|
adamc@873
|
321 string ", uw_malloc(ctx, length",
|
adamc@873
|
322 string (Int.toString i),
|
adamc@873
|
323 string ")};",
|
adamc@873
|
324 newline,
|
adamc@873
|
325 string "out[",
|
adamc@873
|
326 string (Int.toString i),
|
adamc@873
|
327 string "].buffer = b.data;",
|
adamc@873
|
328 newline,
|
adamc@873
|
329 string "out[",
|
adamc@873
|
330 string (Int.toString i),
|
adamc@873
|
331 string "].buffer_length = length",
|
adamc@873
|
332 string (Int.toString i),
|
adamc@873
|
333 string ";",
|
adamc@873
|
334 newline,
|
adamc@873
|
335 string "mysql_stmt_fetch_column(stmt, &out[",
|
adamc@873
|
336 string (Int.toString i),
|
adamc@873
|
337 string "], ",
|
adamc@873
|
338 string (Int.toString i),
|
adamc@873
|
339 string ", 0);",
|
adamc@873
|
340 newline,
|
adamc@873
|
341 string "b;",
|
adamc@873
|
342 newline,
|
adamc@873
|
343 string "})"]
|
adamc@873
|
344 | Time => box [string "({",
|
adamc@873
|
345 string "MYSQL_TIME *mt = buffer",
|
adamc@873
|
346 string (Int.toString i),
|
adamc@873
|
347 string ";",
|
adamc@873
|
348 newline,
|
adamc@873
|
349 newline,
|
adamc@873
|
350 string "struct tm t = {mt->second, mt->minute, mt->hour, mt->day, mt->month, mt->year, 0, 0, -1};",
|
adamc@873
|
351 newline,
|
adamc@873
|
352 string "mktime(&tm);",
|
adamc@873
|
353 newline,
|
adamc@873
|
354 string "})"]
|
adamc@873
|
355 | _ => box [string "buffer",
|
adamc@873
|
356 string (Int.toString i)]
|
adamc@873
|
357 in
|
adamc@873
|
358 case t of
|
adamc@873
|
359 Nullable t => box [string "(is_null",
|
adamc@873
|
360 string (Int.toString i),
|
adamc@873
|
361 string " ? NULL : ",
|
adamc@873
|
362 case t of
|
adamc@873
|
363 String => getter t
|
adamc@873
|
364 | _ => box [string "({",
|
adamc@873
|
365 newline,
|
adamc@873
|
366 string (p_sql_ctype t),
|
adamc@873
|
367 space,
|
adamc@873
|
368 string "*tmp = uw_malloc(ctx, sizeof(",
|
adamc@873
|
369 string (p_sql_ctype t),
|
adamc@873
|
370 string "));",
|
adamc@873
|
371 newline,
|
adamc@873
|
372 string "*tmp = ",
|
adamc@873
|
373 getter t,
|
adamc@873
|
374 string ";",
|
adamc@873
|
375 newline,
|
adamc@873
|
376 string "tmp;",
|
adamc@873
|
377 newline,
|
adamc@873
|
378 string "})"],
|
adamc@873
|
379 string ")"]
|
adamc@873
|
380 | _ => box [string "(is_null",
|
adamc@873
|
381 string (Int.toString i),
|
adamc@873
|
382 string " ? ",
|
adamc@873
|
383 box [string "({",
|
adamc@873
|
384 string (p_sql_ctype t),
|
adamc@873
|
385 space,
|
adamc@873
|
386 string "tmp;",
|
adamc@873
|
387 newline,
|
adamc@873
|
388 string "uw_error(ctx, FATAL, \"Unexpectedly NULL field #",
|
adamc@873
|
389 string (Int.toString i),
|
adamc@873
|
390 string "\");",
|
adamc@873
|
391 newline,
|
adamc@873
|
392 string "tmp;",
|
adamc@873
|
393 newline,
|
adamc@873
|
394 string "})"],
|
adamc@873
|
395 string " : ",
|
adamc@873
|
396 getter t,
|
adamc@873
|
397 string ")"]
|
adamc@873
|
398 end
|
adamc@873
|
399
|
adamc@873
|
400 fun queryCommon {loc, query, cols, doCols} =
|
adamc@873
|
401 box [string "int n, r;",
|
adamc@873
|
402 newline,
|
adamc@873
|
403 string "MYSQL_BIND out[",
|
adamc@873
|
404 string (Int.toString (length cols)),
|
adamc@873
|
405 string "];",
|
adamc@873
|
406 newline,
|
adamc@873
|
407 p_list_sepi (box []) (fn i => fn t =>
|
adamc@873
|
408 let
|
adamc@873
|
409 fun buffers t =
|
adamc@873
|
410 case t of
|
adamc@873
|
411 String => box [string "unsigned long length",
|
adamc@873
|
412 string (Int.toString i),
|
adamc@873
|
413 string ";",
|
adamc@873
|
414 newline]
|
adamc@873
|
415 | Blob => box [string "unsigned long length",
|
adamc@873
|
416 string (Int.toString i),
|
adamc@873
|
417 string ";",
|
adamc@873
|
418 newline]
|
adamc@873
|
419 | _ => box [string (p_sql_ctype t),
|
adamc@873
|
420 space,
|
adamc@873
|
421 string "buffer",
|
adamc@873
|
422 string (Int.toString i),
|
adamc@873
|
423 string ";",
|
adamc@873
|
424 newline]
|
adamc@873
|
425 in
|
adamc@873
|
426 box [string "my_bool is_null",
|
adamc@873
|
427 string (Int.toString i),
|
adamc@873
|
428 string ";",
|
adamc@873
|
429 newline,
|
adamc@873
|
430 case t of
|
adamc@873
|
431 Nullable t => buffers t
|
adamc@873
|
432 | _ => buffers t,
|
adamc@873
|
433 newline]
|
adamc@873
|
434 end) cols,
|
adamc@873
|
435 newline,
|
adamc@873
|
436
|
adamc@873
|
437 string "memset(out, 0, sizeof out);",
|
adamc@873
|
438 newline,
|
adamc@873
|
439 p_list_sepi (box []) (fn i => fn t =>
|
adamc@873
|
440 let
|
adamc@873
|
441 fun buffers t =
|
adamc@873
|
442 case t of
|
adamc@873
|
443 String => box []
|
adamc@873
|
444 | Blob => box []
|
adamc@873
|
445 | _ => box [string "out[",
|
adamc@873
|
446 string (Int.toString i),
|
adamc@873
|
447 string "].buffer = &buffer",
|
adamc@873
|
448 string (Int.toString i),
|
adamc@873
|
449 string ";",
|
adamc@873
|
450 newline]
|
adamc@873
|
451 in
|
adamc@873
|
452 box [string "out[",
|
adamc@873
|
453 string (Int.toString i),
|
adamc@873
|
454 string "].buffer_type = ",
|
adamc@873
|
455 string (p_buffer_type t),
|
adamc@873
|
456 string ";",
|
adamc@873
|
457 newline,
|
adamc@873
|
458 string "out[",
|
adamc@873
|
459 string (Int.toString i),
|
adamc@873
|
460 string "].is_null = &is_null",
|
adamc@873
|
461 string (Int.toString i),
|
adamc@873
|
462 string ";",
|
adamc@873
|
463 newline,
|
adamc@873
|
464
|
adamc@873
|
465 case t of
|
adamc@873
|
466 Nullable t => buffers t
|
adamc@873
|
467 | _ => buffers t,
|
adamc@873
|
468 newline]
|
adamc@873
|
469 end) cols,
|
adamc@873
|
470 newline,
|
adamc@873
|
471
|
adamc@873
|
472 string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"",
|
adamc@873
|
473 string (ErrorMsg.spanToString loc),
|
adamc@873
|
474 string ": Error executing query\");",
|
adamc@873
|
475 newline,
|
adamc@873
|
476 newline,
|
adamc@873
|
477
|
adamc@873
|
478 string "if (mysql_stmt_store_result(stmt)) uw_error(ctx, FATAL, \"",
|
adamc@873
|
479 string (ErrorMsg.spanToString loc),
|
adamc@873
|
480 string ": Error storing query result\");",
|
adamc@873
|
481 newline,
|
adamc@873
|
482 newline,
|
adamc@873
|
483
|
adamc@873
|
484 string "if (mysql_stmt_bind_result(stmt, out)) uw_error(ctx, FATAL, \"",
|
adamc@873
|
485 string (ErrorMsg.spanToString loc),
|
adamc@873
|
486 string ": Error binding query result\");",
|
adamc@873
|
487 newline,
|
adamc@873
|
488 newline,
|
adamc@873
|
489
|
adamc@873
|
490 string "uw_end_region(ctx);",
|
adamc@873
|
491 newline,
|
adamc@873
|
492 string "while ((r = mysql_stmt_fetch(stmt)) == 0) {",
|
adamc@873
|
493 newline,
|
adamc@873
|
494 doCols p_getcol,
|
adamc@873
|
495 string "}",
|
adamc@873
|
496 newline,
|
adamc@873
|
497 newline,
|
adamc@873
|
498
|
adamc@873
|
499 string "if (r != MYSQL_NO_DATA) uw_error(ctx, FATAL, \"",
|
adamc@873
|
500 string (ErrorMsg.spanToString loc),
|
adamc@873
|
501 string ": query result fetching failed\");",
|
adamc@873
|
502 newline]
|
adamc@873
|
503
|
adamc@873
|
504 fun query {loc, cols, doCols} =
|
adamc@873
|
505 box [string "uw_conn *conn = uw_get_db(ctx);",
|
adamc@873
|
506 newline,
|
adamc@873
|
507 string "MYSQL_stmt *stmt = mysql_stmt_init(conn->conn);",
|
adamc@873
|
508 newline,
|
adamc@873
|
509 string "if (stmt == NULL) uw_error(ctx, \"",
|
adamc@873
|
510 string (ErrorMsg.spanToString loc),
|
adamc@873
|
511 string ": can't allocate temporary prepared statement\");",
|
adamc@873
|
512 newline,
|
adamc@873
|
513 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);",
|
adamc@873
|
514 newline,
|
adamc@873
|
515 string "if (mysql_stmt_prepare(stmt, query, strlen(query))) uw_error(ctx, FATAL, \"",
|
adamc@873
|
516 string (ErrorMsg.spanToString loc),
|
adamc@873
|
517 string "\");",
|
adamc@873
|
518 newline,
|
adamc@873
|
519 newline,
|
adamc@873
|
520
|
adamc@873
|
521 p_list_sepi (box []) (fn i => fn t =>
|
adamc@873
|
522 let
|
adamc@873
|
523 fun buffers t =
|
adamc@873
|
524 case t of
|
adamc@873
|
525 String => box []
|
adamc@873
|
526 | Blob => box []
|
adamc@873
|
527 | _ => box [string "out[",
|
adamc@873
|
528 string (Int.toString i),
|
adamc@873
|
529 string "].buffer = &buffer",
|
adamc@873
|
530 string (Int.toString i),
|
adamc@873
|
531 string ";",
|
adamc@873
|
532 newline]
|
adamc@873
|
533 in
|
adamc@873
|
534 box [string "in[",
|
adamc@873
|
535 string (Int.toString i),
|
adamc@873
|
536 string "].buffer_type = ",
|
adamc@873
|
537 string (p_buffer_type t),
|
adamc@873
|
538 string ";",
|
adamc@873
|
539 newline,
|
adamc@873
|
540
|
adamc@873
|
541 case t of
|
adamc@873
|
542 Nullable t => box [string "in[",
|
adamc@873
|
543 string (Int.toString i),
|
adamc@873
|
544 string "].is_null = &is_null",
|
adamc@873
|
545 string (Int.toString i),
|
adamc@873
|
546 string ";",
|
adamc@873
|
547 newline,
|
adamc@873
|
548 buffers t]
|
adamc@873
|
549 | _ => buffers t,
|
adamc@873
|
550 newline]
|
adamc@873
|
551 end) cols,
|
adamc@873
|
552 newline,
|
adamc@873
|
553
|
adamc@873
|
554 queryCommon {loc = loc, cols = cols, doCols = doCols, query = string "query"},
|
adamc@873
|
555
|
adamc@873
|
556 string "uw_pop_cleanup(ctx);",
|
adamc@873
|
557 newline]
|
adamc@873
|
558
|
adamc@873
|
559 fun p_ensql t e =
|
adamc@873
|
560 case t of
|
adamc@873
|
561 Int => box [string "uw_Basis_attrifyInt(ctx, ", e, string ")"]
|
adamc@873
|
562 | Float => box [string "uw_Basis_attrifyFloat(ctx, ", e, string ")"]
|
adamc@873
|
563 | String => e
|
adamc@873
|
564 | Bool => box [string "(", e, string " ? \"TRUE\" : \"FALSE\")"]
|
adamc@873
|
565 | Time => box [string "uw_Basis_attrifyTime(ctx, ", e, string ")"]
|
adamc@873
|
566 | Blob => box [e, string ".data"]
|
adamc@873
|
567 | Channel => box [string "uw_Basis_attrifyChannel(ctx, ", e, string ")"]
|
adamc@873
|
568 | Client => box [string "uw_Basis_attrifyClient(ctx, ", e, string ")"]
|
adamc@873
|
569 | Nullable String => e
|
adamc@873
|
570 | Nullable t => box [string "(",
|
adamc@873
|
571 e,
|
adamc@873
|
572 string " == NULL ? NULL : ",
|
adamc@873
|
573 p_ensql t (box [string "(*", e, string ")"]),
|
adamc@873
|
574 string ")"]
|
adamc@873
|
575
|
adamc@873
|
576 fun queryPrepared {loc, id, query, inputs, cols, doCols} =
|
adamc@873
|
577 box [string "uw_conn *conn = uw_get_db(ctx);",
|
adamc@873
|
578 newline,
|
adamc@873
|
579 string "MYSQL_BIND in[",
|
adamc@873
|
580 string (Int.toString (length inputs)),
|
adamc@873
|
581 string "];",
|
adamc@873
|
582 newline,
|
adamc@873
|
583 p_list_sepi (box []) (fn i => fn t =>
|
adamc@873
|
584 let
|
adamc@873
|
585 fun buffers t =
|
adamc@873
|
586 case t of
|
adamc@873
|
587 String => box [string "unsigned long in_length",
|
adamc@873
|
588 string (Int.toString i),
|
adamc@873
|
589 string ";",
|
adamc@873
|
590 newline]
|
adamc@873
|
591 | Blob => box [string "unsigned long in_length",
|
adamc@873
|
592 string (Int.toString i),
|
adamc@873
|
593 string ";",
|
adamc@873
|
594 newline]
|
adamc@873
|
595 | Time => box [string (p_sql_ctype t),
|
adamc@873
|
596 space,
|
adamc@873
|
597 string "in_buffer",
|
adamc@873
|
598 string (Int.toString i),
|
adamc@873
|
599 string ";",
|
adamc@873
|
600 newline]
|
adamc@873
|
601 | _ => box []
|
adamc@873
|
602 in
|
adamc@873
|
603 box [case t of
|
adamc@873
|
604 Nullable t => box [string "my_bool in_is_null",
|
adamc@873
|
605 string (Int.toString i),
|
adamc@873
|
606 string ";",
|
adamc@873
|
607 newline,
|
adamc@873
|
608 buffers t]
|
adamc@873
|
609 | _ => buffers t,
|
adamc@873
|
610 newline]
|
adamc@873
|
611 end) inputs,
|
adamc@873
|
612 string "MYSQL_STMT *stmt = conn->p",
|
adamc@873
|
613 string (Int.toString id),
|
adamc@873
|
614 string ";",
|
adamc@873
|
615 newline,
|
adamc@873
|
616 newline,
|
adamc@873
|
617
|
adamc@873
|
618 string "memset(in, 0, sizeof in);",
|
adamc@873
|
619 newline,
|
adamc@873
|
620 p_list_sepi (box []) (fn i => fn t =>
|
adamc@873
|
621 let
|
adamc@873
|
622 fun buffers t =
|
adamc@873
|
623 case t of
|
adamc@873
|
624 String => box [string "in[",
|
adamc@873
|
625 string (Int.toString i),
|
adamc@873
|
626 string "].buffer = arg",
|
adamc@873
|
627 string (Int.toString (i + 1)),
|
adamc@873
|
628 string ";",
|
adamc@873
|
629 newline,
|
adamc@873
|
630 string "in_length",
|
adamc@873
|
631 string (Int.toString i),
|
adamc@873
|
632 string "= in[",
|
adamc@873
|
633 string (Int.toString i),
|
adamc@873
|
634 string "].buffer_length = strlen(arg",
|
adamc@873
|
635 string (Int.toString (i + 1)),
|
adamc@873
|
636 string ");",
|
adamc@873
|
637 newline,
|
adamc@873
|
638 string "in[",
|
adamc@873
|
639 string (Int.toString i),
|
adamc@873
|
640 string "].length = &in_length",
|
adamc@873
|
641 string (Int.toString i),
|
adamc@873
|
642 string ";",
|
adamc@873
|
643 newline]
|
adamc@873
|
644 | Blob => box [string "in[",
|
adamc@873
|
645 string (Int.toString i),
|
adamc@873
|
646 string "].buffer = arg",
|
adamc@873
|
647 string (Int.toString (i + 1)),
|
adamc@873
|
648 string ".data;",
|
adamc@873
|
649 newline,
|
adamc@873
|
650 string "in_length",
|
adamc@873
|
651 string (Int.toString i),
|
adamc@873
|
652 string "= in[",
|
adamc@873
|
653 string (Int.toString i),
|
adamc@873
|
654 string "].buffer_length = arg",
|
adamc@873
|
655 string (Int.toString (i + 1)),
|
adamc@873
|
656 string ".size;",
|
adamc@873
|
657 newline,
|
adamc@873
|
658 string "in[",
|
adamc@873
|
659 string (Int.toString i),
|
adamc@873
|
660 string "].length = &in_length",
|
adamc@873
|
661 string (Int.toString i),
|
adamc@873
|
662 string ";",
|
adamc@873
|
663 newline]
|
adamc@873
|
664 | Time =>
|
adamc@873
|
665 let
|
adamc@873
|
666 fun oneField dst src =
|
adamc@873
|
667 box [string "in_buffer",
|
adamc@873
|
668 string (Int.toString i),
|
adamc@873
|
669 string ".",
|
adamc@873
|
670 string dst,
|
adamc@873
|
671 string " = tms.tm_",
|
adamc@873
|
672 string src,
|
adamc@873
|
673 string ";",
|
adamc@873
|
674 newline]
|
adamc@873
|
675 in
|
adamc@873
|
676 box [string "({",
|
adamc@873
|
677 newline,
|
adamc@873
|
678 string "struct tm tms;",
|
adamc@873
|
679 newline,
|
adamc@873
|
680 string "if (localtime_r(&arg",
|
adamc@873
|
681 string (Int.toString (i + 1)),
|
adamc@873
|
682 string ", &tm) == NULL) uw_error(\"",
|
adamc@873
|
683 string (ErrorMsg.spanToString loc),
|
adamc@873
|
684 string ": error converting to MySQL time\");",
|
adamc@873
|
685 newline,
|
adamc@873
|
686 oneField "year" "year",
|
adamc@873
|
687 oneField "month" "mon",
|
adamc@873
|
688 oneField "day" "mday",
|
adamc@873
|
689 oneField "hour" "hour",
|
adamc@873
|
690 oneField "minute" "min",
|
adamc@873
|
691 oneField "second" "sec",
|
adamc@873
|
692 newline,
|
adamc@873
|
693 string "in[",
|
adamc@873
|
694 string (Int.toString i),
|
adamc@873
|
695 string "].buffer = &in_buffer",
|
adamc@873
|
696 string (Int.toString i),
|
adamc@873
|
697 string ";",
|
adamc@873
|
698 newline]
|
adamc@873
|
699 end
|
adamc@873
|
700
|
adamc@873
|
701 | _ => box [string "in[",
|
adamc@873
|
702 string (Int.toString i),
|
adamc@873
|
703 string "].buffer = &arg",
|
adamc@873
|
704 string (Int.toString (i + 1)),
|
adamc@873
|
705 string ";",
|
adamc@873
|
706 newline]
|
adamc@873
|
707 in
|
adamc@873
|
708 box [string "in[",
|
adamc@873
|
709 string (Int.toString i),
|
adamc@873
|
710 string "].buffer_type = ",
|
adamc@873
|
711 string (p_buffer_type t),
|
adamc@873
|
712 string ";",
|
adamc@873
|
713 newline,
|
adamc@873
|
714
|
adamc@873
|
715 case t of
|
adamc@873
|
716 Nullable t => box [string "in[",
|
adamc@873
|
717 string (Int.toString i),
|
adamc@873
|
718 string "].is_null = &in_is_null",
|
adamc@873
|
719 string (Int.toString i),
|
adamc@873
|
720 string ";",
|
adamc@873
|
721 newline,
|
adamc@873
|
722 string "if (arg",
|
adamc@873
|
723 string (Int.toString (i + 1)),
|
adamc@873
|
724 string " == NULL) {",
|
adamc@873
|
725 newline,
|
adamc@873
|
726 box [string "in_is_null",
|
adamc@873
|
727 string (Int.toString i),
|
adamc@873
|
728 string " = 1;",
|
adamc@873
|
729 newline],
|
adamc@873
|
730 string "} else {",
|
adamc@873
|
731 box [case t of
|
adamc@873
|
732 String => box []
|
adamc@873
|
733 | _ =>
|
adamc@873
|
734 box [string (p_sql_ctype t),
|
adamc@873
|
735 space,
|
adamc@873
|
736 string "arg",
|
adamc@873
|
737 string (Int.toString (i + 1)),
|
adamc@873
|
738 string " = *arg",
|
adamc@873
|
739 string (Int.toString (i + 1)),
|
adamc@873
|
740 string ";",
|
adamc@873
|
741 newline],
|
adamc@873
|
742 string "in_is_null",
|
adamc@873
|
743 string (Int.toString i),
|
adamc@873
|
744 string " = 0;",
|
adamc@873
|
745 newline,
|
adamc@873
|
746 buffers t,
|
adamc@873
|
747 newline]]
|
adamc@873
|
748
|
adamc@873
|
749 | _ => buffers t,
|
adamc@873
|
750 newline]
|
adamc@873
|
751 end) inputs,
|
adamc@873
|
752 newline,
|
adamc@873
|
753
|
adamc@873
|
754 queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"",
|
adamc@873
|
755 string (String.toString query),
|
adamc@873
|
756 string "\""]}]
|
adamc@873
|
757
|
adamc@873
|
758 fun dml _ = box []
|
adamc@873
|
759 fun dmlPrepared _ = box []
|
adamc@873
|
760 fun nextval _ = box []
|
adamc@873
|
761 fun nextvalPrepared _ = box []
|
adamc@867
|
762
|
adamc@866
|
763 val () = addDbms {name = "mysql",
|
adamc@866
|
764 header = "mysql/mysql.h",
|
adamc@866
|
765 link = "-lmysqlclient",
|
adamc@866
|
766 global_init = box [string "void uw_client_init() {",
|
adamc@866
|
767 newline,
|
adamc@866
|
768 box [string "if (mysql_library_init(0, NULL, NULL)) {",
|
adamc@866
|
769 newline,
|
adamc@866
|
770 box [string "fprintf(stderr, \"Could not initialize MySQL library\\n\");",
|
adamc@866
|
771 newline,
|
adamc@866
|
772 string "exit(1);",
|
adamc@866
|
773 newline],
|
adamc@866
|
774 string "}",
|
adamc@866
|
775 newline],
|
adamc@866
|
776 string "}",
|
adamc@866
|
777 newline],
|
adamc@867
|
778 init = init,
|
adamc@873
|
779 p_sql_type = p_sql_type,
|
adamc@867
|
780 query = query,
|
adamc@868
|
781 queryPrepared = queryPrepared,
|
adamc@868
|
782 dml = dml,
|
adamc@869
|
783 dmlPrepared = dmlPrepared,
|
adamc@869
|
784 nextval = nextval,
|
adamc@869
|
785 nextvalPrepared = nextvalPrepared}
|
adamc@866
|
786
|
adamc@866
|
787 end
|