LCOV - code coverage report
Current view: top level - sql/server - rel_optimizer.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 312 367 85.0 %
Date: 2024-12-19 23:10:26 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : #include "rel_optimizer.h"
      15             : #include "rel_optimizer_private.h"
      16             : #include "rel_rel.h"
      17             : #include "rel_basetable.h"
      18             : #include "rel_exp.h"
      19             : #include "rel_propagate.h"
      20             : #include "rel_statistics.h"
      21             : #include "sql_privileges.h"
      22             : 
      23             : static sql_rel *
      24     5026666 : rel_properties(visitor *v, sql_rel *rel)
      25             : {
      26     5026666 :         global_props *gp = (global_props*)v->data;
      27             : 
      28             :         /* Don't flag any changes here! */
      29     5026666 :         gp->cnt[(int)rel->op]++;
      30     5026666 :         gp->needs_distinct |= need_distinct(rel);
      31     5026666 :         gp->recursive |= is_recursive(rel);
      32     5026666 :         if (gp->instantiate && is_basetable(rel->op)) {
      33      737681 :                 mvc *sql = v->sql;
      34      737681 :                 sql_table *t = (sql_table *) rel->l;
      35      737681 :                 sql_part *pt;
      36             : 
      37             :                 /* If the plan has a merge table or a child of one, then rel_merge_table_rewrite has to run */
      38      737681 :                 gp->needs_mergetable_rewrite |= (isMergeTable(t) || (t->s && t->s->parts && (pt = partition_find_part(sql->session->tr, t, NULL))));
      39      737641 :                 gp->needs_remote_replica_rewrite |= (isRemote(t) || isReplicaTable(t));
      40             :         }
      41     5026626 :         return rel;
      42             : }
      43             : 
      44             : typedef struct {
      45             :         atom *lval;
      46             :         atom *hval;
      47             :         bte anti:1,
      48             :                 semantics:1;
      49             :         int flag;
      50             :         list *values;
      51             : } range_limit;
      52             : 
      53             : typedef struct {
      54             :         list *cols;
      55             :         list *ranges;
      56             :         sql_rel *sel;
      57             : } merge_table_prune_info;
      58             : 
      59             : static sql_rel *merge_table_prune_and_unionize(visitor *v, sql_rel *mt_rel, merge_table_prune_info *info);
      60             : 
      61             : static sql_rel *
      62         549 : rel_wrap_select_around_mt_child(visitor *v, sql_rel *t, merge_table_prune_info *info)
      63             : {
      64             :         // TODO: it has to be a table (merge table component) add checks
      65         549 :         sql_table *subt = (sql_table *)t->l;
      66             : 
      67         549 :         if (isMergeTable(subt)) {
      68          26 :                 if ((t = merge_table_prune_and_unionize(v, t, info)) == NULL)
      69             :                         return NULL;
      70             :         }
      71             : 
      72         549 :         if (info) {
      73         148 :                 t = rel_select(v->sql->sa, t, NULL);
      74         148 :                 t->exps = exps_copy(v->sql, info->sel->exps);
      75         148 :                 set_processed(t);
      76         148 :                 set_processed(t);
      77             :         }
      78             :         return t;
      79             : }
      80             : 
      81             : #if 0
      82             : static sql_rel *
      83             : rel_unionize_mt_tables_balanced(visitor *v, sql_rel* mt, list* tables, merge_table_prune_info *info)
      84             : {
      85             :         /* This function is creating the union tree in the tables list calling
      86             :          * itself recursively until the tables list has a single entry (the union tree)
      87             :          */
      88             : 
      89             :         /* base case */
      90             :         if (tables->cnt == 1) // XXX: or/and h->next == NULL
      91             :                 return tables->h->data;
      92             :         /* merge (via union) every *two* consecutive nodes of the list */
      93             :         for (node *n = tables->h; n && n->next; n = n->next->next) {
      94             :                 /* first (left) node */
      95             :                 sql_rel *tl = rel_wrap_select_around_mt_child(v, n->data, info);
      96             :                 /* second (right) node */
      97             :                 sql_rel *tr = rel_wrap_select_around_mt_child(v, n->next->data, info);
      98             :                 /* create the union */
      99             :                 sql_rel *tu = rel_setop(v->sql->sa, tl, tr, op_union);
     100             :                 rel_setop_set_exps(v->sql, tu, rel_projections(v->sql, mt, NULL, 1, 1), true);
     101             :                 set_processed(tu);
     102             :                 /* replace the two nodes with the new relation */
     103             :                 list_append_before(tables, n, tu);
     104             :                 list_remove_node(tables, NULL, n);
     105             :                 list_remove_node(tables, NULL, n->next);
     106             :                 // TODO: do i need to rebuild the hash of the list?
     107             :         }
     108             :         return rel_unionize_mt_tables_balanced(v, mt, tables, info);
     109             : }
     110             : #endif
     111             : 
     112             : static sql_rel *
     113         167 : rel_unionize_mt_tables_munion(visitor *v, sql_rel* mt, list* tables, merge_table_prune_info *info)
     114             : {
     115             :         /* create the list of all the operand rels */
     116         167 :         list *rels = sa_list(v->sql->sa);
     117         547 :         for (node *n = tables->h; n; n = n->next) {
     118         380 :                 sql_rel *r = rel_wrap_select_around_mt_child(v, n->data, info);
     119         380 :                 append(rels, r);
     120             :         }
     121             : 
     122             :         /* create the munion */
     123         167 :         sql_rel *mu = rel_setop_n_ary(v->sql->sa, rels, op_munion);
     124         167 :         rel_setop_n_ary_set_exps(v->sql, mu, rel_projections(v->sql, mt, NULL, 1, 1), true);
     125         167 :         set_processed(mu);
     126             : 
     127         167 :         return mu;
     128             : }
     129             : 
     130             : static sql_rel *
     131         356 : merge_table_prune_and_unionize(visitor *v, sql_rel *mt_rel, merge_table_prune_info *info)
     132             : {
     133         356 :         if (mvc_highwater(v->sql))
     134           0 :                 return sql_error(v->sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     135             : 
     136         356 :         sql_rel *nrel = NULL;
     137         356 :         sql_table *mt = (sql_table*) mt_rel->l;
     138         356 :         const char *mtalias = exp_relname(mt_rel->exps->h->data);
     139         356 :         list *tables = sa_list(v->sql->sa);
     140             : 
     141        1034 :         for (node *nt = mt->members->h; nt; nt = nt->next) {
     142         680 :                 sql_part *pd = nt->data;
     143         680 :                 sql_table *pt = find_sql_table_id(v->sql->session->tr, mt->s, pd->member);
     144         680 :                 sqlstore *store = v->sql->session->tr->store;
     145         680 :                 int skip = 0;
     146             : 
     147             :                 /* At the moment we throw an error in the optimizer, but later this rewriter should move out from the optimizers */
     148         680 :                 if ((isMergeTable(pt) || isReplicaTable(pt)) && list_empty(pt->members))
     149           2 :                         return sql_error(v->sql, 02, SQLSTATE(42000) "%s '%s'.'%s' should have at least one table associated",
     150           2 :                                                         TABLE_TYPE_DESCRIPTION(pt->type, pt->properties), pt->s->base.name, pt->base.name);
     151             :                 /* Do not include empty partitions */
     152         678 :                 if (isTable(pt) && pt->access == TABLE_READONLY && !store->storage_api.count_col(v->sql->session->tr, ol_first_node(pt->columns)->data, 10)) /* count active rows only */
     153           1 :                         continue;
     154             : 
     155        1739 :                 for (node *n = mt_rel->exps->h; n && !skip; n = n->next) { /* for each column of the child table */
     156        1062 :                         sql_exp *e = n->data;
     157        1062 :                         int i = 0;
     158        1062 :                         bool first_attempt = true;
     159        1062 :                         atom *cmin = NULL, *cmax = NULL, *rmin = NULL, *rmax = NULL;
     160        1062 :                         list *inlist = NULL;
     161        1062 :                         const char *cname = e->r;
     162        1062 :                         sql_column *mt_col = NULL, *col = NULL;
     163             : 
     164        1062 :                         if (cname[0] == '%') /* Ignore TID and indexes here */
     165          55 :                                 continue;
     166             : 
     167        1007 :                         mt_col = ol_find_name(mt->columns, cname)->data;
     168        1007 :                         col = ol_fetch(pt->columns, mt_col->colnr);
     169        1007 :                         assert(e && e->type == e_column && col);
     170             : 
     171        1007 :                         if (isTable(pt) && info && !list_empty(info->cols) && ATOMlinear(exp_subtype(e)->type->localtype)) {
     172         747 :                                 for (node *nn = info->cols->h ; nn && !skip; nn = nn->next) { /* test if it passes all predicates around it */
     173         388 :                                         if (nn->data == e) {
     174         262 :                                                 range_limit *next = list_fetch(info->ranges, i);
     175         262 :                                                 atom *lval = next->lval, *hval = next->hval;
     176         262 :                                                 list *values = next->values;
     177             : 
     178             :                                                 /* I don't handle cmp_in or cmp_notin cases with anti or null semantics yet */
     179         262 :                                                 if (next->flag == cmp_in && (next->anti || next->semantics))
     180           0 :                                                         continue;
     181             : 
     182         262 :                                                 assert(col && (lval || values));
     183         262 :                                                 if (!skip && pt->access == TABLE_READONLY) {
     184             :                                                         /* check if the part falls within the bounds of the select expression else skip this (keep at least on part-table) */
     185          87 :                                                         if (!cmin && !cmax && first_attempt) {
     186          87 :                                                                 void *min = NULL, *max = NULL;
     187          87 :                                                                 if (sql_trans_ranges(v->sql->session->tr, col, &min, &max) && min && max) {
     188          84 :                                                                         cmin = atom_general_ptr(v->sql->sa, &col->type, min);
     189          84 :                                                                         cmax = atom_general_ptr(v->sql->sa, &col->type, max);
     190             :                                                                 }
     191          87 :                                                                 first_attempt = false; /* no more attempts to read from storage */
     192             :                                                         }
     193             : 
     194          87 :                                                         if (cmin && cmax) {
     195          84 :                                                                 if (lval) {
     196          80 :                                                                         if (!next->semantics && ((lval && lval->isnull) || (hval && hval->isnull))) {
     197             :                                                                                 skip = 1; /* NULL values don't match, skip them */
     198          80 :                                                                         } else if (!next->semantics) {
     199          80 :                                                                                 if (next->flag == cmp_equal) {
     200          20 :                                                                                         skip |= next->anti ? exp_range_overlap(cmin, cmax, lval, hval, false, false) != 0 :
     201          10 :                                                                                                                                         exp_range_overlap(cmin, cmax, lval, hval, false, false) == 0;
     202          70 :                                                                                 } else if (hval != lval) { /* range case */
     203          70 :                                                                                         comp_type lower = range2lcompare(next->flag), higher = range2rcompare(next->flag);
     204         140 :                                                                                         skip |= next->anti ? exp_range_overlap(cmin, cmax, lval, hval, higher == cmp_lt, lower == cmp_gt) != 0 :
     205          70 :                                                                                                                                         exp_range_overlap(cmin, cmax, lval, hval, higher == cmp_lt, lower == cmp_gt) == 0;
     206             :                                                                                 } else {
     207           0 :                                                                                         switch (next->flag) {
     208           0 :                                                                                                 case cmp_gt:
     209           0 :                                                                                                         skip |= next->anti ? VALcmp(&(lval->data), &(cmax->data)) < 0 : VALcmp(&(lval->data), &(cmax->data)) >= 0;
     210           0 :                                                                                                         break;
     211           0 :                                                                                                 case cmp_gte:
     212           0 :                                                                                                         skip |= next->anti ? VALcmp(&(lval->data), &(cmax->data)) <= 0 : VALcmp(&(lval->data), &(cmax->data)) > 0;
     213           0 :                                                                                                         break;
     214           0 :                                                                                                 case cmp_lt:
     215           0 :                                                                                                         skip |= next->anti ? VALcmp(&(lval->data), &(cmax->data)) < 0 : VALcmp(&(cmin->data), &(lval->data)) >= 0;
     216           0 :                                                                                                         break;
     217           0 :                                                                                                 case cmp_lte:
     218           0 :                                                                                                         skip |= next->anti ? VALcmp(&(lval->data), &(cmax->data)) <= 0 : VALcmp(&(cmin->data), &(lval->data)) > 0;
     219           0 :                                                                                                         break;
     220             :                                                                                                 default:
     221             :                                                                                                         break;
     222             :                                                                                         }
     223             :                                                                                 }
     224             :                                                                         }
     225           4 :                                                                 } else if (next->flag == cmp_in) {
     226           4 :                                                                         int nskip = 1;
     227          10 :                                                                         for (node *m = values->h; m && nskip; m = m->next) {
     228           6 :                                                                                 atom *a = m->data;
     229             : 
     230           6 :                                                                                 if (a->isnull)
     231           0 :                                                                                         continue;
     232           6 :                                                                                 nskip &= exp_range_overlap(cmin, cmax, a, a, false, false) == 0;
     233             :                                                                         }
     234           4 :                                                                         skip |= nskip;
     235             :                                                                 }
     236             :                                                         }
     237             :                                                 }
     238         262 :                                                 if (!skip && isPartitionedByColumnTable(mt) && strcmp(mt->part.pcol->base.name, col->base.name) == 0) {
     239         158 :                                                         if (!next->semantics && ((lval && lval->isnull) || (hval && hval->isnull))) {
     240             :                                                                 skip = 1; /* NULL values don't match, skip them */
     241         158 :                                                         } else if (next->semantics) {
     242             :                                                                 /* TODO NOT NULL prunning for partitions that just hold NULL values is still missing */
     243          20 :                                                                 skip |= next->flag == cmp_equal && !next->anti && lval && lval->isnull ? pd->with_nills == 0 : 0; /* *= NULL case */
     244             :                                                         } else {
     245         143 :                                                                 if (isRangePartitionTable(mt)) {
     246         109 :                                                                         if (!rmin || !rmax) { /* initialize lazily */
     247         109 :                                                                                 rmin = atom_general_ptr(v->sql->sa, &col->type, pd->part.range.minvalue);
     248         109 :                                                                                 rmax = atom_general_ptr(v->sql->sa, &col->type, pd->part.range.maxvalue);
     249             :                                                                         }
     250             : 
     251             :                                                                         /* Prune range partitioned tables */
     252         109 :                                                                         if (rmin->isnull && rmax->isnull) {
     253           0 :                                                                                 if (pd->with_nills == 1) /* the partition just holds null values, skip it */
     254         143 :                                                                                         skip = 1;
     255             :                                                                                 /* otherwise it holds all values in the range, cannot be pruned */
     256         109 :                                                                         } else if (rmin->isnull) { /* MINVALUE to limit */
     257           4 :                                                                                 if (lval) {
     258           4 :                                                                                         if (hval != lval) { /* range case */
     259             :                                                                                                 /* There's need to call range2lcompare, because the partition's upper limit is always exclusive */
     260           2 :                                                                                                 skip |= next->anti ? VALcmp(&(lval->data), &(rmax->data)) < 0 : VALcmp(&(lval->data), &(rmax->data)) >= 0;
     261             :                                                                                         } else {
     262           2 :                                                                                                 switch (next->flag) { /* upper limit always exclusive */
     263           2 :                                                                                                         case cmp_equal:
     264             :                                                                                                         case cmp_gt:
     265             :                                                                                                         case cmp_gte:
     266           2 :                                                                                                                 skip |= next->anti ? VALcmp(&(lval->data), &(rmax->data)) < 0 : VALcmp(&(lval->data), &(rmax->data)) >= 0;
     267           2 :                                                                                                                 break;
     268             :                                                                                                         default:
     269             :                                                                                                                 break;
     270             :                                                                                                 }
     271             :                                                                                         }
     272           0 :                                                                                 } else if (next->flag == cmp_in) {
     273           0 :                                                                                         int nskip = 1;
     274           0 :                                                                                         for (node *m = values->h; m && nskip; m = m->next) {
     275           0 :                                                                                                 atom *a = m->data;
     276             : 
     277           0 :                                                                                                 if (a->isnull)
     278           0 :                                                                                                         continue;
     279           0 :                                                                                                 nskip &= VALcmp(&(a->data), &(rmax->data)) >= 0;
     280             :                                                                                         }
     281           0 :                                                                                         skip |= nskip;
     282             :                                                                                 }
     283         105 :                                                                         } else if (rmax->isnull) { /* limit to MAXVALUE */
     284          29 :                                                                                 if (lval) {
     285          26 :                                                                                         if (hval != lval) { /* range case */
     286          17 :                                                                                                 comp_type higher = range2rcompare(next->flag);
     287          17 :                                                                                                 if (higher == cmp_lt) {
     288           6 :                                                                                                         skip |= next->anti ? VALcmp(&(rmin->data), &(hval->data)) < 0 : VALcmp(&(rmin->data), &(hval->data)) >= 0;
     289          11 :                                                                                                 } else if (higher == cmp_lte) {
     290          11 :                                                                                                         skip |= next->anti ? VALcmp(&(rmin->data), &(hval->data)) <= 0 : VALcmp(&(rmin->data), &(hval->data)) > 0;
     291             :                                                                                                 } else {
     292           0 :                                                                                                         assert(0);
     293             :                                                                                                 }
     294             :                                                                                         } else {
     295           9 :                                                                                                 switch (next->flag) {
     296           2 :                                                                                                         case cmp_lt:
     297           2 :                                                                                                                 skip |= next->anti ? VALcmp(&(rmin->data), &(hval->data)) < 0 : VALcmp(&(rmin->data), &(hval->data)) >= 0;
     298           2 :                                                                                                                 break;
     299           6 :                                                                                                         case cmp_equal:
     300             :                                                                                                         case cmp_lte:
     301           6 :                                                                                                                 skip |= next->anti ? VALcmp(&(rmin->data), &(hval->data)) <= 0 : VALcmp(&(rmin->data), &(hval->data)) > 0;
     302           6 :                                                                                                                 break;
     303             :                                                                                                         default:
     304             :                                                                                                                 break;
     305             :                                                                                                 }
     306             :                                                                                         }
     307           3 :                                                                                 } else if (next->flag == cmp_in) {
     308           3 :                                                                                         int nskip = 1;
     309          10 :                                                                                         for (node *m = values->h; m && nskip; m = m->next) {
     310           7 :                                                                                                 atom *a = m->data;
     311             : 
     312           7 :                                                                                                 if (a->isnull)
     313           0 :                                                                                                         continue;
     314           7 :                                                                                                 nskip &= VALcmp(&(rmin->data), &(a->data)) > 0;
     315             :                                                                                         }
     316           3 :                                                                                         skip |= nskip;
     317             :                                                                                 }
     318             :                                                                         } else { /* limit1 to limit2 (general case), limit2 is exclusive */
     319          76 :                                                                                 bool max_differ_min = ATOMcmp(col->type.type->localtype, &rmin->data.val, &rmax->data.val) != 0;
     320             : 
     321          76 :                                                                                 if (lval) {
     322          68 :                                                                                         if (next->flag == cmp_equal) {
     323          24 :                                                                                                 skip |= next->anti ? exp_range_overlap(rmin, rmax, lval, hval, false, max_differ_min) != 0 :
     324          12 :                                                                                                                                                 exp_range_overlap(rmin, rmax, lval, hval, false, max_differ_min) == 0;
     325          56 :                                                                                         } else if (hval != lval) { /* For the between case */
     326          40 :                                                                                                 comp_type higher = range2rcompare(next->flag);
     327          70 :                                                                                                 skip |= next->anti ? exp_range_overlap(rmin, rmax, lval, hval, higher == cmp_lt, max_differ_min) != 0 :
     328          30 :                                                                                                                                                 exp_range_overlap(rmin, rmax, lval, hval, higher == cmp_lt, max_differ_min) == 0;
     329             :                                                                                         } else {
     330          16 :                                                                                                 switch (next->flag) {
     331           4 :                                                                                                         case cmp_gt:
     332           4 :                                                                                                                 skip |= next->anti ? VALcmp(&(lval->data), &(rmax->data)) < 0 : VALcmp(&(lval->data), &(rmax->data)) >= 0;
     333           4 :                                                                                                                 break;
     334           2 :                                                                                                         case cmp_gte:
     335           2 :                                                                                                                 if (max_differ_min)
     336           2 :                                                                                                                         skip |= next->anti ? VALcmp(&(lval->data), &(rmax->data)) < 0 : VALcmp(&(lval->data), &(rmax->data)) >= 0;
     337             :                                                                                                                 else
     338           0 :                                                                                                                         skip |= next->anti ? VALcmp(&(lval->data), &(rmax->data)) <= 0 : VALcmp(&(lval->data), &(rmax->data)) > 0;
     339             :                                                                                                                 break;
     340           2 :                                                                                                         case cmp_lt:
     341           2 :                                                                                                                 skip |= next->anti ? VALcmp(&(rmin->data), &(lval->data)) < 0 : VALcmp(&(rmin->data), &(lval->data)) >= 0;
     342           2 :                                                                                                                 break;
     343           8 :                                                                                                         case cmp_lte:
     344           8 :                                                                                                                 skip |= next->anti ? VALcmp(&(rmin->data), &(lval->data)) <= 0 : VALcmp(&(rmin->data), &(lval->data)) > 0;
     345           8 :                                                                                                                 break;
     346             :                                                                                                         default:
     347             :                                                                                                                 break;
     348             :                                                                                                 }
     349             :                                                                                         }
     350           8 :                                                                                 } else if (next->flag == cmp_in) {
     351           8 :                                                                                         int nskip = 1;
     352          20 :                                                                                         for (node *m = values->h; m && nskip; m = m->next) {
     353          12 :                                                                                                 atom *a = m->data;
     354             : 
     355          12 :                                                                                                 if (a->isnull)
     356           0 :                                                                                                         continue;
     357          12 :                                                                                                 nskip &= exp_range_overlap(rmin, rmax, a, a, false, max_differ_min) == 0;
     358             :                                                                                         }
     359           8 :                                                                                         skip |= nskip;
     360             :                                                                                 }
     361             :                                                                         }
     362             :                                                                 }
     363             : 
     364         143 :                                                                 if (isListPartitionTable(mt) && (next->flag == cmp_equal || next->flag == cmp_in) && !next->anti) {
     365             :                                                                         /* if we find a value equal to one of the predicates, we don't prune */
     366             :                                                                         /* if the partition just holds null values, it will be skipped */
     367          33 :                                                                         if (!inlist) { /* initialize lazily */
     368          32 :                                                                                 inlist = sa_list(v->sql->sa);
     369         106 :                                                                                 for (node *m = pd->part.values->h; m; m = m->next) {
     370          74 :                                                                                         sql_part_value *spv = (sql_part_value*) m->data;
     371          74 :                                                                                         atom *pa = atom_general_ptr(v->sql->sa, &col->type, spv->value);
     372             : 
     373          74 :                                                                                         list_append(inlist, pa);
     374             :                                                                                 }
     375             :                                                                         }
     376             : 
     377          33 :                                                                         if (next->flag == cmp_equal) {
     378          12 :                                                                                 int nskip = 1;
     379          38 :                                                                                 for (node *m = inlist->h; m && nskip; m = m->next) {
     380          26 :                                                                                         atom *pa = m->data;
     381          26 :                                                                                         assert(!pa->isnull);
     382          26 :                                                                                         nskip &= VALcmp(&(pa->data), &(lval->data)) != 0;
     383             :                                                                                 }
     384          12 :                                                                                 skip |= nskip;
     385          21 :                                                                         } else if (next->flag == cmp_in) {
     386          50 :                                                                                 for (node *o = values->h; o && !skip; o = o->next) {
     387          29 :                                                                                         atom *a = o->data;
     388          29 :                                                                                         int nskip = 1;
     389             : 
     390          29 :                                                                                         if (a->isnull)
     391           0 :                                                                                                 continue;
     392          87 :                                                                                         for (node *m = inlist->h; m && nskip; m = m->next) {
     393          58 :                                                                                                 atom *pa = m->data;
     394          58 :                                                                                                 assert(!pa->isnull);
     395          58 :                                                                                                 nskip &= VALcmp(&(pa->data), &(a->data)) != 0;
     396             :                                                                                         }
     397          29 :                                                                                         skip |= nskip;
     398             :                                                                                 }
     399             :                                                                         }
     400             :                                                                 }
     401             :                                                         }
     402             :                                                 }
     403             :                                         }
     404         388 :                                         i++;
     405             :                                 }
     406             :                         }
     407             :                 }
     408         677 :                 if (!skip)
     409         549 :                         append(tables, rel_rename_part(v->sql, rel_basetable(v->sql, pt, pt->base.name), mt_rel, mtalias));
     410             :         }
     411         354 :         if (list_empty(tables)) { /* No table passed the predicates, generate dummy relation */
     412          18 :                 list *converted = sa_list(v->sql->sa);
     413          18 :                 nrel = rel_project_exp(v->sql, exp_atom_bool(v->sql->sa, 1));
     414          18 :                 nrel = rel_select(v->sql->sa, nrel, exp_atom_bool(v->sql->sa, 0));
     415          18 :                 set_processed(nrel);
     416             : 
     417          41 :                 for (node *n = mt_rel->exps->h ; n ; n = n->next) {
     418          23 :                         sql_exp *e = n->data, *a = exp_atom(v->sql->sa, atom_general(v->sql->sa, exp_subtype(e), NULL, 0));
     419          23 :                         exp_prop_alias(v->sql->sa, a, e);
     420          23 :                         list_append(converted, a);
     421             :                 }
     422          18 :                 nrel = rel_project(v->sql->sa, nrel, converted);
     423             :         } else { /* Unionize children tables */
     424             : 
     425         336 :                 if (mvc_debug_on(v->sql, 16)) {
     426             :                         /* In case of a single table there in nothing to unionize */
     427           0 :                         if (tables->cnt == 1) {
     428           0 :                                 nrel = rel_wrap_select_around_mt_child(v, tables->h->data, info);
     429             :                         } else {
     430             :                                 //nrel = rel_unionize_mt_tables_balanced(v, mt_rel, tables, info);
     431           0 :                                 nrel = rel_setop_n_ary(v->sql->sa, tables, op_munion);
     432             :                         }
     433         336 :                 } else if (mvc_debug_on(v->sql, 32)) {
     434           0 :                         for (node *n = tables->h; n ; n = n->next) {
     435           0 :                                 sql_rel *next = n->data;
     436           0 :                                 sql_table *subt = (sql_table *) next->l;
     437             : 
     438           0 :                                 if (isMergeTable(subt)) { /* apply select predicate recursively for nested merge tables */
     439           0 :                                         if (!(next = merge_table_prune_and_unionize(v, next, info)))
     440             :                                                 return NULL;
     441           0 :                                 } else if (info) { /* propagate select under union */
     442           0 :                                         next = rel_select(v->sql->sa, next, NULL);
     443           0 :                                         next->exps = exps_copy(v->sql, info->sel->exps);
     444           0 :                                         set_processed(next);
     445             :                                 }
     446             : 
     447           0 :                                 if (nrel) {
     448           0 :                                         nrel = rel_setop(v->sql->sa, nrel, next, op_union);
     449           0 :                                         rel_setop_set_exps(v->sql, nrel, rel_projections(v->sql, mt_rel, NULL, 1, 1), true);
     450           0 :                                         set_processed(nrel);
     451             :                                 } else {
     452             :                                         nrel = next;
     453             :                                 }
     454             :                         }
     455             :                 } else {
     456         336 :                         if (tables->cnt == 1) {
     457         169 :                                 nrel = rel_wrap_select_around_mt_child(v, tables->h->data, info);
     458             :                         } else {
     459         167 :                                 nrel = rel_unionize_mt_tables_munion(v, mt_rel, tables, info);
     460             :                         }
     461             :                 }
     462             :         }
     463             :         return nrel;
     464             : }
     465             : 
     466             : /* rewrite merge tables into union of base tables */
     467             : static sql_rel *
     468       13384 : rel_merge_table_rewrite_(visitor *v, sql_rel *rel)
     469             : {
     470       13384 :         if (is_modify(rel->op)) {
     471        1113 :                 sql_query *query = query_create(v->sql);
     472        1113 :                 return rel_propagate(query, rel, &v->changes);
     473             :         } else {
     474       12271 :                 sql_rel *bt = rel, *sel = NULL, *nrel = NULL;
     475             : 
     476       12271 :                 if (is_select(rel->op)) {
     477        1937 :                         sel = rel;
     478        1937 :                         bt = rel->l;
     479             :                 }
     480       12271 :                 if (is_basetable(bt->op) && rel_base_table(bt) && isMergeTable((sql_table*)bt->l)) {
     481         620 :                         sql_table *mt = rel_base_table(bt);
     482         620 :                         merge_table_prune_info *info = NULL;
     483             : 
     484         620 :                         if (list_empty(mt->members)) /* in DDL statement cases skip if mergetable is empty */
     485             :                                 return rel;
     486         330 :                         if (sel && !list_empty(sel->exps)) { /* prepare prunning information once */
     487         105 :                                 info = SA_NEW(v->sql->sa, merge_table_prune_info);
     488         210 :                                 *info = (merge_table_prune_info) {
     489         105 :                                         .cols = sa_list(v->sql->sa),
     490         105 :                                         .ranges = sa_list(v->sql->sa),
     491             :                                         .sel = sel
     492             :                                 };
     493         220 :                                 for (node *n = sel->exps->h; n; n = n->next) {
     494         115 :                                         sql_exp *e = n->data, *c = e->l;
     495         115 :                                         int flag = e->flag;
     496             : 
     497         115 :                                         if (e->type != e_cmp || (!is_theta_exp(flag) && flag != cmp_in) || is_symmetric(e) || !(c = rel_find_exp(rel, c)))
     498           7 :                                                 continue;
     499             : 
     500         108 :                                         if (flag == cmp_gt || flag == cmp_gte || flag == cmp_lte || flag == cmp_lt || flag == cmp_equal) {
     501          93 :                                                 sql_exp *l = e->r, *h = e->f;
     502          93 :                                                 atom *lval = exp_flatten(v->sql, v->value_based_opt, l);
     503          93 :                                                 atom *hval = h ? exp_flatten(v->sql, v->value_based_opt, h) : lval;
     504             : 
     505          93 :                                                 if (lval && hval) {
     506          91 :                                                         range_limit *next = SA_NEW(v->sql->sa, range_limit);
     507             : 
     508          91 :                                                         *next = (range_limit) {
     509             :                                                                 .lval = lval,
     510             :                                                                 .hval = hval,
     511             :                                                                 .flag = flag,
     512          91 :                                                                 .anti = is_anti(e),
     513          91 :                                                                 .semantics = is_semantics(e),
     514             :                                                         };
     515          91 :                                                         list_append(info->cols, c);
     516          91 :                                                         list_append(info->ranges, next);
     517             :                                                 }
     518             :                                         }
     519         108 :                                         if (flag == cmp_in) { /* handle in lists */
     520          15 :                                                 list *vals = e->r, *vlist = sa_list(v->sql->sa);
     521             : 
     522          15 :                                                 node *m = NULL;
     523          48 :                                                 for (m = vals->h; m; m = m->next) {
     524          33 :                                                         sql_exp *l = m->data;
     525          33 :                                                         atom *lval = exp_flatten(v->sql, v->value_based_opt, l);
     526             : 
     527          33 :                                                         if (!lval)
     528             :                                                                 break;
     529          33 :                                                         list_append(vlist, lval);
     530             :                                                 }
     531          15 :                                                 if (!m) {
     532          15 :                                                         range_limit *next = SA_NEW(v->sql->sa, range_limit);
     533             : 
     534          15 :                                                         *next = (range_limit) {
     535             :                                                                 .values = vlist, /* mark high as value list */
     536             :                                                                 .flag = flag,
     537          15 :                                                                 .anti = is_anti(e),
     538          15 :                                                                 .semantics = is_semantics(e),
     539             :                                                         };
     540          15 :                                                         list_append(info->cols, c);
     541          15 :                                                         list_append(info->ranges, next);
     542             :                                                 }
     543             :                                         }
     544             :                                 }
     545             :                         }
     546         330 :                         if (!(nrel = merge_table_prune_and_unionize(v, bt, info)))
     547             :                                 return NULL;
     548             :                         /* Always do relation inplace. If the mt relation has more than 1 reference, this is required */
     549         328 :                         if (is_union(nrel->op)) {
     550           0 :                                 rel = rel_inplace_setop(v->sql, rel, nrel->l, nrel->r, op_union, nrel->exps);
     551             :                         } else if (is_munion(nrel->op)) {
     552         150 :                                 rel = rel_inplace_setop_n_ary(v->sql, rel, nrel->l, op_munion, nrel->exps);
     553             :                         } else if (is_select(nrel->op)) {
     554          49 :                                 rel = rel_inplace_select(rel, nrel->l, nrel->exps);
     555             :                         } else if (is_basetable(nrel->op)) {
     556         111 :                                 rel = rel_inplace_basetable(rel, nrel);
     557             :                         } else {
     558           0 :                                 assert(is_simple_project(nrel->op));
     559          18 :                                 rel = rel_inplace_project(v->sql->sa, rel, nrel->l, nrel->exps);
     560          18 :                                 rel->card = exps_card(nrel->exps);
     561             :                         }
     562             :                         /* make sure that we do NOT destroy the subrels */
     563         328 :                         nrel->l = nrel->r = NULL;
     564         328 :                         rel_destroy(nrel);
     565         328 :                         v->changes++;
     566             :                 }
     567             :         }
     568             :         return rel;
     569             : }
     570             : 
     571             : static sql_rel *
     572        1994 : rel_merge_table_rewrite(visitor *v, global_props *gp, sql_rel *rel)
     573             : {
     574        1994 :         (void) gp;
     575        1994 :         return rel_visitor_topdown(v, rel, &rel_merge_table_rewrite_);
     576             : }
     577             : 
     578             : run_optimizer
     579      629382 : bind_merge_table_rewrite(visitor *v, global_props *gp)
     580             : {
     581      629382 :         (void) v;
     582      629382 :         return gp->needs_mergetable_rewrite ? rel_merge_table_rewrite : NULL;
     583             : }
     584             : 
     585             : /* these optimizers/rewriters run in a cycle loop */
     586             : const sql_optimizer pre_sql_optimizers[] = {
     587             :         { 0, "split_select", bind_split_select},
     588             :         { 1, "push_project_down", bind_push_project_down},
     589             :         { 2, "merge_projects", bind_merge_projects},
     590             :         { 3, "push_project_up", bind_push_project_up},
     591             :         { 4, "split_project", bind_split_project},
     592             :         { 5, "remove_redundant_join", bind_remove_redundant_join},
     593             :         { 6, "simplify_math", bind_simplify_math},
     594             :         { 7, "optimize_exps", bind_optimize_exps},
     595             :         { 8, "optimize_select_and_joins_bottomup", bind_optimize_select_and_joins_bottomup},
     596             :         { 9, "project_reduce_casts", bind_project_reduce_casts},
     597             :         {10, "optimize_unions_bottomup", bind_optimize_unions_bottomup},
     598             :         {11, "optimize_projections", bind_optimize_projections},
     599             :         {12, "optimize_joins", bind_optimize_joins},
     600             :         {13, "join_order", bind_join_order},
     601             :         {14, "optimize_semi_and_anti", bind_optimize_semi_and_anti},
     602             :         {15, "optimize_select_and_joins_topdown", bind_optimize_select_and_joins_topdown},
     603             :         {16, "optimize_unions_topdown", bind_optimize_unions_topdown},
     604             :         {17, "dce", bind_dce},
     605             :         {18, "push_func_and_select_down", bind_push_func_and_select_down},
     606             :         {19, "push_topn_and_sample_down", bind_push_topn_and_sample_down},
     607             :         {20, "distinct_project2groupby", bind_distinct_project2groupby},
     608             :         {21, "merge_table_rewrite", bind_merge_table_rewrite},
     609             :         { 0, NULL, NULL}
     610             : };
     611             : 
     612             : /* these optimizers/rewriters only run once after the cycle loop */
     613             : const sql_optimizer post_sql_optimizers[] = {
     614             :         /* Merge table rewrites may introduce remote or replica tables */
     615             :         /* At the moment, make sure the remote table rewriters always run after the merge table one */
     616             :         {23, "rewrite_remote", bind_rewrite_remote},
     617             :         {24, "rewrite_replica", bind_rewrite_replica},
     618             :         {25, "remote_func", bind_remote_func},
     619             :         {26, "get_statistics", bind_get_statistics}, /* gather statistics */
     620             :         {27, "join_order2", bind_join_order2}, /* run join order one more time with statistics */
     621             :         {28, "final_optimization_loop", bind_final_optimization_loop}, /* run select and group by order with statistics gathered  */
     622             :         { 0, NULL, NULL}
     623             :         /* If an optimizer is going to be added, don't forget to update NSQLREWRITERS macro */
     624             : };
     625             : 
     626             : 
     627             : /* for trivial queries don't run optimizers */
     628             : static int
     629      798652 : calculate_opt_level(mvc *sql, sql_rel *rel)
     630             : {
     631      989612 :         if (rel->card <= CARD_ATOM) {
     632      774925 :                 if (is_insert(rel->op))
     633      109726 :                         return rel->r ? calculate_opt_level(sql, rel->r) : 0;
     634      665199 :                 if (is_simple_project(rel->op))
     635      250278 :                         return rel->l ? calculate_opt_level(sql, rel->l) : 0;
     636             :         }
     637             :         return 1;
     638             : }
     639             : 
     640             : static inline sql_rel *
     641     1351107 : run_optimizer_set(visitor *v, sql_optimizer_run *runs, sql_rel *rel, global_props *gp, const sql_optimizer *set)
     642             : {
     643             :         /* if 'runs' is set, it means profiling is intended */
     644    19527833 :         for (int i = 0 ; set[i].name ; i++) {
     645    18176520 :                 run_optimizer opt = NULL;
     646             : 
     647    18176520 :                 if ((opt = set[i].bind_optimizer(v, gp))) {
     648     3596133 :                         if (runs) {
     649           0 :                                 sql_optimizer_run *run = &(runs[set[i].index]);
     650           0 :                                 run->name = set[i].name;
     651           0 :                                 int changes = v->changes;
     652           0 :                                 lng clk = GDKusec();
     653           0 :                                 rel = opt(v, gp, rel);
     654           0 :                                 run->time += (GDKusec() - clk);
     655           0 :                                 run->nchanges += (v->changes - changes);
     656             :                         } else {
     657     3596133 :                                 rel = opt(v, gp, rel);
     658             :                         }
     659             :                 }
     660             :         }
     661     1351313 :         return rel;
     662             : }
     663             : 
     664             : /* 'profile' means to benchmark each individual optimizer run */
     665             : /* 'instantiate' means to rewrite logical tables: (merge, remote, replica tables) */
     666             : static sql_rel *
     667      721052 : rel_optimizer_one(mvc *sql, sql_rel *rel, int profile, int instantiate, int value_based_opt, int storage_based_opt)
     668             : {
     669     1442104 :         global_props gp = (global_props) {.cnt = {0}, .instantiate = (uint8_t)instantiate, .opt_cycle = 0,
     670      721052 :                                                                           .has_special_modify = rel && is_modify(rel->op) && rel->flag&UPD_COMP};
     671      721052 :         visitor v = { .sql = sql, .value_based_opt = value_based_opt, .storage_based_opt = storage_based_opt, .changes = 1, .data = &gp };
     672             : 
     673      721052 :         sql->runs = !(ATOMIC_GET(&GDKdebug) & TESTINGMASK) && profile ? sa_zalloc(sql->sa, NSQLREWRITERS * sizeof(sql_optimizer_run)) : NULL;
     674     1350732 :         for ( ;rel && gp.opt_cycle < 20 && v.changes; gp.opt_cycle++) {
     675      798050 :                 v.changes = 0;
     676      798050 :                 gp = (global_props) {.cnt = {0}, .instantiate = (uint8_t)instantiate, .opt_cycle = gp.opt_cycle, .has_special_modify = gp.has_special_modify};
     677      798050 :                 rel = rel_visitor_topdown(&v, rel, &rel_properties); /* collect relational tree properties */
     678      798557 :                 gp.opt_level = calculate_opt_level(sql, rel);
     679      798557 :                 if (gp.opt_level == 0 && !gp.needs_mergetable_rewrite)
     680             :                         break;
     681      629742 :                 sql->recursive = gp.recursive;
     682      629742 :                 rel = run_optimizer_set(&v, sql->runs, rel, &gp, pre_sql_optimizers);
     683             :         }
     684             : #ifndef NDEBUG
     685      721497 :         assert(gp.opt_cycle < 20);
     686             : #endif
     687             : 
     688             :         /* these optimizers run statistics gathered by the last optimization cycle */
     689      721497 :         rel = run_optimizer_set(&v, sql->runs, rel, &gp, post_sql_optimizers);
     690      721593 :         return rel;
     691             : }
     692             : 
     693             : static sql_exp *
     694      300557 : exp_optimize_one(visitor *v, sql_rel *rel, sql_exp *e, int depth )
     695             : {
     696      300557 :        (void)rel;
     697      300557 :        (void)depth;
     698      300557 :        if (e->type == e_psm && e->flag == PSM_REL && e->l) {
     699       19584 :                e->l = rel_optimizer_one(v->sql, e->l, 0, v->changes, v->value_based_opt, v->storage_based_opt);
     700             :        }
     701      300557 :        return e;
     702             : }
     703             : 
     704             : sql_rel *
     705      724159 : rel_optimizer(mvc *sql, sql_rel *rel, int profile, int instantiate, int value_based_opt, int storage_based_opt)
     706             : {
     707      724159 :         if (rel && rel->op == op_ddl && rel->flag == ddl_psm) {
     708       22604 :                 if (!list_empty(rel->exps)) {
     709       22513 :                         bool changed = 0;
     710       22513 :                         visitor v = { .sql = sql, .value_based_opt = value_based_opt, .storage_based_opt = storage_based_opt, .changes = instantiate };
     711       62772 :                         for(node *n = rel->exps->h; n; n = n->next) {
     712       40259 :                                 sql_exp *e = n->data;
     713       40259 :                                 exp_visitor(&v, rel, e, 1, exp_optimize_one, true, true, true, &changed);
     714             :                         }
     715             :                 }
     716       22604 :                 return rel;
     717             :         } else {
     718      701555 :                 return rel_optimizer_one(sql, rel, profile, instantiate, value_based_opt, storage_based_opt);
     719             :         }
     720             : }

Generated by: LCOV version 1.14