changeset 858:346cf1908a17

Avoid using prepared statements for non-persistent protocols
author Adam Chlipala <adamc@hcoop.net>
date Sat, 27 Jun 2009 10:50:45 -0400
parents 3d2f6cb6d54a
children 60240acd15b9
files src/cgi.sig src/cgi.sml src/cjr.sml src/cjr_print.sml src/prepare.sml src/scriptcheck.sml src/settings.sig src/settings.sml src/sources
diffstat 9 files changed, 420 insertions(+), 312 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cgi.sig	Sat Jun 27 10:50:45 2009 -0400
@@ -0,0 +1,30 @@
+(* Copyright (c) 2008-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 CGI = sig
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cgi.sml	Sat Jun 27 10:50:45 2009 -0400
@@ -0,0 +1,36 @@
+(* Copyright (c) 2008-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 Cgi :> CGI = struct
+
+open Settings
+
+val () = addProtocol {name = "cgi",
+                      link = clibFile "request.o" ^ " " ^ clibFile "cgi.o",
+                      persistent = false}
+
+end
--- a/src/cjr.sml	Sat Jun 27 10:30:51 2009 -0400
+++ b/src/cjr.sml	Sat Jun 27 10:50:45 2009 -0400
@@ -89,11 +89,11 @@
                      query : exp,
                      body : exp,
                      initial : exp,
-                     prepared : int option }
+                     prepared : (int * string) option }
        | EDml of { dml : exp,
-                   prepared : int option }
+                   prepared : (int * string) option }
        | ENextval of { seq : exp,
-                       prepared : int option }
+                       prepared : (int * string) option }
        | EUnurlify of exp * typ
 
 withtype exp = exp' located
--- a/src/cjr_print.sml	Sat Jun 27 10:30:51 2009 -0400
+++ b/src/cjr_print.sml	Sat Jun 27 10:50:45 2009 -0400
@@ -1641,11 +1641,19 @@
                  string "PGresult *res = ",
                  case prepared of
                      NONE => string "PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);"
-                   | SOME n => box [string "PQexecPrepared(conn, \"uw",
-                                    string (Int.toString n),
-                                    string "\", ",
-                                    string (Int.toString (length (getPargs query))),
-                                    string ", paramValues, paramLengths, paramFormats, 0);"],
+                   | SOME (n, s) =>
+                     if #persistent (Settings.currentProtocol ()) then
+                         box [string "PQexecPrepared(conn, \"uw",
+                              string (Int.toString n),
+                              string "\", ",
+                              string (Int.toString (length (getPargs query))),
+                              string ", paramValues, paramLengths, paramFormats, 0);"]
+                     else
+                         box [string "PQexecParams(conn, \"",
+                              string (String.toString s),
+                              string "\", ",
+                              string (Int.toString (length (getPargs query))),
+                              string ", NULL, paramValues, paramLengths, paramFormats, 0);"],
                  newline,
                  newline,
 
@@ -1820,11 +1828,19 @@
              string "PGresult *res = ",
              case prepared of
                  NONE => string "PQexecParams(conn, dml, 0, NULL, NULL, NULL, NULL, 0);"
-               | SOME n => box [string "PQexecPrepared(conn, \"uw",
-                                string (Int.toString n),
-                                string "\", ",
-                                string (Int.toString (length (getPargs dml))),
-                                string ", paramValues, paramLengths, paramFormats, 0);"],
+               | SOME (n, s) =>
+                 if #persistent (Settings.currentProtocol ()) then
+                     box [string "PQexecPrepared(conn, \"uw",
+                          string (Int.toString n),
+                          string "\", ",
+                          string (Int.toString (length (getPargs dml))),
+                          string ", paramValues, paramLengths, paramFormats, 0);"]
+                 else
+                     box [string "PQexecParams(conn, \"",
+                          string (String.toString s),
+                          string "\", ",
+                          string (Int.toString (length (getPargs dml))),
+                          string ", NULL, paramValues, paramLengths, paramFormats, 0);"],
              newline,
              newline,
 
@@ -1892,9 +1908,15 @@
                  string "PGresult *res = ",
                  case prepared of
                      NONE => string "PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0);"
