changeset 1602:e44be6ece475

COALESCE
author Adam Chlipala <adam@chlipala.net>
date Sat, 19 Nov 2011 10:43:57 -0500
parents 78e0d56b594e
children 06958d5a7088
files doc/manual.tex lib/ur/basis.urs src/monoize.sml src/urweb.grm src/urweb.lex tests/coalesce.ur tests/coalesce.urp
diffstat 7 files changed, 58 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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} \\
--- 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
--- 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"), _), _),
                 _), _),
                _), _),
--- 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)
 
--- 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 @@
 <INITIAL> "DELETE"    => (Tokens.DELETE (pos yypos, pos yypos + size yytext));
 <INITIAL> "NULL"      => (Tokens.NULL (pos yypos, pos yypos + size yytext));
 <INITIAL> "IS"        => (Tokens.IS (pos yypos, pos yypos + size yytext));
+<INITIAL> "COALESCE"  => (Tokens.COALESCE (pos yypos, pos yypos + size yytext));
 
 <INITIAL> "CONSTRAINT"=> (Tokens.CCONSTRAINT (pos yypos, pos yypos + size yytext));
 <INITIAL> "UNIQUE"    => (Tokens.UNIQUE (pos yypos, pos yypos + size yytext));
--- /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 => <xml>{[r.1]},</xml>)
--- /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