Mercurial > urweb
comparison src/mysql.sml @ 874:3c7b48040dcf
MySQL demo/sql succeeds in reading no rows
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Sun, 12 Jul 2009 15:05:40 -0400 |
parents | 41971801b62d |
children | c50101ddf7fa |
comparison
equal
deleted
inserted
replaced
873:41971801b62d | 874:3c7b48040dcf |
---|---|
53 | Blob => "MYSQL_TYPE_BLOB" | 53 | Blob => "MYSQL_TYPE_BLOB" |
54 | Channel => "MYSQL_TYPE_LONGLONG" | 54 | Channel => "MYSQL_TYPE_LONGLONG" |
55 | Client => "MYSQL_TYPE_LONG" | 55 | Client => "MYSQL_TYPE_LONG" |
56 | Nullable t => p_buffer_type t | 56 | Nullable t => p_buffer_type t |
57 | 57 |
58 fun p_sql_type_base t = | |
59 case t of | |
60 Int => "bigint" | |
61 | Float => "double" | |
62 | String => "longtext" | |
63 | Bool => "tinyint" | |
64 | Time => "timestamp" | |
65 | Blob => "longblob" | |
66 | Channel => "bigint" | |
67 | Client => "int" | |
68 | Nullable t => p_sql_type_base t | |
69 | |
70 val ident = String.translate (fn #"'" => "PRIME" | |
71 | ch => str ch) | |
72 | |
73 fun checkRel (table, checkNullable) (s, xts) = | |
74 let | |
75 val sl = CharVector.map Char.toLower s | |
76 | |
77 val q = "SELECT COUNT(*) FROM information_schema." ^ table ^ " WHERE table_name = '" | |
78 ^ sl ^ "'" | |
79 | |
80 val q' = String.concat ["SELECT COUNT(*) FROM information_schema.columns WHERE table_name = '", | |
81 sl, | |
82 "' AND (", | |
83 String.concatWith " OR " | |
84 (map (fn (x, t) => | |
85 String.concat ["(column_name = 'uw_", | |
86 CharVector.map | |
87 Char.toLower (ident x), | |
88 "' AND data_type = '", | |
89 p_sql_type_base t, | |
90 "'", | |
91 if checkNullable then | |
92 (" AND is_nullable = '" | |
93 ^ (if isNotNull t then | |
94 "NO" | |
95 else | |
96 "YES") | |
97 ^ "'") | |
98 else | |
99 "", | |
100 ")"]) xts), | |
101 ")"] | |
102 | |
103 val q'' = String.concat ["SELECT COUNT(*) FROM information_schema.columns WHERE table_name = '", | |
104 sl, | |
105 "' AND column_name LIKE 'uw_%'"] | |
106 in | |
107 box [string "if (mysql_query(conn->conn, \"", | |
108 string q, | |
109 string "\")) {", | |
110 newline, | |
111 box [string "mysql_close(conn->conn);", | |
112 newline, | |
113 string "uw_error(ctx, FATAL, \"Query failed:\\n", | |
114 string q, | |
115 string "\");", | |
116 newline], | |
117 string "}", | |
118 newline, | |
119 newline, | |
120 | |
121 string "if ((res = mysql_store_result(conn->conn)) == NULL) {", | |
122 newline, | |
123 box [string "mysql_free_result(res);", | |
124 newline, | |
125 string "mysql_close(conn->conn);", | |
126 newline, | |
127 string "uw_error(ctx, FATAL, \"Result store failed:\\n", | |
128 string q, | |
129 string "\");", | |
130 newline], | |
131 string "}", | |
132 newline, | |
133 newline, | |
134 | |
135 string "if (mysql_num_fields(res) != 1) {", | |
136 newline, | |
137 box [string "mysql_free_result(res);", | |
138 newline, | |
139 string "mysql_close(conn->conn);", | |
140 newline, | |
141 string "uw_error(ctx, FATAL, \"Bad column count:\\n", | |
142 string q, | |
143 string "\");", | |
144 newline], | |
145 string "}", | |
146 newline, | |
147 newline, | |
148 | |
149 string "if ((row = mysql_fetch_row(res)) == NULL) {", | |
150 newline, | |
151 box [string "mysql_free_result(res);", | |
152 newline, | |
153 string "mysql_close(conn->conn);", | |
154 newline, | |
155 string "uw_error(ctx, FATAL, \"Row fetch failed:\\n", | |
156 string q, | |
157 string "\");", | |
158 newline], | |
159 string "}", | |
160 newline, | |
161 newline, | |
162 | |
163 string "if (strcmp(row[0], \"1\")) {", | |
164 newline, | |
165 box [string "mysql_free_result(res);", | |
166 newline, | |
167 string "mysql_close(conn->conn);", | |
168 newline, | |
169 string "uw_error(ctx, FATAL, \"Table '", | |
170 string s, | |
171 string "' does not exist.\");", | |
172 newline], | |
173 string "}", | |
174 newline, | |
175 newline, | |
176 string "mysql_free_result(res);", | |
177 newline, | |
178 newline, | |
179 | |
180 string "if (mysql_query(conn->conn, \"", | |
181 string q', | |
182 string "\")) {", | |
183 newline, | |
184 box [string "mysql_close(conn->conn);", | |
185 newline, | |
186 string "uw_error(ctx, FATAL, \"Query failed:\\n", | |
187 string q', | |
188 string "\");", | |
189 newline], | |
190 string "}", | |
191 newline, | |
192 newline, | |
193 | |
194 string "if ((res = mysql_store_result(conn->conn)) == NULL) {", | |
195 newline, | |
196 box [string "mysql_free_result(res);", | |
197 newline, | |
198 string "mysql_close(conn->conn);", | |
199 newline, | |
200 string "uw_error(ctx, FATAL, \"Result store failed:\\n", | |
201 string q', | |
202 string "\");", | |
203 newline], | |
204 string "}", | |
205 newline, | |
206 newline, | |
207 | |
208 string "if (mysql_num_fields(res) != 1) {", | |
209 newline, | |
210 box [string "mysql_free_result(res);", | |
211 newline, | |
212 string "mysql_close(conn->conn);", | |
213 newline, | |
214 string "uw_error(ctx, FATAL, \"Bad column count:\\n", | |
215 string q', | |
216 string "\");", | |
217 newline], | |
218 string "}", | |
219 newline, | |
220 newline, | |
221 | |
222 string "if ((row = mysql_fetch_row(res)) == NULL) {", | |
223 newline, | |
224 box [string "mysql_free_result(res);", | |
225 newline, | |
226 string "mysql_close(conn->conn);", | |
227 newline, | |
228 string "uw_error(ctx, FATAL, \"Row fetch failed:\\n", | |
229 string q', | |
230 string "\");", | |
231 newline], | |
232 string "}", | |
233 newline, | |
234 newline, | |
235 | |
236 string "if (strcmp(row[0], \"", | |
237 string (Int.toString (length xts)), | |
238 string "\")) {", | |
239 newline, | |
240 box [string "mysql_free_result(res);", | |
241 newline, | |
242 string "mysql_close(conn->conn);", | |
243 newline, | |
244 string "uw_error(ctx, FATAL, \"Table '", | |
245 string s, | |
246 string "' has the wrong column types.\");", | |
247 newline], | |
248 string "}", | |
249 newline, | |
250 newline, | |
251 string "mysql_free_result(res);", | |
252 newline, | |
253 newline, | |
254 | |
255 string "if (mysql_query(conn->conn, \"", | |
256 string q'', | |
257 string "\")) {", | |
258 newline, | |
259 box [string "mysql_close(conn->conn);", | |
260 newline, | |
261 string "uw_error(ctx, FATAL, \"Query failed:\\n", | |
262 string q'', | |
263 string "\");", | |
264 newline], | |
265 string "}", | |
266 newline, | |
267 newline, | |
268 | |
269 string "if ((res = mysql_store_result(conn->conn)) == NULL) {", | |
270 newline, | |
271 box [string "mysql_free_result(res);", | |
272 newline, | |
273 string "mysql_close(conn->conn);", | |
274 newline, | |
275 string "uw_error(ctx, FATAL, \"Result store failed:\\n", | |
276 string q'', | |
277 string "\");", | |
278 newline], | |
279 string "}", | |
280 newline, | |
281 newline, | |
282 | |
283 string "if (mysql_num_fields(res) != 1) {", | |
284 newline, | |
285 box [string "mysql_free_result(res);", | |
286 newline, | |
287 string "mysql_close(conn->conn);", | |
288 newline, | |
289 string "uw_error(ctx, FATAL, \"Bad column count:\\n", | |
290 string q'', | |
291 string "\");", | |
292 newline], | |
293 string "}", | |
294 newline, | |
295 newline, | |
296 | |
297 string "if ((row = mysql_fetch_row(res)) == NULL) {", | |
298 newline, | |
299 box [string "mysql_free_result(res);", | |
300 newline, | |
301 string "mysql_close(conn->conn);", | |
302 newline, | |
303 string "uw_error(ctx, FATAL, \"Row fetch failed:\\n", | |
304 string q'', | |
305 string "\");", | |
306 newline], | |
307 string "}", | |
308 newline, | |
309 newline, | |
310 | |
311 string "if (strcmp(row[0], \"", | |
312 string (Int.toString (length xts)), | |
313 string "\")) {", | |
314 newline, | |
315 box [string "mysql_free_result(res);", | |
316 newline, | |
317 string "mysql_close(conn->conn);", | |
318 newline, | |
319 string "uw_error(ctx, FATAL, \"Table '", | |
320 string s, | |
321 string "' has extra columns.\");", | |
322 newline], | |
323 string "}", | |
324 newline, | |
325 newline, | |
326 string "mysql_free_result(res);", | |
327 newline] | |
328 end | |
329 | |
58 fun init {dbstring, prepared = ss, tables, views, sequences} = | 330 fun init {dbstring, prepared = ss, tables, views, sequences} = |
59 let | 331 let |
60 val host = ref NONE | 332 val host = ref NONE |
61 val user = ref NONE | 333 val user = ref NONE |
62 val passwd = ref NONE | 334 val passwd = ref NONE |
100 ss], | 372 ss], |
101 string "} uw_conn;", | 373 string "} uw_conn;", |
102 newline, | 374 newline, |
103 newline, | 375 newline, |
104 | 376 |
377 string "void uw_client_init(void) {", | |
378 newline, | |
379 box [string "if (mysql_library_init(0, NULL, NULL)) {", | |
380 newline, | |
381 box [string "fprintf(stderr, \"Could not initialize MySQL library\\n\");", | |
382 newline, | |
383 string "exit(1);", | |
384 newline], | |
385 string "}", | |
386 newline], | |
387 string "}", | |
388 newline, | |
389 newline, | |
390 | |
105 if #persistent (currentProtocol ()) then | 391 if #persistent (currentProtocol ()) then |
106 box [string "static void uw_db_prepare(uw_context ctx) {", | 392 box [string "static void uw_db_validate(uw_context ctx) {", |
393 newline, | |
394 string "uw_conn *conn = uw_get_db(ctx);", | |
395 newline, | |
396 string "MYSQL_RES *res;", | |
397 newline, | |
398 string "MYSQL_ROW row;", | |
399 newline, | |
400 newline, | |
401 p_list_sep newline (checkRel ("tables", true)) tables, | |
402 p_list_sep newline (checkRel ("views", false)) views, | |
403 string "}", | |
404 newline, | |
405 newline, | |
406 | |
407 string "static void uw_db_prepare(uw_context ctx) {", | |
107 newline, | 408 newline, |
108 string "uw_conn *conn = uw_get_db(ctx);", | 409 string "uw_conn *conn = uw_get_db(ctx);", |
109 newline, | 410 newline, |
110 string "MYSQL_STMT *stmt;", | 411 string "MYSQL_STMT *stmt;", |
111 newline, | 412 newline, |
145 string "if (stmt == NULL) {", | 446 string "if (stmt == NULL) {", |
146 newline, | 447 newline, |
147 uhoh false "Out of memory allocating prepared statement" [], | 448 uhoh false "Out of memory allocating prepared statement" [], |
148 string "}", | 449 string "}", |
149 newline, | 450 newline, |
451 string "conn->p", | |
452 string (Int.toString i), | |
453 string " = stmt;", | |
454 newline, | |
150 | 455 |
151 string "if (mysql_stmt_prepare(stmt, \"", | 456 string "if (mysql_stmt_prepare(stmt, \"", |
152 string (String.toString s), | 457 string (String.toString s), |
153 string "\", ", | 458 string "\", ", |
154 string (Int.toString (size s)), | 459 string (Int.toString (size s)), |
160 newline, | 465 newline, |
161 string "msg[1023] = 0;", | 466 string "msg[1023] = 0;", |
162 newline, | 467 newline, |
163 uhoh true "Error preparing statement: %s" ["msg"]], | 468 uhoh true "Error preparing statement: %s" ["msg"]], |
164 string "}", | 469 string "}", |
165 newline, | |
166 string "conn->p", | |
167 string (Int.toString i), | |
168 string " = stmt;", | |
169 newline] | 470 newline] |
170 end) | 471 end) |
171 ss, | 472 ss, |
172 | 473 |
173 string "}"] | 474 string "}"] |
197 case !port of | 498 case !port of |
198 NONE => string "0" | 499 NONE => string "0" |
199 | SOME n => string (Int.toString n), | 500 | SOME n => string (Int.toString n), |
200 string ", ", | 501 string ", ", |
201 stringOf unix_socket, | 502 stringOf unix_socket, |
202 string ", 0)) {", | 503 string ", 0) == NULL) {", |
203 newline, | 504 newline, |
204 box [string "char msg[1024];", | 505 box [string "char msg[1024];", |
205 newline, | 506 newline, |
206 string "strncpy(msg, mysql_error(mysql), 1024);", | 507 string "strncpy(msg, mysql_error(mysql), 1024);", |
207 newline, | 508 newline, |
212 string "uw_error(ctx, BOUNDED_RETRY, ", | 513 string "uw_error(ctx, BOUNDED_RETRY, ", |
213 string "\"Connection to MySQL server failed: %s\", msg);"], | 514 string "\"Connection to MySQL server failed: %s\", msg);"], |
214 newline, | 515 newline, |
215 string "}", | 516 string "}", |
216 newline, | 517 newline, |
217 string "conn = calloc(1, sizeof(conn));", | 518 string "conn = calloc(1, sizeof(uw_conn));", |
218 newline, | 519 newline, |
219 string "conn->conn = mysql;", | 520 string "conn->conn = mysql;", |
220 newline, | 521 newline, |
221 string "uw_set_db(ctx, conn);", | 522 string "uw_set_db(ctx, conn);", |
222 newline, | 523 newline, |
469 end) cols, | 770 end) cols, |
470 newline, | 771 newline, |
471 | 772 |
472 string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"", | 773 string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"", |
473 string (ErrorMsg.spanToString loc), | 774 string (ErrorMsg.spanToString loc), |
474 string ": Error executing query\");", | 775 string ": Error executing query: %s\", mysql_error(conn->conn));", |
475 newline, | 776 newline, |
476 newline, | 777 newline, |
477 | 778 |
478 string "if (mysql_stmt_store_result(stmt)) uw_error(ctx, FATAL, \"", | 779 string "if (mysql_stmt_store_result(stmt)) uw_error(ctx, FATAL, \"", |
479 string (ErrorMsg.spanToString loc), | 780 string (ErrorMsg.spanToString loc), |
480 string ": Error storing query result\");", | 781 string ": Error storing query result: %s\", mysql_error(conn->conn));", |
481 newline, | 782 newline, |
482 newline, | 783 newline, |
483 | 784 |
484 string "if (mysql_stmt_bind_result(stmt, out)) uw_error(ctx, FATAL, \"", | 785 string "if (mysql_stmt_bind_result(stmt, out)) uw_error(ctx, FATAL, \"", |
485 string (ErrorMsg.spanToString loc), | 786 string (ErrorMsg.spanToString loc), |
486 string ": Error binding query result\");", | 787 string ": Error binding query result: %s\", mysql_error(conn->conn));", |
487 newline, | 788 newline, |
488 newline, | 789 newline, |
489 | 790 |
490 string "uw_end_region(ctx);", | 791 string "uw_end_region(ctx);", |
491 newline, | 792 newline, |
494 doCols p_getcol, | 795 doCols p_getcol, |
495 string "}", | 796 string "}", |
496 newline, | 797 newline, |
497 newline, | 798 newline, |
498 | 799 |
499 string "if (r != MYSQL_NO_DATA) uw_error(ctx, FATAL, \"", | 800 string "if (r == 1) uw_error(ctx, FATAL, \"", |
500 string (ErrorMsg.spanToString loc), | 801 string (ErrorMsg.spanToString loc), |
501 string ": query result fetching failed\");", | 802 string ": query result fetching failed (%d): %s\", r, mysql_error(conn->conn));", |
502 newline] | 803 newline] |
503 | 804 |
504 fun query {loc, cols, doCols} = | 805 fun query {loc, cols, doCols} = |
505 box [string "uw_conn *conn = uw_get_db(ctx);", | 806 box [string "uw_conn *conn = uw_get_db(ctx);", |
506 newline, | 807 newline, |
512 newline, | 813 newline, |
513 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);", | 814 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);", |
514 newline, | 815 newline, |
515 string "if (mysql_stmt_prepare(stmt, query, strlen(query))) uw_error(ctx, FATAL, \"", | 816 string "if (mysql_stmt_prepare(stmt, query, strlen(query))) uw_error(ctx, FATAL, \"", |
516 string (ErrorMsg.spanToString loc), | 817 string (ErrorMsg.spanToString loc), |
517 string "\");", | 818 string ": error preparing statement: %s\", mysql_error(conn->conn));", |
518 newline, | 819 newline, |
519 newline, | 820 newline, |
520 | 821 |
521 p_list_sepi (box []) (fn i => fn t => | 822 p_list_sepi (box []) (fn i => fn t => |
522 let | 823 let |
758 fun dml _ = box [] | 1059 fun dml _ = box [] |
759 fun dmlPrepared _ = box [] | 1060 fun dmlPrepared _ = box [] |
760 fun nextval _ = box [] | 1061 fun nextval _ = box [] |
761 fun nextvalPrepared _ = box [] | 1062 fun nextvalPrepared _ = box [] |
762 | 1063 |
1064 fun sqlifyString s = "CAST('" ^ String.translate (fn #"'" => "\\'" | |
1065 | #"\\" => "\\\\" | |
1066 | ch => | |
1067 if Char.isPrint ch then | |
1068 str ch | |
1069 else | |
1070 (ErrorMsg.error | |
1071 "Non-printing character found in SQL string literal"; | |
1072 "")) | |
1073 (String.toString s) ^ "' AS longtext)" | |
1074 | |
1075 fun p_cast (s, t) = "CAST(" ^ s ^ " AS " ^ p_sql_type t ^ ")" | |
1076 | |
1077 fun p_blank _ = "?" | |
1078 | |
763 val () = addDbms {name = "mysql", | 1079 val () = addDbms {name = "mysql", |
764 header = "mysql/mysql.h", | 1080 header = "mysql/mysql.h", |
765 link = "-lmysqlclient", | 1081 link = "-lmysqlclient", |
766 global_init = box [string "void uw_client_init() {", | |
767 newline, | |
768 box [string "if (mysql_library_init(0, NULL, NULL)) {", | |
769 newline, | |
770 box [string "fprintf(stderr, \"Could not initialize MySQL library\\n\");", | |
771 newline, | |
772 string "exit(1);", | |
773 newline], | |
774 string "}", | |
775 newline], | |
776 string "}", | |
777 newline], | |
778 init = init, | 1082 init = init, |
779 p_sql_type = p_sql_type, | 1083 p_sql_type = p_sql_type, |
780 query = query, | 1084 query = query, |
781 queryPrepared = queryPrepared, | 1085 queryPrepared = queryPrepared, |
782 dml = dml, | 1086 dml = dml, |
783 dmlPrepared = dmlPrepared, | 1087 dmlPrepared = dmlPrepared, |
784 nextval = nextval, | 1088 nextval = nextval, |
785 nextvalPrepared = nextvalPrepared} | 1089 nextvalPrepared = nextvalPrepared, |
1090 sqlifyString = sqlifyString, | |
1091 p_cast = p_cast, | |
1092 p_blank = p_blank, | |
1093 supportsDeleteAs = false} | |
786 | 1094 |
787 end | 1095 end |