/***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2018, 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 St, Fifth Floor, Boston, MA 02110-1335 USA *****************************************************************************/ /**************************************************//** @file pars/pars0pars.c SQL parser Created 11/19/1996 Heikki Tuuri *******************************************************/ /* Historical note: Innobase executed its first SQL string (CREATE TABLE) on 1/27/1998 */ #include "pars0pars.h" #include "row0sel.h" #include "row0ins.h" #include "row0upd.h" #include "dict0dict.h" #include "dict0mem.h" #include "dict0crea.h" #include "que0que.h" #include "pars0grm.h" #include "pars0opt.h" #include "data0data.h" #include "data0type.h" #include "trx0trx.h" #include "trx0roll.h" #include "eval0eval.h" /* Global variable used while parsing a single procedure or query : the code is NOT re-entrant */ sym_tab_t* pars_sym_tab_global; /* Global variables used to denote certain reserved words, used in constructing the parsing tree */ pars_res_word_t pars_to_binary_token = {PARS_TO_BINARY_TOKEN}; pars_res_word_t pars_substr_token = {PARS_SUBSTR_TOKEN}; pars_res_word_t pars_concat_token = {PARS_CONCAT_TOKEN}; pars_res_word_t pars_instr_token = {PARS_INSTR_TOKEN}; pars_res_word_t pars_length_token = {PARS_LENGTH_TOKEN}; pars_res_word_t pars_count_token = {PARS_COUNT_TOKEN}; pars_res_word_t pars_int_token = {PARS_INT_TOKEN}; pars_res_word_t pars_bigint_token = {PARS_BIGINT_TOKEN}; pars_res_word_t pars_char_token = {PARS_CHAR_TOKEN}; pars_res_word_t pars_update_token = {PARS_UPDATE_TOKEN}; pars_res_word_t pars_asc_token = {PARS_ASC_TOKEN}; pars_res_word_t pars_desc_token = {PARS_DESC_TOKEN}; pars_res_word_t pars_open_token = {PARS_OPEN_TOKEN}; pars_res_word_t pars_close_token = {PARS_CLOSE_TOKEN}; pars_res_word_t pars_share_token = {PARS_SHARE_TOKEN}; pars_res_word_t pars_unique_token = {PARS_UNIQUE_TOKEN}; pars_res_word_t pars_clustered_token = {PARS_CLUSTERED_TOKEN}; /** Global variable used to denote the '*' in SELECT * FROM.. */ ulint pars_star_denoter = 12345678; /******************************************************************** Get user function with the given name.*/ UNIV_INLINE pars_user_func_t* pars_info_lookup_user_func( /*=======================*/ /* out: user func, or NULL if not found */ pars_info_t* info, /* in: info struct */ const char* name) /* in: function name to find*/ { if (info && info->funcs) { ulint i; ib_vector_t* vec = info->funcs; for (i = 0; i < ib_vector_size(vec); i++) { pars_user_func_t* puf; puf = static_cast( ib_vector_get(vec, i)); if (strcmp(puf->name, name) == 0) { return(puf); } } } return(NULL); } /******************************************************************** Get bound identifier with the given name.*/ UNIV_INLINE pars_bound_id_t* pars_info_lookup_bound_id( /*======================*/ /* out: bound literal, or NULL if not found */ pars_info_t* info, /* in: info struct */ const char* name) /* in: bound literal name to find */ { if (info && info->bound_ids) { ulint i; ib_vector_t* vec = info->bound_ids; for (i = 0; i < ib_vector_size(vec); i++) { pars_bound_id_t* bid; bid = static_cast( ib_vector_get(vec, i)); if (strcmp(bid->name, name) == 0) { return(bid); } } } return(NULL); } /******************************************************************** Get bound literal with the given name.*/ UNIV_INLINE pars_bound_lit_t* pars_info_lookup_bound_lit( /*=======================*/ /* out: bound literal, or NULL if not found */ pars_info_t* info, /* in: info struct */ const char* name) /* in: bound literal name to find */ { if (info && info->bound_lits) { ulint i; ib_vector_t* vec = info->bound_lits; for (i = 0; i < ib_vector_size(vec); i++) { pars_bound_lit_t* pbl; pbl = static_cast( ib_vector_get(vec, i)); if (strcmp(pbl->name, name) == 0) { return(pbl); } } } return(NULL); } /*********************************************************************//** Determines the class of a function code. @return function class: PARS_FUNC_ARITH, ... */ static ulint pars_func_get_class( /*================*/ int func) /*!< in: function code: '=', PARS_GE_TOKEN, ... */ { switch (func) { case '+': case '-': case '*': case '/': return(PARS_FUNC_ARITH); case '=': case '<': case '>': case PARS_GE_TOKEN: case PARS_LE_TOKEN: case PARS_NE_TOKEN: return(PARS_FUNC_CMP); case PARS_AND_TOKEN: case PARS_OR_TOKEN: case PARS_NOT_TOKEN: return(PARS_FUNC_LOGICAL); case PARS_COUNT_TOKEN: return(PARS_FUNC_AGGREGATE); case PARS_TO_BINARY_TOKEN: case PARS_SUBSTR_TOKEN: case PARS_CONCAT_TOKEN: case PARS_LENGTH_TOKEN: case PARS_INSTR_TOKEN: case PARS_NOTFOUND_TOKEN: return(PARS_FUNC_PREDEFINED); default: return(PARS_FUNC_OTHER); } } /*********************************************************************//** Parses an operator or predefined function expression. @return own: function node in a query tree */ static func_node_t* pars_func_low( /*==========*/ int func, /*!< in: function token code */ que_node_t* arg) /*!< in: first argument in the argument list */ { func_node_t* node; node = static_cast( mem_heap_alloc(pars_sym_tab_global->heap, sizeof(func_node_t))); node->common.type = QUE_NODE_FUNC; dfield_set_data(&(node->common.val), NULL, 0); node->common.val_buf_size = 0; node->func = func; node->fclass = pars_func_get_class(func); node->args = arg; UT_LIST_ADD_LAST(pars_sym_tab_global->func_node_list, node); return(node); } /*********************************************************************//** Parses a function expression. @return own: function node in a query tree */ func_node_t* pars_func( /*======*/ que_node_t* res_word,/*!< in: function name reserved word */ que_node_t* arg) /*!< in: first argument in the argument list */ { return(pars_func_low(((pars_res_word_t*) res_word)->code, arg)); } /************************************************************************* Rebind a LIKE search string. NOTE: We ignore any '%' characters embedded within the search string.*/ int pars_like_rebind( /*=============*/ /* out, own: function node in a query tree */ sym_node_t* node, /* in: The search string node.*/ const byte* ptr, /* in: literal to (re) bind */ ulint ptr_len)/* in: length of literal to (re) bind*/ { dtype_t* dtype; dfield_t* dfield; ib_like_t op_check; sym_node_t* like_node; sym_node_t* str_node = NULL; ib_like_t op = IB_LIKE_EXACT; int func = PARS_LIKE_TOKEN_EXACT; /* Is this a STRING% ? */ if (ptr[ptr_len - 1] == '%') { op = IB_LIKE_PREFIX; } /* Is this a '%STRING' or %STRING% ?*/ ut_ad(*ptr != '%'); if (node->like_node == NULL) { /* Add the LIKE operator info node to the node list. This will be used during the comparison phase to determine how to match.*/ like_node = sym_tab_add_int_lit(node->sym_table, op); que_node_list_add_last(NULL, like_node); node->like_node = like_node; str_node = sym_tab_add_str_lit(node->sym_table, ptr, ptr_len); que_node_list_add_last(like_node, str_node); } else { like_node = node->like_node; /* Change the value of the string in the existing string node of like node */ str_node = static_cast( que_node_list_get_last(like_node)); /* Must find the string node */ ut_a(str_node); ut_a(str_node != like_node); ut_a(str_node->token_type == SYM_LIT); dfield = que_node_get_val(str_node); dfield_set_data(dfield, ptr, ptr_len); } dfield = que_node_get_val(like_node); dtype = dfield_get_type(dfield); ut_a(dtype_get_mtype(dtype) == DATA_INT); op_check = static_cast( mach_read_from_4(static_cast(dfield_get_data(dfield)))); switch (op_check) { case IB_LIKE_PREFIX: case IB_LIKE_EXACT: break; default: ut_error; } mach_write_to_4(static_cast(dfield_get_data(dfield)), op); dfield = que_node_get_val(node); /* Adjust the length of the search value so the '%' is not visible. Then create and add a search string node to the search value node. Searching for %SUFFIX and %SUBSTR% requires a full table scan and so we set the search value to ''. For PREFIX% we simply remove the trailing '%'.*/ switch (op) { case IB_LIKE_EXACT: dfield = que_node_get_val(str_node); dtype = dfield_get_type(dfield); ut_a(dtype_get_mtype(dtype) == DATA_VARCHAR); dfield_set_data(dfield, ptr, ptr_len); break; case IB_LIKE_PREFIX: func = PARS_LIKE_TOKEN_PREFIX; /* Modify the original node */ dfield_set_len(dfield, ptr_len - 1); dfield = que_node_get_val(str_node); dtype = dfield_get_type(dfield); ut_a(dtype_get_mtype(dtype) == DATA_VARCHAR); dfield_set_data(dfield, ptr, ptr_len - 1); break; default: ut_error; } return(func); } /************************************************************************* Parses a LIKE operator expression. */ static int pars_like_op( /*=========*/ /* out, own: function node in a query tree */ que_node_t* arg) /* in: LIKE comparison string.*/ { char* ptr; ulint ptr_len; int func = PARS_LIKE_TOKEN_EXACT; dfield_t* dfield = que_node_get_val(arg); dtype_t* dtype = dfield_get_type(dfield); ut_a(dtype_get_mtype(dtype) == DATA_CHAR || dtype_get_mtype(dtype) == DATA_VARCHAR); ptr = static_cast(dfield_get_data(dfield)); ptr_len = strlen(ptr); if (ptr_len) { func = pars_like_rebind( static_cast(arg), (byte*) ptr, ptr_len); } return(func); } /*********************************************************************//** Parses an operator expression. @return own: function node in a query tree */ func_node_t* pars_op( /*====*/ int func, /*!< in: operator token code */ que_node_t* arg1, /*!< in: first argument */ que_node_t* arg2) /*!< in: second argument or NULL for an unary operator */ { que_node_list_add_last(NULL, arg1); if (arg2) { que_node_list_add_last(arg1, arg2); } /* We need to parse the string and determine whether it's a PREFIX, SUFFIX or SUBSTRING comparison */ if (func == PARS_LIKE_TOKEN) { ut_a(que_node_get_type(arg2) == QUE_NODE_SYMBOL); func = pars_like_op(arg2); ut_a(func == PARS_LIKE_TOKEN_EXACT || func == PARS_LIKE_TOKEN_PREFIX || func == PARS_LIKE_TOKEN_SUFFIX || func == PARS_LIKE_TOKEN_SUBSTR); } return(pars_func_low(func, arg1)); } /*********************************************************************//** Parses an ORDER BY clause. Order by a single column only is supported. @return own: order-by node in a query tree */ order_node_t* pars_order_by( /*==========*/ sym_node_t* column, /*!< in: column name */ pars_res_word_t* asc) /*!< in: &pars_asc_token or pars_desc_token */ { order_node_t* node; node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(order_node_t))); node->common.type = QUE_NODE_ORDER; node->column = column; if (asc == &pars_asc_token) { node->asc = TRUE; } else { ut_a(asc == &pars_desc_token); node->asc = FALSE; } return(node); } /*********************************************************************//** Determine if a data type is a built-in string data type of the InnoDB SQL parser. @return TRUE if string data type */ static ibool pars_is_string_type( /*================*/ ulint mtype) /*!< in: main data type */ { switch (mtype) { case DATA_VARCHAR: case DATA_CHAR: case DATA_FIXBINARY: case DATA_BINARY: return(TRUE); } return(FALSE); } /*********************************************************************//** Resolves the data type of a function in an expression. The argument data types must already be resolved. */ static void pars_resolve_func_data_type( /*========================*/ func_node_t* node) /*!< in: function node */ { que_node_t* arg; ut_a(que_node_get_type(node) == QUE_NODE_FUNC); arg = node->args; switch (node->func) { case '+': case '-': case '*': case '/': /* Inherit the data type from the first argument (which must not be the SQL null literal whose type is DATA_ERROR) */ dtype_copy(que_node_get_data_type(node), que_node_get_data_type(arg)); ut_a(dtype_get_mtype(que_node_get_data_type(node)) == DATA_INT); break; case PARS_COUNT_TOKEN: ut_a(arg); dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4); break; case PARS_TO_BINARY_TOKEN: if (dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT) { dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0); } else { dtype_set(que_node_get_data_type(node), DATA_BINARY, 0, 0); } break; case PARS_LENGTH_TOKEN: case PARS_INSTR_TOKEN: ut_a(pars_is_string_type(que_node_get_data_type(arg)->mtype)); dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4); break; case PARS_SUBSTR_TOKEN: case PARS_CONCAT_TOKEN: ut_a(pars_is_string_type(que_node_get_data_type(arg)->mtype)); dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0); break; case '>': case '<': case '=': case PARS_GE_TOKEN: case PARS_LE_TOKEN: case PARS_NE_TOKEN: case PARS_AND_TOKEN: case PARS_OR_TOKEN: case PARS_NOT_TOKEN: case PARS_NOTFOUND_TOKEN: /* We currently have no iboolean type: use integer type */ dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4); break; case PARS_LIKE_TOKEN_EXACT: case PARS_LIKE_TOKEN_PREFIX: case PARS_LIKE_TOKEN_SUFFIX: case PARS_LIKE_TOKEN_SUBSTR: dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0); break; default: ut_error; } } /*********************************************************************//** Resolves the meaning of variables in an expression and the data types of functions. It is an error if some identifier cannot be resolved here. */ static void pars_resolve_exp_variables_and_types( /*=================================*/ sel_node_t* select_node, /*!< in: select node or NULL; if this is not NULL then the variable sym nodes are added to the copy_variables list of select_node */ que_node_t* exp_node) /*!< in: expression */ { func_node_t* func_node; que_node_t* arg; sym_node_t* sym_node; sym_node_t* node; ut_a(exp_node); if (que_node_get_type(exp_node) == QUE_NODE_FUNC) { func_node = static_cast(exp_node); arg = func_node->args; while (arg) { pars_resolve_exp_variables_and_types(select_node, arg); arg = que_node_get_next(arg); } pars_resolve_func_data_type(func_node); return; } ut_a(que_node_get_type(exp_node) == QUE_NODE_SYMBOL); sym_node = static_cast(exp_node); if (sym_node->resolved) { return; } /* Not resolved yet: look in the symbol table for a variable or a cursor or a function with the same name */ node = UT_LIST_GET_FIRST(pars_sym_tab_global->sym_list); while (node) { if (node->resolved && ((node->token_type == SYM_VAR) || (node->token_type == SYM_CURSOR) || (node->token_type == SYM_FUNCTION)) && node->name && sym_node->name_len == node->name_len && !memcmp(sym_node->name, node->name, node->name_len)) { /* Found a variable or a cursor declared with the same name */ break; } node = UT_LIST_GET_NEXT(sym_list, node); } if (!node) { fprintf(stderr, "PARSER ERROR: Unresolved identifier %s\n", sym_node->name); } ut_a(node); sym_node->resolved = TRUE; sym_node->token_type = SYM_IMPLICIT_VAR; sym_node->alias = node; sym_node->indirection = node; if (select_node) { UT_LIST_ADD_LAST(select_node->copy_variables, sym_node); } dfield_set_type(que_node_get_val(sym_node), que_node_get_data_type(node)); } /*********************************************************************//** Resolves the meaning of variables in an expression list. It is an error if some identifier cannot be resolved here. Resolves also the data types of functions. */ static void pars_resolve_exp_list_variables_and_types( /*======================================*/ sel_node_t* select_node, /*!< in: select node or NULL */ que_node_t* exp_node) /*!< in: expression list first node, or NULL */ { while (exp_node) { pars_resolve_exp_variables_and_types(select_node, exp_node); exp_node = que_node_get_next(exp_node); } } /*********************************************************************//** Resolves the columns in an expression. */ static void pars_resolve_exp_columns( /*=====================*/ sym_node_t* table_node, /*!< in: first node in a table list */ que_node_t* exp_node) /*!< in: expression */ { func_node_t* func_node; que_node_t* arg; sym_node_t* sym_node; dict_table_t* table; sym_node_t* t_node; ulint n_cols; ulint i; ut_a(exp_node); if (que_node_get_type(exp_node) == QUE_NODE_FUNC) { func_node = static_cast(exp_node); arg = func_node->args; while (arg) { pars_resolve_exp_columns(table_node, arg); arg = que_node_get_next(arg); } return; } ut_a(que_node_get_type(exp_node) == QUE_NODE_SYMBOL); sym_node = static_cast(exp_node); if (sym_node->resolved) { return; } /* Not resolved yet: look in the table list for a column with the same name */ t_node = table_node; while (t_node) { table = t_node->table; n_cols = dict_table_get_n_cols(table); for (i = 0; i < n_cols; i++) { const dict_col_t* col = dict_table_get_nth_col(table, i); const char* col_name = dict_table_get_col_name(table, i); if (sym_node->name_len == strlen(col_name) && !memcmp(sym_node->name, col_name, sym_node->name_len)) { /* Found */ sym_node->resolved = TRUE; sym_node->token_type = SYM_COLUMN; sym_node->table = table; sym_node->col_no = i; sym_node->prefetch_buf = NULL; dict_col_copy_type( col, dfield_get_type(&sym_node ->common.val)); return; } } t_node = static_cast(que_node_get_next(t_node)); } } /*********************************************************************//** Resolves the meaning of columns in an expression list. */ static void pars_resolve_exp_list_columns( /*==========================*/ sym_node_t* table_node, /*!< in: first node in a table list */ que_node_t* exp_node) /*!< in: expression list first node, or NULL */ { while (exp_node) { pars_resolve_exp_columns(table_node, exp_node); exp_node = que_node_get_next(exp_node); } } /*********************************************************************//** Retrieves the table definition for a table name id. */ static void pars_retrieve_table_def( /*====================*/ sym_node_t* sym_node) /*!< in: table node */ { ut_a(sym_node); ut_a(que_node_get_type(sym_node) == QUE_NODE_SYMBOL); /* Open the table only if it is not already opened. */ if (sym_node->token_type != SYM_TABLE_REF_COUNTED) { ut_a(sym_node->table == NULL); sym_node->resolved = TRUE; sym_node->token_type = SYM_TABLE_REF_COUNTED; sym_node->table = dict_table_open_on_name( sym_node->name, true, DICT_ERR_IGNORE_NONE); ut_a(sym_node->table != NULL); } } /*********************************************************************//** Retrieves the table definitions for a list of table name ids. @return number of tables */ static ulint pars_retrieve_table_list_defs( /*==========================*/ sym_node_t* sym_node) /*!< in: first table node in list */ { ulint count = 0; if (sym_node == NULL) { return(count); } while (sym_node) { pars_retrieve_table_def(sym_node); count++; sym_node = static_cast( que_node_get_next(sym_node)); } return(count); } /*********************************************************************//** Adds all columns to the select list if the query is SELECT * FROM ... */ static void pars_select_all_columns( /*====================*/ sel_node_t* select_node) /*!< in: select node already containing the table list */ { sym_node_t* col_node; sym_node_t* table_node; dict_table_t* table; ulint i; select_node->select_list = NULL; table_node = select_node->table_list; while (table_node) { table = table_node->table; for (i = 0; i < dict_table_get_n_user_cols(table); i++) { const char* col_name = dict_table_get_col_name( table, i); col_node = sym_tab_add_id(pars_sym_tab_global, (byte*) col_name, strlen(col_name)); select_node->select_list = que_node_list_add_last( select_node->select_list, col_node); } table_node = static_cast( que_node_get_next(table_node)); } } /*********************************************************************//** Parses a select list; creates a query graph node for the whole SELECT statement. @return own: select node in a query tree */ sel_node_t* pars_select_list( /*=============*/ que_node_t* select_list, /*!< in: select list */ sym_node_t* into_list) /*!< in: variables list or NULL */ { sel_node_t* node; node = sel_node_create(pars_sym_tab_global->heap); node->select_list = select_list; node->into_list = into_list; pars_resolve_exp_list_variables_and_types(NULL, into_list); return(node); } /*********************************************************************//** Checks if the query is an aggregate query, in which case the selct list must contain only aggregate function items. */ static void pars_check_aggregate( /*=================*/ sel_node_t* select_node) /*!< in: select node already containing the select list */ { que_node_t* exp_node; func_node_t* func_node; ulint n_nodes = 0; ulint n_aggregate_nodes = 0; exp_node = select_node->select_list; while (exp_node) { n_nodes++; if (que_node_get_type(exp_node) == QUE_NODE_FUNC) { func_node = static_cast(exp_node); if (func_node->fclass == PARS_FUNC_AGGREGATE) { n_aggregate_nodes++; } } exp_node = que_node_get_next(exp_node); } if (n_aggregate_nodes > 0) { ut_a(n_nodes == n_aggregate_nodes); select_node->is_aggregate = TRUE; } else { select_node->is_aggregate = FALSE; } } /*********************************************************************//** Parses a select statement. @return own: select node in a query tree */ sel_node_t* pars_select_statement( /*==================*/ sel_node_t* select_node, /*!< in: select node already containing the select list */ sym_node_t* table_list, /*!< in: table list */ que_node_t* search_cond, /*!< in: search condition or NULL */ pars_res_word_t* for_update, /*!< in: NULL or &pars_update_token */ pars_res_word_t* lock_shared, /*!< in: NULL or &pars_share_token */ order_node_t* order_by) /*!< in: NULL or an order-by node */ { select_node->state = SEL_NODE_OPEN; select_node->table_list = table_list; select_node->n_tables = pars_retrieve_table_list_defs(table_list); if (select_node->select_list == &pars_star_denoter) { /* SELECT * FROM ... */ pars_select_all_columns(select_node); } if (select_node->into_list) { ut_a(que_node_list_get_len(select_node->into_list) == que_node_list_get_len(select_node->select_list)); } UT_LIST_INIT(select_node->copy_variables, &sym_node_t::col_var_list); pars_resolve_exp_list_columns(table_list, select_node->select_list); pars_resolve_exp_list_variables_and_types(select_node, select_node->select_list); pars_check_aggregate(select_node); select_node->search_cond = search_cond; if (search_cond) { pars_resolve_exp_columns(table_list, search_cond); pars_resolve_exp_variables_and_types(select_node, search_cond); } if (for_update) { ut_a(!lock_shared); select_node->set_x_locks = TRUE; select_node->row_lock_mode = LOCK_X; select_node->consistent_read = FALSE; select_node->read_view = NULL; } else if (lock_shared){ select_node->set_x_locks = FALSE; select_node->row_lock_mode = LOCK_S; select_node->consistent_read = FALSE; select_node->read_view = NULL; } else { select_node->set_x_locks = FALSE; select_node->row_lock_mode = LOCK_S; select_node->consistent_read = TRUE; } select_node->order_by = order_by; if (order_by) { pars_resolve_exp_columns(table_list, order_by->column); } /* The final value of the following fields depend on the environment where the select statement appears: */ select_node->can_get_updated = FALSE; select_node->explicit_cursor = NULL; opt_search_plan(select_node); return(select_node); } /*********************************************************************//** Parses a cursor declaration. @return sym_node */ que_node_t* pars_cursor_declaration( /*====================*/ sym_node_t* sym_node, /*!< in: cursor id node in the symbol table */ sel_node_t* select_node) /*!< in: select node */ { sym_node->resolved = TRUE; sym_node->token_type = SYM_CURSOR; sym_node->cursor_def = select_node; select_node->state = SEL_NODE_CLOSED; select_node->explicit_cursor = sym_node; return(sym_node); } /*********************************************************************//** Parses a function declaration. @return sym_node */ que_node_t* pars_function_declaration( /*======================*/ sym_node_t* sym_node) /*!< in: function id node in the symbol table */ { sym_node->resolved = TRUE; sym_node->token_type = SYM_FUNCTION; /* Check that the function exists. */ ut_a(pars_info_lookup_user_func( pars_sym_tab_global->info, sym_node->name)); return(sym_node); } /*********************************************************************//** Parses a delete or update statement start. @return own: update node in a query tree */ upd_node_t* pars_update_statement_start( /*========================*/ ibool is_delete, /*!< in: TRUE if delete */ sym_node_t* table_sym, /*!< in: table name node */ col_assign_node_t* col_assign_list)/*!< in: column assignment list, NULL if delete */ { upd_node_t* node; node = upd_node_create(pars_sym_tab_global->heap); node->is_delete = is_delete ? PLAIN_DELETE : NO_DELETE; node->table_sym = table_sym; node->col_assign_list = col_assign_list; return(node); } /*********************************************************************//** Parses a column assignment in an update. @return column assignment node */ col_assign_node_t* pars_column_assignment( /*===================*/ sym_node_t* column, /*!< in: column to assign */ que_node_t* exp) /*!< in: value to assign */ { col_assign_node_t* node; node = static_cast( mem_heap_alloc(pars_sym_tab_global->heap, sizeof(col_assign_node_t))); node->common.type = QUE_NODE_COL_ASSIGNMENT; node->col = column; node->val = exp; return(node); } /*********************************************************************//** Processes an update node assignment list. */ static void pars_process_assign_list( /*=====================*/ upd_node_t* node) /*!< in: update node */ { col_assign_node_t* col_assign_list; sym_node_t* table_sym; col_assign_node_t* assign_node; upd_field_t* upd_field; dict_index_t* clust_index; sym_node_t* col_sym; ulint changes_ord_field; ulint changes_field_size; ulint n_assigns; ulint i; table_sym = node->table_sym; col_assign_list = static_cast( node->col_assign_list); clust_index = dict_table_get_first_index(node->table); assign_node = col_assign_list; n_assigns = 0; while (assign_node) { pars_resolve_exp_columns(table_sym, assign_node->col); pars_resolve_exp_columns(table_sym, assign_node->val); pars_resolve_exp_variables_and_types(NULL, assign_node->val); #if 0 ut_a(dtype_get_mtype( dfield_get_type(que_node_get_val( assign_node->col))) == dtype_get_mtype( dfield_get_type(que_node_get_val( assign_node->val)))); #endif /* Add to the update node all the columns found in assignment values as columns to copy: therefore, TRUE */ opt_find_all_cols(TRUE, clust_index, &(node->columns), NULL, assign_node->val); n_assigns++; assign_node = static_cast( que_node_get_next(assign_node)); } node->update = upd_create(n_assigns, pars_sym_tab_global->heap); assign_node = col_assign_list; changes_field_size = UPD_NODE_NO_SIZE_CHANGE; for (i = 0; i < n_assigns; i++) { upd_field = upd_get_nth_field(node->update, i); col_sym = assign_node->col; ulint field_no = dict_index_get_nth_col_pos( clust_index, col_sym->col_no, NULL); ut_ad(field_no < clust_index->n_fields); upd_field_set_field_no(upd_field, static_cast(field_no), clust_index); upd_field->exp = assign_node->val; if (!dict_col_get_fixed_size( dict_index_get_nth_col(clust_index, upd_field->field_no), dict_table_is_comp(node->table))) { changes_field_size = 0; } assign_node = static_cast( que_node_get_next(assign_node)); } /* Find out if the update can modify an ordering field in any index */ changes_ord_field = UPD_NODE_NO_ORD_CHANGE; if (row_upd_changes_some_index_ord_field_binary(node->table, node->update)) { changes_ord_field = 0; } node->cmpl_info = changes_ord_field | changes_field_size; } /*********************************************************************//** Parses an update or delete statement. @return own: update node in a query tree */ upd_node_t* pars_update_statement( /*==================*/ upd_node_t* node, /*!< in: update node */ sym_node_t* cursor_sym, /*!< in: pointer to a cursor entry in the symbol table or NULL */ que_node_t* search_cond) /*!< in: search condition or NULL */ { sym_node_t* table_sym; sel_node_t* sel_node; plan_t* plan; table_sym = node->table_sym; pars_retrieve_table_def(table_sym); node->table = table_sym->table; UT_LIST_INIT(node->columns, &sym_node_t::col_var_list); /* Make the single table node into a list of table nodes of length 1 */ que_node_list_add_last(NULL, table_sym); if (cursor_sym) { pars_resolve_exp_variables_and_types(NULL, cursor_sym); sel_node = cursor_sym->alias->cursor_def; node->searched_update = FALSE; } else { sel_node = pars_select_list(NULL, NULL); pars_select_statement(sel_node, table_sym, search_cond, NULL, &pars_share_token, NULL); node->searched_update = TRUE; sel_node->common.parent = node; } node->select = sel_node; ut_a(!node->is_delete || (node->col_assign_list == NULL)); ut_a(node->is_delete == PLAIN_DELETE || node->col_assign_list != NULL); if (node->is_delete == PLAIN_DELETE) { node->cmpl_info = 0; } else { pars_process_assign_list(node); } if (node->searched_update) { node->has_clust_rec_x_lock = TRUE; sel_node->set_x_locks = TRUE; sel_node->row_lock_mode = LOCK_X; } else { node->has_clust_rec_x_lock = sel_node->set_x_locks; ut_ad(node->has_clust_rec_x_lock); } ut_a(sel_node->n_tables == 1); ut_a(sel_node->consistent_read == FALSE); ut_a(sel_node->order_by == NULL); ut_a(sel_node->is_aggregate == FALSE); sel_node->can_get_updated = TRUE; node->state = UPD_NODE_UPDATE_CLUSTERED; plan = sel_node_get_nth_plan(sel_node, 0); plan->no_prefetch = TRUE; if (!dict_index_is_clust(plan->index)) { plan->must_get_clust = TRUE; node->pcur = &(plan->clust_pcur); } else { node->pcur = &(plan->pcur); } return(node); } /*********************************************************************//** Parses an insert statement. @return own: update node in a query tree */ ins_node_t* pars_insert_statement( /*==================*/ sym_node_t* table_sym, /*!< in: table name node */ que_node_t* values_list, /*!< in: value expression list or NULL */ sel_node_t* select) /*!< in: select condition or NULL */ { ins_node_t* node; dtuple_t* row; ulint ins_type; ut_a(values_list || select); ut_a(!values_list || !select); if (values_list) { ins_type = INS_VALUES; } else { ins_type = INS_SEARCHED; } pars_retrieve_table_def(table_sym); node = ins_node_create(ins_type, table_sym->table, pars_sym_tab_global->heap); row = dtuple_create(pars_sym_tab_global->heap, dict_table_get_n_cols(node->table)); dict_table_copy_types(row, table_sym->table); ins_node_set_new_row(node, row); node->select = select; if (select) { select->common.parent = node; ut_a(que_node_list_get_len(select->select_list) == dict_table_get_n_user_cols(table_sym->table)); } node->values_list = values_list; if (node->values_list) { pars_resolve_exp_list_variables_and_types(NULL, values_list); ut_a(que_node_list_get_len(values_list) == dict_table_get_n_user_cols(table_sym->table)); } return(node); } /*********************************************************************//** Set the type of a dfield. */ static void pars_set_dfield_type( /*=================*/ dfield_t* dfield, /*!< in: dfield */ pars_res_word_t* type, /*!< in: pointer to a type token */ ulint len, /*!< in: length, or 0 */ bool is_not_null) /*!< in: whether the column is NOT NULL. */ { ulint flags = 0; if (is_not_null) { flags |= DATA_NOT_NULL; } if (type == &pars_bigint_token) { ut_a(len == 0); dtype_set(dfield_get_type(dfield), DATA_INT, flags, 8); } else if (type == &pars_int_token) { ut_a(len == 0); dtype_set(dfield_get_type(dfield), DATA_INT, flags, 4); } else if (type == &pars_char_token) { //ut_a(len == 0); dtype_set(dfield_get_type(dfield), DATA_VARCHAR, DATA_ENGLISH | flags, len); } else { ut_error; } } /*********************************************************************//** Parses a variable declaration. @return own: symbol table node of type SYM_VAR */ sym_node_t* pars_variable_declaration( /*======================*/ sym_node_t* node, /*!< in: symbol table node allocated for the id of the variable */ pars_res_word_t* type) /*!< in: pointer to a type token */ { node->resolved = TRUE; node->token_type = SYM_VAR; node->param_type = PARS_NOT_PARAM; pars_set_dfield_type(que_node_get_val(node), type, 0, false); return(node); } /*********************************************************************//** Sets the parent field in a query node list. */ static void pars_set_parent_in_list( /*====================*/ que_node_t* node_list, /*!< in: first node in a list */ que_node_t* parent) /*!< in: parent value to set in all nodes of the list */ { que_common_t* common; common = static_cast(node_list); while (common) { common->parent = parent; common = static_cast(que_node_get_next(common)); } } /*********************************************************************//** Parses an elsif element. @return elsif node */ elsif_node_t* pars_elsif_element( /*===============*/ que_node_t* cond, /*!< in: if-condition */ que_node_t* stat_list) /*!< in: statement list */ { elsif_node_t* node; node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(elsif_node_t))); node->common.type = QUE_NODE_ELSIF; node->cond = cond; pars_resolve_exp_variables_and_types(NULL, cond); node->stat_list = stat_list; return(node); } /*********************************************************************//** Parses an if-statement. @return if-statement node */ if_node_t* pars_if_statement( /*==============*/ que_node_t* cond, /*!< in: if-condition */ que_node_t* stat_list, /*!< in: statement list */ que_node_t* else_part) /*!< in: else-part statement list or elsif element list */ { if_node_t* node; elsif_node_t* elsif_node; node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(if_node_t))); node->common.type = QUE_NODE_IF; node->cond = cond; pars_resolve_exp_variables_and_types(NULL, cond); node->stat_list = stat_list; if (else_part && (que_node_get_type(else_part) == QUE_NODE_ELSIF)) { /* There is a list of elsif conditions */ node->else_part = NULL; node->elsif_list = static_cast(else_part); elsif_node = static_cast(else_part); while (elsif_node) { pars_set_parent_in_list(elsif_node->stat_list, node); elsif_node = static_cast( que_node_get_next(elsif_node)); } } else { node->else_part = else_part; node->elsif_list = NULL; pars_set_parent_in_list(else_part, node); } pars_set_parent_in_list(stat_list, node); return(node); } /*********************************************************************//** Parses a while-statement. @return while-statement node */ while_node_t* pars_while_statement( /*=================*/ que_node_t* cond, /*!< in: while-condition */ que_node_t* stat_list) /*!< in: statement list */ { while_node_t* node; node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(while_node_t))); node->common.type = QUE_NODE_WHILE; node->cond = cond; pars_resolve_exp_variables_and_types(NULL, cond); node->stat_list = stat_list; pars_set_parent_in_list(stat_list, node); return(node); } /*********************************************************************//** Parses a for-loop-statement. @return for-statement node */ for_node_t* pars_for_statement( /*===============*/ sym_node_t* loop_var, /*!< in: loop variable */ que_node_t* loop_start_limit,/*!< in: loop start expression */ que_node_t* loop_end_limit, /*!< in: loop end expression */ que_node_t* stat_list) /*!< in: statement list */ { for_node_t* node; node = static_cast( mem_heap_alloc(pars_sym_tab_global->heap, sizeof(for_node_t))); node->common.type = QUE_NODE_FOR; pars_resolve_exp_variables_and_types(NULL, loop_var); pars_resolve_exp_variables_and_types(NULL, loop_start_limit); pars_resolve_exp_variables_and_types(NULL, loop_end_limit); node->loop_var = loop_var->indirection; ut_a(loop_var->indirection); node->loop_start_limit = loop_start_limit; node->loop_end_limit = loop_end_limit; node->stat_list = stat_list; pars_set_parent_in_list(stat_list, node); return(node); } /*********************************************************************//** Parses an exit statement. @return exit statement node */ exit_node_t* pars_exit_statement(void) /*=====================*/ { exit_node_t* node; node = static_cast( mem_heap_alloc(pars_sym_tab_global->heap, sizeof(exit_node_t))); node->common.type = QUE_NODE_EXIT; return(node); } /*********************************************************************//** Parses a return-statement. @return return-statement node */ return_node_t* pars_return_statement(void) /*=======================*/ { return_node_t* node; node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(return_node_t))); node->common.type = QUE_NODE_RETURN; return(node); } /*********************************************************************//** Parses an assignment statement. @return assignment statement node */ assign_node_t* pars_assignment_statement( /*======================*/ sym_node_t* var, /*!< in: variable to assign */ que_node_t* val) /*!< in: value to assign */ { assign_node_t* node; node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(assign_node_t))); node->common.type = QUE_NODE_ASSIGNMENT; node->var = var; node->val = val; pars_resolve_exp_variables_and_types(NULL, var); pars_resolve_exp_variables_and_types(NULL, val); ut_a(dtype_get_mtype(dfield_get_type(que_node_get_val(var))) == dtype_get_mtype(dfield_get_type(que_node_get_val(val)))); return(node); } /*********************************************************************//** Parses a procedure call. @return function node */ func_node_t* pars_procedure_call( /*================*/ que_node_t* res_word,/*!< in: procedure name reserved word */ que_node_t* args) /*!< in: argument list */ { func_node_t* node; node = pars_func(res_word, args); pars_resolve_exp_list_variables_and_types(NULL, args); return(node); } /*********************************************************************//** Parses a fetch statement. into_list or user_func (but not both) must be non-NULL. @return fetch statement node */ fetch_node_t* pars_fetch_statement( /*=================*/ sym_node_t* cursor, /*!< in: cursor node */ sym_node_t* into_list, /*!< in: variables to set, or NULL */ sym_node_t* user_func) /*!< in: user function name, or NULL */ { sym_node_t* cursor_decl; fetch_node_t* node; /* Logical XOR. */ ut_a(!into_list != !user_func); node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(fetch_node_t))); node->common.type = QUE_NODE_FETCH; pars_resolve_exp_variables_and_types(NULL, cursor); if (into_list) { pars_resolve_exp_list_variables_and_types(NULL, into_list); node->into_list = into_list; node->func = NULL; } else { pars_resolve_exp_variables_and_types(NULL, user_func); node->func = pars_info_lookup_user_func( pars_sym_tab_global->info, user_func->name); ut_a(node->func); node->into_list = NULL; } cursor_decl = cursor->alias; ut_a(cursor_decl->token_type == SYM_CURSOR); node->cursor_def = cursor_decl->cursor_def; if (into_list) { ut_a(que_node_list_get_len(into_list) == que_node_list_get_len(node->cursor_def->select_list)); } return(node); } /*********************************************************************//** Parses an open or close cursor statement. @return fetch statement node */ open_node_t* pars_open_statement( /*================*/ ulint type, /*!< in: ROW_SEL_OPEN_CURSOR or ROW_SEL_CLOSE_CURSOR */ sym_node_t* cursor) /*!< in: cursor node */ { sym_node_t* cursor_decl; open_node_t* node; node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(open_node_t))); node->common.type = QUE_NODE_OPEN; pars_resolve_exp_variables_and_types(NULL, cursor); cursor_decl = cursor->alias; ut_a(cursor_decl->token_type == SYM_CURSOR); node->op_type = static_cast(type); node->cursor_def = cursor_decl->cursor_def; return(node); } /*********************************************************************//** Parses a row_printf-statement. @return row_printf-statement node */ row_printf_node_t* pars_row_printf_statement( /*======================*/ sel_node_t* sel_node) /*!< in: select node */ { row_printf_node_t* node; node = static_cast( mem_heap_alloc( pars_sym_tab_global->heap, sizeof(row_printf_node_t))); node->common.type = QUE_NODE_ROW_PRINTF; node->sel_node = sel_node; sel_node->common.parent = node; return(node); } /*********************************************************************//** Parses a commit statement. @return own: commit node struct */ commit_node_t* pars_commit_statement(void) /*=======================*/ { return(trx_commit_node_create(pars_sym_tab_global->heap)); } /*********************************************************************//** Parses a rollback statement. @return own: rollback node struct */ roll_node_t* pars_rollback_statement(void) /*=========================*/ { return(roll_node_create(pars_sym_tab_global->heap)); } /*********************************************************************//** Parses a column definition at a table creation. @return column sym table node */ sym_node_t* pars_column_def( /*============*/ sym_node_t* sym_node, /*!< in: column node in the symbol table */ pars_res_word_t* type, /*!< in: data type */ sym_node_t* len, /*!< in: length of column, or NULL */ void* is_not_null) /*!< in: if not NULL, column is of type NOT NULL. */ { ulint len2; if (len) { len2 = ulint(eval_node_get_int_val(len)); } else { len2 = 0; } pars_set_dfield_type(que_node_get_val(sym_node), type, len2, is_not_null != NULL); return(sym_node); } /*********************************************************************//** Parses a table creation operation. @return table create subgraph */ tab_node_t* pars_create_table( /*==============*/ sym_node_t* table_sym, /*!< in: table name node in the symbol table */ sym_node_t* column_defs) /*!< in: list of column names */ { dict_table_t* table; sym_node_t* column; tab_node_t* node; const dtype_t* dtype; ulint n_cols; ulint flags = 0; ulint flags2 = DICT_TF2_FTS_AUX_HEX_NAME; DBUG_EXECUTE_IF("innodb_test_wrong_fts_aux_table_name", flags2 &= ~DICT_TF2_FTS_AUX_HEX_NAME;); n_cols = que_node_list_get_len(column_defs); table = dict_table_t::create( {table_sym->name, strlen(table_sym->name)}, nullptr, n_cols, 0, flags, flags2); mem_heap_t* heap = pars_sym_tab_global->heap; column = column_defs; while (column) { dtype = dfield_get_type(que_node_get_val(column)); dict_mem_table_add_col(table, heap, column->name, dtype->mtype, dtype->prtype, dtype->len); column->resolved = TRUE; column->token_type = SYM_COLUMN; column = static_cast(que_node_get_next(column)); } dict_table_add_system_columns(table, heap); node = tab_create_graph_create(table, heap); table_sym->resolved = TRUE; table_sym->token_type = SYM_TABLE; return(node); } /*********************************************************************//** Parses an index creation operation. @return index create subgraph */ ind_node_t* pars_create_index( /*==============*/ pars_res_word_t* unique_def, /*!< in: not NULL if a unique index */ pars_res_word_t* clustered_def, /*!< in: not NULL if a clustered index */ sym_node_t* index_sym, /*!< in: index name node in the symbol table */ sym_node_t* table_sym, /*!< in: table name node in the symbol table */ sym_node_t* column_list) /*!< in: list of column names */ { dict_index_t* index; sym_node_t* column; ind_node_t* node; ulint n_fields; ulint ind_type; n_fields = que_node_list_get_len(column_list); ind_type = 0; if (unique_def) { ind_type = ind_type | DICT_UNIQUE; } if (clustered_def) { ind_type = ind_type | DICT_CLUSTERED; } index = dict_mem_index_create(NULL, index_sym->name, ind_type, n_fields); column = column_list; while (column) { dict_mem_index_add_field(index, column->name, 0); column->resolved = TRUE; column->token_type = SYM_COLUMN; column = static_cast(que_node_get_next(column)); } node = ind_create_graph_create(index, table_sym->name, pars_sym_tab_global->heap, FIL_ENCRYPTION_DEFAULT, FIL_DEFAULT_ENCRYPTION_KEY); table_sym->resolved = TRUE; table_sym->token_type = SYM_TABLE; index_sym->resolved = TRUE; index_sym->token_type = SYM_TABLE; return(node); } /*********************************************************************//** Parses a procedure definition. @return query fork node */ que_fork_t* pars_procedure_definition( /*======================*/ sym_node_t* sym_node, /*!< in: procedure id node in the symbol table */ que_node_t* stat_list) /*!< in: statement list */ { proc_node_t* node; que_fork_t* fork; que_thr_t* thr; mem_heap_t* heap; heap = pars_sym_tab_global->heap; fork = que_fork_create(heap); fork->trx = NULL; thr = que_thr_create(fork, heap, NULL); node = static_cast( mem_heap_alloc(heap, sizeof(proc_node_t))); node->common.type = QUE_NODE_PROC; node->common.parent = thr; sym_node->token_type = SYM_PROCEDURE_NAME; sym_node->resolved = TRUE; node->proc_id = sym_node; node->stat_list = stat_list; pars_set_parent_in_list(stat_list, node); node->sym_tab = pars_sym_tab_global; thr->child = node; pars_sym_tab_global->query_graph = fork; return(fork); } /*************************************************************//** Retrieves characters to the lexical analyzer. */ int pars_get_lex_chars( /*===============*/ char* buf, /*!< in/out: buffer where to copy */ size_t max_size) /*!< in: maximum number of characters which fit in the buffer */ { size_t len = pars_sym_tab_global->string_len - pars_sym_tab_global->next_char_pos; if (len == 0) { return(0); } if (len > max_size) { len = max_size; } memcpy(buf, pars_sym_tab_global->sql_string + pars_sym_tab_global->next_char_pos, len); pars_sym_tab_global->next_char_pos += len; return static_cast(len); } /*************************************************************//** Called by yyparse on error. */ void yyerror( /*====*/ const char* s MY_ATTRIBUTE((unused))) /*!< in: error message string */ { ut_ad(s); ib::fatal() << "PARSER: Syntax error in SQL string"; } /*************************************************************//** Parses an SQL string returning the query graph. @return own: the query graph */ que_t* pars_sql( /*=====*/ pars_info_t* info, /*!< in: extra information, or NULL */ const char* str) /*!< in: SQL string */ { sym_node_t* sym_node; mem_heap_t* heap; que_t* graph; ut_ad(str); heap = mem_heap_create(16000); /* Currently, the parser is not reentrant: */ ut_ad(dict_sys.locked()); pars_sym_tab_global = sym_tab_create(heap); pars_sym_tab_global->string_len = strlen(str); pars_sym_tab_global->sql_string = static_cast( mem_heap_dup(heap, str, pars_sym_tab_global->string_len + 1)); pars_sym_tab_global->next_char_pos = 0; pars_sym_tab_global->info = info; yyparse(); sym_node = UT_LIST_GET_FIRST(pars_sym_tab_global->sym_list); while (sym_node) { ut_a(sym_node->resolved); sym_node = UT_LIST_GET_NEXT(sym_list, sym_node); } graph = pars_sym_tab_global->query_graph; graph->sym_tab = pars_sym_tab_global; graph->info = info; pars_sym_tab_global = NULL; /* fprintf(stderr, "SQL graph size %lu\n", mem_heap_get_size(heap)); */ return(graph); } /** Completes a query graph by adding query thread and fork nodes above it and prepares the graph for running. @param[in] node root node for an incomplete query graph, or NULL for dummy graph @param[in] trx transaction handle @param[in] heap memory heap from which allocated @param[in] prebuilt row prebuilt structure @return query thread node to run */ que_thr_t* pars_complete_graph_for_exec( que_node_t* node, trx_t* trx, mem_heap_t* heap, row_prebuilt_t* prebuilt) { que_fork_t* fork; que_thr_t* thr; fork = que_fork_create(heap); fork->trx = trx; thr = que_thr_create(fork, heap, prebuilt); thr->child = node; if (node) { que_node_set_parent(node, thr); } trx->graph = NULL; return(thr); } /****************************************************************//** Create parser info struct. @return own: info struct */ pars_info_t* pars_info_create(void) /*==================*/ { pars_info_t* info; mem_heap_t* heap; heap = mem_heap_create(512); info = static_cast(mem_heap_zalloc(heap, sizeof(*info))); info->heap = heap; return(info); } /****************************************************************//** Add bound literal. */ void pars_info_add_literal( /*==================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const void* address, /*!< in: address */ ulint length, /*!< in: length of data */ ulint type, /*!< in: type, e.g. DATA_FIXBINARY */ ulint prtype) /*!< in: precise type, e.g. DATA_UNSIGNED */ { pars_bound_lit_t* pbl; ut_ad(!pars_info_get_bound_lit(info, name)); pbl = static_cast( mem_heap_alloc(info->heap, sizeof(*pbl))); pbl->name = name; pbl->address = address; pbl->length = length; pbl->type = type; pbl->prtype = prtype; if (!info->bound_lits) { ib_alloc_t* heap_alloc; heap_alloc = ib_heap_allocator_create(info->heap); info->bound_lits = ib_vector_create(heap_alloc, sizeof(*pbl), 8); } ib_vector_push(info->bound_lits, pbl); } /****************************************************************//** Equivalent to pars_info_add_literal(info, name, str, strlen(str), DATA_VARCHAR, DATA_ENGLISH). */ void pars_info_add_str_literal( /*======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const char* str) /*!< in: string */ { pars_info_add_literal(info, name, str, strlen(str), DATA_VARCHAR, DATA_ENGLISH); } /******************************************************************** If the literal value already exists then it rebinds otherwise it creates a new entry.*/ void pars_info_bind_literal( /*===================*/ pars_info_t* info, /* in: info struct */ const char* name, /* in: name */ const void* address, /* in: address */ ulint length, /* in: length of data */ ulint type, /* in: type, e.g. DATA_FIXBINARY */ ulint prtype) /* in: precise type, e.g. */ { pars_bound_lit_t* pbl; pbl = pars_info_lookup_bound_lit(info, name); if (!pbl) { pars_info_add_literal( info, name, address, length, type, prtype); } else { pbl->address = address; pbl->length = length; sym_tab_rebind_lit(pbl->node, address, length); } } /******************************************************************** If the literal value already exists then it rebinds otherwise it creates a new entry.*/ void pars_info_bind_varchar_literal( /*===========================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const byte* str, /*!< in: string */ ulint str_len) /*!< in: string length */ { pars_bound_lit_t* pbl; pbl = pars_info_lookup_bound_lit(info, name); if (!pbl) { pars_info_add_literal( info, name, str, str_len, DATA_VARCHAR, DATA_ENGLISH); } else { pbl->address = str; pbl->length = str_len; sym_tab_rebind_lit(pbl->node, str, str_len); } } /****************************************************************//** Equivalent to: char buf[4]; mach_write_to_4(buf, val); pars_info_add_literal(info, name, buf, 4, DATA_INT, 0); except that the buffer is dynamically allocated from the info struct's heap. */ void pars_info_add_int4_literal( /*=======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ ulint val) /*!< in: value */ { byte* buf = static_cast(mem_heap_alloc(info->heap, 4)); mach_write_to_4(buf, val); pars_info_add_literal(info, name, buf, 4, DATA_INT, 0); } /******************************************************************** If the literal value already exists then it rebinds otherwise it creates a new entry. */ void pars_info_bind_int4_literal( /*========================*/ pars_info_t* info, /* in: info struct */ const char* name, /* in: name */ const ib_uint32_t* val) /* in: value */ { pars_bound_lit_t* pbl; pbl = pars_info_lookup_bound_lit(info, name); if (!pbl) { pars_info_add_literal(info, name, val, 4, DATA_INT, 0); } else { pbl->address = val; pbl->length = sizeof(*val); sym_tab_rebind_lit(pbl->node, val, sizeof(*val)); } } /******************************************************************** If the literal value already exists then it rebinds otherwise it creates a new entry. */ void pars_info_bind_int8_literal( /*========================*/ pars_info_t* info, /* in: info struct */ const char* name, /* in: name */ const ib_uint64_t* val) /* in: value */ { pars_bound_lit_t* pbl; pbl = pars_info_lookup_bound_lit(info, name); if (!pbl) { pars_info_add_literal( info, name, val, sizeof(*val), DATA_INT, 0); } else { pbl->address = val; pbl->length = sizeof(*val); sym_tab_rebind_lit(pbl->node, val, sizeof(*val)); } } /****************************************************************//** Equivalent to: char buf[8]; mach_write_to_8(buf, val); pars_info_add_literal(info, name, buf, 8, DATA_FIXBINARY, 0); except that the buffer is dynamically allocated from the info struct's heap. */ void pars_info_add_ull_literal( /*======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ ib_uint64_t val) /*!< in: value */ { byte* buf = static_cast(mem_heap_alloc(info->heap, 8)); mach_write_to_8(buf, val); pars_info_add_literal(info, name, buf, 8, DATA_FIXBINARY, 0); } /****************************************************************//** If the literal value already exists then it rebinds otherwise it creates a new entry. */ void pars_info_bind_ull_literal( /*=======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const ib_uint64_t* val) /*!< in: value */ { pars_bound_lit_t* pbl; pbl = pars_info_lookup_bound_lit(info, name); if (!pbl) { pars_info_add_literal( info, name, val, sizeof(*val), DATA_FIXBINARY, 0); } else { pbl->address = val; pbl->length = sizeof(*val); sym_tab_rebind_lit(pbl->node, val, sizeof(*val)); } } /****************************************************************//** Add user function. */ void pars_info_bind_function( /*====================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: function name */ pars_user_func_cb_t func, /*!< in: function address */ void* arg) /*!< in: user-supplied argument */ { pars_user_func_t* puf; puf = pars_info_lookup_user_func(info, name); if (!puf) { if (!info->funcs) { ib_alloc_t* heap_alloc; heap_alloc = ib_heap_allocator_create(info->heap); info->funcs = ib_vector_create( heap_alloc, sizeof(*puf), 8); } /* Create a "new" element */ puf = static_cast( ib_vector_push(info->funcs, NULL)); puf->name = name; } puf->arg = arg; puf->func = func; } /******************************************************************** Add bound id. */ void pars_info_bind_id( /*==============*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const char* id) /*!< in: id */ { pars_bound_id_t* bid; bid = pars_info_lookup_bound_id(info, name); if (!bid) { if (!info->bound_ids) { ib_alloc_t* heap_alloc; heap_alloc = ib_heap_allocator_create(info->heap); info->bound_ids = ib_vector_create( heap_alloc, sizeof(*bid), 8); } /* Create a "new" element */ bid = static_cast( ib_vector_push(info->bound_ids, NULL)); bid->name = name; } bid->id = id; } /******************************************************************** Get bound identifier with the given name.*/ pars_bound_id_t* pars_info_get_bound_id( /*===================*/ /* out: bound id, or NULL if not found */ pars_info_t* info, /* in: info struct */ const char* name) /* in: bound id name to find */ { return(pars_info_lookup_bound_id(info, name)); } /****************************************************************//** Get bound literal with the given name. @return bound literal, or NULL if not found */ pars_bound_lit_t* pars_info_get_bound_lit( /*====================*/ pars_info_t* info, /*!< in: info struct */ const char* name) /*!< in: bound literal name to find */ { return(pars_info_lookup_bound_lit(info, name)); }