changeset 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
files include/urweb.h src/c/urweb.c src/cjr_print.sml src/mono_opt.sml src/monoize.sml src/mysql.sml src/postgres.sml src/prepare.sml src/settings.sig src/settings.sml
diffstat 10 files changed, 520 insertions(+), 97 deletions(-) [+]
line wrap: on
line diff
--- a/include/urweb.h	Sun Jul 12 13:16:05 2009 -0400
+++ b/include/urweb.h	Sun Jul 12 15:05:40 2009 -0400
@@ -205,4 +205,6 @@
 char *uw_heap_front(uw_context);
 void uw_set_heap_front(uw_context, char*);
 
+uw_Basis_string uw_Basis_unAs(uw_context, uw_Basis_string);
+
 #endif
--- a/src/c/urweb.c	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/c/urweb.c	Sun Jul 12 15:05:40 2009 -0400
@@ -2742,3 +2742,38 @@
 
   longjmp(ctx->jmp_buf, RETURN_BLOB);
 }
+
+uw_Basis_string uw_Basis_unAs(uw_context ctx, uw_Basis_string s) {
+  uw_Basis_string r = uw_malloc(ctx, strlen(s) + 1);
+
+  for (; *s; ++s) {
+    if (s[0] == '\'') {
+      *r++ = '\'';
+      for (++s; *s; ++s) {
+        if (s[0] == '\'') {
+          *r++ = '\'';
+          break;
+        } else if (s[0] == '\\') {
+          if (s[1] == '\\') {
+            *r++ = '\\';
+            *r++ = '\\';
+            ++s;
+          } else if (s[1] == '\'') {
+            *r++ = '\\';
+            *r++ = '\'';
+            ++s;
+          } else
+            *r++ = '\'';
+        } else
+          *r++ = s[0];
+      }
+      if (*s == 0) break;
+    } else if (s[0] == 'T' && s[1] == '.')
+      ++s;
+    else
+      *r++ = s[0];
+  }
+
+  return r;
+}
+
--- a/src/cjr_print.sml	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/cjr_print.sml	Sun Jul 12 15:05:40 2009 -0400
@@ -2794,11 +2794,17 @@
                                                  string s,
                                                  string "(",
                                                  p_list (fn (x, t) =>
-                                                            box [string "uw_",
-                                                                 string (CharVector.map Char.toLower x),
-                                                                 space,
-                                                                 string (#p_sql_type (Settings.currentDbms ())
-                                                                                     (sql_type_in env t))]) xts,
+                                                            let
+                                                                val t = sql_type_in env t
+                                                            in
+                                                                box [string "uw_",
+                                                                     string (CharVector.map Char.toLower x),
+                                                                     space,
+                                                                     string (#p_sql_type (Settings.currentDbms ()) t),
+                                                                     case t of
+                                                                         Nullable _ => box []
+                                                                       | _ => string " NOT NULL"]
+                                                            end) xts,
                                                  case (pk, csts) of
                                                      ("", []) => box []
                                                    | _ => string ",",
--- a/src/mono_opt.sml	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/mono_opt.sml	Sun Jul 12 15:05:40 2009 -0400
@@ -83,18 +83,30 @@
                                                   "%" ^ hexIt ch)
 
 
-fun sqlifyInt n = attrifyInt n ^ "::int8"
-fun sqlifyFloat n = attrifyFloat n ^ "::float8"
+fun sqlifyInt n = attrifyInt n ^ "::" ^ #p_sql_type (Settings.currentDbms ()) Settings.Int
+fun sqlifyFloat n = attrifyFloat n ^ "::" ^ #p_sql_type (Settings.currentDbms ()) Settings.Float
 