-                   | SOME n => box [string "PQexecPrepared(conn, \"uw",
-                                    string (Int.toString n),
-                                    string "\", 0, NULL, NULL, NULL, 0);"],
+                   | SOME (n, s) =>
+                     if #persistent (Settings.currentProtocol ()) then
+                         box [string "PQexecPrepared(conn, \"uw",
+                              string (Int.toString n),
+                              string "\", 0, NULL, NULL, NULL, 0);"]
+                     else
+                         box [string "PQexecParams(conn, \"uw",
+                              string (Int.toString n),
+                              string "\", 0, NULL, NULL, NULL, NULL, 0);"],
                  newline,
                  string "uw_Basis_int n;",
                  newline,
@@ -2306,46 +2328,49 @@
              newline,
              string "}"]
       | DPreparedStatements ss =>
-        box [string "static void uw_db_prepare(uw_context ctx) {",
-             newline,
-             string "PGconn *conn = uw_get_db(ctx);",
-             newline,
-             string "PGresult *res;",
-             newline,
-             newline,
+        if #persistent (Settings.currentProtocol ()) then
+            box [string "static void uw_db_prepare(uw_context ctx) {",
+                 newline,
+                 string "PGconn *conn = uw_get_db(ctx);",
+                 newline,
+                 string "PGresult *res;",
+                 newline,
+                 newline,
 
-             p_list_sepi newline (fn i => fn (s, n) =>
-                                             box [string "res = PQprepare(conn, \"uw",
-                                                  string (Int.toString i),
-                                                  string "\", \"",
-                                                  string (String.toString s),
-                                                  string "\", ",
-                                                  string (Int.toString n),
-                                                  string ", NULL);",
-                                                  newline,
-                                                  string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
-                                                  newline,
-                                                  box [string "char msg[1024];",
-                                                       newline,
-                                                       string "strncpy(msg, PQerrorMessage(conn), 1024);",
-                                                       newline,
-                                                       string "msg[1023] = 0;",
-                                                       newline,
-                                                       string "PQclear(res);",
-                                                       newline,
-                                                       string "PQfinish(conn);",
-                                                       newline,
-                                                       string "uw_error(ctx, FATAL, \"Unable to create prepared statement:\\n",
-                                                       string (String.toString s),
-                                                       string "\\n%s\", msg);",
-                                                       newline],
-                                                  string "}",
-                                                  newline,
-                                                  string "PQclear(res);",
-                                                  newline])
-                         ss,
-             
-             string "}"]
+                 p_list_sepi newline (fn i => fn (s, n) =>
+                                                 box [string "res = PQprepare(conn, \"uw",
+                                                      string (Int.toString i),
+                                                      string "\", \"",
+                                                      string (String.toString s),
+                                                      string "\", ",
+                                                      string (Int.toString n),
+                                                      string ", NULL);",
+                                                      newline,
+                                                      string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
+                                                      newline,
+                                                      box [string "char msg[1024];",
+                                                           newline,
+                                                           string "strncpy(msg, PQerrorMessage(conn), 1024);",
+                                                           newline,
+                                                           string "msg[1023] = 0;",
+                                                           newline,
+                                                           string "PQclear(res);",
+                                                           newline,
+                                                           string "PQfinish(conn);",
+                                                           newline,
+                                                           string "uw_error(ctx, FATAL, \"Unable to create prepared statement:\\n",
+                                                           string (String.toString s),
+                                                           string "\\n%s\", msg);",
+                                                           newline],
+                                                      string "}",
+                                                      newline,
+                                                      string "PQclear(res);",
+                                                      newline])
+                             ss,
+                 
+                 string "}"]
+        else
+            string "static void uw_db_prepare(uw_context ctx) { }"
 
       | DJavaScript s => box [string "static char jslib[] = \"",
                               string (String.toString s),
@@ -2928,256 +2953,259 @@
                                           | _ => NONE) ds
 
         val validate =
