annotate src/mysql.sml @ 890:034eeb099564

Blobs tested in MySQL and SQLite
author Adam Chlipala <adamc@hcoop.net>
date Sat, 18 Jul 2009 10:27:32 -0400
parents bcad392e288e
children 6a284a3519ba
rev   line source
adamc@879 1 (* Copyright (c) 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@874 58 fun p_sql_type_base t =
adamc@874 59 case t of
adamc@874 60 Int => "bigint"
adamc@874 61 | Float => "double"
adamc@874 62 | String => "longtext"
adamc@874 63 | Bool => "tinyint"
adamc@874 64 | Time => "timestamp"
adamc@874 65 | Blob => "longblob"
adamc@874 66 | Channel => "bigint"
adamc@874 67 | Client => "int"
adamc@874 68 | Nullable t => p_sql_type_base t
adamc@874 69
adamc@874 70 val ident = String.translate (fn #"'" => "PRIME"
adamc@874 71 | ch => str ch)
adamc@874 72
adamc@874 73 fun checkRel (table, checkNullable) (s, xts) =
adamc@874 74 let
adamc@874 75 val sl = CharVector.map Char.toLower s
adamc@874 76
adamc@874 77 val q = "SELECT COUNT(*) FROM information_schema." ^ table ^ " WHERE table_name = '"
adamc@874 78 ^ sl ^ "'"
adamc@874 79
adamc@874 80 val q' = String.concat ["SELECT COUNT(*) FROM information_schema.columns WHERE table_name = '",
adamc@874 81 sl,
adamc@874 82 "' AND (",
adamc@874 83 String.concatWith " OR "
adamc@874 84 (map (fn (x, t) =>
adamc@874 85 String.concat ["(column_name = 'uw_",
adamc@874 86 CharVector.map
adamc@874 87 Char.toLower (ident x),
adamc@874 88 "' AND data_type = '",
adamc@874 89 p_sql_type_base t,
adamc@874 90 "'",
adamc@874 91 if checkNullable then
adamc@874 92 (" AND is_nullable = '"
adamc@874 93 ^ (if isNotNull t then
adamc@874 94 "NO"
adamc@874 95 else
adamc@874 96 "YES")
adamc@874 97 ^ "'")
adamc@874 98 else
adamc@874 99 "",
adamc@874 100 ")"]) xts),
adamc@874 101 ")"]
adamc@874 102
adamc@874 103 val q'' = String.concat ["SELECT COUNT(*) FROM information_schema.columns WHERE table_name = '",
adamc@874 104 sl,
adamc@874 105 "' AND column_name LIKE 'uw_%'"]
adamc@874 106 in
adamc@874 107 box [string "if (mysql_query(conn->conn, \"",
adamc@874 108 string q,
adamc@874 109 string "\")) {",
adamc@874 110 newline,
adamc@874 111 box [string "mysql_close(conn->conn);",
adamc@874 112 newline,
adamc@874 113 string "uw_error(ctx, FATAL, \"Query failed:\\n",
adamc@874 114 string q,
adamc@874 115 string "\");",
adamc@874 116 newline],
adamc@874 117 string "}",
adamc@874 118 newline,
adamc@874 119 newline,
adamc@874 120
adamc@874 121 string "if ((res = mysql_store_result(conn->conn)) == NULL) {",
adamc@874 122 newline,
adamc@874 123 box [string "mysql_free_result(res);",
adamc@874 124 newline,
adamc@874 125 string "mysql_close(conn->conn);",
adamc@874 126 newline,
adamc@874 127 string "uw_error(ctx, FATAL, \"Result store failed:\\n",
adamc@874 128 string q,
adamc@874 129 string "\");",
adamc@874 130 newline],
adamc@874 131 string "}",
adamc@874 132 newline,
adamc@874 133 newline,
adamc@874 134
adamc@874 135 string "if (mysql_num_fields(res) != 1) {",
adamc@874 136 newline,
adamc@874 137 box [string "mysql_free_result(res);",
adamc@874 138 newline,
adamc@874 139 string "mysql_close(conn->conn);",
adamc@874 140 newline,
adamc@874 141 string "uw_error(ctx, FATAL, \"Bad column count:\\n",
adamc@874 142 string q,
adamc@874 143 string "\");",
adamc@874 144 newline],
adamc@874 145 string "}",
adamc@874 146 newline,
adamc@874 147 newline,
adamc@874 148
adamc@874 149 string "if ((row = mysql_fetch_row(res)) == NULL) {",
adamc@874 150 newline,
adamc@874 151 box [string "mysql_free_result(res);",
adamc@874 152 newline,
adamc@874 153 string "mysql_close(conn->conn);",
adamc@874 154 newline,
adamc@874 155 string "uw_error(ctx, FATAL, \"Row fetch failed:\\n",
adamc@874 156 string q,
adamc@874 157 string "\");",
adamc@874 158 newline],
adamc@874 159 string "}",
adamc@874 160 newline,
adamc@874 161 newline,
adamc@874 162
adamc@874 163 string "if (strcmp(row[0], \"1\")) {",
adamc@874 164 newline,
adamc@874 165 box [string "mysql_free_result(res);",
adamc@874 166 newline,
adamc@874 167 string "mysql_close(conn->conn);",
adamc@874 168 newline,
adamc@874 169 string "uw_error(ctx, FATAL, \"Table '",
adamc@874 170 string s,
adamc@874 171 string "' does not exist.\");",
adamc@874 172 newline],
adamc@874 173 string "}",
adamc@874 174 newline,
adamc@874 175 newline,
adamc@874 176 string "mysql_free_result(res);",
adamc@874 177 newline,
adamc@874 178 newline,
adamc@874 179
adamc@874 180 string "if (mysql_query(conn->conn, \"",
adamc@874 181 string q',
adamc@874 182 string "\")) {",
adamc@874 183 newline,
adamc@874 184 box [string "mysql_close(conn->conn);",
adamc@874 185 newline,
adamc@874 186 string "uw_error(ctx, FATAL, \"Query failed:\\n",
adamc@874 187 string q',
adamc@874 188 string "\");",
adamc@874 189 newline],
adamc@874 190 string "}",
adamc@874 191 newline,
adamc@874 192 newline,
adamc@874 193
adamc@874 194 string "if ((res = mysql_store_result(conn->conn)) == NULL) {",
adamc@874 195 newline,
adamc@874 196 box [string "mysql_free_result(res);",
adamc@874 197 newline,
adamc@874 198 string "mysql_close(conn->conn);",
adamc@874 199 newline,
adamc@874 200 string "uw_error(ctx, FATAL, \"Result store failed:\\n",
adamc@874 201 string q',
adamc@874 202 string "\");",
adamc@874 203 newline],
adamc@874 204 string "}",
adamc@874 205 newline,
adamc@874 206 newline,
adamc@874 207
adamc@874 208 string "if (mysql_num_fields(res) != 1) {",
adamc@874 209 newline,
adamc@874 210 box [string "mysql_free_result(res);",
adamc@874 211 newline,
adamc@874 212 string "mysql_close(conn->conn);",
adamc@874 213 newline,
adamc@874 214 string "uw_error(ctx, FATAL, \"Bad column count:\\n",
adamc@874 215 string q',
adamc@874 216 string "\");",
adamc@874 217 newline],
adamc@874 218 string "}",
adamc@874 219 newline,
adamc@874 220 newline,
adamc@874 221
adamc@874 222 string "if ((row = mysql_fetch_row(res)) == NULL) {",
adamc@874 223 newline,
adamc@874 224 box [string "mysql_free_result(res);",
adamc@874 225 newline,
adamc@874 226 string "mysql_close(conn->conn);",
adamc@874 227 newline,
adamc@874 228 string "uw_error(ctx, FATAL, \"Row fetch failed:\\n",
adamc@874 229 string q',
adamc@874 230 string "\");",
adamc@874 231 newline],
adamc@874 232 string "}",
adamc@874 233 newline,
adamc@874 234 newline,
adamc@874 235
adamc@874 236 string "if (strcmp(row[0], \"",
adamc@874 237 string (Int.toString (length xts)),
adamc@874 238 string "\")) {",
adamc@874 239 newline,
adamc@874 240 box [string "mysql_free_result(res);",
adamc@874 241 newline,
adamc@874 242 string "mysql_close(conn->conn);",
adamc@874 243 newline,
adamc@874 244 string "uw_error(ctx, FATAL, \"Table '",
adamc@874 245 string s,
adamc@874 246 string "' has the wrong column types.\");",
adamc@874 247 newline],
adamc@874 248 string "}",
adamc@874 249 newline,
adamc@874 250 newline,
adamc@874 251 string "mysql_free_result(res);",
adamc@874 252 newline,
adamc@874 253 newline,
adamc@874 254
adamc@874 255 string "if (mysql_query(conn->conn, \"",
adamc@874 256 string q'',
adamc@874 257 string "\")) {",
adamc@874 258 newline,
adamc@874 259 box [string "mysql_close(conn->conn);",
adamc@874 260 newline,
adamc@874 261 string "uw_error(ctx, FATAL, \"Query failed:\\n",
adamc@874 262 string q'',
adamc@874 263 string "\");",
adamc@874 264 newline],
adamc@874 265 string "}",
adamc@874 266 newline,
adamc@874 267 newline,
adamc@874 268
adamc@874 269 string "if ((res = mysql_store_result(conn->conn)) == NULL) {",
adamc@874 270 newline,
adamc@874 271 box [string "mysql_free_result(res);",
adamc@874 272 newline,
adamc@874 273 string "mysql_close(conn->conn);",
adamc@874 274 newline,
adamc@874 275 string "uw_error(ctx, FATAL, \"Result store failed:\\n",
adamc@874 276 string q'',
adamc@874 277 string "\");",
adamc@874 278 newline],
adamc@874 279 string "}",
adamc@874 280 newline,
adamc@874 281 newline,
adamc@874 282
adamc@874 283 string "if (mysql_num_fields(res) != 1) {",
adamc@874 284 newline,
adamc@874 285 box [string "mysql_free_result(res);",
adamc@874 286 newline,
adamc@874 287 string "mysql_close(conn->conn);",
adamc@874 288 newline,
adamc@874 289 string "uw_error(ctx, FATAL, \"Bad column count:\\n",
adamc@874 290 string q'',
adamc@874 291 string "\");",
adamc@874 292 newline],
adamc@874 293 string "}",
adamc@874 294 newline,
adamc@874 295 newline,
adamc@874 296
adamc@874 297 string "if ((row = mysql_fetch_row(res)) == NULL) {",
adamc@874 298 newline,
adamc@874 299 box [string "mysql_free_result(res);",
adamc@874 300 newline,
adamc@874 301 string "mysql_close(conn->conn);",
adamc@874 302 newline,
adamc@874 303 string "uw_error(ctx, FATAL, \"Row fetch failed:\\n",
adamc@874 304 string q'',
adamc@874 305 string "\");",
adamc@874 306 newline],
adamc@874 307 string "}",
adamc@874 308 newline,
adamc@874 309 newline,
adamc@874 310
adamc@874 311 string "if (strcmp(row[0], \"",
adamc@874 312 string (Int.toString (length xts)),
adamc@874 313 string "\")) {",
adamc@874 314 newline,
adamc@874 315 box [string "mysql_free_result(res);",
adamc@874 316 newline,
adamc@874 317 string "mysql_close(conn->conn);",
adamc@874 318 newline,
adamc@874 319 string "uw_error(ctx, FATAL, \"Table '",
adamc@874 320 string s,
adamc@874 321 string "' has extra columns.\");",
adamc@874 322 newline],
adamc@874 323 string "}",
adamc@874 324 newline,
adamc@874 325 newline,
adamc@874 326 string "mysql_free_result(res);",
adamc@874 327 newline]
adamc@874 328 end
adamc@874 329
adamc@872 330 fun init {dbstring, prepared = ss, tables, views, sequences} =
adamc@866 331 let
adamc@866 332 val host = ref NONE
adamc@866 333 val user = ref NONE
adamc@866 334 val passwd = ref NONE
adamc@866 335 val db = ref NONE
adamc@866 336 val port = ref NONE
adamc@866 337 val unix_socket = ref NONE
adamc@866 338
adamc@866 339 fun stringOf r = case !r of
adamc@866 340 NONE => string "NULL"
adamc@866 341 | SOME s => box [string "\"",
adamc@866 342 string (String.toString s),
adamc@866 343 string "\""]
adamc@866 344 in
adamc@866 345 app (fn s =>
adamc@866 346 case String.fields (fn ch => ch = #"=") s of
adamc@866 347 [name, value] =>
adamc@866 348 (case name of
adamc@866 349 "host" =>
adamc@866 350 if size value > 0 andalso String.sub (value, 0) = #"/" then
adamc@866 351 unix_socket := SOME value
adamc@866 352 else
adamc@866 353 host := SOME value
adamc@866 354 | "hostaddr" => host := SOME value
adamc@866 355 | "port" => port := Int.fromString value
adamc@866 356 | "dbname" => db := SOME value
adamc@866 357 | "user" => user := SOME value
adamc@866 358 | "password" => passwd := SOME value
adamc@866 359 | _ => ())
adamc@866 360 | _ => ()) (String.tokens Char.isSpace dbstring);
adamc@866 361
adamc@866 362 box [string "typedef struct {",
adamc@866 363 newline,
adamc@866 364 box [string "MYSQL *conn;",
adamc@866 365 newline,
adamc@866 366 p_list_sepi (box [])
adamc@866 367 (fn i => fn _ =>
adamc@866 368 box [string "MYSQL_STMT *p",
adamc@866 369 string (Int.toString i),
adamc@866 370 string ";",
adamc@866 371 newline])
adamc@866 372 ss],
adamc@866 373 string "} uw_conn;",
adamc@866 374 newline,
adamc@866 375 newline,
adamc@866 376
adamc@874 377 string "void uw_client_init(void) {",
adamc@874 378 newline,
adamc@879 379 box [string "uw_sqlfmtInt = \"%lld%n\";",
adamc@879 380 newline,
adamc@879 381 string "uw_sqlfmtFloat = \"%g%n\";",
adamc@879 382 newline,
adamc@879 383 string "uw_Estrings = 0;",
adamc@879 384 newline,
adamc@879 385 string "uw_sqlsuffixString = \"\";",
adamc@879 386 newline,
adamc@879 387 string "uw_sqlsuffixBlob = \"\";",
adamc@879 388 newline,
adamc@879 389 string "uw_sqlfmtUint4 = \"%u%n\";",
adamc@879 390 newline,
adamc@879 391 newline,
adamc@879 392
adamc@879 393 string "if (mysql_library_init(0, NULL, NULL)) {",
adamc@874 394 newline,
adamc@874 395 box [string "fprintf(stderr, \"Could not initialize MySQL library\\n\");",
adamc@874 396 newline,
adamc@874 397 string "exit(1);",
adamc@874 398 newline],
adamc@874 399 string "}",
adamc@874 400 newline],
adamc@874 401 string "}",
adamc@874 402 newline,
adamc@874 403 newline,
adamc@874 404
adamc@866 405 if #persistent (currentProtocol ()) then
adamc@874 406 box [string "static void uw_db_validate(uw_context ctx) {",
adamc@874 407 newline,
adamc@874 408 string "uw_conn *conn = uw_get_db(ctx);",
adamc@874 409 newline,
adamc@874 410 string "MYSQL_RES *res;",
adamc@874 411 newline,
adamc@874 412 string "MYSQL_ROW row;",
adamc@874 413 newline,
adamc@874 414 newline,
adamc@874 415 p_list_sep newline (checkRel ("tables", true)) tables,
adamc@884 416 p_list_sep newline (fn name => checkRel ("tables", true)
adamc@884 417 (name, [("id", Settings.Client)])) sequences,
adamc@874 418 p_list_sep newline (checkRel ("views", false)) views,
adamc@874 419 string "}",
adamc@874 420 newline,
adamc@874 421 newline,
adamc@874 422
adamc@874 423 string "static void uw_db_prepare(uw_context ctx) {",
adamc@866 424 newline,
adamc@866 425 string "uw_conn *conn = uw_get_db(ctx);",
adamc@866 426 newline,
adamc@866 427 string "MYSQL_STMT *stmt;",
adamc@866 428 newline,
adamc@866 429 newline,
adamc@866 430
adamc@866 431 p_list_sepi newline (fn i => fn (s, n) =>
adamc@866 432 let
adamc@866 433 fun uhoh this s args =
adamc@866 434 box [p_list_sepi (box [])
adamc@866 435 (fn j => fn () =>
adamc@866 436 box [string
adamc@866 437 "mysql_stmt_close(conn->p",
adamc@866 438 string (Int.toString j),
adamc@866 439 string ");",
adamc@866 440 newline])
adamc@866 441 (List.tabulate (i, fn _ => ())),
adamc@866 442 box (if this then
adamc@866 443 [string
adamc@866 444 "mysql_stmt_close(conn->p",
adamc@866 445 string (Int.toString i),
adamc@866 446 string ");",
adamc@866 447 newline]
adamc@866 448 else
adamc@866 449 []),
adamc@866 450 string "mysql_close(conn->conn);",
adamc@866 451 newline,
adamc@866 452 string "uw_error(ctx, FATAL, \"",
adamc@866 453 string s,
adamc@866 454 string "\"",
adamc@866 455 p_list_sep (box []) (fn s => box [string ", ",
adamc@866 456 string s]) args,
adamc@866 457 string ");",
adamc@866 458 newline]
adamc@866 459 in
adamc@866 460 box [string "stmt = mysql_stmt_init(conn->conn);",
adamc@866 461 newline,
adamc@866 462 string "if (stmt == NULL) {",
adamc@866 463 newline,
adamc@866 464 uhoh false "Out of memory allocating prepared statement" [],
adamc@866 465 string "}",
adamc@866 466 newline,
adamc@874 467 string "conn->p",
adamc@874 468 string (Int.toString i),
adamc@874 469 string " = stmt;",
adamc@874 470 newline,
adamc@866 471
adamc@866 472 string "if (mysql_stmt_prepare(stmt, \"",
adamc@866 473 string (String.toString s),
adamc@866 474 string "\", ",
adamc@866 475 string (Int.toString (size s)),
adamc@866 476 string ")) {",
adamc@866 477 newline,
adamc@866 478 box [string "char msg[1024];",
adamc@866 479 newline,
adamc@866 480 string "strncpy(msg, mysql_stmt_error(stmt), 1024);",
adamc@866 481 newline,
adamc@866 482 string "msg[1023] = 0;",
adamc@866 483 newline,
adamc@866 484 uhoh true "Error preparing statement: %s" ["msg"]],
adamc@866 485 string "}",
adamc@866 486 newline]
adamc@866 487 end)
adamc@866 488 ss,
adamc@866 489
adamc@866 490 string "}"]
adamc@866 491 else
adamc@882 492 box [string "static void uw_db_prepare(uw_context ctx) { }",
adamc@882 493 newline,
adamc@882 494 string "static void uw_db_validate(uw_context ctx) { }"],
adamc@866 495 newline,
adamc@866 496 newline,
adamc@866 497
adamc@866 498 string "void uw_db_init(uw_context ctx) {",
adamc@866 499 newline,
adamc@866 500 string "MYSQL *mysql = mysql_init(NULL);",
adamc@866 501 newline,
adamc@866 502 string "uw_conn *conn;",
adamc@866 503 newline,
adamc@866 504 string "if (mysql == NULL) uw_error(ctx, FATAL, ",
adamc@866 505 string "\"libmysqlclient can't allocate a connection.\");",
adamc@866 506 newline,
adamc@866 507 string "if (mysql_real_connect(mysql, ",
adamc@866 508 stringOf host,
adamc@866 509 string ", ",
adamc@866 510 stringOf user,
adamc@866 511 string ", ",
adamc@866 512 stringOf passwd,
adamc@866 513 string ", ",
adamc@866 514 stringOf db,
adamc@866 515 string ", ",
adamc@866 516 case !port of
adamc@866 517 NONE => string "0"
adamc@866 518 | SOME n => string (Int.toString n),
adamc@866 519 string ", ",
adamc@866 520 stringOf unix_socket,
adamc@874 521 string ", 0) == NULL) {",
adamc@866 522 newline,
adamc@866 523 box [string "char msg[1024];",
adamc@866 524 newline,
adamc@866 525 string "strncpy(msg, mysql_error(mysql), 1024);",
adamc@866 526 newline,
adamc@866 527 string "msg[1023] = 0;",
adamc@866 528 newline,
adamc@866 529 string "mysql_close(mysql);",
adamc@866 530 newline,
adamc@866 531 string "uw_error(ctx, BOUNDED_RETRY, ",
adamc@866 532 string "\"Connection to MySQL server failed: %s\", msg);"],
adamc@866 533 newline,
adamc@866 534 string "}",
adamc@866 535 newline,
adamc@874 536 string "conn = calloc(1, sizeof(uw_conn));",
adamc@866 537 newline,
adamc@866 538 string "conn->conn = mysql;",
adamc@866 539 newline,
adamc@866 540 string "uw_set_db(ctx, conn);",
adamc@866 541 newline,
adamc@866 542 string "uw_db_validate(ctx);",
adamc@866 543 newline,
adamc@866 544 string "uw_db_prepare(ctx);",
adamc@866 545 newline,
adamc@866 546 string "}",
adamc@866 547 newline,
adamc@866 548 newline,
adamc@866 549
adamc@866 550 string "void uw_db_close(uw_context ctx) {",
adamc@866 551 newline,
adamc@866 552 string "uw_conn *conn = uw_get_db(ctx);",
adamc@866 553 newline,
adamc@866 554 p_list_sepi (box [])
adamc@866 555 (fn i => fn _ =>
adamc@866 556 box [string "if (conn->p",
adamc@866 557 string (Int.toString i),
adamc@866 558 string ") mysql_stmt_close(conn->p",
adamc@866 559 string (Int.toString i),
adamc@866 560 string ");",
adamc@866 561 newline])
adamc@866 562 ss,
adamc@866 563 string "mysql_close(conn->conn);",
adamc@866 564 newline,
adamc@866 565 string "}",
adamc@866 566 newline,
adamc@866 567 newline,
adamc@866 568
adamc@866 569 string "int uw_db_begin(uw_context ctx) {",
adamc@866 570 newline,
adamc@866 571 string "uw_conn *conn = uw_get_db(ctx);",
adamc@866 572 newline,
adamc@866 573 newline,
adamc@866 574 string "return mysql_query(conn->conn, \"SET TRANSACTION ISOLATION LEVEL SERIALIZABLE\")",
adamc@866 575 newline,
adamc@866 576 string " || mysql_query(conn->conn, \"BEGIN\");",
adamc@866 577 newline,
adamc@866 578 string "}",
adamc@866 579 newline,
adamc@866 580 newline,
adamc@866 581
adamc@866 582 string "int uw_db_commit(uw_context ctx) {",
adamc@866 583 newline,
adamc@866 584 string "uw_conn *conn = uw_get_db(ctx);",
adamc@866 585 newline,
adamc@866 586 string "return mysql_commit(conn->conn);",
adamc@866 587 newline,
adamc@866 588 string "}",
adamc@866 589 newline,
adamc@866 590 newline,
adamc@866 591
adamc@866 592 string "int uw_db_rollback(uw_context ctx) {",
adamc@866 593 newline,
adamc@866 594 string "uw_conn *conn = uw_get_db(ctx);",
adamc@866 595 newline,
adamc@866 596 string "return mysql_rollback(conn->conn);",
adamc@866 597 newline,
adamc@866 598 string "}",
adamc@866 599 newline,
adamc@866 600 newline]
adamc@866 601 end
adamc@866 602
adamc@880 603 fun p_getcol {loc, wontLeakStrings = _, col = i, typ = t} =
adamc@873 604 let
adamc@873 605 fun getter t =
adamc@873 606 case t of
adamc@873 607 String => box [string "({",
adamc@873 608 newline,
adamc@873 609 string "uw_Basis_string s = uw_malloc(ctx, length",
adamc@873 610 string (Int.toString i),
adamc@873 611 string " + 1);",
adamc@873 612 newline,
adamc@873 613 string "out[",
adamc@873 614 string (Int.toString i),
adamc@873 615 string "].buffer = s;",
adamc@873 616 newline,
adamc@873 617 string "out[",
adamc@873 618 string (Int.toString i),
adamc@873 619 string "].buffer_length = length",
adamc@873 620 string (Int.toString i),
adamc@873 621 string " + 1;",
adamc@873 622 newline,
adamc@873 623 string "mysql_stmt_fetch_column(stmt, &out[",
adamc@873 624 string (Int.toString i),
adamc@873 625 string "], ",
adamc@873 626 string (Int.toString i),
adamc@873 627 string ", 0);",
adamc@873 628 newline,
adamc@873 629 string "s[length",
adamc@873 630 string (Int.toString i),
adamc@873 631 string "] = 0;",
adamc@873 632 newline,
adamc@873 633 string "s;",
adamc@873 634 newline,
adamc@873 635 string "})"]
adamc@873 636 | Blob => box [string "({",
adamc@873 637 newline,
adamc@873 638 string "uw_Basis_blob b = {length",
adamc@873 639 string (Int.toString i),
adamc@873 640 string ", uw_malloc(ctx, length",
adamc@873 641 string (Int.toString i),
adamc@873 642 string ")};",
adamc@873 643 newline,
adamc@873 644 string "out[",
adamc@873 645 string (Int.toString i),
adamc@873 646 string "].buffer = b.data;",
adamc@873 647 newline,
adamc@873 648 string "out[",
adamc@873 649 string (Int.toString i),
adamc@873 650 string "].buffer_length = length",
adamc@873 651 string (Int.toString i),
adamc@873 652 string ";",
adamc@873 653 newline,
adamc@873 654 string "mysql_stmt_fetch_column(stmt, &out[",
adamc@873 655 string (Int.toString i),
adamc@873 656 string "], ",
adamc@873 657 string (Int.toString i),
adamc@873 658 string ", 0);",
adamc@873 659 newline,
adamc@873 660 string "b;",
adamc@873 661 newline,
adamc@873 662 string "})"]
adamc@873 663 | Time => box [string "({",
adamc@876 664 string "MYSQL_TIME *mt = &buffer",
adamc@873 665 string (Int.toString i),
adamc@873 666 string ";",
adamc@873 667 newline,
adamc@873 668 newline,
adamc@888 669 string "struct tm t = {mt->second, mt->minute, mt->hour, mt->day, mt->month-1, mt->year, 0, 0, -1};",
adamc@873 670 newline,
adamc@876 671 string "mktime(&t);",
adamc@873 672 newline,
adamc@873 673 string "})"]
adamc@889 674 | Channel => box [string "({",
adamc@889 675 string "uw_Basis_channel ch = {buffer",
adamc@889 676 string (Int.toString i),
adamc@889 677 string " >> 32, buffer",
adamc@889 678 string (Int.toString i),
adamc@889 679 string " & 0xFFFFFFFF};",
adamc@889 680 newline,
adamc@889 681 string "ch;",
adamc@889 682 newline,
adamc@889 683 string "})"]
adamc@873 684 | _ => box [string "buffer",
adamc@873 685 string (Int.toString i)]
adamc@873 686 in
adamc@873 687 case t of
adamc@873 688 Nullable t => box [string "(is_null",
adamc@873 689 string (Int.toString i),
adamc@873 690 string " ? NULL : ",
adamc@873 691 case t of
adamc@873 692 String => getter t
adamc@873 693 | _ => box [string "({",
adamc@873 694 newline,
adamc@873 695 string (p_sql_ctype t),
adamc@873 696 space,
adamc@873 697 string "*tmp = uw_malloc(ctx, sizeof(",
adamc@873 698 string (p_sql_ctype t),
adamc@873 699 string "));",
adamc@873 700 newline,
adamc@873 701 string "*tmp = ",
adamc@873 702 getter t,
adamc@873 703 string ";",
adamc@873 704 newline,
adamc@873 705 string "tmp;",
adamc@873 706 newline,
adamc@873 707 string "})"],
adamc@873 708 string ")"]
adamc@873 709 | _ => box [string "(is_null",
adamc@873 710 string (Int.toString i),
adamc@873 711 string " ? ",
adamc@873 712 box [string "({",
adamc@873 713 string (p_sql_ctype t),
adamc@873 714 space,
adamc@873 715 string "tmp;",
adamc@873 716 newline,
adamc@873 717 string "uw_error(ctx, FATAL, \"Unexpectedly NULL field #",
adamc@873 718 string (Int.toString i),
adamc@873 719 string "\");",
adamc@873 720 newline,
adamc@873 721 string "tmp;",
adamc@873 722 newline,
adamc@873 723 string "})"],
adamc@873 724 string " : ",
adamc@873 725 getter t,
adamc@873 726 string ")"]
adamc@873 727 end
adamc@873 728
adamc@873 729 fun queryCommon {loc, query, cols, doCols} =
adamc@873 730 box [string "int n, r;",
adamc@873 731 newline,
adamc@873 732 string "MYSQL_BIND out[",
adamc@873 733 string (Int.toString (length cols)),
adamc@873 734 string "];",
adamc@873 735 newline,
adamc@873 736 p_list_sepi (box []) (fn i => fn t =>
adamc@873 737 let
adamc@873 738 fun buffers t =
adamc@873 739 case t of
adamc@873 740 String => box [string "unsigned long length",
adamc@873 741 string (Int.toString i),
adamc@873 742 string ";",
adamc@873 743 newline]
adamc@873 744 | Blob => box [string "unsigned long length",
adamc@873 745 string (Int.toString i),
adamc@873 746 string ";",
adamc@873 747 newline]
adamc@876 748 | Time => box [string "MYSQL_TIME buffer",
adamc@876 749 string (Int.toString i),
adamc@876 750 string ";",
adamc@876 751 newline]
adamc@889 752 | Channel => box [string "unsigned long long buffer",
adamc@889 753 string (Int.toString i),
adamc@889 754 string ";",
adamc@889 755 newline]
adamc@873 756 | _ => box [string (p_sql_ctype t),
adamc@873 757 space,
adamc@873 758 string "buffer",
adamc@873 759 string (Int.toString i),
adamc@873 760 string ";",
adamc@873 761 newline]
adamc@873 762 in
adamc@873 763 box [string "my_bool is_null",
adamc@873 764 string (Int.toString i),
adamc@873 765 string ";",
adamc@873 766 newline,
adamc@873 767 case t of
adamc@873 768 Nullable t => buffers t
adamc@873 769 | _ => buffers t,
adamc@873 770 newline]
adamc@873 771 end) cols,
adamc@873 772 newline,
adamc@873 773
adamc@873 774 string "memset(out, 0, sizeof out);",
adamc@873 775 newline,
adamc@873 776 p_list_sepi (box []) (fn i => fn t =>
adamc@873 777 let
adamc@873 778 fun buffers t =
adamc@873 779 case t of
adamc@875 780 String => box [string "out[",
adamc@875 781 string (Int.toString i),
adamc@875 782 string "].length = &length",
adamc@875 783 string (Int.toString i),
adamc@875 784 string ";",
adamc@875 785 newline]
adamc@875 786 | Blob => box [string "out[",
adamc@875 787 string (Int.toString i),
adamc@875 788 string "].length = &length",
adamc@875 789 string (Int.toString i),
adamc@875 790 string ";",
adamc@875 791 newline]
adamc@873 792 | _ => box [string "out[",
adamc@873 793 string (Int.toString i),
adamc@873 794 string "].buffer = &buffer",
adamc@873 795 string (Int.toString i),
adamc@873 796 string ";",
adamc@873 797 newline]
adamc@873 798 in
adamc@873 799 box [string "out[",
adamc@873 800 string (Int.toString i),
adamc@873 801 string "].buffer_type = ",
adamc@873 802 string (p_buffer_type t),
adamc@873 803 string ";",
adamc@873 804 newline,
adamc@873 805 string "out[",
adamc@873 806 string (Int.toString i),
adamc@873 807 string "].is_null = &is_null",
adamc@873 808 string (Int.toString i),
adamc@873 809 string ";",
adamc@873 810 newline,
adamc@873 811
adamc@873 812 case t of
adamc@873 813 Nullable t => buffers t
adamc@873 814 | _ => buffers t,
adamc@873 815 newline]
adamc@873 816 end) cols,
adamc@873 817 newline,
adamc@873 818
adamc@875 819 string "if (mysql_stmt_reset(stmt)) uw_error(ctx, FATAL, \"",
adamc@875 820 string (ErrorMsg.spanToString loc),
adamc@875 821 string ": Error reseting statement: %s\\n%s\", ",
adamc@875 822 query,
adamc@875 823 string ", mysql_error(conn->conn));",
adamc@875 824 newline,
adamc@875 825 newline,
adamc@875 826
adamc@873 827 string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"",
adamc@873 828 string (ErrorMsg.spanToString loc),
adamc@875 829 string ": Error executing query: %s\\n%s\", ",
adamc@875 830 query,
adamc@875 831 string ", mysql_error(conn->conn));",
adamc@875 832 newline,
adamc@875 833 newline,
adamc@875 834
adamc@875 835 string "if (mysql_stmt_bind_result(stmt, out)) uw_error(ctx, FATAL, \"",
adamc@875 836 string (ErrorMsg.spanToString loc),
adamc@875 837 string ": Error binding query result: %s\\n%s\", ",
adamc@875 838 query,
adamc@875 839 string ", mysql_error(conn->conn));",
adamc@873 840 newline,
adamc@873 841 newline,
adamc@873 842
adamc@873 843 string "if (mysql_stmt_store_result(stmt)) uw_error(ctx, FATAL, \"",
adamc@873 844 string (ErrorMsg.spanToString loc),
adamc@875 845 string ": Error storing query result: %s\\n%s\", ",
adamc@875 846 query,
adamc@875 847 string ", mysql_error(conn->conn));",
adamc@873 848 newline,
adamc@873 849 newline,
adamc@873 850
adamc@873 851 string "uw_end_region(ctx);",
adamc@873 852 newline,
adamc@875 853 string "while (1) {",
adamc@875 854 newline,
adamc@875 855 string "r = mysql_stmt_fetch(stmt);",
adamc@875 856 newline,
adamc@875 857 string "if (r != 0 && r != MYSQL_DATA_TRUNCATED) break;",
adamc@873 858 newline,
adamc@873 859 doCols p_getcol,
adamc@873 860 string "}",
adamc@873 861 newline,
adamc@873 862 newline,
adamc@873 863
adamc@874 864 string "if (r == 1) uw_error(ctx, FATAL, \"",
adamc@873 865 string (ErrorMsg.spanToString loc),
adamc@875 866 string ": query result fetching failed: %s\\n%s\", ",
adamc@875 867 query,
adamc@875 868 string ", mysql_error(conn->conn));",
adamc@875 869 newline,
adamc@875 870 newline,
adamc@875 871
adamc@875 872 string "if (mysql_stmt_reset(stmt)) uw_error(ctx, FATAL, \"",
adamc@875 873 string (ErrorMsg.spanToString loc),
adamc@875 874 string ": Error reseting statement: %s\\n%s\", ",
adamc@875 875 query,
adamc@875 876 string ", mysql_error(conn->conn));",
adamc@875 877 newline,
adamc@875 878 newline]
adamc@873 879
adamc@873 880 fun query {loc, cols, doCols} =
adamc@873 881 box [string "uw_conn *conn = uw_get_db(ctx);",
adamc@873 882 newline,
adamc@876 883 string "MYSQL_STMT *stmt = mysql_stmt_init(conn->conn);",
adamc@873 884 newline,
adamc@875 885 string "if (stmt == NULL) uw_error(ctx, FATAL, \"",
adamc@873 886 string (ErrorMsg.spanToString loc),
adamc@873 887 string ": can't allocate temporary prepared statement\");",
adamc@873 888 newline,
adamc@873 889 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);",
adamc@873 890 newline,
adamc@873 891 string "if (mysql_stmt_prepare(stmt, query, strlen(query))) uw_error(ctx, FATAL, \"",
adamc@873 892 string (ErrorMsg.spanToString loc),
adamc@875 893 string ": error preparing statement: %s\\n%s\", query, mysql_error(conn->conn));",
adamc@873 894 newline,
adamc@873 895 newline,
adamc@873 896
adamc@873 897 queryCommon {loc = loc, cols = cols, doCols = doCols, query = string "query"},
adamc@873 898
adamc@873 899 string "uw_pop_cleanup(ctx);",
adamc@873 900 newline]
adamc@873 901
adamc@879 902 fun queryPrepared {loc, id, query, inputs, cols, doCols, nested} =
adamc@873 903 box [string "uw_conn *conn = uw_get_db(ctx);",
adamc@873 904 newline,
adamc@873 905 string "MYSQL_BIND in[",
adamc@873 906 string (Int.toString (length inputs)),
adamc@873 907 string "];",
adamc@873 908 newline,
adamc@873 909 p_list_sepi (box []) (fn i => fn t =>
adamc@873 910 let
adamc@873 911 fun buffers t =
adamc@873 912 case t of
adamc@873 913 String => box [string "unsigned long in_length",
adamc@873 914 string (Int.toString i),
adamc@873 915 string ";",
adamc@873 916 newline]
adamc@873 917 | Blob => box [string "unsigned long in_length",
adamc@873 918 string (Int.toString i),
adamc@873 919 string ";",
adamc@873 920 newline]
adamc@876 921 | Time => box [string "MYSQL_TIME in_buffer",
adamc@873 922 string (Int.toString i),
adamc@889 923 string ";", newline]
adamc@873 924 | _ => box []
adamc@873 925 in
adamc@873 926 box [case t of
adamc@873 927 Nullable t => box [string "my_bool in_is_null",
adamc@873 928 string (Int.toString i),
adamc@873 929 string ";",
adamc@873 930 newline,
adamc@873 931 buffers t]
adamc@873 932 | _ => buffers t,
adamc@873 933 newline]
adamc@873 934 end) inputs,
adamc@873 935
adamc@879 936 if nested then
adamc@879 937 box [string "MYSQL_STMT *stmt;",
adamc@879 938 newline]
adamc@879 939 else
adamc@879 940 box [string "MYSQL_STMT *stmt = conn->p",
adamc@879 941 string (Int.toString id),
adamc@879 942 string ";",
adamc@879 943 newline,
adamc@879 944 newline,
adamc@879 945
adamc@879 946 string "if (stmt == NULL) {",
adamc@879 947 newline],
adamc@879 948
adamc@878 949 box [string "stmt = mysql_stmt_init(conn->conn);",
adamc@878 950 newline,
adamc@878 951 string "if (stmt == NULL) uw_error(ctx, FATAL, \"Out of memory allocating prepared statement\");",
adamc@878 952 newline,
adamc@880 953 if nested then
adamc@880 954 box [string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);",
adamc@880 955 newline]
adamc@880 956 else
adamc@880 957 box [],
adamc@878 958 string "if (mysql_stmt_prepare(stmt, \"",
adamc@878 959 string (String.toString query),
adamc@878 960 string "\", ",
adamc@878 961 string (Int.toString (size query)),
adamc@878 962 string ")) {",
adamc@878 963 newline,
adamc@878 964 box [string "char msg[1024];",
adamc@878 965 newline,
adamc@878 966 string "strncpy(msg, mysql_stmt_error(stmt), 1024);",
adamc@878 967 newline,
adamc@878 968 string "msg[1023] = 0;",
adamc@878 969 newline,
adamc@880 970 if nested then
adamc@880 971 box []
adamc@880 972 else
adamc@880 973 box [string "mysql_stmt_close(stmt);",
adamc@880 974 newline],
adamc@878 975 string "uw_error(ctx, FATAL, \"Error preparing statement: %s\", msg);",
adamc@878 976 newline],
adamc@878 977 string "}",
adamc@878 978 newline,
adamc@879 979 if nested then
adamc@879 980 box []
adamc@879 981 else
adamc@879 982 box [string "conn->p",
adamc@879 983 string (Int.toString id),
adamc@879 984 string " = stmt;",
adamc@879 985 newline]],
adamc@879 986 if nested then
adamc@879 987 box []
adamc@879 988 else
adamc@879 989 box [string "}",
adamc@879 990 newline],
adamc@878 991 newline,
adamc@878 992
adamc@873 993 string "memset(in, 0, sizeof in);",
adamc@873 994 newline,
adamc@873 995 p_list_sepi (box []) (fn i => fn t =>
adamc@873 996 let
adamc@873 997 fun buffers t =
adamc@873 998 case t of
adamc@873 999 String => box [string "in[",
adamc@873 1000 string (Int.toString i),
adamc@873 1001 string "].buffer = arg",
adamc@873 1002 string (Int.toString (i + 1)),
adamc@873 1003 string ";",
adamc@873 1004 newline,
adamc@873 1005 string "in_length",
adamc@873 1006 string (Int.toString i),
adamc@873 1007 string "= in[",
adamc@873 1008 string (Int.toString i),
adamc@873 1009 string "].buffer_length = strlen(arg",
adamc@873 1010 string (Int.toString (i + 1)),
adamc@873 1011 string ");",
adamc@873 1012 newline,
adamc@873 1013 string "in[",
adamc@873 1014 string (Int.toString i),
adamc@873 1015 string "].length = &in_length",
adamc@873 1016 string (Int.toString i),
adamc@873 1017 string ";",
adamc@873 1018 newline]
adamc@873 1019 | Blob => box [string "in[",
adamc@873 1020 string (Int.toString i),
adamc@873 1021 string "].buffer = arg",
adamc@873 1022 string (Int.toString (i + 1)),
adamc@873 1023 string ".data;",
adamc@873 1024 newline,
adamc@873 1025 string "in_length",
adamc@873 1026 string (Int.toString i),
adamc@873 1027 string "= in[",
adamc@873 1028 string (Int.toString i),
adamc@873 1029 string "].buffer_length = arg",
adamc@873 1030 string (Int.toString (i + 1)),
adamc@873 1031 string ".size;",
adamc@873 1032 newline,
adamc@873 1033 string "in[",
adamc@873 1034 string (Int.toString i),
adamc@873 1035 string "].length = &in_length",
adamc@873 1036 string (Int.toString i),
adamc@873 1037 string ";",
adamc@873 1038 newline]
adamc@873 1039 | Time =>
adamc@873 1040 let
adamc@873 1041 fun oneField dst src =
adamc@873 1042 box [string "in_buffer",
adamc@873 1043 string (Int.toString i),
adamc@873 1044 string ".",
adamc@873 1045 string dst,
adamc@873 1046 string " = tms.tm_",
adamc@873 1047 string src,
adamc@873 1048 string ";",
adamc@873 1049 newline]
adamc@873 1050 in
adamc@873 1051 box [string "({",
adamc@873 1052 newline,
adamc@873 1053 string "struct tm tms;",
adamc@873 1054 newline,
adamc@873 1055 string "if (localtime_r(&arg",
adamc@873 1056 string (Int.toString (i + 1)),
adamc@873 1057 string ", &tm) == NULL) uw_error(\"",
adamc@873 1058 string (ErrorMsg.spanToString loc),
adamc@873 1059 string ": error converting to MySQL time\");",
adamc@873 1060 newline,
adamc@873 1061 oneField "year" "year",
adamc@888 1062 box [string "in_buffer",
adamc@888 1063 string (Int.toString i),
adamc@888 1064 string ".month = tms.tm_mon + 1;",
adamc@888 1065 newline],
adamc@873 1066 oneField "day" "mday",
adamc@873 1067 oneField "hour" "hour",
adamc@873 1068 oneField "minute" "min",
adamc@873 1069 oneField "second" "sec",
adamc@873 1070 newline,
adamc@873 1071 string "in[",
adamc@873 1072 string (Int.toString i),
adamc@873 1073 string "].buffer = &in_buffer",
adamc@873 1074 string (Int.toString i),
adamc@873 1075 string ";",
adamc@873 1076 newline]
adamc@873 1077 end
adamc@889 1078 | Channel => box [string "in_buffer",
adamc@889 1079 string (Int.toString i),
adamc@889 1080 string " = ((unsigned long long)arg",
adamc@889 1081 string (Int.toString (i + 1)),
adamc@889 1082 string ".cli << 32) | arg",
adamc@889 1083 string (Int.toString (i + 1)),
adamc@889 1084 string ".chn;",
adamc@889 1085 newline,
adamc@889 1086 string "in[",
adamc@889 1087 string (Int.toString i),
adamc@889 1088 string "].buffer = &in_buffer",
adamc@889 1089 string (Int.toString i),
adamc@889 1090 string ";",
adamc@889 1091 newline]
adamc@873 1092
adamc@873 1093 | _ => box [string "in[",
adamc@873 1094 string (Int.toString i),
adamc@873 1095 string "].buffer = &arg",
adamc@873 1096 string (Int.toString (i + 1)),
adamc@873 1097 string ";",
adamc@873 1098 newline]
adamc@873 1099 in
adamc@873 1100 box [string "in[",
adamc@873 1101 string (Int.toString i),
adamc@873 1102 string "].buffer_type = ",
adamc@873 1103 string (p_buffer_type t),
adamc@873 1104 string ";",
adamc@873 1105 newline,
adamc@873 1106
adamc@873 1107 case t of
adamc@873 1108 Nullable t => box [string "in[",
adamc@873 1109 string (Int.toString i),
adamc@873 1110 string "].is_null = &in_is_null",
adamc@873 1111 string (Int.toString i),
adamc@873 1112 string ";",
adamc@873 1113 newline,
adamc@873 1114 string "if (arg",
adamc@873 1115 string (Int.toString (i + 1)),
adamc@873 1116 string " == NULL) {",
adamc@873 1117 newline,
adamc@873 1118 box [string "in_is_null",
adamc@873 1119 string (Int.toString i),
adamc@873 1120 string " = 1;",
adamc@873 1121 newline],
adamc@873 1122 string "} else {",
adamc@873 1123 box [case t of
adamc@873 1124 String => box []
adamc@873 1125 | _ =>
adamc@873 1126 box [string (p_sql_ctype t),
adamc@873 1127 space,
adamc@876 1128 string "tmp = *arg",
adamc@876 1129 string (Int.toString (i + 1)),
adamc@876 1130 string ";",
adamc@876 1131 newline,
adamc@876 1132 string (p_sql_ctype t),
adamc@876 1133 space,
adamc@873 1134 string "arg",
adamc@873 1135 string (Int.toString (i + 1)),
adamc@876 1136 string " = tmp;",
adamc@873 1137 newline],
adamc@873 1138 string "in_is_null",
adamc@873 1139 string (Int.toString i),
adamc@873 1140 string " = 0;",
adamc@873 1141 newline,
adamc@873 1142 buffers t,
adamc@876 1143 newline],
adamc@876 1144 string "}",
adamc@876 1145 newline]
adamc@873 1146
adamc@873 1147 | _ => buffers t,
adamc@873 1148 newline]
adamc@873 1149 end) inputs,
adamc@873 1150 newline,
adamc@873 1151
adamc@875 1152 string "if (mysql_stmt_bind_param(stmt, in)) uw_error(ctx, FATAL, \"",
adamc@875 1153 string (ErrorMsg.spanToString loc),
adamc@875 1154 string ": error binding parameters\");",
adamc@875 1155 newline,
adamc@875 1156
adamc@873 1157 queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"",
adamc@873 1158 string (String.toString query),
adamc@879 1159 string "\""]},
adamc@879 1160
adamc@879 1161 if nested then
adamc@879 1162 box [string "uw_pop_cleanup(ctx);",
adamc@879 1163 newline]
adamc@879 1164 else
adamc@879 1165 box []]
adamc@873 1166
adamc@875 1167 fun dmlCommon {loc, dml} =
adamc@875 1168 box [string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"",
adamc@875 1169 string (ErrorMsg.spanToString loc),
adamc@875 1170 string ": Error executing DML: %s\\n%s\", ",
adamc@875 1171 dml,
adamc@875 1172 string ", mysql_error(conn->conn));",
adamc@875 1173 newline,
adamc@875 1174 newline]
adamc@875 1175
adamc@875 1176 fun dml loc =
adamc@875 1177 box [string "uw_conn *conn = uw_get_db(ctx);",
adamc@875 1178 newline,
adamc@875 1179 string "MYSQL_stmt *stmt = mysql_stmt_init(conn->conn);",
adamc@875 1180 newline,
adamc@875 1181 string "if (stmt == NULL) uw_error(ctx, \"",
adamc@875 1182 string (ErrorMsg.spanToString loc),
adamc@875 1183 string ": can't allocate temporary prepared statement\");",
adamc@875 1184 newline,
adamc@875 1185 string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);",
adamc@875 1186 newline,
adamc@875 1187 string "if (mysql_stmt_prepare(stmt, dml, strlen(dml))) uw_error(ctx, FATAL, \"",
adamc@875 1188 string (ErrorMsg.spanToString loc),
adamc@875 1189 string ": error preparing statement: %s\\n%s\", dml, mysql_error(conn->conn));",
adamc@875 1190 newline,
adamc@875 1191 newline,
adamc@875 1192
adamc@875 1193 dmlCommon {loc = loc, dml = string "dml"},
adamc@875 1194
adamc@875 1195 string "uw_pop_cleanup(ctx);",
adamc@875 1196 newline]
adamc@875 1197
adamc@875 1198 fun dmlPrepared {loc, id, dml, inputs} =
adamc@875 1199 box [string "uw_conn *conn = uw_get_db(ctx);",
adamc@875 1200 newline,
adamc@875 1201 string "MYSQL_BIND in[",
adamc@875 1202 string (Int.toString (length inputs)),
adamc@875 1203 string "];",
adamc@875 1204 newline,
adamc@875 1205 p_list_sepi (box []) (fn i => fn t =>
adamc@875 1206 let
adamc@875 1207 fun buffers t =
adamc@875 1208 case t of
adamc@875 1209 String => box [string "unsigned long in_length",
adamc@875 1210 string (Int.toString i),
adamc@875 1211 string ";",
adamc@875 1212 newline]
adamc@875 1213 | Blob => box [string "unsigned long in_length",
adamc@875 1214 string (Int.toString i),
adamc@875 1215 string ";",
adamc@875 1216 newline]
adamc@876 1217 | Time => box [string "MYSQL_TIME in_buffer",
adamc@875 1218 string (Int.toString i),
adamc@875 1219 string ";",
adamc@875 1220 newline]
adamc@889 1221 | Channel => box [string "unsigned long long in_buffer",
adamc@889 1222 string (Int.toString i),
adamc@889 1223 string ";",
adamc@889 1224 newline]
adamc@875 1225 | _ => box []
adamc@875 1226 in
adamc@875 1227 box [case t of
adamc@875 1228 Nullable t => box [string "my_bool in_is_null",
adamc@875 1229 string (Int.toString i),
adamc@875 1230 string ";",
adamc@875 1231 newline,
adamc@875 1232 buffers t]
adamc@875 1233 | _ => buffers t,
adamc@875 1234 newline]
adamc@875 1235 end) inputs,
adamc@875 1236 string "MYSQL_STMT *stmt = conn->p",
adamc@875 1237 string (Int.toString id),
adamc@875 1238 string ";",
adamc@875 1239 newline,
adamc@875 1240 newline,
adamc@875 1241
adamc@878 1242 string "if (stmt == NULL) {",
adamc@878 1243 newline,
adamc@878 1244 box [string "stmt = mysql_stmt_init(conn->conn);",
adamc@878 1245 newline,
adamc@878 1246 string "if (stmt == NULL) uw_error(ctx, FATAL, \"Out of memory allocating prepared statement\");",
adamc@878 1247 newline,
adamc@878 1248 string "if (mysql_stmt_prepare(stmt, \"",
adamc@878 1249 string (String.toString dml),
adamc@878 1250 string "\", ",
adamc@878 1251 string (Int.toString (size dml)),
adamc@878 1252 string ")) {",
adamc@878 1253 newline,
adamc@878 1254 box [string "char msg[1024];",
adamc@878 1255 newline,
adamc@878 1256 string "strncpy(msg, mysql_stmt_error(stmt), 1024);",
adamc@878 1257 newline,
adamc@878 1258 string "msg[1023] = 0;",
adamc@878 1259 newline,
adamc@878 1260 string "uw_error(ctx, FATAL, \"Error preparing statement: %s\", msg);",
adamc@878 1261 newline],
adamc@878 1262 string "}",
adamc@878 1263 newline,
adamc@878 1264 string "conn->p",
adamc@878 1265 string (Int.toString id),
adamc@878 1266 string " = stmt;",
adamc@878 1267 newline],
adamc@878 1268 string "}",
adamc@878 1269 newline,
adamc@878 1270 newline,
adamc@878 1271
adamc@875 1272 string "memset(in, 0, sizeof in);",
adamc@875 1273 newline,
adamc@875 1274 p_list_sepi (box []) (fn i => fn t =>
adamc@875 1275 let
adamc@875 1276 fun buffers t =
adamc@875 1277 case t of
adamc@875 1278 String => box [string "in[",
adamc@875 1279 string (Int.toString i),
adamc@875 1280 string "].buffer = arg",
adamc@875 1281 string (Int.toString (i + 1)),
adamc@875 1282 string ";",
adamc@875 1283 newline,
adamc@875 1284 string "in_length",
adamc@875 1285 string (Int.toString i),
adamc@875 1286 string "= in[",
adamc@875 1287 string (Int.toString i),
adamc@875 1288 string "].buffer_length = strlen(arg",
adamc@875 1289 string (Int.toString (i + 1)),
adamc@875 1290 string ");",
adamc@875 1291 newline,
adamc@875 1292 string "in[",
adamc@875 1293 string (Int.toString i),
adamc@875 1294 string "].length = &in_length",
adamc@875 1295 string (Int.toString i),
adamc@875 1296 string ";",
adamc@875 1297 newline]
adamc@875 1298 | Blob => box [string "in[",
adamc@875 1299 string (Int.toString i),
adamc@875 1300 string "].buffer = arg",
adamc@875 1301 string (Int.toString (i + 1)),
adamc@875 1302 string ".data;",
adamc@875 1303 newline,
adamc@875 1304 string "in_length",
adamc@875 1305 string (Int.toString i),
adamc@875 1306 string "= in[",
adamc@875 1307 string (Int.toString i),
adamc@875 1308 string "].buffer_length = arg",
adamc@875 1309 string (Int.toString (i + 1)),
adamc@875 1310 string ".size;",
adamc@875 1311 newline,
adamc@875 1312 string "in[",
adamc@875 1313 string (Int.toString i),
adamc@875 1314 string "].length = &in_length",
adamc@875 1315 string (Int.toString i),
adamc@875 1316 string ";",
adamc@875 1317 newline]
adamc@875 1318 | Time =>
adamc@875 1319 let
adamc@875 1320 fun oneField dst src =
adamc@875 1321 box [string "in_buffer",
adamc@875 1322 string (Int.toString i),
adamc@875 1323 string ".",
adamc@875 1324 string dst,
adamc@875 1325 string " = tms.tm_",
adamc@875 1326 string src,
adamc@875 1327 string ";",
adamc@875 1328 newline]
adamc@875 1329 in
adamc@875 1330 box [string "({",
adamc@875 1331 newline,
adamc@875 1332 string "struct tm tms;",
adamc@875 1333 newline,
adamc@875 1334 string "if (localtime_r(&arg",
adamc@875 1335 string (Int.toString (i + 1)),
adamc@875 1336 string ", &tm) == NULL) uw_error(\"",
adamc@875 1337 string (ErrorMsg.spanToString loc),
adamc@875 1338 string ": error converting to MySQL time\");",
adamc@875 1339 newline,
adamc@875 1340 oneField "year" "year",
adamc@875 1341 oneField "month" "mon",
adamc@875 1342 oneField "day" "mday",
adamc@875 1343 oneField "hour" "hour",
adamc@875 1344 oneField "minute" "min",
adamc@875 1345 oneField "second" "sec",
adamc@875 1346 newline,
adamc@875 1347 string "in[",
adamc@875 1348 string (Int.toString i),
adamc@875 1349 string "].buffer = &in_buffer",
adamc@875 1350 string (Int.toString i),
adamc@875 1351 string ";",
adamc@875 1352 newline]
adamc@875 1353 end
adamc@889 1354 | Channel => box [string "in_buffer",
adamc@889 1355 string (Int.toString i),
adamc@889 1356 string " = ((unsigned long long)arg",
adamc@889 1357 string (Int.toString (i + 1)),
adamc@889 1358 string ".cli << 32) | arg",
adamc@889 1359 string (Int.toString (i + 1)),
adamc@889 1360 string ".chn;",
adamc@889 1361 newline,
adamc@889 1362 string "in[",
adamc@889 1363 string (Int.toString i),
adamc@889 1364 string "].buffer = &in_buffer",
adamc@889 1365 string (Int.toString i),
adamc@889 1366 string ";",
adamc@889 1367 newline]
adamc@875 1368
adamc@875 1369 | _ => box [string "in[",
adamc@875 1370 string (Int.toString i),
adamc@875 1371 string "].buffer = &arg",
adamc@875 1372 string (Int.toString (i + 1)),
adamc@875 1373 string ";",
adamc@875 1374 newline]
adamc@875 1375 in
adamc@875 1376 box [string "in[",
adamc@875 1377 string (Int.toString i),
adamc@875 1378 string "].buffer_type = ",
adamc@875 1379 string (p_buffer_type t),
adamc@875 1380 string ";",
adamc@875 1381 newline,
adamc@889 1382
adamc@889 1383 case t of
adamc@889 1384 Channel => box [string "in[",
adamc@889 1385 string (Int.toString i),
adamc@889 1386 string "].is_unsigned = 1;",
adamc@889 1387 newline]
adamc@889 1388 | _ => box [],
adamc@875 1389
adamc@875 1390 case t of
adamc@875 1391 Nullable t => box [string "in[",
adamc@875 1392 string (Int.toString i),
adamc@875 1393 string "].is_null = &in_is_null",
adamc@875 1394 string (Int.toString i),
adamc@875 1395 string ";",
adamc@875 1396 newline,
adamc@875 1397 string "if (arg",
adamc@875 1398 string (Int.toString (i + 1)),
adamc@875 1399 string " == NULL) {",
adamc@875 1400 newline,
adamc@875 1401 box [string "in_is_null",
adamc@875 1402 string (Int.toString i),
adamc@875 1403 string " = 1;",
adamc@875 1404 newline],
adamc@875 1405 string "} else {",
adamc@875 1406 box [case t of
adamc@875 1407 String => box []
adamc@875 1408 | _ =>
adamc@875 1409 box [string (p_sql_ctype t),
adamc@875 1410 space,
adamc@876 1411 string "tmp = *arg",
adamc@876 1412 string (Int.toString (i + 1)),
adamc@876 1413 string ";",
adamc@876 1414 newline,
adamc@876 1415 string (p_sql_ctype t),
adamc@876 1416 space,
adamc@875 1417 string "arg",
adamc@875 1418 string (Int.toString (i + 1)),
adamc@876 1419 string " = tmp;",
adamc@875 1420 newline],
adamc@875 1421 string "in_is_null",
adamc@875 1422 string (Int.toString i),
adamc@875 1423 string " = 0;",
adamc@875 1424 newline,
adamc@875 1425 buffers t,
adamc@876 1426 newline],
adamc@876 1427 string "}",
adamc@876 1428 newline]
adamc@875 1429
adamc@875 1430 | _ => buffers t,
adamc@875 1431 newline]
adamc@875 1432 end) inputs,
adamc@875 1433 newline,
adamc@875 1434
adamc@875 1435 string "if (mysql_stmt_bind_param(stmt, in)) uw_error(ctx, FATAL, \"",
adamc@875 1436 string (ErrorMsg.spanToString loc),
adamc@875 1437 string ": error binding parameters\");",
adamc@875 1438 newline,
adamc@875 1439
adamc@875 1440 dmlCommon {loc = loc, dml = box [string "\"",
adamc@875 1441 string (String.toString dml),
adamc@875 1442 string "\""]}]
adamc@875 1443
adamc@878 1444 fun nextval {loc, seqE, seqName} =
adamc@878 1445 box [string "uw_conn *conn = uw_get_db(ctx);",
adamc@878 1446 newline,
adamc@878 1447 string "char *insert = ",
adamc@878 1448 case seqName of
adamc@878 1449 SOME s => string ("\"INSERT INTO " ^ s ^ " VALUES ()\"")
adamc@878 1450 | NONE => box [string "uw_Basis_strcat(ctx, \"INSERT INTO \", uw_Basis_strcat(ctx, ",
adamc@878 1451 seqE,
adamc@878 1452 string ", \" VALUES ()\"))"],
adamc@878 1453 string ";",
adamc@878 1454 newline,
adamc@878 1455 string "char *delete = ",
adamc@878 1456 case seqName of
adamc@878 1457 SOME s => string ("\"DELETE FROM " ^ s ^ "\"")
adamc@878 1458 | NONE => box [string "uw_Basis_strcat(ctx, \"DELETE FROM \", ",
adamc@878 1459 seqE,
adamc@878 1460 string ")"],
adamc@878 1461 string ";",
adamc@878 1462 newline,
adamc@878 1463 newline,
adamc@878 1464
adamc@878 1465 string "if (mysql_query(conn->conn, insert)) uw_error(ctx, FATAL, \"'nextval' INSERT failed\");",
adamc@878 1466 newline,
adamc@878 1467 string "n = mysql_insert_id(conn->conn);",
adamc@878 1468 newline,
adamc@878 1469 string "if (mysql_query(conn->conn, delete)) uw_error(ctx, FATAL, \"'nextval' DELETE failed\");",
adamc@878 1470 newline]
adamc@878 1471
adamc@878 1472 fun nextvalPrepared _ = raise Fail "MySQL.nextvalPrepared called"
adamc@867 1473
adamc@877 1474 fun sqlifyString s = "'" ^ String.translate (fn #"'" => "\\'"
adamc@877 1475 | #"\\" => "\\\\"
adamc@877 1476 | ch =>
adamc@877 1477 if Char.isPrint ch then
adamc@877 1478 str ch
adamc@877 1479 else
adamc@877 1480 (ErrorMsg.error
adamc@877 1481 "Non-printing character found in SQL string literal";
adamc@877 1482 ""))
adamc@877 1483 (String.toString s) ^ "'"
adamc@874 1484
adamc@877 1485 fun p_cast (s, _) = s
adamc@874 1486
adamc@874 1487 fun p_blank _ = "?"
adamc@874 1488
adamc@866 1489 val () = addDbms {name = "mysql",
adamc@866 1490 header = "mysql/mysql.h",
adamc@866 1491 link = "-lmysqlclient",
adamc@867 1492 init = init,
adamc@873 1493 p_sql_type = p_sql_type,
adamc@867 1494 query = query,
adamc@868 1495 queryPrepared = queryPrepared,
adamc@868 1496 dml = dml,
adamc@869 1497 dmlPrepared = dmlPrepared,
adamc@869 1498 nextval = nextval,
adamc@874 1499 nextvalPrepared = nextvalPrepared,
adamc@874 1500 sqlifyString = sqlifyString,
adamc@874 1501 p_cast = p_cast,
adamc@874 1502 p_blank = p_blank,
adamc@877 1503 supportsDeleteAs = false,
adamc@886 1504 supportsUpdateAs = false,
adamc@884 1505 createSequence = fn s => "CREATE TABLE " ^ s ^ " (uw_id INTEGER PRIMARY KEY AUTO_INCREMENT)",
adamc@878 1506 textKeysNeedLengths = true,
adamc@879 1507 supportsNextval = false,
adamc@882 1508 supportsNestedPrepared = false,
adamc@890 1509 sqlPrefix = "SET storage_engine=InnoDB;\n\n",
adamc@890 1510 supportsOctetLength = true}
adamc@866 1511
adamc@866 1512 end