Skip to content

Commit

Permalink
MDEV-10342 Providing compatibility for basic SQL built-in functions
Browse files Browse the repository at this point in the history
Adding functions NVL() and NVL2().
  • Loading branch information
Alexander Barkov committed Apr 5, 2017
1 parent 7e7ba7c commit 30bec86
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 93 deletions.
7 changes: 7 additions & 0 deletions mysql-test/suite/compat/oracle/r/func_case.result
@@ -0,0 +1,7 @@
SET sql_mode=ORACLE;
SELECT NVL(NULL, 'a'), NVL('a', 'b');
NVL(NULL, 'a') NVL('a', 'b')
a a
SELECT NVL2(NULL, 'a', 'b'), NVL2('a', 'b', 'c');
NVL2(NULL, 'a', 'b') NVL2('a', 'b', 'c')
b b
9 changes: 9 additions & 0 deletions mysql-test/suite/compat/oracle/t/func_case.test
@@ -0,0 +1,9 @@
#
# Testing CASE and its abbreviations
#

SET sql_mode=ORACLE;

SELECT NVL(NULL, 'a'), NVL('a', 'b');

SELECT NVL2(NULL, 'a', 'b'), NVL2('a', 'b', 'c');
41 changes: 41 additions & 0 deletions sql/item.h
Expand Up @@ -543,6 +543,47 @@ class Item: public Value_source,

void push_note_converted_to_negative_complement(THD *thd);
void push_note_converted_to_positive_complement(THD *thd);

/* Helper methods, to get an Item value from another Item */
double val_real_from_item(Item *item)
{
DBUG_ASSERT(fixed == 1);
double value= item->val_real();
null_value= item->null_value;
return value;
}
longlong val_int_from_item(Item *item)
{
DBUG_ASSERT(fixed == 1);
longlong value= item->val_int();
null_value= item->null_value;
return value;
}
String *val_str_from_item(Item *item, String *str)
{
DBUG_ASSERT(fixed == 1);
String *res= item->val_str(str);
if (res)
res->set_charset(collation.collation);
if ((null_value= item->null_value))
res= NULL;
return res;
}
my_decimal *val_decimal_from_item(Item *item, my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
my_decimal *value= item->val_decimal(decimal_value);
if ((null_value= item->null_value))
value= NULL;
return value;
}
bool get_date_with_conversion_from_item(Item *item,
MYSQL_TIME *ltime, uint fuzzydate)
{
DBUG_ASSERT(fixed == 1);
return (null_value= item->get_date_with_conversion(ltime, fuzzydate));
}

public:
/*
Cache val_str() into the own buffer, e.g. to evaluate constant
Expand Down
85 changes: 0 additions & 85 deletions sql/item_cmpfunc.cc
Expand Up @@ -2365,91 +2365,6 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref)
}


void Item_func_if::cache_type_info(Item *source)
{
Type_std_attributes::set(source);
set_handler_by_field_type(source->field_type());
maybe_null= source->maybe_null;
}


void
Item_func_if::fix_length_and_dec()
{
// Let IF(cond, expr, NULL) and IF(cond, NULL, expr) inherit type from expr.
if (args[1]->type() == NULL_ITEM)
{
cache_type_info(args[2]);
maybe_null= true;
// If both arguments are NULL, make resulting type BINARY(0).
if (args[2]->type() == NULL_ITEM)
set_handler_by_field_type(MYSQL_TYPE_STRING);
return;
}
if (args[2]->type() == NULL_ITEM)
{
cache_type_info(args[1]);
maybe_null= true;
return;
}
Item_func_case_abbreviation2::fix_length_and_dec2(args + 1);
}


double
Item_func_if::real_op()
{
DBUG_ASSERT(fixed == 1);
Item *arg= args[0]->val_bool() ? args[1] : args[2];
double value= arg->val_real();
null_value=arg->null_value;
return value;
}

longlong
Item_func_if::int_op()
{
DBUG_ASSERT(fixed == 1);
Item *arg= args[0]->val_bool() ? args[1] : args[2];
longlong value=arg->val_int();
null_value=arg->null_value;
return value;
}

String *
Item_func_if::str_op(String *str)
{
DBUG_ASSERT(fixed == 1);
Item *arg= args[0]->val_bool() ? args[1] : args[2];
String *res=arg->val_str(str);
if (res)
res->set_charset(collation.collation);
if ((null_value=arg->null_value))
res= NULL;
return res;
}


my_decimal *
Item_func_if::decimal_op(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
Item *arg= args[0]->val_bool() ? args[1] : args[2];
my_decimal *value= arg->val_decimal(decimal_value);
if ((null_value= arg->null_value))
value= NULL;
return value;
}


bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate)
{
DBUG_ASSERT(fixed == 1);
Item *arg= args[0]->val_bool() ? args[1] : args[2];
return (null_value= arg->get_date_with_conversion(ltime, fuzzydate));
}


void Item_func_nullif::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields, uint flags)
{
Expand Down
110 changes: 102 additions & 8 deletions sql/item_cmpfunc.h
Expand Up @@ -993,6 +993,7 @@ class Item_func_coalesce :public Item_func_hybrid_field_type
Case abbreviations that aggregate its result field type by two arguments:
IFNULL(arg1, arg2)
IF(switch, arg1, arg2)
NVL2(switch, arg1, arg2)
*/
class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type
{
Expand All @@ -1003,6 +1004,34 @@ class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type
fix_attributes(items, 2);
}
uint decimal_precision2(Item **args) const;

