LCOV - code coverage report
Current view: top level - sql/server - sql_partition.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 98 178 55.1 %
Date: 2024-11-15 19:37: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       14953 : partition_find_mergetables(mvc *sql, sql_table *t)
      27             : {
      28       14953 :         sql_trans *tr = sql->session->tr;
      29       14953 :         list *res = NULL;
      30       14953 :         sql_part *pt = NULL;
      31             : 
      32       44949 :         for (; t; t = pt?pt->t:NULL) {
      33       14999 :                 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       14953 :         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        6858 : sql_partition_validate_key(mvc *sql, sql_table *nt, sql_key *k, const char* op)
      56             : {
      57        6858 :         if (k->type != fkey && k->type != ckey) {
      58        5901 :                 const char *keys = (k->type == pkey) ? "primary" : k->type == unndkey ? "nndunique" :  "unique";
      59        5901 :                 assert(k->type == pkey || k->type == ukey || k->type == unndkey);
      60             : 
      61        5901 :                 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        5895 :                 } else if (isPartitionedByExpressionTable(nt)) {
      75           4 :                         list *kcols, *pcols;
      76           4 :                         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_munion:
     147           0 :                         for (node *n = ((list*)rel->l)->h; n; n = n->next)
     148           0 :                                 rel_find_table_columns(sql, n->data, t, cols);
     149             :                         break;
     150           0 :                 case op_semi:
     151             :                 case op_anti:
     152             :                 case op_groupby:
     153             :                 case op_project:
     154             :                 case op_select:
     155             :                 case op_topn:
     156             :                 case op_sample:
     157           0 :                         if (rel->l)
     158             :                                 rel_find_table_columns(sql, rel->l, t, cols);
     159             :                         break;
     160           0 :                 case op_insert:
     161             :                 case op_update:
     162             :                 case op_delete:
     163           0 :                         if (rel->r)
     164             :                                 rel_find_table_columns(sql, rel->r, t, cols);
     165             :                         break;
     166           0 :                 case op_ddl:
     167           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) {
     168           0 :                                 if (rel->l)
     169             :                                         rel_find_table_columns(sql, rel->l, t, cols);
     170             :                         } else if (rel->flag == ddl_list || rel->flag == ddl_exception) {
     171           0 :                                 if (rel->l)
     172           0 :                                         rel_find_table_columns(sql, rel->l, t, cols);
     173           0 :                                 if (rel->r)
     174             :                                         rel_find_table_columns(sql, rel->r, t, cols);
     175             :                         }
     176             :                         break;
     177             :         }
     178             : }
     179             : 
     180             : static void
     181          90 : exp_find_table_columns(mvc *sql, sql_exp *e, sql_table *t, list *cols)
     182             : {
     183         100 :         if (mvc_highwater(sql)) {
     184           0 :                 (void) sql_error(sql, 10, SQLSTATE(42000) "Query too complex: running out of stack space");
     185           0 :                 return;
     186             :         }
     187             : 
     188         100 :         if (!e)
     189             :                 return;
     190         100 :         switch (e->type) {
     191           0 :                 case e_psm: {
     192           0 :                         if (e->flag & PSM_RETURN) {
     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_WHILE) {
     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 :                         } else if (e->flag & PSM_IF) {
     200           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     201           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     202           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     203           0 :                                 if (e->f)
     204           0 :                                         for (node *n = ((list*)e->f)->h ; n ; n = n->next)
     205           0 :                                                 exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     206           0 :                         } else if (e->flag & PSM_REL) {
     207           0 :                                 rel_find_table_columns(sql, e->l, t, cols);
     208           0 :                         } else if (e->flag & PSM_EXCEPTION) {
     209           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     210             :                         }
     211             :                 } break;
     212          10 :                 case e_convert: {
     213          10 :                         exp_find_table_columns(sql, e->l, t, cols);
     214          10 :                 } break;
     215          17 :                 case e_atom: {
     216          17 :                         if (e->f)
     217           0 :                                 for (node *n = ((list*)e->f)->h ; n ; n = n->next)
     218           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     219             :                 } break;
     220          37 :                 case e_aggr:
     221             :                 case e_func: {
     222          37 :                         if (e->l)
     223          96 :                                 for (node *n = ((list*)e->l)->h ; n ; n = n->next)
     224          59 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     225          37 :                         if (e->type == e_func && e->r)
     226           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     227           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     228             :                 } break;
     229          36 :                 case e_column: {
     230          36 :                         if (!strcmp(e->l, t->base.name)) {
     231          36 :                                 sql_column *col = find_sql_column(t, e->r);
     232          36 :                                 if (col) {
     233          36 :                                         int *cnr = SA_NEW(cols->sa, int);
     234          36 :                                         *cnr = col->colnr;
     235          36 :                                         list_append(cols, cnr);
     236             :                                 }
     237             :                         }
     238             :                 } break;
     239           0 :                 case e_cmp: {
     240           0 :                         if (e->flag == cmp_in || e->flag == cmp_notin) {
     241           0 :                                 exp_find_table_columns(sql, e->l, t, cols);
     242           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     243           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     244           0 :                         } else if (e->flag == cmp_or || e->flag == cmp_filter) {
     245           0 :                                 for (node *n = ((list*)e->l)->h ; n ; n = n->next)
     246           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     247           0 :                                 for (node *n = ((list*)e->r)->h ; n ; n = n->next)
     248           0 :                                         exp_find_table_columns(sql, (sql_exp*) n->data, t, cols);
     249             :                         } else {
     250           0 :                                 if (e->l)
     251           0 :                                         exp_find_table_columns(sql, e->l, t, cols);
     252           0 :                                 if (e->r)
     253           0 :                                         exp_find_table_columns(sql, e->r, t, cols);
     254           0 :                                 if (e->f)
     255             :                                         exp_find_table_columns(sql, e->f, t, cols);
     256             :                         }
     257             :                 } break;
     258             :         }
     259             : }
     260             : 
     261             : str
     262          34 : bootstrap_partition_expression(mvc *sql, sql_table *mt, int instantiate)
     263             : {
     264          34 :         sql_exp *exp;
     265          34 :         char *query, *msg = NULL;
     266          34 :         sql_class sql_ec;
     267          34 :         sql_rel *r;
     268             : 
     269          34 :         assert(isPartitionedByExpressionTable(mt));
     270             : 
     271          34 :         if (sql->emode == m_prepare)
     272           0 :                 throw(SQL,"sql.partition", SQLSTATE(42000) "Partition expressions not compilable with prepared statements");
     273             : 
     274          34 :         r = rel_basetable(sql, mt, mt->base.name);
     275          34 :         query = mt->part.pexp->exp;
     276          34 :         if (!(exp = rel_parse_val(sql, mt->s, query, NULL, sql->emode, r))) {
     277           3 :                 if (*sql->errstr) {
     278           3 :                         if (strlen(sql->errstr) > 6 && sql->errstr[5] == '!')
     279           3 :                                 throw(SQL, "sql.partition", "%s", sql->errstr);
     280             :                         else
     281           0 :                                 throw(SQL, "sql.partition", SQLSTATE(42000) "%s", sql->errstr);
     282             :                 }
     283           0 :                 throw(SQL,"sql.partition", SQLSTATE(42000) "Incorrect expression '%s'", query);
     284             :         }
     285             : 
     286          31 :         assert(mt->part.pexp->cols);
     287          31 :         exp_find_table_columns(sql, exp, mt, mt->part.pexp->cols);
     288             : 
     289          31 :         mt->part.pexp->type = *exp_subtype(exp);
     290          31 :         sql_ec = mt->part.pexp->type.type->eclass;
     291          31 :         if (!(sql_ec == EC_BIT || EC_VARCHAR(sql_ec) || EC_TEMP(sql_ec) || sql_ec == EC_POS || sql_ec == EC_NUM ||
     292           0 :                  EC_INTERVAL(sql_ec)|| sql_ec == EC_DEC || sql_ec == EC_BLOB)) {
     293           0 :                 char *err = sql_subtype_string(sql->ta, &(mt->part.pexp->type));
     294           0 :                 if (!err) {
     295           0 :                         throw(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     296             :                 } else {
     297           0 :                         msg = createException(SQL, "sql.partition",
     298             :                                                                   SQLSTATE(42000) "Column type %s not supported for the expression return value", err);
     299             :                 }
     300             :         }
     301             : 
     302          31 :         if (instantiate && !msg) {
     303          24 :                 r = rel_project(sql->sa, r, NULL);
     304          24 :                 sql_rel *base = r->l, *nr = r;
     305          24 :                 r->l = NULL; /* omit table from list of dependencies */
     306          24 :                 (void) rel_project_add_exp(sql, r, exp);
     307             : 
     308          24 :                 nr = sql_processrelation(sql, nr, 0, instantiate, 0, 0);
     309          24 :                 if (nr) {
     310          24 :                         list *blist = rel_dependencies(sql, nr);
     311          24 :                         if (mvc_create_dependencies(sql, blist, mt->base.id, FUNC_DEPENDENCY))
     312           0 :                                 msg = createException(SQL, "sql.partition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     313             :                 }
     314          24 :                 r->l = base;
     315             :         }
     316             : 
     317             :         return msg;
     318             : }
     319             : 
     320             : str
     321          33 : parse_sql_parts(mvc *sql, sql_table *mt)
     322             : {
     323          33 :         str res = NULL;
     324             : 
     325          33 :         if (isPartitionedByExpressionTable(mt) && (res = bootstrap_partition_expression(sql, mt, 0)) != NULL)
     326             :                 return res;
     327             :         return res;
     328             : }

Generated by: LCOV version 1.14