LCOV - code coverage report
Current view: top level - sql/server - sql_partition.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 98 175 56.0 %
Date: 2024-04-25 20:03:45 Functions: 7 8 87.5 %

          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             : 
      15             : #include "sql_partition.h"
      16             : #include "rel_rel.h"
      17             : #include "rel_basetable.h"
      18             : #include "rel_exp.h"
      19             : #include "sql_mvc.h"
      20             : #include "sql_catalog.h"
      21             : #include "sql_relation.h"
      22             : #include "rel_updates.h"
      23             : #include "mal_exception.h"
      24             : 
      25             : list *
      26       14480 : partition_find_mergetables(mvc *sql, sql_table *t)
      27             : {
      28       14480 :         sql_trans *tr = sql->session->tr;
      29       14480 :         list *res = NULL;
      30       14480 :         sql_part *pt = NULL;
      31             : 
      32       43528 :         for (; t; t = pt?pt->t:NULL) {
      33       14524 :                 if ((pt=partition_find_part(tr, t, NULL))) {
      34          44 :                         if (!res)
      35          44 :                                 res = sa_list(sql->sa);
      36          44 :                         list_append(res, pt);
      37             :                 }
      38             :         }
      39       14480 :         return res;
      40             : }
      41             : 
      42             : static int
      43           4 : key_column_colnr(sql_kc *pkey)
      44             : {
      45           4 :         return pkey->c->colnr;
      46             : }
      47             : 
      48             : static int
      49           4 : table_column_colnr(int *colnr)
      50             : {
      51           4 :         return *colnr;
      52             : }
      53             : 
      54             : str
      55        6680 : sql_partition_validate_key(mvc *sql, sql_table *nt, sql_key *k, const char* op)
      56             : {
      57        6680 :         if (k->type != fkey) {
      58        5758 :                 const char *keys = (k->type == pkey) ? "primary" : "unique";
      59        5758 :                 assert(k->type == pkey || k->type == ukey);
      60             : 
      61        5758 :                 if (isPartitionedByColumnTable(nt)) {
      62           6 :                         assert(nt->part.pcol);
      63           6 :                         if (list_length(k->columns) != 1) {
      64           0 :                                 throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
      65           0 :                                           "columns must match the columns used in the partition definition", op, nt->s->base.name,
      66             :                                           nt->base.name, keys);
      67             :                         } else {
      68           6 :                                 sql_kc *kcol = k->columns->h->data;
      69           6 :                                 if (kcol->c->colnr != nt->part.pcol->colnr)
      70           3 :                                         throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
      71           3 :                                                   "columns must match the columns used in the partition definition", op, nt->s->base.name,
      72             :                                                   nt->base.name, keys);
      73             :                         }
      74        5752 :                 } else if (isPartitionedByExpressionTable(nt)) {
      75           4 :                         list *kcols, *pcols;
      76           4 :                         sql_allocator *p1, *p2;
      77             : 
      78           4 :                         assert(nt->part.pexp->cols);
      79           4 :                         if (list_length(k->columns) != list_length(nt->part.pexp->cols))
      80           1 :                                 throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
      81           1 :                                           "columns must match the columns used in the partition definition", op, nt->s->base.name,
      82             :                                           nt->base.name, keys);
      83             : 
      84           3 :                         p1 = k->columns->sa; /* save the original sql allocators */
      85           3 :                         p2 = nt->part.pexp->cols->sa;
      86           3 :                         k->columns->sa = sql->sa;
      87           3 :                         nt->part.pexp->cols->sa = sql->sa;
      88           3 :                         kcols = list_sort(k->columns, (fkeyvalue)&key_column_colnr, NULL);
      89           3 :                         pcols = list_sort(nt->part.pexp->cols, (fkeyvalue)&table_column_colnr, NULL);
      90           3 :                         k->columns->sa = p1;
      91           3 :                         nt->part.pexp->cols->sa = p2;
      92             : 
      93           5 :                         for (node *nn = kcols->h, *mm = pcols->h; nn && mm; nn = nn->next, mm = mm->next) {
      94           4 :                                 sql_kc *kcol = nn->data;
      95           4 :                                 int *colnr = mm->data;
      96           4 :                                 if (kcol->c->colnr != *colnr)
      97           2 :                                         throw(SQL, "sql.partition", SQLSTATE(42000) "%s TABLE: %s.%s: in a partitioned table, the %s key's "
      98           2 :                                                   "columns must match the columns used in the partition definition", op, nt->s->base.name,
      99             :                                                   nt->base.name, keys);
     100             :                         }
     101             :                 }
     102             :         }
     103             :         return NULL;
     104             : }
     105             : 
     106             : static void exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols);
     107             : 
     108             : static void
     109           0 : rel_find_table_columns(mvc* sql, sql_rel* rel, sql_table *t, list *cols)
     110             : {
     111           0 :         if (mvc_highwater(sql)) {
     112           0 :                 (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     113           0 :                 return;
     114             :         }
     115             : 
     116           0 :         if (!rel)
     117             :                 return;
     118             : 
     119           0 :         if (rel->exps)
     120           0 :                 for (node *n = rel->exps->h ; n ; n = n->next)
     121           0 :                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     122             : 
     123           0 :         switch (rel->op) {
     124             :                 case op_basetable:
     125             :                 case op_truncate:
     126             :                         break;
     127           0 :                 case op_table:
     128           0 :                         if (IS_TABLE_PROD_FUNC(rel->flag) || rel->flag == TABLE_FROM_RELATION) {
     129           0 :                                 if (rel->l)
     130             :                                         rel_find_table_columns(sql, rel->l, t, cols);
     131             :                         }
     132             :                         break;
     133           0 :                 case op_join:
     134             :                 case op_left:
     135             :                 case op_right:
     136             :                 case op_full:
     137             :                 case op_union:
     138             :                 case op_inter:
     139             :                 case op_except:
     140             :                 case op_merge:
     141           0 :                         if (rel->l)
     142           0 :                                 rel_find_table_columns(sql, rel->l, t, cols);
     143           0 :                         if (rel->r)
     144             :                                 rel_find_table_columns(sql, rel->r, t, cols);
     145             :                         break;
     146           0 :                 case op_semi:
     147             :                 case op_anti:
     148             :                 case op_groupby:
     149             :                 case op_project:
     150             :                 case op_select:
     151             :                 case op_topn:
     152             :                 case op_sample:
     153           0 :                         if (rel->l)
     154             :                                 rel_find_table_columns(sql, rel->l, t, cols);
     155             :                         break;
     156           0 :                 case op_insert:
     157             :                 case op_update:
     158             :                 case op_delete:
     159           0 :                         if (rel->r)
     160             :                                 rel_find_table_columns(sql, rel->r, t, cols);
     161             :                         break;
     162           0 :                 case op_ddl:
     163           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) {
     164           0 :                                 if (rel->l)
     165             :                                         rel_find_table_columns(sql, rel->l, t, cols);
     166             :                         } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
     167           0 :                                 if (rel->l)
     168           0 :                                         rel_find_table_columns(sql, rel->l, t, cols);
     169           0 :                                 if (rel->r)
     170             :                                         rel_find_table_columns(sql, rel->r, t, cols);
     171             :                         }
     172             :                         break;
     173             :         }
     174             : }
     175             : 
     176             : static void
     177          72 : exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols)
     178             : {
     179          90 :         if (mvc_highwater(sql)) {
     180           0 :                 (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     181           0 :                 return;
     182             :         }
     183             : 
     184          90 :         if (!e)
     185             :                 return;
     186          90 :         switch (e->type) {
     187           0 :                 case e_psm: {
     188           0 :                         if (e->flag & PSM_RETURN) {
     189           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     190           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     191           0 :                         } else if (e->flag & PSM_WHILE) {
     192           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     193           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     194           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     195           0 :                         } else if (e->flag & PSM_IF) {
     196           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     197           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     198           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     199           0 :                                 if (e->f)
     200           0 :                                         for (node *n = ((list*)e->f)->h ; n ; n = n->next)
     201           0 :                                                 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     202           0 :                         } else if (e->flag & PSM_REL) {
     203           0 :                                 rel_find_table_columns(sql, e->l, t, cols);
     204           0 :                         } else if (e->flag & PSM_EXCEPTION) {
     205           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     206             :                         }
     207             :                 } break;
     208          18 :                 case e_convert: {
     209          18 :                         exp_find_table_columns(sql, e->l, t, cols);
     210          18 :                 } break;
     211          17 :                 case e_atom: {
     212          17 :                         if (e->f)
     213           0 :                                 for (node *n = ((list*)e->f)->h ; n ; n = n->next)
     214           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     215             :                 } break;
     216          28 :                 case e_aggr:
     217             :                 case e_func: {
     218          28 :                         if (e->l)
     219          78 :                                 for (node *n = ((list*)e->l)->h ; n ; n = n->next)
     220          50 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     221          28 :                         if (e->type == e_func && e->r)
     222           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     223           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     224             :                 } break;
     225          27 :                 case e_column: {
     226          27 :                         if (!strcmp(e->l, t->base.name)) {
     227          27 :                                 sql_column *col = find_sql_column(t, e->r);
     228          27 :                                 if (col) {
     229          27 :                                         int *cnr = SA_NEW(cols->sa, int);
     230          27 :                                         *cnr = col->colnr;
     231          27 :                                         list_append(cols, cnr);
     232             :                                 }
     233             :                         }
     234             :                 } break;
     235           0 :                 case e_cmp: {
     236           0 :                         if (e->flag == cmp_in || e->flag == cmp_notin) {
     237           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     238           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     239           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     240           0 :                         } else if (e->flag == cmp_or || e->flag == cmp_filter) {
     241           0 :                                 for (node *n = ((list*)e->l)->h ; n ; n = n->next)
     242           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     243           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     244           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     245             :                         } else {
     246           0 :                                 if (e->l)
     247           0 :                                         exp_find_table_columns(sql, e->l, t, cols);
     248           0 :                                 if (e->r)
     249           0 :                                         exp_find_table_columns(sql, e->r, t, cols);
     250           0 :                                 if (e->f)
     251             :                                         exp_find_table_columns(sql, e->f, t, cols);
     252             :                         }
     253             :                 } break;
     254             :         }
     255             : }
     256             : 
     257             : str
     258          25 : bootstrap_partition_expression(mvc *sql, sql_table *mt, int instantiate)
     259             : {
     260          25 :         sql_exp *exp;
     261          25 :         char *query, *msg = NULL;
     262          25 :         sql_class sql_ec;
     263          25 :         sql_rel *r;
     264             : 
     265          25 :         assert(isPartitionedByExpressionTable(mt));
     266             : 
     267          25 :         if (sql->emode == m_prepare)
     268           0 :                 throw(SQL,"sql.partition", SQLSTATE(42000) "Partition expressions not compilable with prepared statements");
     269             : 
     270          25 :         r = rel_basetable(sql, mt, mt->base.name);
     271          25 :         query = mt->part.pexp->exp;
     272          25 :         if (!(exp = rel_parse_val(sql, mt->s, query, NULL, sql->emode, r))) {
     273           3 :                 if (*sql->errstr) {
     274           3 :                         if (strlen(sql->errstr) > 6 && sql->errstr[5] == '!')
     275           3 :                                 throw(SQL, "sql.partition", "%s", sql->errstr);
     276             :                         else
     277           0 :                                 throw(SQL, "sql.partition", SQLSTATE(42000) "%s", sql->errstr);
     278             :                 }
     279           0 :                 throw(SQL,"sql.partition", SQLSTATE(42000) "Incorrect expression '%s'", query);
     280             :         }
     281             : 
     282          22 :         assert(mt->part.pexp->cols);
     283          22 :         exp_find_table_columns(sql, exp, mt, mt->part.pexp->cols);
     284             : 
     285          22 :         mt->part.pexp->type = *exp_subtype(exp);
     286          22 :         sql_ec = mt->part.pexp->type.type->eclass;
     287          22 :         if (!(sql_ec == EC_BIT || EC_VARCHAR(sql_ec) || EC_TEMP(sql_ec) || sql_ec == EC_POS || sql_ec == EC_NUM ||
     288           0 :                  EC_INTERVAL(sql_ec)|| sql_ec == EC_DEC || sql_ec == EC_BLOB)) {
     289           0 :                 char *err = sql_subtype_string(sql->ta, &(mt->part.pexp->type));
     290           0 :                 if (!err) {
     291           0 :                         throw(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     292             :                 } else {
     293           0 :                         msg = createException(SQL, "sql.partition",
     294             :                                                                   SQLSTATE(42000) "Column type %s not supported for the expression return value", err);
     295             :                 }
     296             :         }
     297             : 
     298          22 :         if (instantiate && !msg) {
     299          19 :                 r = rel_project(sql->sa, r, NULL);
     300          19 :                 sql_rel *base = r->l, *nr = r;
     301          19 :                 r->l = NULL; /* omit table from list of dependencies */
     302          19 :                 (void) rel_project_add_exp(sql, r, exp);
     303             : 
     304          19 :                 nr = sql_processrelation(sql, nr, 0, 0, 0, 0);
     305          19 :                 if (nr) {
     306          19 :                         list *blist = rel_dependencies(sql, nr);
     307          19 :                         if (mvc_create_dependencies(sql, blist, mt->base.id, FUNC_DEPENDENCY))
     308           0 :                                 msg = createException(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     309             :                 }
     310          19 :                 r->l = base;
     311             :         }
     312             : 
     313             :         return msg;
     314             : }
     315             : 
     316             : str
     317          45 : parse_sql_parts(mvc *sql, sql_table *mt)
     318             : {
     319          45 :         str res = NULL;
     320             : 
     321          45 :         if (isPartitionedByExpressionTable(mt) && (res = bootstrap_partition_expression(sql, mt, 0)) != NULL)
     322             :                 return res;
     323             :         return res;
     324             : }

Generated by: LCOV version 1.14