Changeset: f201bf96179f for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/f201bf96179f
Branch: default
Log Message:

merge with returning


diffs (truncated from 855 to 300 lines):

diff --git a/sql/ChangeLog b/sql/ChangeLog
--- a/sql/ChangeLog
+++ b/sql/ChangeLog
@@ -1,6 +1,24 @@
 # ChangeLog file for sql
 # This file is updated with Maddlog
 
+* Tue Oct  8 2024 Yunus Koning <yunus.kon...@monetdbsolutions.com>
+- Introduce the RETURNING clause for INSERT, UPDATE and DELETE statements.
+  Specifying a RETURNING clause causes the SQL statement to return the
+  modified records which can be queried using SELECT like expressions
+  in the RETURNING clause. Aggregate functions are allowed.
+  This is a common non-standard SQL extension.
+  
+  Examples:
+  
+  INSERT INTO foo values (1,10), (-1,-10) RETURNING i+2*j AS bar
+  ----
+  21
+  -21
+  
+  UPDATE foo SET i = -i WHERE i >0 RETURNING sum(j), count(j)
+  ----
+  -60|3
+
 * Tue Sep 10 2024 Lucas Pereira <lucas.pere...@monetdbsolutions.com>
 - Introduce division_min_scale SQL environment variable for specifying
   minimum scale of the division result. The default value is 3.
