diff options
Diffstat (limited to 'storage/innobase/eval')
-rw-r--r-- | storage/innobase/eval/eval0eval.cc | 643 | ||||
-rw-r--r-- | storage/innobase/eval/eval0proc.cc | 286 |
2 files changed, 929 insertions, 0 deletions
diff --git a/storage/innobase/eval/eval0eval.cc b/storage/innobase/eval/eval0eval.cc new file mode 100644 index 00000000..73ab113c --- /dev/null +++ b/storage/innobase/eval/eval0eval.cc @@ -0,0 +1,643 @@ +/***************************************************************************** + +Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 2021, MariaDB Corporation. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/**************************************************//** +@file eval/eval0eval.cc +SQL evaluator: evaluates simple data structures, like expressions, in +a query graph + +Created 12/29/1997 Heikki Tuuri +*******************************************************/ + +#include "eval0eval.h" +#include "data0data.h" +#include "row0sel.h" +#include "rem0cmp.h" + +/** Dummy adress used when we should allocate a buffer of size 0 in +eval_node_alloc_val_buf */ + +static byte eval_dummy; + +/************************************************************************* +Gets the like node from the node */ +UNIV_INLINE +que_node_t* +que_node_get_like_node( +/*===================*/ + /* out: next node in a list of nodes */ + que_node_t* node) /* in: node in a list */ +{ + return(((sym_node_t*) node)->like_node); +} + +/*****************************************************************//** +Allocate a buffer from global dynamic memory for a value of a que_node. +NOTE that this memory must be explicitly freed when the query graph is +freed. If the node already has an allocated buffer, that buffer is freed +here. NOTE that this is the only function where dynamic memory should be +allocated for a query node val field. +@return pointer to allocated buffer */ +byte* +eval_node_alloc_val_buf( +/*====================*/ + que_node_t* node, /*!< in: query graph node; sets the val field + data field to point to the new buffer, and + len field equal to size */ + ulint size) /*!< in: buffer size */ +{ + dfield_t* dfield; + byte* data; + + ut_ad(que_node_get_type(node) == QUE_NODE_SYMBOL + || que_node_get_type(node) == QUE_NODE_FUNC); + + dfield = que_node_get_val(node); + + data = static_cast<byte*>(dfield_get_data(dfield)); + + if (data != &eval_dummy) { + ut_free(data); + } + + if (size == 0) { + data = &eval_dummy; + } else { + data = static_cast<byte*>(ut_malloc_nokey(size)); + } + + que_node_set_val_buf_size(node, size); + + dfield_set_data(dfield, data, size); + + return(data); +} + +/*****************************************************************//** +Free the buffer from global dynamic memory for a value of a que_node, +if it has been allocated in the above function. The freeing for pushed +column values is done in sel_col_prefetch_buf_free. */ +void +eval_node_free_val_buf( +/*===================*/ + que_node_t* node) /*!< in: query graph node */ +{ + dfield_t* dfield; + byte* data; + + ut_ad(que_node_get_type(node) == QUE_NODE_SYMBOL + || que_node_get_type(node) == QUE_NODE_FUNC); + + dfield = que_node_get_val(node); + + data = static_cast<byte*>(dfield_get_data(dfield)); + + if (que_node_get_val_buf_size(node) > 0) { + ut_a(data); + + ut_free(data); + } +} + +/********************************************************************* +Evaluates a LIKE comparison node. +@return the result of the comparison */ +UNIV_INLINE +ibool +eval_cmp_like( +/*==========*/ + que_node_t* arg1, /* !< in: left operand */ + que_node_t* arg2) /* !< in: right operand */ +{ + ib_like_t op; + que_node_t* arg3; + que_node_t* arg4; + const dfield_t* dfield; + + arg3 = que_node_get_like_node(arg2); + + /* Get the comparison type operator */ + ut_a(arg3); + + dfield = que_node_get_val(arg3); + ut_ad(dtype_get_mtype(dfield_get_type(dfield)) == DATA_INT); + op = static_cast<ib_like_t>( + mach_read_from_4(static_cast<const byte*>( + dfield_get_data(dfield)))); + + switch (op) { + case IB_LIKE_PREFIX: + arg4 = que_node_get_next(arg3); + return(!cmp_dfield_dfield_like_prefix(que_node_get_val(arg1), + que_node_get_val(arg4))); + case IB_LIKE_EXACT: + return(!cmp_dfield_dfield(que_node_get_val(arg1), + que_node_get_val(arg2))); + } + + ut_error; + return(FALSE); +} + +/********************************************************************* +Evaluates a comparison node. +@return the result of the comparison */ +ibool +eval_cmp( +/*=====*/ + func_node_t* cmp_node) /*!< in: comparison node */ +{ + que_node_t* arg1; + que_node_t* arg2; + int res; + ibool val = FALSE; /* remove warning */ + + ut_ad(que_node_get_type(cmp_node) == QUE_NODE_FUNC); + + arg1 = cmp_node->args; + arg2 = que_node_get_next(arg1); + + switch (cmp_node->func) { + case '<': + case '=': + case '>': + case PARS_LE_TOKEN: + case PARS_NE_TOKEN: + case PARS_GE_TOKEN: + res = cmp_dfield_dfield( + que_node_get_val(arg1), que_node_get_val(arg2)); + + switch (cmp_node->func) { + case '<': + val = (res < 0); + break; + case '=': + val = (res == 0); + break; + case '>': + val = (res > 0); + break; + case PARS_LE_TOKEN: + val = (res <= 0); + break; + case PARS_NE_TOKEN: + val = (res != 0); + break; + case PARS_GE_TOKEN: + val = (res >= 0); + break; + } + break; + default: + val = eval_cmp_like(arg1, arg2); + break; + } + + eval_node_set_ibool_val(cmp_node, val); + + return(val); +} + +/*****************************************************************//** +Evaluates a logical operation node. */ +UNIV_INLINE +void +eval_logical( +/*=========*/ + func_node_t* logical_node) /*!< in: logical operation node */ +{ + que_node_t* arg1; + que_node_t* arg2; + ibool val1; + ibool val2 = 0; /* remove warning */ + ibool val = 0; /* remove warning */ + int func; + + ut_ad(que_node_get_type(logical_node) == QUE_NODE_FUNC); + + arg1 = logical_node->args; + arg2 = que_node_get_next(arg1); /* arg2 is NULL if func is 'NOT' */ + + val1 = eval_node_get_ibool_val(arg1); + + if (arg2) { + val2 = eval_node_get_ibool_val(arg2); + } + + func = logical_node->func; + + if (func == PARS_AND_TOKEN) { + val = val1 & val2; + } else if (func == PARS_OR_TOKEN) { + val = val1 | val2; + } else if (func == PARS_NOT_TOKEN) { + val = TRUE - val1; + } else { + ut_error; + } + + eval_node_set_ibool_val(logical_node, val); +} + +/*****************************************************************//** +Evaluates an arithmetic operation node. */ +UNIV_INLINE +void +eval_arith( +/*=======*/ + func_node_t* arith_node) /*!< in: arithmetic operation node */ +{ + que_node_t* arg1; + que_node_t* arg2; + lint val1; + lint val2 = 0; /* remove warning */ + lint val; + int func; + + ut_ad(que_node_get_type(arith_node) == QUE_NODE_FUNC); + + arg1 = arith_node->args; + arg2 = que_node_get_next(arg1); /* arg2 is NULL if func is unary '-' */ + + val1 = eval_node_get_int_val(arg1); + + if (arg2) { + val2 = eval_node_get_int_val(arg2); + } + + func = arith_node->func; + + if (func == '+') { + val = val1 + val2; + } else if ((func == '-') && arg2) { + val = val1 - val2; + } else if (func == '-') { + val = -val1; + } else if (func == '*') { + val = val1 * val2; + } else { + ut_ad(func == '/'); + val = val1 / val2; + } + + eval_node_set_int_val(arith_node, val); +} + +/*****************************************************************//** +Evaluates an aggregate operation node. */ +UNIV_INLINE +void +eval_aggregate( +/*===========*/ + func_node_t* node) /*!< in: aggregate operation node */ +{ + lint val; + + ut_ad(que_node_get_type(node) == QUE_NODE_FUNC); + + val = eval_node_get_int_val(node); + + ut_a(node->func == PARS_COUNT_TOKEN); + val = val + 1; + eval_node_set_int_val(node, val); +} + +/*****************************************************************//** +Evaluates a notfound-function node. */ +UNIV_INLINE +void +eval_notfound( +/*==========*/ + func_node_t* func_node) /*!< in: function node */ +{ + sym_node_t* cursor; + sel_node_t* sel_node; + ibool ibool_val; + + ut_ad(func_node->func == PARS_NOTFOUND_TOKEN); + + cursor = static_cast<sym_node_t*>(func_node->args); + + ut_ad(que_node_get_type(cursor) == QUE_NODE_SYMBOL); + + if (cursor->token_type == SYM_LIT) { + ut_ad(!memcmp(dfield_get_data(que_node_get_val(cursor)), + "SQL", 3)); + sel_node = cursor->sym_table->query_graph->last_sel_node; + } else { + sel_node = cursor->alias->cursor_def; + } + + if (sel_node->state == SEL_NODE_NO_MORE_ROWS) { + ibool_val = TRUE; + } else { + ibool_val = FALSE; + } + + eval_node_set_ibool_val(func_node, ibool_val); +} + +/*****************************************************************//** +Evaluates a substr-function node. */ +UNIV_INLINE +void +eval_substr( +/*========*/ + func_node_t* func_node) /*!< in: function node */ +{ + que_node_t* arg1; + que_node_t* arg2; + que_node_t* arg3; + dfield_t* dfield; + byte* str1; + ulint len1; + ulint len2; + + arg1 = func_node->args; + arg2 = que_node_get_next(arg1); + + ut_ad(func_node->func == PARS_SUBSTR_TOKEN); + + arg3 = que_node_get_next(arg2); + + str1 = static_cast<byte*>(dfield_get_data(que_node_get_val(arg1))); + + const ulint str1_len = dfield_get_len(que_node_get_val(arg1)); + + len1 = (ulint) eval_node_get_int_val(arg2); + len2 = (ulint) eval_node_get_int_val(arg3); + + dfield = que_node_get_val(func_node); + + if (len1 > str1_len) { + len2 = 0; + } else { + str1 += len1; + if (len2 > str1_len - len1) { + len2 = str1_len - len1; + } + } + + dfield_set_data(dfield, str1, len2); +} + +/*****************************************************************//** +Evaluates an instr-function node. */ +static +void +eval_instr( +/*=======*/ + func_node_t* func_node) /*!< in: function node */ +{ + que_node_t* arg1; + que_node_t* arg2; + dfield_t* dfield1; + dfield_t* dfield2; + lint int_val; + byte* str1; + byte* str2; + byte match_char; + ulint len1; + ulint len2; + ulint i; + ulint j; + + arg1 = func_node->args; + arg2 = que_node_get_next(arg1); + + dfield1 = que_node_get_val(arg1); + dfield2 = que_node_get_val(arg2); + + str1 = static_cast<byte*>(dfield_get_data(dfield1)); + str2 = static_cast<byte*>(dfield_get_data(dfield2)); + + len1 = dfield_get_len(dfield1); + len2 = dfield_get_len(dfield2); + + if (len2 == 0) { + ut_error; + } + + match_char = str2[0]; + + for (i = 0; i < len1; i++) { + /* In this outer loop, the number of matched characters is 0 */ + + if (str1[i] == match_char) { + + if (i + len2 > len1) { + + break; + } + + for (j = 1;; j++) { + /* We have already matched j characters */ + + if (j == len2) { + int_val = lint(i) + 1; + + goto match_found; + } + + if (str1[i + j] != str2[j]) { + + break; + } + } + } + } + + int_val = 0; + +match_found: + eval_node_set_int_val(func_node, int_val); +} + +/*****************************************************************//** +Evaluates a predefined function node. */ +static +void +eval_concat( +/*========*/ + func_node_t* func_node) /*!< in: function node */ +{ + que_node_t* arg; + dfield_t* dfield; + byte* data; + ulint len; + ulint len1; + + arg = func_node->args; + len = 0; + + while (arg) { + len1 = dfield_get_len(que_node_get_val(arg)); + + len += len1; + + arg = que_node_get_next(arg); + } + + data = eval_node_ensure_val_buf(func_node, len); + + arg = func_node->args; + len = 0; + + while (arg) { + dfield = que_node_get_val(arg); + len1 = dfield_get_len(dfield); + + memcpy(data + len, dfield_get_data(dfield), len1); + + len += len1; + + arg = que_node_get_next(arg); + } +} + +/*****************************************************************//** +Evaluates a predefined function node. If the first argument is an integer, +this function looks at the second argument which is the integer length in +bytes, and converts the integer to a VARCHAR. +If the first argument is of some other type, this function converts it to +BINARY. */ +UNIV_INLINE +void +eval_to_binary( +/*===========*/ + func_node_t* func_node) /*!< in: function node */ +{ + que_node_t* arg1; + que_node_t* arg2; + dfield_t* dfield; + byte* str1; + ulint len; + ulint len1; + + arg1 = func_node->args; + + str1 = static_cast<byte*>(dfield_get_data(que_node_get_val(arg1))); + + if (dtype_get_mtype(que_node_get_data_type(arg1)) != DATA_INT) { + + len = dfield_get_len(que_node_get_val(arg1)); + + dfield = que_node_get_val(func_node); + + dfield_set_data(dfield, str1, len); + + return; + } + + arg2 = que_node_get_next(arg1); + + len1 = (ulint) eval_node_get_int_val(arg2); + + if (len1 > 4) { + + ut_error; + } + + dfield = que_node_get_val(func_node); + + dfield_set_data(dfield, str1 + (4 - len1), len1); +} + +/*****************************************************************//** +Evaluate LENGTH(). */ +inline void eval_length(func_node_t* func_node) +{ + eval_node_set_int_val(func_node, + dfield_get_len(que_node_get_val + (func_node->args))); +} + +/*****************************************************************//** +Evaluates a function node. */ +void +eval_func( +/*======*/ + func_node_t* func_node) /*!< in: function node */ +{ + que_node_t* arg; + ulint fclass; + + ut_ad(que_node_get_type(func_node) == QUE_NODE_FUNC); + + fclass = func_node->fclass; + const int func = func_node->func; + + arg = func_node->args; + + /* Evaluate first the argument list */ + while (arg) { + eval_exp(arg); + + /* The functions are not defined for SQL null argument + values, except for eval_cmp and notfound */ + + if (dfield_is_null(que_node_get_val(arg)) + && (fclass != PARS_FUNC_CMP) + && (func != PARS_NOTFOUND_TOKEN)) { + ut_error; + } + + arg = que_node_get_next(arg); + } + + switch (fclass) { + case PARS_FUNC_CMP: + eval_cmp(func_node); + return; + case PARS_FUNC_ARITH: + eval_arith(func_node); + return; + case PARS_FUNC_AGGREGATE: + eval_aggregate(func_node); + return; + case PARS_FUNC_PREDEFINED: + switch (func) { + case PARS_NOTFOUND_TOKEN: + eval_notfound(func_node); + return; + case PARS_SUBSTR_TOKEN: + eval_substr(func_node); + return; + case PARS_INSTR_TOKEN: + eval_instr(func_node); + return; + case PARS_CONCAT_TOKEN: + eval_concat(func_node); + return; + case PARS_TO_BINARY_TOKEN: + eval_to_binary(func_node); + return; + case PARS_LENGTH_TOKEN: + eval_length(func_node); + return; + default: + ut_error; + } + case PARS_FUNC_LOGICAL: + eval_logical(func_node); + return; + } + + ut_error; +} diff --git a/storage/innobase/eval/eval0proc.cc b/storage/innobase/eval/eval0proc.cc new file mode 100644 index 00000000..7e39443f --- /dev/null +++ b/storage/innobase/eval/eval0proc.cc @@ -0,0 +1,286 @@ +/***************************************************************************** + +Copyright (c) 1998, 2016, Oracle and/or its affiliates. All Rights Reserved. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/**************************************************//** +@file eval/eval0proc.cc +Executes SQL stored procedures and their control structures + +Created 1/20/1998 Heikki Tuuri +*******************************************************/ + +#include "eval0proc.h" + +/**********************************************************************//** +Performs an execution step of an if-statement node. +@return query thread to run next or NULL */ +que_thr_t* +if_step( +/*====*/ + que_thr_t* thr) /*!< in: query thread */ +{ + if_node_t* node; + elsif_node_t* elsif_node; + + ut_ad(thr); + + node = static_cast<if_node_t*>(thr->run_node); + ut_ad(que_node_get_type(node) == QUE_NODE_IF); + + if (thr->prev_node == que_node_get_parent(node)) { + + /* Evaluate the condition */ + + eval_exp(node->cond); + + if (eval_node_get_ibool_val(node->cond)) { + + /* The condition evaluated to TRUE: start execution + from the first statement in the statement list */ + + thr->run_node = node->stat_list; + + } else if (node->else_part) { + thr->run_node = node->else_part; + + } else if (node->elsif_list) { + elsif_node = node->elsif_list; + + for (;;) { + eval_exp(elsif_node->cond); + + if (eval_node_get_ibool_val( + elsif_node->cond)) { + + /* The condition evaluated to TRUE: + start execution from the first + statement in the statement list */ + + thr->run_node = elsif_node->stat_list; + + break; + } + + elsif_node = static_cast<elsif_node_t*>( + que_node_get_next(elsif_node)); + + if (elsif_node == NULL) { + thr->run_node = NULL; + + break; + } + } + } else { + thr->run_node = NULL; + } + } else { + /* Move to the next statement */ + ut_ad(que_node_get_next(thr->prev_node) == NULL); + + thr->run_node = NULL; + } + + if (thr->run_node == NULL) { + thr->run_node = que_node_get_parent(node); + } + + return(thr); +} + +/**********************************************************************//** +Performs an execution step of a while-statement node. +@return query thread to run next or NULL */ +que_thr_t* +while_step( +/*=======*/ + que_thr_t* thr) /*!< in: query thread */ +{ + while_node_t* node; + + ut_ad(thr); + + node = static_cast<while_node_t*>(thr->run_node); + ut_ad(que_node_get_type(node) == QUE_NODE_WHILE); + + ut_ad((thr->prev_node == que_node_get_parent(node)) + || (que_node_get_next(thr->prev_node) == NULL)); + + /* Evaluate the condition */ + + eval_exp(node->cond); + + if (eval_node_get_ibool_val(node->cond)) { + + /* The condition evaluated to TRUE: start execution + from the first statement in the statement list */ + + thr->run_node = node->stat_list; + } else { + thr->run_node = que_node_get_parent(node); + } + + return(thr); +} + +/**********************************************************************//** +Performs an execution step of an assignment statement node. +@return query thread to run next or NULL */ +que_thr_t* +assign_step( +/*========*/ + que_thr_t* thr) /*!< in: query thread */ +{ + assign_node_t* node; + + ut_ad(thr); + + node = static_cast<assign_node_t*>(thr->run_node); + ut_ad(que_node_get_type(node) == QUE_NODE_ASSIGNMENT); + + /* Evaluate the value to assign */ + + eval_exp(node->val); + + eval_node_copy_val(node->var->alias, node->val); + + thr->run_node = que_node_get_parent(node); + + return(thr); +} + +/**********************************************************************//** +Performs an execution step of a for-loop node. +@return query thread to run next or NULL */ +que_thr_t* +for_step( +/*=====*/ + que_thr_t* thr) /*!< in: query thread */ +{ + for_node_t* node; + que_node_t* parent; + lint loop_var_value; + + ut_ad(thr); + + node = static_cast<for_node_t*>(thr->run_node); + + ut_ad(que_node_get_type(node) == QUE_NODE_FOR); + + parent = que_node_get_parent(node); + + if (thr->prev_node != parent) { + + /* Move to the next statement */ + thr->run_node = que_node_get_next(thr->prev_node); + + if (thr->run_node != NULL) { + + return(thr); + } + + /* Increment the value of loop_var */ + + loop_var_value = 1 + eval_node_get_int_val(node->loop_var); + } else { + /* Initialize the loop */ + + eval_exp(node->loop_start_limit); + eval_exp(node->loop_end_limit); + + loop_var_value = eval_node_get_int_val(node->loop_start_limit); + + node->loop_end_value + = (int) eval_node_get_int_val(node->loop_end_limit); + } + + /* Check if we should do another loop */ + + if (loop_var_value > node->loop_end_value) { + + /* Enough loops done */ + + thr->run_node = parent; + } else { + eval_node_set_int_val(node->loop_var, loop_var_value); + + thr->run_node = node->stat_list; + } + + return(thr); +} + +/**********************************************************************//** +Performs an execution step of an exit statement node. +@return query thread to run next or NULL */ +que_thr_t* +exit_step( +/*======*/ + que_thr_t* thr) /*!< in: query thread */ +{ + exit_node_t* node; + que_node_t* loop_node; + + ut_ad(thr); + + node = static_cast<exit_node_t*>(thr->run_node); + + ut_ad(que_node_get_type(node) == QUE_NODE_EXIT); + + /* Loops exit by setting thr->run_node as the loop node's parent, so + find our containing loop node and get its parent. */ + + loop_node = que_node_get_containing_loop_node(node); + + /* If someone uses an EXIT statement outside of a loop, this will + trigger. */ + ut_a(loop_node); + + thr->run_node = que_node_get_parent(loop_node); + + return(thr); +} + +/**********************************************************************//** +Performs an execution step of a return-statement node. +@return query thread to run next or NULL */ +que_thr_t* +return_step( +/*========*/ + que_thr_t* thr) /*!< in: query thread */ +{ + return_node_t* node; + que_node_t* parent; + + ut_ad(thr); + + node = static_cast<return_node_t*>(thr->run_node); + + ut_ad(que_node_get_type(node) == QUE_NODE_RETURN); + + parent = node; + + while (que_node_get_type(parent) != QUE_NODE_PROC) { + + parent = que_node_get_parent(parent); + } + + ut_a(parent); + + thr->run_node = que_node_get_parent(parent); + + return(thr); +} |