mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
cleanup: Item_func_case
reorder items in args[] array. Instead of
when1,then1,when2,then2,...[,case][,else]
sort them as
[case,]when1,when2,...,then1,then2,...[,else]
in this case all items used for comparison take a continuous part
of the array and can be aggregated directly. and all items that
can be returned take a continuous part of the array and can be
aggregated directly. Old code had to copy them to a temporary
array before aggreation, and then copy back (thd->change_item_tree)
everything that was changed.
this is a 10.3 version of bf1ca14ff3
This commit is contained in:
@ -1630,7 +1630,7 @@ c
|
||||
NULL
|
||||
Warnings:
|
||||
Note 1105 DBUG: [0] arg=1 handler=0 (bigint)
|
||||
Note 1105 DBUG: [1] arg=3 handler=1 (decimal)
|
||||
Note 1105 DBUG: [1] arg=2 handler=1 (decimal)
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# MDEV-11555 CASE with a mixture of TIME and DATETIME returns a wrong result
|
||||
@ -1649,9 +1649,9 @@ good was_bad_now_good
|
||||
one one
|
||||
Warnings:
|
||||
Note 1105 DBUG: [0] arg=1 handler=0 (time)
|
||||
Note 1105 DBUG: [1] arg=3 handler=0 (time)
|
||||
Note 1105 DBUG: [1] arg=2 handler=0 (time)
|
||||
Note 1105 DBUG: [0] arg=1 handler=0 (time)
|
||||
Note 1105 DBUG: [1] arg=3 handler=0 (time)
|
||||
Note 1105 DBUG: [2] arg=5 handler=2 (datetime)
|
||||
Note 1105 DBUG: [1] arg=2 handler=0 (time)
|
||||
Note 1105 DBUG: [2] arg=3 handler=2 (datetime)
|
||||
SET SESSION debug_dbug="-d,Predicant_to_list_comparator";
|
||||
SET SESSION debug_dbug="-d,Item_func_in";
|
||||
|
@ -2890,6 +2890,28 @@ Item_func_nullif::is_null()
|
||||
return (null_value= (!compare() ? 1 : args[2]->null_value));
|
||||
}
|
||||
|
||||
void Item_func_case::reorder_args(uint start)
|
||||
{
|
||||
/*
|
||||
Reorder args, to have at first the optional CASE expression, then all WHEN
|
||||
expressions, then all THEN expressions. And the optional ELSE expression
|
||||
at the end.
|
||||
|
||||
We reorder an even number of arguments, starting from start.
|
||||
*/
|
||||
uint count = (arg_count - start) / 2;
|
||||
const size_t size= sizeof(Item*) * count * 2;
|
||||
Item **arg_buffer= (Item **)my_safe_alloca(size);
|
||||
memcpy(arg_buffer, &args[start], size);
|
||||
for (uint i= 0; i < count; i++)
|
||||
{
|
||||
args[start + i]= arg_buffer[i*2];
|
||||
args[start + i + count]= arg_buffer[i*2 + 1];
|
||||
}
|
||||
my_safe_afree(arg_buffer, size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Find and return matching items for CASE or ELSE item if all compares
|
||||
@ -2917,8 +2939,8 @@ Item *Item_func_case_searched::find_item()
|
||||
uint count= when_count();
|
||||
for (uint i= 0 ; i < count ; i++)
|
||||
{
|
||||
if (args[2 * i]->val_bool())
|
||||
return args[2 * i + 1];
|
||||
if (args[i]->val_bool())
|
||||
return args[i + count];
|
||||
}
|
||||
Item **pos= Item_func_case_searched::else_expr_addr();
|
||||
return pos ? pos[0] : 0;
|
||||
@ -2930,7 +2952,7 @@ Item *Item_func_case_simple::find_item()
|
||||
/* Compare every WHEN argument with it and return the first match */
|
||||
uint idx;
|
||||
if (!Predicant_to_list_comparator::cmp(this, &idx, NULL))
|
||||
return args[idx + 1];
|
||||
return args[idx + when_count()];
|
||||
Item **pos= Item_func_case_simple::else_expr_addr();
|
||||
return pos ? pos[0] : 0;
|
||||
}
|
||||
@ -2940,7 +2962,7 @@ Item *Item_func_decode_oracle::find_item()
|
||||
{
|
||||
uint idx;
|
||||
if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx))
|
||||
return args[idx + 1];
|
||||
return args[idx + when_count()];
|
||||
Item **pos= Item_func_decode_oracle::else_expr_addr();
|
||||
return pos ? pos[0] : 0;
|
||||
}
|
||||
@ -3038,27 +3060,11 @@ bool Item_func_case::time_op(MYSQL_TIME *ltime)
|
||||
|
||||
bool Item_func_case::fix_fields(THD *thd, Item **ref)
|
||||
{
|
||||
/*
|
||||
buff should match stack usage from
|
||||
Item_func_case::val_int() -> Item_func_case::find_item()
|
||||
*/
|
||||
uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2];
|
||||
|
||||
if (!(arg_buffer= (Item**) thd->alloc(sizeof(Item*)*(arg_count))))
|
||||
return TRUE;
|
||||
|
||||
bool res= Item_func::fix_fields(thd, ref);
|
||||
|
||||
Item **pos= else_expr_addr();
|
||||
if (!pos || pos[0]->maybe_null)
|
||||
maybe_null= 1;
|
||||
|
||||
/*
|
||||
Call check_stack_overrun after fix_fields to be sure that stack variable
|
||||
is not optimized away
|
||||
*/
|
||||
if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
|
||||
return TRUE; // Fatal error flag is set!
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3068,8 +3074,11 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
|
||||
THD::change_item_tree() if needed.
|
||||
*/
|
||||
|
||||
static void change_item_tree_if_needed(THD *thd, Item **place, Item *new_value)
|
||||
static void propagate_and_change_item_tree(THD *thd, Item **place,
|
||||
COND_EQUAL *cond,
|
||||
const Item::Context &ctx)
|
||||
{
|
||||
Item *new_value= (*place)->propagate_equal_fields(thd, ctx, cond);
|
||||
if (new_value && *place != new_value)
|
||||
thd->change_item_tree(place, new_value);
|
||||
}
|
||||
@ -3087,8 +3096,8 @@ bool Item_func_case_simple::prepare_predicant_and_values(THD *thd,
|
||||
for (uint i= 0 ; i < ncases; i++)
|
||||
{
|
||||
if (nulls_equal ?
|
||||
add_value("case..when", this, i * 2 + 1) :
|
||||
add_value_skip_null("case..when", this, i * 2 + 1, &have_null))
|
||||
add_value("case..when", this, i + 1) :
|
||||
add_value_skip_null("case..when", this, i + 1, &have_null))
|
||||
return true;
|
||||
}
|
||||
all_values_added(&tmp, &type_cnt, &m_found_types);
|
||||
@ -3102,16 +3111,14 @@ bool Item_func_case_simple::prepare_predicant_and_values(THD *thd,
|
||||
void Item_func_case_searched::fix_length_and_dec()
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
Item **else_ptr= Item_func_case_searched::else_expr_addr();
|
||||
aggregate_then_and_else_arguments(thd, &args[1], when_count(), else_ptr);
|
||||
aggregate_then_and_else_arguments(thd, when_count());
|
||||
}
|
||||
|
||||
|
||||
void Item_func_case_simple::fix_length_and_dec()
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
Item **else_ptr= Item_func_case_simple::else_expr_addr();
|
||||
if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr))
|
||||
if (!aggregate_then_and_else_arguments(thd, when_count() + 1))
|
||||
aggregate_switch_and_when_arguments(thd, false);
|
||||
}
|
||||
|
||||
@ -3119,8 +3126,7 @@ void Item_func_case_simple::fix_length_and_dec()
|
||||
void Item_func_decode_oracle::fix_length_and_dec()
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
Item **else_ptr= Item_func_decode_oracle::else_expr_addr();
|
||||
if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr))
|
||||
if (!aggregate_then_and_else_arguments(thd, when_count() + 1))
|
||||
aggregate_switch_and_when_arguments(thd, true);
|
||||
}
|
||||
|
||||
@ -3130,40 +3136,16 @@ void Item_func_decode_oracle::fix_length_and_dec()
|
||||
and collations when string result
|
||||
|
||||
@param THD - current thd
|
||||
@param them_expr - the pointer to the leftmost THEN argument in args[]
|
||||
@param count - the number or THEN..ELSE pairs
|
||||
@param else_epxr - the pointer to the ELSE arguments in args[]
|
||||
(or NULL is there is not ELSE)
|
||||
@param start - an element in args to start aggregating from
|
||||
*/
|
||||
bool Item_func_case::aggregate_then_and_else_arguments(THD *thd,
|
||||
Item **then_expr,
|
||||
uint count,
|
||||
Item **else_expr)
|
||||
bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, uint start)
|
||||
{
|
||||
Item **agg= arg_buffer;
|
||||
uint nagg;
|
||||
|
||||
for (nagg= 0 ; nagg < count ; nagg++)
|
||||
agg[nagg]= then_expr[nagg * 2];
|
||||
|
||||
if (else_expr)
|
||||
agg[nagg++]= *else_expr;
|
||||
|
||||
if (aggregate_for_result(func_name(), agg, nagg, true))
|
||||
if (aggregate_for_result(func_name(), args + start, arg_count - start, true))
|
||||
return true;
|
||||
|
||||
if (fix_attributes(agg, nagg))
|
||||
if (fix_attributes(args + start, arg_count - start))
|
||||
return true;
|
||||
|
||||
/*
|
||||
Copy all modified THEN and ELSE items back to then_expr[] array.
|
||||
Some of the items might have been changed to Item_func_conv_charset.
|
||||
*/
|
||||
for (nagg= 0 ; nagg < count ; nagg++)
|
||||
change_item_tree_if_needed(thd, &then_expr[nagg * 2], agg[nagg]);
|
||||
|
||||
if (else_expr)
|
||||
change_item_tree_if_needed(thd, else_expr, agg[nagg++]);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3175,8 +3157,6 @@ bool Item_func_case::aggregate_then_and_else_arguments(THD *thd,
|
||||
bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd,
|
||||
bool nulls_eq)
|
||||
{
|
||||
Item **agg= arg_buffer;
|
||||
uint nagg;
|
||||
uint ncases= when_count();
|
||||
m_found_types= 0;
|
||||
if (prepare_predicant_and_values(thd, &m_found_types, nulls_eq))
|
||||
@ -3190,17 +3170,7 @@ bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd,
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
As the predicant expression and WHEN expressions
|
||||
are intermixed in args[] array THEN and ELSE items,
|
||||
extract the first expression and all WHEN expressions into
|
||||
a temporary array, to process them easier.
|
||||
*/
|
||||
agg[0]= args[0]; // The predicant
|
||||
for (nagg= 0; nagg < ncases ; nagg++)
|
||||
agg[nagg+1]= args[nagg * 2 + 1];
|
||||
nagg++;
|
||||
if (!(m_found_types= collect_cmp_types(agg, nagg)))
|
||||
if (!(m_found_types= collect_cmp_types(args, ncases + 1)))
|
||||
return true;
|
||||
|
||||
if (m_found_types & (1U << STRING_RESULT))
|
||||
@ -3229,17 +3199,8 @@ bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd,
|
||||
|
||||
CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END
|
||||
*/
|
||||
if (agg_arg_charsets_for_comparison(cmp_collation, agg, nagg))
|
||||
if (agg_arg_charsets_for_comparison(cmp_collation, args, ncases + 1))
|
||||
return true;
|
||||
/*
|
||||
Now copy first expression and all WHEN expressions back to args[]
|
||||
arrray, because some of the items might have been changed to converters
|
||||
(e.g. Item_func_conv_charset, or Item_string for constants).
|
||||
*/
|
||||
change_item_tree_if_needed(thd, &args[0], agg[0]);
|
||||
|
||||
for (nagg= 0; nagg < ncases; nagg++)
|
||||
change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg + 1]);
|
||||
}
|
||||
|
||||
if (make_unique_cmp_items(thd, cmp_collation.collation))
|
||||
@ -3256,78 +3217,57 @@ Item* Item_func_case_simple::propagate_equal_fields(THD *thd,
|
||||
const Type_handler *first_expr_cmp_handler;
|
||||
|
||||
first_expr_cmp_handler= args[0]->type_handler_for_comparison();
|
||||
for (uint i= 0; i < arg_count; i++)
|
||||
/*
|
||||
Cannot replace the CASE (the switch) argument if
|
||||
there are multiple comparison types were found, or found a single
|
||||
comparison type that is not equal to args[0]->cmp_type().
|
||||
|
||||
- Example: multiple comparison types, can't propagate:
|
||||
WHERE CASE str_column
|
||||
WHEN 'string' THEN TRUE
|
||||
WHEN 1 THEN TRUE
|
||||
ELSE FALSE END;
|
||||
|
||||
- Example: a single incompatible comparison type, can't propagate:
|
||||
WHERE CASE str_column
|
||||
WHEN DATE'2001-01-01' THEN TRUE
|
||||
ELSE FALSE END;
|
||||
|
||||
- Example: a single incompatible comparison type, can't propagate:
|
||||
WHERE CASE str_column
|
||||
WHEN 1 THEN TRUE
|
||||
ELSE FALSE END;
|
||||
|
||||
- Example: a single compatible comparison type, ok to propagate:
|
||||
WHERE CASE str_column
|
||||
WHEN 'str1' THEN TRUE
|
||||
WHEN 'str2' THEN TRUE
|
||||
ELSE FALSE END;
|
||||
*/
|
||||
if (m_found_types == (1UL << first_expr_cmp_handler->cmp_type()))
|
||||
propagate_and_change_item_tree(thd, &args[0], cond,
|
||||
Context(ANY_SUBST, first_expr_cmp_handler, cmp_collation.collation));
|
||||
|
||||
/*
|
||||
These arguments are in comparison.
|
||||
Allow invariants of the same value during propagation.
|
||||
Note, as we pass ANY_SUBST, none of the WHEN arguments will be
|
||||
replaced to zero-filled constants (only IDENTITY_SUBST allows this).
|
||||
Such a change for WHEN arguments would require rebuilding cmp_items.
|
||||
*/
|
||||
uint i, count= when_count();
|
||||
for (i= 1; i <= count; i++)
|
||||
{
|
||||
/*
|
||||
These arguments are in comparison.
|
||||
Allow invariants of the same value during propagation.
|
||||
Note, as we pass ANY_SUBST, none of the WHEN arguments will be
|
||||
replaced to zero-filled constants (only IDENTITY_SUBST allows this).
|
||||
Such a change for WHEN arguments would require rebuilding cmp_items.
|
||||
*/
|
||||
Item *new_item= 0;
|
||||
if (i == 0) // Then CASE (the switch) argument
|
||||
{
|
||||
/*
|
||||
Cannot replace the CASE (the switch) argument if
|
||||
there are multiple comparison types were found, or found a single
|
||||
comparison type that is not equal to args[0]->cmp_type().
|
||||
|
||||
- Example: multiple comparison types, can't propagate:
|
||||
WHERE CASE str_column
|
||||
WHEN 'string' THEN TRUE
|
||||
WHEN 1 THEN TRUE
|
||||
ELSE FALSE END;
|
||||
|
||||
- Example: a single incompatible comparison type, can't propagate:
|
||||
WHERE CASE str_column
|
||||
WHEN DATE'2001-01-01' THEN TRUE
|
||||
ELSE FALSE END;
|
||||
|
||||
- Example: a single incompatible comparison type, can't propagate:
|
||||
WHERE CASE str_column
|
||||
WHEN 1 THEN TRUE
|
||||
ELSE FALSE END;
|
||||
|
||||
- Example: a single compatible comparison type, ok to propagate:
|
||||
WHERE CASE str_column
|
||||
WHEN 'str1' THEN TRUE
|
||||
WHEN 'str2' THEN TRUE
|
||||
ELSE FALSE END;
|
||||
*/
|
||||
if (m_found_types == (1UL << first_expr_cmp_handler->cmp_type()))
|
||||
new_item= args[i]->propagate_equal_fields(thd,
|
||||
Context(
|
||||
ANY_SUBST,
|
||||
first_expr_cmp_handler,
|
||||
cmp_collation.collation),
|
||||
cond);
|
||||
}
|
||||
else if ((i % 2) == 1 && i != arg_count - 1) // WHEN arguments
|
||||
{
|
||||
/*
|
||||
These arguments are in comparison.
|
||||
Allow invariants of the same value during propagation.
|
||||
Note, as we pass ANY_SUBST, none of the WHEN arguments will be
|
||||
replaced to zero-filled constants (only IDENTITY_SUBST allows this).
|
||||
Such a change for WHEN arguments would require rebuilding cmp_items.
|
||||
*/
|
||||
Type_handler_hybrid_field_type tmp(first_expr_cmp_handler);
|
||||
if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison()))
|
||||
new_item= args[i]->propagate_equal_fields(thd,
|
||||
Context(
|
||||
ANY_SUBST,
|
||||
tmp.type_handler(),
|
||||
cmp_collation.collation),
|
||||
cond);
|
||||
}
|
||||
else // THEN and ELSE arguments (they are not in comparison)
|
||||
{
|
||||
new_item= args[i]->propagate_equal_fields(thd, Context_identity(), cond);
|
||||
}
|
||||
if (new_item && new_item != args[i])
|
||||
thd->change_item_tree(&args[i], new_item);
|
||||
Type_handler_hybrid_field_type tmp(first_expr_cmp_handler);
|
||||
if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison()))
|
||||
propagate_and_change_item_tree(thd, &args[i], cond,
|
||||
Context(ANY_SUBST, tmp.type_handler(), cmp_collation.collation));
|
||||
}
|
||||
|
||||
// THEN and ELSE arguments (they are not in comparison)
|
||||
for (; i < arg_count; i++)
|
||||
propagate_and_change_item_tree(thd, &args[i], cond, Context_identity());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -3339,9 +3279,9 @@ void Item_func_case::print_when_then_arguments(String *str,
|
||||
for (uint i=0 ; i < count ; i++)
|
||||
{
|
||||
str->append(STRING_WITH_LEN("when "));
|
||||
items[i * 2]->print_parenthesised(str, query_type, precedence());
|
||||
items[i]->print_parenthesised(str, query_type, precedence());
|
||||
str->append(STRING_WITH_LEN(" then "));
|
||||
items[i * 2 + 1]->print_parenthesised(str, query_type, precedence());
|
||||
items[i + count]->print_parenthesised(str, query_type, precedence());
|
||||
str->append(' ');
|
||||
}
|
||||
}
|
||||
@ -3381,6 +3321,28 @@ void Item_func_case_simple::print(String *str, enum_query_type query_type)
|
||||
}
|
||||
|
||||
|
||||
void Item_func_decode_oracle::print(String *str, enum_query_type query_type)
|
||||
{
|
||||
str->append(func_name());
|
||||
str->append('(');
|
||||
args[0]->print(str, query_type);
|
||||
for (uint i= 1, count= when_count() ; i <= count; i++)
|
||||
{
|
||||
str->append(',');
|
||||
args[i]->print(str, query_type);
|
||||
str->append(',');
|
||||
args[i+count]->print(str, query_type);
|
||||
}
|
||||
Item **else_expr= Item_func_case_simple::else_expr_addr();
|
||||
if (else_expr)
|
||||
{
|
||||
str->append(',');
|
||||
(*else_expr)->print(str, query_type);
|
||||
}
|
||||
str->append(')');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Coalesce - return first not NULL argument.
|
||||
*/
|
||||
@ -4910,17 +4872,14 @@ Item *Item_cond::propagate_equal_fields(THD *thd,
|
||||
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
|
||||
DBUG_ASSERT(arg_count == 0);
|
||||
List_iterator<Item> li(list);
|
||||
Item *item;
|
||||
while ((item= li++))
|
||||
while (li++)
|
||||
{
|
||||
/*
|
||||
The exact value of the second parameter to propagate_equal_fields()
|
||||
The exact value of the last parameter to propagate_and_change_item_tree()
|
||||
is not important at this point. Item_func derivants will create and
|
||||
pass their own context to the arguments.
|
||||
*/
|
||||
Item *new_item= item->propagate_equal_fields(thd, Context_boolean(), cond);
|
||||
if (new_item && new_item != item)
|
||||
thd->change_item_tree(li.ref(), new_item);
|
||||
propagate_and_change_item_tree(thd, li.ref(), cond, Context_boolean());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -2104,15 +2104,13 @@ class Item_func_case :public Item_func_case_expression
|
||||
protected:
|
||||
String tmp_value;
|
||||
DTCollation cmp_collation;
|
||||
Item **arg_buffer;
|
||||
bool aggregate_then_and_else_arguments(THD *thd,
|
||||
Item **items, uint count,
|
||||
Item **else_expr);
|
||||
bool aggregate_then_and_else_arguments(THD *thd, uint count);
|
||||
virtual Item **else_expr_addr() const= 0;
|
||||
virtual Item *find_item()= 0;
|
||||
void print_when_then_arguments(String *str, enum_query_type query_type,
|
||||
Item **items, uint count);
|
||||
void print_else_argument(String *str, enum_query_type query_type, Item *item);
|
||||
void reorder_args(uint start);
|
||||
public:
|
||||
Item_func_case(THD *thd, List<Item> &list)
|
||||
:Item_func_case_expression(thd, list)
|
||||
@ -2129,13 +2127,6 @@ public:
|
||||
enum precedence precedence() const { return BETWEEN_PRECEDENCE; }
|
||||
CHARSET_INFO *compare_collation() const { return cmp_collation.collation; }
|
||||
bool need_parentheses_in_default() { return true; }
|
||||
Item *build_clone(THD *thd)
|
||||
{
|
||||
Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd);
|
||||
if (clone)
|
||||
clone->arg_buffer= 0;
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -2156,6 +2147,7 @@ public:
|
||||
:Item_func_case(thd, list)
|
||||
{
|
||||
DBUG_ASSERT(arg_count >= 2);
|
||||
reorder_args(0);
|
||||
}
|
||||
void print(String *str, enum_query_type query_type);
|
||||
void fix_length_and_dec();
|
||||
@ -2200,6 +2192,7 @@ public:
|
||||
m_found_types(0)
|
||||
{
|
||||
DBUG_ASSERT(arg_count >= 3);
|
||||
reorder_args(1);
|
||||
}
|
||||
void cleanup()
|
||||
{
|
||||
@ -2233,8 +2226,7 @@ public:
|
||||
:Item_func_case_simple(thd, list)
|
||||
{ }
|
||||
const char *func_name() const { return "decode_oracle"; }
|
||||
void print(String *str, enum_query_type query_type)
|
||||
{ Item_func::print(str, query_type); }
|
||||
void print(String *str, enum_query_type query_type);
|
||||
void fix_length_and_dec();
|
||||
Item *find_item();
|
||||
Item *get_copy(THD *thd)
|
||||
|
Reference in New Issue
Block a user