void cache_type_info(const Item *source, bool maybe_null_arg)
{
Type_std_attributes::set(source);
set_handler_by_field_type(source->field_type());
maybe_null= maybe_null_arg;
}

void fix_length_and_dec2_eliminate_null(Item **items)
{
// Let IF(cond, expr, NULL) and IF(cond, NULL, expr) inherit type from expr.
if (items[0]->type() == NULL_ITEM)
{
cache_type_info(items[1], true);
// If both arguments are NULL, make resulting type BINARY(0).
if (items[1]->type() == NULL_ITEM)
set_handler_by_field_type(MYSQL_TYPE_STRING);
}
else if (items[1]->type() == NULL_ITEM)
{
cache_type_info(items[0], true);
}
else
{
fix_length_and_dec2(items);
}
}

public:
Item_func_case_abbreviation2(THD *thd, Item *a, Item *b):
Item_func_hybrid_field_type(thd, a, b) { }
Expand Down Expand Up @@ -1040,19 +1069,61 @@ class Item_func_ifnull :public Item_func_case_abbreviation2
};


class Item_func_if :public Item_func_case_abbreviation2
/**
Case abbreviations that have a switch argument and
two return arguments to choose from. Returns the value
of either of the two return arguments depending on the switch argument value.
IF(switch, arg1, arg2)
NVL(switch, arg1, arg2)
*/
class Item_func_case_abbreviation2_switch: public Item_func_case_abbreviation2
{
protected:
virtual Item *find_item() const= 0;

public:
Item_func_case_abbreviation2_switch(THD *thd, Item *a, Item *b, Item *c)
:Item_func_case_abbreviation2(thd, a, b, c)
{ }

bool date_op(MYSQL_TIME *ltime, uint fuzzydate)
{
return get_date_with_conversion_from_item(find_item(), ltime, fuzzydate);
}
longlong int_op()
{
return val_int_from_item(find_item());
}
double real_op()
{
return val_real_from_item(find_item());
}
my_decimal *decimal_op(my_decimal *decimal_value)
{
return val_decimal_from_item(find_item(), decimal_value);
}
String *str_op(String *str)
{
return val_str_from_item(find_item(), str);
}
};


class Item_func_if :public Item_func_case_abbreviation2_switch
{
protected:
Item *find_item() const { return args[0]->val_bool() ? args[1] : args[2]; }

public:
Item_func_if(THD *thd, Item *a, Item *b, Item *c):
Item_func_case_abbreviation2(thd, a, b, c)
Item_func_case_abbreviation2_switch(thd, a, b, c)
{}
bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
String *str_op(String *);
bool fix_fields(THD *, Item **);
void fix_length_and_dec();
void fix_length_and_dec()
{
fix_length_and_dec2_eliminate_null(args + 1);
}
uint decimal_precision() const
{
return Item_func_case_abbreviation2::decimal_precision2(args + 1);
Expand All @@ -1067,6 +1138,29 @@ class Item_func_if :public Item_func_case_abbreviation2
};


class Item_func_nvl2 :public Item_func_case_abbreviation2_switch
{
protected:
Item *find_item() const { return args[0]->is_null() ? args[2] : args[1]; }

public:
Item_func_nvl2(THD *thd, Item *a, Item *b, Item *c):
Item_func_case_abbreviation2_switch(thd, a, b, c)
{}
const char *func_name() const { return "nvl2"; }
void fix_length_and_dec()
{
fix_length_and_dec2_eliminate_null(args + 1);
}
uint decimal_precision() const
{
return Item_func_case_abbreviation2::decimal_precision2(args + 1);
}
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_func_nvl2>(thd, mem_root, this); }
};


class Item_func_nullif :public Item_func_hybrid_field_type
{
Arg_comparator cmp;
Expand Down
24 changes: 24 additions & 0 deletions sql/item_create.cc
Expand Up @@ -711,6 +711,19 @@ class Create_func_contains : public Create_func_arg2
#endif


class Create_func_nvl2 : public Create_func_arg3
{
public:
virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);

static Create_func_nvl2 s_singleton;

protected:
Create_func_nvl2() {}
virtual ~Create_func_nvl2() {}
};


class Create_func_conv : public Create_func_arg3
{
public:
Expand Down Expand Up @@ -3931,6 +3944,15 @@ Create_func_contains::create_2_arg(THD *thd, Item *arg1, Item *arg2)
#endif


Create_func_nvl2 Create_func_nvl2::s_singleton;

Item*
Create_func_nvl2::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3)
{
return new (thd->mem_root) Item_func_nvl2(thd, arg1, arg2, arg3);
}


Create_func_conv Create_func_conv::s_singleton;

Item*
Expand Down Expand Up @@ -6884,6 +6906,8 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
{ { C_STRING_WITH_LEN("MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
{ { C_STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)},
{ { C_STRING_WITH_LEN("NVL") }, BUILDER(Create_func_ifnull)},
{ { C_STRING_WITH_LEN("NVL2") }, BUILDER(Create_func_nvl2)},
{ { C_STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)},
{ { C_STRING_WITH_LEN("NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)},
{ { C_STRING_WITH_LEN("NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)},
Expand Down

0 comments on commit 30bec86

Please sign in to comment.