maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #12673
Re: 57c19902326: MDEV-20017 Implement TO_CHAR() Oracle compatible function
Hi, Monty!
On Apr 13, Michael Widenius wrote:
> commit 57c19902326
> Author: Michael Widenius <michael.widenius@xxxxxxxxx>
> Date: Sun Jan 24 23:56:43 2021 +0200
>
> diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
> index aecb00563f7..b23522ac830 100644
> --- a/sql/share/errmsg-utf8.txt
> +++ b/sql/share/errmsg-utf8.txt
> @@ -7975,3 +7975,5 @@ ER_PK_INDEX_CANT_BE_IGNORED
> eng "A primary key cannot be marked as IGNORE"
> ER_FUNCTION_CANNOT_BE_USED_IN_CLAUSE
> eng "Function '%s' cannot be used in the %s clause"
> +ER_ORACLE_COMPAT_FUNCTION_ERROR
> + eng "Oracle compatibility function error: %s"
Why? We normally just say "invalid argument" or something, in no other case
we say "oracle compatibility function" or "sybase compatibility function" or
"odbc compatibility function".
> diff --git a/sql/sql_string.cc b/sql/sql_string.cc
> index 95a57017c53..9c57bb22085 100644
> --- a/sql/sql_string.cc
> +++ b/sql/sql_string.cc
> @@ -1275,3 +1275,15 @@ void Binary_string::shrink(size_t arg_length)
> }
> }
> }
> +
> +bool Binary_string::strfill(char fill, size_t len)
> +{
> + if (len)
> + {
> + if (alloc(length() + len))
> + return 1;
> + memset(Ptr + str_length, fill, len);
> + str_length+= (uint32) len;
> + }
> + return 0;
> +}
There's Binary_string::fill() already.
better use it or, at the very least, declare
bool strfill(char fill, size_t len) { return fill(str_length + len, fill); }
in sql_string.h. And this swapped order of arguments is confusing,
please fix it too.
In fact, I think it's confusing to have both:
fill(max_len, fill_char)
strfill(fill_char, fill_length)
if you want to keep both, it'd be better to rename them to something
that somehow reflects the difference, for example:
strfill -> append_many or append_repeated
but really, I personally would just delete strfill.
> diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
> index af266956b05..9b78d6c159e 100644
> --- a/sql/item_timefunc.h
> +++ b/sql/item_timefunc.h
> @@ -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);
> + }
As far as I understand, this warning_message was introduced to issue
the same error for every row, even if the format string is const_item and
is parsed only once, in fix_fields.
I don't think it's worth the trouble. Two simpler approaches are:
* if the format string is invalid - parse it every time even if const, or
* if the const format string is invalid - issue the error only once.
so, please, remove warning_message and just use push_warning or my_error
where the error is discovered.
> + ~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
> {
> bool check_arguments() const override
> diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
> index 04d913b0fca..44d2ec7912d 100644
> --- a/sql/item_timefunc.cc
> +++ b/sql/item_timefunc.cc
> @@ -1914,6 +1913,805 @@ String *Item_func_date_format::val_str(String *str)
> return 0;
> }
>
> +/*
> + Oracle has many formatting models, we list all but only part of them
> + are implemented, because some models depend on oracle functions
> + which mariadb is not supported.
> +
> + Models for datetime, used by TO_CHAR/TO_DATE. Normal format characters are
> + stored as short integer < 256, while format characters are stored as a
> + integer > 256
> +*/
> +
> +#define FMT_BASE 128
128? or 256?
> +#define FMT_AD FMT_BASE+1
> +#define FMT_AD_DOT FMT_BASE+2
> +#define FMT_AM FMT_BASE+3
> +#define FMT_AM_DOT FMT_BASE+4
> +#define FMT_BC FMT_BASE+5
> +#define FMT_BC_DOT FMT_BASE+6
> +#define FMT_CC FMT_BASE+7
> +#define FMT_SCC FMT_BASE+8
> +#define FMT_D FMT_BASE+9
> +#define FMT_DAY FMT_BASE+10
> +#define FMT_DD FMT_BASE+11
> +#define FMT_DDD FMT_BASE+12
> +#define FMT_DL FMT_BASE+13
> +#define FMT_DS FMT_BASE+14
> +#define FMT_DY FMT_BASE+15
> +#define FMT_E FMT_BASE+16
> +#define FMT_EE FMT_BASE+17
> +#define FMT_FF FMT_BASE+18
> +#define FMT_FM FMT_BASE+19
> +#define FMT_FX FMT_BASE+20
> +#define FMT_HH FMT_BASE+21
> +#define FMT_HH12 FMT_BASE+22
> +#define FMT_HH24 FMT_BASE+23
> +#define FMT_IW FMT_BASE+24
> +#define FMT_I FMT_BASE+25
> +#define FMT_IY FMT_BASE+26
> +#define FMT_IYY FMT_BASE+27
> +#define FMT_IYYY FMT_BASE+28
> +#define FMT_J FMT_BASE+29
> +#define FMT_MI FMT_BASE+30
> +#define FMT_MM FMT_BASE+31
> +#define FMT_MON FMT_BASE+32
> +#define FMT_MONTH FMT_BASE+33
> +#define FMT_PM FMT_BASE+34
> +#define FMT_PM_DOT FMT_BASE+35
> +#define FMT_RM FMT_BASE+37
> +#define FMT_RR FMT_BASE+38
> +#define FMT_RRRR FMT_BASE+39
> +#define FMT_SS FMT_BASE+40
> +#define FMT_SSSSSS FMT_BASE+41
> +#define FMT_TS FMT_BASE+42
> +#define FMT_TZD FMT_BASE+43
> +#define FMT_TZH FMT_BASE+44
> +#define FMT_TZM FMT_BASE+45
> +#define FMT_TZR FMT_BASE+46
> +#define FMT_W FMT_BASE+47
> +#define FMT_WW FMT_BASE+48
> +#define FMT_X FMT_BASE+49
> +#define FMT_Y FMT_BASE+50
> +#define FMT_YY FMT_BASE+51
> +#define FMT_YYY FMT_BASE+52
> +#define FMT_YYYY FMT_BASE+53
> +#define FMT_YYYY_COMMA FMT_BASE+54
> +#define FMT_YEAR FMT_BASE+55
> +#define FMT_SYYYY FMT_BASE+56
> +#define FMT_SYEAR FMT_BASE+57
Not enum? Not even safe (with parentheses) #define?
enum would be ideal here but at the very least make these defines safe.
> +
> +
> +/**
> + Modify the quotation flag and check whether the subsequent process is skipped
Could you reword it please?
> +
> + @param cftm Character or FMT... format descriptor
> + @param quotation_flag Points to 'true' if we are inside a quoted string
> +
> + @return true If we are inside a quoted string or if we found a '"' character
> + @return false Otherwise
> +*/
> +
> +static inline bool check_quotation(uint16 cfmt, bool *quotation_flag)
> +{
> + if (cfmt == '"')
> + {
> + *quotation_flag= !*quotation_flag;
> + return true;
> + }
> + return *quotation_flag;
> +}
> +
> +#define INVALID_CHARACTER(x) (((x) >= 'A' && (x) <= 'Z') ||((x) >= '0' && (x) <= '9') || (x) >= 127 || ((x) < 32))
why not to make this static inline too?
side-effect safe, no risk of double evaluation of x.
> +
> +
> +/**
> + Special characters are directly output in the result
> +
> + @return 0 If found not acceptable character
> + @return # Number of copied characters
> +*/
> +
> +static uint parse_special(char cfmt, const char *ptr, const char *end,
> + uint16 *array)
> +{
> + int offset= 0;
> + char tmp1;
> +
> + /* Non-printable character and Multibyte encoded characters */
> + if (INVALID_CHARACTER(cfmt))
> + return 0;
> +
> + /*
> + * '&' with text is used for variable input, but '&' with other
> + * special charaters like '|'. '*' is used as separator
> + */
> + if (cfmt == '&' && ptr + 1 < end)
> + {
> + tmp1= my_toupper(system_charset_info, *(ptr+1));
> + if (tmp1 >= 'A' && tmp1 <= 'Z')
> + return 0;
> + }
> +
> + do {
> + /*
> + Continuously store the special characters in fmt_array until non-special
> + characters appear
> + */
> + *array++= (uint16) (uchar) *ptr++;
> + offset++;
> + if (ptr == end)
> + break;
> + tmp1= my_toupper(system_charset_info, *ptr);
> + } while (!INVALID_CHARACTER(tmp1) && tmp1 != '"');
> + return offset;
> +}
> +
> +
> +/**
> + Parse the format string, convert it to an compact array and calculate the
> + length of output string
> +
> + @param format Format string
> + @param fmt_len Function will store max length of formated date string here
> +
> + @return 0 ok. fmt_len is updated
> + @return 1 error. In this case 'warning_string' is set to error message
> +*/
> +
> +bool Item_func_tochar::parse_format_string(const String *format, uint *fmt_len)
> +{
> + const char *ptr, *end;
> + uint16 *tmp_fmt= fmt_array;
> + uint tmp_len= 0;
> + int offset= 0;
> + bool quotation_flag= false;
> +
> + ptr= format->ptr();
> + end= ptr + format->length();
> +
> + if (format->length() > MAX_DATETIME_FORMAT_MODEL_LEN)
> + {
> + warning_message.append(STRING_WITH_LEN("datetime format string is too "
> + "long"));
> + return 1;
> + }
> +
> + for (; ptr < end; ptr++, tmp_fmt++)
> + {
> + uint ulen;
> + char cfmt, next_char;
> +
> + cfmt= my_toupper(system_charset_info, *ptr);
> +
> + /*
> + Oracle datetime format support text in double quotation marks like
> + 'YYYY"abc"MM"xyz"DD', When this happens, store the text and quotation
> + marks, and use the text as a separator in make_date_time_oracle.
> +
> + NOTE: the quotation mark is not print in return value. for example:
> + select TO_CHAR(sysdate, 'YYYY"abc"MM"xyzDD"') will return 2021abc01xyz11
> + */
> + if (check_quotation(cfmt, "ation_flag))
> + {
> + *tmp_fmt= *ptr;
> + tmp_len+= 1;
> + continue;
> + }
> +
> + switch (cfmt) {
> + case 'A': // AD/A.D./AM/A.M.
> + if (ptr+1 >= end)
> + goto error;
> + next_char= my_toupper(system_charset_info, *(ptr+1));
> + if (next_char == 'D')
> + {
> + *tmp_fmt= FMT_AD;
> + ptr+= 1;
> + tmp_len+= 2;
> + }
> + else if (next_char == 'M')
> + {
> + *tmp_fmt= FMT_AM;
> + ptr+= 1;
> + tmp_len+= 2;
> + }
> + else if (next_char == '.' && ptr+3 < end && *(ptr+3) == '.')
> + {
> + if (my_toupper(system_charset_info, *(ptr+2)) == 'D')
> + {
> + *tmp_fmt= FMT_AD_DOT;
> + ptr+= 3;
> + tmp_len+= 4;
> + }
> + else if (my_toupper(system_charset_info, *(ptr+2)) == 'M')
> + {
> + *tmp_fmt= FMT_AM_DOT;
> + ptr+= 3;
> + tmp_len+= 4;
> + }
> + else
> + goto error;
> + }
> + else
> + goto error;
> + break;
> + case 'B': // BC and B.C
> + if (ptr+1 >= end)
> + goto error;
> + next_char= my_toupper(system_charset_info, *(ptr+1));
> + if (next_char == 'C')
> + {
> + *tmp_fmt= FMT_BC;
> + ptr+= 1;
> + tmp_len+= 2;
> + }
> + else if (next_char == '.' && ptr+3 < end &&
> + my_toupper(system_charset_info, *(ptr+2)) == 'C' &&
> + *(ptr+3) == '.')
> + {
> + *tmp_fmt= FMT_BC_DOT;
> + ptr+= 3;
> + tmp_len+= 4;
> + }
> + else
> + goto error;
> + break;
> + case 'P': // PM or P.M.
> + next_char= my_toupper(system_charset_info, *(ptr+1));
> + if (next_char == 'M')
> + {
> + *tmp_fmt= FMT_PM;
> + ptr+= 1;
> + tmp_len+= 2;
> + }
> + else if (next_char == '.' &&
> + my_toupper(system_charset_info, *(ptr+2)) == 'M' &&
> + my_toupper(system_charset_info, *(ptr+3)) == '.')
> + {
> + *tmp_fmt= FMT_PM_DOT;
> + ptr+= 3;
> + tmp_len+= 4;
> + }
> + else
> + goto error;
> + break;
> + case 'Y': // Y, YY, YYY o YYYYY
> + if (ptr + 1 == end || my_toupper(system_charset_info, *(ptr+1)) != 'Y')
> + {
> + *tmp_fmt= FMT_Y;
> + tmp_len+= 1;
> + break;
> + }
> + if (ptr + 2 == end ||
> + my_toupper(system_charset_info, *(ptr+2)) != 'Y') /* YY */
> + {
> + *tmp_fmt= FMT_YY;
> + ulen= 2;
> + }
> + else
> + {
> + if (ptr + 3 < end && my_toupper(system_charset_info, *(ptr+3)) == 'Y')
> + {
> + *tmp_fmt= FMT_YYYY;
> + ulen= 4;
> + }
> + else
> + {
> + *tmp_fmt= FMT_YYY;
> + ulen= 3;
> + }
> + }
> + ptr+= ulen-1;
> + tmp_len+= ulen;
> + break;
> +
> + case 'R': // RR or RRRR
> + if (ptr + 1 == end || my_toupper(system_charset_info, *(ptr+1)) != 'R')
> + goto error;
> +
> + if (ptr + 2 == end || my_toupper(system_charset_info, *(ptr+2)) != 'R')
> + {
> + *tmp_fmt= FMT_RR;
> + ulen= 2;
> + }
> + else
> + {
> + if (ptr + 3 >= end || my_toupper(system_charset_info, *(ptr+3)) != 'R')
> + goto error;
> + *tmp_fmt= FMT_RRRR;
> + ulen= 4;
> + }
> + ptr+= ulen-1;
> + tmp_len+= ulen;
> + break;
> + case 'M':
> + {
> + char tmp1;
> + if (ptr + 1 >= end)
> + goto error;
> +
> + tmp1= my_toupper(system_charset_info, *(ptr+1));
> + if (tmp1 == 'M')
> + {
> + *tmp_fmt= FMT_MM;
> + tmp_len+= 2;
> + ptr+= 1;
> + }
> + else if (tmp1 == 'I')
> + {
> + *tmp_fmt= FMT_MI;
> + tmp_len+= 2;
> + ptr+= 1;
> + }
> + else if (tmp1 == 'O')
> + {
> + if (ptr + 2 >= end)
> + goto error;
> + char tmp2= my_toupper(system_charset_info, *(ptr+2));
> + if (tmp2 != 'N')
> + goto error;
> +
> + if (ptr + 4 >= end ||
> + my_toupper(system_charset_info, *(ptr+3)) != 'T' ||
> + my_toupper(system_charset_info, *(ptr+4)) != 'H')
> + {
> + *tmp_fmt= FMT_MON;
> + tmp_len+= 3;
> + ptr+= 2;
> + }
> + else
> + {
> + *tmp_fmt= FMT_MONTH;
> + tmp_len+= (locale->max_month_name_length *
> + my_charset_utf8mb3_bin.mbmaxlen);
> + ptr+= 4;
> + }
> + }
> + else
> + goto error;
> + }
> + break;
> + case 'D': // DD, DY, or DAY
> + {
> + if (ptr + 1 >= end)
> + goto error;
> + char tmp1= my_toupper(system_charset_info, *(ptr+1));
> +
> + if (tmp1 == 'D')
> + {
> + *tmp_fmt= FMT_DD;
> + tmp_len+= 2;
> + }
> + else if (tmp1 == 'Y')
> + {
> + *tmp_fmt= FMT_DY;
> + tmp_len+= 3;
> + }
> + else if (tmp1 == 'A') // DAY
> + {
> + if (ptr + 2 == end || my_toupper(system_charset_info, *(ptr+2)) != 'Y')
> + goto error;
> + *tmp_fmt= FMT_DAY;
> + tmp_len+= locale->max_day_name_length * my_charset_utf8mb3_bin.mbmaxlen;
> + ptr+= 1;
> + }
> + else
> + goto error;
> + ptr+= 1;
> + }
> + break;
> + case 'H': // HH, HH12 or HH23
> + {
> + char tmp1, tmp2, tmp3;
> + if (ptr + 1 >= end)
> + goto error;
> + tmp1= my_toupper(system_charset_info, *(ptr+1));
> +
> + if (tmp1 != 'H')
> + goto error;
> +
> + if (ptr+3 >= end)
> + {
> + *tmp_fmt= FMT_HH;
> + ptr+= 1;
> + }
> + else
> + {
> + tmp2= *(ptr+2);
> + tmp3= *(ptr+3);
> +
> + if (tmp2 == '1' && tmp3 == '2')
> + {
> + *tmp_fmt= FMT_HH12;
> + ptr+= 3;
> + }
> + else if (tmp2 == '2' && tmp3 == '4')
> + {
> + *tmp_fmt= FMT_HH24;
> + ptr+= 3;
> + }
> + else
> + {
> + *tmp_fmt= FMT_HH;
> + ptr+= 1;
> + }
> + }
> + tmp_len+= 2;
> + break;
> + }
> + case 'S': // SS
> + if (ptr + 1 == end || my_toupper(system_charset_info, *(ptr+1)) != 'S')
> + goto error;
> +
> + *tmp_fmt= FMT_SS;
> + tmp_len+= 2;
> + ptr+= 1;
> + break;
> + case '|':
> + /*
> + If only one '|' just ignore it, else append others, for example:
> + TO_CHAR('2000-11-05', 'YYYY|MM||||DD') --> 200011|||05
> + */
> + if (ptr + 1 == end || *(ptr+1) != '|')
> + {
> + tmp_fmt--;
> + break;
> + }
> + ptr++; // Skip first '|'
> + do
> + {
> + *tmp_fmt++= *ptr++;
> + tmp_len++;
> + } while ((ptr < end) && *ptr == '|');
> + ptr--; // Fix ptr for above for loop
> + tmp_fmt--;
> + break;
> +
> + default:
> + offset= parse_special(cfmt, ptr, end, tmp_fmt);
> + if (!offset)
> + goto error;
> + /* ptr++ is in the for loop, so we must move ptr to offset-1 */
> + ptr+= (offset-1);
> + tmp_fmt+= (offset-1);
> + tmp_len+= offset;
> + break;
> + }
> + }
> + *fmt_len= tmp_len;
> + *tmp_fmt= 0;
> + return 0;
> +
> +error:
> + warning_message.append(STRING_WITH_LEN("date format not recognized at "));
> + warning_message.append(ptr, MY_MIN(8, end- ptr));
> + return 1;
> +}
> +
> +
> +static inline bool append_val(int val, int size, String *str)
> +{
> + ulong len= 0;
> + char intbuff[15];
> +
> + len= (ulong) (int10_to_str(val, intbuff, 10) - intbuff);
> + return str->append_with_prefill(intbuff, len, size, '0');
> +}
> +
> +
> +static bool make_date_time_oracle(const uint16 *fmt_array,
> + const MYSQL_TIME *l_time,
> + const MY_LOCALE *locale,
> + String *str)
> +{
> + bool quotation_flag= false;
> + const uint16 *ptr= fmt_array;
> + uint hours_i;
> + uint weekday;
> +
> + str->length(0);
> +
> + while (*ptr)
> + {
> + if (check_quotation(*ptr, "ation_flag))
> + {
> + /* don't display '"' in the result, so if it is '"', skip it */
> + if (*ptr != '"')
> + {
> + DBUG_ASSERT(*ptr <= 255);
> + str->append((char) *ptr);
> + }
> + ptr++;
> + continue;
> + }
> +
> + switch (*ptr) {
> +
> + case FMT_AM:
> + case FMT_PM:
> + if (l_time->hour > 11)
> + str->append("PM", 2);
> + else
> + str->append("AM", 2);
> + break;
> +
> + case FMT_AM_DOT:
> + case FMT_PM_DOT:
> + if (l_time->hour > 11)
> + str->append(STRING_WITH_LEN("P.M."));
> + else
> + str->append(STRING_WITH_LEN("A.M."));
> + break;
> +
> + case FMT_AD:
> + case FMT_BC:
> + if (l_time->year > 0)
> + str->append(STRING_WITH_LEN("AD"));
> + else
> + str->append(STRING_WITH_LEN("BC"));
> + break;
> +
> + case FMT_AD_DOT:
> + case FMT_BC_DOT:
> + if (l_time->year > 0)
> + str->append(STRING_WITH_LEN("A.D."));
> + else
> + str->append(STRING_WITH_LEN("B.C."));
> + break;
> +
> + case FMT_Y:
> + if (append_val(l_time->year%10, 1, str))
> + goto err_exit;
> + break;
> +
> + case FMT_YY:
> + case FMT_RR:
> + if (append_val(l_time->year%100, 2, str))
> + goto err_exit;
> + break;
> +
> + case FMT_YYY:
> + if (append_val(l_time->year%1000, 3, str))
> + goto err_exit;
> + break;
> +
> + case FMT_YYYY:
> + case FMT_RRRR:
> + if (append_val(l_time->year, 4, str))
> + goto err_exit;
> + break;
> +
> + case FMT_MM:
> + if (append_val(l_time->month, 2, str))
> + goto err_exit;
> + break;
> +
> + case FMT_MON:
> + {
> + if (l_time->month == 0)
> + {
> + str->append("00", 2);
> + }
> + else
> + {
> + const char *month_name= (locale->ab_month_names->
> + type_names[l_time->month-1]);
> + size_t m_len= strlen(month_name);
> + str->append(month_name, m_len, system_charset_info);
> + }
> + }
> + break;
> +
> + case FMT_MONTH:
> + {
> + if (l_time->month == 0)
> + {
> + str->append("00", 2);
> + }
> + else
> + {
> + const char *month_name= (locale->month_names->
> + type_names[l_time->month-1]);
> + size_t month_byte_len= strlen(month_name);
> + size_t month_char_len;
> + str->append(month_name, month_byte_len, system_charset_info);
> + month_char_len= my_numchars_mb(&my_charset_utf8mb3_general_ci,
> + month_name, month_name +
> + month_byte_len);
> + if (str->strfill(' ', locale->max_month_name_length - month_char_len))
> + goto err_exit;
> + }
> + }
> + break;
> +
> + case FMT_DD:
> + if (append_val(l_time->day, 2, str))
> + goto err_exit;
> + break;
> +
> + case FMT_DY:
> + {
> + if (l_time->day == 0)
> + str->append("00", 2);
> + else
> + {
> + weekday= calc_weekday(calc_daynr(l_time->year,l_time->month,
> + l_time->day), 0);
> + const char *day_name= locale->ab_day_names->type_names[weekday];
> + str->append(day_name, strlen(day_name), system_charset_info);
> + }
> + }
> + break;
> +
> + case FMT_DAY:
> + {
> + if (l_time->day == 0)
> + str->append("00", 2, system_charset_info);
> + else
> + {
> + const char *day_name;
> + size_t day_byte_len, day_char_len;
> + weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
> + l_time->day), 0);
> + day_name= locale->day_names->type_names[weekday];
> + day_byte_len= strlen(day_name);
> + str->append(day_name, day_byte_len, system_charset_info);
> + day_char_len= my_numchars_mb(&my_charset_utf8mb3_general_ci,
> + day_name, day_name + day_byte_len);
> + if (str->strfill(' ', locale->max_day_name_length - day_char_len))
> + goto err_exit;
> + }
> + }
> + break;
> +
> + case FMT_HH12:
> + case FMT_HH:
> + hours_i= (l_time->hour%24 + 11)%12+1;
> + if (append_val(hours_i, 2, str))
> + goto err_exit;
> + break;
> +
> + case FMT_HH24:
> + if (append_val(l_time->hour, 2, str))
> + goto err_exit;
> + break;
> +
> + case FMT_MI:
> + if (append_val(l_time->minute, 2, str))
> + goto err_exit;
> + break;
> +
> + case FMT_SS:
> + if (append_val(l_time->second, 2, str))
> + goto err_exit;
> + break;
> +
> + default:
> + str->append((char) *ptr);
> + }
> +
> + ptr++;
> + };
> + return false;
> +
> +err_exit:
> + return true;
> +}
> +
> +
> +bool Item_func_tochar::fix_length_and_dec()
> +{
> + thd= current_thd;
> + CHARSET_INFO *cs= thd->variables.collation_connection;
> + Item *arg1= args[1]->this_item();
> + my_repertoire_t repertoire= arg1->collation.repertoire;
> + StringBuffer<STRING_BUFFER_USUAL_SIZE> buffer;
> + String *str;
> +
> + locale= thd->variables.lc_time_names;
> + if (!thd->variables.lc_time_names->is_ascii)
> + repertoire|= MY_REPERTOIRE_EXTENDED;
> + collation.set(cs, arg1->collation.derivation, repertoire);
> +
> + /* first argument must be datetime or string */
> + enum_field_types arg0_mysql_type= args[0]->field_type();
> +
> + max_length= 0;
> + switch (arg0_mysql_type) {
> + case MYSQL_TYPE_TIME:
> + case MYSQL_TYPE_DATE:
> + case MYSQL_TYPE_DATETIME:
> + case MYSQL_TYPE_TIMESTAMP:
> + case MYSQL_TYPE_VARCHAR:
> + case MYSQL_TYPE_STRING:
> + break;
> + default:
> + {
> + my_printf_error(ER_ORACLE_COMPAT_FUNCTION_ERROR,
> + ER(ER_ORACLE_COMPAT_FUNCTION_ERROR),
> + MYF(0),
> + "data type of first argument must be type "
> + "date/datetime/time or string");
that's not how MariaDB works, it converts types.
In particular, using an integer 20200101 in the date context is
perfectly ok.
> + return TRUE;
> + }
> + }
> + if (args[1]->basic_const_item() && (str= args[1]->val_str(&buffer)))
> + {
> + uint ulen;
> + fixed_length= 1;
> + if (parse_format_string(str, &ulen))
> + {
> + my_printf_error(ER_ORACLE_COMPAT_FUNCTION_ERROR,
> + ER(ER_ORACLE_COMPAT_FUNCTION_ERROR),
> + MYF(0),
> + warning_message.c_ptr());
> + return TRUE;
> + }
> + max_length= (uint32) (ulen * collation.collation->mbmaxlen);
> + }
> + else
> + {
> + fixed_length= 0;
> + max_length= (uint32) MY_MIN(arg1->max_length * 10 *
> + collation.collation->mbmaxlen,
> + MAX_BLOB_WIDTH);
> + }
> + set_maybe_null();
> + return FALSE;
> +}
> +
> +
> +String *Item_func_tochar::val_str(String* str)
> + {
> + StringBuffer<64> format_buffer;
> + String *format;
> + MYSQL_TIME l_time;
> + const MY_LOCALE *lc= locale;
> + date_conv_mode_t mode= TIME_CONV_NONE;
> + size_t max_result_length= max_length;
> +
> + if (warning_message.length())
> + goto null_date;
> +
> + if ((null_value= args[0]->get_date(thd, &l_time,
> + Temporal::Options(mode, thd))))
> + return 0;
> +
> + if (!fixed_length)
> + {
> + uint ulen;
> + if (!(format= args[1]->val_str(&format_buffer)) || !format->length() ||
> + parse_format_string(format, &ulen))
> + goto null_date;
> + max_result_length= ((size_t) ulen) * collation.collation->mbmaxlen;
> + }
> +
> + if (str->alloc(max_result_length))
> + goto null_date;
> +
> + /* Create the result string */
> + str->set_charset(collation.collation);
> + if (!make_date_time_oracle(fmt_array, &l_time, lc, str))
> + return str;
> +
> +null_date:
> +
> + if (warning_message.length())
> + {
> + push_warning_printf(thd,
> + Sql_condition::WARN_LEVEL_WARN,
> + ER_ORACLE_COMPAT_FUNCTION_ERROR,
> + ER_THD(thd, ER_ORACLE_COMPAT_FUNCTION_ERROR),
> + warning_message.c_ptr());
> + if (!fixed_length)
> + warning_message.length(0);
> + }
> +
> + null_value= 1;
> + return 0;
> +}
> +
>
> bool Item_func_from_unixtime::fix_length_and_dec()
> {
Regards,
Sergei
Follow ups