Mercurial > urweb
comparison src/mysql.sml @ 873:41971801b62d
MySQL query gets up to C linking
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Sun, 12 Jul 2009 13:16:05 -0400 |
parents | 9654bce27cff |
children | 3c7b48040dcf |
comparison
equal
deleted
inserted
replaced
872:9654bce27cff | 873:41971801b62d |
---|---|
28 structure MySQL :> MYSQL = struct | 28 structure MySQL :> MYSQL = struct |
29 | 29 |
30 open Settings | 30 open Settings |
31 open Print.PD | 31 open Print.PD |
32 open Print | 32 open Print |
33 | |
34 fun p_sql_type t = | |
35 case t of | |
36 Int => "bigint" | |
37 | Float => "double" | |
38 | String => "longtext" | |
39 | Bool => "bool" | |
40 | Time => "timestamp" | |
41 | Blob => "longblob" | |
42 | Channel => "bigint" | |
43 | Client => "int" | |
44 | Nullable t => p_sql_type t | |
45 | |
46 fun p_buffer_type t = | |
47 case t of | |
48 Int => "MYSQL_TYPE_LONGLONG" | |
49 | Float => "MYSQL_TYPE_DOUBLE" | |
50 | String => "MYSQL_TYPE_STRING" | |
51 | Bool => "MYSQL_TYPE_LONG" | |
52 | Time => "MYSQL_TYPE_TIME" | |
53 | Blob => "MYSQL_TYPE_BLOB" | |
54 | Channel => "MYSQL_TYPE_LONGLONG" | |
55 | Client => "MYSQL_TYPE_LONG" | |
56 | Nullable t => p_buffer_type t | |
33 | 57 |
34 fun init {dbstring, prepared = ss, tables, views, sequences} = | 58 fun init {dbstring, prepared = ss, tables, views, sequences} = |
35 let | 59 let |
36 val host = ref NONE | 60 val host = ref NONE |
37 val user = ref NONE | 61 val user = ref NONE |
136 newline, | 160 newline, |
137 string "msg[1023] = 0;", | 161 string "msg[1023] = 0;", |
138 newline, | 162 newline, |
139 uhoh true "Error preparing statement: %s" ["msg"]], | 163 uhoh true "Error preparing statement: %s" ["msg"]], |
140 string "}", | 164 string "}", |
165 newline, | |
166 string "conn->p", | |
167 string (Int.toString i), | |
168 string " = stmt;", | |
141 newline] | 169 newline] |
142 end) | 170 end) |
143 ss, | 171 ss, |
144 | 172 |
145 string "}"] | 173 string "}"] |
251 string "}", | 279 string "}", |
252 newline, | 280 newline, |
253 newline] | 281 newline] |
254 end | 282 end |
255 | 283 |
256 fun query _ = raise Fail "MySQL query" | 284 fun p_getcol {wontLeakStrings = _, col = i, typ = t} = |
257 fun queryPrepared _ = raise Fail "MySQL queryPrepared" | 285 let |
258 fun dml _ = raise Fail "MySQL dml" | 286 fun getter t = |
259 fun dmlPrepared _ = raise Fail "MySQL dmlPrepared" | 287 case t of |
260 fun nextval _ = raise Fail "MySQL nextval" | 288 String => box [string "({", |
261 fun nextvalPrepared _ = raise Fail "MySQL nextvalPrepared" | 289 newline, |
290 string "uw_Basis_string s = uw_malloc(ctx, length", | |
291 string (Int.toString i), | |
292 string " + 1);", | |
293 newline, | |
294 string "out[", | |
295 string (Int.toString i), | |
296 string "].buffer = s;", | |
297 newline, | |
298 string "out[", | |
299 string (Int.toString i), | |
300 string "].buffer_length = length", | |
301 string (Int.toString i), | |
302 string " + 1;", | |
303 newline, | |
304 string "mysql_stmt_fetch_column(stmt, &out[", | |
305 string (Int.toString i), | |
306 string "], ", | |
307 string (Int.toString i), | |
308 string ", 0);", | |
309 newline, | |
310 string "s[length", | |
311 string (Int.toString i), | |
312 string "] = 0;", | |
313 newline, | |
314 string "s;", | |
315 newline, | |
316 string "})"] | |
317 | Blob => box [string "({", | |
318 newline, | |
319 string "uw_Basis_blob b = {length", | |
320 string (Int.toString i), | |
321 string ", uw_malloc(ctx, length", | |
322 string (Int.toString i), | |
323 string ")};", | |
324 newline, | |
325 string "out[", | |
326 string (Int.toString i), | |
327 string "].buffer = b.data;", | |
328 newline, | |
329 string "out[", | |
330 string (Int.toString i), | |
331 string "].buffer_length = length", | |
332 string (Int.toString i), | |
333 string ";", | |
334 newline, | |
335 string "mysql_stmt_fetch_column(stmt, &out[", | |
336 string (Int.toString i), | |
337 string "], ", | |
338 string (Int.toString i), | |
339 string ", 0);", | |
340 newline, | |
341 string "b;", | |
342 newline, | |
343 string "})"] | |
344 | Time => box [string "({", | |
345 string "MYSQL_TIME *mt = buffer", | |
346 string (Int.toString i), | |
347 string ";", | |
348 newline, | |
349 newline, | |
350 string "struct tm t = {mt->second, mt->minute, mt->hour, mt->day, mt->month, mt->year, 0, 0, -1};", | |
351 newline, | |
352 string "mktime(&tm);", | |
353 newline, | |
354 string "})"] | |
355 | _ => box [string "buffer", | |
356 string (Int.toString i)] | |
357 in | |
358 case t of | |
359 Nullable t => box [string "(is_null", | |
360 string (Int.toString i), | |
361 string " ? NULL : ", | |
362 case t of | |
363 String => getter t | |
364 | _ => box [string "({", | |
365 newline, | |
366 string (p_sql_ctype t), | |
367 space, | |
368 string "*tmp = uw_malloc(ctx, sizeof(", | |
369 string (p_sql_ctype t), | |
370 string "));", | |
371 newline, | |
372 string "*tmp = ", | |
373 getter t, | |
374 string ";", | |
375 newline, | |
376 string "tmp;", | |
377 newline, | |
378 string "})"], | |
379 string ")"] | |
380 | _ => box [string "(is_null", | |
381 string (Int.toString i), | |
382 string " ? ", | |
383 box [string "({", | |
384 string (p_sql_ctype t), | |
385 space, | |
386 string "tmp;", | |
387 newline, | |
388 string "uw_error(ctx, FATAL, \"Unexpectedly NULL field #", | |
389 string (Int.toString i), | |
390 string "\");", | |
391 newline, | |
392 string "tmp;", | |
393 newline, | |
394 string "})"], | |
395 string " : ", | |
396 getter t, | |
397 string ")"] | |
398 end | |
399 | |
400 fun queryCommon {loc, query, cols, doCols} = | |
401 box [string "int n, r;", | |
402 newline, | |
403 string "MYSQL_BIND out[", | |
404 string (Int.toString (length cols)), | |
405 string "];", | |
406 newline, | |
407 p_list_sepi (box []) (fn i => fn t => | |
408 let | |
409 fun buffers t = | |
410 case t of | |
411 String => box [string "unsigned long length", | |
412 string (Int.toString i), | |
413 string ";", | |
414 newline] | |
415 | Blob => box [string "unsigned long length", | |
416 string (Int.toString i), | |
417 string ";", | |
418 newline] | |
419 | _ => box [string (p_sql_ctype t), | |
420 space, | |
421 string "buffer", | |
422 string (Int.toString i), | |
423 string ";", | |
424 newline] | |
425 in | |
426 box [string "my_bool is_null", | |
427 string (Int.toString i), | |
428 string ";", | |
429 newline, | |
430 case t of | |
431 Nullable t => buffers t | |
432 | _ => buffers t, | |
433 newline] | |
434 end) cols, | |
435 newline, | |
436 | |
437 string "memset(out, 0, sizeof out);", | |
438 newline, | |
439 p_list_sepi (box []) (fn i => fn t => | |
440 let | |
441 fun buffers t = | |
442 case t of | |
443 String => box [] | |
444 | Blob => box [] | |
445 | _ => box [string "out[", | |
446 string (Int.toString i), | |
447 string "].buffer = &buffer", | |
448 string (Int.toString i), | |
449 string ";", | |
450 newline] | |
451 in | |
452 box [string "out[", | |
453 string (Int.toString i), | |
454 string "].buffer_type = ", | |
455 string (p_buffer_type t), | |
456 string ";", | |
457 newline, | |
458 string "out[", | |
459 string (Int.toString i), | |
460 string "].is_null = &is_null", | |
461 string (Int.toString i), | |
462 string ";", | |
463 newline, | |
464 | |
465 case t of | |
466 Nullable t => buffers t | |
467 | _ => buffers t, | |
468 newline] | |
469 end) cols, | |
470 newline, | |
471 | |
472 string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"", | |
473 string (ErrorMsg.spanToString loc), | |
474 string ": Error executing query\");", | |
475 newline, | |
476 newline, | |
477 | |
478 string "if (mysql_stmt_store_result(stmt)) uw_error(ctx, FATAL, \"", | |
479 string (ErrorMsg.spanToString loc), | |
480 string ": Error storing query result\");", | |
481 newline, | |
482 newline, | |
483 | |
484 string "if (mysql_stmt_bind_result(stmt, out)) uw_error(ctx, FATAL, \"", | |
485 string (ErrorMsg.spanToString loc), | |
486 string ": Error binding query result\");", | |
487 newline, | |
488 newline, | |
489 | |
490 string "uw_end_region(ctx);", | |
491 newline, | |
492 string "while ((r = mysql_stmt_fetch(stmt)) == 0) {", | |
493 newline, | |
494 doCols p_getcol, | |
495 string "}", | |
496 newline, | |
497 newline, | |
498 | |
499 string "if (r != MYSQL_NO_DATA) uw_error(ctx, FATAL, \"", | |
500 string (ErrorMsg.spanToString loc), | |
501 string ": query result fetching failed\");", | |
502 newline] | |
503 | |
504 fun query {loc, cols, doCols} = | |
505 box [string "uw_conn *conn = uw_get_db(ctx);", | |
506 newline, | |
507 string "MYSQL_stmt *stmt = mysql_stmt_init(conn->conn);", | |
508 newline, | |
509 string "if (stmt == NULL) uw_error(ctx, \"", | |
510 string (ErrorMsg.spanToString loc), | |
511 string ": can't allocate temporary prepared statement\");", | |
512 newline, | |
513 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);", | |
514 newline, | |
515 string "if (mysql_stmt_prepare(stmt, query, strlen(query))) uw_error(ctx, FATAL, \"", | |
516 string (ErrorMsg.spanToString loc), | |
517 string "\");", | |
518 newline, | |
519 newline, | |
520 | |
521 p_list_sepi (box []) (fn i => fn t => | |
522 let | |
523 fun buffers t = | |
524 case t of | |
525 String => box [] | |
526 | Blob => box [] | |
527 | _ => box [string "out[", | |
528 string (Int.toString i), | |
529 string "].buffer = &buffer", | |
530 string (Int.toString i), | |
531 string ";", | |
532 newline] | |
533 in | |
534 box [string "in[", | |
535 string (Int.toString i), | |
536 string "].buffer_type = ", | |
537 string (p_buffer_type t), | |
538 string ";", | |
539 newline, | |
540 | |
541 case t of | |
542 Nullable t => box [string "in[", | |
543 string (Int.toString i), | |
544 string "].is_null = &is_null", | |
545 string (Int.toString i), | |
546 string ";", | |
547 newline, | |
548 buffers t] | |
549 | _ => buffers t, | |
550 newline] | |
551 end) cols, | |
552 newline, | |
553 | |
554 queryCommon {loc = loc, cols = cols, doCols = doCols, query = string "query"}, | |
555 | |
556 string "uw_pop_cleanup(ctx);", | |
557 newline] | |
558 | |
559 fun p_ensql t e = | |
560 case t of | |
561 Int => box [string "uw_Basis_attrifyInt(ctx, ", e, string ")"] | |
562 | Float => box [string "uw_Basis_attrifyFloat(ctx, ", e, string ")"] | |
563 | String => e | |
564 | Bool => box [string "(", e, string " ? \"TRUE\" : \"FALSE\")"] | |
565 | Time => box [string "uw_Basis_attrifyTime(ctx, ", e, string ")"] | |
566 | Blob => box [e, string ".data"] | |
567 | Channel => box [string "uw_Basis_attrifyChannel(ctx, ", e, string ")"] | |
568 | Client => box [string "uw_Basis_attrifyClient(ctx, ", e, string ")"] | |
569 | Nullable String => e | |
570 | Nullable t => box [string "(", | |
571 e, | |
572 string " == NULL ? NULL : ", | |
573 p_ensql t (box [string "(*", e, string ")"]), | |
574 string ")"] | |
575 | |
576 fun queryPrepared {loc, id, query, inputs, cols, doCols} = | |
577 box [string "uw_conn *conn = uw_get_db(ctx);", | |
578 newline, | |
579 string "MYSQL_BIND in[", | |
580 string (Int.toString (length inputs)), | |
581 string "];", | |
582 newline, | |
583 p_list_sepi (box []) (fn i => fn t => | |
584 let | |
585 fun buffers t = | |
586 case t of | |
587 String => box [string "unsigned long in_length", | |
588 string (Int.toString i), | |
589 string ";", | |
590 newline] | |
591 | Blob => box [string "unsigned long in_length", | |
592 string (Int.toString i), | |
593 string ";", | |
594 newline] | |
595 | Time => box [string (p_sql_ctype t), | |
596 space, | |
597 string "in_buffer", | |
598 string (Int.toString i), | |
599 string ";", | |
600 newline] | |
601 | _ => box [] | |
602 in | |
603 box [case t of | |
604 Nullable t => box [string "my_bool in_is_null", | |
605 string (Int.toString i), | |
606 string ";", | |
607 newline, | |
608 buffers t] | |
609 | _ => buffers t, | |
610 newline] | |
611 end) inputs, | |
612 string "MYSQL_STMT *stmt = conn->p", | |
613 string (Int.toString id), | |
614 string ";", | |
615 newline, | |
616 newline, | |
617 | |
618 string "memset(in, 0, sizeof in);", | |
619 newline, | |
620 p_list_sepi (box []) (fn i => fn t => | |
621 let | |
622 fun buffers t = | |
623 case t of | |
624 String => box [string "in[", | |
625 string (Int.toString i), | |
626 string "].buffer = arg", | |
627 string (Int.toString (i + 1)), | |
628 string ";", | |
629 newline, | |
630 string "in_length", | |
631 string (Int.toString i), | |
632 string "= in[", | |
633 string (Int.toString i), | |
634 string "].buffer_length = strlen(arg", | |
635 string (Int.toString (i + 1)), | |
636 string ");", | |
637 newline, | |
638 string "in[", | |
639 string (Int.toString i), | |
640 string "].length = &in_length", | |
641 string (Int.toString i), | |
642 string ";", | |
643 newline] | |
644 | Blob => box [string "in[", | |
645 string (Int.toString i), | |
646 string "].buffer = arg", | |
647 string (Int.toString (i + 1)), | |
648 string ".data;", | |
649 newline, | |
650 string "in_length", | |
651 string (Int.toString i), | |
652 string "= in[", | |
653 string (Int.toString i), | |
654 string "].buffer_length = arg", | |
655 string (Int.toString (i + 1)), | |
656 string ".size;", | |
657 newline, | |
658 string "in[", | |
659 string (Int.toString i), | |
660 string "].length = &in_length", | |
661 string (Int.toString i), | |
662 string ";", | |
663 newline] | |
664 | Time => | |
665 let | |
666 fun oneField dst src = | |
667 box [string "in_buffer", | |
668 string (Int.toString i), | |
669 string ".", | |
670 string dst, | |
671 string " = tms.tm_", | |
672 string src, | |
673 string ";", | |
674 newline] | |
675 in | |
676 box [string "({", | |
677 newline, | |
678 string "struct tm tms;", | |
679 newline, | |
680 string "if (localtime_r(&arg", | |
681 string (Int.toString (i + 1)), | |
682 string ", &tm) == NULL) uw_error(\"", | |
683 string (ErrorMsg.spanToString loc), | |
684 string ": error converting to MySQL time\");", | |
685 newline, | |
686 oneField "year" "year", | |
687 oneField "month" "mon", | |
688 oneField "day" "mday", | |
689 oneField "hour" "hour", | |
690 oneField "minute" "min", | |
691 oneField "second" "sec", | |
692 newline, | |
693 string "in[", | |
694 string (Int.toString i), | |
695 string "].buffer = &in_buffer", | |
696 string (Int.toString i), | |
697 string ";", | |
698 newline] | |
699 end | |
700 | |
701 | _ => box [string "in[", | |
702 string (Int.toString i), | |
703 string "].buffer = &arg", | |
704 string (Int.toString (i + 1)), | |
705 string ";", | |
706 newline] | |
707 in | |
708 box [string "in[", | |
709 string (Int.toString i), | |
710 string "].buffer_type = ", | |
711 string (p_buffer_type t), | |
712 string ";", | |
713 newline, | |
714 | |
715 case t of | |
716 Nullable t => box [string "in[", | |
717 string (Int.toString i), | |
718 string "].is_null = &in_is_null", | |
719 string (Int.toString i), | |
720 string ";", | |
721 newline, | |
722 string "if (arg", | |
723 string (Int.toString (i + 1)), | |
724 string " == NULL) {", | |
725 newline, | |
726 box [string "in_is_null", | |
727 string (Int.toString i), | |
728 string " = 1;", | |
729 newline], | |
730 string "} else {", | |
731 box [case t of | |
732 String => box [] | |
733 | _ => | |
734 box [string (p_sql_ctype t), | |
735 space, | |
736 string "arg", | |
737 string (Int.toString (i + 1)), | |
738 string " = *arg", | |
739 string (Int.toString (i + 1)), | |
740 string ";", | |
741 newline], | |
742 string "in_is_null", | |
743 string (Int.toString i), | |
744 string " = 0;", | |
745 newline, | |
746 buffers t, | |
747 newline]] | |
748 | |
749 | _ => buffers t, | |
750 newline] | |
751 end) inputs, | |
752 newline, | |
753 | |
754 queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"", | |
755 string (String.toString query), | |
756 string "\""]}] | |
757 | |
758 fun dml _ = box [] | |
759 fun dmlPrepared _ = box [] | |
760 fun nextval _ = box [] | |
761 fun nextvalPrepared _ = box [] | |
262 | 762 |
263 val () = addDbms {name = "mysql", | 763 val () = addDbms {name = "mysql", |
264 header = "mysql/mysql.h", | 764 header = "mysql/mysql.h", |
265 link = "-lmysqlclient", | 765 link = "-lmysqlclient", |
266 global_init = box [string "void uw_client_init() {", | 766 global_init = box [string "void uw_client_init() {", |
274 string "}", | 774 string "}", |
275 newline], | 775 newline], |
276 string "}", | 776 string "}", |
277 newline], | 777 newline], |
278 init = init, | 778 init = init, |
779 p_sql_type = p_sql_type, | |
279 query = query, | 780 query = query, |
280 queryPrepared = queryPrepared, | 781 queryPrepared = queryPrepared, |
281 dml = dml, | 782 dml = dml, |
282 dmlPrepared = dmlPrepared, | 783 dmlPrepared = dmlPrepared, |
283 nextval = nextval, | 784 nextval = nextval, |