summaryrefslogtreecommitdiff
path: root/storage/innobase/pars/pars0pars.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/pars/pars0pars.cc')
-rw-r--r--storage/innobase/pars/pars0pars.cc2631
1 files changed, 2631 insertions, 0 deletions
diff --git a/storage/innobase/pars/pars0pars.cc b/storage/innobase/pars/pars0pars.cc
new file mode 100644
index 00000000000..a4ab85adc36
--- /dev/null
+++ b/storage/innobase/pars/pars0pars.cc
@@ -0,0 +1,2631 @@
+/*****************************************************************************
+
+Copyright (c) 1996, 2011, 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., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 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"
+
+#ifdef UNIV_NONINL
+#include "pars0pars.ic"
+#endif
+
+#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 "lock0lock.h"
+#include "eval0eval.h"
+
+#ifdef UNIV_SQL_DEBUG
+/** If the following is set TRUE, the lexer will print the SQL string
+as it tokenizes it */
+UNIV_INTERN ibool pars_print_lexed = FALSE;
+#endif /* UNIV_SQL_DEBUG */
+
+/* Global variable used while parsing a single procedure or query : the code is
+NOT re-entrant */
+UNIV_INTERN sym_tab_t* pars_sym_tab_global;
+
+/* Global variables used to denote certain reserved words, used in
+constructing the parsing tree */
+
+UNIV_INTERN pars_res_word_t pars_to_char_token = {PARS_TO_CHAR_TOKEN};
+UNIV_INTERN pars_res_word_t pars_to_number_token = {PARS_TO_NUMBER_TOKEN};
+UNIV_INTERN pars_res_word_t pars_to_binary_token = {PARS_TO_BINARY_TOKEN};
+UNIV_INTERN pars_res_word_t pars_binary_to_number_token = {PARS_BINARY_TO_NUMBER_TOKEN};
+UNIV_INTERN pars_res_word_t pars_substr_token = {PARS_SUBSTR_TOKEN};
+UNIV_INTERN pars_res_word_t pars_replstr_token = {PARS_REPLSTR_TOKEN};
+UNIV_INTERN pars_res_word_t pars_concat_token = {PARS_CONCAT_TOKEN};
+UNIV_INTERN pars_res_word_t pars_instr_token = {PARS_INSTR_TOKEN};
+UNIV_INTERN pars_res_word_t pars_length_token = {PARS_LENGTH_TOKEN};
+UNIV_INTERN pars_res_word_t pars_sysdate_token = {PARS_SYSDATE_TOKEN};
+UNIV_INTERN pars_res_word_t pars_printf_token = {PARS_PRINTF_TOKEN};
+UNIV_INTERN pars_res_word_t pars_assert_token = {PARS_ASSERT_TOKEN};
+UNIV_INTERN pars_res_word_t pars_rnd_token = {PARS_RND_TOKEN};
+UNIV_INTERN pars_res_word_t pars_rnd_str_token = {PARS_RND_STR_TOKEN};
+UNIV_INTERN pars_res_word_t pars_count_token = {PARS_COUNT_TOKEN};
+UNIV_INTERN pars_res_word_t pars_sum_token = {PARS_SUM_TOKEN};
+UNIV_INTERN pars_res_word_t pars_distinct_token = {PARS_DISTINCT_TOKEN};
+UNIV_INTERN pars_res_word_t pars_binary_token = {PARS_BINARY_TOKEN};
+UNIV_INTERN pars_res_word_t pars_blob_token = {PARS_BLOB_TOKEN};
+UNIV_INTERN pars_res_word_t pars_int_token = {PARS_INT_TOKEN};
+UNIV_INTERN pars_res_word_t pars_bigint_token = {PARS_BIGINT_TOKEN};
+UNIV_INTERN pars_res_word_t pars_char_token = {PARS_CHAR_TOKEN};
+UNIV_INTERN pars_res_word_t pars_float_token = {PARS_FLOAT_TOKEN};
+UNIV_INTERN pars_res_word_t pars_update_token = {PARS_UPDATE_TOKEN};
+UNIV_INTERN pars_res_word_t pars_asc_token = {PARS_ASC_TOKEN};
+UNIV_INTERN pars_res_word_t pars_desc_token = {PARS_DESC_TOKEN};
+UNIV_INTERN pars_res_word_t pars_open_token = {PARS_OPEN_TOKEN};
+UNIV_INTERN pars_res_word_t pars_close_token = {PARS_CLOSE_TOKEN};
+UNIV_INTERN pars_res_word_t pars_share_token = {PARS_SHARE_TOKEN};
+UNIV_INTERN pars_res_word_t pars_unique_token = {PARS_UNIQUE_TOKEN};
+UNIV_INTERN pars_res_word_t pars_clustered_token = {PARS_CLUSTERED_TOKEN};
+
+/** Global variable used to denote the '*' in SELECT * FROM.. */
+UNIV_INTERN 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<pars_user_func_t*>(
+ 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<pars_bound_id_t*>(
+ 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<pars_bound_lit_t*>(
+ 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: case PARS_SUM_TOKEN:
+ return(PARS_FUNC_AGGREGATE);
+
+ case PARS_TO_CHAR_TOKEN:
+ case PARS_TO_NUMBER_TOKEN:
+ case PARS_TO_BINARY_TOKEN:
+ case PARS_BINARY_TO_NUMBER_TOKEN:
+ case PARS_SUBSTR_TOKEN:
+ case PARS_CONCAT_TOKEN:
+ case PARS_LENGTH_TOKEN:
+ case PARS_INSTR_TOKEN:
+ case PARS_SYSDATE_TOKEN:
+ case PARS_NOTFOUND_TOKEN:
+ case PARS_PRINTF_TOKEN:
+ case PARS_ASSERT_TOKEN:
+ case PARS_RND_TOKEN:
+ case PARS_RND_STR_TOKEN:
+ case PARS_REPLSTR_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<func_node_t*>(
+ 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(func_node_list, pars_sym_tab_global->func_node_list,
+ node);
+ return(node);
+}
+
+/*********************************************************************//**
+Parses a function expression.
+@return own: function node in a query tree */
+UNIV_INTERN
+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% ?*/
+ if (*ptr == '%') {
+ op = (op == IB_LIKE_PREFIX) ? IB_LIKE_SUBSTR : IB_LIKE_SUFFIX;
+ }
+
+ 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<sym_node_t*>(
+ 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<ib_like_t>(
+ mach_read_from_4(static_cast<byte*>(dfield_get_data(dfield))));
+
+ switch (op_check) {
+ case IB_LIKE_PREFIX:
+ case IB_LIKE_SUFFIX:
+ case IB_LIKE_SUBSTR:
+ case IB_LIKE_EXACT:
+ break;
+
+ default:
+ ut_error;
+ }
+
+ mach_write_to_4(static_cast<byte*>(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;
+
+ case IB_LIKE_SUFFIX:
+ func = PARS_LIKE_TOKEN_SUFFIX;
+
+ /* Modify the original node */
+ /* Make it an '' empty string */
+ dfield_set_len(dfield, 0);
+
+ 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 + 1, ptr_len - 1);
+ break;
+
+ case IB_LIKE_SUBSTR:
+ func = PARS_LIKE_TOKEN_SUBSTR;
+
+ /* Modify the original node */
+ /* Make it an '' empty string */
+ dfield_set_len(dfield, 0);
+
+ 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 + 1, ptr_len - 2);
+ 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<char*>(dfield_get_data(dfield));
+ ptr_len = strlen(ptr);
+
+ if (ptr_len) {
+
+ func = pars_like_rebind(
+ static_cast<sym_node_t*>(arg), (byte*) ptr, ptr_len);
+ }
+
+ return(func);
+}
+/*********************************************************************//**
+Parses an operator expression.
+@return own: function node in a query tree */
+UNIV_INTERN
+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 */
+UNIV_INTERN
+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<order_node_t*>(
+ 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 PARS_SUM_TOKEN:
+ 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_CHAR_TOKEN:
+ case PARS_RND_STR_TOKEN:
+ ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT);
+ dtype_set(que_node_get_data_type(node), DATA_VARCHAR,
+ DATA_ENGLISH, 0);
+ 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_TO_NUMBER_TOKEN:
+ case PARS_BINARY_TO_NUMBER_TOKEN:
+ 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_SYSDATE_TOKEN:
+ ut_a(arg == NULL);
+ 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_RND_TOKEN:
+ ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT);
+ 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<func_node_t*>(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<sym_node_t*>(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)
+ && (ut_memcmp(sym_node->name, node->name,
+ node->name_len) == 0)) {
+
+ /* 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(col_var_list, 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<func_node_t*>(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<sym_node_t*>(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 == ut_strlen(col_name))
+ && (0 == ut_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<sym_node_t*>(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);
+
+ 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<sym_node_t*>(
+ 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,
+ ut_strlen(col_name));
+
+ select_node->select_list = que_node_list_add_last(
+ select_node->select_list, col_node);
+ }
+
+ table_node = static_cast<sym_node_t*>(
+ 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 */
+UNIV_INTERN
+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<func_node_t*>(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 */
+UNIV_INTERN
+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);
+
+ 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 */
+UNIV_INTERN
+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 */
+UNIV_INTERN
+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_get_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 */
+UNIV_INTERN
+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;
+
+ 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 */
+UNIV_INTERN
+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<col_assign_node_t*>(
+ 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<col_assign_node_t*>(
+ 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<col_assign_node_t*>(
+ 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;
+
+ upd_field_set_field_no(upd_field, dict_index_get_nth_col_pos(
+ clust_index, col_sym->col_no),
+ clust_index, NULL);
+ 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<col_assign_node_t*>(
+ 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 */
+UNIV_INTERN
+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);
+
+ /* 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 || (node->col_assign_list != NULL));
+
+ if (node->is_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_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 */
+UNIV_INTERN
+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 */
+ ibool is_unsigned, /*!< in: if TRUE, column is
+ UNSIGNED. */
+ ibool is_not_null) /*!< in: if TRUE, column is
+ NOT NULL. */
+{
+ ulint flags = 0;
+
+ if (is_not_null) {
+ flags |= DATA_NOT_NULL;
+ }
+
+ if (is_unsigned) {
+ flags |= DATA_UNSIGNED;
+ }
+
+ 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 if (type == &pars_binary_token) {
+ ut_a(len != 0);
+
+ dtype_set(dfield_get_type(dfield), DATA_FIXBINARY,
+ DATA_BINARY_TYPE | flags, len);
+ } else if (type == &pars_blob_token) {
+ ut_a(len == 0);
+
+ dtype_set(dfield_get_type(dfield), DATA_BLOB,
+ DATA_BINARY_TYPE | flags, 0);
+ } else {
+ ut_error;
+ }
+}
+
+/*********************************************************************//**
+Parses a variable declaration.
+@return own: symbol table node of type SYM_VAR */
+UNIV_INTERN
+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, FALSE);
+
+ return(node);
+}
+
+/*********************************************************************//**
+Parses a procedure parameter declaration.
+@return own: symbol table node of type SYM_VAR */
+UNIV_INTERN
+sym_node_t*
+pars_parameter_declaration(
+/*=======================*/
+ sym_node_t* node, /*!< in: symbol table node allocated for the
+ id of the parameter */
+ ulint param_type,
+ /*!< in: PARS_INPUT or PARS_OUTPUT */
+ pars_res_word_t* type) /*!< in: pointer to a type token */
+{
+ ut_a((param_type == PARS_INPUT) || (param_type == PARS_OUTPUT));
+
+ pars_variable_declaration(node, type);
+
+ node->param_type = param_type;
+
+ 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<que_common_t*>(node_list);
+
+ while (common) {
+ common->parent = parent;
+
+ common = static_cast<que_common_t*>(que_node_get_next(common));
+ }
+}
+
+/*********************************************************************//**
+Parses an elsif element.
+@return elsif node */
+UNIV_INTERN
+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<elsif_node_t*>(
+ 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 */
+UNIV_INTERN
+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<if_node_t*>(
+ 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<elsif_node_t*>(else_part);
+
+ elsif_node = static_cast<elsif_node_t*>(else_part);
+
+ while (elsif_node) {
+ pars_set_parent_in_list(elsif_node->stat_list, node);
+
+ elsif_node = static_cast<elsif_node_t*>(
+ 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 */
+UNIV_INTERN
+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<while_node_t*>(
+ 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 */
+UNIV_INTERN
+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<for_node_t*>(
+ 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 */
+UNIV_INTERN
+exit_node_t*
+pars_exit_statement(void)
+/*=====================*/
+{
+ exit_node_t* node;
+
+ node = static_cast<exit_node_t*>(
+ 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 */
+UNIV_INTERN
+return_node_t*
+pars_return_statement(void)
+/*=======================*/
+{
+ return_node_t* node;
+
+ node = static_cast<return_node_t*>(
+ 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 */
+UNIV_INTERN
+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<assign_node_t*>(
+ 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 */
+UNIV_INTERN
+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 */
+UNIV_INTERN
+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<fetch_node_t*>(
+ 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_get_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 */
+UNIV_INTERN
+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<open_node_t*>(
+ 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<open_node_op>(type);
+ node->cursor_def = cursor_decl->cursor_def;
+
+ return(node);
+}
+
+/*********************************************************************//**
+Parses a row_printf-statement.
+@return row_printf-statement node */
+UNIV_INTERN
+row_printf_node_t*
+pars_row_printf_statement(
+/*======================*/
+ sel_node_t* sel_node) /*!< in: select node */
+{
+ row_printf_node_t* node;
+
+ node = static_cast<row_printf_node_t*>(
+ 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 */
+UNIV_INTERN
+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 */
+UNIV_INTERN
+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 */
+UNIV_INTERN
+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_unsigned, /*!< in: if not NULL, column
+ is of type UNSIGNED. */
+ void* is_not_null) /*!< in: if not NULL, column
+ is of type NOT NULL. */
+{
+ ulint len2;
+
+ if (len) {
+ len2 = eval_node_get_int_val(len);
+ } else {
+ len2 = 0;
+ }
+
+ pars_set_dfield_type(que_node_get_val(sym_node), type, len2,
+ is_unsigned != NULL, is_not_null != NULL);
+
+ return(sym_node);
+}
+
+/*********************************************************************//**
+Parses a table creation operation.
+@return table create subgraph */
+UNIV_INTERN
+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 */
+ sym_node_t* compact, /* in: non-NULL if COMPACT table. */
+ sym_node_t* block_size, /* in: block size (can be NULL) */
+ void* not_fit_in_memory __attribute__((unused)))
+ /*!< in: a non-NULL pointer means that
+ this is a table which in simulations
+ should be simulated as not fitting
+ in memory; thread is put to sleep
+ to simulate disk accesses; NOTE that
+ this flag is not stored to the data
+ dictionary on disk, and the database
+ will forget about non-NULL value if
+ it has to reload the table definition
+ from disk */
+{
+ dict_table_t* table;
+ sym_node_t* column;
+ tab_node_t* node;
+ const dtype_t* dtype;
+ ulint n_cols;
+ ulint flags = 0;
+
+ if (compact != NULL) {
+ flags |= DICT_TF_COMPACT;
+ }
+
+ if (block_size != NULL) {
+ ulint size;
+ dfield_t* dfield;
+
+ dfield = que_node_get_val(block_size);
+
+ ut_a(dfield_get_len(dfield) == 4);
+ size = mach_read_from_4(static_cast<byte*>(
+ dfield_get_data(dfield)));
+
+
+ switch (size) {
+ case 0:
+ break;
+
+ case 1: case 2: case 4: case 8: case 16:
+ flags |= DICT_TF_COMPACT;
+ /* FTS-FIXME: needs the zip changes */
+ /* flags |= size << DICT_TF_COMPRESSED_SHIFT; */
+ break;
+
+ default:
+ ut_error;
+ }
+ }
+
+ n_cols = que_node_list_get_len(column_defs);
+
+ /* As the InnoDB SQL parser is for internal use only,
+ for creating some system tables, this function will only
+ create tables in the old (not compact) record format. */
+ table = dict_mem_table_create(table_sym->name, 0, n_cols, flags, 0);
+
+#ifdef UNIV_DEBUG
+ if (not_fit_in_memory != NULL) {
+ table->does_not_fit_in_memory = TRUE;
+ }
+#endif /* UNIV_DEBUG */
+ column = column_defs;
+
+ while (column) {
+ dtype = dfield_get_type(que_node_get_val(column));
+
+ dict_mem_table_add_col(table, table->heap,
+ column->name, dtype->mtype,
+ dtype->prtype, dtype->len);
+ column->resolved = TRUE;
+ column->token_type = SYM_COLUMN;
+
+ column = static_cast<sym_node_t*>(que_node_get_next(column));
+ }
+
+ node = tab_create_graph_create(table, pars_sym_tab_global->heap);
+
+ table_sym->resolved = TRUE;
+ table_sym->token_type = SYM_TABLE;
+
+ return(node);
+}
+
+/*********************************************************************//**
+Parses an index creation operation.
+@return index create subgraph */
+UNIV_INTERN
+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(table_sym->name, index_sym->name, 0,
+ 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<sym_node_t*>(que_node_get_next(column));
+ }
+
+ node = ind_create_graph_create(index, pars_sym_tab_global->heap);
+
+ 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 */
+UNIV_INTERN
+que_fork_t*
+pars_procedure_definition(
+/*======================*/
+ sym_node_t* sym_node, /*!< in: procedure id node in the symbol
+ table */
+ sym_node_t* param_list, /*!< in: parameter declaration list */
+ 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(NULL, NULL, QUE_FORK_PROCEDURE, heap);
+ fork->trx = NULL;
+
+ thr = que_thr_create(fork, heap);
+
+ node = static_cast<proc_node_t*>(
+ 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->param_list = param_list;
+ 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);
+}
+
+/*************************************************************//**
+Parses a stored procedure call, when this is not within another stored
+procedure, that is, the client issues a procedure call directly.
+In MySQL/InnoDB, stored InnoDB procedures are invoked via the
+parsed procedure tree, not via InnoDB SQL, so this function is not used.
+@return query graph */
+UNIV_INTERN
+que_fork_t*
+pars_stored_procedure_call(
+/*=======================*/
+ sym_node_t* sym_node __attribute__((unused)))
+ /*!< in: stored procedure name */
+{
+ ut_error;
+ return(NULL);
+}
+
+/*************************************************************//**
+Retrieves characters to the lexical analyzer. */
+UNIV_INTERN
+int
+pars_get_lex_chars(
+/*===============*/
+ char* buf, /*!< in/out: buffer where to copy */
+ int max_size) /*!< in: maximum number of characters which fit
+ in the buffer */
+{
+ int len;
+
+ len = pars_sym_tab_global->string_len
+ - pars_sym_tab_global->next_char_pos;
+ if (len == 0) {
+#ifdef YYDEBUG
+ /* fputs("SQL string ends\n", stderr); */
+#endif
+ return(0);
+ }
+
+ if (len > max_size) {
+ len = max_size;
+ }
+
+#ifdef UNIV_SQL_DEBUG
+ if (pars_print_lexed) {
+
+ if (len >= 5) {
+ len = 5;
+ }
+
+ fwrite(pars_sym_tab_global->sql_string
+ + pars_sym_tab_global->next_char_pos,
+ 1, len, stderr);
+ }
+#endif /* UNIV_SQL_DEBUG */
+
+ ut_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(len);
+}
+
+/*************************************************************//**
+Called by yyparse on error. */
+UNIV_INTERN
+void
+yyerror(
+/*====*/
+ const char* s __attribute__((unused)))
+ /*!< in: error message string */
+{
+ ut_ad(s);
+
+ fputs("PARSER ERROR: Syntax error in SQL string\n", stderr);
+
+ ut_error;
+}
+
+/*************************************************************//**
+Parses an SQL string returning the query graph.
+@return own: the query graph */
+UNIV_INTERN
+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(mutex_own(&(dict_sys->mutex)));
+
+ pars_sym_tab_global = sym_tab_create(heap);
+
+ pars_sym_tab_global->string_len = strlen(str);
+ pars_sym_tab_global->sql_string = static_cast<char*>(
+ 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. The fork created is of
+type QUE_FORK_MYSQL_INTERFACE.
+@return query thread node to run */
+UNIV_INTERN
+que_thr_t*
+pars_complete_graph_for_exec(
+/*=========================*/
+ que_node_t* node, /*!< in: root node for an incomplete
+ query graph */
+ trx_t* trx, /*!< in: transaction handle */
+ mem_heap_t* heap) /*!< in: memory heap from which allocated */
+{
+ que_fork_t* fork;
+ que_thr_t* thr;
+
+ fork = que_fork_create(NULL, NULL, QUE_FORK_MYSQL_INTERFACE, heap);
+ fork->trx = trx;
+
+ thr = que_thr_create(fork, heap);
+
+ thr->child = node;
+
+ que_node_set_parent(node, thr);
+
+ trx->graph = NULL;
+
+ return(thr);
+}
+
+/****************************************************************//**
+Create parser info struct.
+@return own: info struct */
+UNIV_INTERN
+pars_info_t*
+pars_info_create(void)
+/*==================*/
+{
+ pars_info_t* info;
+ mem_heap_t* heap;
+
+ heap = mem_heap_create(512);
+
+ info = static_cast<pars_info_t*>(mem_heap_alloc(heap, sizeof(*info)));
+
+ info->heap = heap;
+ info->funcs = NULL;
+ info->bound_lits = NULL;
+ info->bound_ids = NULL;
+ info->graph_owns_us = TRUE;
+
+ return(info);
+}
+
+/****************************************************************//**
+Free info struct and everything it contains. */
+UNIV_INTERN
+void
+pars_info_free(
+/*===========*/
+ pars_info_t* info) /*!< in, own: info struct */
+{
+ mem_heap_free(info->heap);
+}
+
+/****************************************************************//**
+Add bound literal. */
+UNIV_INTERN
+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<pars_bound_lit_t*>(
+ 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). */
+UNIV_INTERN
+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.*/
+UNIV_INTERN
+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.*/
+UNIV_INTERN
+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. */
+UNIV_INTERN
+void
+pars_info_add_int4_literal(
+/*=======================*/
+ pars_info_t* info, /*!< in: info struct */
+ const char* name, /*!< in: name */
+ lint val) /*!< in: value */
+{
+ byte* buf = static_cast<byte*>(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. */
+UNIV_INTERN
+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. */
+UNIV_INTERN
+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. */
+UNIV_INTERN
+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<byte*>(mem_heap_alloc(info->heap, 8));
+
+ mach_write_to_8(buf, val);
+
+ pars_info_add_literal(info, name, buf, 8, DATA_FIXBINARY, 0);
+}
+
+/****************************************************************//**
+Add user function. */
+UNIV_INTERN
+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<pars_user_func_t*>(
+ ib_vector_push(info->funcs, NULL));
+ puf->name = name;
+ }
+
+ puf->arg = arg;
+ puf->func = func;
+}
+
+/********************************************************************
+Add bound id. */
+UNIV_INTERN
+void
+pars_info_bind_id(
+/*==============*/
+ pars_info_t* info, /*!< in: info struct */
+ ibool copy_name, /* in: copy name if TRUE */
+ 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<pars_bound_id_t*>(
+ ib_vector_push(info->bound_ids, NULL));
+
+ bid->name = (copy_name)
+ ? mem_heap_strdup(info->heap, 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 user function with the given name.
+@return user func, or NULL if not found */
+UNIV_INTERN
+pars_user_func_t*
+pars_info_get_user_func(
+/*====================*/
+ pars_info_t* info, /*!< in: info struct */
+ const char* name) /*!< in: function name to find*/
+{
+ return(pars_info_lookup_user_func(info, name));
+}
+
+/****************************************************************//**
+Get bound literal with the given name.
+@return bound literal, or NULL if not found */
+UNIV_INTERN
+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));
+}