changeset 879:b2a175a0f2ef

Demo working with MySQL
author Adam Chlipala <adamc@hcoop.net>
date Thu, 16 Jul 2009 18:10:29 -0400
parents a8952047e1d3
children 8e9f2d247dba
files include/urweb.h src/c/urweb.c src/checknest.sig src/checknest.sml src/cjr.sml src/cjr_print.sml src/compiler.sig src/compiler.sml src/mysql.sml src/postgres.sml src/prepare.sml src/settings.sig src/settings.sml src/sources
diffstat 14 files changed, 354 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/include/urweb.h	Thu Jul 16 16:29:13 2009 -0400
+++ b/include/urweb.h	Thu Jul 16 18:10:29 2009 -0400
@@ -207,4 +207,11 @@
 
 uw_Basis_string uw_Basis_unAs(uw_context, uw_Basis_string);
 
+extern char *uw_sqlfmtInt;
+extern char *uw_sqlfmtFloat;
+extern int uw_Estrings;
+extern char *uw_sqlsuffixString;
+extern char *uw_sqlsuffixBlob;
+extern char *uw_sqlfmtUint4;
+
 #endif
--- a/src/c/urweb.c	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/c/urweb.c	Thu Jul 16 18:10:29 2009 -0400
@@ -1932,13 +1932,15 @@
   return r;
 }
 
+char *uw_sqlfmtInt = "%lld::int8%n";
+
 char *uw_Basis_sqlifyInt(uw_context ctx, uw_Basis_int n) {
   int len;
   char *r;
 
   uw_check_heap(ctx, INTS_MAX + 6);
   r = ctx->heap.front;
-  sprintf(r, "%lld::int8%n", n, &len);
+  sprintf(r, uw_sqlfmtInt, n, &len);
   ctx->heap.front += len+1;
   return r;
 }
@@ -1950,13 +1952,15 @@
     return uw_Basis_sqlifyInt(ctx, *n);
 }
 
+char *uw_sqlfmtFloat = "%g::float8%n";
+
 char *uw_Basis_sqlifyFloat(uw_context ctx, uw_Basis_float n) {
   int len;
   char *r;
 
   uw_check_heap(ctx, FLOATS_MAX + 8);
   r = ctx->heap.front;
-  sprintf(r, "%g::float8%n", n, &len);
+  sprintf(r, uw_sqlfmtFloat, n, &len);
   ctx->heap.front += len+1;
   return r;
 }
@@ -1968,14 +1972,17 @@
     return uw_Basis_sqlifyFloat(ctx, *n);
 }
 
+int uw_Estrings = 1;
+char *uw_sqlsuffixString = "::text";
 
 uw_Basis_string uw_Basis_sqlifyString(uw_context ctx, uw_Basis_string s) {
   char *r, *s2;
 
-  uw_check_heap(ctx, strlen(s) * 2 + 10);
+  uw_check_heap(ctx, strlen(s) * 2 + 3 + uw_Estrings + strlen(uw_sqlsuffixString));
 
   r = s2 = ctx->heap.front;
-  *s2++ = 'E';
+  if (uw_Estrings)
+    *s2++ = 'E';
   *s2++ = '\'';
 
   for (; *s; s++) {
@@ -1993,26 +2000,32 @@
     default:
       if (isprint(c))
         *s2++ = c;
-      else {
+      else if (uw_Estrings) {
         sprintf(s2, "\\%03o", c);
         s2 += 4;
       }
+      else
+        uw_error(ctx, FATAL, "Non-printable character %u in string to SQLify", c);
     }
   }
 
-  strcpy(s2, "'::text");
-  ctx->heap.front = s2 + 8;
+  *s2++ = '\'';
+  strcpy(s2, uw_sqlsuffixString);
+  ctx->heap.front = s2 + 1 + strlen(uw_sqlsuffixString);
   return r;
 }
 
