# HG changeset patch # User Adam Chlipala # Date 1321717437 18000 # Node ID e44be6ece475dbf37f548510feb34a1e23043c47 # Parent 78e0d56b594ed17766c814702eb81ede151165de COALESCE diff -r 78e0d56b594e -r e44be6ece475 doc/manual.tex --- a/doc/manual.tex Sat Nov 19 10:26:19 2011 -0500 +++ b/doc/manual.tex Sat Nov 19 10:43:57 2011 -0500 @@ -1709,6 +1709,15 @@ \hspace{.1in} \to \mt{sql\_exp} \; \mt{tables} \; \mt{agg} \; \mt{exps} \; (\mt{option} \; \mt{t}) \to \mt{sql\_exp} \; \mt{tables} \; \mt{agg} \; \mt{exps} \; \mt{bool} \end{array}$$ +As another way of dealing with null values, there is also a restricted form of the standard \cd{COALESCE} function. +$$\begin{array}{l} + \mt{val} \; \mt{sql\_coalesce} : \mt{tables} ::: \{\{\mt{Type}\}\} \to \mt{agg} ::: \{\{\mt{Type}\}\} \to \mt{exps} ::: \{\mt{Type}\} \\ + \hspace{.1in} \to \mt{t} ::: \mt{Type} \\ + \hspace{.1in} \to \mt{sql\_exp} \; \mt{tables} \; \mt{agg} \; \mt{exps} \; (\mt{option} \; \mt{t}) \\ + \hspace{.1in} \to \mt{sql\_exp} \; \mt{tables} \; \mt{agg} \; \mt{exps} \; \mt{t} \\ + \hspace{.1in} \to \mt{sql\_exp} \; \mt{tables} \; \mt{agg} \; \mt{exps} \; \mt{t} +\end{array}$$ + We have generic nullary, unary, and binary operators. $$\begin{array}{l} \mt{con} \; \mt{sql\_nfunc} :: \mt{Type} \to \mt{Type} \\ @@ -2140,6 +2149,7 @@ &&& \ell & \textrm{primitive type literals} \\ &&& \mt{NULL} & \textrm{null value (injection of $\mt{None}$)} \\ &&& E \; \mt{IS} \; \mt{NULL} & \textrm{nullness test} \\ + &&& \mt{COALESCE}(E, E) & \textrm{take first non-null value} \\ &&& n & \textrm{nullary operators} \\ &&& u \; E & \textrm{unary operators} \\ &&& E \; b \; E & \textrm{binary operators} \\ diff -r 78e0d56b594e -r e44be6ece475 lib/ur/basis.urs --- a/lib/ur/basis.urs Sat Nov 19 10:26:19 2011 -0500 +++ b/lib/ur/basis.urs Sat Nov 19 10:43:57 2011 -0500 @@ -474,6 +474,12 @@ -> sql_exp tables agg exps (option t) -> sql_exp tables agg exps bool +val sql_coalesce : tables ::: {{Type}} -> agg ::: {{Type}} -> exps ::: {Type} + -> t ::: Type + -> sql_exp tables agg exps (option t) + -> sql_exp tables agg exps t + -> sql_exp tables agg exps t + val sql_if_then_else : tables ::: {{Type}} -> agg ::: {{Type}} -> exps ::: {Type} -> t ::: Type -> sql_exp tables agg exps bool diff -r 78e0d56b594e -r e44be6ece475 src/monoize.sml --- a/src/monoize.sml Sat Nov 19 10:26:19 2011 -0500 +++ b/src/monoize.sml Sat Nov 19 10:43:57 2011 -0500 @@ -2808,6 +2808,28 @@ (L.ECApp ( (L.ECApp ( (L.ECApp ( + (L.EFfi ("Basis", "sql_coalesce"), _), _), + _), _), + _), _), + _), _)) => + let + val s = (L'.TFfi ("Basis", "string"), loc) + fun sc s = (L'.EPrim (Prim.String s), loc) + in + ((L'.EAbs ("x1", s, (L'.TFun (s, s), loc), + (L'.EAbs ("x1", s, s, + strcat [sc "COALESCE(", + (L'.ERel 1, loc), + sc ",", + (L'.ERel 0, loc), + sc ")"]), loc)), loc), + fm) + end + + | (L.ECApp ( + (L.ECApp ( + (L.ECApp ( + (L.ECApp ( (L.EFfi ("Basis", "sql_if_then_else"), _), _), _), _), _), _), diff -r 78e0d56b594e -r e44be6ece475 src/urweb.grm --- a/src/urweb.grm Sat Nov 19 10:26:19 2011 -0500 +++ b/src/urweb.grm Sat Nov 19 10:43:57 2011 -0500 @@ -244,7 +244,7 @@ | TRUE | FALSE | CAND | OR | NOT | COUNT | AVG | SUM | MIN | MAX | ASC | DESC - | INSERT | INTO | VALUES | UPDATE | SET | DELETE | NULL | IS + | INSERT | INTO | VALUES | UPDATE | SET | DELETE | NULL | IS | COALESCE | CURRENT_TIMESTAMP | NE | LT | LE | GT | GE | CCONSTRAINT | UNIQUE | CHECK | PRIMARY | FOREIGN | KEY | ON | NO | ACTION | RESTRICT | CASCADE | REFERENCES @@ -1881,6 +1881,14 @@ in (EApp (e, sqlexp), loc) end) + | COALESCE LPAREN sqlexp COMMA sqlexp RPAREN + (let + val loc = s (COALESCEright, sqlexp2right) + val e = (EVar (["Basis"], "sql_coalesce", Infer), loc) + val e = (EApp (e, sqlexp1), loc) + in + (EApp (e, sqlexp2), loc) + end) | fname LPAREN sqlexp RPAREN (let val loc = s (fnameleft, RPARENright) diff -r 78e0d56b594e -r e44be6ece475 src/urweb.lex --- a/src/urweb.lex Sat Nov 19 10:26:19 2011 -0500 +++ b/src/urweb.lex Sat Nov 19 10:43:57 2011 -0500 @@ -499,6 +499,7 @@ "DELETE" => (Tokens.DELETE (pos yypos, pos yypos + size yytext)); "NULL" => (Tokens.NULL (pos yypos, pos yypos + size yytext)); "IS" => (Tokens.IS (pos yypos, pos yypos + size yytext)); + "COALESCE" => (Tokens.COALESCE (pos yypos, pos yypos + size yytext)); "CONSTRAINT"=> (Tokens.CCONSTRAINT (pos yypos, pos yypos + size yytext)); "UNIQUE" => (Tokens.UNIQUE (pos yypos, pos yypos + size yytext)); diff -r 78e0d56b594e -r e44be6ece475 tests/coalesce.ur --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/coalesce.ur Sat Nov 19 10:43:57 2011 -0500 @@ -0,0 +1,6 @@ +table t : { A : option int } + +fun main () : transaction page = + queryX (SELECT COALESCE(t.A, 13) + FROM t) + (fn r => {[r.1]},) diff -r 78e0d56b594e -r e44be6ece475 tests/coalesce.urp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/coalesce.urp Sat Nov 19 10:43:57 2011 -0500 @@ -0,0 +1,4 @@ +database dbname=test +sql coalesce.sql + +coalesce