# HG changeset patch # User Adam Chlipala # Date 1283709657 14400 # Node ID acabf39350603d46c3bb37699f2dcebe03413171 # Parent a671c986f517b9e9c080aff8a1b878b2ab440527 tryDml diff -r a671c986f517 -r acabf3935060 lib/ur/basis.urs --- a/lib/ur/basis.urs Sun Sep 05 12:50:06 2010 -0400 +++ b/lib/ur/basis.urs Sun Sep 05 14:00:57 2010 -0400 @@ -535,6 +535,8 @@ type dml val dml : dml -> transaction unit +val tryDml : dml -> transaction (option string) +(* Returns an error message on failure. *) val insert : fields ::: {Type} -> uniques ::: {{Unit}} -> sql_table fields uniques diff -r a671c986f517 -r acabf3935060 src/checknest.sml --- a/src/checknest.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/checknest.sml Sun Sep 05 14:00:57 2010 -0400 @@ -138,9 +138,10 @@ | SOME {id, query, ...} => SOME {id = id, query = query, nested = IS.member (expUses globals body, id)}}, loc) - | EDml {dml, prepared} => + | EDml {dml, prepared, mode} => (EDml {dml = ae dml, - prepared = prepared}, loc) + prepared = prepared, + mode = mode}, loc) | ENextval {seq, prepared} => (ENextval {seq = ae seq, diff -r a671c986f517 -r acabf3935060 src/cjr.sml --- a/src/cjr.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/cjr.sml Sun Sep 05 14:00:57 2010 -0400 @@ -56,6 +56,8 @@ withtype pat = pat' located +datatype failure_mode = datatype Settings.failure_mode + datatype exp' = EPrim of Prim.t | ERel of int @@ -92,7 +94,8 @@ initial : exp, prepared : {id : int, query : string, nested : bool} option } | EDml of { dml : exp, - prepared : {id : int, dml : string} option } + prepared : {id : int, dml : string} option, + mode : failure_mode } | ENextval of { seq : exp, prepared : {id : int, query : string} option } | ESetval of { seq : exp, count : exp } diff -r a671c986f517 -r acabf3935060 src/cjr_print.sml --- a/src/cjr_print.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/cjr_print.sml Sun Sep 05 14:00:57 2010 -0400 @@ -1791,8 +1791,11 @@ box []] end - | EDml {dml, prepared} => - box [string "(uw_begin_region(ctx), ({", + | EDml {dml, prepared, mode} => + box [case mode of + Settings.Error => box [] + | Settings.None => string "({const char *uw_errmsg = NULL;", + string "(uw_begin_region(ctx), ({", newline, case prepared of NONE => box [string "char *dml = ", @@ -1800,7 +1803,7 @@ string ";", newline, newline, - #dml (Settings.currentDbms ()) loc] + #dml (Settings.currentDbms ()) (loc, mode)] | SOME {id, dml = dml'} => let val inputs = getPargs dml @@ -1823,16 +1826,23 @@ #dmlPrepared (Settings.currentDbms ()) {loc = loc, id = id, dml = dml', - inputs = map #2 inputs}] + inputs = map #2 inputs, + mode = mode}] end, newline, newline, - string "uw_end_region(ctx);", newline, - string "uw_unit_v;", + + case mode of + Settings.Error => string "uw_unit_v;" + | Settings.None => string "uw_errmsg ? uw_strdup(ctx, uw_errmsg) : NULL;", + newline, - string "}))"] + string "}))", + case mode of + Settings.Error => box [] + | Settings.None => string ";})"] | ENextval {seq, prepared} => box [string "({", diff -r a671c986f517 -r acabf3935060 src/cjrize.sml --- a/src/cjrize.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/cjrize.sml Sun Sep 05 14:00:57 2010 -0400 @@ -455,11 +455,11 @@ query = query, body = body, initial = initial, prepared = NONE}, loc), sm) end - | L.EDml e => + | L.EDml (e, mode) => let val (e, sm) = cifyExp (e, sm) in - ((L'.EDml {dml = e, prepared = NONE}, loc), sm) + ((L'.EDml {dml = e, prepared = NONE, mode = mode}, loc), sm) end | L.ENextval e => diff -r a671c986f517 -r acabf3935060 src/iflow.sml --- a/src/iflow.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/iflow.sml Sun Sep 05 14:00:57 2010 -0400 @@ -2040,7 +2040,7 @@ val (ts, cs) = MonoUtil.Exp.fold {typ = fn (_, st) => st, exp = fn (e, st as (cs, ts)) => case e of - EDml e => + EDml (e, _) => (case parse dml e of NONE => st | SOME c => @@ -2080,7 +2080,7 @@ (St.assert [AReln (Eq, [r, x])]; evalExp (acc :: r :: env) b k))} q end) - | EDml e => + | EDml (e, _) => (case parse dml e of NONE => (print ("Warning: Information flow checker can't parse DML command at " ^ ErrorMsg.spanToString loc ^ "\n"); @@ -2400,7 +2400,7 @@ query = doExp env query, body = doExp (Unknown :: Unknown :: env) body, initial = doExp env initial}, loc) - | EDml e1 => + | EDml (e1, mode) => (case parse dml e1 of NONE => () | SOME c => @@ -2410,7 +2410,7 @@ tables := SS.add (!tables, tab) | Update (tab, _, _) => tables := SS.add (!tables, tab); - (EDml (doExp env e1), loc)) + (EDml (doExp env e1, mode), loc)) | ENextval e1 => (ENextval (doExp env e1), loc) | ESetval (e1, e2) => (ESetval (doExp env e1, doExp env e2), loc) | EUnurlify (e1, t, b) => (EUnurlify (doExp env e1, t, b), loc) diff -r a671c986f517 -r acabf3935060 src/jscomp.sml --- a/src/jscomp.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/jscomp.sml Sun Sep 05 14:00:57 2010 -0400 @@ -1147,11 +1147,11 @@ ((EQuery {exps = exps, tables = tables, state = state, query = query, body = body, initial = initial}, loc), st) end - | EDml e => + | EDml (e, mode) => let val (e, st) = exp outer (e, st) in - ((EDml e, loc), st) + ((EDml (e, mode), loc), st) end | ENextval e => let diff -r a671c986f517 -r acabf3935060 src/mono.sml --- a/src/mono.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/mono.sml Sun Sep 05 14:00:57 2010 -0400 @@ -66,6 +66,8 @@ datatype effect = datatype Export.effect datatype export_kind = datatype Export.export_kind +datatype failure_mode = datatype Settings.failure_mode + datatype exp' = EPrim of Prim.t | ERel of int @@ -104,7 +106,7 @@ query : exp, body : exp, initial : exp } - | EDml of exp + | EDml of exp * failure_mode | ENextval of exp | ESetval of exp * exp diff -r a671c986f517 -r acabf3935060 src/mono_print.sml --- a/src/mono_print.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/mono_print.sml Sun Sep 05 14:00:57 2010 -0400 @@ -322,9 +322,9 @@ string "in", space, p_exp (E.pushERel (E.pushERel env "r" dummyt NONE) "acc" dummyt NONE) body] - | EDml e => box [string "dml(", - p_exp env e, - string ")"] + | EDml (e, _) => box [string "dml(", + p_exp env e, + string ")"] | ENextval e => box [string "nextval(", p_exp env e, string ")"] diff -r a671c986f517 -r acabf3935060 src/mono_reduce.sml --- a/src/mono_reduce.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/mono_reduce.sml Sun Sep 05 14:00:57 2010 -0400 @@ -465,7 +465,7 @@ [ReadDb], summarize (d + 2) body] - | EDml e => summarize d e @ [WriteDb] + | EDml (e, _) => summarize d e @ [WriteDb] | ENextval e => summarize d e @ [WriteDb] | ESetval (e1, e2) => summarize d e1 @ summarize d e2 @ [WriteDb] | EUnurlify (e, _, _) => summarize d e diff -r a671c986f517 -r acabf3935060 src/mono_util.sml --- a/src/mono_util.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/mono_util.sml Sun Sep 05 14:00:57 2010 -0400 @@ -332,10 +332,10 @@ initial = initial'}, loc))))))) - | EDml e => + | EDml (e, fm) => S.map2 (mfe ctx e, fn e' => - (EDml e', loc)) + (EDml (e', fm), loc)) | ENextval e => S.map2 (mfe ctx e, fn e' => diff -r a671c986f517 -r acabf3935060 src/monoize.sml --- a/src/monoize.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/monoize.sml Sun Sep 05 14:00:57 2010 -0400 @@ -1748,7 +1748,15 @@ let val (e, fm) = monoExp (env, st, fm) e in - ((L'.EDml e, loc), + ((L'.EDml (e, L'.Error), loc), + fm) + end + + | L.EFfiApp ("Basis", "tryDml", [e]) => + let + val (e, fm) = monoExp (env, st, fm) e + in + ((L'.EDml (e, L'.None), loc), fm) end @@ -4014,13 +4022,13 @@ val e = foldl (fn ((x, v), e) => (L'.ESeq ( - (L'.EDml (L'.EStrcat ( - (L'.EPrim (Prim.String ("UPDATE uw_" - ^ tab - ^ " SET uw_" - ^ x - ^ " = NULL WHERE ")), loc), - cond (x, v)), loc), loc), + (L'.EDml ((L'.EStrcat ( + (L'.EPrim (Prim.String ("UPDATE uw_" + ^ tab + ^ " SET uw_" + ^ x + ^ " = NULL WHERE ")), loc), + cond (x, v)), loc), L'.Error), loc), e), loc)) e nullable @@ -4039,7 +4047,7 @@ ^ tab ^ " WHERE ")), loc), cond eb), loc) - ebs), loc), + ebs, L'.Error), loc), e), loc) in e @@ -4067,15 +4075,15 @@ [] => e | (x, _) :: ebs => (L'.ESeq ( - (L'.EDml (L'.EPrim (Prim.String - (foldl (fn ((x, _), s) => - s ^ ", uw_" ^ x ^ " = NULL") - ("UPDATE uw_" - ^ tab - ^ " SET uw_" - ^ x - ^ " = NULL") - ebs)), loc), loc), + (L'.EDml ((L'.EPrim (Prim.String + (foldl (fn ((x, _), s) => + s ^ ", uw_" ^ x ^ " = NULL") + ("UPDATE uw_" + ^ tab + ^ " SET uw_" + ^ x + ^ " = NULL") + ebs)), loc), L'.Error), loc), e), loc) val e = @@ -4083,8 +4091,8 @@ [] => e | eb :: ebs => (L'.ESeq ( - (L'.EDml (L'.EPrim (Prim.String ("DELETE FROM uw_" - ^ tab)), loc), loc), + (L'.EDml ((L'.EPrim (Prim.String ("DELETE FROM uw_" + ^ tab)), loc), L'.Error), loc), e), loc) in e diff -r a671c986f517 -r acabf3935060 src/mysql.sml --- a/src/mysql.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/mysql.sml Sun Sep 05 14:00:57 2010 -0400 @@ -1194,16 +1194,19 @@ else box []] -fun dmlCommon {loc, dml} = - box [string "if (mysql_stmt_execute(stmt)) uw_error(ctx, FATAL, \"", - string (ErrorMsg.spanToString loc), - string ": Error executing DML: %s\\n%s\", ", - dml, - string ", mysql_error(conn->conn));", +fun dmlCommon {loc, dml, mode} = + box [string "if (mysql_stmt_execute(stmt)) ", + case mode of + Settings.Error => box [string "uw_error(ctx, FATAL, \"", + string (ErrorMsg.spanToString loc), + string ": Error executing DML: %s\\n%s\", ", + dml, + string ", mysql_error(conn->conn));"] + | Settings.None => string "uw_errmsg = mysql_error(conn->conn);", newline, newline] -fun dml loc = +fun dml (loc, mode) = box [string "uw_conn *conn = uw_get_db(ctx);", newline, string "MYSQL_STMT *stmt = mysql_stmt_init(conn->conn);", @@ -1220,12 +1223,12 @@ newline, newline, - dmlCommon {loc = loc, dml = string "dml"}, + dmlCommon {loc = loc, dml = string "dml", mode = mode}, string "uw_pop_cleanup(ctx);", newline] -fun dmlPrepared {loc, id, dml, inputs} = +fun dmlPrepared {loc, id, dml, inputs, mode} = box [string "uw_conn *conn = uw_get_db(ctx);", newline, string "MYSQL_BIND in[", @@ -1471,7 +1474,7 @@ dmlCommon {loc = loc, dml = box [string "\"", string (String.toCString dml), - string "\""]}] + string "\""], mode = mode}] fun nextval {loc, seqE, seqName} = box [string "uw_conn *conn = uw_get_db(ctx);", diff -r a671c986f517 -r acabf3935060 src/postgres.sml --- a/src/postgres.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/postgres.sml Sun Sep 05 14:00:57 2010 -0400 @@ -708,7 +708,7 @@ string (String.toCString query), string "\""]}] -fun dmlCommon {loc, dml} = +fun dmlCommon {loc, dml, mode} = box [string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating DML result.\");", newline, newline, @@ -723,13 +723,15 @@ newline], string "}", newline, - string "PQclear(res);", - newline, - string "uw_error(ctx, FATAL, \"", - string (ErrorMsg.spanToString loc), - string ": DML failed:\\n%s\\n%s\", ", - dml, - string ", PQerrorMessage(conn));", + case mode of + Settings.Error => box [string "PQclear(res);", + newline, + string "uw_error(ctx, FATAL, \"", + string (ErrorMsg.spanToString loc), + string ": DML failed:\\n%s\\n%s\", ", + dml, + string ", PQerrorMessage(conn));"] + | Settings.None => string "uw_errmsg = PQerrorMessage(conn);", newline], string "}", newline, @@ -738,15 +740,15 @@ string "PQclear(res);", newline] -fun dml loc = +fun dml (loc, mode) = box [string "PGconn *conn = uw_get_db(ctx);", newline, string "PGresult *res = PQexecParams(conn, dml, 0, NULL, NULL, NULL, NULL, 0);", newline, newline, - dmlCommon {loc = loc, dml = string "dml"}] + dmlCommon {loc = loc, dml = string "dml", mode = mode}] -fun dmlPrepared {loc, id, dml, inputs} = +fun dmlPrepared {loc, id, dml, inputs, mode} = box [string "PGconn *conn = uw_get_db(ctx);", newline, string "const int paramFormats[] = { ", @@ -787,7 +789,7 @@ newline, dmlCommon {loc = loc, dml = box [string "\"", string (String.toCString dml), - string "\""]}] + string "\""], mode = mode}] fun nextvalCommon {loc, query} = box [string "if (res == NULL) uw_error(ctx, FATAL, \"Out of memory allocating nextval result.\");", diff -r a671c986f517 -r acabf3935060 src/prepare.sml --- a/src/prepare.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/prepare.sml Sun Sep 05 14:00:57 2010 -0400 @@ -246,11 +246,11 @@ initial = initial, prepared = SOME {id = id, query = s, nested = true}}, loc), st) end - | EDml {dml, ...} => + | EDml {dml, mode, ...} => (case prepString (dml, st) of NONE => (e, st) | SOME (id, s, st) => - ((EDml {dml = dml, prepared = SOME {id = id, dml = s}}, loc), st)) + ((EDml {dml = dml, prepared = SOME {id = id, dml = s}, mode = mode}, loc), st)) | ENextval {seq, ...} => if #supportsNextval (Settings.currentDbms ()) then diff -r a671c986f517 -r acabf3935060 src/settings.sig --- a/src/settings.sig Sun Sep 05 12:50:06 2010 -0400 +++ b/src/settings.sig Sun Sep 05 14:00:57 2010 -0400 @@ -124,6 +124,8 @@ val isBlob : sql_type -> bool val isNotNull : sql_type -> bool + datatype failure_mode = Error | None + type dbms = { name : string, (* Call it this on the command line *) @@ -149,9 +151,9 @@ -> Print.PD.pp_desc, nested : bool} -> Print.PD.pp_desc, - dml : ErrorMsg.span -> Print.PD.pp_desc, + dml : ErrorMsg.span * failure_mode -> Print.PD.pp_desc, dmlPrepared : {loc : ErrorMsg.span, id : int, dml : string, - inputs : sql_type list} -> Print.PD.pp_desc, + inputs : sql_type list, mode : failure_mode} -> Print.PD.pp_desc, nextval : {loc : ErrorMsg.span, seqE : Print.PD.pp_desc, seqName : string option} -> Print.PD.pp_desc, nextvalPrepared : {loc : ErrorMsg.span, id : int, query : string} -> Print.PD.pp_desc, setval : {loc : ErrorMsg.span, seqE : Print.PD.pp_desc, count : Print.PD.pp_desc} -> Print.PD.pp_desc, diff -r a671c986f517 -r acabf3935060 src/settings.sml --- a/src/settings.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/settings.sml Sun Sep 05 14:00:57 2010 -0400 @@ -363,6 +363,8 @@ fun isNotNull (Nullable _) = false | isNotNull _ = true +datatype failure_mode = Error | None + type dbms = { name : string, header : string, @@ -384,9 +386,9 @@ -> Print.PD.pp_desc, nested : bool} -> Print.PD.pp_desc, - dml : ErrorMsg.span -> Print.PD.pp_desc, + dml : ErrorMsg.span * failure_mode -> Print.PD.pp_desc, dmlPrepared : {loc : ErrorMsg.span, id : int, dml : string, - inputs : sql_type list} -> Print.PD.pp_desc, + inputs : sql_type list, mode : failure_mode} -> Print.PD.pp_desc, nextval : {loc : ErrorMsg.span, seqName : string option, seqE : Print.PD.pp_desc} -> Print.PD.pp_desc, nextvalPrepared : {loc : ErrorMsg.span, id : int, query : string} -> Print.PD.pp_desc, setval : {loc : ErrorMsg.span, seqE : Print.PD.pp_desc, count : Print.PD.pp_desc} -> Print.PD.pp_desc, diff -r a671c986f517 -r acabf3935060 src/sqlite.sml --- a/src/sqlite.sml Sun Sep 05 12:50:06 2010 -0400 +++ b/src/sqlite.sml Sun Sep 05 14:00:57 2010 -0400 @@ -688,7 +688,7 @@ box [string "uw_pop_cleanup(ctx);", newline]] -fun dmlCommon {loc, dml} = +fun dmlCommon {loc, dml, mode} = box [string "int r;", newline, @@ -701,14 +701,17 @@ newline, newline, - string "if (r != SQLITE_DONE) uw_error(ctx, FATAL, \"", - string (ErrorMsg.spanToString loc), - string ": DML step failed: %s
%s\", ", - dml, - string ", sqlite3_errmsg(conn->conn));", + string "if (r != SQLITE_DONE) ", + case mode of + Settings.Error => box [string "uw_error(ctx, FATAL, \"", + string (ErrorMsg.spanToString loc), + string ": DML step failed: %s
%s\", ", + dml, + string ", sqlite3_errmsg(conn->conn));"] + | Settings.None => string "uw_errmsg = sqlite3_errmsg(conn->conn);", newline] -fun dml loc = +fun dml (loc, mode) = box [string "uw_conn *conn = uw_get_db(ctx);", newline, string "sqlite3_stmt *stmt;", @@ -721,12 +724,12 @@ newline, newline, - dmlCommon {loc = loc, dml = string "dml"}, + dmlCommon {loc = loc, dml = string "dml", mode = mode}, string "uw_pop_cleanup(ctx);", newline] -fun dmlPrepared {loc, id, dml, inputs} = +fun dmlPrepared {loc, id, dml, inputs, mode = mode} = box [string "uw_conn *conn = uw_get_db(ctx);", newline, p_pre_inputs inputs, @@ -761,7 +764,7 @@ dmlCommon {loc = loc, dml = box [string "\"", string (String.toCString dml), - string "\""]}, + string "\""], mode = mode}, string "uw_pop_cleanup(ctx);", newline, diff -r a671c986f517 -r acabf3935060 tests/tryDml.ur --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tryDml.ur Sun Sep 05 14:00:57 2010 -0400 @@ -0,0 +1,13 @@ +table t : {Id : int} + PRIMARY KEY Id + +fun doStuff () = + dml (INSERT INTO t (Id) VALUES (0)); + o1 <- tryDml (INSERT INTO t (Id) VALUES (0)); + dml (INSERT INTO t (Id) VALUES (1)); + o2 <- tryDml (INSERT INTO t (Id) VALUES (1)); + return {[o1]}; {[o2]} + +fun main () = return +
+
diff -r a671c986f517 -r acabf3935060 tests/tryDml.urp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tryDml.urp Sun Sep 05 14:00:57 2010 -0400 @@ -0,0 +1,4 @@ +database dbname=trydml +sql trydml.sql + +tryDml diff -r a671c986f517 -r acabf3935060 tests/tryDml.urs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tryDml.urs Sun Sep 05 14:00:57 2010 -0400 @@ -0,0 +1,1 @@ +val main : unit -> transaction page