LCOV - code coverage report
Current view: top level - sql/server - sql_partition.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 147 231 63.6 %
Date: 2021-10-13 02:24:04 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : #include "monetdb_config.h"
      10             : 
      11             : #include "sql_partition.h"
      12             : #include "rel_rel.h"
      13             : #include "rel_basetable.h"
      14             : #include "rel_exp.h"
      15             : #include "sql_mvc.h"
      16             : #include "sql_catalog.h"
      17             : #include "sql_relation.h"
      18             : #include "rel_unnest.h"
      19             : #include "rel_optimizer.h"
      20             : #include "rel_updates.h"
      21             : #include "mal_exception.h"
      22             : 
      23             : list *
      24       10023 : partition_find_mergetables(mvc *sql, sql_table *t)
      25             : {
      26       10023 :         sql_trans *tr = sql->session->tr;
      27             :         list *res = NULL;
      28             :         sql_part *pt = NULL;
      29             : 
      30       30159 :         for (; t; t = pt?pt->t:NULL) {
      31       10068 :                 if ((pt=partition_find_part(tr, t, NULL))) {
      32          45 :                         if (!res)
      33          45 :                                 res = sa_list(sql->sa);
      34          45 :                         list_append(res, pt);
      35             :                 }
      36             :         }
      37       10023 :         return res;
      38             : }
      39             : 
      40             : static int
      41           4 : key_column_colnr(sql_kc *pkey)
      42             : {
      43           4 :         return pkey->c->colnr;
      44             : }
      45             : 
      46             : static int
      47           4 : table_column_colnr(int *colnr)
      48             : {
      49           4 :         return *colnr;
      50             : }
      51             : 
      52             : str
      53        5694 : sql_partition_validate_key(mvc *sql, sql_table *nt, sql_key *k, const char* op)
      54             : {
      55        5694 :         if (k->type != fkey) {
      56        4784 :                 const char *keys = (k->type == pkey) ? "primary" : "unique";
      57        4784 :                 assert(k->type == pkey || k->type == ukey);
      58             : 
      59        4784 :                 if (isPartitionedByColumnTable(nt)) {
      60           6 :                         assert(nt->part.pcol);
      61           6 :                         if (list_length(k->columns) != 1) {
      62           0 :                                 throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
      63           0 :                                           "columns must match the columns used in the partition definition", op, nt->s->base.name,
      64             :                                           nt->base.name, keys);
      65             :                         } else {
      66           6 :                                 sql_kc *kcol = k->columns->h->data;
      67           6 :                                 if (kcol->c->colnr != nt->part.pcol->colnr)
      68           3 :                                         throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
      69           3 :                                                   "columns must match the columns used in the partition definition", op, nt->s->base.name,
      70             :                                                   nt->base.name, keys);
      71             :                         }
      72        4778 :                 } else if (isPartitionedByExpressionTable(nt)) {
      73             :                         list *kcols, *pcols;
      74             :                         sql_allocator *p1, *p2;
      75             : 
      76           4 :                         assert(nt->part.pexp->cols);
      77           4 :                         if (list_length(k->columns) != list_length(nt->part.pexp->cols))
      78           1 :                                 throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
      79           1 :                                           "columns must match the columns used in the partition definition", op, nt->s->base.name,
      80             :                                           nt->base.name, keys);
      81             : 
      82           3 :                         p1 = k->columns->sa; /* save the original sql allocators */
      83           3 :                         p2 = nt->part.pexp->cols->sa;
      84           3 :                         k->columns->sa = sql->sa;
      85           3 :                         nt->part.pexp->cols->sa = sql->sa;
      86           3 :                         kcols = list_sort(k->columns, (fkeyvalue)&key_column_colnr, NULL);
      87           3 :                         pcols = list_sort(nt->part.pexp->cols, (fkeyvalue)&table_column_colnr, NULL);
      88           3 :                         k->columns->sa = p1;
      89           3 :                         nt->part.pexp->cols->sa = p2;
      90             : 
      91           5 :                         for (node *nn = kcols->h, *mm = pcols->h; nn && mm; nn = nn->next, mm = mm->next) {
      92           4 :                                 sql_kc *kcol = nn->data;
      93           4 :                                 int *colnr = mm->data;
      94           4 :                                 if (kcol->c->colnr != *colnr)
      95           2 :                                         throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
      96           2 :                                                   "columns must match the columns used in the partition definition", op, nt->s->base.name,
      97             :                                                   nt->base.name, keys);
      98             :                         }
      99             :                 }
     100             :         }
     101             :         return NULL;
     102             : }
     103             : 
     104             : static void exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols);
     105             : 
     106             : static void
     107           0 : rel_find_table_columns(mvc* sql, sql_rel* rel, sql_table *t, list *cols)
     108             : {
     109           0 :         if (THRhighwater()) {
     110           0 :                 (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     111           0 :                 return;
     112             :         }
     113             : 
     114           0 :         if (!rel)
     115             :                 return;
     116             : 
     117           0 :         if (rel->exps)
     118           0 :                 for (node *n = rel->exps->h ; n ; n = n->next)
     119           0 :                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     120             : 
     121           0 :         switch (rel->op) {
     122             :                 case op_basetable:
     123             :                 case op_truncate:
     124             :                         break;
     125           0 :                 case op_table:
     126           0 :                         if (IS_TABLE_PROD_FUNC(rel->flag) || rel->flag == TABLE_FROM_RELATION) {
     127           0 :                                 if (rel->l)
     128             :                                         rel_find_table_columns(sql, rel->l, t, cols);
     129             :                         }
     130             :                         break;
     131           0 :                 case op_join:
     132             :                 case op_left:
     133             :                 case op_right:
     134             :                 case op_full:
     135             :                 case op_union:
     136             :                 case op_inter:
     137             :                 case op_except:
     138             :                 case op_merge:
     139           0 :                         if (rel->l)
     140           0 :                                 rel_find_table_columns(sql, rel->l, t, cols);
     141           0 :                         if (rel->r)
     142             :                                 rel_find_table_columns(sql, rel->r, t, cols);
     143             :                         break;
     144           0 :                 case op_semi:
     145             :                 case op_anti:
     146             :                 case op_groupby:
     147             :                 case op_project:
     148             :                 case op_select:
     149             :                 case op_topn:
     150             :                 case op_sample:
     151           0 :                         if (rel->l)
     152             :                                 rel_find_table_columns(sql, rel->l, t, cols);
     153             :                         break;
     154           0 :                 case op_insert:
     155             :                 case op_update:
     156             :                 case op_delete:
     157           0 :                         if (rel->r)
     158             :                                 rel_find_table_columns(sql, rel->r, t, cols);
     159             :                         break;
     160           0 :                 case op_ddl:
     161           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) {
     162           0 :                                 if (rel->l)
     163             :                                         rel_find_table_columns(sql, rel->l, t, cols);
     164           0 :                         } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
     165           0 :                                 if (rel->l)
     166           0 :                                         rel_find_table_columns(sql, rel->l, t, cols);
     167           0 :                                 if (rel->r)
     168             :                                         rel_find_table_columns(sql, rel->r, t, cols);
     169             :                         }
     170             :                         break;
     171             :         }
     172           0 : }
     173             : 
     174             : static void
     175          72 : exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols)
     176             : {
     177          90 :         if (THRhighwater()) {
     178           0 :                 (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     179           0 :                 return;
     180             :         }
     181             : 
     182          90 :         if (!e)
     183             :                 return;
     184          90 :         switch (e->type) {
     185           0 :                 case e_psm: {
     186           0 :                         if (e->flag & PSM_RETURN) {
     187           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     188           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     189           0 :                         } else if (e->flag & PSM_WHILE) {
     190           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     191           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     192           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     193           0 :                         } else if (e->flag & PSM_IF) {
     194           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     195           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     196           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     197           0 :                                 if (e->f)
     198           0 :                                         for (node *n = ((list*)e->f)->h ; n ; n = n->next)
     199           0 :                                                 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     200           0 :                         } else if (e->flag & PSM_REL) {
     201           0 :                                 rel_find_table_columns(sql, e->l, t, cols);
     202           0 :                         } else if (e->flag & PSM_EXCEPTION) {
     203           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     204             :                         }
     205             :                 } break;
     206          18 :                 case e_convert: {
     207          18 :                         exp_find_table_columns(sql, e->l, t, cols);
     208          18 :                 } break;
     209          17 :                 case e_atom: {
     210          17 :                         if (e->f)
     211           0 :                                 for (node *n = ((list*)e->f)->h ; n ; n = n->next)
     212           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     213             :                 } break;
     214          28 :                 case e_aggr:
     215             :                 case e_func: {
     216          28 :                         if (e->l)
     217          78 :                                 for (node *n = ((list*)e->l)->h ; n ; n = n->next)
     218          50 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     219          28 :                         if (e->type == e_func && e->r)
     220           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     221           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     222             :                 } break;
     223          27 :                 case e_column: {
     224          27 :                         if (!strcmp(e->l, t->base.name)) {
     225          27 :                                 sql_column *col = find_sql_column(t, e->r);
     226          27 :                                 if (col) {
     227          27 :                                         int *cnr = SA_NEW(cols->sa, int);
     228          27 :                                         *cnr = col->colnr;
     229          27 :                                         list_append(cols, cnr);
     230             :                                 }
     231             :                         }
     232             :                 } break;
     233           0 :                 case e_cmp: {
     234           0 :                         if (e->flag == cmp_in || e->flag == cmp_notin) {
     235           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     236           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     237           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     238           0 :                         } else if (e->flag == cmp_or || e->flag == cmp_filter) {
     239           0 :                                 for (node *n = ((list*)e->l)->h ; n ; n = n->next)
     240           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     241           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     242           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     243             :                         } else {
     244           0 :                                 if (e->l)
     245           0 :                                         exp_find_table_columns(sql, e->l, t, cols);
     246           0 :                                 if (e->r)
     247           0 :                                         exp_find_table_columns(sql, e->r, t, cols);
     248           0 :                                 if (e->f)
     249             :                                         exp_find_table_columns(sql, e->f, t, cols);
     250             :                         }
     251             :                 } break;
     252             :         }
     253          72 : }
     254             : 
     255             : str
     256          25 : bootstrap_partition_expression(mvc *sql, sql_table *mt, int instantiate)
     257             : {
     258             :         sql_exp *exp;
     259             :         char *query, *msg = NULL;
     260             :         sql_class sql_ec;
     261             :         sql_rel *r;
     262             : 
     263          25 :         assert(isPartitionedByExpressionTable(mt));
     264             : 
     265          25 :         if (sql->emode == m_prepare)
     266           0 :                 throw(SQL,"sql.partition", SQLSTATE(42000) "Partition expressions not compilable with prepared statements");
     267             : 
     268          25 :         r = rel_basetable(sql, mt, mt->base.name);
     269          25 :         query = mt->part.pexp->exp;
     270          25 :         if (!(exp = rel_parse_val(sql, mt->s, query, NULL, sql->emode, r))) {
     271           3 :                 if (*sql->errstr) {
     272           3 :                         if (strlen(sql->errstr) > 6 && sql->errstr[5] == '!')
     273           3 :                                 throw(SQL, "sql.partition", "%s", sql->errstr);
     274             :                         else
     275           0 :                                 throw(SQL, "sql.partition", SQLSTATE(42000) "%s", sql->errstr);
     276             :                 }
     277           0 :                 throw(SQL,"sql.partition", SQLSTATE(42000) "Incorrect expression '%s'", query);
     278             :         }
     279             : 
     280          22 :         assert(mt->part.pexp->cols);
     281          22 :         exp_find_table_columns(sql, exp, mt, mt->part.pexp->cols);
     282             : 
     283          22 :         mt->part.pexp->type = *exp_subtype(exp);
     284          22 :         sql_ec = mt->part.pexp->type.type->eclass;
     285          22 :         if (!(sql_ec == EC_BIT || EC_VARCHAR(sql_ec) || EC_TEMP(sql_ec) || sql_ec == EC_POS || sql_ec == EC_NUM ||
     286           0 :                  EC_INTERVAL(sql_ec)|| sql_ec == EC_DEC || sql_ec == EC_BLOB)) {
     287           0 :                 char *err = sql_subtype_string(sql->ta, &(mt->part.pexp->type));
     288           0 :                 if (!err) {
     289           0 :                         throw(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     290             :                 } else {
     291           0 :                         msg = createException(SQL, "sql.partition",
     292             :                                                                   SQLSTATE(42000) "Column type %s not supported for the expression return value", err);
     293             :                 }
     294             :         }
     295             : 
     296          22 :         if (instantiate && !msg) {
     297          19 :                 r = rel_project(sql->sa, r, NULL);
     298          19 :                 sql_rel *base = r->l, *nr = r;
     299          19 :                 r->l = NULL; /* omit table from list of dependencies */
     300          19 :                 (void) rel_project_add_exp(sql, r, exp);
     301             : 
     302          19 :                 nr = sql_processrelation(sql, nr, 0, 0);
     303          19 :                 if (nr) {
     304          19 :                         list *blist = rel_dependencies(sql, nr);
     305          19 :                         if (mvc_create_dependencies(sql, blist, mt->base.id, FUNC_DEPENDENCY))
     306           0 :                                 msg = createException(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     307             :                 }
     308          19 :                 r->l = base;
     309             :         }
     310             : 
     311             :         return msg;
     312             : }
     313             : 
     314             : void
     315         948 : find_partition_type(sql_subtype *tpe, sql_table *mt)
     316             : {
     317         948 :         if (isPartitionedByColumnTable(mt)) {
     318         832 :                 *tpe = mt->part.pcol->type;
     319         116 :         } else if (isPartitionedByExpressionTable(mt)) {
     320         116 :                 *tpe = mt->part.pexp->type;
     321             :         } else {
     322           0 :                 assert(0);
     323             :         }
     324         948 : }
     325             : 
     326             : str
     327          45 : initialize_sql_parts(mvc *sql, sql_table *mt)
     328             : {
     329             :         str res = NULL;
     330             :         sql_subtype found;
     331             :         int localtype;
     332          45 :         sql_trans *tr = sql->session->tr;
     333             : 
     334          45 :         if (isPartitionedByExpressionTable(mt) && (res = bootstrap_partition_expression(sql, mt, 0)) != NULL)
     335             :                 return res;
     336             : 
     337          45 :         find_partition_type(&found, mt);
     338          45 :         localtype = found.type->localtype;
     339             : 
     340          45 :         if (localtype != TYPE_str && mt->members && list_length(mt->members)) {
     341         144 :                 for (node *n = mt->members->h; n; n = n->next) {
     342          99 :                         sql_part *p = n->data;
     343             : 
     344          99 :                         if (isListPartitionTable(mt)) {
     345         129 :                                 for (node *m = p->part.values->h; m; m = m->next) {
     346          96 :                                         sql_part_value *v = (sql_part_value*) m->data, ov = *v;
     347             :                                         ValRecord vvalue;
     348             :                                         ptr ok;
     349             : 
     350          96 :                                         vvalue = (ValRecord) {.vtype = TYPE_void,};
     351          96 :                                         ok = VALinit(&vvalue, TYPE_str, v->value);
     352          96 :                                         if (ok)
     353          96 :                                                 ok = VALconvert(localtype, &vvalue);
     354          96 :                                         if (ok) {
     355          96 :                                                 v->value = SA_NEW_ARRAY(tr->sa, char, vvalue.len);
     356          96 :                                                 memcpy(v->value, VALget(&vvalue), vvalue.len);
     357          96 :                                                 v->length = vvalue.len;
     358             :                                         }
     359          96 :                                         VALclear(&vvalue);
     360          96 :                                         if (!ok) {
     361           0 :                                                 res = createException(SQL, "sql.partition",
     362             :                                                                                           SQLSTATE(42000) "Internal error while bootstrapping partitioned tables");
     363           0 :                                                 return res;
     364             :                                         }
     365          96 :                                         _DELETE(ov.value);
     366             :                                 }
     367          66 :                         } else if (isRangePartitionTable(mt)) {
     368             :                                 ValRecord vmin, vmax;
     369             :                                 ptr ok;
     370             : 
     371          66 :                                 vmin = vmax = (ValRecord) {.vtype = TYPE_void,};
     372          66 :                                 ok = VALinit(&vmin, TYPE_str, p->part.range.minvalue);
     373          66 :                                 if (ok)
     374          66 :                                         ok = VALinit(&vmax, TYPE_str, p->part.range.maxvalue);
     375          66 :                                 _DELETE(p->part.range.minvalue);
     376          66 :                                 _DELETE(p->part.range.maxvalue);
     377          66 :                                 if (ok) {
     378         174 :                                         if (strNil((const char *)VALget(&vmin)) &&
     379          42 :                                                 strNil((const char *)VALget(&vmax))) {
     380          32 :                                                 const void *nil_ptr = ATOMnilptr(localtype);
     381          32 :                                                 size_t nil_len = ATOMlen(localtype, nil_ptr);
     382             : 
     383          32 :                                                 p->part.range.minvalue = SA_NEW_ARRAY(tr->sa, char, nil_len);
     384          32 :                                                 p->part.range.maxvalue = SA_NEW_ARRAY(tr->sa, char, nil_len);
     385          32 :                                                 memcpy(p->part.range.minvalue, nil_ptr, nil_len);
     386          32 :                                                 memcpy(p->part.range.maxvalue, nil_ptr, nil_len);
     387          32 :                                                 p->part.range.minlength = nil_len;
     388          32 :                                                 p->part.range.maxlength = nil_len;
     389             :                                         } else {
     390          34 :                                                 ok = VALconvert(localtype, &vmin);
     391          34 :                                                 if (ok)
     392          34 :                                                         ok = VALconvert(localtype, &vmax);
     393          34 :                                                 if (ok) {
     394          34 :                                                         p->part.range.minvalue = SA_NEW_ARRAY(tr->sa, char, vmin.len);
     395          34 :                                                         p->part.range.maxvalue = SA_NEW_ARRAY(tr->sa, char, vmax.len);
     396          34 :                                                         memcpy(p->part.range.minvalue, VALget(&vmin), vmin.len);
     397          34 :                                                         memcpy(p->part.range.maxvalue, VALget(&vmax), vmax.len);
     398          34 :                                                         p->part.range.minlength = vmin.len;
     399          34 :                                                         p->part.range.maxlength = vmax.len;
     400             :                                                 }
     401             :                                         }
     402             :                                 }
     403          66 :                                 VALclear(&vmin);
     404          66 :                                 VALclear(&vmax);
     405          66 :                                 if (!ok) {
     406           0 :                                         res = createException(SQL, "sql.partition",
     407             :                                                                                   SQLSTATE(42000) "Internal error while bootstrapping partitioned tables");
     408           0 :                                         return res;
     409             :                                 }
     410             :                         }
     411             :                 }
     412             :         }
     413             :         return res;
     414             : }

Generated by: LCOV version 1.14