LCOV - code coverage report
Current view: top level - sql/server - rel_distribute.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 139 165 84.2 %
Date: 2021-10-13 02:24:04 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : #include "monetdb_config.h"
      10             : #include "rel_distribute.h"
      11             : #include "rel_rel.h"
      12             : #include "rel_basetable.h"
      13             : #include "rel_exp.h"
      14             : #include "rel_prop.h"
      15             : #include "rel_dump.h"
      16             : #include "sql_privileges.h"
      17             : 
      18             : static int
      19     2155334 : has_remote_or_replica( sql_rel *rel )
      20             : {
      21     3953075 :         if (!rel)
      22             :                 return 0;
      23             : 
      24     3908613 :         switch (rel->op) {
      25     1100598 :         case op_basetable: {
      26     1100598 :                 sql_table *t = rel->l;
      27             : 
      28     1100598 :                 return t && (isReplicaTable(t) || isRemote(t));
      29             :         }
      30        2884 :         case op_table:
      31        2884 :                 if (IS_TABLE_PROD_FUNC(rel->flag) || rel->flag == TABLE_FROM_RELATION)
      32        2884 :                         return has_remote_or_replica( rel->l );
      33             :                 break;
      34     1006148 :         case op_join:
      35             :         case op_left:
      36             :         case op_right:
      37             :         case op_full:
      38             : 
      39             :         case op_semi:
      40             :         case op_anti:
      41             : 
      42             :         case op_union:
      43             :         case op_inter:
      44             :         case op_except:
      45             :         case op_merge:
      46             : 
      47             :         case op_insert:
      48             :         case op_update:
      49             :         case op_delete:
      50     1006148 :                 return has_remote_or_replica( rel->l ) || has_remote_or_replica( rel->r );
      51     1794855 :         case op_project:
      52             :         case op_select:
      53             :         case op_groupby:
      54             :         case op_topn:
      55             :         case op_sample:
      56             :         case op_truncate:
      57     1794855 :                 return has_remote_or_replica( rel->l );
      58        4128 :         case op_ddl:
      59        4128 :                 if (rel->flag == ddl_output || rel->flag == ddl_create_seq || rel->flag == ddl_alter_seq /*|| rel->flag == ddl_alter_table || rel->flag == ddl_create_table || rel->flag == ddl_create_view*/)
      60           2 :                         return has_remote_or_replica( rel->l );
      61        4126 :                 if (rel->flag == ddl_list || rel->flag == ddl_exception)
      62           0 :                         return has_remote_or_replica( rel->l ) || has_remote_or_replica( rel->r );
      63             :                 break;
      64             :         }
      65             :         return 0;
      66             : }
      67             : 
      68             : static sql_rel *
      69           4 : rewrite_replica(mvc *sql, list *exps, sql_table *t, sql_table *p, int remote_prop)
      70             : {
      71             :         node *n, *m;
      72           4 :         sql_rel *r = rel_basetable(sql, p, t->base.name);
      73             :         int allowed = 1;
      74             : 
      75           4 :         if (!table_privs(sql, p, PRIV_SELECT)) /* Test for privileges */
      76             :                 allowed = 0;
      77             : 
      78          12 :         for (n = exps->h; n; n = n->next) {
      79           8 :                 sql_exp *e = n->data;
      80           8 :                 const char *nname = exp_name(e);
      81             : 
      82           8 :                 node *nn = ol_find_name(t->columns, nname);
      83           8 :                 if (nn) {
      84           4 :                         sql_column *c = nn->data;
      85             : 
      86           4 :                         if (!allowed && !column_privs(sql, ol_fetch(p->columns, c->colnr), PRIV_SELECT))
      87           0 :                                 return sql_error(sql, 02, SQLSTATE(42000) "The user %s SELECT permissions on table '%s.%s' don't match %s '%s.%s'", get_string_global_var(sql, "current_user"),
      88           0 :                                                                  p->s->base.name, p->base.name, TABLE_TYPE_DESCRIPTION(t->type, t->properties), t->s->base.name, t->base.name);
      89           4 :                         rel_base_use(sql, r, c->colnr);
      90           4 :                 } else if (strcmp(nname, TID) == 0) {
      91           4 :                         rel_base_use_tid(sql, r);
      92             :                 } else {
      93           0 :                         assert(0);
      94             :                 }
      95             :         }
      96           4 :         r = rewrite_basetable(sql, r);
      97          12 :         for (n = exps->h, m = r->exps->h; n && m; n = n->next, m = m->next) {
      98           8 :                 sql_exp *e = n->data;
      99           8 :                 sql_exp *ne = m->data;
     100             : 
     101           8 :                 exp_prop_alias(sql->sa, ne, e);
     102             :         }
     103             : 
     104             :         /* set_remote() */
     105           4 :         if (remote_prop && p && isRemote(p)) {
     106           0 :                 char *local_name = sa_strconcat(sql->sa, sa_strconcat(sql->sa, p->s->base.name, "."), p->base.name);
     107           0 :                 prop *p = r->p = prop_create(sql->sa, PROP_REMOTE, r->p);
     108           0 :                 p->value = local_name;
     109             :         }
     110             :         return r;
     111             : }
     112             : 
     113             : static sql_rel *
     114           6 : replica_rewrite(visitor *v, sql_table *t, list *exps)
     115             : {
     116             :         sql_rel *res = NULL;
     117           7 :         const char *uri = (const char *) v->data;
     118             : 
     119           7 :         if (mvc_highwater(v->sql))
     120           0 :                 return sql_error(v->sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     121             : 
     122           7 :         if (uri) {
     123             :                 /* replace by the replica which matches the uri */
     124           3 :                 for (node *n = t->members->h; n; n = n->next) {
     125           2 :                         sql_part *p = n->data;
     126           2 :                         sql_table *pt = find_sql_table_id(v->sql->session->tr, t->s, p->member);
     127             : 
     128           2 :                         if (isRemote(pt) && strcmp(uri, pt->query) == 0) {
     129           0 :                                 res = rewrite_replica(v->sql, exps, t, pt, 0);
     130           0 :                                 break;
     131             :                         }
     132             :                 }
     133             :         } else { /* no match, find one without remote or use first */
     134             :                 sql_table *pt = NULL;
     135             :                 int remote = 1;
     136             : 
     137           9 :                 for (node *n = t->members->h; n; n = n->next) {
     138           8 :                         sql_part *p = n->data;
     139           8 :                         sql_table *next = find_sql_table_id(v->sql->session->tr, t->s, p->member);
     140             : 
     141             :                         /* give preference to local tables and avoid empty merge or replica tables */
     142           8 :                         if (!isRemote(next) && ((!isReplicaTable(next) && !isMergeTable(next)) || !list_empty(next->members))) {
     143             :                                 pt = next;
     144             :                                 remote = 0;
     145             :                                 break;
     146             :                         }
     147             :                 }
     148           6 :                 if (!pt) {
     149           1 :                         sql_part *p = t->members->h->data;
     150           1 :                         pt = find_sql_table_id(v->sql->session->tr, t->s, p->member);
     151             :                 }
     152             : 
     153           6 :                 if ((isMergeTable(pt) || isReplicaTable(pt)) && list_empty(pt->members))
     154           1 :                         return sql_error(v->sql, 02, SQLSTATE(42000) "The %s '%s.%s' should have at least one table associated",
     155           1 :                                                                 TABLE_TYPE_DESCRIPTION(pt->type, pt->properties), pt->s->base.name, pt->base.name);
     156           5 :                 res = isReplicaTable(pt) ? replica_rewrite(v, pt, exps) : rewrite_replica(v->sql, exps, t, pt, remote);
     157             :         }
     158             :         return res;
     159             : }
     160             : 
     161             : static sql_rel *
     162     1900853 : replica(visitor *v, sql_rel *rel)
     163             : {
     164             :         /* for merge statement join, ignore the multiple references */
     165     1900853 :         if (rel_is_ref(rel) && !(rel->flag&MERGE_LEFT)) {
     166       86002 :                 if (has_remote_or_replica(rel)) {
     167           0 :                         sql_rel *nrel = rel_copy(v->sql, rel, 1);
     168             : 
     169           0 :                         rel_destroy(rel);
     170             :                         rel = nrel;
     171             :                 } else {
     172             :                         return rel;
     173             :                 }
     174             :         }
     175     1814851 :         if (is_basetable(rel->op)) {
     176      477897 :                 sql_table *t = rel->l;
     177             : 
     178      477897 :                 if (t && isReplicaTable(t)) {
     179          13 :                         if (list_empty(t->members)) /* in DDL statement cases skip if replica is empty */
     180             :                                 return rel;
     181             : 
     182           6 :                         sql_rel *r = replica_rewrite(v, t, rel->exps);
     183           6 :                         rel_destroy(rel);
     184             :                         rel = r;
     185             :                 }
     186             :         }
     187             :         return rel;
     188             : }
     189             : 
     190             : static sql_rel *
     191     1523235 : distribute(visitor *v, sql_rel *rel)
     192             : {
     193             :         prop *p, *pl, *pr;
     194             : 
     195             :         /* for merge statement join, ignore the multiple references */
     196     1523235 :         if (rel_is_ref(rel) && !(rel->flag&MERGE_LEFT)) {
     197       57037 :                 if (has_remote_or_replica(rel)) {
     198           3 :                         sql_rel *nrel = rel_copy(v->sql, rel, 1);
     199             : 
     200           3 :                         rel_destroy(rel);
     201             :                         rel = nrel;
     202             :                 } else {
     203             :                         return rel;
     204             :                 }
     205             :         }
     206     1466201 :         sql_rel *l = rel->l, *r = rel->r; /* look on left and right relations after possibly doing rel_copy */
     207             : 
     208     1466201 :         switch (rel->op) {
     209      365086 :         case op_basetable: {
     210             :                 sql_table *t = rel->l;
     211             : 
     212             :                 /* set_remote() */
     213      365086 :                 if (t && isRemote(t) && (p = find_prop(rel->p, PROP_REMOTE)) == NULL) {
     214         236 :                         char *local_name = sa_strconcat(v->sql->sa, sa_strconcat(v->sql->sa, t->s->base.name, "."), t->base.name);
     215         236 :                         p = rel->p = prop_create(v->sql->sa, PROP_REMOTE, rel->p);
     216         236 :                         p->value = local_name;
     217             :                 }
     218             :         } break;
     219        5741 :         case op_table:
     220        5741 :                 if (IS_TABLE_PROD_FUNC(rel->flag) || rel->flag == TABLE_FROM_RELATION) {
     221        5741 :                         if (l && (p = find_prop(l->p, PROP_REMOTE)) != NULL) {
     222           4 :                                 l->p = prop_remove(l->p, p);
     223           4 :                                 if (!find_prop(rel->p, PROP_REMOTE)) {
     224           4 :                                         p->p = rel->p;
     225           4 :                                         rel->p = p;
     226             :                                 }
     227             :                         }
     228             :                 } break;
     229      306678 :         case op_join:
     230             :         case op_left:
     231             :         case op_right:
     232             :         case op_full:
     233             : 
     234             :         case op_semi:
     235             :         case op_anti:
     236             : 
     237             :         case op_union:
     238             :         case op_inter:
     239             :         case op_except:
     240             : 
     241             :         case op_insert:
     242             :         case op_update:
     243             :         case op_delete:
     244             :         case op_merge:
     245      328524 :                 if (is_join(rel->op) && list_empty(rel->exps) &&
     246       43683 :                         find_prop(l->p, PROP_REMOTE) == NULL &&
     247       21837 :                         find_prop(r->p, PROP_REMOTE) == NULL) {
     248             :                         /* cleanup replica's */
     249       21829 :                         visitor rv = { .sql = v->sql };
     250             : 
     251       21829 :                         l = rel->l = rel_visitor_bottomup(&rv, l, &replica);
     252       21829 :                         rv.data = NULL;
     253       21829 :                         r = rel->r = rel_visitor_bottomup(&rv, r, &replica);
     254       21829 :                         if ((!l || !r) && v->sql->session->status) /* if the recursive calls failed */
     255           0 :                                 return NULL;
     256             :                 }
     257      535737 :                 if ((is_join(rel->op) || is_semi(rel->op) || is_set(rel->op)) &&
     258      229094 :                         (pl = find_prop(l->p, PROP_REMOTE)) != NULL &&
     259          47 :                         find_prop(r->p, PROP_REMOTE) == NULL) {
     260          12 :                         visitor rv = { .sql = v->sql, .data = pl->value };
     261             : 
     262          12 :                         if (!(r = rel_visitor_bottomup(&rv, r, &replica)) && v->sql->session->status)
     263           0 :                                 return NULL;
     264          12 :                         rv.data = NULL;
     265          12 :                         if (!(r = rel->r = rel_visitor_bottomup(&rv, r, &distribute)) && v->sql->session->status)
     266             :                                 return NULL;
     267      535713 :                 } else if ((is_join(rel->op) || is_semi(rel->op) || is_set(rel->op)) &&
     268      458071 :                         find_prop(l->p, PROP_REMOTE) == NULL &&
     269      229024 :                         (pr = find_prop(r->p, PROP_REMOTE)) != NULL) {
     270          24 :                         visitor rv = { .sql = v->sql, .data = pr->value };
     271             : 
     272          24 :                         if (!(l = rel_visitor_bottomup(&rv, l, &replica)) && v->sql->session->status)
     273           0 :                                 return NULL;
     274          24 :                         rv.data = NULL;
     275          24 :                         if (!(l = rel->l = rel_visitor_bottomup(&rv, l, &distribute)) && v->sql->session->status)
     276             :                                 return NULL;
     277             :                 }
     278             : 
     279      306678 :                 if (rel->flag&MERGE_LEFT) /* search for any remote tables but don't propagate over to this relation */
     280             :                         return rel;
     281             : 
     282      306584 :                 if (l && (pl = find_prop(l->p, PROP_REMOTE)) != NULL &&
     283          35 :                         r && (pr = find_prop(r->p, PROP_REMOTE)) != NULL &&
     284          23 :                         strcmp(pl->value, pr->value) == 0) {
     285           2 :                         l->p = prop_remove(l->p, pl);
     286           2 :                         r->p = prop_remove(r->p, pr);
     287           2 :                         if (!find_prop(rel->p, PROP_REMOTE)) {
     288           2 :                                 pl->p = rel->p;
     289           2 :                                 rel->p = pl;
     290             :                         }
     291             :                 }
     292             :                 break;
     293      570069 :         case op_project:
     294             :         case op_select:
     295             :         case op_groupby:
     296             :         case op_topn:
     297             :         case op_sample:
     298             :         case op_truncate:
     299      570069 :                 if (l && (p = find_prop(l->p, PROP_REMOTE)) != NULL) {
     300         287 :                         l->p = prop_remove(l->p, p);
     301         287 :                         if (!find_prop(rel->p, PROP_REMOTE)) {
     302         236 :                                 p->p = rel->p;
     303         236 :                                 rel->p = p;
     304             :                         }
     305             :                 }
     306             :                 break;
     307      218627 :         case op_ddl:
     308      218627 :                 if (rel->flag == ddl_output || rel->flag == ddl_create_seq || rel->flag == ddl_alter_seq /*|| rel->flag == ddl_alter_table || rel->flag == ddl_create_table || rel->flag == ddl_create_view*/) {
     309         348 :                         if (l && (p = find_prop(l->p, PROP_REMOTE)) != NULL) {
     310           0 :                                 l->p = prop_remove(l->p, p);
     311           0 :                                 if (!find_prop(rel->p, PROP_REMOTE)) {
     312           0 :                                         p->p = rel->p;
     313           0 :                                         rel->p = p;
     314             :                                 }
     315             :                         }
     316      218279 :                 } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
     317         754 :                         if (l && (pl = find_prop(l->p, PROP_REMOTE)) != NULL &&
     318           0 :                                 r && (pr = find_prop(r->p, PROP_REMOTE)) != NULL &&
     319           0 :                                 strcmp(pl->value, pr->value) == 0) {
     320           0 :                                 l->p = prop_remove(l->p, pl);
     321           0 :                                 r->p = prop_remove(r->p, pr);
     322           0 :                                 if (!find_prop(rel->p, PROP_REMOTE)) {
     323           0 :                                         pl->p = rel->p;
     324           0 :                                         rel->p = pl;
     325             :                                 }
     326             :                         }
     327             :                 }
     328             :                 break;
     329             :         }
     330             :         return rel;
     331             : }
     332             : 
     333             : static sql_rel *
     334     1523067 : rel_remote_func(visitor *v, sql_rel *rel)
     335             : {
     336             :         (void) v;
     337             : 
     338             :         int rused = 1 << 2; /* Don't modify the same relation twice */
     339     1523067 :         if (rel->used & rused)
     340             :                 return rel;
     341     1078912 :         rel->used |= rused;
     342             : 
     343     1078912 :         if (find_prop(rel->p, PROP_REMOTE) != NULL) {
     344         186 :                 list *exps = rel_projections(v->sql, rel, NULL, 1, 1);
     345         186 :                 rel = rel_relational_func(v->sql->sa, rel, exps);
     346             :         }
     347             :         return rel;
     348             : }
     349             : 
     350             : sql_rel *
     351      362701 : rel_distribute(mvc *sql, sql_rel *rel)
     352             : {
     353      362701 :         visitor v = { .sql = sql };
     354             : 
     355      362701 :         rel = rel_visitor_bottomup(&v, rel, &distribute);
     356      362701 :         v.data = NULL;
     357      362701 :         rel = rel_visitor_bottomup(&v, rel, &replica);
     358      362701 :         rel = rel_visitor_bottomup(&v, rel, &rel_remote_func);
     359      362701 :         return rel;
     360             : }

Generated by: LCOV version 1.14