+char *uw_sqlsuffixBlob = "::bytea";
+
 uw_Basis_string uw_Basis_sqlifyBlob(uw_context ctx, uw_Basis_blob b) {
   char *r, *s2;
   size_t i;
 
-  uw_check_heap(ctx, b.size * 5 + 11);
+  uw_check_heap(ctx, b.size * 5 + 3 + uw_Estrings + strlen(uw_sqlsuffixBlob));
 
   r = s2 = ctx->heap.front;
-  *s2++ = 'E';
+  if (uw_Estrings)
+    *s2++ = 'E';
   *s2++ = '\'';
 
   for (i = 0; i < b.size; ++i) {
@@ -2030,15 +2043,18 @@
     default:
       if (isprint(c))
         *s2++ = c;
-      else {
+      else if (uw_Estrings) {
         sprintf(s2, "\\\\%03o", c);
         s2 += 5;
       }
+      else
+        uw_error(ctx, FATAL, "Non-printable character %u in blob to SQLify", c);
     }
   }
 
-  strcpy(s2, "'::bytea");
-  ctx->heap.front = s2 + 9;
+  *s2++ = '\'';
+  strcpy(s2, uw_sqlsuffixBlob);
+  ctx->heap.front = s2 + 1 + strlen(uw_sqlsuffixBlob);
   return r;
 }
 
@@ -2049,7 +2065,7 @@
 
   uw_check_heap(ctx, INTS_MAX + 7);
   r = ctx->heap.front;
-  sprintf(r, "%lld::int8%n", combo, &len);
+  sprintf(r, uw_sqlfmtInt, combo, &len);
   ctx->heap.front += len+1;
   return r;
 }
@@ -2066,13 +2082,15 @@
   return r;
 }
 
