mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 10:56:12 +01:00 
			
		
		
		
	 14521992b0
			
		
	
	
	14521992b0
	
	
	
		
			
			eval_substr(): Do not allow the string buffer of the first argument to be extended. Trim the length of the returned result if it would exceed the end of the buffer.
		
			
				
	
	
		
			643 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			643 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*****************************************************************************
 | |
| 
 | |
| Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
 | |
| Copyright (c) 2019, 2021, MariaDB Corporation.
 | |
| 
 | |
| This program is free software; you can redistribute it and/or modify it under
 | |
| the terms of the GNU General Public License as published by the Free Software
 | |
| Foundation; version 2 of the License.
 | |
| 
 | |
| This program is distributed in the hope that it will be useful, but WITHOUT
 | |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | |
| FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License along with
 | |
| this program; if not, write to the Free Software Foundation, Inc.,
 | |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
 | |
| 
 | |
| *****************************************************************************/
 | |
| 
 | |
| /**************************************************//**
 | |
| @file eval/eval0eval.cc
 | |
| SQL evaluator: evaluates simple data structures, like expressions, in
 | |
| a query graph
 | |
| 
 | |
| Created 12/29/1997 Heikki Tuuri
 | |
| *******************************************************/
 | |
| 
 | |
| #include "eval0eval.h"
 | |
| #include "data0data.h"
 | |
| #include "row0sel.h"
 | |
| #include "rem0cmp.h"
 | |
| 
 | |
| /** Dummy adress used when we should allocate a buffer of size 0 in
 | |
| eval_node_alloc_val_buf */
 | |
| 
 | |
| static byte	eval_dummy;
 | |
| 
 | |
| /*************************************************************************
 | |
| Gets the like node from the node */
 | |
| UNIV_INLINE
 | |
| que_node_t*
 | |
| que_node_get_like_node(
 | |
| /*===================*/
 | |
| 				/* out: next node in a list of nodes */
 | |
| 	que_node_t*     node)   /* in: node in a list */
 | |
