Commit 81d9bed3 authored by Monty's avatar Monty Committed by Sergei Golubchik

MDEV-20017 Implement TO_CHAR() Oracle compatible function

TO_CHAR(expr, fmt)
- expr: required parameter, data/time/timestamp type expression
- fmt: optional parameter, format string, supports
  YYYY/YYY/YY/RRRR/RR/MM/MON/MONTH/MI/DD/DY/HH/HH12/HH24/SS and special
  characters. The default value is "YYYY-MM-DD HH24:MI:SS"

In Oracle, TO_CHAR() can also be used to convert numbers to strings, but
this is not supported. This will gave an error in this patch.

Other things:
- If format strings is a constant, it's evaluated only once and if there
  is any errors in it, they are given at once and the statement will abort.

Original author: woqutech
Lots of optimizations and cleanups done as part of review
parent cf93209c
To run a test suite under this directory, you should use the format:
mysql-test-run --suite=compat/oracle
or to run one test:
mysql-test-run compat/oracle.test_name
This diff is collapsed.
This diff is collapsed.
...@@ -2132,6 +2132,19 @@ class Create_func_to_base64 : public Create_func_arg1 ...@@ -2132,6 +2132,19 @@ class Create_func_to_base64 : public Create_func_arg1
}; };
class Create_func_to_char : public Create_native_func
{
public:
virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
static Create_func_to_char s_singleton;
protected:
Create_func_to_char() {}
virtual ~Create_func_to_char() {}
};
class Create_func_to_days : public Create_func_arg1 class Create_func_to_days : public Create_func_arg1
{ {
public: public:
...@@ -5142,6 +5155,44 @@ Create_func_to_base64::create_1_arg(THD *thd, Item *arg1) ...@@ -5142,6 +5155,44 @@ Create_func_to_base64::create_1_arg(THD *thd, Item *arg1)
} }
Create_func_to_char Create_func_to_char::s_singleton;
Item*
Create_func_to_char::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
int arg_count= 0;
if (item_list != NULL)
arg_count= item_list->elements;
switch (arg_count) {
case 1:
{
Item *param_1= item_list->pop();
Item *i0= new (thd->mem_root) Item_string_sys(thd, "YYYY-MM-DD HH24:MI:SS", 21);
func= new (thd->mem_root) Item_func_tochar(thd, param_1, i0);
break;
}
case 2:
{
Item *param_1= item_list->pop();
Item *param_2= item_list->pop();
func= new (thd->mem_root) Item_func_tochar(thd, param_1, param_2);
break;
}
default:
{
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
}
return func;
}
Create_func_to_days Create_func_to_days::s_singleton; Create_func_to_days Create_func_to_days::s_singleton;
Item* Item*
...@@ -5601,6 +5652,7 @@ static Native_func_registry func_array[] = ...@@ -5601,6 +5652,7 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)}, { { STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)},
{ { STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)}, { { STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)},
{ { STRING_WITH_LEN("TO_BASE64") }, BUILDER(Create_func_to_base64)}, { { STRING_WITH_LEN("TO_BASE64") }, BUILDER(Create_func_to_base64)},
{ { STRING_WITH_LEN("TO_CHAR") }, BUILDER(Create_func_to_char)},
{ { STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)}, { { STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)},
{ { STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)}, { { STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)},
{ { STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)}, { { STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)},
......
This diff is collapsed.
...@@ -978,6 +978,57 @@ class Item_func_time_format: public Item_func_date_format ...@@ -978,6 +978,57 @@ class Item_func_time_format: public Item_func_date_format
}; };
/* the max length of datetime format models string in Oracle is 144 */
#define MAX_DATETIME_FORMAT_MODEL_LEN 144
class Item_func_tochar :public Item_str_func
{
const MY_LOCALE *locale;
THD *thd;
String warning_message;
bool fixed_length;
/*
When datetime format models is parsed, use uint16 integers to
represent the format models and store in fmt_array.
*/
uint16 fmt_array[MAX_DATETIME_FORMAT_MODEL_LEN+1];
bool check_arguments() const override
{
return check_argument_types_can_return_text(1, arg_count);
}
public:
Item_func_tochar(THD *thd, Item *a, Item *b):
Item_str_func(thd, a, b), locale(0)
{
/* NOTE: max length of warning message is 64 */
warning_message.alloc(64);
warning_message.length(0);
}
~Item_func_tochar() { warning_message.free(); }
String *val_str(String *str) override;
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("to_char") };
return name;
}
bool fix_length_and_dec() override;
bool parse_format_string(const String *format, uint *fmt_len);
bool check_vcol_func_processor(void *arg) override
{
if (arg_count > 2)
return false;
return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_tochar>(thd, this); }
};
class Item_func_from_unixtime :public Item_datetimefunc class Item_func_from_unixtime :public Item_datetimefunc
{ {
bool check_arguments() const override bool check_arguments() const override
......
...@@ -7988,5 +7988,3 @@ ER_JSON_TABLE_MULTIPLE_MATCHES ...@@ -7988,5 +7988,3 @@ ER_JSON_TABLE_MULTIPLE_MATCHES
eng "Can't store multiple matches of the path in the column '%s' of JSON_TABLE '%s'." eng "Can't store multiple matches of the path in the column '%s' of JSON_TABLE '%s'."
ER_WITH_TIES_NEEDS_ORDER ER_WITH_TIES_NEEDS_ORDER
eng "FETCH ... WITH TIES requires ORDER BY clause to be present" eng "FETCH ... WITH TIES requires ORDER BY clause to be present"
ER_FUNCTION_CANNOT_BE_USED_IN_CLAUSE
eng "Function '%s' cannot be used in the %s clause"
...@@ -502,16 +502,16 @@ bool String::set_ascii(const char *str, size_t arg_length) ...@@ -502,16 +502,16 @@ bool String::set_ascii(const char *str, size_t arg_length)
/* This is used by mysql.cc */ /* This is used by mysql.cc */
bool Binary_string::fill(uint32 max_length,char fill_char) bool Binary_string::fill(size_t max_length,char fill_char)
{ {
if (str_length > max_length) if (str_length > max_length)
Ptr[str_length=max_length]=0; Ptr[str_length= (uint32) max_length]=0;
else else
{ {
if (realloc(max_length)) if (realloc(max_length))
return TRUE; return TRUE;
bfill(Ptr+str_length,max_length-str_length,fill_char); bfill(Ptr+str_length,max_length-str_length,fill_char);
str_length=max_length; str_length= (uint32) max_length;
} }
return FALSE; return FALSE;
} }
......
...@@ -740,7 +740,7 @@ class Binary_string: public Static_binary_string ...@@ -740,7 +740,7 @@ class Binary_string: public Static_binary_string
thread_specific= s.thread_specific; thread_specific= s.thread_specific;
s.alloced= 0; s.alloced= 0;
} }
bool fill(uint32 max_length,char fill); bool fill(size_t max_length,char fill);
/* /*
Replace substring with string Replace substring with string
If wrong parameter or not enough memory, do nothing If wrong parameter or not enough memory, do nothing
......
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