+char *uw_sqlfmtUint4 = "%u::int4%n";
+
 char *uw_Basis_sqlifyClient(uw_context ctx, uw_Basis_client cli) {
   int len;
   char *r;
 
   uw_check_heap(ctx, INTS_MAX + 7);
   r = ctx->heap.front;
-  sprintf(r, "%u::int4%n", cli, &len);
+  sprintf(r, uw_sqlfmtUint4, cli, &len);
   ctx->heap.front += len+1;
   return r;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/checknest.sig	Thu Jul 16 18:10:29 2009 -0400
@@ -0,0 +1,32 @@
+(* Copyright (c) 2009, Adam Chlipala
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - The names of contributors may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *)
+
+signature CHECKNEST = sig
+
+    val annotate : Cjr.file -> Cjr.file
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/checknest.sml	Thu Jul 16 18:10:29 2009 -0400
@@ -0,0 +1,178 @@
+(* Copyright (c) 2009, Adam Chlipala
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - The names of contributors may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *)
+
+structure Checknest :> CHECKNEST = struct
+
+open Cjr
+
+structure IS = IntBinarySet
+structure IM = IntBinaryMap
+
+fun expUses globals =
+    let
+        fun eu (e, _) =
+            case e of
+                EPrim _ => IS.empty
+              | ERel _ => IS.empty
+              | ENamed n => Option.getOpt (IM.find (globals, n), IS.empty)
+              | ECon (_, _, NONE) => IS.empty
+              | ECon (_, _, SOME e) => eu e
+              | ENone _ => IS.empty
+              | ESome (_, e) => eu e
+              | EFfi _ => IS.empty
+              | EFfiApp (_, _, es) => foldl IS.union IS.empty (map eu es)
+              | EApp (e, es) => foldl IS.union (eu e) (map eu es)
+
+              | EUnop (_, e) => eu e
+              | EBinop (_, e1, e2) => IS.union (eu e1, eu e2)
+
+              | ERecord (_, xes) => foldl (fn ((_, e), s) => IS.union (eu e, s)) IS.empty xes
+              | EField (e, _) => eu e
+
+              | ECase (e, pes, _) => foldl (fn ((_, e), s) => IS.union (eu e, s)) (eu e) pes
+
+              | EError (e, _) => eu e
+              | EReturnBlob {blob, mimeType, ...} => IS.union (eu blob, eu mimeType)
+
+              | EWrite e => eu e
+              | ESeq (e1, e2) => IS.union (eu e1, eu e2)
+              | ELet (_, _, e1, e2) => IS.union (eu e1, eu e2)
+
+              | EQuery {query, body, initial, prepared, ...} =>
+                let
+                    val s = IS.union (eu query, IS.union (eu body, eu initial))
+                in
+                    case prepared of
+                        SOME {id, ...} => IS.add (s, id)
+                      | _ => s
+                end
+              | EDml {dml, prepared, ...} =>
+                let
+                    val s = eu dml
+                in
+                    case prepared of
+                        SOME {id, ...} => IS.add (s, id)
+                      | _ => s
+                end
+              | ENextval {seq, prepared, ...} =>
+                let
+                    val s = eu seq
+                in
+                    case prepared of
+                        SOME {id, ...} => IS.add (s, id)
+                      | _ => s
+                end
+
+              | EUnurlify (e, _) => eu e
+    in
+        eu
+    end
+
+fun annotateExp globals =
+    let
+        fun ae (e as (_, loc)) =
+            case #1 e of
+                EPrim _ => e
+              | ERel _ => e
+              | ENamed n => e
+              | ECon (_, _, NONE) => e
+              | ECon (dk, pc, SOME e) => (ECon (dk, pc, SOME (ae e)), loc)
+              | ENone _ => e
+              | ESome (t, e) => (ESome (t, ae e), loc)
+              | EFfi _ => e
+              | EFfiApp (m, f, es) => (EFfiApp (m, f, map ae es), loc)
+              | EApp (e, es) => (EApp (ae e, map ae es), loc)
+
+              | EUnop (uo, e) => (EUnop (uo, ae e), loc)
+              | EBinop (bo, e1, e2) => (EBinop (bo, ae e1, ae e2), loc)
+
+              | ERecord (n, xes) => (ERecord (n, map (fn (x, e) => (x, ae e)) xes), loc)
+              | EField (e, f) => (EField (ae e, f), loc)
+
+              | ECase (e, pes, ts) => (ECase (ae e, map (fn (p, e) => (p, ae e)) pes, ts), loc)
+
+              | EError (e, t) => (EError (ae e, t), loc)
+              | EReturnBlob {blob, mimeType, t} => (EReturnBlob {blob = ae blob, mimeType = ae mimeType, t = t}, loc)
+
+              | EWrite e => (EWrite (ae e), loc)
+              | ESeq (e1, e2) => (ESeq (ae e1, ae e2), loc)
+              | ELet (x, t, e1, e2) => (ELet (x, t, ae e1, ae e2), loc)
+
+              | EQuery {exps, tables, rnum, state, query, body, initial, prepared} =>
+                (EQuery {exps = exps,
+                         tables = tables,
+                         rnum = rnum,
+                         state = state,
+                         query = ae query,
+                         body = ae body,
+                         initial = ae initial,
+                         prepared = case prepared of
+                                        NONE => NONE
+                                      | SOME {id, query, ...} => SOME {id = id, query = query,
+                                                                       nested = IS.member (expUses globals body, id)}},
+                 loc)
+              | EDml {dml, prepared} =>
+                (EDml {dml = ae dml,
+                       prepared = prepared}, loc)
+
+              | ENextval {seq, prepared} =>
+                (ENextval {seq = ae seq,
+                           prepared = prepared}, loc)
+
+              | EUnurlify (e, t) => (EUnurlify (ae e, t), loc)
+    in
+        ae
+    end
+
+fun annotate (ds, syms) =
+    let
+        val globals =
+            foldl (fn ((d, _), globals) =>
+                      case d of
+                          DVal (_, n, _, e) => IM.insert (globals, n, expUses globals e)
+                        | DFun (_, n, _, _, e) => IM.insert (globals, n, expUses globals e)
+                        | DFunRec fs =>
+                          let
+                              val s = foldl (fn ((_, _, _, _, e), s) => IS.union (expUses globals e, s)) IS.empty fs
+                          in
+                              foldl (fn ((_, n, _, _, _), globals) => IM.insert (globals, n, s)) globals fs
+                          end
+                        | _ => globals) IM.empty ds
+
+        val ds =
+            map (fn d as (_, loc) =>
+                    case #1 d of
+                        DVal (x, n, t, e) => (DVal (x, n, t, annotateExp globals e), loc)
+                      | DFun (x, n, ts, t, e) => (DFun (x, n, ts, t, annotateExp globals e), loc)
+                      | DFunRec fs => (DFunRec
+                                           (map (fn (x, n, ts, t, e) => (x, n, ts, t, annotateExp globals e)) fs), loc)
+                      | _ => d) ds
+    in
+        (ds, syms)
+    end
+
+end
--- a/src/cjr.sml	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/cjr.sml	Thu Jul 16 18:10:29 2009 -0400
@@ -89,11 +89,11 @@
                      query : exp,
                      body : exp,
                      initial : exp,
-                     prepared : (int * string) option }
+                     prepared : {id : int, query : string, nested : bool} option }
        | EDml of { dml : exp,
-                   prepared : (int * string) option }
+                   prepared : {id : int, dml : string} option }
        | ENextval of { seq : exp,
-                       prepared : (int * string) option }
+                       prepared : {id : int, query : string} option }
        | EUnurlify of exp * typ
 
 withtype exp = exp' located
