summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorgkodinov/kgeorge@macbook.gmz <>2007-03-02 16:25:56 +0200
committergkodinov/kgeorge@macbook.gmz <>2007-03-02 16:25:56 +0200
commitbe75593165afccdc4dc829b8de5add5f0477b1f3 (patch)
tree063e0ea949c0c8921128bb894268763c63ea4220 /sql
parenta119169bf0844585ad1707a147ff4081ff894489 (diff)
downloadmariadb-git-be75593165afccdc4dc829b8de5add5f0477b1f3.tar.gz
Bug #19342:
Several problems here : 1. The conversion to double of an hex string const item was not taking into account the unsigned flag. 2. IN was not behaving in the same was way as comparisons when performed over an INT/DATE/DATETIME/TIMESTAMP column and a constant. The ordinary comparisons in that case convert the constant to an INTEGER value and do int comparisons. Fixed the IN to do the same. 3. IN is not taking into account the unsigned flag when calculating <expr> IN (<int_const1>, <int_const2>, ...). Extended the implementation of IN to store and process the unsigned flag for its arguments.
Diffstat (limited to 'sql')
-rw-r--r--sql/item.h5
-rw-r--r--sql/item_cmpfunc.cc130
-rw-r--r--sql/item_cmpfunc.h17
3 files changed, 144 insertions, 8 deletions
diff --git a/sql/item.h b/sql/item.h
index 6c41aa09f80..f02d4f0c65d 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1766,7 +1766,10 @@ public:
Item_hex_string(const char *str,uint str_length);
enum Type type() const { return VARBIN_ITEM; }
double val_real()
- { DBUG_ASSERT(fixed == 1); return (double) Item_hex_string::val_int(); }
+ {
+ DBUG_ASSERT(fixed == 1);
+ return (double) (ulonglong) Item_hex_string::val_int();
+ }
longlong val_int();
bool basic_const_item() const { return 1; }
String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 08f9c16384a..f239e3ea18e 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -2034,9 +2034,100 @@ void Item_func_coalesce::fix_length_and_dec()
Classes and function for the IN operator
****************************************************************************/
-static int cmp_longlong(void *cmp_arg, longlong *a,longlong *b)
+/*
+ Determine which of the signed longlong arguments is bigger
+
+ SYNOPSIS
+ cmp_longs()
+ a_val left argument
+ b_val right argument
+
+ DESCRIPTION
+ This function will compare two signed longlong arguments
+ and will return -1, 0, or 1 if left argument is smaller than,
+ equal to or greater than the right argument.
+
+ RETURN VALUE
+ -1 left argument is smaller than the right argument.
+ 0 left argument is equal to the right argument.
+ 1 left argument is greater than the right argument.
+*/
+static inline int cmp_longs (longlong a_val, longlong b_val)
{
- return *a < *b ? -1 : *a == *b ? 0 : 1;
+ return a_val < b_val ? -1 : a_val == b_val ? 0 : 1;
+}
+
+
+/*
+ Determine which of the unsigned longlong arguments is bigger
+
+ SYNOPSIS
+ cmp_ulongs()
+ a_val left argument
+ b_val right argument
+
+ DESCRIPTION
+ This function will compare two unsigned longlong arguments
+ and will return -1, 0, or 1 if left argument is smaller than,
+ equal to or greater than the right argument.
+
+ RETURN VALUE
+ -1 left argument is smaller than the right argument.
+ 0 left argument is equal to the right argument.
+ 1 left argument is greater than the right argument.
+*/
+static inline int cmp_ulongs (ulonglong a_val, ulonglong b_val)
+{
+ return a_val < b_val ? -1 : a_val == b_val ? 0 : 1;
+}
+
+
+/*
+ Compare two integers in IN value list format (packed_longlong)
+
+ SYNOPSIS
+ cmp_longlong()
+ cmp_arg an argument passed to the calling function (qsort2)
+ a left argument
+ b right argument
+
+ DESCRIPTION
+ This function will compare two integer arguments in the IN value list
+ format and will return -1, 0, or 1 if left argument is smaller than,
+ equal to or greater than the right argument.
+ It's used in sorting the IN values list and finding an element in it.
+ Depending on the signedness of the arguments cmp_longlong() will
+ compare them as either signed (using cmp_longs()) or unsigned (using
+ cmp_ulongs()).
+
+ RETURN VALUE
+ -1 left argument is smaller than the right argument.
+ 0 left argument is equal to the right argument.
+ 1 left argument is greater than the right argument.
+*/
+int cmp_longlong(void *cmp_arg,
+ in_longlong::packed_longlong *a,
+ in_longlong::packed_longlong *b)
+{
+ if (a->unsigned_flag != b->unsigned_flag)
+ {
+ /*
+ One of the args is unsigned and is too big to fit into the
+ positive signed range. Report no match.
+ */
+ if (a->unsigned_flag && ((ulonglong) a->val) > LONGLONG_MAX ||
+ b->unsigned_flag && ((ulonglong) b->val) > LONGLONG_MAX)
+ return a->unsigned_flag ? 1 : -1;
+ /*
+ Although the signedness differs both args can fit into the signed
+ positive range. Make them signed and compare as usual.
+ */
+ return cmp_longs (a->val, b->val);
+ }
+ if (a->unsigned_flag)
+ return cmp_ulongs ((ulonglong) a->val, (ulonglong) b->val);
+ else
+ return cmp_longs (a->val, b->val);
}
static int cmp_double(void *cmp_arg, double *a,double *b)
@@ -2161,19 +2252,23 @@ void in_row::set(uint pos, Item *item)
}
in_longlong::in_longlong(uint elements)
- :in_vector(elements,sizeof(longlong),(qsort2_cmp) cmp_longlong, 0)
+ :in_vector(elements,sizeof(packed_longlong),(qsort2_cmp) cmp_longlong, 0)
{}
void in_longlong::set(uint pos,Item *item)
{
- ((longlong*) base)[pos]=item->val_int();
+ struct packed_longlong *buff= &((packed_longlong*) base)[pos];
+
+ buff->val= item->val_int();
+ buff->unsigned_flag= item->unsigned_flag;
}
byte *in_longlong::get_value(Item *item)
{
- tmp= item->val_int();
+ tmp.val= item->val_int();
if (item->null_value)
return 0;
+ tmp.unsigned_flag= item->unsigned_flag;
return (byte*) &tmp;
}
@@ -2494,6 +2589,31 @@ void Item_func_in::fix_length_and_dec()
*/
if (const_itm && !nulls_in_row())
{
+ /*
+ IN must compare INT/DATE/DATETIME/TIMESTAMP columns and constants
+ as int values (the same way as equality does).
+ So we must check here if the column on the left and all the constant
+ values on the right can be compared as integers and adjust the
+ comparison type accordingly.
+ */
+ if (args[0]->real_item()->type() == FIELD_ITEM &&
+ thd->lex->sql_command != SQLCOM_CREATE_VIEW &&
+ thd->lex->sql_command != SQLCOM_SHOW_CREATE &&
+ cmp_type != INT_RESULT)
+ {
+ Field *field= ((Item_field*) (args[0]->real_item()))->field;
+ if (field->can_be_compared_as_longlong())
+ {
+ bool all_converted= TRUE;
+ for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++)
+ {
+ if (!convert_constant_item (thd, field, &arg[0]))
+ all_converted= FALSE;
+ }
+ if (all_converted)
+ cmp_type= INT_RESULT;
+ }
+ }
switch (cmp_type) {
case STRING_RESULT:
array=new in_string(arg_count-1,(qsort2_cmp) srtcmp_in,
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index f18728c554b..80115549336 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -731,7 +731,16 @@ public:
class in_longlong :public in_vector
{
- longlong tmp;
+ /*
+ Here we declare a temporary variable (tmp) of the same type as the
+ elements of this vector. tmp is used in finding if a given value is in
+ the list.
+ */
+ struct packed_longlong
+ {
+ longlong val;
+ longlong unsigned_flag; // Use longlong, not bool, to preserve alignment
+ } tmp;
public:
in_longlong(uint elements);
void set(uint pos,Item *item);
@@ -747,8 +756,12 @@ public:
}
void value_to_item(uint pos, Item *item)
{
- ((Item_int*)item)->value= ((longlong*)base)[pos];
+ ((Item_int*) item)->value= ((packed_longlong*) base)[pos].val;
+ ((Item_int*) item)->unsigned_flag=
+ ((packed_longlong*) base)[pos].unsigned_flag;
}
+
+ friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b);
};
class in_double :public in_vector