Hi, Monty! On Apr 13, Michael Widenius wrote:
commit 57c19902326 Author: Michael Widenius <michael.widenius@gmail.com> 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