--- a/src/cjr_print.sml	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/cjr_print.sml	Thu Jul 16 18:10:29 2009 -0400
@@ -1654,7 +1654,7 @@
                                  {loc = loc,
                                   cols = map (fn (_, t) => sql_type_in env t) outputs,
                                   doCols = doCols}]
-                   | SOME (id, query) =>
+                   | SOME {id, query, nested} =>
                      box [p_list_sepi newline
                                       (fn i => fn (e, t) =>
                                                   box [p_sql_type t,
@@ -1676,7 +1676,8 @@
                                           query = query,
                                           inputs = map #2 inputs,
                                           cols = map (fn (_, t) => sql_type_in env t) outputs,
-                                          doCols = doCols}],
+                                          doCols = doCols,
+                                          nested = nested}],
                  newline,
 
                  if wontLeakAnything then
@@ -1703,7 +1704,7 @@
                               newline,
                               newline,
                               #dml (Settings.currentDbms ()) loc]
-               | SOME (id, dml') =>
+               | SOME {id, dml = dml'} =>
                  let
                      val inputs = getPargs dml
                  in
@@ -1748,7 +1749,7 @@
                                                              seqName = case #1 seq of
                                                                            EPrim (Prim.String s) => SOME s
                                                                          | _ => NONE}
-               | SOME (id, query) => #nextvalPrepared (Settings.currentDbms ()) {loc = loc,
+               | SOME {id, query} => #nextvalPrepared (Settings.currentDbms ()) {loc = loc,
                                                                                  id = id,
                                                                                  query = query},
              newline,
--- a/src/compiler.sig	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/compiler.sig	Thu Jul 16 18:10:29 2009 -0400
@@ -99,6 +99,7 @@
     val cjrize : (Mono.file, Cjr.file) phase
     val scriptcheck : (Cjr.file, Cjr.file) phase
     val prepare : (Cjr.file, Cjr.file) phase
+    val checknest : (Cjr.file, Cjr.file) phase
     val sqlify : (Mono.file, Cjr.file) phase
 
     val toParseJob : (string, job) transform
@@ -138,6 +139,7 @@
     val toCjrize : (string, Cjr.file) transform
     val toScriptcheck : (string, Cjr.file) transform
     val toPrepare : (string, Cjr.file) transform
+    val toChecknest : (string, Cjr.file) transform
     val toSqlify : (string, Cjr.file) transform
 
 end
--- a/src/compiler.sml	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/compiler.sml	Thu Jul 16 18:10:29 2009 -0400
@@ -884,6 +884,13 @@
 
 val toPrepare = transform prepare "prepare" o toScriptcheck
 
