Changeset: 95e70998012e for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/95e70998012e
Added Files:
        sql/test/merge-partitions/Tests/mergepart33.test
Modified Files:
        sql/server/rel_propagate.c
        sql/server/rel_updates.c
        sql/server/sql_partition.c
        sql/server/sql_partition.h
        sql/test/merge-partitions/Tests/All
Branch: Jul2021
Log Message:

Validate inserts/updates on nested mergetables. At the moment, inserts and 
updates on complex indexes on partitioned merge tables cannot be done


diffs (truncated from 342 to 300 lines):

diff --git a/sql/server/rel_propagate.c b/sql/server/rel_propagate.c
--- a/sql/server/rel_propagate.c
+++ b/sql/server/rel_propagate.c
@@ -644,6 +644,7 @@ rel_generate_subupdates(mvc *sql, sql_re
                sql_table *sub = find_sql_table_id(sql->session->tr, t->s, 
pt->member);
                sql_rel *s1, *dup = NULL;
                list *uexps = exps_copy(sql, rel->exps), *checked_updates = 
new_exp_list(sql->sa);
+               sql_rel *bt = rel_basetable(sql, sub, sub->base.name);
 
                if (!update_allowed(sql, sub, sub->base.name, "UPDATE", 
"update", 0))
                        return NULL;
@@ -657,6 +658,7 @@ rel_generate_subupdates(mvc *sql, sql_re
 
                                if (!c)
                                        return sql_error(sql, ERR_NOTFOUND, 
SQLSTATE(42S22) "UPDATE: no such column '%s.%s'\n", sub->base.name, cname);
+                               rel_base_use(sql, bt, c->colnr);
                                if (!(e = update_check_column(sql, sub, c, e, 
rel, c->base.name, "UPDATE")))
                                        return NULL;
                        }
@@ -671,7 +673,7 @@ rel_generate_subupdates(mvc *sql, sql_re
                for (node *ne = checked_updates->h ; ne ; ne = ne->next)
                        ne->data = exp_change_column_table(sql, (sql_exp*) 
ne->data, t, sub);
 
-               s1 = rel_update(sql, rel_basetable(sql, sub, sub->base.name), 
dup, NULL, checked_updates);
+               s1 = rel_update(sql, bt, dup, NULL, checked_updates);
                if (just_one == 0) {
                        sel = rel_list(sql->sa, sel, s1);
                } else {
@@ -812,6 +814,8 @@ rel_generate_subinserts(sql_query *query
                }
 
                new_table = rel_basetable(sql, sub, sub->base.name);
+               rel_base_use_all(query->sql, new_table);
+               new_table = rewrite_basetable(query->sql, new_table);
                new_table->p = prop_create(sql->sa, PROP_USED, new_table->p); 
/* don't create infinite loops in the optimizer */
 
                if (isPartitionedByExpressionTable(t)) {
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
@@ -15,6 +15,7 @@
 #include "rel_exp.h"
 #include "rel_schema.h"
 #include "sql_privileges.h"
+#include "sql_partition.h"
 #include "rel_unnest.h"
 #include "rel_optimizer.h"
 #include "rel_dump.h"
@@ -391,9 +392,23 @@ rel_inserts(mvc *sql, sql_table *t, sql_
        return exps;
 }
 
+static bool
+has_complex_indexes(sql_table *t)
+{
+       for (node *n = ol_first_node(t->idxs); n; n = n->next) {
+               sql_idx *i = n->data;
+
+               if (hash_index(i->type) || oid_index(i->type) || i->type == 
no_idx)
+                       return true;
+       }
+       return false;
+}
+
 sql_table *
 insert_allowed(mvc *sql, sql_table *t, char *tname, char *op, char *opname)
 {
+       list *mts = NULL;
+
        if (!t) {
                if (sql->session->status) /* if find_table_or_view_on_scope was 
already called, don't overwrite error message */
                        return NULL;
@@ -404,6 +419,8 @@ insert_allowed(mvc *sql, sql_table *t, c
                return sql_error(sql, 02, SQLSTATE(42000) "%s: cannot %s merge 
table '%s'", op, opname, tname);
        } else if ((isRangePartitionTable(t) || isListPartitionTable(t)) && 
list_length(t->members)==0) {
                return sql_error(sql, 02, SQLSTATE(42000) "%s: %s partitioned 
table '%s' has no partitions set", op, isListPartitionTable(t)?"list":"range", 
tname);
+       } else if ((isRangePartitionTable(t) || isListPartitionTable(t)) && 
has_complex_indexes(t)) {
+               return sql_error(sql, 02, SQLSTATE(42000) "%s: not possible to 
insert into a partitioned table with complex indexes at the moment", op);
        } else if (isRemote(t)) {
                return sql_error(sql, 02, SQLSTATE(42000) "%s: cannot %s remote 
table '%s' from this server at the moment", op, opname, tname);
        } else if (isReplicaTable(t)) {
@@ -413,10 +430,16 @@ insert_allowed(mvc *sql, sql_table *t, c
        }
        if (t && !isTempTable(t) && store_readonly(sql->session->tr->store))
                return sql_error(sql, 02, SQLSTATE(42000) "%s: %s table '%s' 
not allowed in readonly mode", op, opname, tname);
+       if (has_complex_indexes(t) && (mts = partition_find_mergetables(sql, 
t))) {
+               for (node *n = mts->h ; n ; n = n->next) {
+                       sql_part *pt = n->data;
 
-       if (!table_privs(sql, t, PRIV_INSERT)) {
+                       if ((isRangePartitionTable(pt->t) || 
isListPartitionTable(pt->t)))
+                               return sql_error(sql, 02, SQLSTATE(42000) "%s: 
not possible to insert into a partitioned table with complex indexes at the 
moment", op);
+               }
+       }
+       if (!table_privs(sql, t, PRIV_INSERT))
                return sql_error(sql, 02, SQLSTATE(42000) "%s: insufficient 
privileges for user '%s' to %s table '%s'", op, get_string_global_var(sql, 
"current_user"), opname, tname);
-       }
        return t;
 }
 
@@ -431,6 +454,8 @@ copy_allowed(mvc *sql, int from)
 sql_table *
 update_allowed(mvc *sql, sql_table *t, char *tname, char *op, char *opname, 
int is_delete)
 {
+       list *mts = NULL;
+
        if (!t) {
                return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42S02) "%s: no 
such table '%s'", op, tname);
        } else if (isView(t)) {
@@ -441,6 +466,8 @@ update_allowed(mvc *sql, sql_table *t, c
                return sql_error(sql, 02, SQLSTATE(42000) "%s: cannot %s merge 
table '%s' has no partitions set", op, opname, tname);
        } else if ((isRangePartitionTable(t) || isListPartitionTable(t)) && 
list_length(t->members)==0) {
                return sql_error(sql, 02, SQLSTATE(42000) "%s: %s partitioned 
table '%s' has no partitions set", op, isListPartitionTable(t)?"list":"range", 
tname);
+       } else if ((isRangePartitionTable(t) || isListPartitionTable(t)) && 
has_complex_indexes(t)) {
+               return sql_error(sql, 02, SQLSTATE(42000) "%s: not possible to 
update a partitioned table with complex indexes at the moment", op);
        } else if (isRemote(t)) {
                return sql_error(sql, 02, SQLSTATE(42000) "%s: cannot %s remote 
table '%s' from this server at the moment", op, opname, tname);
        } else if (isReplicaTable(t)) {
@@ -450,6 +477,14 @@ update_allowed(mvc *sql, sql_table *t, c
        }
        if (t && !isTempTable(t) && store_readonly(sql->session->tr->store))
                return sql_error(sql, 02, SQLSTATE(42000) "%s: %s table '%s' 
not allowed in readonly mode", op, opname, tname);
+       if (has_complex_indexes(t) && (mts = partition_find_mergetables(sql, 
t))) {
+               for (node *n = mts->h ; n ; n = n->next) {
+                       sql_part *pt = n->data;
+
+                       if ((isRangePartitionTable(pt->t) || 
isListPartitionTable(pt->t)))
+                               return sql_error(sql, 02, SQLSTATE(42000) "%s: 
not possible to update a partitioned table with complex indexes at the moment", 
op);
+               }
+       }
        if ((is_delete == 1 && !table_privs(sql, t, PRIV_DELETE)) || (is_delete 
== 2 && !table_privs(sql, t, PRIV_TRUNCATE)))
                return sql_error(sql, 02, SQLSTATE(42000) "%s: insufficient 
privileges for user '%s' to %s table '%s'", op, get_string_global_var(sql, 
"current_user"), opname, tname);
        return t;
@@ -925,25 +960,29 @@ static sql_rel *
 update_generate_assignments(sql_query *query, sql_table *t, sql_rel *r, 
sql_rel *bt, dlist *assignmentlist, const char *action)
 {
        mvc *sql = query->sql;
-       sql_table *mt = NULL;
        sql_exp **updates = SA_ZNEW_ARRAY(sql->sa, sql_exp*, 
ol_length(t->columns));
-       list *exps, *pcols = NULL;
+       list *exps, *mts = partition_find_mergetables(sql, t);
        dnode *n;
        const char *rname = NULL;
 
-       if (isPartitionedByColumnTable(t) || isPartitionedByExpressionTable(t))
-               mt = t;
-       else if (partition_find_part(sql->session->tr, t, NULL))
-               mt = partition_find_part(sql->session->tr, t, NULL)->t;
+       if (!list_empty(mts)) {
+               for (node *nn = mts->h; nn; ) { /* extract mergetable from the 
parts */
+                       node *next = nn->next;
+                       sql_part *pt = nn->data;
 
-       if (mt && isPartitionedByColumnTable(mt)) {
-               pcols = sa_list(sql->sa);
-               int *nid = sa_alloc(sql->sa, sizeof(int));
-               *nid = mt->part.pcol->colnr;
-               list_append(pcols, nid);
-       } else if (mt && isPartitionedByExpressionTable(mt)) {
-               pcols = mt->part.pexp->cols;
+                       if (isPartitionedByColumnTable(pt->t) || 
isPartitionedByExpressionTable(pt->t))
+                               nn->data = pt->t;
+                       else
+                               list_remove_node(mts, NULL, nn);
+                       nn = next;
+               }
        }
+       if (isPartitionedByColumnTable(t) || isPartitionedByExpressionTable(t)) 
{ /* validate update on mergetable */
+               if (!mts)
+                       mts = sa_list(sql->sa);
+               list_append(mts, t);
+       }
+
        /* first create the project */
        exps = list_append(new_exp_list(sql->sa), exp_column(sql->sa, rname = 
rel_name(r), TID, sql_bind_localtype("oid"), CARD_MULTI, 0, 1));
 
@@ -1020,14 +1059,18 @@ update_generate_assignments(sql_query *q
                                        return sql_error(sql, ERR_NOTFOUND, 
SQLSTATE(42S22) "%s: no such column '%s.%s'", action, t->base.name, cname);
                                if (updates[c->colnr])
                                        return sql_error(sql, 02, 
SQLSTATE(42000) "%s: Multiple assignments to same column '%s'", action, 
c->base.name);
-                               if (mt && pcols) {
-                                       for (node *nn = pcols->h; nn; nn = 
n->next) {
-                                               int next = *(int*) nn->data;
-                                               if (next == c->colnr) {
-                                                       if 
(isPartitionedByColumnTable(mt)) {
+                               if (!list_empty(mts)) {
+                                       for (node *nn = mts->h; nn; nn = 
nn->next) {
+                                               sql_table *mt = nn->data;
+
+                                               if 
(isPartitionedByColumnTable(mt)) {
+                                                       if 
(mt->part.pcol->colnr == c->colnr)
                                                                return 
sql_error(sql, 02, SQLSTATE(42000) "%s: Update on the partitioned column is not 
possible at the moment", action);
-                                                       } else if 
(isPartitionedByExpressionTable(mt)) {
-                                                               return 
sql_error(sql, 02, SQLSTATE(42000) "%s: Update a column used by the partition's 
expression is not possible at the moment", action);
+                                               } else if 
(isPartitionedByExpressionTable(mt)) {
+                                                       for (node *nnn = 
mt->part.pexp->cols->h ; nnn ; nnn = nnn->next) {
+                                                               int next = 
*(int*) nnn->data;
+                                                               if (next == 
c->colnr)
+                                                                       return 
sql_error(sql, 02, SQLSTATE(42000) "%s: Update a column used by the partition's 
expression is not possible at the moment", action);
                                                        }
                                                }
                                        }
@@ -1053,14 +1096,18 @@ update_generate_assignments(sql_query *q
                                return sql_error(sql, ERR_NOTFOUND, 
SQLSTATE(42S22) "%s: no such column '%s.%s'", action, t->base.name, cname);
                        if (updates[c->colnr])
                                return sql_error(sql, 02, SQLSTATE(42000) "%s: 
Multiple assignments to same column '%s'", action, c->base.name);
-                       if (mt && pcols) {
-                               for (node *nn = pcols->h; nn; nn = nn->next) {
-                                       int next = *(int*) nn->data;
-                                       if (next == c->colnr) {
-                                               if 
(isPartitionedByColumnTable(mt)) {
+                       if (!list_empty(mts)) {
+                               for (node *nn = mts->h; nn; nn = nn->next) {
+                                       sql_table *mt = nn->data;
+
+                                       if (isPartitionedByColumnTable(mt)) {
+                                               if (mt->part.pcol->colnr == 
c->colnr)
                                                        return sql_error(sql, 
02, SQLSTATE(42000) "%s: Update on the partitioned column is not possible at 
the moment", action);
-                                               } else if 
(isPartitionedByExpressionTable(mt)) {
-                                                       return sql_error(sql, 
02, SQLSTATE(42000) "%s: Update a column used by the partition's expression is 
not possible at the moment", action);
+                                       } else if 
(isPartitionedByExpressionTable(mt)) {
+                                               for (node *nnn = 
mt->part.pexp->cols->h ; nnn ; nnn = nnn->next) {
+                                                       int next = *(int*) 
nnn->data;
+                                                       if (next == c->colnr)
+                                                               return 
sql_error(sql, 02, SQLSTATE(42000) "%s: Update a column used by the partition's 
expression is not possible at the moment", action);
                                                }
                                        }
                                }
@@ -1715,7 +1762,8 @@ copyfromloader(sql_query *query, dlist *
        char *tname = qname_schema_object(qname);
        sql_subfunc *loader = NULL;
        sql_rel *rel = NULL;
-       sql_table* t;
+       sql_table *t;
+       list *mts;
 
        if (!copy_allowed(sql, 1))
                return sql_error(sql, 02, SQLSTATE(42000) "COPY INTO: 
insufficient privileges: "
@@ -1724,12 +1772,15 @@ copyfromloader(sql_query *query, dlist *
        //TODO the COPY LOADER INTO should return an insert relation (instead 
of ddl) to handle partitioned tables properly
        if (insert_allowed(sql, t, tname, "COPY INTO", "copy into") == NULL)
                return NULL;
-       else if (isPartitionedByColumnTable(t) || 
isPartitionedByExpressionTable(t))
+       if (isPartitionedByColumnTable(t) || isPartitionedByExpressionTable(t))
                return sql_error(sql, 02, SQLSTATE(42000) "COPY LOADER INTO: 
not possible for partitioned tables at the moment");
-       else if (partition_find_part(sql->session->tr, t, NULL)) {
-               sql_part *mt = partition_find_part(sql->session->tr, t, NULL);
-               if (mt && (isPartitionedByColumnTable(mt->t) || 
isPartitionedByExpressionTable(mt->t)))
-                       return sql_error(sql, 02, SQLSTATE(42000) "COPY LOADER 
INTO: not possible for tables child of partitioned tables at the moment");
+       if ((mts = partition_find_mergetables(sql, t))) {
+               for (node *n = mts->h ; n ; n = n->next) {
+                       sql_part *pt = n->data;
+
+                       if ((isPartitionedByColumnTable(pt->t) || 
isPartitionedByExpressionTable(pt->t)))
+                               return sql_error(sql, 02, SQLSTATE(42000) "COPY 
LOADER INTO: not possible for tables child of partitioned tables at the 
moment");
+               }
        }
 
        rel = rel_loader_function(query, fcall, new_exp_list(sql->sa), &loader);
diff --git a/sql/server/sql_partition.c b/sql/server/sql_partition.c
--- a/sql/server/sql_partition.c
+++ b/sql/server/sql_partition.c
@@ -20,6 +20,20 @@
 #include "rel_updates.h"
 #include "mal_exception.h"
 
+list *
+partition_find_mergetables(mvc *sql, sql_table *t)
+{
+       sql_trans *tr = sql->session->tr;
+       list *res = NULL;
+
+       for(sql_part *pt = partition_find_part(tr, t, NULL); pt; pt = 
partition_find_part(tr, t, pt)) {
+               if (!res)
+                       res = sa_list(sql->sa);
+               list_append(res, pt);
+       }
+       return res;
+}
+
 static int
 key_column_colnr(sql_kc *pkey)
 {
diff --git a/sql/server/sql_partition.h b/sql/server/sql_partition.h
--- a/sql/server/sql_partition.h
+++ b/sql/server/sql_partition.h
@@ -12,6 +12,7 @@
 #include "sql_mvc.h"
 #include "sql_catalog.h"
 
+extern list* partition_find_mergetables(mvc *sql, sql_table *t);
 extern str sql_partition_validate_key(mvc *sql, sql_table *nt, sql_key *k, 
const char* op);
 extern str bootstrap_partition_expression(mvc* sql, sql_table *mt, int 
instantiate);
 extern void find_partition_type(sql_subtype *tpe, sql_table *mt);
diff --git a/sql/test/merge-partitions/Tests/All 
b/sql/test/merge-partitions/Tests/All
--- a/sql/test/merge-partitions/Tests/All
+++ b/sql/test/merge-partitions/Tests/All
@@ -31,3 +31,4 @@ mergepart29
 mergepart30
 mergepart31
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to