-            box [string "static void uw_db_validate(uw_context ctx) {",
-                 newline,
-                 string "PGconn *conn = uw_get_db(ctx);",
-                 newline,
-                 string "PGresult *res;",
-                 newline,
-                 newline,
-                 p_list_sep newline
-                            (fn (s, xts) =>
-                                let
-                                    val sl = CharVector.map Char.toLower s
+            if #persistent (Settings.currentProtocol ()) then
+                box [string "static void uw_db_validate(uw_context ctx) {",
+                     newline,
+                     string "PGconn *conn = uw_get_db(ctx);",
+                     newline,
+                     string "PGresult *res;",
+                     newline,
+                     newline,
+                     p_list_sep newline
+                                (fn (s, xts) =>
+                                    let
+                                        val sl = CharVector.map Char.toLower s
 
-                                    val q = "SELECT COUNT(*) FROM pg_class WHERE relname = '"
-                                            ^ sl ^ "'"
+                                        val q = "SELECT COUNT(*) FROM pg_class WHERE relname = '"
+                                                ^ sl ^ "'"
 
-                                    val q' = String.concat ["SELECT COUNT(*) FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '",
-                                                            sl,
-                                                            "') AND (",
-                                                            String.concatWith " OR "
-                                                              (map (fn (x, t) =>
-                                                                       String.concat ["(attname = 'uw_",
-                                                                                      CharVector.map
-                                                                                          Char.toLower (ident x),
-                                                                                      "' AND atttypid = (SELECT oid FROM pg_type",
-                                                                                      " WHERE typname = '",
-                                                                                      p_sqltype_base' env t,
-                                                                                      "') AND attnotnull = ",
-                                                                                      if is_not_null t then
-                                                                                          "TRUE"
-                                                                                      else
-                                                                                          "FALSE",
-                                                                                      ")"]) xts),
-                                                            ")"]
+                                        val q' = String.concat ["SELECT COUNT(*) FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '",
+                                                                sl,
+                                                                "') AND (",
+                                                                String.concatWith " OR "
+                                                                                  (map (fn (x, t) =>
+                                                                                           String.concat ["(attname = 'uw_",
+                                                                                                          CharVector.map
+                                                                                                              Char.toLower (ident x),
+                                                                                                          "' AND atttypid = (SELECT oid FROM pg_type",
+                                                                                                          " WHERE typname = '",
+                                                                                                          p_sqltype_base' env t,
+                                                                                                          "') AND attnotnull = ",
+                                                                                                          if is_not_null t then
+                                                                                                              "TRUE"
+                                                                                                          else
+                                                                                                              "FALSE",
+                                                                                                          ")"]) xts),
+                                                                ")"]
 
-                                    val q'' = String.concat ["SELECT COUNT(*) FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '",
-                                                             sl,
-                                                             "') AND attname LIKE 'uw_%'"]
-                                in
-                                    box [string "res = PQexec(conn, \"",
-                                         string q,
-                                         string "\");",
-                                         newline,
-                                         newline,
-                                         string "if (res == NULL) {",
-                                         newline,
-                                         box [string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
-                                         newline,
-                                         box [string "char msg[1024];",
-                                              newline,
-                                              string "strncpy(msg, PQerrorMessage(conn), 1024);",
-                                              newline,
-                                              string "msg[1023] = 0;",
-                                              newline,
-                                              string "PQclear(res);",
-                                              newline,
-                                              string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Query failed:\\n",
-                                              string q,
-                                              string "\\n%s\", msg);",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "if (strcmp(PQgetvalue(res, 0, 0), \"1\")) {",
-                                         newline,
-                                         box [string "PQclear(res);",
-                                              newline,
-                                              string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Table '",
-                                              string s,
-                                              string "' does not exist.\");",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "PQclear(res);",
-                                         newline,
+                                        val q'' = String.concat ["SELECT COUNT(*) FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '",
+                                                                 sl,
+                                                                 "') AND attname LIKE 'uw_%'"]
+                                    in
+                                        box [string "res = PQexec(conn, \"",
+                                             string q,
+                                             string "\");",
+                                             newline,
+                                             newline,
+                                             string "if (res == NULL) {",
+                                             newline,
+                                             box [string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
+                                             newline,
+                                             box [string "char msg[1024];",
+                                                  newline,
+                                                  string "strncpy(msg, PQerrorMessage(conn), 1024);",
+                                                  newline,
+                                                  string "msg[1023] = 0;",
+                                                  newline,
+                                                  string "PQclear(res);",
+                                                  newline,
+                                                  string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Query failed:\\n",
+                                                  string q,
+                                                  string "\\n%s\", msg);",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "if (strcmp(PQgetvalue(res, 0, 0), \"1\")) {",
+                                             newline,
+                                             box [string "PQclear(res);",
+                                                  newline,
+                                                  string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Table '",
+                                                  string s,
+                                                  string "' does not exist.\");",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "PQclear(res);",
+                                             newline,
 
-                                         string "res = PQexec(conn, \"",
-                                         string q',
-                                         string "\");",
-                                         newline,
-                                         newline,
-                                         string "if (res == NULL) {",
-                                         newline,
-                                         box [string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
-                                         newline,
-                                         box [string "char msg[1024];",
-                                              newline,
-                                              string "strncpy(msg, PQerrorMessage(conn), 1024);",
-                                              newline,
-                                              string "msg[1023] = 0;",
-                                              newline,
-                                              string "PQclear(res);",
-                                              newline,
-                                              string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Query failed:\\n",
-                                              string q',
-                                              string "\\n%s\", msg);",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "if (strcmp(PQgetvalue(res, 0, 0), \"",
-                                         string (Int.toString (length xts)),
-                                         string "\")) {",
-                                         newline,
-                                         box [string "PQclear(res);",
-                                              newline,
-                                              string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Table '",
-                                              string s,
-                                              string "' has the wrong column types.\");",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "PQclear(res);",
-                                         newline,
-                                         newline,
+                                             string "res = PQexec(conn, \"",
+                                             string q',
+                                             string "\");",
+                                             newline,
+                                             newline,
+                                             string "if (res == NULL) {",
+                                             newline,
+                                             box [string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
+                                             newline,
+                                             box [string "char msg[1024];",
+                                                  newline,
+                                                  string "strncpy(msg, PQerrorMessage(conn), 1024);",
+                                                  newline,
+                                                  string "msg[1023] = 0;",
+                                                  newline,
+                                                  string "PQclear(res);",
+                                                  newline,
+                                                  string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Query failed:\\n",
+                                                  string q',
+                                                  string "\\n%s\", msg);",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "if (strcmp(PQgetvalue(res, 0, 0), \"",
+                                             string (Int.toString (length xts)),
+                                             string "\")) {",
+                                             newline,
+                                             box [string "PQclear(res);",
+                                                  newline,
+                                                  string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Table '",
+                                                  string s,
+                                                  string "' has the wrong column types.\");",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "PQclear(res);",
+                                             newline,
+                                             newline,
 
-                                         string "res = PQexec(conn, \"",
-                                         string q'',
-                                         string "\");",
-                                         newline,
-                                         newline,
-                                         string "if (res == NULL) {",
-                                         newline,
-                                         box [string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
-                                         newline,
-                                         box [string "char msg[1024];",
-                                              newline,
-                                              string "strncpy(msg, PQerrorMessage(conn), 1024);",
-                                              newline,
-                                              string "msg[1023] = 0;",
-                                              newline,
-                                              string "PQclear(res);",
-                                              newline,
-                                              string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Query failed:\\n",
-                                              string q'',
-                                              string "\\n%s\", msg);",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "if (strcmp(PQgetvalue(res, 0, 0), \"",
-                                         string (Int.toString (length xts)),
-                                         string "\")) {",
-                                         newline,
-                                         box [string "PQclear(res);",
-                                              newline,
-                                              string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Table '",
-                                              string s,
-                                              string "' has extra columns.\");",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "PQclear(res);",
-                                         newline]
-                                end) tables,
+                                             string "res = PQexec(conn, \"",
+                                             string q'',
+                                             string "\");",
+                                             newline,
+                                             newline,
+                                             string "if (res == NULL) {",
+                                             newline,
+                                             box [string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
+                                             newline,
+                                             box [string "char msg[1024];",
+                                                  newline,
+                                                  string "strncpy(msg, PQerrorMessage(conn), 1024);",
+                                                  newline,
+                                                  string "msg[1023] = 0;",
+                                                  newline,
+                                                  string "PQclear(res);",
+                                                  newline,
+                                                  string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Query failed:\\n",
+                                                  string q'',
+                                                  string "\\n%s\", msg);",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "if (strcmp(PQgetvalue(res, 0, 0), \"",
+                                             string (Int.toString (length xts)),
+                                             string "\")) {",
+                                             newline,
+                                             box [string "PQclear(res);",
+                                                  newline,
+                                                  string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Table '",
+                                                  string s,
+                                                  string "' has extra columns.\");",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "PQclear(res);",
+                                             newline]
+                                    end) tables,
 
-                 p_list_sep newline
-                            (fn s =>
-                                let
-                                    val sl = CharVector.map Char.toLower s
+                     p_list_sep newline
+                                (fn s =>
+                                    let
+                                        val sl = CharVector.map Char.toLower s
 
-                                    val q = "SELECT COUNT(*) FROM pg_class WHERE relname = '"
-                                            ^ sl ^ "' AND relkind = 'S'"
-                                in
-                                    box [string "res = PQexec(conn, \"",
-                                         string q,
-                                         string "\");",
-                                         newline,
-                                         newline,
-                                         string "if (res == NULL) {",
-                                         newline,
-                                         box [string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
-                                         newline,
-                                         box [string "char msg[1024];",
-                                              newline,
-                                              string "strncpy(msg, PQerrorMessage(conn), 1024);",
-                                              newline,
-                                              string "msg[1023] = 0;",
-                                              newline,
-                                              string "PQclear(res);",
-                                              newline,
-                                              string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Query failed:\\n",
-                                              string q,
-                                              string "\\n%s\", msg);",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "if (strcmp(PQgetvalue(res, 0, 0), \"1\")) {",
-                                         newline,
-                                         box [string "PQclear(res);",
-                                              newline,
-                                              string "PQfinish(conn);",
-                                              newline,
-                                              string "uw_error(ctx, FATAL, \"Sequence '",
-                                              string s,
-                                              string "' does not exist.\");",
-                                              newline],
-                                         string "}",
-                                         newline,
-                                         newline,
-                                         string "PQclear(res);",
-                                         newline]
-                                end) sequences,
+                                        val q = "SELECT COUNT(*) FROM pg_class WHERE relname = '"
+                                                ^ sl ^ "' AND relkind = 'S'"
+                                    in
+                                        box [string "res = PQexec(conn, \"",
+                                             string q,
+                                             string "\");",
+                                             newline,
+                                             newline,
+                                             string "if (res == NULL) {",
+                                             newline,
+                                             box [string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Out of memory allocating query result.\");",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "if (PQresultStatus(res) != PGRES_TUPLES_OK) {",
+                                             newline,
+                                             box [string "char msg[1024];",
+                                                  newline,
+                                                  string "strncpy(msg, PQerrorMessage(conn), 1024);",
+                                                  newline,
+                                                  string "msg[1023] = 0;",
+                                                  newline,
+                                                  string "PQclear(res);",
+                                                  newline,
+                                                  string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Query failed:\\n",
+                                                  string q,
+                                                  string "\\n%s\", msg);",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "if (strcmp(PQgetvalue(res, 0, 0), \"1\")) {",
+                                             newline,
+                                             box [string "PQclear(res);",
+                                                  newline,
+                                                  string "PQfinish(conn);",
+                                                  newline,
+                                                  string "uw_error(ctx, FATAL, \"Sequence '",
+                                                  string s,
+                                                  string "' does not exist.\");",
+                                                  newline],
+                                             string "}",
+                                             newline,
+                                             newline,
+                                             string "PQclear(res);",
+                                             newline]
+                                    end) sequences,
 
-                 string "}"]
+                     string "}"]
+            else
+                string "static void uw_db_validate(uw_context ctx) { }"
 
         val hasDb = List.exists (fn (DDatabase _, _) => true | _ => false) ds
 
--- a/src/prepare.sml	Sat Jun 27 10:30:51 2009 -0400
+++ b/src/prepare.sml	Sat Jun 27 10:50:45 2009 -0400
@@ -196,18 +196,26 @@
                           initial = initial, prepared = NONE}, loc),
                  sns)
               | SOME (ss, n) =>
-                ((EQuery {exps = exps, tables = tables, rnum = rnum,
-                          state = state, query = query, body = body,
-                          initial = initial, prepared = SOME (#2 sns)}, loc),
-                 ((String.concat (rev ss), n) :: #1 sns, #2 sns + 1))
+                let
+                    val s = String.concat (rev ss)
+                in
+                    ((EQuery {exps = exps, tables = tables, rnum = rnum,
+                              state = state, query = query, body = body,
+                              initial = initial, prepared = SOME (#2 sns, s)}, loc),
+                     ((s, n) :: #1 sns, #2 sns + 1))
+                end
         end
 
       | EDml {dml, ...} =>
         (case prepString (dml, [], 0) of
              NONE => (e, sns)
            | SOME (ss, n) =>
-             ((EDml {dml = dml, prepared = SOME (#2 sns)}, loc),
-              ((String.concat (rev ss), n) :: #1 sns, #2 sns + 1)))
+             let
+                 val s = String.concat (rev ss)
+             in
+                 ((EDml {dml = dml, prepared = SOME (#2 sns, s)}, loc),
+                  ((s, n) :: #1 sns, #2 sns + 1))
+             end)
 
       | ENextval {seq, ...} =>
         let
@@ -224,8 +232,12 @@
             case prepString (s, [], 0) of
                 NONE => (e, sns)
               | SOME (ss, n) =>
-                ((ENextval {seq = seq, prepared = SOME (#2 sns)}, loc),
-                 ((String.concat (rev ss), n) :: #1 sns, #2 sns + 1))
+                let
+                    val s = String.concat (rev ss)
+                in
+                    ((ENextval {seq = seq, prepared = SOME (#2 sns, s)}, loc),
+                     ((s, n) :: #1 sns, #2 sns + 1))
+                end
         end
 
       | EUnurlify (e, t) =>
--- a/src/scriptcheck.sml	Sat Jun 27 10:30:51 2009 -0400
+++ b/src/scriptcheck.sml	Sat Jun 27 10:50:45 2009 -0400
@@ -165,7 +165,7 @@
         val ps = map (fn (ek, x, n, ts, t, _) =>
                          (ek, x, n, ts, t,
                           if IS.member (push_ids, n) then
-                              (if not (#supportsPush proto) andalso not (!foundBad) then
+                              (if not (#persistent proto) andalso not (!foundBad) then
                                    (foundBad := true;
                                     ErrorMsg.error ("This program needs server push, but the current protocol ("
                                                     ^ #name proto ^ ") doesn't support that."))
--- a/src/settings.sig	Sat Jun 27 10:30:51 2009 -0400
+++ b/src/settings.sig	Sat Jun 27 10:50:45 2009 -0400
@@ -89,7 +89,7 @@
     type protocol = {
         name : string,      (* Call it this on the command line *)
         link : string,      (* Pass these linker arguments *)
-        supportsPush : bool (* Is Ur/Web message-passing supported? *)
+        persistent : bool   (* Multiple requests per process? *)
     }
     val addProtocol : protocol -> unit
     val getProtocol : string -> protocol option
@@ -99,4 +99,6 @@
     val setDebug : bool -> unit
     val getDebug : unit -> bool
 
+    val clibFile : string -> string
+
 end
--- a/src/settings.sml	Sat Jun 27 10:30:51 2009 -0400
+++ b/src/settings.sml	Sat Jun 27 10:50:45 2009 -0400
@@ -252,7 +252,7 @@
 type protocol = {
      name : string,
      link : string,
-     supportsPush : bool
+     persistent : bool
 }
 val protocols = ref ([] : protocol list)
 fun addProtocol p = protocols := p :: !protocols
@@ -263,12 +263,9 @@
 
 val http = {name = "http",
             link = clibFile "request.o" ^ " " ^ clibFile "http.o",
-            supportsPush = true}
+            persistent = true}
 
 val () = addProtocol http
-val () = addProtocol {name = "cgi",
-                      link = clibFile "request.o" ^ " " ^ clibFile "cgi.o",
-                      supportsPush = false}
 
 val curProto = ref http
 fun setProtocol name =
--- a/src/sources	Sat Jun 27 10:30:51 2009 -0400
+++ b/src/sources	Sat Jun 27 10:50:45 2009 -0400
@@ -16,6 +16,9 @@
 settings.sig
 settings.sml
 
+cgi.sig
+cgi.sml
+
 print.sig
 print.sml