+val checknest = {
+    func = fn f => if #supportsNestedPrepared (Settings.currentDbms ()) then f else Checknest.annotate f,
+    print = CjrPrint.p_file CjrEnv.empty
+}
+
+val toChecknest = transform checknest "checknest" o toPrepare
+
 val sqlify = {
     func = Cjrize.cjrize,
     print = CjrPrint.p_sql CjrEnv.empty
@@ -924,7 +931,7 @@
     end
 
 fun compile job =
-    case run toPrepare job of
+    case run toChecknest job of
         NONE => print "Ur compilation failed\n"
       | SOME file =>
         let
--- a/src/mysql.sml	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/mysql.sml	Thu Jul 16 18:10:29 2009 -0400
@@ -1,4 +1,4 @@
-(* Copyright (c) 2008-2009, Adam Chlipala
+(* Copyright (c) 2009, Adam Chlipala
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -376,7 +376,21 @@
 
              string "void uw_client_init(void) {",
              newline,
-             box [string "if (mysql_library_init(0, NULL, NULL)) {",
+             box [string "uw_sqlfmtInt = \"%lld%n\";",
+                  newline,
+                  string "uw_sqlfmtFloat = \"%g%n\";",
+                  newline,
+                  string "uw_Estrings = 0;",
+                  newline,
+                  string "uw_sqlsuffixString = \"\";",
+                  newline,
+                  string "uw_sqlsuffixBlob = \"\";",
+                  newline,
+                  string "uw_sqlfmtUint4 = \"%u%n\";",
+                  newline,
+                  newline,
+
+                  string "if (mysql_library_init(0, NULL, NULL)) {",
                   newline,
                   box [string "fprintf(stderr, \"Could not initialize MySQL library\\n\");",
                        newline,
@@ -867,7 +881,7 @@
          string "uw_pop_cleanup(ctx);",
          newline]
 
-fun queryPrepared {loc, id, query, inputs, cols, doCols} =
+fun queryPrepared {loc, id, query, inputs, cols, doCols, nested} =
     box [string "uw_conn *conn = uw_get_db(ctx);",
          newline,
          string "MYSQL_BIND in[",
@@ -901,18 +915,25 @@
                                                      | _ => buffers t,
                                                    newline]
                                           end) inputs,
-         string "MYSQL_STMT *stmt = conn->p",
-         string (Int.toString id),
-         string ";",
-         newline,
-         newline,
 
-         string "if (stmt == NULL) {",
-         newline,
+         if nested then
+             box [string "MYSQL_STMT *stmt;",
+                  newline]
+         else
+             box [string "MYSQL_STMT *stmt = conn->p",
+                  string (Int.toString id),
+                  string ";",
+                  newline,
+                  newline,
+
+                  string "if (stmt == NULL) {",
+                  newline],
+
          box [string "stmt = mysql_stmt_init(conn->conn);",
               newline,
               string "if (stmt == NULL) uw_error(ctx, FATAL, \"Out of memory allocating prepared statement\");",
               newline,
+              string "uw_push_cleanup(ctx, (void (*)(void *))mysql_stmt_close, stmt);",
               string "if (mysql_stmt_prepare(stmt, \"",
               string (String.toString query),
               string "\", ",
@@ -929,12 +950,18 @@
                    newline],
               string "}",
               newline,
-              string "conn->p",
-              string (Int.toString id),
-              string " = stmt;",
-              newline],
-         string "}",
-         newline,
+              if nested then
+                  box []
+              else
+                  box [string "conn->p",
+                       string (Int.toString id),
+                       string " = stmt;",
+                       newline]],
+         if nested then
+             box []
+         else
+             box [string "}",
+                  newline],
          newline,
 
          string "memset(in, 0, sizeof in);",
@@ -1086,7 +1113,13 @@
 
          queryCommon {loc = loc, cols = cols, doCols = doCols, query = box [string "\"",
                                                                             string (String.toString query),
-                                                                            string "\""]}]
+                                                                            string "\""]},
+
+         if nested then
+             box [string "uw_pop_cleanup(ctx);",
+                  newline]
+         else
+             box []]
 
 fun dmlCommon {loc, dml} =
     box [string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"",
@@ -1402,6 +1435,7 @@
                   supportsDeleteAs = false,
                   createSequence = fn s => "CREATE TABLE " ^ s ^ " (id INTEGER PRIMARY KEY AUTO_INCREMENT)",
                   textKeysNeedLengths = true,
-                  supportsNextval = false}
+                  supportsNextval = false,
+                  supportsNestedPrepared = false}
 
 end