-fun sqlifyString s = "E'" ^ String.translate (fn #"'" => "\\'"
-                                               | #"\\" => "\\\\"
-                                               | ch =>
-                                                 if Char.isPrint ch then
-                                                     str ch
-                                                 else
-                                                     "\\" ^ StringCvt.padLeft #"0" 3
-                                                                              (Int.fmt StringCvt.OCT (ord ch)))
-                                             (String.toString s) ^ "'::text"
+fun sqlifyString s = #sqlifyString (Settings.currentDbms ()) s
+
+fun unAs s =
+    let
+        fun doChars (cs, acc) =
+            case cs of
+                #"T" :: #"." :: cs => doChars (cs, acc)
+              | #"'" :: cs => doString (cs, acc)
+              | ch :: cs => doChars (cs, ch :: acc)
+              | [] => String.implode (rev acc)
+
+        and doString (cs, acc) =
+            case cs of
+                #"\\" :: #"\\" :: cs => doString (cs, #"\\" :: #"\\" :: acc)
+              | #"\\" :: #"'" :: cs => doString (cs, #"'" :: #"\\" :: acc)
+              | #"'" :: cs => doChars (cs, #"'" :: acc)
+              | ch :: cs => doString (cs, ch :: acc)
+              | [] => String.implode (rev acc)
+    in
+        doChars (String.explode s, [])
+    end
 
 fun exp e =
     case e of
@@ -442,6 +454,33 @@
             EPrim (Prim.String s)
         end
 