diff --git a/sql/backends/monet5/rel_bin.c b/sql/backends/monet5/rel_bin.c
--- a/sql/backends/monet5/rel_bin.c
+++ b/sql/backends/monet5/rel_bin.c
@@ -5238,7 +5238,7 @@ rel2bin_insert(backend *be, sql_rel *rel
 {
        mvc *sql = be->mvc;
        list *l;
-       stmt *inserts = NULL, *insert = NULL, *ddl = NULL, *pin = NULL, 
**updates, *ret = NULL, *cnt = NULL, *pos = NULL;
+       stmt *inserts = NULL, *insert = NULL, *ddl = NULL, *pin = NULL, 
**updates, *ret = NULL, *cnt = NULL, *pos = NULL, *returning = NULL;
        int idx_ins = 0, len = 0;
        node *n, *m, *idx_m = NULL;
        sql_rel *tr = rel->l, *prel = rel->r;
@@ -5352,6 +5352,20 @@ rel2bin_insert(backend *be, sql_rel *rel
        if (!insert)
                return NULL;
 
+       if (rel->returning) {
+               list* il = sa_list(sql->sa);
+               sql_rel* inner = rel->l;
+               assert(inner->op == op_basetable);
+               for (n = inner->exps->h, m = inserts->op4.lval->h; n && m; n = 
n->next, m = m->next) {
+                       sql_exp* ce     = n->data;
+                       stmt*   ins     = m->data;
+                       stmt*   s       = stmt_rename(be, ce, ins);// label 
each insert statement with the corresponding col exp label
+                       append(il, s);
+               }
+               returning = stmt_list(be, il);
+               sql->type = Q_TABLE;
+       }
+
        if (!sql_insert_triggers(be, t, updates, 1))
                return sql_error(sql, 10, SQLSTATE(27000) "INSERT INTO: 
triggers failed for table '%s'", t->base.name);
        /* update predicate list */
@@ -5368,7 +5382,7 @@ rel2bin_insert(backend *be, sql_rel *rel
                        return sql_error(sql, 10, SQLSTATE(HY013) 
MAL_MALLOC_FAIL);
                if (t->s && isGlobal(t) && !isGlobalTemp(t))
                        stmt_add_dependency_change(be, t, ret);
-               return ret;
+               return returning?returning:ret;
        }
 }
 
@@ -6398,6 +6412,18 @@ rel2bin_update(backend *be, sql_rel *rel
                        append(l, stmt_update_col(be,  c, tids, 
updates[c->colnr]));
        }
 
+       stmt* returning = NULL;
+       if (rel->returning) {
+               sql_rel* b = rel->l;
+               int refcnt = b->ref.refcnt; // HACK: forces recalculation of 
base columns since they are assumed to be updated
+               b->ref.refcnt = 1;
+               returning = subrel_bin(be, b, refs);
+               b->ref.refcnt = refcnt;
+               returning->cand = tids;
+               returning = subrel_project(be, returning, refs, b);
+               sql->type = Q_TABLE;
+       }
+
        if (cascade_updates(be, t, tids, updates)) {
                if (sql->cascade_action)
                        sql->cascade_action = NULL;
@@ -6426,7 +6452,7 @@ rel2bin_update(backend *be, sql_rel *rel
                sql->cascade_action = NULL;
        if (rel->r && !rel_predicates(be, rel->r))
                return NULL;
-       return cnt;
+       return returning?returning:cnt;
 }
 
 static int
@@ -6644,7 +6670,7 @@ static stmt *
 rel2bin_delete(backend *be, sql_rel *rel, list *refs)
 {
        mvc *sql = be->mvc;
-       stmt *stdelete = NULL, *tids = NULL;
+       stmt *stdelete = NULL, *tids = NULL, *returning = NULL;
        sql_rel *tr = rel->l;
        sql_table *t = NULL;
 
@@ -6661,6 +6687,14 @@ rel2bin_delete(backend *be, sql_rel *rel
                assert(rows->type == st_list);
                tids = rows->op4.lval->h->data; /* TODO this should be the 
candidate list instead */
        }
+
+       if (rel->returning) {
+               returning = subrel_bin(be, rel->l, refs);
+               returning->cand = tids;
+               returning = subrel_project(be, returning, refs, rel->l);
+               sql->type = Q_TABLE;
+       }
+
        stdelete = sql_delete(be, t, tids);
        if (sql->cascade_action)
                sql->cascade_action = NULL;
@@ -6669,7 +6703,7 @@ rel2bin_delete(backend *be, sql_rel *rel
 
        if (rel->r && !rel_predicates(be, rel->r))
                return NULL;
-       return stdelete;
+       return returning?returning:stdelete;
 }
 
 struct tablelist {
@@ -7507,17 +7541,17 @@ subrel_bin(backend *be, sql_rel *rel, li
                break;
        case op_insert:
                s = rel2bin_insert(be, rel, refs);
-               if (sql->type == Q_TABLE)
+               if (!(rel->returning) && sql->type == Q_TABLE)
                        sql->type = Q_UPDATE;
                break;
        case op_update:
                s = rel2bin_update(be, rel, refs);
-               if (sql->type == Q_TABLE)
+               if (!(rel->returning) && sql->type == Q_TABLE)
                        sql->type = Q_UPDATE;
                break;
        case op_delete:
                s = rel2bin_delete(be, rel, refs);
-               if (sql->type == Q_TABLE)
+               if (!(rel->returning) && sql->type == Q_TABLE)
                        sql->type = Q_UPDATE;
                break;
        case op_truncate:
diff --git a/sql/backends/monet5/sql.c b/sql/backends/monet5/sql.c
--- a/sql/backends/monet5/sql.c
+++ b/sql/backends/monet5/sql.c
@@ -5522,6 +5522,61 @@ SQLcheck(Client cntxt, MalBlkPtr mb, Mal
        return MAL_SUCCEED;
 }
 
+static str
+SQLread_dump_rel(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
+{
+       mvc *m = NULL;
+       str msg = NULL;
+       buffer *b = NULL;
+       stream *s = NULL;
+       char *res = NULL;
+       str *r = getArgReference_str(stk, pci, 0);
+       char *input = *getArgReference_str(stk, pci, 1);
+
+       if ((msg = getSQLContext(cntxt, mb, &m, NULL)) != NULL)
+               return msg;
+       if ((msg = checkSQLContext(cntxt)) != NULL)
+               return msg;
+
+       list *refs = sa_list(m->sa);
+       if (refs == NULL)
+               goto bailout;
+
+       int pos = 0;
+       sql_rel* rel = rel_read(m, input, &pos, refs);
+       if (!rel)
+               throw(SQL, "SQLread_dump_rel", SQLSTATE(42000) "failed to read 
relational plan");
+
+       b = buffer_create(1024);
+       if(b == NULL)
+               goto bailout;
+       s = buffer_wastream(b, "exp_dump");
+       if(s == NULL)
+               goto bailout;
+
+       refs = sa_list(m->sa);
+       if (refs == NULL)
+               goto bailout;
+
+       rel_print_refs(m, s, rel, 0, refs, 0);
+       rel_print_(m, s, rel, 0, refs, 0);
+       res = buffer_get_buf(b);
+
+       if (!(*r = GDKstrdup(res)))
+               goto bailout;
+
+       mnstr_destroy(s);
+       return MAL_SUCCEED;
+
+bailout:
+       if (s)
+               mnstr_destroy(s);
+       else if (b)
+               buffer_destroy(b);
+       throw(SQL, "SQLread_dump_rel", SQLSTATE(HY013) MAL_MALLOC_FAIL);
+}
+
+
 static mel_func sql_init_funcs[] = {
  pattern("sql", "shutdown", SQLshutdown_wrap, true, "", args(1,3, 
arg("",str),arg("delay",bte),arg("force",bit))),
  pattern("sql", "shutdown", SQLshutdown_wrap, true, "", args(1,3, 
arg("",str),arg("delay",sht),arg("force",bit))),
@@ -6454,6 +6509,7 @@ static mel_func sql_init_funcs[] = {
  pattern("sql", "vacuum", SQLstr_auto_vacuum, true, "auto vacuum string column 
of given table with interval(sec)", args(0,3, 
arg("sname",str),arg("tname",str),arg("interval", int))),
  pattern("sql", "stop_vacuum", SQLstr_stop_vacuum, true, "stop auto vacuum", 
args(0,2, arg("sname",str),arg("tname",str))),
  pattern("sql", "check", SQLcheck, false, "Return sql string of check 
constraint.", args(1,3, arg("sql",str), arg("sname", str), arg("name", str))),
+ pattern("sql", "read_dump_rel", SQLread_dump_rel, false, "Reads sql_rel 
string into sql_rel object and then writes it to the return value", args(1,2, 
arg("sql",str), arg("sql_rel", str))),
  { .imp=NULL }
 };
 #include "mal_import.h"
diff --git a/sql/include/sql_relation.h b/sql/include/sql_relation.h
--- a/sql/include/sql_relation.h
+++ b/sql/include/sql_relation.h
@@ -312,7 +312,8 @@ typedef struct relation {
         processed:1,   /* fully processed or still in the process of building 
*/
         outer:1,       /* used as outer (ungrouped) */
         grouped:1,     /* groupby processed all the group by exps */
-        single:1;
+        single:1,
+        returning:1; /*update|delete|insert relations return modified records*/
        /*
         * Used by rewriters at rel_unnest, rel_optimizer and rel_distribute so 
a relation is not modified twice
         * The list is kept at rel_optimizer_private.h Please update it 
accordingly
diff --git a/sql/server/rel_dump.c b/sql/server/rel_dump.c
--- a/sql/server/rel_dump.c
+++ b/sql/server/rel_dump.c
@@ -1894,10 +1894,11 @@ rel_read(mvc *sql, char *r, int *pos, li
                (void)readInt(r,pos);
                skipWS(r, pos);
                (*pos)++; /* ( */
-               (void)readInt(r,pos); /* skip nr refs */
+               int cnt = readInt(r,pos);
                (*pos)++; /* ) */
                if (!(rel = rel_read(sql, r, pos, refs)))
                        return NULL;
+               rel->ref.refcnt = cnt;
                append(refs, rel);
                skipWS(r,pos);
        }
@@ -1908,7 +1909,7 @@ rel_read(mvc *sql, char *r, int *pos, li
                *pos += (int) strlen("REF");
                skipWS(r, pos);
                nr = readInt(r,pos); /* skip nr refs */
-               return rel_dup(list_fetch(refs, nr-1));
+               return list_fetch(refs, nr-1);
        }
 
        if (r[*pos] == 'i' && r[*pos+1] == 'n' && r[*pos+2] == 's') {
@@ -1931,6 +1932,7 @@ rel_read(mvc *sql, char *r, int *pos, li
 
                if (!(rel = rel_insert(sql, lrel, rrel)) || !(rel = 
read_rel_properties(sql, rel, r, pos)))
                        return NULL;
+               return rel;
        }
 
        if (r[*pos] == 'd' && r[*pos+1] == 'e' && r[*pos+2] == 'l') {
@@ -1953,6 +1955,8 @@ rel_read(mvc *sql, char *r, int *pos, li
 
                if (!(rel = rel_delete(sql->sa, lrel, rrel)) || !(rel = 
read_rel_properties(sql, rel, r, pos)))
                        return NULL;
+
+               return rel;
        }
 
        if (r[*pos] == 't' && r[*pos+1] == 'r' && r[*pos+2] == 'u') {
@@ -2006,10 +2010,11 @@ rel_read(mvc *sql, char *r, int *pos, li
                if (!update_allowed(sql, t, t->base.name, "UPDATE", "update", 
0) )
                        return NULL;
 
+               skipWS(r, pos);
                if (!(exps = read_exps(sql, lrel, rrel, NULL, r, pos, '[', 0, 
1))) /* columns to be updated */
                        return NULL;
 
-               for (node *n = rel->exps->h ; n ; n = n->next) {
+               for (node *n = exps->h ; n ; n = n->next) {
                        sql_exp *e = (sql_exp *) n->data;
                        const char *cname = exp_name(e);
 
@@ -2026,6 +2031,8 @@ rel_read(mvc *sql, char *r, int *pos, li
 
                if (!(rel = rel_update(sql, lrel, rrel, NULL, nexps)) || !(rel 
= read_rel_properties(sql, rel, r, pos)))
                        return NULL;
+
+               return rel;
        }
_______________________________________________
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org

Reply via email to