comparison src/sqlite.sml @ 885:e6070333d8a8

demo/sql works with SQLite
author Adam Chlipala <adamc@hcoop.net>
date Fri, 17 Jul 2009 16:29:36 -0400
parents
children 5805fa825fe8
comparison
equal deleted inserted replaced
884:ced093080e17 885:e6070333d8a8
1 (* Copyright (c) 2009, Adam Chlipala
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - The names of contributors may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 *)
27
28 structure SQLite :> SQLITE = struct
29
30 open Settings
31 open Print.PD
32 open Print
33
34 fun p_sql_type t =
35 case t of
36 Int => "integer"
37 | Float => "real"
38 | String => "text"
39 | Bool => "integer"
40 | Time => "integer"
41 | Blob => "blob"
42 | Channel => "integer"
43 | Client => "integer"
44 | Nullable t => p_sql_type t
45
46 val ident = String.translate (fn #"'" => "PRIME"
47 | ch => str ch)
48
49 fun checkRel (table, checkNullable) (s, xts) =
50 let
51 val q = "SELECT COUNT(*) FROM sqlite_master WHERE type = '" ^ table ^ "' AND name = '"
52 ^ s ^ "'"
53 in
54 box [string "if (sqlite3_prepare_v2(conn->conn, \"",
55 string q,
56 string "\", -1, &stmt, NULL) != SQLITE_OK) {",
57 newline,
58 box [string "sqlite3_close(conn->conn);",
59 newline,
60 string "uw_error(ctx, FATAL, \"Query preparation failed:\\n",
61 string q,
62 string "\");",
63 newline],
64 string "}",
65 newline,
66 newline,
67
68 string "while ((res = sqlite3_step(stmt)) == SQLITE_BUSY)",
69 newline,
70 box [string "sleep(1);",
71 newline],
72 newline,
73 string "if (res == SQLITE_DONE) {",
74 newline,
75 box [string "sqlite3_finalize(stmt);",
76 newline,
77 string "sqlite3_close(conn->conn);",
78 newline,
79 string "uw_error(ctx, FATAL, \"No row returned:\\n",
80 string q,
81 string "\");",
82 newline],
83 string "}",
84 newline,
85 newline,
86 string "if (res != SQLITE_ROW) {",
87 newline,
88 box [string "sqlite3_finalize(stmt);",
89 newline,
90 string "sqlite3_close(conn->conn);",
91 newline,
92 string "uw_error(ctx, FATAL, \"Error getting row:\\n",
93 string q,
94 string "\");",
95 newline],
96 string "}",
97 newline,
98 newline,
99
100 string "if (sqlite3_column_count(stmt) != 1) {",
101 newline,
102 box [string "sqlite3_finalize(stmt);",
103 newline,
104 string "sqlite3_close(conn->conn);",
105 newline,
106 string "uw_error(ctx, FATAL, \"Bad column count:\\n",
107 string q,
108 string "\");",
109 newline],
110 string "}",
111 newline,
112 newline,
113
114 string "if (sqlite3_column_int(stmt, 0) != 1) {",
115 newline,
116 box [string "sqlite3_finalize(stmt);",
117 newline,
118 string "sqlite3_close(conn->conn);",
119 newline,
120 string "uw_error(ctx, FATAL, \"Table '",
121 string s,
122 string "' does not exist.\");",
123 newline],
124 string "}",
125 newline,
126 newline,
127 string "sqlite3_finalize(stmt);",
128 newline]
129 end
130
131 fun init {dbstring, prepared = ss, tables, views, sequences} =
132 let
133 val db = ref dbstring
134 in
135 app (fn s =>
136 case String.fields (fn ch => ch = #"=") s of
137 [name, value] =>
138 (case name of
139 "dbname" => db := value
140 | _ => ())
141 | _ => ()) (String.tokens Char.isSpace dbstring);
142
143 box [string "typedef struct {",
144 newline,
145 box [string "sqlite3 *conn;",
146 newline,
147 p_list_sepi (box [])
148 (fn i => fn _ =>
149 box [string "sqlite3_stmt *p",
150 string (Int.toString i),
151 string ";",
152 newline])
153 ss],
154 string "} uw_conn;",
155 newline,
156 newline,
157
158 string "void uw_client_init(void) {",
159 newline,
160 box [string "uw_sqlfmtInt = \"%lld%n\";",
161 newline,
162 string "uw_sqlfmtFloat = \"%g%n\";",
163 newline,
164 string "uw_Estrings = 0;",
165 newline,
166 string "uw_sqlsuffixString = \"\";",
167 newline,
168 string "uw_sqlsuffixBlob = \"\";",
169 newline,
170 string "uw_sqlfmtUint4 = \"%u%n\";",
171 newline],
172 string "}",
173 newline,
174 newline,
175
176 if #persistent (currentProtocol ()) then
177 box [string "static void uw_db_validate(uw_context ctx) {",
178 newline,
179 string "uw_conn *conn = uw_get_db(ctx);",
180 newline,
181 string "sqlite3_stmt *stmt;",
182 newline,
183 string "int res;",
184 newline,
185 newline,
186 p_list_sep newline (checkRel ("table", true)) tables,
187 p_list_sep newline (fn name => checkRel ("table", true)
188 (name, [("id", Settings.Client)])) sequences,
189 p_list_sep newline (checkRel ("view", false)) views,
190 string "}",
191 newline,
192 newline,
193
194 string "static void uw_db_prepare(uw_context ctx) {",
195 newline,
196 string "uw_conn *conn = uw_get_db(ctx);",
197 newline,
198 newline,
199
200 p_list_sepi newline (fn i => fn (s, n) =>
201 let
202 fun uhoh this s args =
203 box [p_list_sepi (box [])
204 (fn j => fn () =>
205 box [string
206 "sqlite3_finalize(conn->p",
207 string (Int.toString j),
208 string ");",
209 newline])
210 (List.tabulate (i, fn _ => ())),
211 box (if this then
212 [string
213 "sqlite3_finalize(conn->p",
214 string (Int.toString i),
215 string ");",
216 newline]
217 else
218 []),
219 string "sqlite3_close(conn->conn);",
220 newline,
221 string "uw_error(ctx, FATAL, \"",
222 string s,
223 string "\"",
224 p_list_sep (box []) (fn s => box [string ", ",
225 string s]) args,
226 string ");",
227 newline]
228 in
229 box [string "if (sqlite3_prepare_v2(conn->conn, \"",
230 string (String.toString s),
231 string "\", -1, &conn->p",
232 string (Int.toString i),
233 string ", NULL) != SQLITE_OK) {",
234 newline,
235 uhoh false ("Error preparing statement: "
236 ^ String.toString s) [],
237 string "}",
238 newline]
239 end)
240 ss,
241
242 string "}"]
243 else
244 box [string "static void uw_db_prepare(uw_context ctx) { }",
245 newline,
246 string "static void uw_db_validate(uw_context ctx) { }"],
247 newline,
248 newline,
249
250 string "void uw_db_init(uw_context ctx) {",
251 newline,
252 string "sqlite3 *sqlite;",
253 newline,
254 string "uw_conn *conn;",
255 newline,
256 newline,
257 string "if (sqlite3_open(\"",
258 string (!db),
259 string "\", &sqlite) != SQLITE_OK) uw_error(ctx, FATAL, ",
260 string "\"Can't open SQLite database.\");",
261 newline,
262 newline,
263 string "conn = calloc(1, sizeof(uw_conn));",
264 newline,
265 string "conn->conn = sqlite;",
266 newline,
267 string "uw_set_db(ctx, conn);",
268 newline,
269 string "uw_db_validate(ctx);",
270 newline,
271 string "uw_db_prepare(ctx);",
272 newline,
273 string "}",
274 newline,
275 newline,
276
277 string "void uw_db_close(uw_context ctx) {",
278 newline,
279 string "uw_conn *conn = uw_get_db(ctx);",
280 newline,
281 p_list_sepi (box [])
282 (fn i => fn _ =>
283 box [string "if (conn->p",
284 string (Int.toString i),
285 string ") sqlite3_finalize(conn->p",
286 string (Int.toString i),
287 string ");",
288 newline])
289 ss,
290 string "sqlite3_close(conn->conn);",
291 newline,
292 string "}",
293 newline,
294 newline,
295
296 string "int uw_db_begin(uw_context ctx) {",
297 newline,
298 string "uw_conn *conn = uw_get_db(ctx);",
299 newline,
300 newline,
301 string "if (sqlite3_exec(conn->conn, \"BEGIN\", NULL, NULL, NULL) == SQLITE_OK)",
302 newline,
303 box [string "return 0;",
304 newline],
305 string "else {",
306 newline,
307 box [string "fprintf(stderr, \"Begin error: %s\\n\", sqlite3_errmsg(conn->conn));",
308 newline,
309 string "return 1;",
310 newline],
311 string "}",
312 newline,
313 string "}",
314 newline,
315 string "int uw_db_commit(uw_context ctx) {",
316 newline,
317 string "uw_conn *conn = uw_get_db(ctx);",
318 newline,
319 string "if (sqlite3_exec(conn->conn, \"COMMIT\", NULL, NULL, NULL) == SQLITE_OK)",
320 newline,
321 box [string "return 0;",
322 newline],
323 string "else {",
324 newline,
325 box [string "fprintf(stderr, \"Commit error: %s\\n\", sqlite3_errmsg(conn->conn));",
326 newline,
327 string "return 1;",
328 newline],
329 string "}",
330 newline,
331 string "}",
332 newline,
333 newline,
334
335 string "int uw_db_rollback(uw_context ctx) {",
336 newline,
337 string "uw_conn *conn = uw_get_db(ctx);",
338 newline,
339 string "if (sqlite3_exec(conn->conn, \"ROLLBACK\", NULL, NULL, NULL) == SQLITE_OK)",
340 newline,
341 box [string "return 0;",
342 newline],
343 string "else {",
344 newline,
345 box [string "fprintf(stderr, \"Rollback error: %s\\n\", sqlite3_errmsg(conn->conn));",
346 newline,
347 string "return 1;",
348 newline],
349 string "}",
350 newline,
351 string "}",
352 newline,
353 newline]
354 end
355
356 fun p_getcol {loc, wontLeakStrings, col = i, typ = t} =
357 let
358 fun p_unsql t =
359 case t of
360 Int => box [string "sqlite3_column_int64(stmt, ", string (Int.toString i), string ")"]
361 | Float => box [string "sqlite3_column_double(stmt, ", string (Int.toString i), string ")"]
362 | String =>
363 if wontLeakStrings then
364 box [string "sqlite3_column_text(stmt, ", string (Int.toString i), string ")"]
365 else
366 box [string "uw_strdup(ctx, sqlite3_column_text(stmt, ", string (Int.toString i), string "))"]
367 | Bool => box [string "(uw_Basis_bool)sqlite3_column_int(stmt, ", string (Int.toString i), string ")"]
368 | Time => box [string "(uw_Basis_time)sqlite3_column_int64(stmt, ", string (Int.toString i), string ")"]
369 | Blob => box [string "({",
370 newline,
371 string "char *data = sqlite3_column_blob(stmt, ",
372 string (Int.toString i),
373 string ");",
374 newline,
375 string "uw_Basis_blob b = {sqlite3_column_bytes(stmt, ",
376 string (Int.toString i),
377 string "), data};",
378 newline,
379 string "b;",
380 newline,
381 string "})"]
382 | Channel => box [string "sqlite3_column_int64(stmt, ", string (Int.toString i), string ")"]
383 | Client => box [string "sqlite3_column_int(stmt, ", string (Int.toString i), string ")"]
384
385 | Nullable _ => raise Fail "Postgres: Recursive Nullable"
386
387 fun getter t =
388 case t of
389 Nullable t =>
390 box [string "(sqlite3_column_type(stmt, ",
391 string (Int.toString i),
392 string ") == SQLITE_NULL ? NULL : ",
393 case t of
394 String => getter t
395 | _ => box [string "({",
396 newline,
397 string (p_sql_ctype t),
398 space,
399 string "*tmp = uw_malloc(ctx, sizeof(",
400 string (p_sql_ctype t),
401 string "));",
402 newline,
403 string "*tmp = ",
404 getter t,
405 string ";",
406 newline,
407 string "tmp;",
408 newline,
409 string "})"],
410 string ")"]
411 | _ =>
412 box [string "(sqlite3_column_type(stmt, ",
413 string (Int.toString i),
414 string ") == SQLITE_NULL ? ",
415 box [string "({",
416 string (p_sql_ctype t),
417 space,
418 string "tmp;",
419 newline,
420 string "uw_error(ctx, FATAL, \"",
421 string (ErrorMsg.spanToString loc),
422 string ": Unexpectedly NULL field #",
423 string (Int.toString i),
424 string "\");",
425 newline,
426 string "tmp;",
427 newline,
428 string "})"],
429 string " : ",
430 p_unsql t,
431 string ")"]
432 in
433 getter t
434 end
435
436 fun queryCommon {loc, query, cols, doCols} =
437 box [string "int r;",
438 newline,
439
440 string "sqlite3_reset(stmt);",
441 newline,
442
443 string "uw_end_region(ctx);",
444 newline,
445 string "while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {",
446 newline,
447 doCols p_getcol,
448 string "}",
449 newline,
450 newline,
451
452 string "if (r == SQLITE_BUSY) {",
453 box [string "sleep(1);",
454 newline,
455 string "uw_error(ctx, UNLIMITED_RETRY, \"Database is busy\");",
456 newline],
457 string "}",
458 newline,
459 newline,
460
461 string "if (r != SQLITE_DONE) uw_error(ctx, FATAL, \"",
462 string (ErrorMsg.spanToString loc),
463 string ": query step failed: %s\\n%s\", ",
464 query,
465 string ", sqlite3_errmsg(conn->conn));",
466 newline,
467 newline]
468
469 fun query {loc, cols, doCols} =
470 box [string "uw_conn *conn = uw_get_db(ctx);",
471 newline,
472 string "sqlite3 *stmt;",
473 newline,
474 newline,
475 string "if (sqlite3_prepare_v2(conn->conn, query, -1, &stmt, NULL) != SQLITE_OK) uw_error(ctx, FATAL, \"Error preparing statement: %s\\n%s\", sqlite3_errmsg(conn->conn));",
476 newline,
477 newline,
478 string "uw_push_cleanup(ctx, (void (*)(void *))sqlite3_finalize, stmt);",
479 newline,
480 newline,
481
482 queryCommon {loc = loc, cols = cols, doCols = doCols, query = string "query"},
483
484 string "uw_pop_cleanup(ctx);",
485 newline]
486
487 fun p_inputs loc =
488 p_list_sepi (box [])
489 (fn i => fn t =>
490 let
491 fun bind (t, arg) =
492 case t of
493 Int => box [string "sqlite3_bind_int64(stmt, ",
494 string (Int.toString (i + 1)),
495 string ", ",
496 arg,
497 string ")"]
498 | Float => box [string "sqlite3_bind_double(stmt, ",
499 string (Int.toString (i + 1)),
500 string ", ",
501 arg,
502 string ")"]
503 | String => box [string "sqlite3_bind_text(stmt, ",
504 string (Int.toString (i + 1)),
505 string ", ",
506 arg,
507 string ", -1, SQLITE_TRANSIENT)"]
508 | Bool => box [string "sqlite3_bind_int(stmt, ",
509 string (Int.toString (i + 1)),
510 string ", ",
511 arg,
512 string ")"]
513 | Time => box [string "sqlite3_bind_int64(stmt, ",
514 string (Int.toString (i + 1)),
515 string ", ",
516 arg,
517 string ")"]
518 | Blob => box [string "sqlite3_bind_blob(stmt, ",
519 string (Int.toString (i + 1)),
520 string ", ",
521 arg,
522 string ".data, ",
523 arg,
524 string ".size, SQLITE_TRANSIENT"]
525 | Channel => box [string "sqlite_bind_int64(stmt, ",
526 string (Int.toString (i + 1)),
527 string ", ",
528 arg,
529 string ")"]
530 | Client => box [string "sqlite3_bind_int(stmt, ",
531 string (Int.toString (i + 1)),
532 string ", ",
533 arg,
534 string ")"]
535 | Nullable t => box [string "(",
536 arg,
537 string " == NULL ? sqlite3_bind_null(stmt, ",
538 string (Int.toString (i + 1)),
539 string ") : ",
540 bind (t, case t of
541 String => arg
542 | _ => box [string "(*", arg, string ")"]),
543 string ")"]
544 in
545 box [string "if (",
546 bind (t, box [string "arg", string (Int.toString (i + 1))]),
547 string " != SQLITE_OK) uw_error(ctx, FATAL, \"",
548 string (ErrorMsg.spanToString loc),
549 string ": Error binding parameter #",
550 string (Int.toString (i + 1)),
551 string ": %s\", sqlite3_errmsg(conn->conn));",
552 newline]
553 end)
554
555 fun queryPrepared {loc, id, query, inputs, cols, doCols, nested} =
556 box [string "uw_conn *conn = uw_get_db(ctx);",
557 newline,
558 if nested then
559 box [string "sqlite3_stmt *stmt;",
560 newline]
561 else
562 box [string "sqlite3_stmt *stmt = conn->p",
563 string (Int.toString id),
564 string ";",
565 newline,
566 newline,
567
568 string "if (stmt == NULL) {",
569 newline],
570
571 string "if (sqlite3_prepare_v2(conn->conn, \"",
572 string (String.toString query),
573 string "\", -1, &stmt, NULL) != SQLITE_OK) uw_error(ctx, FATAL, \"Error preparing statement: ",
574 string (String.toString query),
575 string "\\n%s\", sqlite3_errmsg(conn->conn));",
576 newline,
577 if nested then
578 box [string "uw_push_cleanup(ctx, (void (*)(void *))sqlite3_finalize, stmt);",
579 newline]
580 else
581 box [string "conn->p",
582 string (Int.toString id),
583 string " = stmt;",
584 newline,
585 string "}",
586 newline,
587 newline,
588 string "uw_push_cleanup(ctx, (void (*)(void *))sqlite3_clear_bindings, stmt);",
589 newline,
590 string "uw_push_cleanup(ctx, (void (*)(void *))sqlite3_reset, stmt);",
591 newline],
592 newline,
593
594 p_inputs loc inputs,
595 newline,
596
597 queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"",
598 string (String.toString query),
599 string "\""]},
600
601 string "uw_pop_cleanup(ctx);",
602 newline,
603 if nested then
604 box []
605 else
606 box [string "uw_pop_cleanup(ctx);",
607 newline]]
608
609 fun dmlCommon {loc, dml} =
610 box [string "int r;",
611 newline,
612
613 string "if ((r = sqlite3_step(stmt)) == SQLITE_BUSY) {",
614 box [string "sleep(1);",
615 newline,
616 string "uw_error(ctx, UNLIMITED_RETRY, \"Database is busy\");",
617 newline],
618 string "}",
619 newline,
620 newline,
621
622 string "if (r != SQLITE_DONE) uw_error(ctx, FATAL, \"",
623 string (ErrorMsg.spanToString loc),
624 string ": DML step failed: %s\\n%s\", ",
625 dml,
626 string ", sqlite3_errmsg(conn->conn));",
627 newline]
628
629 fun dml loc =
630 box [string "uw_conn *conn = uw_get_db(ctx);",
631 newline,
632 string "sqlite3 *stmt;",
633 newline,
634 newline,
635 string "if (sqlite3_prepare_v2(conn->conn, dml, -1, &stmt, NULL) != SQLITE_OK) uw_error(ctx, FATAL, \"Error preparing statement: %s\\n%s\", dml, sqlite3_errmsg(conn->conn));",
636 newline,
637 newline,
638 string "uw_push_cleanup(ctx, (void (*)(void *))sqlite3_finalize, stmt);",
639 newline,
640 newline,
641
642 dmlCommon {loc = loc, dml = string "dml"},
643
644 string "uw_pop_cleanup(ctx);",
645 newline]
646
647 fun dmlPrepared {loc, id, dml, inputs} =
648 box [string "uw_conn *conn = uw_get_db(ctx);",
649 newline,
650 string "sqlite3_stmt *stmt = conn->p",
651 string (Int.toString id),
652 string ";",
653 newline,
654 newline,
655
656 string "if (stmt == NULL) {",
657 newline,
658 box [string "if (sqlite3_prepare_v2(conn->conn, \"",
659 string (String.toString dml),
660 string "\", -1, &stmt, NULL) != SQLITE_OK) uw_error(ctx, FATAL, \"Error preparing statement: ",
661 string (String.toString dml),
662 string "\\n%s\", sqlite3_errmsg(conn->conn));",
663 newline,
664 string "conn->p",
665 string (Int.toString id),
666 string " = stmt;",
667 newline],
668 string "}",
669 newline,
670
671 string "uw_push_cleanup(ctx, (void (*)(void *))sqlite3_clear_bindings, stmt);",
672 newline,
673 string "uw_push_cleanup(ctx, (void (*)(void *))sqlite3_reset, stmt);",
674 newline,
675
676 p_inputs loc inputs,
677 newline,
678
679 dmlCommon {loc = loc, dml = box [string "\"",
680 string (String.toString dml),
681 string "\""]},
682
683 string "uw_pop_cleanup(ctx);",
684 newline,
685 string "uw_pop_cleanup(ctx);",
686 newline]
687
688 fun nextval {loc, seqE, seqName} =
689 box [string "uw_conn *conn = uw_get_db(ctx);",
690 newline,
691 string "char *insert = ",
692 case seqName of
693 SOME s => string ("\"INSERT INTO " ^ s ^ " VALUES ()\"")
694 | NONE => box [string "uw_Basis_strcat(ctx, \"INSERT INTO \", uw_Basis_strcat(ctx, ",
695 seqE,
696 string ", \" VALUES ()\"))"],
697 string ";",
698 newline,
699 string "char *delete = ",
700 case seqName of
701 SOME s => string ("\"DELETE FROM " ^ s ^ "\"")
702 | NONE => box [string "uw_Basis_strcat(ctx, \"DELETE FROM \", ",
703 seqE,
704 string ")"],
705 string ";",
706 newline,
707 newline,
708
709 string "if (sqlite3_exec(conn->conn, insert, NULL, NULL, NULL) != SQLITE_OK) uw_error(ctx, FATAL, \"'nextval' INSERT failed\");",
710 newline,
711 string "n = sqlite3_last_insert_rowid(conn->conn);",
712 newline,
713 string "if (sqlite3_exec(conn->conn, delete, NULL, NULL, NULL) != SQLITE_OK) uw_error(ctx, FATAL, \"'nextval' DELETE failed\");",
714 newline]
715
716 fun nextvalPrepared _ = raise Fail "SQLite.nextvalPrepared called"
717
718 fun sqlifyString s = "'" ^ String.translate (fn #"'" => "''"
719 | ch =>
720 if Char.isPrint ch then
721 str ch
722 else
723 (ErrorMsg.error
724 "Non-printing character found in SQL string literal";
725 ""))
726 (String.toString s) ^ "'"
727
728 fun p_cast (s, _) = s
729
730 fun p_blank _ = "?"
731
732 val () = addDbms {name = "sqlite",
733 header = "sqlite3.h",
734 link = "-lsqlite3",
735 init = init,
736 p_sql_type = p_sql_type,
737 query = query,
738 queryPrepared = queryPrepared,
739 dml = dml,
740 dmlPrepared = dmlPrepared,
741 nextval = nextval,
742 nextvalPrepared = nextvalPrepared,
743 sqlifyString = sqlifyString,
744 p_cast = p_cast,
745 p_blank = p_blank,
746 supportsDeleteAs = false,
747 createSequence = fn s => "CREATE TABLE " ^ s ^ " (id INTEGER PRIMARY KEY AUTO INCREMENT)",
748 textKeysNeedLengths = false,
749 supportsNextval = false,
750 supportsNestedPrepared = false,
751 sqlPrefix = ""}
752
753 end