Commit fee46323 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-15758 Split Item_bool_func::get_mm_leaf() into virtual methods in Field and Type_handler

parent 6bfeace1
......@@ -48,6 +48,9 @@ class Item_equal;
class Virtual_tmp_table;
class Qualified_column_ident;
class Table_ident;
class SEL_ARG;
class RANGE_OPT_PARAM;
struct KEY_PART;
enum enum_check_fields
{
......@@ -847,6 +850,10 @@ class Field: public Value_source
to be quoted when used in constructing an SQL query.
*/
virtual bool str_needs_quotes() { return FALSE; }
const Type_handler *type_handler_for_comparison() const
{
return type_handler()->type_handler_for_comparison();
}
Item_result result_type () const
{
return type_handler()->result_type();
......@@ -1375,6 +1382,59 @@ class Field: public Value_source
}
int warn_if_overflow(int op_result);
Copy_func *get_identical_copy_func() const;
bool can_optimize_scalar_range(const RANGE_OPT_PARAM *param,
const KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op,
const Item *value) const;
uchar *make_key_image(MEM_ROOT *mem_root, const KEY_PART *key_part);
SEL_ARG *get_mm_leaf_int(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value,
bool unsigned_field);
/*
Make a leaf tree for the cases when the value was stored
to the field exactly, without any truncation, rounding or adjustments.
For example, if we stored an INT value into an INT column,
and value->save_in_field_no_warnings() returned 0,
we know that the value was stored exactly.
*/
SEL_ARG *stored_field_make_mm_leaf_exact(RANGE_OPT_PARAM *param,
KEY_PART *key_part,
scalar_comparison_op op,
Item *value);
/*
Make a leaf tree for the cases when we don't know if
the value was stored to the field without any data loss,
or was modified to a smaller or a greater value.
Used for the data types whose methods Field::store*()
silently adjust the value. This is the most typical case.
*/
SEL_ARG *stored_field_make_mm_leaf(RANGE_OPT_PARAM *param,
KEY_PART *key_part,
scalar_comparison_op op, Item *value);
/*
Make a leaf tree when an INT value was stored into a field of INT type,
and some truncation happened. Tries to adjust the range search condition
when possible, e.g. "tinytint < 300" -> "tinyint <= 127".
Can also return SEL_ARG_IMPOSSIBLE(), and NULL (not sargable).
*/
SEL_ARG *stored_field_make_mm_leaf_bounded_int(RANGE_OPT_PARAM *param,
KEY_PART *key_part,
scalar_comparison_op op,
Item *value,
bool unsigned_field);
/*
Make a leaf tree when some truncation happened during
value->save_in_field_no_warning(this), and we cannot yet adjust the range
search condition for the current combination of the field and the value
data types.
Returns SEL_ARG_IMPOSSIBLE() for "=" and "<=>".
Returns NULL (not sargable) for other comparison operations.
*/
SEL_ARG *stored_field_make_mm_leaf_truncated(RANGE_OPT_PARAM *prm,
scalar_comparison_op,
Item *value);
public:
void set_table_name(String *alias)
{
......@@ -1547,6 +1607,10 @@ class Field: public Value_source
const Item *item,
bool is_eq_func) const;
virtual SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value)= 0;
bool can_optimize_outer_join_table_elimination(const Item_bool_func *cond,
const Item *item) const
{
......@@ -1708,6 +1772,9 @@ class Field_num :public Field {
{
return pos_in_interval_val_real(min, max);
}
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value);
};
......@@ -1764,6 +1831,9 @@ class Field_str :public Field {
return pos_in_interval_val_str(min, max, length_size());
}
bool test_if_equality_guarantees_uniqueness(const Item *const_item) const;
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value);
};
/* base class for Field_string, Field_varstring and Field_blob */
......@@ -2061,6 +2131,12 @@ class Field_int :public Field_num
uint32 prec= type_limits_int()->precision();
return Information_schema_numeric_attributes(prec, 0);
}
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value)
{
return get_mm_leaf_int(param, key_part, cond, op, value, unsigned_flag);
}
};
......@@ -2543,6 +2619,9 @@ class Field_temporal: public Field {
{
return true;
}
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value);
};
......@@ -2823,14 +2902,31 @@ class Field_year :public Field_tiny {
};
class Field_date :public Field_temporal_with_date {
class Field_date_common: public Field_temporal_with_date
{
public:
Field_date_common(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
const LEX_CSTRING *field_name_arg)
:Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH,
null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{}
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value);
};
class Field_date :public Field_date_common
{
void store_TIME(MYSQL_TIME *ltime);
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
public:
Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
:Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg) {}
:Field_date_common(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg) {}
const Type_handler *type_handler() const { return &type_handler_date; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
......@@ -2858,14 +2954,15 @@ class Field_date :public Field_temporal_with_date {
};
class Field_newdate :public Field_temporal_with_date {
class Field_newdate :public Field_date_common
{
void store_TIME(MYSQL_TIME *ltime);
bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const;
public:
Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
:Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
:Field_date_common(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{}
const Type_handler *type_handler() const { return &type_handler_newdate; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
......@@ -4180,6 +4277,12 @@ class Field_bit :public Field {
}
void hash(ulong *nr, ulong *nr2);
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value)
{
return get_mm_leaf_int(param, key_part, cond, op, value, true);
}
private:
virtual size_t do_last_null_byte() const;
int save_field_metadata(uchar *first_byte);
......
......@@ -6507,7 +6507,7 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg)
comparison context, and it's safe to replace it to the constant from
item_equal.
*/
DBUG_ASSERT(type_handler()->type_handler_for_comparison()->cmp_type() ==
DBUG_ASSERT(type_handler_for_comparison()->cmp_type() ==
item_equal->compare_type_handler()->cmp_type());
return const_item2;
}
......@@ -9941,73 +9941,14 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
{
Item_result res_type=item_cmp_type(field->result_type(),
item->result_type());
/*
We have to check field->cmp_type() instead of res_type,
as result_type() - and thus res_type - can never be TIME_RESULT (yet).
*/
if (field->cmp_type() == TIME_RESULT)
{
MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time;
if (field->type() == MYSQL_TYPE_TIME)
{
field->get_time(&field_time);
item->get_time(&item_time);
}
else
{
field->get_date(&field_time, TIME_INVALID_DATES);
item->get_date(&item_time, TIME_INVALID_DATES);
if (item_time.time_type == MYSQL_TIMESTAMP_TIME)
if (time_to_datetime(thd, &item_time, item_time_cmp= &item_time2))
return 1;
}
return my_time_compare(&field_time, item_time_cmp);
}
if (res_type == STRING_RESULT)
Type_handler_hybrid_field_type cmp(field->type_handler_for_comparison());
if (cmp.aggregate_for_comparison(item->type_handler_for_comparison()))
{
char item_buff[MAX_FIELD_WIDTH];
char field_buff[MAX_FIELD_WIDTH];
String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin);
String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin);
String *item_result= item->val_str(&item_tmp);
/*
Some implementations of Item::val_str(String*) actually modify
the field Item::null_value, hence we can't check it earlier.
*/
if (item->null_value)
return 0;
String *field_result= field->val_str(&field_tmp);
return sortcmp(field_result, item_result, field->charset());
}
if (res_type == INT_RESULT)
return 0; // Both are of type int
if (res_type == DECIMAL_RESULT)
{
my_decimal item_buf, *item_val,
field_buf, *field_val;
item_val= item->val_decimal(&item_buf);
if (item->null_value)
return 0;
field_val= field->val_decimal(&field_buf);
return my_decimal_cmp(field_val, item_val);
}
/*
The patch for Bug#13463415 started using this function for comparing
BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode.
Prefixing the auto variables with volatile fixes the problem....
*/
volatile double result= item->val_real();
if (item->null_value)
// At fix_fields() time we checked that "field" and "item" are comparable
DBUG_ASSERT(0);
return 0;
volatile double field_result= field->val_real();
if (field_result < result)
return -1;
else if (field_result > result)
return 1;
return 0;
}
return cmp.type_handler()->stored_field_cmp_to_item(thd, field, item);
}
......
......@@ -78,6 +78,20 @@ class Item_func :public Item_func_or_sum,
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC,
NEG_FUNC, GSYSVAR_FUNC, IN_OPTIMIZER_FUNC, DYNCOL_FUNC,
JSON_EXTRACT_FUNC };
static scalar_comparison_op functype_to_scalar_comparison_op(Functype type)
{
switch (type) {
case EQ_FUNC: return SCALAR_CMP_EQ;
case EQUAL_FUNC: return SCALAR_CMP_EQUAL;
case LT_FUNC: return SCALAR_CMP_LT;
case LE_FUNC: return SCALAR_CMP_LE;
case GE_FUNC: return SCALAR_CMP_GE;
case GT_FUNC: return SCALAR_CMP_GT;
default: break;
}
DBUG_ASSERT(0);
return SCALAR_CMP_EQ;
}
enum Type type() const { return FUNC_ITEM; }
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
Item_func(THD *thd): Item_func_or_sum(thd)
......
......@@ -1892,6 +1892,118 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_,
left=right= &null_element;
}
/*
A number of helper classes:
SEL_ARG_LE, SEL_ARG_LT, SEL_ARG_GT, SEL_ARG_GE,
to share the code between:
Field::stored_field_make_mm_leaf()
Field::stored_field_make_mm_leaf_exact()
*/
class SEL_ARG_LE: public SEL_ARG
{
public:
SEL_ARG_LE(const uchar *key, Field *field)
:SEL_ARG(field, key, key)
{
if (!field->real_maybe_null())
min_flag= NO_MIN_RANGE; // From start
else
{
min_value= is_null_string;
min_flag= NEAR_MIN; // > NULL
}
}
};
class SEL_ARG_LT: public SEL_ARG_LE
{
public:
/*
Use this constructor if value->save_in_field() went precisely,
without any data rounding or truncation.
*/
SEL_ARG_LT(const uchar *key, Field *field)
:SEL_ARG_LE(key, field)
{ max_flag= NEAR_MAX; }
/*
Use this constructor if value->save_in_field() returned success,
but we don't know if rounding or truncation happened
(as some Field::store() do not report minor data changes).
*/
SEL_ARG_LT(THD *thd, const uchar *key, Field *field, Item *value)
:SEL_ARG_LE(key, field)
{
if (stored_field_cmp_to_item(thd, field, value) == 0)
max_flag= NEAR_MAX;
}
};
class SEL_ARG_GT: public SEL_ARG
{
public:
/*
Use this constructor if value->save_in_field() went precisely,
without any data rounding or truncation.
*/
SEL_ARG_GT(const uchar *key, const KEY_PART *key_part, Field *field)
:SEL_ARG(field, key, key)
{
// Don't use open ranges for partial key_segments
if (!(key_part->flag & HA_PART_KEY_SEG))
min_flag= NEAR_MIN;
max_flag= NO_MAX_RANGE;
}
/*
Use this constructor if value->save_in_field() returned success,
but we don't know if rounding or truncation happened
(as some Field::store() do not report minor data changes).
*/
SEL_ARG_GT(THD *thd, const uchar *key,
const KEY_PART *key_part, Field *field, Item *value)
:SEL_ARG(field, key, key)
{
// Don't use open ranges for partial key_segments
if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
(stored_field_cmp_to_item(thd, field, value) <= 0))
min_flag= NEAR_MIN;
max_flag= NO_MAX_RANGE;
}
};
class SEL_ARG_GE: public SEL_ARG
{
public:
/*
Use this constructor if value->save_in_field() went precisely,
without any data rounding or truncation.
*/
SEL_ARG_GE(const uchar *key, Field *field)
:SEL_ARG(field, key, key)
{
max_flag= NO_MAX_RANGE;
}
/*
Use this constructor if value->save_in_field() returned success,
but we don't know if rounding or truncation happened
(as some Field::store() do not report minor data changes).
*/
SEL_ARG_GE(THD *thd, const uchar *key,
const KEY_PART *key_part, Field *field, Item *value)
:SEL_ARG(field, key, key)
{
// Don't use open ranges for partial key_segments
if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
(stored_field_cmp_to_item(thd, field, value) < 0))
min_flag= NEAR_MIN;
max_flag= NO_MAX_RANGE;
}
};
SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent,
SEL_ARG **next_arg)
{
......@@ -8015,52 +8127,112 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
SEL_ARG *
Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
Field *field, KEY_PART *key_part,
Item_func::Functype type, Item *value)
Item_func::Functype functype, Item *value)
{
uint maybe_null=(uint) field->real_maybe_null();
SEL_ARG *tree= 0;
MEM_ROOT *alloc= param->mem_root;
uchar *str;
int err;
DBUG_ENTER("Item_bool_func::get_mm_leaf");
DBUG_ASSERT(value); // IS NULL and IS NOT NULL are handled separately
if (key_part->image_type != Field::itRAW)
DBUG_RETURN(0); // e.g. SPATIAL index
DBUG_RETURN(field->get_mm_leaf(param, key_part, this,
functype_to_scalar_comparison_op(functype),
value));
}
if (param->using_real_indexes &&
!field->optimize_range(param->real_keynr[key_part->key],
key_part->part) &&
type != EQ_FUNC &&
type != EQUAL_FUNC)
goto end; // Can't optimize this
if (!field->can_optimize_range(this, value,
type == EQUAL_FUNC || type == EQ_FUNC))
goto end;
bool Field::can_optimize_scalar_range(const RANGE_OPT_PARAM *param,
const KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op,
const Item *value) const
{
bool is_eq_func= op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL;
if ((param->using_real_indexes &&
!optimize_range(param->real_keynr[key_part->key],
key_part->part) && !is_eq_func) ||
!can_optimize_range(cond, value, is_eq_func))
return false;
return true;
}
uchar *Field::make_key_image(MEM_ROOT *mem_root, const KEY_PART *key_part)
{
DBUG_ENTER("Field::make_key_image");
uint maybe_null= (uint) real_maybe_null();
uchar *str;
if (!(str= (uchar*) alloc_root(mem_root, key_part->store_length + 1)))
DBUG_RETURN(0);
if (maybe_null)
*str= (uchar) is_real_null(); // Set to 1 if null
get_key_image(str + maybe_null, key_part->length, key_part->image_type);
DBUG_RETURN(str);
}
SEL_ARG *Field::stored_field_make_mm_leaf_truncated(RANGE_OPT_PARAM *param,
scalar_comparison_op op,
Item *value)
{
DBUG_ENTER("Field::stored_field_make_mm_leaf_truncated");
if ((op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) &&
value->result_type() == item_cmp_type(result_type(),
value->result_type()))
DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this));
/*
TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE
for the cases like int_field > 999999999999999999999999 as well.
*/
DBUG_RETURN(0);
}
err= value->save_in_field_no_warnings(field, 1);
SEL_ARG *Field_num::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value)
{
DBUG_ENTER("Field_num::get_mm_leaf");
if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
DBUG_RETURN(0);
int err= value->save_in_field_no_warnings(this, 1);
if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
DBUG_RETURN(&null_element);
if (err > 0 && cmp_type() != value->result_type())
DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value));
DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
}
SEL_ARG *Field_temporal::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value)
{
DBUG_ENTER("Field_temporal::get_mm_leaf");
if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
DBUG_RETURN(0);
int err= value->save_in_field_no_warnings(this, 1);
if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
DBUG_RETURN(&null_element);
if (err > 0)
{
if (field->type_handler() == &type_handler_enum ||
field->type_handler() == &type_handler_set)
{
if (type == EQ_FUNC || type == EQUAL_FUNC)
tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
goto end;
}
DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value));
DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
}
if (err == 2 && field->cmp_type() == STRING_RESULT)
{
if (type == EQ_FUNC || type == EQUAL_FUNC)
tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
else
tree= NULL; /* Cannot infer anything */
goto end;
}
if (err == 3 && field->type() == FIELD_TYPE_DATE)
SEL_ARG *Field_date_common::get_mm_leaf(RANGE_OPT_PARAM *prm,
KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op,
Item *value)
{
DBUG_ENTER("Field_date_common::get_mm_leaf");
if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
DBUG_RETURN(0);
int err= value->save_in_field_no_warnings(this, 1);
if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
DBUG_RETURN(&null_element);
if (err > 0)
{
if (err == 3)
{
/*
We were saving DATETIME into a DATE column, the conversion went ok
......@@ -8080,81 +8252,86 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
be done together with other types at the end of this function
(grep for stored_field_cmp_to_item)
*/
if (type == EQ_FUNC || type == EQUAL_FUNC)
{
tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
goto end;
}
// Continue with processing non-equality ranges
}
else if (field->cmp_type() != value->result_type())
{
if ((type == EQ_FUNC || type == EQUAL_FUNC) &&
value->result_type() == item_cmp_type(field->result_type(),
value->result_type()))
{
tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
goto end;
}
else
{
/*
TODO: We should return trees of the type SEL_ARG::IMPOSSIBLE
for the cases like int_field > 999999999999999999999999 as well.
*/
tree= 0;
goto end;
}
}
/*
guaranteed at this point: err > 0; field and const of same type
If an integer got bounded (e.g. to within 0..255 / -128..127)
for < or >, set flags as for <= or >= (no NEAR_MAX / NEAR_MIN)
*/
else if (err == 1 && field->result_type() == INT_RESULT)
{
if (type == EQ_FUNC || type == EQUAL_FUNC) // e.g. tinyint = 200
{
tree= new (alloc) SEL_ARG_IMPOSSIBLE(field);
goto end;
}
if (type == LT_FUNC && (value->val_int() > 0))
type= LE_FUNC;
else if (type == GT_FUNC &&
(field->type() != FIELD_TYPE_BIT) &&
!((Field_num*)field)->unsigned_flag &&
!value->unsigned_flag &&
(value->val_int() < 0))
type= GE_FUNC;
if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL)
DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this));
DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
}
DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value));
}
else if (err < 0)
DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
}
SEL_ARG *Field_str::get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value)
{
DBUG_ENTER("Field_str::get_mm_leaf");
if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
DBUG_RETURN(0);
int err= value->save_in_field_no_warnings(this, 1);
if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
DBUG_RETURN(&null_element);
if (err > 0)
{
/* This happens when we try to insert a NULL field in a not null column */
tree= &null_element; // cmp with NULL is never TRUE
goto end;
if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL)
DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this));
DBUG_RETURN(NULL); /* Cannot infer anything */
}
DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
}
/*
Any sargable predicate except "<=>" involving NULL as a constant is always
FALSE
*/
if (type != EQUAL_FUNC && field->is_real_null())
SEL_ARG *Field::get_mm_leaf_int(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value,
bool unsigned_field)
{
DBUG_ENTER("Field::get_mm_leaf_int");
if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
DBUG_RETURN(0);
int err= value->save_in_field_no_warnings(this, 1);
if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
DBUG_RETURN(&null_element);
if (err > 0)
{
tree= &null_element;
goto end;
if (value->result_type() != INT_RESULT)
DBUG_RETURN(stored_field_make_mm_leaf_truncated(prm, op, value));
else
DBUG_RETURN(stored_field_make_mm_leaf_bounded_int(prm, key_part,
op, value,
unsigned_field));
}
str= (uchar*) alloc_root(alloc, key_part->store_length+1);
if (!str)
goto end;
if (maybe_null)
*str= (uchar) field->is_real_null(); // Set to 1 if null
field->get_key_image(str+maybe_null, key_part->length,
key_part->image_type);
if (!(tree= new (alloc) SEL_ARG(field, str, str)))
goto end; // out of memory
if (value->result_type() != INT_RESULT)
DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
DBUG_RETURN(stored_field_make_mm_leaf_exact(prm, key_part, op, value));
}
/*
This method is called when:
- value->save_in_field_no_warnings() returned err > 0
- and both field and "value" are of integer data types
If an integer got bounded (e.g. to within 0..255 / -128..127)
for < or >, set flags as for <= or >= (no NEAR_MAX / NEAR_MIN)
*/
SEL_ARG *Field::stored_field_make_mm_leaf_bounded_int(RANGE_OPT_PARAM *param,
KEY_PART *key_part,
scalar_comparison_op op,
Item *value,
bool unsigned_field)
{
DBUG_ENTER("Field::stored_field_make_mm_leaf_bounded_int");
if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) // e.g. tinyint = 200
DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this));
longlong item_val= value->val_int();
if (op == SCALAR_CMP_LT && item_val > 0)
op= SCALAR_CMP_LE; // e.g. rewrite (tinyint < 200) to (tinyint <= 127)
else if (op == SCALAR_CMP_GT && !unsigned_field &&
!value->unsigned_flag && item_val < 0)
op= SCALAR_CMP_GE; // e.g. rewrite (tinyint > -200) to (tinyint >= -128)
/*
Check if we are comparing an UNSIGNED integer with a negative constant.
......@@ -8167,66 +8344,74 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
negative integers (which otherwise fails because at query execution time
negative integers are cast to unsigned if compared with unsigned).
*/
if (field->result_type() == INT_RESULT &&
value->result_type() == INT_RESULT &&
((field->type() == FIELD_TYPE_BIT ||
((Field_num *) field)->unsigned_flag) &&
!value->unsigned_flag))
if (unsigned_field && !value->unsigned_flag && item_val < 0)
{
longlong item_val= value->val_int();
if (item_val < 0)
{
if (type == LT_FUNC || type == LE_FUNC)
{
tree->type= SEL_ARG::IMPOSSIBLE;
goto end;
}
if (type == GT_FUNC || type == GE_FUNC)
{
tree= 0;
goto end;
}
}
if (op == SCALAR_CMP_LT || op == SCALAR_CMP_LE) // e.g. uint < -1
DBUG_RETURN(new (param->mem_root) SEL_ARG_IMPOSSIBLE(this));
if (op == SCALAR_CMP_GT || op == SCALAR_CMP_GE) // e.g. uint > -1
DBUG_RETURN(0);
}
DBUG_RETURN(stored_field_make_mm_leaf_exact(param, key_part, op, value));
}
switch (type) {
case LT_FUNC:
if (stored_field_cmp_to_item(param->thd, field, value) == 0)
tree->max_flag=NEAR_MAX;
/* fall through */
case LE_FUNC:
if (!maybe_null)
tree->min_flag=NO_MIN_RANGE; /* From start */
else
{ // > NULL
tree->min_value=is_null_string;
tree->min_flag=NEAR_MIN;
}
break;
case GT_FUNC:
/* Don't use open ranges for partial key_segments */
if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
(stored_field_cmp_to_item(param->thd, field, value) <= 0))
tree->min_flag=NEAR_MIN;
tree->max_flag= NO_MAX_RANGE;
break;
case GE_FUNC:
/* Don't use open ranges for partial key_segments */
if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
(stored_field_cmp_to_item(param->thd, field, value) < 0))
tree->min_flag= NEAR_MIN;
tree->max_flag=NO_MAX_RANGE;
break;
case EQ_FUNC:
case EQUAL_FUNC:
break;
default:
DBUG_ASSERT(0);
SEL_ARG *Field::stored_field_make_mm_leaf(RANGE_OPT_PARAM *param,
KEY_PART *key_part,
scalar_comparison_op op,
Item *value)
{
DBUG_ENTER("Field::stored_field_make_mm_leaf");
THD *thd= param->thd;
MEM_ROOT *mem_root= param->mem_root;
uchar *str;
if (!(str= make_key_image(param->mem_root, key_part)))
DBUG_RETURN(0);
switch (op) {
case SCALAR_CMP_LE:
DBUG_RETURN(new (mem_root) SEL_ARG_LE(str, this));
case SCALAR_CMP_LT:
DBUG_RETURN(new (mem_root) SEL_ARG_LT(thd, str, this, value));
case SCALAR_CMP_GT:
DBUG_RETURN(new (mem_root) SEL_ARG_GT(thd, str, key_part, this, value));
case SCALAR_CMP_GE:
DBUG_RETURN(new (mem_root) SEL_ARG_GE(thd, str, key_part, this, value));
case SCALAR_CMP_EQ:
case SCALAR_CMP_EQUAL:
DBUG_RETURN(new (mem_root) SEL_ARG(this, str, str));
break;
}
DBUG_ASSERT(0);
DBUG_RETURN(NULL);
}
end:
DBUG_RETURN(tree);
SEL_ARG *Field::stored_field_make_mm_leaf_exact(RANGE_OPT_PARAM *param,
KEY_PART *key_part,
scalar_comparison_op op,
Item *value)
{
DBUG_ENTER("Field::stored_field_make_mm_leaf_exact");
uchar *str;
if (!(str= make_key_image(param->mem_root, key_part)))
DBUG_RETURN(0);
switch (op) {
case SCALAR_CMP_LE:
DBUG_RETURN(new (param->mem_root) SEL_ARG_LE(str, this));
case SCALAR_CMP_LT:
DBUG_RETURN(new (param->mem_root) SEL_ARG_LT(str, this));
case SCALAR_CMP_GT:
DBUG_RETURN(new (param->mem_root) SEL_ARG_GT(str, key_part, this));
case SCALAR_CMP_GE:
DBUG_RETURN(new (param->mem_root) SEL_ARG_GE(str, this));
case SCALAR_CMP_EQ:
case SCALAR_CMP_EQUAL:
DBUG_RETURN(new (param->mem_root) SEL_ARG(this, str, str));
break;
}
DBUG_ASSERT(0);
DBUG_RETURN(NULL);
}
......
......@@ -6574,3 +6574,92 @@ void Type_handler_timestamp_common::
else
c->set_handler(&type_handler_timestamp);
}
/***************************************************************************/
int Type_handler_temporal_with_date::stored_field_cmp_to_item(THD *thd,
Field *field,
Item *item) const
{
MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time;
field->get_date(&field_time, TIME_INVALID_DATES);
item->get_date(&item_time, TIME_INVALID_DATES);
if (item_time.time_type == MYSQL_TIMESTAMP_TIME &&
time_to_datetime(thd, &item_time, item_time_cmp= &item_time2))
return 1;
return my_time_compare(&field_time, item_time_cmp);
}
int Type_handler_time_common::stored_field_cmp_to_item(THD *thd,
Field *field,
Item *item) const
{
MYSQL_TIME field_time, item_time;
field->get_time(&field_time);
item->get_time(&item_time);
return my_time_compare(&field_time, &item_time);
}
int Type_handler_string_result::stored_field_cmp_to_item(THD *thd,
Field *field,
Item *item) const
{
StringBuffer<MAX_FIELD_WIDTH> item_tmp;
StringBuffer<MAX_FIELD_WIDTH> field_tmp;
String *item_result= item->val_str(&item_tmp);
/*
Some implementations of Item::val_str(String*) actually modify
the field Item::null_value, hence we can't check it earlier.
*/
if (item->null_value)
return 0;
String *field_result= field->val_str(&field_tmp);
return sortcmp(field_result, item_result, field->charset());
}
int Type_handler_int_result::stored_field_cmp_to_item(THD *thd,
Field *field,
Item *item) const
{
DBUG_ASSERT(0); // Not used yet
return 0;
}
int Type_handler_decimal_result::stored_field_cmp_to_item(THD *thd,
Field *field,
Item *item) const
{
my_decimal item_buf, *item_val, field_buf, *field_val;
item_val= item->val_decimal(&item_buf);
if (item->null_value)
return 0;
field_val= field->val_decimal(&field_buf);
return my_decimal_cmp(field_val, item_val);
}
int Type_handler_real_result::stored_field_cmp_to_item(THD *thd,
Field *field,
Item *item) const
{
/*
The patch for Bug#13463415 started using this function for comparing
BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode.
Prefixing the auto variables with volatile fixes the problem....
*/
volatile double result= item->val_real();
if (item->null_value)
return 0;
volatile double field_result= field->val_real();
if (field_result < result)
return -1;
else if (field_result > result)
return 1;
return 0;
}
......@@ -77,6 +77,17 @@ struct SORT_FIELD_ATTR;
class Vers_history_point;
enum scalar_comparison_op
{
SCALAR_CMP_EQ,
SCALAR_CMP_EQUAL,
SCALAR_CMP_LT,
SCALAR_CMP_LE,
SCALAR_CMP_GE,
SCALAR_CMP_GT
};
/**
Class Time is designed to store valid TIME values.
......@@ -1184,6 +1195,8 @@ class Type_handler
{
return this;
}
virtual int
stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const= 0;
virtual CHARSET_INFO *charset_for_protocol(const Item *item) const;
virtual const Type_handler*
type_handler_adjusted_to_max_octet_length(uint max_octet_length,
......@@ -1562,6 +1575,11 @@ class Type_handler_row: public Type_handler
return ROW_RESULT;
}
const Type_handler *type_handler_for_comparison() const;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const
{
DBUG_ASSERT(0);
return 0;
}
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer) const
{
......@@ -1888,6 +1906,7 @@ class Type_handler_real_result: public Type_handler_numeric
Item_result cmp_type() const { return REAL_RESULT; }
virtual ~Type_handler_real_result() {}
const Type_handler *type_handler_for_comparison() const;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer) const;
void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
......@@ -1965,6 +1984,7 @@ class Type_handler_decimal_result: public Type_handler_numeric
Item_result cmp_type() const { return DECIMAL_RESULT; }
virtual ~Type_handler_decimal_result() {};
const Type_handler *type_handler_for_comparison() const;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer) const;
Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const;
......@@ -2174,6 +2194,7 @@ class Type_handler_int_result: public Type_handler_numeric
bool is_limit_clause_valid_type() const { return true; }
virtual ~Type_handler_int_result() {}
const Type_handler *type_handler_for_comparison() const;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer) const;
Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const;
......@@ -2237,6 +2258,7 @@ class Type_handler_int_result: public Type_handler_numeric
bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
};
......@@ -2331,6 +2353,7 @@ class Type_handler_string_result: public Type_handler
CHARSET_INFO *charset_for_protocol(const Item *item) const;
virtual ~Type_handler_string_result() {}
const Type_handler *type_handler_for_comparison() const;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
const Type_handler *
type_handler_adjusted_to_max_octet_length(uint max_octet_length,
CHARSET_INFO *cs) const;
......@@ -2885,6 +2908,7 @@ class Type_handler_time_common: public Type_handler_temporal_result
return Item_divisor_precision_increment_with_seconds(item);
}
const Type_handler *type_handler_for_comparison() const;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
void Column_definition_implicit_upgrade(Column_definition *c) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
bool Item_save_in_value(Item *item, st_value *value) const;
......@@ -2986,6 +3010,7 @@ class Type_handler_temporal_with_date: public Type_handler_temporal_result
virtual ~Type_handler_temporal_with_date() {}
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
Item *a, Item *b) const;
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
bool Item_save_in_value(Item *item, st_value *value) const;
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment