summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2016-09-17 08:24:05 +0400
committerAlexander Barkov <bar@mariadb.org>2017-04-05 15:02:48 +0400
commit30bec863cf710f9135439f410e1c31e4df873fd1 (patch)
tree6f78961390733bfadec431053e9f909eeb88e2b5
parent7e7ba7cb948910e138922d6524cc125f9aa02848 (diff)
downloadmariadb-git-30bec863cf710f9135439f410e1c31e4df873fd1.tar.gz
MDEV-10342 Providing compatibility for basic SQL built-in functions
Adding functions NVL() and NVL2().
-rw-r--r--mysql-test/suite/compat/oracle/r/func_case.result7
-rw-r--r--mysql-test/suite/compat/oracle/t/func_case.test9
-rw-r--r--sql/item.h41
-rw-r--r--sql/item_cmpfunc.cc85
-rw-r--r--sql/item_cmpfunc.h110
-rw-r--r--sql/item_create.cc24
6 files changed, 183 insertions, 93 deletions
diff --git a/mysql-test/suite/compat/oracle/r/func_case.result b/mysql-test/suite/compat/oracle/r/func_case.result
new file mode 100644
index 00000000000..dfe2d165b88
--- /dev/null
+++ b/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
diff --git a/mysql-test/suite/compat/oracle/t/func_case.test b/mysql-test/suite/compat/oracle/t/func_case.test
new file mode 100644
index 00000000000..d5e0d650975
--- /dev/null
+++ b/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');
diff --git a/sql/item.h b/sql/item.h
index 6f22c05b308..a02cf8f0d57 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -543,6 +543,47 @@ protected:
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
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 7457708948f..f5ba6f206c2 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -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)
{
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 2af4a32d844..a94105b352d 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -993,6 +993,7 @@ public:
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
{
@@ -1003,6 +1004,34 @@ protected:
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) { }
@@ -1040,19 +1069,61 @@ public:
};
-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);
@@ -1067,6 +1138,29 @@ private:
};
+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;
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 455da1f5ba5..978459acf0e 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -711,6 +711,19 @@ protected:
#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:
@@ -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*
@@ -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)},