LCOV - code coverage report
Current view: top level - sql/server - rel_propagate.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 618 698 88.5 %
Date: 2024-10-03 20:03:20 Functions: 21 21 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_propagate.h"
      15             : #include "rel_basetable.h"
      16             : #include "rel_exp.h"
      17             : #include "rel_select.h"
      18             : #include "rel_updates.h"
      19             : #include "sql_partition.h"
      20             : 
      21             : extern sql_rel *rel_list(allocator *sa, sql_rel *l, sql_rel *r);
      22             : 
      23             : static sql_exp*
      24         275 : rel_generate_anti_expression(mvc *sql, sql_rel **anti_rel, sql_table *mt, sql_table *pt)
      25             : {
      26         275 :         sql_exp* res = NULL;
      27             : 
      28         275 :         *anti_rel = rel_basetable(sql, pt, pt->base.name);
      29             : 
      30         275 :         if (isPartitionedByColumnTable(mt)) {
      31         240 :                 int colr = mt->part.pcol->colnr;
      32             : 
      33         240 :                 res = rel_base_bind_colnr(sql, *anti_rel, colr);
      34         240 :                 return res;
      35          35 :         } else if (isPartitionedByExpressionTable(mt)) {
      36          35 :                 *anti_rel = rel_project(sql->sa, *anti_rel, NULL);
      37          35 :                 if (!(res = rel_parse_val(sql, mt->s, mt->part.pexp->exp, NULL, sql->emode, (*anti_rel)->l)))
      38             :                         return NULL;
      39          35 :                 set_processed(*anti_rel);
      40             :         } else {
      41           0 :                 assert(0);
      42             :         }
      43          35 :         (*anti_rel)->exps = new_exp_list(sql->sa);
      44          35 :         append((*anti_rel)->exps, res);
      45          35 :         res = exp_ref(sql, res);
      46          35 :         return res;
      47             : }
      48             : 
      49             : static sql_rel*
      50         116 : rel_create_common_relation(mvc *sql, sql_rel *rel, sql_table *t)
      51             : {
      52         116 :         if (isPartitionedByColumnTable(t)) {
      53          56 :                 return rel_dup(rel->r);
      54          60 :         } else if (isPartitionedByExpressionTable(t)) {
      55          14 :                 sql_rel *inserts;
      56          14 :                 list *l = new_exp_list(sql->sa);
      57             : 
      58          14 :                 rel->r = rel_project(sql->sa, rel->r, l);
      59          14 :                 set_processed((sql_rel*)rel->r);
      60          14 :                 inserts = ((sql_rel*)(rel->r))->l;
      61          41 :                 for (node *n = ol_first_node(t->columns), *m = inserts->exps->h; n && m; n = n->next, m = m->next) {
      62          27 :                         sql_column *col = n->data;
      63          27 :                         sql_exp *before = m->data, *help;
      64             : 
      65          27 :                         help = exp_ref(sql, before);
      66          27 :                         exp_setalias(help, before->alias.label, t->base.name, col->base.name);
      67          27 :                         list_append(l, help);
      68             :                 }
      69          14 :                 return rel_dup(rel->r);
      70             :         }
      71             :         return NULL;
      72             : }
      73             : 
      74             : static sql_exp*
      75         352 : rel_generate_anti_insert_expression(mvc *sql, sql_rel **anti_rel, sql_table *t)
      76             : {
      77         352 :         sql_exp* res = NULL;
      78             : 
      79         352 :         if ((*anti_rel)->op != op_project && (*anti_rel)->op != op_basetable && (*anti_rel)->op != op_table) {
      80          20 :                 sql_rel *inserts; /* In a nested partition case the operation is a op_select, then a projection must be created */
      81          20 :                 list *l = new_exp_list(sql->sa);
      82          20 :                 *anti_rel = rel_project(sql->sa, *anti_rel, l);
      83             : 
      84          20 :                 inserts = (*anti_rel)->l;
      85          20 :                 if (inserts->op != op_project && inserts->op != op_union && inserts->op != op_basetable && inserts->op != op_table)
      86          20 :                         inserts = inserts->l;
      87          60 :                 for (node *n = ol_first_node(t->columns), *m = inserts->exps->h; n && m; n = n->next, m = m->next) {
      88          40 :                         sql_column *col = n->data;
      89          40 :                         sql_exp *before = m->data, *help;
      90             : 
      91          40 :                         help = exp_ref(sql, before);
      92          40 :                         exp_setalias(help, before->alias.label, t->base.name, col->base.name);
      93          40 :                         list_append(l, help);
      94             :                 }
      95             :         }
      96             : 
      97         690 :         if (isPartitionedByColumnTable(t)) {
      98         338 :                 int colr = t->part.pcol->colnr;
      99         338 :                 res = list_fetch((*anti_rel)->exps, colr);
     100          14 :         } else if (isPartitionedByExpressionTable(t)) {
     101          14 :                 *anti_rel = rel_project(sql->sa, *anti_rel, rel_projections(sql, *anti_rel, NULL, 1, 1));
     102          14 :                 if (!(res = rel_parse_val(sql, t->s, t->part.pexp->exp, NULL, sql->emode, (*anti_rel)->l)))
     103             :                         return NULL;
     104          14 :                 exp_label(sql->sa, res, ++sql->label);
     105          14 :                 append((*anti_rel)->exps, res);
     106             :         } else {
     107           0 :                 assert(0);
     108             :         }
     109         352 :         res = exp_ref(sql, res);
     110         352 :         return res;
     111             : }
     112             : 
     113             : static sql_exp *
     114         598 : generate_partition_limits(sql_query *query, sql_rel **r, symbol *s, sql_subtype tpe, bool nilok)
     115             : {
     116         598 :         mvc *sql = query->sql;
     117         598 :         if (!s) {
     118             :                 return NULL;
     119         598 :         } else if (s->token == SQL_NULL && !nilok) {
     120           8 :                 return sql_error(sql, 02, SQLSTATE(42000) "ALTER TABLE: range bound cannot be null");
     121         590 :         } else if (s->token == SQL_MINVALUE) {
     122          39 :                 atom *amin = atom_general(sql->sa, &tpe, NULL, 0);
     123          39 :                 if (!amin) {
     124           0 :                         char *err = sql_subtype_string(sql->ta, &tpe);
     125           0 :                         if (!err)
     126           0 :                                 return sql_error(sql, 02, SQLSTATE(HY013) MAL_MALLOC_FAIL);
     127           0 :                         sql_error(sql, 02, SQLSTATE(42000) "ALTER TABLE: absolute minimum value not available for %s type", err);
     128           0 :                         return NULL;
     129             :                 }
     130          39 :                 return exp_atom(sql->sa, amin);
     131         551 :         } else if (s->token == SQL_MAXVALUE) {
     132          38 :                 atom *amax = atom_general(sql->sa, &tpe, NULL, 0);
     133          38 :                 if (!amax) {
     134           0 :                         char *err = sql_subtype_string(sql->ta, &tpe);
     135           0 :                         if (!err)
     136           0 :                                 return sql_error(sql, 02, SQLSTATE(HY013) MAL_MALLOC_FAIL);
     137           0 :                         sql_error(sql, 02, SQLSTATE(42000) "ALTER TABLE: absolute maximum value not available for %s type", err);
     138           0 :                         return NULL;
     139             :                 }
     140          38 :                 return exp_atom(sql->sa, amax);
     141             :         } else {
     142         513 :                 exp_kind ek = {type_value, card_value, FALSE};
     143         513 :                 sql_exp *e = rel_value_exp2(query, r, s, sql_sel | sql_values, ek);
     144             : 
     145         513 :                 if (!e)
     146             :                         return NULL;
     147         512 :                 return exp_check_type(sql, &tpe, r ? *r : NULL, e, type_equal);
     148             :         }
     149             : }
     150             : 
     151             : static sql_exp*
     152         209 : create_range_partition_anti_rel(sql_query* query, sql_table *mt, sql_table *pt, bit with_nills, sql_exp *pmin, sql_exp *pmax, bool all_ranges, bool max_equal_min)
     153             : {
     154         209 :         mvc *sql = query->sql;
     155         209 :         sql_rel *anti_rel;
     156         209 :         sql_exp *aggr, *anti_exp = NULL, *anti_le, *e1, *e2, *anti_nils;
     157         209 :         sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
     158         209 :         sql_subtype tpe;
     159             : 
     160         209 :         find_partition_type(&tpe, mt);
     161             : 
     162         209 :         anti_le = rel_generate_anti_expression(sql, &anti_rel, mt, pt);
     163         209 :         anti_nils = rel_unop_(sql, anti_rel, anti_le, "sys", "isnull", card_value);
     164         209 :         set_has_no_nil(anti_nils);
     165         209 :         if (pmin && pmax) {
     166             :                 /* type could have changed because of partition expression */
     167         195 :                 if (!(anti_le = exp_check_type(sql, &tpe, NULL, anti_le, type_equal)))
     168             :                         return NULL;
     169         195 :                 if (all_ranges) { /*if holds all values in range, don't generate the range comparison */
     170          13 :                         assert(!with_nills);
     171             :                 } else {
     172         182 :                         sql_exp *range1, *range2;
     173             : 
     174         182 :                         e1 = exp_copy(sql, pmin);
     175         182 :                         if (!(e1 = exp_check_type(sql, &tpe, NULL, e1, type_equal)))
     176             :                                 return NULL;
     177             : 
     178         182 :                         if (max_equal_min) {
     179           3 :                                 anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_notequal);
     180             :                         } else {
     181         179 :                                 e2 = exp_copy(sql, pmax);
     182         179 :                                 if (!(e2 = exp_check_type(sql, &tpe, NULL, e2, type_equal)))
     183             :                                         return NULL;
     184             : 
     185         179 :                                 range1 = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_lt);
     186         179 :                                 range2 = exp_compare(sql->sa, exp_copy(sql, anti_le), e2, cmp_gte);
     187         179 :                                 anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), range1),
     188             :                                                                 list_append(new_exp_list(sql->sa), range2), 0);
     189             :                         }
     190             :                 }
     191         182 :                 if (!with_nills) {
     192         175 :                         anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
     193         175 :                         if (anti_exp)
     194         162 :                                 anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
     195             :                                                                   list_append(new_exp_list(sql->sa), anti_nils), 0);
     196             :                         else
     197             :                                 anti_exp = anti_nils;
     198             :                 }
     199             :         } else {
     200          14 :                 anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
     201             :         }
     202             : 
     203         209 :         anti_rel = rel_select(sql->sa, anti_rel, anti_exp);
     204         209 :         set_processed(anti_rel);
     205         209 :         anti_rel = rel_groupby(sql, anti_rel, NULL);
     206         209 :         aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_rel->card, 0);
     207         209 :         (void) rel_groupby_add_aggr(sql, anti_rel, aggr);
     208         209 :         set_processed(anti_rel);
     209         209 :         exp_label(sql->sa, aggr, ++sql->label);
     210             : 
     211         209 :         return exp_rel(sql, anti_rel);
     212             : }
     213             : 
     214             : static sql_exp*
     215          66 : create_list_partition_anti_rel(sql_query* query, sql_table *mt, sql_table *pt, bit with_nills, list *anti_exps)
     216             : {
     217          66 :         mvc *sql = query->sql;
     218          66 :         sql_rel *anti_rel;
     219          66 :         sql_exp *aggr, *anti_exp, *anti_le, *anti_nils;
     220          66 :         sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
     221          66 :         sql_subtype tpe;
     222             : 
     223          66 :         find_partition_type(&tpe, mt);
     224             : 
     225          66 :         anti_le = rel_generate_anti_expression(sql, &anti_rel, mt, pt);
     226          66 :         anti_nils = rel_unop_(sql, anti_rel, anti_le, "sys", "isnull", card_value);
     227             : 
     228          66 :         set_has_no_nil(anti_nils);
     229          66 :         if (list_length(anti_exps) > 0) {
     230          63 :                 sql_exp *ae = anti_exps->h->data;
     231          63 :                 sql_subtype *ntpe = exp_subtype(ae);
     232             :                 /* function may need conversion */
     233          63 :                 if (!(anti_le = exp_check_type(sql, ntpe, NULL, anti_le, type_equal)))
     234             :                         return NULL;
     235          63 :                 anti_exp = exp_in(sql->sa, anti_le, anti_exps, cmp_notin);
     236          63 :                 if (!with_nills) {
     237          54 :                         anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
     238          54 :                         anti_exp = exp_or(sql->sa, append(new_exp_list(sql->sa), anti_exp),
     239             :                                                           append(new_exp_list(sql->sa), anti_nils), 0);
     240             :                 }
     241             :         } else {
     242           3 :                 assert(with_nills);
     243           3 :                 anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
     244             :         }
     245             : 
     246          66 :         anti_rel = rel_select(sql->sa, anti_rel, anti_exp);
     247          66 :         set_processed(anti_rel);
     248          66 :         anti_rel = rel_groupby(sql, anti_rel, NULL);
     249          66 :         aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_rel->card, 0);
     250          66 :         (void) rel_groupby_add_aggr(sql, anti_rel, aggr);
     251          66 :         set_processed(anti_rel);
     252          66 :         exp_label(sql->sa, aggr, ++sql->label);
     253          66 :         return exp_rel(sql, anti_rel);
     254             : }
     255             : 
     256             : static sql_exp *
     257           7 : add_check_count(mvc *sql,  sql_exp *a, sql_exp *b)
     258             : {
     259           7 :         if (!a)
     260             :                 return b;
     261           7 :         sql_subtype *lng = sql_bind_localtype("lng");
     262           7 :     sql_subfunc *add = sql_bind_func_result(sql, "sys", "sql_add", F_FUNC, true, lng, 2, lng, lng);
     263           7 :         return exp_binop(sql->sa, a, b, add);
     264             : }
     265             : 
     266             : static sql_rel *
     267         275 : propagate_validation_to_upper_tables(sql_query* query, sql_table *mt, sql_table *pt, sql_rel *rel, sql_exp *check_count)
     268             : {
     269         275 :         mvc *sql = query->sql;
     270         275 :         sql_part *it = NULL;
     271             : 
     272         282 :         for (sql_table *prev = mt ; prev; prev = it?it->t:NULL) {
     273         282 :                 if ((it=partition_find_part(sql->session->tr, prev, NULL)) == NULL)
     274             :                         break;
     275           7 :                 sql_part *spt = it;
     276           7 :                 if (spt) {
     277           7 :                         sql_subtype tp;
     278           7 :                         find_partition_type(&tp, it->t);
     279             : 
     280           7 :                         if (isRangePartitionTable(it->t)) {
     281           1 :                                 int tpe = tp.type->localtype;
     282           1 :                                 int (*atomcmp)(const void *, const void *) = ATOMcompare(tpe);
     283           1 :                                 const void *nil = ATOMnilptr(tpe);
     284           1 :                                 sql_exp *e1 = NULL, *e2 = NULL;
     285           1 :                                 bool found_all = false, max_equal_min = false;
     286             : 
     287           1 :                                 if (atomcmp(spt->part.range.minvalue, nil) != 0 && atomcmp(spt->part.range.maxvalue, nil) != 0) {
     288           1 :                                         max_equal_min = ATOMcmp(tpe, spt->part.range.maxvalue, spt->part.range.minvalue) == 0;
     289           1 :                                         e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, spt->part.range.minvalue));
     290           1 :                                         if (!max_equal_min)
     291           1 :                                                 e2 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, spt->part.range.maxvalue));
     292             :                                 } else {
     293           0 :                                         assert(spt->with_nills);
     294           0 :                                         found_all = is_bit_nil(spt->with_nills);
     295             :                                 }
     296           1 :                                 if (!found_all || !spt->with_nills) {
     297           1 :                                         sql_exp *nres = create_range_partition_anti_rel(query, it->t, pt, spt->with_nills, e1, e2, false, max_equal_min);
     298           1 :                                         check_count = add_check_count(sql, check_count, nres);
     299             :                                 }
     300           6 :                         } else if (isListPartitionTable(it->t)) {
     301           6 :                                 list *exps = new_exp_list(sql->sa);
     302          28 :                                 for (node *n = spt->part.values->h ; n ; n = n->next) {
     303          22 :                                         sql_part_value *next = (sql_part_value*) n->data;
     304          22 :                                         sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, next->value));
     305          22 :                                         list_append(exps, e1);
     306             :                                 }
     307           6 :                                 sql_exp *nres = create_list_partition_anti_rel(query, it->t, pt, spt->with_nills, exps);
     308           6 :                                 check_count = add_check_count(sql, check_count, nres);
     309             :                         } else {
     310           0 :                                 assert(0);
     311             :                         }
     312             :                 } else { /* the sql_part should exist */
     313             :                         assert(0);
     314             :                 }
     315             :         }
     316         275 :         if (check_count) {
     317         268 :                 append(rel->exps, check_count);
     318             :         } else {
     319           7 :                 append(rel->exps, exp_atom_lng(sql->sa, 0));
     320             :         }
     321         275 :         return rel;
     322             : }
     323             : 
     324             : sql_rel *
     325         221 : rel_alter_table_add_partition_range(sql_query* query, sql_table *mt, sql_table *pt, char *sname, char *tname, char *sname2,
     326             :                                                                         char *tname2, symbol* min, symbol* max, bit with_nills, int update)
     327             : {
     328         221 :         mvc *sql = query->sql;
     329         221 :         sql_rel *rel_psm = rel_create(sql->sa);
     330         221 :         list *exps = new_exp_list(sql->sa);
     331         221 :         sql_exp *pmin, *pmax;
     332         221 :         sql_subtype tpe;
     333         221 :         bool all_ranges = false;
     334         221 :         sql_exp *check_count = NULL;
     335             : 
     336         221 :         if (!rel_psm || !exps)
     337             :                 return NULL;
     338             : 
     339         221 :         find_partition_type(&tpe, mt);
     340             : 
     341         221 :         assert((!min && !max && with_nills) || (min && max));
     342         221 :         if (min && max) {
     343         207 :                 pmin = generate_partition_limits(query, &rel_psm, min, tpe, false);
     344         207 :                 pmax = generate_partition_limits(query, &rel_psm, max, tpe, false);
     345         207 :                 if (!pmin || !pmax)
     346             :                         return NULL;
     347         201 :                 if (min->token == SQL_MINVALUE && max->token == SQL_MAXVALUE && with_nills)
     348           7 :                         with_nills = bit_nil; /* holds all values in range */
     349         382 :                 all_ranges = (min->token == SQL_MINVALUE && max->token == SQL_MAXVALUE);
     350             :         } else {
     351          14 :                 pmin = exp_atom(sql->sa, atom_general(sql->sa, &tpe, NULL, 0));
     352          14 :                 pmax = exp_atom(sql->sa, atom_general(sql->sa, &tpe, NULL, 0));
     353             :         }
     354             : 
     355             :         /* generate the psm statement */
     356         215 :         append(exps, exp_atom_clob(sql->sa, sname));
     357         215 :         append(exps, exp_atom_clob(sql->sa, tname));
     358         215 :         assert((sname2 && tname2) || (!sname2 && !tname2));
     359         215 :         if (sname2) {
     360         215 :                 append(exps, exp_atom_clob(sql->sa, sname2));
     361         215 :                 append(exps, exp_atom_clob(sql->sa, tname2));
     362             :         }
     363         215 :         append(exps, pmin);
     364         215 :         append(exps, pmax);
     365         215 :         append(exps, is_bit_nil(with_nills) ? exp_atom(sql->sa, atom_general(sql->sa, sql_bind_localtype("bit"), NULL, 0)) : exp_atom_bool(sql->sa, with_nills));
     366         215 :         append(exps, exp_atom_int(sql->sa, update));
     367         215 :         rel_psm->l = NULL;
     368         215 :         rel_psm->r = NULL;
     369         215 :         rel_psm->op = op_ddl;
     370         215 :         rel_psm->flag = ddl_alter_table_add_range_partition;
     371         215 :         rel_psm->exps = exps;
     372         215 :         rel_psm->card = CARD_MULTI;
     373         215 :         rel_psm->nrcols = 0;
     374             : 
     375         215 :         if (!is_bit_nil(with_nills)) {
     376         208 :                 bool min_max_equal = false;
     377         208 :                 if (pmin && pmax && pmin->type == e_atom && pmax->type == e_atom && pmin->l && pmax->l) {
     378         162 :                         atom *e1 = pmin->l, *e2 = pmax->l;
     379         162 :                         min_max_equal = ATOMcmp(tpe.type->localtype, &e1->data.val, &e2->data.val) == 0;
     380             :                 }
     381         236 :                 check_count = create_range_partition_anti_rel(query, mt, pt, with_nills, (min && max) ? pmin : NULL, (min && max) ? pmax : NULL, all_ranges, min_max_equal);
     382             :         }
     383         215 :         return propagate_validation_to_upper_tables(query, mt, pt, rel_psm, check_count); /* this adds the check_count to the rel_psm exps list */
     384             : }
     385             : 
     386             : sql_rel *
     387          62 : rel_alter_table_add_partition_list(sql_query *query, sql_table *mt, sql_table *pt, char *sname, char *tname, char *sname2,
     388             :                                                                    char *tname2, dlist* values, bit with_nills, int update)
     389             : {
     390          62 :         mvc *sql = query->sql;
     391          62 :         sql_rel *rel_psm = rel_create(sql->sa);
     392          62 :         list *exps = new_exp_list(sql->sa), *lvals = new_exp_list(sql->sa);
     393          62 :         sql_subtype tpe;
     394          62 :         sql_exp *converted_values = NULL;
     395             : 
     396          62 :         if (!rel_psm || !exps)
     397             :                 return NULL;
     398             : 
     399          62 :         find_partition_type(&tpe, mt);
     400             : 
     401          62 :         if (values) {
     402         241 :                 for (dnode *dn = values->h; dn ; dn = dn->next) { /* parse the atoms and generate the expressions */
     403         184 :                         symbol* next = dn->data.sym;
     404         184 :                         sql_exp *pnext = generate_partition_limits(query, &rel_psm, next, tpe, true);
     405             : 
     406         184 :                         if (!pnext)
     407             :                                 return NULL;
     408         183 :                         if (next->token == SQL_NULL)
     409           1 :                                 return sql_error(sql, 02, SQLSTATE(42000) "ALTER TABLE: a list value cannot be null");
     410         182 :                         append(lvals, pnext);
     411             :                 }
     412             :         }
     413             : 
     414          60 :         converted_values = exp_values(sql->sa, lvals);
     415          60 :         if (!(converted_values = exp_values_set_supertype(sql, converted_values, &tpe)))
     416             :                 return NULL;
     417         239 :         for (node *n = ((list*)converted_values->f)->h ; n ; n = n->next)
     418         179 :                 if (!(n->data = exp_check_type(sql, &tpe, NULL, n->data, type_equal)))
     419             :                         return NULL;
     420             : 
     421             :         /* generate the psm statement */
     422          60 :         append(exps, exp_atom_clob(sql->sa, sname));
     423          60 :         append(exps, exp_atom_clob(sql->sa, tname));
     424          60 :         assert((sname2 && tname2) || (!sname2 && !tname2));
     425          60 :         if (sname2) {
     426          60 :                 append(exps, exp_atom_clob(sql->sa, sname2));
     427          60 :                 append(exps, exp_atom_clob(sql->sa, tname2));
     428             :         }
     429          60 :         append(exps, exp_atom_bool(sql->sa, with_nills));
     430          60 :         append(exps, exp_atom_int(sql->sa, update));
     431          60 :         rel_psm->l = NULL;
     432          60 :         rel_psm->r = NULL;
     433          60 :         rel_psm->op = op_ddl;
     434          60 :         rel_psm->flag = ddl_alter_table_add_list_partition;
     435          60 :         rel_psm->exps = exps;
     436          60 :         rel_psm->card = CARD_MULTI;
     437          60 :         rel_psm->nrcols = 0;
     438             : 
     439          60 :         sql_exp *check_count = create_list_partition_anti_rel(query, mt, pt, with_nills, exps_copy(sql, (list*)converted_values->f));
     440          60 :         rel_psm = propagate_validation_to_upper_tables(query, mt, pt, rel_psm, check_count); /* this adds check_count to the rel_psm exps list */
     441          60 :         rel_psm->exps = list_merge(rel_psm->exps, converted_values->f, (fdup)NULL);
     442          60 :         return rel_psm;
     443             : }
     444             : 
     445             : static sql_rel* rel_change_base_table(mvc* sql, sql_rel* rel, sql_table* oldt, sql_table* newt);
     446             : 
     447             : static sql_exp*
     448         218 : exp_change_column_table(mvc *sql, sql_exp *e, sql_table* oldt, sql_table* newt)
     449             : {
     450         218 :         if (mvc_highwater(sql))
     451           0 :                 return sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     452             : 
     453         218 :         if (!e)
     454             :                 return NULL;
     455         218 :         switch(e->type) {
     456           0 :                 case e_psm: {
     457           0 :                         if (e->flag & PSM_RETURN) {
     458           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     459           0 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     460           0 :                         } else if (e->flag & PSM_WHILE) {
     461           0 :                                 e->l = exp_change_column_table(sql, e->l, oldt, newt);
     462           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     463           0 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     464           0 :                         } else if (e->flag & PSM_IF) {
     465           0 :                                 e->l = exp_change_column_table(sql, e->l, oldt, newt);
     466           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     467           0 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     468           0 :                                 if (e->f)
     469           0 :                                         for (node *n = ((list*)e->f)->h ; n ; n = n->next)
     470           0 :                                                 n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     471           0 :                         } else if (e->flag & PSM_REL) {
     472           0 :                                 rel_change_base_table(sql, e->l, oldt, newt);
     473           0 :                         } else if (e->flag & PSM_EXCEPTION) {
     474           0 :                                 e->l = exp_change_column_table(sql, e->l, oldt, newt);
     475             :                         }
     476             :                 } break;
     477           3 :                 case e_convert: {
     478           3 :                         e->l = exp_change_column_table(sql, e->l, oldt, newt);
     479           3 :                 } break;
     480          44 :                 case e_atom: {
     481          44 :                         if (e->f)
     482           0 :                                 for (node *n = ((list*)e->f)->h ; n ; n = n->next)
     483           0 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     484             :                 } break;
     485           7 :                 case e_aggr:
     486             :                 case e_func: {
     487           7 :                         if (e->l)
     488          21 :                                 for (node *n = ((list*)e->l)->h ; n ; n = n->next)
     489          14 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     490           7 :                         if (e->type == e_func && e->r)
     491           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     492           0 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     493             :                 } break;
     494         143 :                 case e_column: {
     495         143 :                         if (!strcmp(e->l, oldt->base.name))
     496         143 :                                 e->l = sa_strdup(sql->sa, newt->base.name);
     497             :                 } break;
     498          21 :                 case e_cmp: {
     499          21 :                         if (e->flag == cmp_in || e->flag == cmp_notin) {
     500           2 :                                 e->l = exp_change_column_table(sql, e->l, oldt, newt);
     501           6 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     502           4 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     503          19 :                         } else if (e->flag == cmp_or || e->flag == cmp_filter) {
     504           0 :                                 for (node *n = ((list*)e->l)->h ; n ; n = n->next)
     505           0 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     506           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     507           0 :                                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     508             :                         } else {
     509          19 :                                 if (e->l)
     510          19 :                                         e->l = exp_change_column_table(sql, e->l, oldt, newt);
     511          19 :                                 if (e->r)
     512          19 :                                         e->r = exp_change_column_table(sql, e->r, oldt, newt);
     513          19 :                                 if (e->f)
     514           0 :                                         e->f = exp_change_column_table(sql, e->f, oldt, newt);
     515             :                         }
     516             :                 } break;
     517             :         }
     518         218 :         if (exp_relname(e) && !strcmp(exp_relname(e), oldt->base.name))
     519         159 :                 e->alias.rname = newt->base.name;
     520             :         return e;
     521             : }
     522             : 
     523             : static sql_rel*
     524          69 : rel_change_base_table(mvc* sql, sql_rel* rel, sql_table* oldt, sql_table* newt)
     525             : {
     526          69 :         if (mvc_highwater(sql))
     527           0 :                 return sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     528             : 
     529          69 :         if (!rel)
     530             :                 return NULL;
     531             : 
     532          69 :         if (rel->exps) {
     533         194 :                 for (node *n = rel->exps->h ; n ; n = n->next)
     534         125 :                         n->data = exp_change_column_table(sql, (sql_exp*) n->data, oldt, newt);
     535          69 :                 list_hash_clear(rel->exps);
     536             :         }
     537             : 
     538          69 :         switch (rel->op) {
     539          24 :                 case op_basetable:
     540          24 :                         if (rel->l == oldt)
     541          24 :                                 rel->l = newt;
     542             :                         break;
     543           0 :                 case op_table:
     544           0 :                         if (IS_TABLE_PROD_FUNC(rel->flag) || rel->flag == TABLE_FROM_RELATION) {
     545           0 :                                 if (rel->l)
     546           0 :                                         rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
     547             :                         }
     548             :                         break;
     549           0 :                 case op_join:
     550             :                 case op_left:
     551             :                 case op_right:
     552             :                 case op_full:
     553             :                 case op_semi:
     554             :                 case op_anti:
     555             :                 case op_union:
     556             :                 case op_inter:
     557             :                 case op_except:
     558             :                 case op_insert:
     559             :                 case op_update:
     560             :                 case op_delete:
     561             :                 case op_merge:
     562           0 :                         if (rel->l)
     563           0 :                                 rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
     564           0 :                         if (rel->r)
     565           0 :                                 rel->r = rel_change_base_table(sql, rel->r, oldt, newt);
     566             :                         break;
     567           0 :                 case op_munion:
     568           0 :                         assert(rel->l);
     569           0 :                         for (node *n = ((list*)rel->l)->h; n; n = n->next)
     570           0 :                                 n->data = rel_change_base_table(sql, n->data, oldt, newt);
     571             :                         break;
     572          45 :                 case op_groupby:
     573             :                 case op_project:
     574             :                 case op_select:
     575             :                 case op_topn:
     576             :                 case op_sample:
     577             :                 case op_truncate:
     578          45 :                         if (rel->l)
     579          45 :                                 rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
     580             :                         break;
     581           0 :                 case op_ddl:
     582           0 :                         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) {
     583           0 :                                 if (rel->l)
     584           0 :                                         rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
     585             :                         } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
     586           0 :                                 if (rel->l)
     587           0 :                                         rel->l = rel_change_base_table(sql, rel->l, oldt, newt);
     588           0 :                                 if (rel->r)
     589           0 :                                         rel->r = rel_change_base_table(sql, rel->r, oldt, newt);
     590             :                         }
     591             :                 break;
     592             :         }
     593             :         return rel;
     594             : }
     595             : 
     596             : static sql_rel *
     597          17 : rel_truncate_duplicate(mvc *sql, sql_rel *table, sql_rel *ori)
     598             : {
     599          17 :         sql_rel *r = rel_create(sql->sa);
     600             : 
     601          17 :         r->exps = exps_copy(sql, ori->exps);
     602          17 :         r->op = op_truncate;
     603          17 :         r->l = table;
     604          17 :         r->r = NULL;
     605          17 :         return r;
     606             : }
     607             : 
     608             : static sql_rel*
     609          21 : rel_generate_subdeletes(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
     610             : {
     611          21 :         int just_one = 1;
     612          21 :         sql_rel *sel = NULL;
     613             : 
     614          56 :         for (node *n = t->members->h; n; n = n->next) {
     615          35 :                 sql_part *pt = (sql_part *) n->data;
     616          35 :                 sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
     617          35 :                 sql_rel *s1, *dup = NULL;
     618             : 
     619          69 :                 if (!update_allowed(sql, sub, sub->base.name, is_delete(rel->op) ? "DELETE": "TRUNCATE",
     620          35 :                                                    is_delete(rel->op) ? "delete": "truncate",  is_delete(rel->op) ? 1 : 2))
     621             :                         return NULL;
     622             : 
     623          35 :                 if (rel->r) {
     624           8 :                         dup = rel_copy(sql, rel->r, 1);
     625           8 :                         dup = rel_change_base_table(sql, dup, t, sub);
     626             :                 }
     627          35 :                 if (is_delete(rel->op))
     628          18 :                         s1 = rel_delete(sql->sa, rel_basetable(sql, sub, sub->base.name), dup);
     629             :                 else
     630          17 :                         s1 = rel_truncate_duplicate(sql, rel_basetable(sql, sub, sub->base.name), rel);
     631          35 :                 if (just_one == 0) {
     632          14 :                         sel = rel_list(sql->sa, sel, s1);
     633             :                 } else {
     634             :                         sel = s1;
     635             :                         just_one = 0;
     636             :                 }
     637          35 :                 (*changes)++;
     638             :         }
     639          21 :         rel_destroy(rel);
     640          21 :         return sel;
     641             : }
     642             : 
     643             : static sql_rel*
     644          11 : rel_generate_subupdates(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
     645             : {
     646          11 :         int just_one = 1;
     647          11 :         sql_rel *sel = NULL;
     648             : 
     649          27 :         for (node *n = t->members->h; n; n = n->next) {
     650          16 :                 sql_part *pt = (sql_part *) n->data;
     651          16 :                 sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
     652          16 :                 sql_rel *s1, *dup = NULL;
     653          16 :                 list *uexps = exps_copy(sql, rel->exps), *checked_updates = new_exp_list(sql->sa);
     654          16 :                 sql_rel *bt = rel_basetable(sql, sub, sub->base.name);
     655             : 
     656          16 :                 if (!update_allowed(sql, sub, sub->base.name, "UPDATE", "update", 0))
     657             :                         return NULL;
     658             : 
     659          48 :                 for (node *n = uexps->h ; n ; n = n->next) {
     660          32 :                         sql_exp *e = (sql_exp *) n->data;
     661          32 :                         const char *cname = exp_name(e);
     662             : 
     663          32 :                         if (cname[0] != '%') { /* Skip TID column */
     664          16 :                                 sql_column *c = mvc_bind_column(sql, sub, cname);
     665             : 
     666          16 :                                 if (!c)
     667           0 :                                         return sql_error(sql, ERR_NOTFOUND, SQLSTATE(42S22) "UPDATE: no such column '%s.%s'\n", sub->base.name, cname);
     668          16 :                                 rel_base_use(sql, bt, c->colnr);
     669          16 :                                 if (!(e = update_check_column(sql, sub, c, e, rel, c->base.name, "UPDATE")))
     670             :                                         return NULL;
     671             :                         }
     672          32 :                         list_append(checked_updates, e);
     673             :                 }
     674             : 
     675          16 :                 if (rel->r) {
     676          16 :                         dup = rel_copy(sql, rel->r, 1);
     677          16 :                         dup = rel_change_base_table(sql, dup, t, sub);
     678             :                 }
     679             : 
     680          48 :                 for (node *ne = checked_updates->h ; ne ; ne = ne->next)
     681          32 :                         ne->data = exp_change_column_table(sql, (sql_exp*) ne->data, t, sub);
     682             : 
     683          16 :                 s1 = rel_update(sql, bt, dup, NULL, checked_updates);
     684          16 :                 if (just_one == 0) {
     685           5 :                         sel = rel_list(sql->sa, sel, s1);
     686             :                 } else {
     687             :                         sel = s1;
     688             :                         just_one = 0;
     689             :                 }
     690          16 :                 (*changes)++;
     691             :         }
     692          11 :         rel_destroy(rel);
     693          11 :         return sel;
     694             : }
     695             : 
     696             : static sql_rel*
     697         102 : rel_generate_subinserts(sql_query *query, sql_rel *rel, sql_table *t, int *changes,
     698             :                                                 const char *operation, const char *desc)
     699             : {
     700         102 :         mvc *sql = query->sql;
     701         102 :         int just_one = 1, found_nils = 0, found_all_range_values = 0;
     702         102 :         sql_rel *new_table = NULL, *sel = NULL, *anti_rel = NULL;
     703         102 :         sql_exp *anti_exp = NULL, *anti_le = NULL, *anti_nils = NULL, *accum = NULL, *aggr = NULL;
     704         102 :         list *anti_exps = new_exp_list(sql->sa);
     705         102 :         sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
     706         102 :         char buf[BUFSIZ];
     707         102 :         sql_subtype tp;
     708             : 
     709         102 :         find_partition_type(&tp, t);
     710         102 :         if (isPartitionedByColumnTable(t)) {
     711          92 :                 anti_rel = rel_dup(rel->r);
     712          10 :         } else if (isPartitionedByExpressionTable(t)) {
     713          10 :                 anti_rel = rel_create_common_relation(sql, rel, t);
     714          10 :                 if (!anti_rel)
     715             :                         return NULL;
     716             :         } else {
     717           0 :                 assert(0);
     718             :         }
     719         102 :         anti_le = rel_generate_anti_insert_expression(sql, &anti_rel, t);
     720             : 
     721         309 :         for (node *n = t->members->h; n; n = n->next) {
     722         209 :                 sql_part *pt = (sql_part *) n->data;
     723         209 :                 sql_table *sub = find_sql_table_id(sql->session->tr, t->s, pt->member);
     724         209 :                 sql_rel *s1 = NULL, *dup = NULL;
     725         209 :                 sql_exp *le = NULL;
     726             : 
     727         209 :                 if (!insert_allowed(sql, sub, sub->base.name, "INSERT", "insert"))
     728           2 :                         return NULL;
     729             : 
     730         207 :                 if (isPartitionedByColumnTable(t)) {
     731         190 :                         dup = rel_dup(rel->r);
     732         190 :                         le = rel_generate_anti_insert_expression(sql, &dup, t);
     733          17 :                 } else if (isPartitionedByExpressionTable(t)) {
     734          17 :                         dup = rel_dup(anti_rel);
     735          17 :                         le = anti_le;
     736             :                 } else {
     737           0 :                         assert(0);
     738             :                 }
     739             : 
     740         207 :                 if (isRangePartitionTable(t)) {
     741         114 :                         sql_exp *range = NULL, *full_range = NULL;
     742         114 :                         int tpe = tp.type->localtype;
     743         114 :                         int (*atomcmp)(const void *, const void *) = ATOMcompare(tpe);
     744         114 :                         const void *nil = ATOMnilptr(tpe);
     745         114 :                         bool is_min_nil = atomcmp(pt->part.range.minvalue, nil) == 0, is_max_nil = atomcmp(pt->part.range.maxvalue, nil) == 0;
     746             : 
     747         114 :                         if (is_min_nil && is_max_nil) {
     748           8 :                                 found_all_range_values |= (pt->with_nills != 1);
     749           8 :                                 found_nils |= is_bit_nil(pt->with_nills);
     750           8 :                                 if (pt->with_nills == false) { /* full range without nils */
     751           2 :                                         sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
     752             : 
     753           2 :                                         set_has_no_nil(nils);
     754           2 :                                         nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 0), cmp_equal);
     755           2 :                                         full_range = range = nils; /* ugh */
     756             :                                 }
     757         106 :                         } else if (is_min_nil) {
     758           1 :                                 full_range = range = exp_compare(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue)), cmp_lt);
     759         105 :                         } else if (is_max_nil) {
     760           5 :                                 full_range = range = exp_compare(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue)), cmp_gte);
     761             :                         } else {
     762         100 :                                 bool max_equal_min = ATOMcmp(tpe, pt->part.range.maxvalue, pt->part.range.minvalue) == 0;
     763             : 
     764         100 :                                 full_range = range = max_equal_min ?
     765         100 :                                         exp_compare(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue)), cmp_equal) :
     766         100 :                                         exp_compare2(sql->sa, le, exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue)),
     767             :                                                                                           exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue)), 1, 0);
     768             :                         }
     769         114 :                         if (pt->with_nills == true) { /* handle the nulls case */
     770          19 :                                 sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
     771             : 
     772          19 :                                 set_has_no_nil(nils);
     773          19 :                                 nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 1), cmp_equal);
     774          19 :                                 if (full_range) {
     775          14 :                                         full_range = exp_or(sql->sa, list_append(new_exp_list(sql->sa), full_range),
     776             :                                                                                 list_append(new_exp_list(sql->sa), nils), 0);
     777             :                                 } else {
     778             :                                         full_range = nils;
     779             :                                 }
     780             :                                 found_nils = 1;
     781             :                         }
     782         114 :                         if (accum && range) {
     783          55 :                                 accum = exp_or(sql->sa, list_append(new_exp_list(sql->sa), accum),
     784          55 :                                                            list_append(new_exp_list(sql->sa), exp_copy(sql, range)), 0);
     785          59 :                         } else if (range) {
     786          53 :                                 accum = exp_copy(sql, range);
     787             :                         }
     788         114 :                         if (full_range) {
     789         113 :                                 dup = rel_select(sql->sa, dup, full_range);
     790         113 :                                 set_processed(dup);
     791             :                         }
     792          93 :                 } else if (isListPartitionTable(t)) {
     793          93 :                         sql_exp *ein = NULL;
     794             : 
     795          93 :                         if (list_length(pt->part.values)) { /* if the partition holds non-null values */
     796          91 :                                 list *exps = new_exp_list(sql->sa);
     797         364 :                                 for (node *nn = pt->part.values->h ; nn ; nn = nn->next) {
     798         273 :                                         sql_part_value *next = (sql_part_value*) nn->data;
     799         273 :                                         sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, next->value));
     800         273 :                                         list_append(exps, e1);
     801         273 :                                         list_append(anti_exps, exp_copy(sql, e1));
     802             :                                 }
     803          91 :                                 ein = exp_in(sql->sa, le, exps, cmp_in);
     804             :                         } else {
     805           2 :                                 assert(pt->with_nills);
     806             :                         }
     807          93 :                         if (pt->with_nills) { /* handle the nulls case */
     808          23 :                                 sql_exp *nils = rel_unop_(sql, dup, le, "sys", "isnull", card_value);
     809             : 
     810          23 :                                 set_has_no_nil(nils);
     811          23 :                                 nils = exp_compare(sql->sa, nils, exp_atom_bool(sql->sa, 1), cmp_equal);
     812          23 :                                 if (ein) {
     813          21 :                                         ein = exp_or(sql->sa, list_append(new_exp_list(sql->sa), ein),
     814             :                                                                  list_append(new_exp_list(sql->sa), nils), 0);
     815             :                                 } else {
     816             :                                         ein = nils;
     817             :                                 }
     818             :                                 found_nils = 1;
     819             :                         }
     820          93 :                         dup = rel_select(sql->sa, dup, ein);
     821          93 :                         set_processed(dup);
     822             :                 } else {
     823           0 :                         assert(0);
     824             :                 }
     825             : 
     826         207 :                 new_table = rel_basetable(sql, sub, sub->base.name);
     827         207 :                 rel_base_use_all(query->sql, new_table);
     828         207 :                 new_table = rewrite_basetable(query->sql, new_table);
     829         207 :                 new_table->p = prop_create(sql->sa, PROP_USED, new_table->p); /* don't create infinite loops in the optimizer */
     830             : 
     831         207 :                 if (isPartitionedByExpressionTable(t)) {
     832          17 :                         sql_exp *del;
     833          17 :                         dup = rel_project(sql->sa, dup, rel_projections(sql, dup, NULL, 1, 1));
     834          17 :                         del = list_fetch(dup->exps, list_length(dup->exps) - 1);
     835          17 :                         list_remove_data(dup->exps, NULL, del);
     836             :                 }
     837             : 
     838         207 :                 s1 = rel_insert(query->sql, new_table, dup);
     839         207 :                 if (just_one == 0) {
     840         107 :                         sel = rel_list(sql->sa, sel, s1);
     841             :                 } else {
     842             :                         sel = s1;
     843             :                         just_one = 0;
     844             :                 }
     845         207 :                 (*changes)++;
     846             :         }
     847             : 
     848         100 :         if (!found_all_range_values || !found_nils) {
     849             :                 /* generate the exception */
     850          98 :                 if (isRangePartitionTable(t)) {
     851          54 :                         if (accum) {
     852          52 :                                 set_anti(accum);
     853          52 :                                 anti_exp = accum;
     854             :                         }
     855          44 :                 } else if (isListPartitionTable(t)) {
     856          44 :                         if (list_length(anti_exps))
     857          43 :                                 anti_exp = exp_in(sql->sa, anti_le, anti_exps, cmp_notin);
     858             :                 } else {
     859           0 :                         assert(0);
     860             :                 }
     861          98 :                 if (!found_nils) {
     862          57 :                         assert(anti_exp);
     863          57 :                         anti_nils = rel_unop_(sql, NULL, anti_le, "sys", "isnull", card_value);
     864          57 :                         set_has_no_nil(anti_nils);
     865          57 :                         anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
     866          57 :                         anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
     867             :                                                         list_append(new_exp_list(sql->sa), anti_nils), 0);
     868          41 :                 } else if (!anti_exp) {
     869           3 :                         anti_nils = rel_unop_(sql, NULL, exp_copy(sql, anti_le), "sys", "isnull", card_value);
     870           3 :                         set_has_no_nil(anti_nils);
     871           3 :                         anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
     872             :                 }
     873             :                 /* generate a count aggregation for the values not present in any of the partitions */
     874          98 :                 anti_rel = rel_select(sql->sa, anti_rel, anti_exp);
     875          98 :                 set_processed(anti_rel);
     876          98 :                 anti_rel = rel_groupby(sql, anti_rel, NULL);
     877          98 :                 aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_rel->card, 0);
     878          98 :                 (void) rel_groupby_add_aggr(sql, anti_rel, aggr);
     879          98 :                 set_processed(anti_rel);
     880          98 :                 exp_label(sql->sa, aggr, ++sql->label);
     881             : 
     882          98 :                 aggr = exp_ref(sql, aggr);
     883         196 :                 snprintf(buf, BUFSIZ, "%s: the %s violates the partition %s of values", operation, desc,
     884          98 :                                 isRangePartitionTable(t) ? "range (NB higher limit exclusive)" : "list");
     885             : 
     886          98 :                 sql_exp *exception = exp_exception(sql->sa, aggr, buf);
     887          98 :                 sel = rel_exception(query->sql->sa, sel, anti_rel, list_append(new_exp_list(query->sql->sa), exception));
     888             :         }
     889         100 :         rel_destroy(rel);
     890         100 :         return sel;
     891             : }
     892             : 
     893             : static sql_rel*
     894         102 : rel_propagate_insert(sql_query *query, sql_rel *rel, sql_table *t, int *changes)
     895             : {
     896         102 :         return rel_generate_subinserts(query, rel, t, changes, "INSERT", "insert");
     897             : }
     898             : 
     899             : static sql_rel*
     900          21 : rel_propagate_delete(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
     901             : {
     902          21 :         return rel_generate_subdeletes(sql, rel, t, changes);
     903             : }
     904             : 
     905             : static bool
     906          11 : update_move_across_partitions(sql_rel *rel, sql_table *t)
     907             : {
     908          33 :         for (node *n = ((sql_rel*)rel->r)->exps->h; n; n = n->next) {
     909          22 :                 sql_exp* exp = (sql_exp*) n->data;
     910          22 :                 if (exp->type == e_column && exp->l && exp->r && !strcmp((char*)exp->l, t->base.name)) {
     911          11 :                         char* colname = (char*)exp->r;
     912             : 
     913          11 :                         if (isPartitionedByColumnTable(t)) {
     914           8 :                                 if (!strcmp(colname, t->part.pcol->base.name))
     915             :                                         return true;
     916           3 :                         } else if (isPartitionedByExpressionTable(t)) {
     917           6 :                                 for (node *nn = t->part.pexp->cols->h; nn; nn = nn->next) {
     918           3 :                                         int next = *(int*) nn->data;
     919           3 :                                         sql_column *col = find_sql_column(t, colname);
     920           3 :                                         if (col && next == col->colnr)
     921             :                                                 return true;
     922             :                                 }
     923             :                         } else {
     924           0 :                                 assert(0);
     925             :                         }
     926             :                 }
     927             :         }
     928             :         return false;
     929             : }
     930             : 
     931             : static sql_rel*
     932          11 : rel_propagate_update(mvc *sql, sql_rel *rel, sql_table *t, int *changes)
     933             : {
     934          11 :         bool found_partition_col = update_move_across_partitions(rel, t);
     935          11 :         sql_rel *sel = NULL;
     936             : 
     937          11 :         if (!found_partition_col) { /* easy scenario where the partitioned column is not being updated, just propagate */
     938          11 :                 sel = rel_generate_subupdates(sql, rel, t, changes);
     939             :         } else { /* harder scenario, has to insert and delete across partitions. */
     940             :                 /*sql_exp *exception = NULL;
     941             :                 sql_rel *inserts = NULL, *deletes = NULL, *anti_rel = NULL;
     942             : 
     943             :                 deletes = rel_generate_subdeletes(sql, rel, t, changes);
     944             :                 inserts = rel_generate_subinserts(query, rel, &anti_rel, &exception, t, changes, "UPDATE", "update");
     945             :                 inserts = rel_exception(sql->sa, inserts, anti_rel, list_append(new_exp_list(sql->sa), exception));
     946             :                 return rel_list(sql->sa, deletes, inserts);*/
     947           0 :                 assert(0);
     948             :         }
     949          11 :         return sel;
     950             : }
     951             : 
     952             : static sql_rel*
     953         106 : rel_subtable_insert(sql_query *query, sql_rel *rel, sql_table *t, int *changes)
     954             : {
     955         106 :         mvc *sql = query->sql;
     956         106 :         sql_part *upper = partition_find_part(sql->session->tr, t, NULL);
     957         106 :         if (!upper)
     958             :                 return NULL;
     959         106 :         sql_part *pt = upper;
     960         106 :         sql_rel *anti_dup = rel_create_common_relation(sql, rel, upper->t), *left = rel->l;
     961         106 :         if (!anti_dup)
     962             :                 return NULL;
     963          60 :         sql_exp *anti_exp = NULL, *anti_le = rel_generate_anti_insert_expression(sql, &anti_dup, upper->t), *aggr = NULL,
     964          60 :                         *exception = NULL, *anti_nils = NULL;
     965          60 :         list *anti_exps = new_exp_list(sql->sa);
     966          60 :         sql_subfunc *cf = sql_bind_func(sql, "sys", "count", sql_bind_localtype("void"), NULL, F_AGGR, true, true);
     967          60 :         char buf[BUFSIZ];
     968          60 :         bool found_nils = false, found_all_range_values = false;
     969          60 :         sql_subtype tp;
     970             : 
     971          60 :         find_partition_type(&tp, upper->t);
     972          60 :         if (isRangePartitionTable(upper->t)) {
     973          37 :                 int tpe = tp.type->localtype;
     974          37 :                 int (*atomcmp)(const void *, const void *) = ATOMcompare(tpe);
     975          37 :                 const void *nil = ATOMnilptr(tpe);
     976             : 
     977          37 :                 if (pt->with_nills == true || is_bit_nil(pt->with_nills))
     978             :                         found_nils = true;
     979             : 
     980          37 :                 if (atomcmp(pt->part.range.minvalue, nil) == 0) {
     981           7 :                         if (atomcmp(pt->part.range.maxvalue, nil) == 0) {
     982           7 :                                 found_all_range_values = pt->with_nills != 1;
     983           7 :                                 if (pt->with_nills == true) {
     984           4 :                                         anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
     985           4 :                                         set_has_no_nil(anti_nils);
     986           4 :                                         anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
     987             :                                 }
     988             :                         } else {
     989           0 :                                 sql_exp *e2 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue));
     990           0 :                                 anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e2, cmp_gte);
     991             :                         }
     992             :                 } else {
     993          30 :                         if (atomcmp(pt->part.range.maxvalue, nil) == 0) {
     994           2 :                                 sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue));
     995           2 :                                 anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_lt);
     996             :                         } else {
     997          28 :                                 sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.minvalue));
     998          28 :                                 bool max_equal_min = ATOMcmp(tpe, pt->part.range.maxvalue, pt->part.range.minvalue) == 0;
     999             : 
    1000          28 :                                 if (max_equal_min) {
    1001           0 :                                         anti_exp = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_notequal);
    1002             :                                 } else {
    1003          28 :                                         sql_exp *e2 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, pt->part.range.maxvalue)),
    1004          28 :                                                 *range1 = exp_compare(sql->sa, exp_copy(sql, anti_le), e1, cmp_lt),
    1005          28 :                                                 *range2 = exp_compare(sql->sa, exp_copy(sql, anti_le), e2, cmp_gte);
    1006             : 
    1007          28 :                                         anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), range1),
    1008             :                                                         list_append(new_exp_list(sql->sa), range2), 0);
    1009             :                                 }
    1010             :                         }
    1011             :                 }
    1012          37 :                 if (!pt->with_nills) { /* handle the nulls case */
    1013          30 :                         anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
    1014          30 :                         set_has_no_nil(anti_nils);
    1015          30 :                         anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
    1016          30 :                         if (anti_exp)
    1017          28 :                                 anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
    1018             :                                                  list_append(new_exp_list(sql->sa), anti_nils), 0);
    1019             :                         else
    1020             :                                 anti_exp = anti_nils;
    1021             :                 }
    1022          23 :         } else if (isListPartitionTable(upper->t)) {
    1023          23 :                 if (list_length(pt->part.values)) { /* if the partition holds non-null values */
    1024          91 :                         for (node *n = pt->part.values->h ; n ; n = n->next) {
    1025          69 :                                 sql_part_value *next = (sql_part_value*) n->data;
    1026          69 :                                 sql_exp *e1 = exp_atom(sql->sa, atom_general_ptr(sql->sa, &tp, next->value));
    1027          69 :                                 list_append(anti_exps, exp_copy(sql, e1));
    1028             :                         }
    1029          22 :                         anti_exp = exp_in(sql->sa, exp_copy(sql, anti_le), anti_exps, cmp_notin);
    1030             : 
    1031          22 :                         if (!pt->with_nills) { /* handle the nulls case */
    1032          18 :                                 anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
    1033          18 :                                 set_has_no_nil(anti_nils);
    1034          18 :                                 anti_nils = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 1), cmp_equal);
    1035          18 :                                 anti_exp = exp_or(sql->sa, list_append(new_exp_list(sql->sa), anti_exp),
    1036             :                                                                   list_append(new_exp_list(sql->sa), anti_nils), 0);
    1037             :                         }
    1038             :                 } else {
    1039           1 :                         assert(pt->with_nills);
    1040           1 :                         anti_nils = rel_unop_(sql, anti_dup, exp_copy(sql, anti_le), "sys", "isnull", card_value);
    1041           1 :                         set_has_no_nil(anti_nils);
    1042           1 :                         anti_exp = exp_compare(sql->sa, anti_nils, exp_atom_bool(sql->sa, 0), cmp_equal);
    1043             :                 }
    1044             :         } else {
    1045           0 :                 assert(0);
    1046             :         }
    1047             : 
    1048          60 :         if (!found_all_range_values || !found_nils) {
    1049             :                 /* generate a count aggregation for the values not present in any of the partitions */
    1050          59 :                 anti_dup = rel_select(sql->sa, anti_dup, anti_exp);
    1051          59 :                 set_processed(anti_dup);
    1052          59 :                 anti_dup = rel_groupby(sql, anti_dup, NULL);
    1053          59 :                 aggr = exp_aggr(sql->sa, NULL, cf, 0, 0, anti_dup->card, 0);
    1054          59 :                 (void) rel_groupby_add_aggr(sql, anti_dup, aggr);
    1055          59 :                 exp_label(sql->sa, aggr, ++sql->label);
    1056          59 :                 set_processed(anti_dup);
    1057             : 
    1058             :                 /* generate the exception */
    1059          59 :                 aggr = exp_ref(sql, aggr);
    1060         118 :                 snprintf(buf, BUFSIZ, "INSERT: table %s.%s is part of merge table %s.%s and the insert violates the "
    1061          59 :                                 "partition %s of values", t->s->base.name, t->base.name, upper->t->s->base.name,
    1062          59 :                                 upper->t->base.name, isRangePartitionTable(upper->t) ? "range" : "list");
    1063          59 :                 exception = exp_exception(sql->sa, aggr, buf);
    1064             : 
    1065          59 :                 left->p = prop_create(sql->sa, PROP_USED, left->p);
    1066          59 :                 (*changes)++;
    1067             : 
    1068          59 :                 rel = rel_exception(sql->sa, rel, anti_dup, list_append(new_exp_list(sql->sa), exception));
    1069             :         }
    1070             :         return rel;
    1071             : }
    1072             : 
    1073             : static sql_rel*
    1074          60 : rel_find_propagate( sql_rel *rel)
    1075             : {
    1076          60 :         if (is_ddl(rel->op) && rel->flag == ddl_list)
    1077          59 :                         return rel->r;
    1078           1 :         if (is_ddl(rel->op) && rel->flag == ddl_exception)
    1079           0 :                         return rel->r;
    1080           1 :         assert(is_insert(rel->op));
    1081             :         return rel;
    1082             : }
    1083             : 
    1084             : sql_rel *
    1085        1113 : rel_propagate(sql_query *query, sql_rel *rel, int *changes)
    1086             : {
    1087        1113 :         mvc *sql = query->sql;
    1088        1113 :         bool isSubtable = false;
    1089        1113 :         sql_rel *l = rel->l, *propagate = rel;
    1090             : 
    1091        1113 :         if (l->op == op_basetable) {
    1092        1097 :                 sql_table *t = l->l;
    1093             : 
    1094        1097 :                 if (partition_find_part(sql->session->tr, t, NULL) && !find_prop(l->p, PROP_USED)) {
    1095         200 :                         isSubtable = true;
    1096         200 :                         if (is_insert(rel->op)) { /* insertion directly to sub-table (must do validation) */
    1097         106 :                                 sql_rel *nrel = rel_subtable_insert(query, rel, t, changes);
    1098         106 :                                 if (!nrel)
    1099             :                                         return rel;
    1100          60 :                                 rel = nrel;
    1101          60 :                                 propagate = rel_find_propagate(nrel);
    1102          60 :                                 isSubtable = (rel != propagate);
    1103             :                         }
    1104             :                 }
    1105        1051 :                 if (isMergeTable(t)) {
    1106         134 :                         assert(list_length(t->members));
    1107         134 :                         if (is_delete(propagate->op) || is_truncate(propagate->op)) { /* propagate deletions to the partitions */
    1108          21 :                                 rel = rel_propagate_delete(sql, rel, t, changes);
    1109         113 :                         } else if (isRangePartitionTable(t) || isListPartitionTable(t)) {
    1110         113 :                                 if (is_insert(propagate->op)) { /* on inserts create a selection for each partition */
    1111         102 :                                         if (isSubtable) {
    1112           2 :                                                 rel->r = rel_propagate_insert(query, propagate, t, changes);
    1113             :                                         } else {
    1114         100 :                                                 rel = rel_propagate_insert(query, rel, t, changes);
    1115             :                                         }
    1116          11 :                                 } else if (is_update(propagate->op)) { /* for updates propagate like in deletions */
    1117          11 :                                         rel = rel_propagate_update(sql, rel, t, changes);
    1118             :                                 } else {
    1119           0 :                                         assert(0);
    1120             :                                 }
    1121             :                         } else {
    1122           0 :                                 assert(0);
    1123             :                         }
    1124             :                 }
    1125             :         }
    1126             :         return rel;
    1127             : }

Generated by: LCOV version 1.14