summaryrefslogtreecommitdiff
path: root/sql/item_cmpfunc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r--sql/item_cmpfunc.cc93
1 files changed, 91 insertions, 2 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index a65cbbf37d9..4f18d849461 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -35,6 +35,7 @@
#define PCRE2_STATIC 1 /* Important on Windows */
#include "pcre2.h" /* pcre2 header file */
+#include "my_json_writer.h"
/*
Compare row signature of two expressions
@@ -417,9 +418,18 @@ bool Item_func::setup_args_and_comparator(THD *thd, Arg_comparator *cmp)
if (args[0]->cmp_type() == STRING_RESULT &&
args[1]->cmp_type() == STRING_RESULT)
{
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+
DTCollation tmp;
- if (agg_arg_charsets_for_comparison(tmp, args, 2))
- return true;
+ bool ret= agg_arg_charsets_for_comparison(tmp, args, 2);
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
+ if (ret)
+ return ret;
+
cmp->m_compare_collation= tmp.collation;
}
// Convert constants when compared to int/year field
@@ -4341,6 +4351,56 @@ Item_func_in::fix_fields(THD *thd, Item **ref)
}
+Item *Item_func_in::in_predicate_to_equality_transformer(THD *thd, uchar *arg)
+{
+ if (!array || have_null || !all_items_are_consts(args + 1, arg_count - 1))
+ return this; /* Transformation is not applicable */
+
+ /*
+ If all elements in the array of constant values are equal and there are
+ no NULLs in the list then clause
+ - "a IN (e1,..,en)" can be converted to "a = e1"
+ - "a NOT IN (e1,..,en)" can be converted to "a != e1".
+ This means an object of Item_func_in can be replaced with an object of
+ Item_func_eq for IN (e1,..,en) clause or Item_func_ne for
+ NOT IN (e1,...,en).
+ */
+
+ /*
+ Since the array is sorted it's enough to compare the first and the last
+ elements to tell whether all elements are equal
+ */
+ if (array->compare_elems(0, array->used_count - 1))
+ {
+ /* Not all elements are equal, transformation is not possible */
+ return this;
+ }
+
+ Json_writer_object trace_wrapper(thd);
+ trace_wrapper.add("transformation", "in_predicate_to_equality")
+ .add("before", this);
+
+ Item *new_item= nullptr;
+ if (negated)
+ new_item= new (thd->mem_root) Item_func_ne(thd, args[0], args[1]);
+ else
+ new_item= new (thd->mem_root) Item_func_eq(thd, args[0], args[1]);
+ if (new_item)
+ {
+ new_item->set_name(thd, name);
+ if (new_item->fix_fields(thd, &new_item))
+ {
+ /*
+ If there are any problems during fixing fields, there is no need to
+ return an error, just discard the transformation
+ */
+ new_item= this;
+ }
+ }
+ trace_wrapper.add("after", new_item);
+ return new_item;
+}
+
bool
Item_func_in::eval_not_null_tables(void *opt_arg)
{
@@ -5162,6 +5222,35 @@ Item *Item_cond::transform(THD *thd, Item_transformer transformer, uchar *arg)
/**
+ Transform an Item_cond object with a transformer callback function.
+
+ This is like transform() but doesn't use change_item_tree(),
+ because top-level expression is stored in prep_where/prep_on anyway and
+ is restored from there, there is no need to use change_item_tree().
+
+ Furthermore, it can be actually harmful to use it, if build_equal_items()
+ had replaced Item_eq with Item_equal and deleted list_node with a pointer
+ to Item_eq. In this case rollback_item_tree_changes() would modify the
+ deleted list_node.
+*/
+Item *Item_cond::top_level_transform(THD *thd, Item_transformer transformer, uchar *arg)
+{
+ DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
+
+ List_iterator<Item> li(list);
+ Item *item;
+ while ((item= li++))
+ {
+ Item *new_item= item->top_level_transform(thd, transformer, arg);
+ if (!new_item)
+ return 0;
+ *li.ref()= new_item;
+ }
+ return Item_func::transform(thd, transformer, arg);
+}
+
+
+/**
Compile Item_cond object with a processor and a transformer
callback functions.