Changeset: 5397d31acb7d for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=5397d31acb7d
Added Files:
        sql/test/merge-statements/Tests/mergestmt01.sql
        sql/test/merge-statements/Tests/mergestmt01.stable.err
        sql/test/merge-statements/Tests/mergestmt01.stable.out
Modified Files:
        sql/server/rel_updates.c
        sql/server/sql_parser.y
        sql/test/merge-statements/Tests/All
        sql/test/merge-statements/Tests/mergestmt00.stable.err
Branch: merge-statements
Log Message:

Progress for insert and update statements within a merge statement. Allow 
sub-relation expressions to be used in them.


diffs (truncated from 508 to 300 lines):

diff --git a/sql/server/rel_updates.c b/sql/server/rel_updates.c
--- a/sql/server/rel_updates.c
+++ b/sql/server/rel_updates.c
@@ -458,7 +458,7 @@ update_allowed(mvc *sql, sql_table *t, c
 }
 
 static sql_rel *
-insert_generate_inserts(mvc *sql, size_t *nrows, sql_table *t, dlist *columns, 
symbol *val_or_q, const char* action)
+insert_generate_inserts(mvc *sql, sql_table *t, dlist *columns, symbol 
*val_or_q, const char* action)
 {
        sql_rel *r = NULL;
        size_t rowcount = 1;
@@ -557,12 +557,69 @@ insert_generate_inserts(mvc *sql, size_t
        r->exps = rel_inserts(sql, t, r, collist, rowcount, 0, action);
        if(!r->exps)
                return NULL;
-       if(nrows)
-               *nrows = rowcount;
        return r;
 }
 
 static sql_rel *
+merge_generate_inserts(mvc *sql, sql_table *t, sql_rel *r, dlist *columns, 
symbol *val_or_q)
+{
+       sql_rel *res = NULL;
+       list *collist = check_table_columns(sql, t, columns, "MERGE", 
t->base.name);
+
+       if (!collist)
+               return NULL;
+
+       if (val_or_q->token == SQL_VALUES) {
+               list *exps = new_exp_list(sql->sa);
+               dlist *rowlist = val_or_q->data.lval;
+
+               if (!rowlist->h) {
+                       res = rel_project(sql->sa, NULL, NULL);
+                       if (!columns)
+                               collist = NULL;
+               } else {
+                       node *m;
+                       dnode *n;
+                       dlist *inserts = rowlist->h->data.lval;
+
+                       if (dlist_length(rowlist) != 1)
+                               return sql_error(sql, 02, SQLSTATE(21S01) 
"MERGE: number of insert rows must be exactly one in a merge statement");
+                       if (dlist_length(inserts) != list_length(collist))
+                               return sql_error(sql, 02, SQLSTATE(21S01) 
"MERGE: number of values doesn't match number of columns of table '%s'", 
t->base.name);
+
+                       for (n = inserts->h, m = collist->h; n && m; n = 
n->next, m = m->next) {
+                               sql_column *c = m->data;
+                               sql_exp *ins = insert_value(sql, c, &r, 
n->data.sym, "MERGE");
+                               if (!ins)
+                                       return NULL;
+                               if (!ins->name)
+                                       exp_label(sql->sa, ins, ++sql->label);
+                               list_append(exps, ins);
+                       }
+               }
+               if (collist)
+                       res = rel_project(sql->sa, r, exps);
+       } else {
+               exp_kind ek = {type_value, card_relation, TRUE};
+               res = rel_subquery(sql, r, val_or_q, ek, APPLY_JOIN);
+               if (!res)
+                       return NULL;
+               if (res->op != op_project || res->r || need_distinct(res))
+                       res = rel_project(sql->sa, res, rel_projections(sql, 
res, NULL, 1, 0));
+       }
+       if (!res)
+               return NULL;
+       if ((res->exps && list_length(res->exps) != list_length(collist)) || 
!res->exps)
+               return sql_error(sql, 02, SQLSTATE(21S01) "MERGE: query result 
doesn't match number of columns in table '%s'", t->base.name);
+
+       res->l = r;
+       res->exps = rel_inserts(sql, t, res, collist, 2, 0, "MERGE");
+       if(!res->exps)
+               return NULL;
+       return res;
+}
+
+static sql_rel *
 insert_into(mvc *sql, dlist *qname, dlist *columns, symbol *val_or_q)
 {
        char *sname = qname_schema(qname);
@@ -586,7 +643,7 @@ insert_into(mvc *sql, dlist *qname, dlis
        }
        if (insert_allowed(sql, t, tname, "INSERT INTO", "insert into") == 
NULL) 
                return NULL;
-       r = insert_generate_inserts(sql, NULL, t, columns, val_or_q, "INSERT 
INTO");
+       r = insert_generate_inserts(sql, t, columns, val_or_q, "INSERT INTO");
        if(!r)
                return NULL;
        return rel_insert_table(sql, t, t->base.name, r);
@@ -1335,7 +1392,7 @@ rel_select_merge(sql_allocator *sa, sql_
 static sql_rel *
 merge_into_table(mvc *sql, dlist *qname, str alias, symbol *tref, symbol 
*search_cond, dlist *merge_list)
 {
-       char *sname = qname_schema(qname), *tname = qname_table(qname);
+       char *sname = qname_schema(qname), *tname = qname_table(qname), 
*alias_name;
        sql_schema *s = NULL;
        sql_table *t = NULL;
        sql_rel *bt, *joined, *join_rel = NULL, *extra_project = NULL, *insert 
= NULL, *upd_del = NULL, *res = NULL;
@@ -1368,6 +1425,7 @@ merge_into_table(mvc *sql, dlist *qname,
                for(node *nn = bt->exps->h ; nn ; nn = nn->next)
                        exp_setname(sql->sa, (sql_exp*) nn->data, alias, NULL); 
//the last parameter is optional, hence NULL
        }
+       alias_name = alias ? alias : t->base.name;
        if (rel_name(bt) && rel_name(joined) && strcmp(rel_name(bt), 
rel_name(joined)) == 0)
                return sql_error(sql, 02, SQLSTATE(42000) "MERGE: '%s' on both 
sides of the joining condition", rel_name(bt));
 
@@ -1403,11 +1461,9 @@ merge_into_table(mvc *sql, dlist *qname,
                                if((processed & MERGE_INSERT) == MERGE_INSERT)
                                        join_rel = rel_dup(join_rel);
 
-                               extra_project = rel_project(sql->sa, join_rel, 
rel_projections(sql, joined, NULL, 1, 0));
-                               e = exp_column(sql->sa, t->base.name, TID, 
sql_bind_localtype("oid"), CARD_MULTI, 0, 1);
-                               append(extra_project->exps, e);
+                               extra_project = rel_project(sql->sa, join_rel, 
rel_projections(sql, joined, NULL, 0, 0));
 
-                               for (node *n = extra_project->exps->h; n && 
n->next; n = n->next) { //don't iterate over TID
+                               for (node *n = extra_project->exps->h; n ; n = 
n->next) {
                                        sql_exp *exp = (sql_exp*) n->data;
                                        sql_exp *input = exp_column(sql->sa, 
exp_relname(exp), exp_name(exp), exp_subtype(exp), exp->card,
                                                                                
                has_nil(exp), is_intern(exp));
@@ -1416,11 +1472,16 @@ merge_into_table(mvc *sql, dlist *qname,
                                        append(se_exps, null_values);
                                }
 
+                               extra_project->exps = 
list_merge(extra_project->exps, rel_projections(sql, bt, NULL, 1, 0), 
(fdup)NULL);
+                               extra_project->exps = 
list_merge(extra_project->exps, rel_projections(sql, joined, NULL, 1, 0), 
(fdup)NULL);
+                               e = exp_column(sql->sa, alias_name, TID, 
sql_bind_localtype("oid"), CARD_MULTI, 0, 1);
+                               append(extra_project->exps, e);
+
                                upd_del = rel_select_merge(sql->sa, 
extra_project, se_exps);
-                               upd_del = rel_project(sql->sa, upd_del, 
rel_projections(sql, extra_project, NULL, 0, 0));
-                               for (node *n = upd_del->exps->h; n ; n = 
n->next)
-                                       exp_setname(sql->sa, (sql_exp*) 
n->data, t->base.name, NULL); //the last parameter is optional, hence NULL
+                               upd_del = rel_project(sql->sa, upd_del, 
rel_projections(sql, bt, NULL, 1, 0));
+                               upd_del->exps = list_merge(upd_del->exps, 
rel_projections(sql, joined, NULL, 1, 0), (fdup)NULL);
                                append(upd_del->exps, exp_copy(sql->sa, e));
+
                                upd_del = update_generate_assignments(sql, t, 
upd_del, rel_dup(bt), sts->h->data.lval, "MERGE");
                        } else if(uptdel == SQL_DELETE) {
                                if (!update_allowed(sql, t, tname, "MERGE", 
"merge", 1))
@@ -1428,11 +1489,9 @@ merge_into_table(mvc *sql, dlist *qname,
                                if((processed & MERGE_INSERT) == MERGE_INSERT)
                                        join_rel = rel_dup(join_rel);
 
-                               extra_project = rel_project(sql->sa, join_rel, 
rel_projections(sql, joined, NULL, 1, 0));
-                               e = exp_column(sql->sa, t->base.name, TID, 
sql_bind_localtype("oid"), CARD_MULTI, 0, 1);
-                               append(extra_project->exps, e);
+                               extra_project = rel_project(sql->sa, join_rel, 
rel_projections(sql, joined, NULL, 0, 0));
 
-                               for (node *n = extra_project->exps->h; n && 
n->next; n = n->next) { //don't iterate over TID
+                               for (node *n = extra_project->exps->h; n ; n = 
n->next) {
                                        sql_exp *exp = (sql_exp*) n->data;
                                        sql_exp *input = exp_column(sql->sa, 
exp_relname(exp), exp_name(exp), exp_subtype(exp), exp->card,
                                                                                
                has_nil(exp), is_intern(exp));
@@ -1441,6 +1500,9 @@ merge_into_table(mvc *sql, dlist *qname,
                                        append(se_exps, null_values);
                                }
 
+                               e = exp_column(sql->sa, alias_name, TID, 
sql_bind_localtype("oid"), CARD_MULTI, 0, 1);
+                               append(extra_project->exps, e);
+
                                upd_del = rel_select_merge(sql->sa, 
extra_project, se_exps);
                                upd_del = rel_project(sql->sa, upd_del, 
append(new_exp_list(sql->sa), exp_copy(sql->sa, e)));
                                upd_del = rel_delete(sql->sa, rel_dup(bt), 
upd_del);
@@ -1452,7 +1514,6 @@ merge_into_table(mvc *sql, dlist *qname,
                } else if(token == SQL_MERGE_NO_MATCH) {
                        sql_rel *to_ins = NULL;
                        list *se_exps = new_exp_list(sql->sa);
-                       size_t rowcount = 0;
 
                        if((processed & MERGE_INSERT) == MERGE_INSERT)
                                return sql_error(sql, 02, SQLSTATE(42000) 
"MERGE: only one WHEN NOT MATCHED clause is allowed");
@@ -1464,7 +1525,7 @@ merge_into_table(mvc *sql, dlist *qname,
                        if((processed & MERGE_UPDATE_DELETE) == 
MERGE_UPDATE_DELETE)
                                join_rel = rel_dup(join_rel);
 
-                       extra_project = rel_project(sql->sa, join_rel, 
rel_projections(sql, joined, NULL, 1, 0));
+                       extra_project = rel_project(sql->sa, join_rel, 
rel_projections(sql, joined, NULL, 0, 0));
 
                        for (node *n = extra_project->exps->h; n; n = n->next) {
                                sql_exp *exp = (sql_exp*) n->data;
@@ -1475,13 +1536,16 @@ merge_into_table(mvc *sql, dlist *qname,
                                append(se_exps, null_values);
                        }
 
+                       extra_project->exps = list_merge(extra_project->exps, 
rel_projections(sql, bt, NULL, 1, 0), (fdup)NULL);
+                       extra_project->exps = list_merge(extra_project->exps, 
rel_projections(sql, joined, NULL, 1, 0), (fdup)NULL);
+
                        to_ins = rel_select_merge(sql->sa, extra_project, 
se_exps);
-                       insert = insert_generate_inserts(sql, &rowcount, t, 
sts->h->data.lval, sts->h->next->data.sym, "MERGE");
+                       to_ins = rel_project(sql->sa, to_ins, 
rel_projections(sql, bt, NULL, 1, 0));
+                       to_ins->exps = list_merge(to_ins->exps, 
rel_projections(sql, joined, NULL, 1, 0), (fdup)NULL);
+
+                       insert = merge_generate_inserts(sql, t, to_ins, 
sts->h->data.lval, sts->h->next->data.sym);
                        if(!insert)
                                return NULL;
-                       if(rowcount != 2)
-                               return sql_error(sql, 02, SQLSTATE(42000) 
"MERGE: the insert values list must contain exactly one row");
-                       insert->l = to_ins;
                        insert = rel_insert(sql, rel_dup(bt), insert);
                        if(!insert)
                                return NULL;
diff --git a/sql/server/sql_parser.y b/sql/server/sql_parser.y
--- a/sql/server/sql_parser.y
+++ b/sql/server/sql_parser.y
@@ -3092,12 +3092,7 @@ merge_update_or_delete:
  ;
 
 merge_insert:
-   INSERT values_or_query_spec
-   { dlist *l = L();
-     append_list(l, NULL);
-     append_symbol(l, $2);
-     $$ = _symbol_create_list( SQL_INSERT, l ); }
- | INSERT column_commalist_parens values_or_query_spec
+   INSERT opt_column_list values_or_query_spec
    { dlist *l = L();
      append_list(l, $2);
      append_symbol(l, $3);
diff --git a/sql/test/merge-statements/Tests/All 
b/sql/test/merge-statements/Tests/All
--- a/sql/test/merge-statements/Tests/All
+++ b/sql/test/merge-statements/Tests/All
@@ -1,1 +1,2 @@
 mergestmt00
+mergestmt01
diff --git a/sql/test/merge-statements/Tests/mergestmt00.stable.err 
b/sql/test/merge-statements/Tests/mergestmt00.stable.err
--- a/sql/test/merge-statements/Tests/mergestmt00.stable.err
+++ b/sql/test/merge-statements/Tests/mergestmt00.stable.err
@@ -34,8 +34,8 @@ ERROR = !SELECT: no such column 'predata
 CODE  = 42S22
 MAPI  = (monetdb) /var/tmp/mtest-27206/.s.monetdb.34138
 QUERY = merge into predata using (select aa, bb from merging) sub on 
predata.bb = sub.bb when not matched then insert values (1, 1), (2,2); --error, 
only one row allowed in insert
-ERROR = !MERGE: the insert values list must contain exactly one row
-CODE  = 42000
+ERROR = !MERGE: number of insert rows must be exactly one in a merge statement
+CODE  = 21S01
 
 # 12:58:53 >  
 # 12:58:53 >  "Done."
diff --git a/sql/test/merge-statements/Tests/mergestmt01.sql 
b/sql/test/merge-statements/Tests/mergestmt01.sql
new file mode 100644
--- /dev/null
+++ b/sql/test/merge-statements/Tests/mergestmt01.sql
@@ -0,0 +1,56 @@
+create table merging (aa int, bb clob);
+create table predata (aa int, bb int);
+
+start transaction;
+
+insert into merging values (-100, 1);
+insert into predata values (15, 3), (3, 1), (2, 1), (5, 3), (NULL, 2), (3, 2);
+
+merge into predata using (select aa, bb from merging) sub on predata.bb = 
sub.bb
+      when matched then delete when not matched then insert values (6, 6);
+select aa, bb from predata;
+
+delete from predata;
+insert into predata values (15, 3), (3, 1), (2, 1), (5, 3), (4, 1), (6, 3);
+
+merge into predata using (select aa, bb from merging) sub on predata.bb = 
sub.bb
+      when not matched then insert values (null, null) when matched then 
update set bb = 3;
+select aa, bb from predata;
+
+delete from predata;
+insert into predata values (15, 3), (3, 1), (2, 1), (5, 3), (8, 2), (NULL, 4);
+
+merge into predata using (select aa, bb from merging) as sub on predata.bb = 
sub.bb
+      when matched then update set bb = predata.bb + 1;
+merge into predata othertt using (select aa, bb from merging) as sub on 
othertt.bb = sub.bb
+      when matched then update set bb = othertt.bb + sub.bb;
+select aa, bb from predata;
+
+delete from predata;
+insert into predata values (15, 1), (3, 1), (6, 3), (8, 2);
+
+merge into predata using (select aa, bb from merging) as sub on predata.bb = 
sub.bb
+      when not matched then insert values (sub.aa, 2);
+select aa, bb from predata;
+
+merge into predata othertt using (select aa, bb from merging) as sub on 
othertt.bb = sub.bb
+      when not matched then insert values (sub.aa + 5, othertt.bb - 1);
+select aa, bb from predata;
+
+delete from predata;
+insert into predata values (2, 2);
+
+merge into predata using (select aa, bb from merging) as sub on predata.bb = 
sub.bb
+      when not matched then insert select 41, -12;
+select aa, bb from predata;
+
+rollback;
+
+merge into predata using (select aa, bb from merging) as sub on predata.bb = 
sub.bb
+      when matched then update set bb = bb - 1; --error, bb is ambiguous
+
+merge into predata using (select aa, bb from merging) as sub on predata.bb = 
sub.bb
_______________________________________________
checkin-list mailing list
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to