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

Generated by: LCOV version 1.14