| {
 | |
| 	return(((sym_node_t*) node)->like_node);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Allocate a buffer from global dynamic memory for a value of a que_node.
 | |
| NOTE that this memory must be explicitly freed when the query graph is
 | |
| freed. If the node already has an allocated buffer, that buffer is freed
 | |
| here. NOTE that this is the only function where dynamic memory should be
 | |
| allocated for a query node val field.
 | |
| @return pointer to allocated buffer */
 | |
| byte*
 | |
| eval_node_alloc_val_buf(
 | |
| /*====================*/
 | |
| 	que_node_t*	node,	/*!< in: query graph node; sets the val field
 | |
| 				data field to point to the new buffer, and
 | |
| 				len field equal to size */
 | |
| 	ulint		size)	/*!< in: buffer size */
 | |
| {
 | |
| 	dfield_t*	dfield;
 | |
| 	byte*		data;
 | |
| 
 | |
| 	ut_ad(que_node_get_type(node) == QUE_NODE_SYMBOL
 | |
| 	      || que_node_get_type(node) == QUE_NODE_FUNC);
 | |
| 
 | |
| 	dfield = que_node_get_val(node);
 | |
| 
 | |
| 	data = static_cast<byte*>(dfield_get_data(dfield));
 | |
| 
 | |
| 	if (data != &eval_dummy) {
 | |
| 		ut_free(data);
 | |
| 	}
 | |
| 
 | |
| 	if (size == 0) {
 | |
| 		data = &eval_dummy;
 | |
| 	} else {
 | |
| 		data = static_cast<byte*>(ut_malloc_nokey(size));
 | |
| 	}
 | |
| 
 | |
| 	que_node_set_val_buf_size(node, size);
 | |
| 
 | |
| 	dfield_set_data(dfield, data, size);
 | |
| 
 | |
| 	return(data);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Free the buffer from global dynamic memory for a value of a que_node,
 | |
| if it has been allocated in the above function. The freeing for pushed
 | |
| column values is done in sel_col_prefetch_buf_free. */
 | |
| void
 | |
| eval_node_free_val_buf(
 | |
| /*===================*/
 | |
| 	que_node_t*	node)	/*!< in: query graph node */
 | |
| {
 | |
| 	dfield_t*	dfield;
 | |
| 	byte*		data;
 | |
| 
 | |
| 	ut_ad(que_node_get_type(node) == QUE_NODE_SYMBOL
 | |
| 	      || que_node_get_type(node) == QUE_NODE_FUNC);
 | |
| 
 | |
| 	dfield = que_node_get_val(node);
 | |
| 
 | |
| 	data = static_cast<byte*>(dfield_get_data(dfield));
 | |
| 
 | |
| 	if (que_node_get_val_buf_size(node) > 0) {
 | |
| 		ut_a(data);
 | |
| 
 | |
| 		ut_free(data);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*********************************************************************
 | |
| Evaluates a LIKE comparison node.
 | |
| @return the result of the comparison */
 | |
| UNIV_INLINE
 | |
| ibool
 | |
| eval_cmp_like(
 | |
| /*==========*/
 | |
| 	que_node_t*	arg1,		/* !< in: left operand */
 | |
| 	que_node_t*	arg2)		/* !< in: right operand */
 | |
| {
 | |
| 	ib_like_t	op;
 | |
| 	que_node_t*	arg3;
 | |
| 	que_node_t*	arg4;
 | |
| 	const dfield_t*	dfield;
 | |
| 
 | |
| 	arg3 = que_node_get_like_node(arg2);
 | |
| 
 | |
| 	/* Get the comparison type operator */
 | |
| 	ut_a(arg3);
 | |
| 
 | |
| 	dfield = que_node_get_val(arg3);
 | |
| 	ut_ad(dtype_get_mtype(dfield_get_type(dfield)) == DATA_INT);
 | |
| 	op = static_cast<ib_like_t>(
 | |
| 		mach_read_from_4(static_cast<const byte*>(
 | |
| 					 dfield_get_data(dfield))));
 | |
| 
 | |
| 	switch (op) {
 | |
| 	case IB_LIKE_PREFIX:
 | |
| 		arg4 = que_node_get_next(arg3);
 | |
| 		return(!cmp_dfield_dfield_like_prefix(que_node_get_val(arg1),
 | |
| 						      que_node_get_val(arg4)));
 | |
| 	case IB_LIKE_EXACT:
 | |
| 		return(!cmp_dfield_dfield(que_node_get_val(arg1),
 | |
| 					  que_node_get_val(arg2)));
 | |
| 	}
 | |
| 
 | |
| 	ut_error;
 | |
| 	return(FALSE);
 | |
| }
 | |
| 
 | |
| /*********************************************************************
 | |
| Evaluates a comparison node.
 | |
| @return the result of the comparison */
 | |
| ibool
 | |
| eval_cmp(
 | |
| /*=====*/
 | |
| 	func_node_t*	cmp_node)	/*!< in: comparison node */
 | |
| {
 | |
| 	que_node_t*	arg1;
 | |
| 	que_node_t*	arg2;
 | |
| 	int		res;
 | |
| 	ibool		val	= FALSE; /* remove warning */
 | |
| 
 | |
| 	ut_ad(que_node_get_type(cmp_node) == QUE_NODE_FUNC);
 | |
| 
 | |
| 	arg1 = cmp_node->args;
 | |
| 	arg2 = que_node_get_next(arg1);
 | |
| 
 | |
| 	switch (cmp_node->func) {
 | |
| 	case '<':
 | |
| 	case '=':
 | |
| 	case '>':
 | |
| 	case PARS_LE_TOKEN:
 | |
| 	case PARS_NE_TOKEN:
 | |
| 	case PARS_GE_TOKEN:
 | |
| 		res = cmp_dfield_dfield(
 | |
| 			que_node_get_val(arg1), que_node_get_val(arg2));
 | |
| 
 | |
| 		switch (cmp_node->func) {
 | |
| 		case '<':
 | |
| 			val = (res < 0);
 | |
| 			break;
 | |
| 		case '=':
 | |
| 			val = (res == 0);
 | |
| 			break;
 | |
| 		case '>':
 | |
| 			val = (res > 0);
 | |
| 			break;
 | |
| 		case PARS_LE_TOKEN:
 | |
| 			val = (res <= 0);
 | |
| 			break;
 | |
| 		case PARS_NE_TOKEN:
 | |
| 			val = (res != 0);
 | |
| 			break;
 | |
| 		case PARS_GE_TOKEN:
 | |
| 			val = (res >= 0);
 | |
| 			break;
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		val = eval_cmp_like(arg1, arg2);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	eval_node_set_ibool_val(cmp_node, val);
 | |
| 
 | |
| 	return(val);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates a logical operation node. */
 | |
| UNIV_INLINE
 | |
| void
 | |
| eval_logical(
 | |
| /*=========*/
 | |
| 	func_node_t*	logical_node)	/*!< in: logical operation node */
 | |
| {
 | |
| 	que_node_t*	arg1;
 | |
| 	que_node_t*	arg2;
 | |
| 	ibool		val1;
 | |
| 	ibool		val2 = 0; /* remove warning */
 | |
| 	ibool		val = 0;  /* remove warning */
 | |
| 	int		func;
 | |
| 
 | |
| 	ut_ad(que_node_get_type(logical_node) == QUE_NODE_FUNC);
 | |
| 
 | |
| 	arg1 = logical_node->args;
 | |
| 	arg2 = que_node_get_next(arg1); /* arg2 is NULL if func is 'NOT' */
 | |
| 
 | |
| 	val1 = eval_node_get_ibool_val(arg1);
 | |
| 
 | |
| 	if (arg2) {
 | |
| 		val2 = eval_node_get_ibool_val(arg2);
 | |
| 	}
 | |
| 
 | |
| 	func = logical_node->func;
 | |
| 
 | |
| 	if (func == PARS_AND_TOKEN) {
 | |
| 		val = val1 & val2;
 | |
| 	} else if (func == PARS_OR_TOKEN) {
 | |
| 		val = val1 | val2;
 | |
| 	} else if (func == PARS_NOT_TOKEN) {
 | |
| 		val = TRUE - val1;
 | |
| 	} else {
 | |
| 		ut_error;
 | |
| 	}
 | |
| 
 | |
| 	eval_node_set_ibool_val(logical_node, val);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates an arithmetic operation node. */
 | |
| UNIV_INLINE
 | |
| void
 | |
| eval_arith(
 | |
| /*=======*/
 | |
| 	func_node_t*	arith_node)	/*!< in: arithmetic operation node */
 | |
| {
 | |
| 	que_node_t*	arg1;
 | |
| 	que_node_t*	arg2;
 | |
| 	lint		val1;
 | |
| 	lint		val2 = 0; /* remove warning */
 | |
| 	lint		val;
 | |
| 	int		func;
 | |
| 
 | |
| 	ut_ad(que_node_get_type(arith_node) == QUE_NODE_FUNC);
 | |
| 
 | |
| 	arg1 = arith_node->args;
 | |
| 	arg2 = que_node_get_next(arg1); /* arg2 is NULL if func is unary '-' */
 | |
| 
 | |
| 	val1 = eval_node_get_int_val(arg1);
 | |
| 
 | |
| 	if (arg2) {
 | |
| 		val2 = eval_node_get_int_val(arg2);
 | |
| 	}
 | |
| 
 | |
| 	func = arith_node->func;
 | |
| 
 | |
| 	if (func == '+') {
 | |
| 		val = val1 + val2;
 | |
| 	} else if ((func == '-') && arg2) {
 | |
| 		val = val1 - val2;
 | |
| 	} else if (func == '-') {
 | |
| 		val = -val1;
 | |
| 	} else if (func == '*') {
 | |
| 		val = val1 * val2;
 | |
| 	} else {
 | |
| 		ut_ad(func == '/');
 | |
| 		val = val1 / val2;
 | |
| 	}
 | |
| 
 | |
| 	eval_node_set_int_val(arith_node, val);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates an aggregate operation node. */
 | |
| UNIV_INLINE
 | |
| void
 | |
| eval_aggregate(
 | |
| /*===========*/
 | |
| 	func_node_t*	node)	/*!< in: aggregate operation node */
 | |
| {
 | |
| 	lint		val;
 | |
| 
 | |
| 	ut_ad(que_node_get_type(node) == QUE_NODE_FUNC);
 | |
| 
 | |
| 	val = eval_node_get_int_val(node);
 | |
| 
 | |
| 	ut_a(node->func == PARS_COUNT_TOKEN);
 | |
| 	val = val + 1;
 | |
| 	eval_node_set_int_val(node, val);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates a notfound-function node. */
 | |
| UNIV_INLINE
 | |
| void
 | |
| eval_notfound(
 | |
| /*==========*/
 | |
| 	func_node_t*	func_node)	/*!< in: function node */
 | |
| {
 | |
| 	sym_node_t*	cursor;
 | |
| 	sel_node_t*	sel_node;
 | |
| 	ibool		ibool_val;
 | |
| 
 | |
| 	ut_ad(func_node->func == PARS_NOTFOUND_TOKEN);
 | |
| 
 | |
| 	cursor = static_cast<sym_node_t*>(func_node->args);
 | |
| 
 | |
| 	ut_ad(que_node_get_type(cursor) == QUE_NODE_SYMBOL);
 | |
| 
 | |
| 	if (cursor->token_type == SYM_LIT) {
 | |
| 		ut_ad(!memcmp(dfield_get_data(que_node_get_val(cursor)),
 | |
| 			      "SQL", 3));
 | |
| 		sel_node = cursor->sym_table->query_graph->last_sel_node;
 | |
| 	} else {
 | |
| 		sel_node = cursor->alias->cursor_def;
 | |
| 	}
 | |
| 
 | |
| 	if (sel_node->state == SEL_NODE_NO_MORE_ROWS) {
 | |
| 		ibool_val = TRUE;
 | |
| 	} else {
 | |
| 		ibool_val = FALSE;
 | |
| 	}
 | |
| 
 | |
| 	eval_node_set_ibool_val(func_node, ibool_val);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates a substr-function node. */
 | |
| UNIV_INLINE
 | |
| void
 | |
| eval_substr(
 | |
| /*========*/
 | |
| 	func_node_t*	func_node)	/*!< in: function node */
 | |
| {
 | |
| 	que_node_t*	arg1;
 | |
| 	que_node_t*	arg2;
 | |
| 	que_node_t*	arg3;
 | |
| 	dfield_t*	dfield;
 | |
| 	byte*		str1;
 | |
| 	ulint		len1;
 | |
| 	ulint		len2;
 | |
| 
 | |
| 	arg1 = func_node->args;
 | |
| 	arg2 = que_node_get_next(arg1);
 | |
| 
 | |
| 	ut_ad(func_node->func == PARS_SUBSTR_TOKEN);
 | |
| 
 | |
| 	arg3 = que_node_get_next(arg2);
 | |
| 
 | |
| 	str1 = static_cast<byte*>(dfield_get_data(que_node_get_val(arg1)));
 | |
| 
 | |
| 	const ulint str1_len = dfield_get_len(que_node_get_val(arg1));
 | |
| 
 | |
| 	len1 = (ulint) eval_node_get_int_val(arg2);
 | |
| 	len2 = (ulint) eval_node_get_int_val(arg3);
 | |
| 
 | |
| 	dfield = que_node_get_val(func_node);
 | |
| 
 | |
| 	if (len1 > str1_len) {
 | |
| 		len2 = 0;
 | |
| 	} else {
 | |
| 		str1 += len1;
 | |
| 		if (len2 > str1_len - len1) {
 | |
| 			len2 = str1_len - len1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	dfield_set_data(dfield, str1, len2);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates an instr-function node. */
 | |
| static
 | |
| void
 | |
| eval_instr(
 | |
| /*=======*/
 | |
| 	func_node_t*	func_node)	/*!< in: function node */
 | |
| {
 | |
| 	que_node_t*	arg1;
 | |
| 	que_node_t*	arg2;
 | |
| 	dfield_t*	dfield1;
 | |
| 	dfield_t*	dfield2;
 | |
| 	lint		int_val;
 | |
| 	byte*		str1;
 | |
| 	byte*		str2;
 | |
| 	byte		match_char;
 | |
| 	ulint		len1;
 | |
| 	ulint		len2;
 | |
| 	ulint		i;
 | |
| 	ulint		j;
 | |
| 
 | |
| 	arg1 = func_node->args;
 | |
| 	arg2 = que_node_get_next(arg1);
 | |
| 
 | |
| 	dfield1 = que_node_get_val(arg1);
 | |
| 	dfield2 = que_node_get_val(arg2);
 | |
| 
 | |
| 	str1 = static_cast<byte*>(dfield_get_data(dfield1));
 | |
| 	str2 = static_cast<byte*>(dfield_get_data(dfield2));
 | |
| 
 | |
| 	len1 = dfield_get_len(dfield1);
 | |
| 	len2 = dfield_get_len(dfield2);
 | |
| 
 | |
| 	if (len2 == 0) {
 | |
| 		ut_error;
 | |
| 	}
 | |
| 
 | |
| 	match_char = str2[0];
 | |
| 
 | |
| 	for (i = 0; i < len1; i++) {
 | |
| 		/* In this outer loop, the number of matched characters is 0 */
 | |
| 
 | |
| 		if (str1[i] == match_char) {
 | |
| 
 | |
| 			if (i + len2 > len1) {
 | |
| 
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			for (j = 1;; j++) {
 | |
| 				/* We have already matched j characters */
 | |
| 
 | |
| 				if (j == len2) {
 | |
| 					int_val = lint(i) + 1;
 | |
| 
 | |
| 					goto match_found;
 | |
| 				}
 | |
| 
 | |
| 				if (str1[i + j] != str2[j]) {
 | |
| 
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	int_val = 0;
 | |
| 
 | |
| match_found:
 | |
| 	eval_node_set_int_val(func_node, int_val);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates a predefined function node. */
 | |
| static
 | |
| void
 | |
| eval_concat(
 | |
| /*========*/
 | |
| 	func_node_t*	func_node)	/*!< in: function node */
 | |
| {
 | |
| 	que_node_t*	arg;
 | |
| 	dfield_t*	dfield;
 | |
| 	byte*		data;
 | |
| 	ulint		len;
 | |
| 	ulint		len1;
 | |
| 
 | |
| 	arg = func_node->args;
 | |
| 	len = 0;
 | |
| 
 | |
| 	while (arg) {
 | |
| 		len1 = dfield_get_len(que_node_get_val(arg));
 | |
| 
 | |
| 		len += len1;
 | |
| 
 | |
| 		arg = que_node_get_next(arg);
 | |
| 	}
 | |
| 
 | |
| 	data = eval_node_ensure_val_buf(func_node, len);
 | |
| 
 | |
| 	arg = func_node->args;
 | |
| 	len = 0;
 | |
| 
 | |
| 	while (arg) {
 | |
| 		dfield = que_node_get_val(arg);
 | |
| 		len1 = dfield_get_len(dfield);
 | |
| 
 | |
| 		memcpy(data + len, dfield_get_data(dfield), len1);
 | |
| 
 | |
| 		len += len1;
 | |
| 
 | |
| 		arg = que_node_get_next(arg);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates a predefined function node. If the first argument is an integer,
 | |
| this function looks at the second argument which is the integer length in
 | |
| bytes, and converts the integer to a VARCHAR.
 | |
| If the first argument is of some other type, this function converts it to
 | |
| BINARY. */
 | |
| UNIV_INLINE
 | |
| void
 | |
| eval_to_binary(
 | |
| /*===========*/
 | |
| 	func_node_t*	func_node)	/*!< in: function node */
 | |
| {
 | |
| 	que_node_t*	arg1;
 | |
| 	que_node_t*	arg2;
 | |
| 	dfield_t*	dfield;
 | |
| 	byte*		str1;
 | |
| 	ulint		len;
 | |
| 	ulint		len1;
 | |
| 
 | |
| 	arg1 = func_node->args;
 | |
| 
 | |
| 	str1 = static_cast<byte*>(dfield_get_data(que_node_get_val(arg1)));
 | |
| 
 | |
| 	if (dtype_get_mtype(que_node_get_data_type(arg1)) != DATA_INT) {
 | |
| 
 | |
| 		len = dfield_get_len(que_node_get_val(arg1));
 | |
| 
 | |
| 		dfield = que_node_get_val(func_node);
 | |
| 
 | |
| 		dfield_set_data(dfield, str1, len);
 | |
| 
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	arg2 = que_node_get_next(arg1);
 | |
| 
 | |
| 	len1 = (ulint) eval_node_get_int_val(arg2);
 | |
| 
 | |
| 	if (len1 > 4) {
 | |
| 
 | |
| 		ut_error;
 | |
| 	}
 | |
| 
 | |
| 	dfield = que_node_get_val(func_node);
 | |
| 
 | |
| 	dfield_set_data(dfield, str1 + (4 - len1), len1);
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluate LENGTH(). */
 | |
| inline void eval_length(func_node_t* func_node)
 | |
| {
 | |
| 	eval_node_set_int_val(func_node,
 | |
| 			      dfield_get_len(que_node_get_val
 | |
| 					     (func_node->args)));
 | |
| }
 | |
| 
 | |
| /*****************************************************************//**
 | |
| Evaluates a function node. */
 | |
| void
 | |
| eval_func(
 | |
| /*======*/
 | |
| 	func_node_t*	func_node)	/*!< in: function node */
 | |
| {
 | |
| 	que_node_t*	arg;
 | |
| 	ulint		fclass;
 | |
| 
 | |
| 	ut_ad(que_node_get_type(func_node) == QUE_NODE_FUNC);
 | |
| 
 | |
| 	fclass = func_node->fclass;
 | |
| 	const int func = func_node->func;
 | |
| 
 | |
| 	arg = func_node->args;
 | |
| 
 | |
| 	/* Evaluate first the argument list */
 | |
| 	while (arg) {
 | |
| 		eval_exp(arg);
 | |
| 
 | |
| 		/* The functions are not defined for SQL null argument
 | |
| 		values, except for eval_cmp and notfound */
 | |
| 
 | |
| 		if (dfield_is_null(que_node_get_val(arg))
 | |
| 		    && (fclass != PARS_FUNC_CMP)
 | |
| 		    && (func != PARS_NOTFOUND_TOKEN)) {
 | |
| 			ut_error;
 | |
| 		}
 | |
| 
 | |
| 		arg = que_node_get_next(arg);
 | |
| 	}
 | |
| 
 | |
| 	switch (fclass) {
 | |
| 	case PARS_FUNC_CMP:
 | |
| 		eval_cmp(func_node);
 | |
| 		return;
 | |
| 	case PARS_FUNC_ARITH:
 | |
| 		eval_arith(func_node);
 | |
| 		return;
 | |
| 	case PARS_FUNC_AGGREGATE:
 | |
| 		eval_aggregate(func_node);
 | |
| 		return;
 | |
| 	case PARS_FUNC_PREDEFINED:
 | |
| 		switch (func) {
 | |
| 		case PARS_NOTFOUND_TOKEN:
 | |
| 			eval_notfound(func_node);
 | |
| 			return;
 | |
| 		case PARS_SUBSTR_TOKEN:
 | |
| 			eval_substr(func_node);
 | |
| 			return;
 | |
| 		case PARS_INSTR_TOKEN:
 | |
| 			eval_instr(func_node);
 | |
| 			return;
 | |
| 		case PARS_CONCAT_TOKEN:
 | |
| 			eval_concat(func_node);
 | |
| 			return;
 | |
| 		case PARS_TO_BINARY_TOKEN:
 | |
| 			eval_to_binary(func_node);
 | |
| 			return;
 | |
| 		case PARS_LENGTH_TOKEN:
 | |
| 			eval_length(func_node);
 | |
| 			return;
 | |
| 		default:
 | |
| 			ut_error;
 | |
| 		}
 | |
| 	case PARS_FUNC_LOGICAL:
 | |
| 		eval_logical(func_node);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ut_error;
 | |
| }
 |