+      | EFfiApp ("Basis", "unAs", [(EPrim (Prim.String s), _)]) => 
+        EPrim (Prim.String (unAs s))
+      | EFfiApp ("Basis", "unAs", [e']) =>
+        let
+            fun parts (e as (_, loc)) =
+                case #1 e of
+                    EStrcat (s1, s2) =>
+                    (case (parts s1, parts s2) of
+                         (SOME p1, SOME p2) => SOME (p1 @ p2)
+                       | _ => NONE)
+                  | EPrim (Prim.String s) => SOME [(EPrim (Prim.String (unAs s)), loc)]
+                  | EFfiApp ("Basis", f, [_]) =>
+                    if String.isPrefix "sqlify" f then
+                        SOME [e]
+                    else
+                        NONE
+                  | _ => NONE
+        in
+            case parts e' of
+                SOME [e] => #1 e
+              | SOME es =>
+                (case rev es of
+                     (e as (_, loc)) :: es => #1 (foldl (fn (e, es) => (EStrcat (e, es), loc)) e es)
+                   | [] => raise Fail "MonoOpt impossible nil")
+              | NONE => e
+        end
+        
       | _ => e
 
 and optExp e = #1 (U.Exp.map {typ = typ, exp = exp} e)
--- a/src/monoize.sml	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/monoize.sml	Sun Jul 12 15:05:40 2009 -0400
@@ -1604,10 +1604,16 @@
             in
                 ((L'.EAbs ("tab", s, (L'.TFun (s, s), loc),
                            (L'.EAbs ("e", s, s,
-                                     strcat [sc "DELETE FROM ",
-                                             (L'.ERel 1, loc),
-                                             sc " AS T WHERE ",
-                                             (L'.ERel 0, loc)]), loc)), loc),
+                                     if #supportsDeleteAs (Settings.currentDbms ()) then
+                                         strcat [sc "DELETE FROM ",
+                                                 (L'.ERel 1, loc),
+                                                 sc " AS T WHERE ",
+                                                 (L'.ERel 0, loc)]
+                                     else
+                                         strcat [sc "DELETE FROM ",
+                                                 (L'.ERel 1, loc),
+                                                 sc " WHERE ",
+                                                 (L'.EFfiApp ("Basis", "unAs", [(L'.ERel 0, loc)]), loc)]), loc)), loc),
                  fm)
             end
 
--- a/src/mysql.sml	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/mysql.sml	Sun Jul 12 15:05:40 2009 -0400
@@ -55,6 +55,278 @@
       | Client => "MYSQL_TYPE_LONG"
       | Nullable t => p_buffer_type t
 
+fun p_sql_type_base t =
+    case t of
+        Int => "bigint"
+      | Float => "double"
+      | String => "longtext"
+      | Bool => "tinyint"
+      | Time => "timestamp"
+      | Blob => "longblob"
+      | Channel => "bigint"
+      | Client => "int"
+      | Nullable t => p_sql_type_base t
+
+val ident = String.translate (fn #"'" => "PRIME"
+                               | ch => str ch)
+
+fun checkRel (table, checkNullable) (s, xts) =
+    let
+        val sl = CharVector.map Char.toLower s
+
+        val q = "SELECT COUNT(*) FROM information_schema." ^ table ^ " WHERE table_name = '"
+                ^ sl ^ "'"
+
+        val q' = String.concat ["SELECT COUNT(*) FROM information_schema.columns WHERE table_name = '",
+                                sl,
+                                "' AND (",
+                                String.concatWith " OR "
+                                                  (map (fn (x, t) =>
+                                                           String.concat ["(column_name = 'uw_",
+                                                                          CharVector.map
+                                                                              Char.toLower (ident x),
+                                                                          "' AND data_type = '",
+                                                                          p_sql_type_base t,
+                                                                          "'",
+                                                                          if checkNullable then
+                                                                              (" AND is_nullable = '"
+                                                                               ^ (if isNotNull t then
+                                                                                      "NO"
+                                                                                  else
+                                                                                      "YES")
+                                                                               ^ "'")
+                                                                          else
+                                                                              "",
+                                                                          ")"]) xts),
+                                ")"]
+
+        val q'' = String.concat ["SELECT COUNT(*) FROM information_schema.columns WHERE table_name = '",
+                                 sl,
+                                 "' AND column_name LIKE 'uw_%'"]
+    in
+        box [string "if (mysql_query(conn->conn, \"",
+             string q,
+             string "\")) {",
+             newline,
+             box [string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Query failed:\\n",
+                  string q,
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if ((res = mysql_store_result(conn->conn)) == NULL) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Result store failed:\\n",
+                  string q,
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if (mysql_num_fields(res) != 1) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Bad column count:\\n",
+                  string q,
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if ((row = mysql_fetch_row(res)) == NULL) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Row fetch failed:\\n",
+                  string q,
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if (strcmp(row[0], \"1\")) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Table '",
+                  string s,
+                  string "' does not exist.\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+             string "mysql_free_result(res);",
+             newline,
+             newline,
+
+             string "if (mysql_query(conn->conn, \"",
+             string q',
+             string "\")) {",
+             newline,
+             box [string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Query failed:\\n",
+                  string q',
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if ((res = mysql_store_result(conn->conn)) == NULL) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Result store failed:\\n",
+                  string q',
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if (mysql_num_fields(res) != 1) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Bad column count:\\n",
+                  string q',
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if ((row = mysql_fetch_row(res)) == NULL) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Row fetch failed:\\n",
+                  string q',
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if (strcmp(row[0], \"",
+             string (Int.toString (length xts)),
+             string "\")) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Table '",
+                  string s,
+                  string "' has the wrong column types.\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+             string "mysql_free_result(res);",
+             newline,
+             newline,
+             
+             string "if (mysql_query(conn->conn, \"",
+             string q'',
+             string "\")) {",
+             newline,
+             box [string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Query failed:\\n",
+                  string q'',
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if ((res = mysql_store_result(conn->conn)) == NULL) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Result store failed:\\n",
+                  string q'',
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if (mysql_num_fields(res) != 1) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Bad column count:\\n",
+                  string q'',
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if ((row = mysql_fetch_row(res)) == NULL) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Row fetch failed:\\n",
+                  string q'',
+                  string "\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
+             string "if (strcmp(row[0], \"",
+             string (Int.toString (length xts)),
+             string "\")) {",
+             newline,
+             box [string "mysql_free_result(res);",
+                  newline,
+                  string "mysql_close(conn->conn);",
+                  newline,
+                  string "uw_error(ctx, FATAL, \"Table '",
+                  string s,
+                  string "' has extra columns.\");",
+                  newline],
+             string "}",
+             newline,
+             newline,
+             string "mysql_free_result(res);",
+             newline]
+    end
+
 fun init {dbstring, prepared = ss, tables, views, sequences} =
     let
         val host = ref NONE
@@ -102,8 +374,37 @@
              newline,
              newline,
 
+             string "void uw_client_init(void) {",
+             newline,
+             box [string "if (mysql_library_init(0, NULL, NULL)) {",
+                  newline,
+                  box [string "fprintf(stderr, \"Could not initialize MySQL library\\n\");",
+                       newline,
+                       string "exit(1);",
+                       newline],
+                  string "}",
+                  newline],
+             string "}",
+             newline,
+             newline,
+
              if #persistent (currentProtocol ()) then
-                 box [string "static void uw_db_prepare(uw_context ctx) {",
+                 box [string "static void uw_db_validate(uw_context ctx) {",
+                      newline,
+                      string "uw_conn *conn = uw_get_db(ctx);",
+                      newline,
+                      string "MYSQL_RES *res;",
+                      newline,
+                      string "MYSQL_ROW row;",
+                      newline,
+                      newline,
+                      p_list_sep newline (checkRel ("tables", true)) tables,
+                      p_list_sep newline (checkRel ("views", false)) views,
+                      string "}",
+                      newline,
+                      newline,
+
+                      string "static void uw_db_prepare(uw_context ctx) {",
                       newline,
                       string "uw_conn *conn = uw_get_db(ctx);",
                       newline,
@@ -147,6 +448,10 @@
                                                                uhoh false "Out of memory allocating prepared statement" [],
                                                                string "}",
                                                                newline,
+                                                               string "conn->p",
+                                                               string (Int.toString i),
+                                                               string " = stmt;",
+                                                               newline,
 
                                                                string "if (mysql_stmt_prepare(stmt, \"",
                                                                string (String.toString s),
@@ -162,10 +467,6 @@
                                                                     newline,
                                                                     uhoh true "Error preparing statement: %s" ["msg"]],
                                                                string "}",
-                                                               newline,
-                                                               string "conn->p",
-                                                               string (Int.toString i),
-                                                               string " = stmt;",
                                                                newline]
                                                       end)
                                   ss,
@@ -199,7 +500,7 @@
                | SOME n => string (Int.toString n),
              string ", ",
              stringOf unix_socket,
-             string ", 0)) {",
+             string ", 0) == NULL) {",
              newline,
              box [string "char msg[1024];",
                   newline,
@@ -214,7 +515,7 @@
              newline,
              string "}",
              newline,
-             string "conn = calloc(1, sizeof(conn));",
+             string "conn = calloc(1, sizeof(uw_conn));",
              newline,
              string "conn->conn = mysql;",
              newline,
@@ -471,19 +772,19 @@
 
          string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"",
          string (ErrorMsg.spanToString loc),
-         string ": Error executing query\");",
+         string ": Error executing query: %s\", mysql_error(conn->conn));",
          newline,
          newline,
 
          string "if (mysql_stmt_store_result(stmt)) uw_error(ctx, FATAL, \"",
          string (ErrorMsg.spanToString loc),
-         string ": Error storing query result\");",
+         string ": Error storing query result: %s\", mysql_error(conn->conn));",
          newline,
          newline,
 
          string "if (mysql_stmt_bind_result(stmt, out)) uw_error(ctx, FATAL, \"",
          string (ErrorMsg.spanToString loc),
-         string ": Error binding query result\");",
+         string ": Error binding query result: %s\", mysql_error(conn->conn));",
          newline,
          newline,
 
@@ -496,9 +797,9 @@
          newline,
          newline,
 
-         string "if (r != MYSQL_NO_DATA) uw_error(ctx, FATAL, \"",
+         string "if (r == 1) uw_error(ctx, FATAL, \"",
          string (ErrorMsg.spanToString loc),
-         string ": query result fetching failed\");",
+         string ": query result fetching failed (%d): %s\", r, mysql_error(conn->conn));",
          newline]    
 
 fun query {loc, cols, doCols} =
@@ -514,7 +815,7 @@
          newline,
          string "if (mysql_stmt_prepare(stmt, query, strlen(query))) uw_error(ctx, FATAL, \"",
          string (ErrorMsg.spanToString loc),
-         string "\");",
+         string ": error preparing statement: %s\", mysql_error(conn->conn));",
          newline,
          newline,
 
@@ -760,21 +1061,24 @@
 fun nextval _ = box []
 fun nextvalPrepared _ = box []
 
+fun sqlifyString s = "CAST('" ^ String.translate (fn #"'" => "\\'"
+                                                   | #"\\" => "\\\\"
+                                                   | ch =>
+                                                     if Char.isPrint ch then
+                                                         str ch
+                                                     else
+                                                         (ErrorMsg.error
+                                                              "Non-printing character found in SQL string literal";
+                                                          ""))
+                                                 (String.toString s) ^ "' AS longtext)"
+
+fun p_cast (s, t) = "CAST(" ^ s ^ " AS " ^ p_sql_type t ^ ")"
+
+fun p_blank _ = "?"
+
 val () = addDbms {name = "mysql",
                   header = "mysql/mysql.h",
                   link = "-lmysqlclient",
-                  global_init = box [string "void uw_client_init() {",
-                                     newline,
-                                     box [string "if (mysql_library_init(0, NULL, NULL)) {",
-                                          newline,
-                                          box [string "fprintf(stderr, \"Could not initialize MySQL library\\n\");",
-                                               newline,
-                                               string "exit(1);",
-                                               newline],
-                                          string "}",
-                                          newline],
-                              string "}",
-                                     newline],
                   init = init,
                   p_sql_type = p_sql_type,
                   query = query,
@@ -782,6 +1086,10 @@
                   dml = dml,
                   dmlPrepared = dmlPrepared,
                   nextval = nextval,
-                  nextvalPrepared = nextvalPrepared}
+                  nextvalPrepared = nextvalPrepared,
+                  sqlifyString = sqlifyString,
+                  p_cast = p_cast,
+                  p_blank = p_blank,
+                  supportsDeleteAs = false}
 
 end
--- a/src/postgres.sml	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/postgres.sml	Sun Jul 12 15:05:40 2009 -0400
@@ -247,7 +247,11 @@
 
 fun init {dbstring, prepared = ss, tables, views, sequences} =
     box [if #persistent (currentProtocol ()) then
-             box [string "static void uw_db_validate(uw_context ctx) {",
+             box [string "void uw_client_init() { }",
+                  newline,
+                  newline,
+
+                  string "static void uw_db_validate(uw_context ctx) {",
                   newline,
                   string "PGconn *conn = uw_get_db(ctx);",
                   newline,
@@ -509,10 +513,10 @@
                          String => getter t
                        | _ => box [string "({",
                                    newline,
-                                   string (p_sql_type t),
+                                   string (p_sql_ctype t),
                                    space,
                                    string "*tmp = uw_malloc(ctx, sizeof(",
-                                   string (p_sql_type t),
+                                   string (p_sql_ctype t),
                                    string "));",
                                    newline,
                                    string "*tmp = ",
@@ -528,7 +532,7 @@
                      string (Int.toString i),
                      string ") ? ",
                      box [string "({",
-                          string (p_sql_type t),
+                          string (p_sql_ctype t),
                           space,
                           string "tmp;",
                           newline,
@@ -828,11 +832,23 @@
                                                 string (String.toString query),
                                                 string "\""]}]
 
+fun sqlifyString s = "E'" ^ String.translate (fn #"'" => "\\'"
+                                               | #"\\" => "\\\\"
+                                               | ch =>
+                                                 if Char.isPrint ch then
+                                                     str ch
+                                                 else
+                                                     "\\" ^ StringCvt.padLeft #"0" 3
+                                                                              (Int.fmt StringCvt.OCT (ord ch)))
+                                             (String.toString s) ^ "'::text"
+
+fun p_cast (s, t) = s ^ "::" ^ p_sql_type t
+
+fun p_blank (n, t) = p_cast ("$" ^ Int.toString n, t)
+
 val () = addDbms {name = "postgres",
                   header = "postgresql/libpq-fe.h",
                   link = "-lpq",
-                  global_init = box [string "void uw_client_init() { }",
-                                     newline],
                   p_sql_type = p_sql_type,
                   init = init,
                   query = query,
@@ -840,7 +856,12 @@
                   dml = dml,
                   dmlPrepared = dmlPrepared,
                   nextval = nextval,
-                  nextvalPrepared = nextvalPrepared}
+                  nextvalPrepared = nextvalPrepared,
+                  sqlifyString = sqlifyString,
+                  p_cast = p_cast,
+                  p_blank = p_blank,
+                  supportsDeleteAs = true}
+
 val () = setDbms "postgres"
 
 end
--- a/src/prepare.sml	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/prepare.sml	Sun Jul 12 15:05:40 2009 -0400
@@ -28,47 +28,45 @@
 structure Prepare :> PREPARE = struct
 
 open Cjr
+open Settings
 
 fun prepString (e, ss, n) =
-    case #1 e of
-        EPrim (Prim.String s) =>
-        SOME (s :: ss, n)
-      | EFfiApp ("Basis", "strcat", [e1, e2]) =>
-        (case prepString (e1, ss, n) of
-             NONE => NONE
-           | SOME (ss, n) => prepString (e2, ss, n))
-      | EFfiApp ("Basis", "sqlifyInt", [e]) =>
-        SOME ("$" ^ Int.toString (n + 1) ^ "::int8" :: ss, n + 1)
-      | EFfiApp ("Basis", "sqlifyFloat", [e]) =>
-        SOME ("$" ^ Int.toString (n + 1) ^ "::float8" :: ss, n + 1)
-      | EFfiApp ("Basis", "sqlifyString", [e]) =>
-        SOME ("$" ^ Int.toString (n + 1) ^ "::text" :: ss, n + 1)
-      | EFfiApp ("Basis", "sqlifyBool", [e]) =>
-        SOME ("$" ^ Int.toString (n + 1) ^ "::bool" :: ss, n + 1)
-      | EFfiApp ("Basis", "sqlifyTime", [e]) =>
-        SOME ("$" ^ Int.toString (n + 1) ^ "::timestamp" :: ss, n + 1)
-      | EFfiApp ("Basis", "sqlifyBlob", [e]) =>
-        SOME ("$" ^ Int.toString (n + 1) ^ "::bytea" :: ss, n + 1)
-      | EFfiApp ("Basis", "sqlifyChannel", [e]) =>
-        SOME ("$" ^ Int.toString (n + 1) ^ "::int8" :: ss, n + 1)
-      | EFfiApp ("Basis", "sqlifyClient", [e]) =>
-        SOME ("$" ^ Int.toString (n + 1) ^ "::int4" :: ss, n + 1)
+    let
+        fun doOne t =
+            SOME (#p_blank (Settings.currentDbms ()) (n + 1, t) :: ss, n + 1)
+    in
+        case #1 e of
+            EPrim (Prim.String s) =>
+            SOME (s :: ss, n)
+          | EFfiApp ("Basis", "strcat", [e1, e2]) =>
+            (case prepString (e1, ss, n) of
+                 NONE => NONE
+               | SOME (ss, n) => prepString (e2, ss, n))
+          | EFfiApp ("Basis", "sqlifyInt", [e]) => doOne Int
+          | EFfiApp ("Basis", "sqlifyFloat", [e]) => doOne Float
+          | EFfiApp ("Basis", "sqlifyString", [e]) => doOne String
+          | EFfiApp ("Basis", "sqlifyBool", [e]) => doOne Bool
+          | EFfiApp ("Basis", "sqlifyTime", [e]) => doOne Time
+          | EFfiApp ("Basis", "sqlifyBlob", [e]) => doOne Blob
+          | EFfiApp ("Basis", "sqlifyChannel", [e]) => doOne Channel
+          | EFfiApp ("Basis", "sqlifyClient", [e]) => doOne Client
 
-      | ECase (e,
-               [((PNone _, _),
-                 (EPrim (Prim.String "NULL"), _)),
-                ((PSome (_, (PVar _, _)), _),
-                 (EFfiApp (m, x, [(ERel 0, _)]), _))],
-               _) => prepString ((EFfiApp (m, x, [e]), #2 e), ss, n)
+          | ECase (e,
+                   [((PNone _, _),
+                     (EPrim (Prim.String "NULL"), _)),
+                    ((PSome (_, (PVar _, _)), _),
+                     (EFfiApp (m, x, [(ERel 0, _)]), _))],
+                   _) => prepString ((EFfiApp (m, x, [e]), #2 e), ss, n)
 
-      | ECase (e,
-               [((PCon (_, PConFfi {mod = "Basis", con = "True", ...}, _), _),
-                 (EPrim (Prim.String "TRUE"), _)),
-                ((PCon (_, PConFfi {mod = "Basis", con = "False", ...}, _), _),
-                 (EPrim (Prim.String "FALSE"), _))],
-               _) => SOME ("$" ^ Int.toString (n + 1) ^ "::bool" :: ss, n + 1)
+          | ECase (e,
+                   [((PCon (_, PConFfi {mod = "Basis", con = "True", ...}, _), _),
+                     (EPrim (Prim.String "TRUE"), _)),
+                    ((PCon (_, PConFfi {mod = "Basis", con = "False", ...}, _), _),
+                     (EPrim (Prim.String "FALSE"), _))],
+                   _) => doOne Bool
 
-      | _ => NONE
+          | _ => NONE
+    end
 
 fun prepExp (e as (_, loc), sns) =
     case #1 e of
--- a/src/settings.sig	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/settings.sig	Sun Jul 12 15:05:40 2009 -0400
@@ -123,15 +123,13 @@
          (* Include this C header file *)
          link : string,
          (* Pass these linker arguments *)
-         global_init : Print.PD.pp_desc,
-         (* Define uw_client_init() *)
          p_sql_type : sql_type -> string,
          init : {dbstring : string,
                  prepared : (string * int) list,
                  tables : (string * (string * sql_type) list) list,
                  views : (string * (string * sql_type) list) list,
                  sequences : string list} -> Print.PD.pp_desc,
-         (* Define uw_db_init(), uw_db_close(), uw_db_begin(), uw_db_commit(), and uw_db_rollback() *)
+         (* Define uw_client_init(), uw_db_init(), uw_db_close(), uw_db_begin(), uw_db_commit(), and uw_db_rollback() *)
          query : {loc : ErrorMsg.span, cols : sql_type list,
                   doCols : ({wontLeakStrings : bool, col : int, typ : sql_type} -> Print.PD.pp_desc)
                            -> Print.PD.pp_desc}
@@ -145,7 +143,11 @@
          dmlPrepared : {loc : ErrorMsg.span, id : int, dml : string,
                         inputs : sql_type list} -> Print.PD.pp_desc,
          nextval : ErrorMsg.span -> Print.PD.pp_desc,
-         nextvalPrepared : {loc : ErrorMsg.span, id : int, query : string} -> Print.PD.pp_desc
+         nextvalPrepared : {loc : ErrorMsg.span, id : int, query : string} -> Print.PD.pp_desc,
+         sqlifyString : string -> string,
+         p_cast : string * sql_type -> string,
+         p_blank : int * sql_type -> string (* Prepared statement input *),
+         supportsDeleteAs : bool
     }
 
     val addDbms : dbms -> unit
--- a/src/settings.sml	Sun Jul 12 13:16:05 2009 -0400
+++ b/src/settings.sml	Sun Jul 12 15:05:40 2009 -0400
@@ -314,7 +314,6 @@
      name : string,
      header : string,
      link : string,
-     global_init : Print.PD.pp_desc,
      p_sql_type : sql_type -> string,
      init : {dbstring : string,
              prepared : (string * int) list,
@@ -334,14 +333,17 @@
      dmlPrepared : {loc : ErrorMsg.span, id : int, dml : string,
                     inputs : sql_type list} -> Print.PD.pp_desc,
      nextval : ErrorMsg.span -> Print.PD.pp_desc,
-     nextvalPrepared : {loc : ErrorMsg.span, id : int, query : string} -> Print.PD.pp_desc
+     nextvalPrepared : {loc : ErrorMsg.span, id : int, query : string} -> Print.PD.pp_desc,
+     sqlifyString : string -> string,
+     p_cast : string * sql_type -> string,
+     p_blank : int * sql_type -> string,
+     supportsDeleteAs : bool
 }
 
 val dbmses = ref ([] : dbms list)
 val curDb = ref ({name = "",
                   header = "",
                   link = "",
-                  global_init = Print.box [],
                   p_sql_type = fn _ => "",
                   init = fn _ => Print.box [],
                   query = fn _ => Print.box [],
@@ -349,7 +351,11 @@
                   dml = fn _ => Print.box [],
                   dmlPrepared = fn _ => Print.box [],
                   nextval = fn _ => Print.box [],
-                  nextvalPrepared = fn _ => Print.box []} : dbms)
+                  nextvalPrepared = fn _ => Print.box [],
+                  sqlifyString = fn s => s,
+                  p_cast = fn _ => "",
+                  p_blank = fn _ => "",
+                  supportsDeleteAs = false} : dbms)
 
 fun addDbms v = dbmses := v :: !dbmses
 fun setDbms s =