--- a/src/postgres.sml	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/postgres.sml	Thu Jul 16 18:10:29 2009 -0400
@@ -247,7 +247,21 @@
 
 fun init {dbstring, prepared = ss, tables, views, sequences} =
     box [if #persistent (currentProtocol ()) then
-             box [string "void uw_client_init(void) { }",
+             box [string "void uw_client_init(void) {",
+                  newline,
+                  box [string "uw_sqlfmtInt = \"%lld::int8%n\";",
+                       newline,
+                       string "uw_sqlfmtFloat = \"%g::float8%n\";",
+                       newline,
+                       string "uw_Estrings = 1;",
+                       newline,
+                       string "uw_sqlsuffixString = \"::text\";",
+                       newline,
+                       string "uw_sqlsuffixBlob = \"::bytea\";",
+                       newline,
+                       string "uw_sqlfmtUint4 = \"%u::int4%n\";",
+                       newline],
+                  string "}",
                   newline,
                   newline,
 
@@ -639,7 +653,7 @@
                            p_ensql t (box [string "(*", e, string ")"]),
                            string ")"]
 
-fun queryPrepared {loc, id, query, inputs, cols, doCols} =
+fun queryPrepared {loc, id, query, inputs, cols, doCols, nested = _} =
     box [string "PGconn *conn = uw_get_db(ctx);",
          newline,
          string "const int paramFormats[] = { ",
@@ -782,8 +796,6 @@
          newline,
          newline,
 
-         string "uw_end_region(ctx);",
-         newline,
          string "n = PQntuples(res);",
          newline,
          string "if (n != 1) {",
@@ -811,7 +823,7 @@
     let
         val query = case seqName of
                         SOME s =>
-                        string ("SELECT NEXTVAL('" ^ s ^ "')")
+                        string ("\"SELECT NEXTVAL('" ^ s ^ "')\"")
                       | _ => box [string "uw_Basis_strcat(ctx, \"SELECT NEXTVAL('\", uw_Basis_strcat(ctx, ",
                                   seqE,
                                   string ", \"')\"))"]
@@ -878,7 +890,8 @@
                   supportsDeleteAs = true,
                   createSequence = fn s => "CREATE SEQUENCE " ^ s,
                   textKeysNeedLengths = false,
-                  supportsNextval = true}
+                  supportsNextval = true,
+                  supportsNestedPrepared = true}
 
 val () = setDbms "postgres"
 
--- a/src/prepare.sml	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/prepare.sml	Thu Jul 16 18:10:29 2009 -0400
@@ -199,7 +199,7 @@
                 in
                     ((EQuery {exps = exps, tables = tables, rnum = rnum,
                               state = state, query = query, body = body,
-                              initial = initial, prepared = SOME (#2 sns, s)}, loc),
+                              initial = initial, prepared = SOME {id = #2 sns, query = s, nested = true}}, loc),
                      ((s, n) :: #1 sns, #2 sns + 1))
                 end
         end
@@ -211,7 +211,7 @@
              let
                  val s = String.concat (rev ss)
              in
-                 ((EDml {dml = dml, prepared = SOME (#2 sns, s)}, loc),
+                 ((EDml {dml = dml, prepared = SOME {id = #2 sns, dml = s}}, loc),
                   ((s, n) :: #1 sns, #2 sns + 1))
              end)
 
@@ -234,7 +234,7 @@
                     let
                         val s = String.concat (rev ss)
                     in
-                        ((ENextval {seq = seq, prepared = SOME (#2 sns, s)}, loc),
+                        ((ENextval {seq = seq, prepared = SOME {id = #2 sns, query = s}}, loc),
                          ((s, n) :: #1 sns, #2 sns + 1))
                     end
             end
--- a/src/settings.sig	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/settings.sig	Thu Jul 16 18:10:29 2009 -0400
@@ -137,7 +137,8 @@
          queryPrepared : {loc : ErrorMsg.span, id : int, query : string,
                           inputs : sql_type list, cols : sql_type list,
                           doCols : ({wontLeakStrings : bool, col : int, typ : sql_type} -> Print.PD.pp_desc)
-                                   -> Print.PD.pp_desc}
+                                   -> Print.PD.pp_desc,
+                          nested : bool}
                          -> Print.PD.pp_desc,
          dml : ErrorMsg.span -> Print.PD.pp_desc,
          dmlPrepared : {loc : ErrorMsg.span, id : int, dml : string,
@@ -150,7 +151,8 @@
          supportsDeleteAs : bool,
          createSequence : string -> string,
          textKeysNeedLengths : bool,
-         supportsNextval : bool
+         supportsNextval : bool,
+         supportsNestedPrepared : bool
     }
 
     val addDbms : dbms -> unit
--- a/src/settings.sml	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/settings.sml	Thu Jul 16 18:10:29 2009 -0400
@@ -327,7 +327,8 @@
      queryPrepared : {loc : ErrorMsg.span, id : int, query : string,
                       inputs : sql_type list, cols : sql_type list,
                       doCols : ({wontLeakStrings : bool, col : int, typ : sql_type} -> Print.PD.pp_desc)
-                               -> Print.PD.pp_desc}
+                               -> Print.PD.pp_desc,
+                      nested : bool}
                      -> Print.PD.pp_desc,
      dml : ErrorMsg.span -> Print.PD.pp_desc,
      dmlPrepared : {loc : ErrorMsg.span, id : int, dml : string,
@@ -340,7 +341,8 @@
      supportsDeleteAs : bool,
      createSequence : string -> string,
      textKeysNeedLengths : bool,
-     supportsNextval : bool
+     supportsNextval : bool,
+     supportsNestedPrepared : bool
 }
 
 val dbmses = ref ([] : dbms list)
@@ -361,7 +363,8 @@
                   supportsDeleteAs = false,
                   createSequence = fn _ => "",
                   textKeysNeedLengths = false,
-                  supportsNextval = false} : dbms)
+                  supportsNextval = false,
+                  supportsNestedPrepared = false} : dbms)
 
 fun addDbms v = dbmses := v :: !dbmses
 fun setDbms s =
--- a/src/sources	Thu Jul 16 16:29:13 2009 -0400
+++ b/src/sources	Thu Jul 16 18:10:29 2009 -0400
@@ -192,6 +192,9 @@
 prepare.sig
 prepare.sml
 
+checknest.sig
+checknest.sml
+
 compiler.sig
 compiler.sml