# HG changeset patch # User Adam Chlipala # Date 1256242556 14400 # Node ID 16f7cb0891b6bdd94c4bf3634db571d00895cf4a # Parent 6b0f3853cc8122fc9feaf6fbc852ce0f403120a1 Initial support for char in SQL diff -r 6b0f3853cc81 -r 16f7cb0891b6 demo/more/conference.ur --- a/demo/more/conference.ur Thu Oct 22 14:05:48 2009 -0400 +++ b/demo/more/conference.ur Thu Oct 22 16:15:56 2009 -0400 @@ -9,6 +9,7 @@ con review :: {(Type * Type)} constraint [Paper, User] ~ review val review : $(map meta review) + val reviewFolder : folder review val submissionDeadline : time val summarizePaper : $(map fst paper) -> xbody @@ -26,7 +27,7 @@ table authorship : {Paper : int, User : int} PRIMARY KEY (Paper, User), - CONSTRAINT Paper FOREIGN KEY Paper REFERENCES paper(Id), + CONSTRAINT Paper FOREIGN KEY Paper REFERENCES paper(Id) ON DELETE CASCADE, CONSTRAINT User FOREIGN KEY User REFERENCES user(Id) con review = [Paper = int, User = int] ++ map fst M.review @@ -249,6 +250,7 @@ and one id = + me <- getLogin; checkPaper id; ro <- oneOrNoRows (SELECT paper.{{map fst M.paper}}, octet_length(paper.Document) AS N FROM paper @@ -258,6 +260,10 @@ JOIN user ON authorship.User = user.Id WHERE authorship.Paper = {[id]}) (fn r =>
  • {[r.User.Nam]}
  • ); + myReview <- oneOrNoRows1 (SELECT review.{{map fst M.review}} + FROM review + WHERE review.User = {[me.Id]} + AND review.Paper = {[id]}); case ro of None => error Paper not found! | Some r => return @@ -274,6 +280,24 @@
    No paper uploaded yet.
    else Download paper ({[r.N]} bytes)} + +
    + + {case myReview of + None => +

    Add Your Review

    + +
    + {allWidgets M.review M.reviewFolder} +
    +
    + | Some myReview => +

    Edit Your Review

    + +
    + {allPopulated M.review myReview M.reviewFolder} +
    +
    }
    and download id = diff -r 6b0f3853cc81 -r 16f7cb0891b6 demo/more/conference.urp --- a/demo/more/conference.urp Thu Oct 22 14:05:48 2009 -0400 +++ b/demo/more/conference.urp Thu Oct 22 16:15:56 2009 -0400 @@ -1,5 +1,6 @@ allow mime application/pdf +$/string $/option $/list meta diff -r 6b0f3853cc81 -r 16f7cb0891b6 demo/more/conference.urs --- a/demo/more/conference.urs Thu Oct 22 14:05:48 2009 -0400 +++ b/demo/more/conference.urs Thu Oct 22 16:15:56 2009 -0400 @@ -7,6 +7,7 @@ con review :: {(Type * Type)} constraint [Paper, User] ~ review val review : $(map Meta.meta review) + val reviewFolder : folder review val submissionDeadline : time val summarizePaper : $(map fst paper) -> xbody diff -r 6b0f3853cc81 -r 16f7cb0891b6 demo/more/conference1.ur --- a/demo/more/conference1.ur Thu Oct 22 14:05:48 2009 -0400 +++ b/demo/more/conference1.ur Thu Oct 22 16:15:56 2009 -0400 @@ -3,7 +3,7 @@ open Conference.Make(struct val paper = {Title = title, Abstract = abstract} - val review = {} + val review = {Rating = dropdown "Rating" (#"A" :: #"B" :: #"C" :: #"D" :: [])} val submissionDeadline = readError "2009-10-22 23:59:59" diff -r 6b0f3853cc81 -r 16f7cb0891b6 demo/more/conferenceFields.ur --- a/demo/more/conferenceFields.ur Thu Oct 22 14:05:48 2009 -0400 +++ b/demo/more/conferenceFields.ur Thu Oct 22 16:15:56 2009 -0400 @@ -2,3 +2,22 @@ val title = string "Title" val abstract = textarea "Abstract" + +fun charIn s = + if String.length s = 0 then + error Impossible: Empty option value + else + String.sub s 0 + +con dropdown = (char, string) +fun dropdown name opts = {Nam = name, + Show = txt, + Widget = fn [nm :: Name] => + {List.mapX (fn x => ) opts} + , + WidgetPopulated = fn [nm :: Name] v => + {List.mapX (fn x => ) opts} + , + Parse = charIn, + Inject = _} + diff -r 6b0f3853cc81 -r 16f7cb0891b6 demo/more/conferenceFields.urs --- a/demo/more/conferenceFields.urs Thu Oct 22 14:05:48 2009 -0400 +++ b/demo/more/conferenceFields.urs Thu Oct 22 16:15:56 2009 -0400 @@ -1,2 +1,5 @@ val title : Meta.meta (string, string) val abstract : Meta.meta (string, string) + +con dropdown :: (Type * Type) +val dropdown : string -> list char -> Meta.meta dropdown diff -r 6b0f3853cc81 -r 16f7cb0891b6 demo/more/meta.ur --- a/demo/more/meta.ur Thu Oct 22 14:05:48 2009 -0400 +++ b/demo/more/meta.ur Thu Oct 22 16:15:56 2009 -0400 @@ -52,6 +52,18 @@ [_] fl r +fun allPopulated [ts ::: {(Type * Type)}] (r : $(map meta ts)) (vs : $(map fst ts)) (fl : folder ts) = + foldR2 [meta] [fst] [fn cols :: {(Type * Type)} => + xml form [] (map snd cols)] + (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}] [[nm] ~ rest] + (m : meta p) v (acc : xml form [] (map snd rest)) => + + {[m.Nam]}: {m.WidgetPopulated [nm] v}
    + {useMore acc} +
    ) + + [_] fl r vs + fun allPopulatedTr [ts ::: {(Type * Type)}] (r : $(map meta ts)) (vs : $(map fst ts)) (fl : folder ts) = foldR2 [meta] [fst] [fn cols :: {(Type * Type)} => xml [Body, Form, Tr] [] (map snd cols)] diff -r 6b0f3853cc81 -r 16f7cb0891b6 demo/more/meta.urs --- a/demo/more/meta.urs Thu Oct 22 14:05:48 2009 -0400 +++ b/demo/more/meta.urs Thu Oct 22 16:15:56 2009 -0400 @@ -18,6 +18,9 @@ val allWidgets : ts ::: {(Type * Type)} -> $(map meta ts) -> folder ts -> xml form [] (map snd ts) +val allPopulated : ts ::: {(Type * Type)} -> $(map meta ts) -> $(map fst ts) -> folder ts + -> xml form [] (map snd ts) + val allPopulatedTr : ts ::: {(Type * Type)} -> $(map meta ts) -> $(map fst ts) -> folder ts -> xml ([Tr] ++ form) [] (map snd ts) diff -r 6b0f3853cc81 -r 16f7cb0891b6 include/urweb.h --- a/include/urweb.h Thu Oct 22 14:05:48 2009 -0400 +++ b/include/urweb.h Thu Oct 22 16:15:56 2009 -0400 @@ -88,6 +88,7 @@ char *uw_Basis_attrifyInt(uw_context, uw_Basis_int); char *uw_Basis_attrifyFloat(uw_context, uw_Basis_float); char *uw_Basis_attrifyString(uw_context, uw_Basis_string); +char *uw_Basis_attrifyChar(uw_context, uw_Basis_char); char *uw_Basis_attrifyTime(uw_context, uw_Basis_time); char *uw_Basis_attrifyChannel(uw_context, uw_Basis_channel); char *uw_Basis_attrifyClient(uw_context, uw_Basis_client); @@ -96,6 +97,7 @@ uw_unit uw_Basis_attrifyInt_w(uw_context, uw_Basis_int); uw_unit uw_Basis_attrifyFloat_w(uw_context, uw_Basis_float); uw_unit uw_Basis_attrifyString_w(uw_context, uw_Basis_string); +uw_unit uw_Basis_attrifyChar_w(uw_context, uw_Basis_char); char *uw_Basis_urlifyInt(uw_context, uw_Basis_int); char *uw_Basis_urlifyFloat(uw_context, uw_Basis_float); @@ -133,6 +135,7 @@ uw_Basis_string uw_Basis_sqlifyInt(uw_context, uw_Basis_int); uw_Basis_string uw_Basis_sqlifyFloat(uw_context, uw_Basis_float); uw_Basis_string uw_Basis_sqlifyString(uw_context, uw_Basis_string); +uw_Basis_string uw_Basis_sqlifyChar(uw_context, uw_Basis_char); uw_Basis_string uw_Basis_sqlifyBool(uw_context, uw_Basis_bool); uw_Basis_string uw_Basis_sqlifyTime(uw_context, uw_Basis_time); uw_Basis_string uw_Basis_sqlifyBlob(uw_context, uw_Basis_blob); @@ -216,6 +219,7 @@ extern char *uw_sqlfmtFloat; extern int uw_Estrings; extern char *uw_sqlsuffixString; +extern char *uw_sqlsuffixChar; extern char *uw_sqlsuffixBlob; extern char *uw_sqlfmtUint4; diff -r 6b0f3853cc81 -r 16f7cb0891b6 lib/ur/basis.urs --- a/lib/ur/basis.urs Thu Oct 22 14:05:48 2009 -0400 +++ b/lib/ur/basis.urs Thu Oct 22 16:15:56 2009 -0400 @@ -165,6 +165,7 @@ val sql_int : sql_injectable_prim int val sql_float : sql_injectable_prim float val sql_string : sql_injectable_prim string +val sql_char : sql_injectable_prim char val sql_time : sql_injectable_prim time val sql_blob : sql_injectable_prim blob val sql_channel : t ::: Type -> sql_injectable_prim (channel t) diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/c/urweb.c --- a/src/c/urweb.c Thu Oct 22 14:05:48 2009 -0400 +++ b/src/c/urweb.c Thu Oct 22 16:15:56 2009 -0400 @@ -1388,6 +1388,32 @@ return result; } +char *uw_Basis_attrifyChar(uw_context ctx, uw_Basis_char c) { + char *result, *p; + uw_check_heap(ctx, 7); + + result = p = ctx->heap.front; + + if (c == '"') { + strcpy(p, """); + p += 6; + } else if (c == '&') { + strcpy(p, "&"); + p += 5; + } + else if (isprint(c)) + *p++ = c; + else { + int len2; + sprintf(p, "&#%d;%n", c, &len2); + p += len2; + } + + *p++ = 0; + ctx->heap.front = p; + return result; +} + char *uw_Basis_attrifyCss_class(uw_context ctx, uw_Basis_css_class s) { return s; } @@ -1438,6 +1464,24 @@ return uw_unit_v; } +uw_unit uw_Basis_attrifyChar_w(uw_context ctx, uw_Basis_char c) { + uw_check(ctx, 6); + + if (c == '"') + uw_write_unsafe(ctx, """); + else if (c == '&') + uw_write_unsafe(ctx, "&"); + else if (isprint(c)) + uw_writec_unsafe(ctx, c); + else { + uw_write_unsafe(ctx, "&#"); + uw_Basis_attrifyInt_w_unsafe(ctx, c); + uw_writec_unsafe(ctx, ';'); + } + + return uw_unit_v; +} + char *uw_Basis_urlifyInt(uw_context ctx, uw_Basis_int n) { int len; @@ -1988,6 +2032,7 @@ int uw_Estrings = 1; char *uw_sqlsuffixString = "::text"; +char *uw_sqlsuffixChar = "::char"; uw_Basis_string uw_Basis_sqlifyString(uw_context ctx, uw_Basis_string s) { char *r, *s2; @@ -2035,6 +2080,48 @@ return r; } +uw_Basis_string uw_Basis_sqlifyChar(uw_context ctx, uw_Basis_char c) { + char *r, *s2; + + uw_check_heap(ctx, 5 + uw_Estrings + strlen(uw_sqlsuffixChar)); + + r = s2 = ctx->heap.front; + if (uw_Estrings) + *s2++ = 'E'; + *s2++ = '\''; + + switch (c) { + case '\'': + if (uw_Estrings) + strcpy(s2, "\\'"); + else + strcpy(s2, "''"); + s2 += 2; + break; + case '\\': + if (uw_Estrings) { + strcpy(s2, "\\\\"); + s2 += 2; + } else + *s2++ = '\\'; + break; + default: + if (isprint(c)) + *s2++ = c; + else if (uw_Estrings) { + sprintf(s2, "\\%03o", c); + s2 += 4; + } + else + uw_error(ctx, FATAL, "Non-printable character %u in char to SQLify", c); + } + + *s2++ = '\''; + strcpy(s2, uw_sqlsuffixChar); + ctx->heap.front = s2 + 1 + strlen(uw_sqlsuffixChar); + return r; +} + char *uw_sqlsuffixBlob = "::bytea"; uw_Basis_string uw_Basis_sqlifyBlob(uw_context ctx, uw_Basis_blob b) { diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/cjr_print.sml --- a/src/cjr_print.sml Thu Oct 22 14:05:48 2009 -0400 +++ b/src/cjr_print.sml Thu Oct 22 16:15:56 2009 -0400 @@ -491,20 +491,7 @@ TFfi ("Basis", "file") => true | _ => false -fun p_sql_type' t = - case t of - Int => "uw_Basis_int" - | Float => "uw_Basis_float" - | String => "uw_Basis_string" - | Bool => "uw_Basis_bool" - | Time => "uw_Basis_time" - | Blob => "uw_Basis_blob" - | Channel => "uw_Basis_channel" - | Client => "uw_Basis_client" - | Nullable String => "uw_Basis_string" - | Nullable t => p_sql_type' t ^ "*" - -fun p_sql_type t = string (p_sql_type' t) +fun p_sql_type t = string (Settings.p_sql_ctype t) fun getPargs (e, _) = case e of @@ -1308,6 +1295,7 @@ TFfi ("Basis", "int") => Int | TFfi ("Basis", "float") => Float | TFfi ("Basis", "string") => String + | TFfi ("Basis", "char") => Char | TFfi ("Basis", "bool") => Bool | TFfi ("Basis", "time") => Time | TFfi ("Basis", "blob") => Blob diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/mono_opt.sml --- a/src/mono_opt.sml Thu Oct 22 14:05:48 2009 -0400 +++ b/src/mono_opt.sml Thu Oct 22 16:15:56 2009 -0400 @@ -45,12 +45,16 @@ else Real.toString n -val attrifyString = String.translate (fn #"\"" => """ - | #"&" => "&" - | ch => if Char.isPrint ch then - str ch - else - "&#" ^ Int.toString (ord ch) ^ ";") +fun attrifyChar ch = + case ch of + #"\"" => """ + | #"&" => "&" + | ch => if Char.isPrint ch then + str ch + else + "&#" ^ Int.toString (ord ch) ^ ";" + +val attrifyString = String.translate attrifyChar val urlifyInt = attrifyInt val urlifyFloat = attrifyFloat @@ -95,6 +99,7 @@ fun sqlifyFloat n = #p_cast (Settings.currentDbms ()) (attrifyFloat n, Settings.Float) fun sqlifyString s = #sqlifyString (Settings.currentDbms ()) s +fun sqlifyChar ch = #sqlifyString (Settings.currentDbms ()) (str ch) fun unAs s = let @@ -260,6 +265,13 @@ | EWrite (EFfiApp ("Basis", "attrifyString", [e]), _) => EFfiApp ("Basis", "attrifyString_w", [e]) + | EFfiApp ("Basis", "attrifyChar", [(EPrim (Prim.Char s), _)]) => + EPrim (Prim.String (attrifyChar s)) + | EWrite (EFfiApp ("Basis", "attrifyChar", [(EPrim (Prim.Char s), _)]), loc) => + EWrite (EPrim (Prim.String (attrifyChar s)), loc) + | EWrite (EFfiApp ("Basis", "attrifyChar", [e]), _) => + EFfiApp ("Basis", "attrifyChar_w", [e]) + | EFfiApp ("Basis", "attrifyCss_class", [(EPrim (Prim.String s), _)]) => EPrim (Prim.String s) | EWrite (EFfiApp ("Basis", "attrifyCss_class", [(EPrim (Prim.String s), _)]), loc) => @@ -318,6 +330,8 @@ result = (TFfi ("Basis", "string"), loc)}), loc) | EFfiApp ("Basis", "sqlifyString", [(EPrim (Prim.String n), _)]) => EPrim (Prim.String (sqlifyString n)) + | EFfiApp ("Basis", "sqlifyChar", [(EPrim (Prim.Char n), _)]) => + EPrim (Prim.String (sqlifyChar n)) | EWrite (ECase (discE, pes, {disc, ...}), loc) => optExp (ECase (discE, diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/monoize.sml --- a/src/monoize.sml Thu Oct 22 14:05:48 2009 -0400 +++ b/src/monoize.sml Thu Oct 22 16:15:56 2009 -0400 @@ -1921,6 +1921,10 @@ ((L'.EAbs ("x", (L'.TFfi ("Basis", "string"), loc), (L'.TFfi ("Basis", "string"), loc), (L'.EFfiApp ("Basis", "sqlifyString", [(L'.ERel 0, loc)]), loc)), loc), fm) + | L.EFfi ("Basis", "sql_char") => + ((L'.EAbs ("x", (L'.TFfi ("Basis", "char"), loc), (L'.TFfi ("Basis", "string"), loc), + (L'.EFfiApp ("Basis", "sqlifyChar", [(L'.ERel 0, loc)]), loc)), loc), + fm) | L.EFfi ("Basis", "sql_time") => ((L'.EAbs ("x", (L'.TFfi ("Basis", "time"), loc), (L'.TFfi ("Basis", "string"), loc), (L'.EFfiApp ("Basis", "sqlifyTime", [(L'.ERel 0, loc)]), loc)), loc), diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/mysql.sml --- a/src/mysql.sml Thu Oct 22 14:05:48 2009 -0400 +++ b/src/mysql.sml Thu Oct 22 16:15:56 2009 -0400 @@ -36,6 +36,7 @@ Int => "bigint" | Float => "double" | String => "longtext" + | Char => "char" | Bool => "bool" | Time => "timestamp" | Blob => "longblob" @@ -48,6 +49,7 @@ Int => "MYSQL_TYPE_LONGLONG" | Float => "MYSQL_TYPE_DOUBLE" | String => "MYSQL_TYPE_STRING" + | Char => "MYSQL_TYPE_TINY" | Bool => "MYSQL_TYPE_LONG" | Time => "MYSQL_TYPE_TIMESTAMP" | Blob => "MYSQL_TYPE_BLOB" @@ -60,6 +62,7 @@ Int => "bigint" | Float => "double" | String => "longtext" + | Char => "char" | Bool => "tinyint" | Time => "timestamp" | Blob => "longblob" @@ -386,6 +389,8 @@ newline, string "uw_sqlsuffixString = \"\";", newline, + string "uw_sqlsuffixChar = \"\";", + newline, string "uw_sqlsuffixBlob = \"\";", newline, string "uw_sqlfmtUint4 = \"%u%n\";", diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/postgres.sml --- a/src/postgres.sml Thu Oct 22 14:05:48 2009 -0400 +++ b/src/postgres.sml Thu Oct 22 16:15:56 2009 -0400 @@ -39,6 +39,7 @@ Int => "int8" | Float => "float8" | String => "text" + | Char => "char" | Bool => "bool" | Time => "timestamp" | Blob => "bytea" @@ -51,6 +52,7 @@ Int => "bigint" | Float => "double precision" | String => "text" + | Char => "character" | Bool => "boolean" | Time => "timestamp without time zone" | Blob => "bytea" @@ -257,6 +259,8 @@ newline, string "uw_sqlsuffixString = \"::text\";", newline, + string "uw_sqlsuffixChar = \"::char\";", + newline, string "uw_sqlsuffixBlob = \"::bytea\";", newline, string "uw_sqlfmtUint4 = \"%u::int4%n\";", @@ -505,6 +509,7 @@ e else box [string "uw_strdup(ctx, ", e, string ")"] + | Char => box [e, string "[0]"] | Bool => box [string "uw_Basis_stringToBool_error(ctx, ", e, string ")"] | Time => box [string "uw_Basis_unsqlTime(ctx, ", e, string ")"] | Blob => box [string "uw_Basis_stringToBlob_error(ctx, ", @@ -643,6 +648,7 @@ Int => box [string "uw_Basis_attrifyInt(ctx, ", e, string ")"] | Float => box [string "uw_Basis_attrifyFloat(ctx, ", e, string ")"] | String => e + | Char => box [string "uw_Basis_attrifyChar(ctx, ", e, string ")"] | Bool => box [string "(", e, string " ? \"TRUE\" : \"FALSE\")"] | Time => box [string "uw_Basis_attrifyTime(ctx, ", e, string ")"] | Blob => box [e, string ".data"] diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/settings.sig --- a/src/settings.sig Thu Oct 22 14:05:48 2009 -0400 +++ b/src/settings.sig Thu Oct 22 16:15:56 2009 -0400 @@ -105,6 +105,7 @@ Int | Float | String + | Char | Bool | Time | Blob diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/settings.sml --- a/src/settings.sml Thu Oct 22 14:05:48 2009 -0400 +++ b/src/settings.sml Thu Oct 22 16:15:56 2009 -0400 @@ -282,6 +282,7 @@ Int | Float | String + | Char | Bool | Time | Blob @@ -298,6 +299,7 @@ Int => "uw_Basis_int" | Float => "uw_Basis_float" | String => "uw_Basis_string" + | Char => "uw_Basis_char" | Bool => "uw_Basis_bool" | Time => "uw_Basis_time" | Blob => "uw_Basis_blob" diff -r 6b0f3853cc81 -r 16f7cb0891b6 src/sqlite.sml --- a/src/sqlite.sml Thu Oct 22 14:05:48 2009 -0400 +++ b/src/sqlite.sml Thu Oct 22 16:15:56 2009 -0400 @@ -36,6 +36,7 @@ Int => "integer" | Float => "real" | String => "text" + | Char => "integer" | Bool => "integer" | Time => "text" | Blob => "blob" @@ -165,6 +166,8 @@ newline, string "uw_sqlsuffixString = \"\";", newline, + string "uw_sqlsuffixChar = \"\";", + newline, string "uw_sqlsuffixBlob = \"\";", newline, string "uw_sqlfmtUint4 = \"%u%n\";", @@ -370,6 +373,7 @@ box [string "sqlite3_column_text(stmt, ", string (Int.toString i), string ")"] else box [string "uw_strdup(ctx, sqlite3_column_text(stmt, ", string (Int.toString i), string "))"] + | Char => box [string "sqlite3_column_int(stmt, ", string (Int.toString i), string ")"] | Bool => box [string "(uw_Basis_bool)sqlite3_column_int(stmt, ", string (Int.toString i), string ")"] | Time => box [string "uw_Basis_stringToTime_error(ctx, sqlite3_column_text(stmt, ", string (Int.toString i), string "))"] | Blob => box [string "({", @@ -523,6 +527,11 @@ string ", ", arg, string ", -1, SQLITE_TRANSIENT)"] + | Char => box [string "sqlite3_bind_int(stmt, ", + string (Int.toString (i + 1)), + string ", ", + arg, + string ")"] | Bool => box [string "sqlite3_bind_int(stmt, ", string (Int.toString (i + 1)), string ", ",