From c09338141a9e18a371d6b047b0d64f9dcda4babe Mon Sep 17 00:00:00 2001 From: ctshim <78332782+ctshim@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:22:38 +0900 Subject: [PATCH] [CBRD-25302] Strengthens checks on date_lang_string_literal in functions such as TO_DATE() (#5632) http://jira.cubrid.org/browse/CBRD-25302 * Strengthens checks on date_lang_string_literal in functions such as TO_DATE() * Functions that take 'date_lang_string_literal' as an argument - TO_CHAR - TO_DATE, TO_DATETIME, TO_DATETIME_TZ - TO_TIME, TO_TIMESTAMP, TO_TIMESTAMP_TZ NOTE: this commit is erased when fixing conflict during merging with develop --- msg/en_US.utf8/cubrid.msg | 1 + msg/ko_KR.utf8/cubrid.msg | 1 + src/base/language_support.c | 43 ++----- src/base/language_support.h | 3 +- src/parser/csql_grammar.y | 59 +++++---- src/parser/parse_tree_cl.c | 58 +++++++-- src/parser/parser_message.h | 1 + src/parser/type_checking.c | 231 ++++++++++++++++++------------------ src/query/fetch.c | 112 +++++++++++++---- src/query/string_opfunc.c | 88 ++++++++++++-- 10 files changed, 377 insertions(+), 220 deletions(-) diff --git a/msg/en_US.utf8/cubrid.msg b/msg/en_US.utf8/cubrid.msg index b46e151d48e..e16e6cb6819 100644 --- a/msg/en_US.utf8/cubrid.msg +++ b/msg/en_US.utf8/cubrid.msg @@ -1951,6 +1951,7 @@ $set 8 MSGCAT_SET_PARSER_SEMANTIC 322 Table column '%1$s.%2$s' has not been defined 323 Stored procedure/function '%1$s' has OUT or IN OUT arguments 324 '%1$s' is not a record variable. +325 The argument specifying the language must be a string literal. 325 The argument specifying the language must be a string literal. 326 Stored procedure/function "%1$s" does not exist. diff --git a/msg/ko_KR.utf8/cubrid.msg b/msg/ko_KR.utf8/cubrid.msg index 6583c4fa666..e07f91be2e2 100644 --- a/msg/ko_KR.utf8/cubrid.msg +++ b/msg/ko_KR.utf8/cubrid.msg @@ -1950,6 +1950,7 @@ $set 8 MSGCAT_SET_PARSER_SEMANTIC 322 테이블 컬럼 '%1$s.%2$s'이 정의되지 않았습니다 323 저장 프로시저/함수 '%1$s' 이(가) OUT 또는 IN OUT 인수를 가지고 있습니다. 324 '%1$s'은 레코드 변수가 아닙니다. +325 언어를 지정하는 인수는 문자열 리터럴이어야 합니다. 325 언어를 지정하는 인수는 문자열 리터럴이어야 합니다. 326 저장 프로시저/함수 "%1$s"이(가) 존재하지 않습니다. diff --git a/src/base/language_support.c b/src/base/language_support.c index 4b12349e01d..69f0c33e038 100644 --- a/src/base/language_support.c +++ b/src/base/language_support.c @@ -2311,36 +2311,6 @@ lang_set_flag_from_lang (const char *lang_str, bool has_user_format, bool has_us status = lang_get_lang_id_from_name (lang_str, &lang); } - if (lang_set_flag_from_lang_id (lang, has_user_format, has_user_lang, flag) == 0) - { - return status; - } - - assert (lang == INTL_LANG_ENGLISH); - - return 1; -} - -/* - * lang_set_flag_from_lang - set a flag according to language identifier - * - * return: 0 if language string OK and flag was set, non-zero otherwise - * lang(in): language identier - * has_user_format(in): true if user has given a format, false otherwise - * has_user_lang(in): true if user has given a language, false otherwise - * flag(out): bit flag : bits 0 and 1 are user flags, bits 2 - 31 are for - * language identification - * Bit 0 : if set, the format was given by user -* Bit 1 : if set, the language was given by user - * Bit 2 - 31 : INTL_LANG - * Consider change this flag to store the language as value - * instead of as bit map - * - * Note : function is used in context of some date-string functions. - */ -int -lang_set_flag_from_lang_id (const INTL_LANG lang, bool has_user_format, bool has_user_lang, int *flag) -{ int lang_val = (int) lang; *flag = 0; @@ -2351,13 +2321,15 @@ lang_set_flag_from_lang_id (const INTL_LANG lang, bool has_user_format, bool has if (lang_val >= lang_Count_locales) { lang_val = (int) INTL_LANG_ENGLISH; - *flag |= lang_val << 2; - return 1; + status = 1; } - *flag |= lang_val << 2; + *flag |= (lang_val << 2); - return 0; + assert (((*flag) & LANG_LOADED_LOCALES_PARITY_MASK) == 0); + *flag |= LANG_LOADED_LOCALES_PARITY; + + return status; } /* @@ -2378,7 +2350,8 @@ lang_get_lang_id_from_flag (const int flag, bool * has_user_format, bool * has_u *has_user_format = ((flag & 0x1) == 0x1) ? true : false; *has_user_lang = ((flag & 0x2) == 0x2) ? true : false; - lang_val = flag >> 2; + assert ((flag & LANG_LOADED_LOCALES_PARITY_MASK) == LANG_LOADED_LOCALES_PARITY); + lang_val = (flag & ~LANG_LOADED_LOCALES_PARITY_MASK) >> 2; if (lang_val >= 0 && lang_val < lang_Count_locales) { diff --git a/src/base/language_support.h b/src/base/language_support.h index d035d86724b..eb1695a39a5 100644 --- a/src/base/language_support.h +++ b/src/base/language_support.h @@ -51,6 +51,8 @@ #define LANG_MAX_COLLATIONS 256 #define LANG_MAX_BUILTIN_COLLATIONS 32 #define LANG_MAX_LOADED_LOCALES 32 +#define LANG_LOADED_LOCALES_PARITY_MASK 0xFFFFF000 +#define LANG_LOADED_LOCALES_PARITY 0x75A5C000 #define LANG_COERCIBLE_COLL LANG_SYS_COLLATION #define LANG_COERCIBLE_CODESET LANG_SYS_CODESET @@ -300,7 +302,6 @@ extern "C" extern int lang_get_lang_id_from_name (const char *lang_name, INTL_LANG * lang_id); extern const char *lang_get_lang_name_from_id (const INTL_LANG lang_id); extern int lang_set_flag_from_lang (const char *lang_str, bool has_user_format, bool has_user_lang, int *flag); - extern int lang_set_flag_from_lang_id (const INTL_LANG lang, bool has_user_format, bool has_user_lang, int *flag); extern INTL_LANG lang_get_lang_id_from_flag (const int flag, bool * has_user_format, bool * has_user_lang); extern const char *lang_date_format_parse (const INTL_LANG lang_id, const INTL_CODESET codeset, const DB_TYPE type, INTL_CODESET * format_codeset); diff --git a/src/parser/csql_grammar.y b/src/parser/csql_grammar.y index a6fa3c9903c..d60cd82076b 100644 --- a/src/parser/csql_grammar.y +++ b/src/parser/csql_grammar.y @@ -26393,32 +26393,51 @@ parser_make_date_lang (int arg_cnt, PT_NODE * arg3) if (arg3 && arg_cnt == 3) { char *lang_str; - if ((arg3->type_enum == PT_TYPE_CHAR || arg3->type_enum == PT_TYPE_NCHAR) && arg3->info.value.data_value.str != NULL) + if (arg3->node_type == PT_VALUE) { - date_lang = parser_new_node (this_parser, PT_VALUE); - if (!date_lang) + if ((arg3->type_enum == PT_TYPE_CHAR || arg3->type_enum == PT_TYPE_NCHAR) + && arg3->info.value.data_value.str != NULL) { - er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (PT_NODE)); - return NULL; - } - int flag = 0; - lang_str = (char *) arg3->info.value.data_value.str->bytes; - if (lang_set_flag_from_lang (lang_str, 1, 1, &flag)) - { - PT_ERROR (this_parser, arg3, "check syntax at 'date_lang'"); + date_lang = parser_new_node (this_parser, PT_VALUE); + if (!date_lang) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (PT_NODE)); + return NULL; + } + int flag = 0; + lang_str = (char *) arg3->info.value.data_value.str->bytes; + if (lang_set_flag_from_lang (lang_str, 1, 1, &flag)) + { + //PT_ERROR (this_parser, arg3, "check syntax at 'date_lang'"); + PT_ERRORmf (this_parser, arg3, MSGCAT_SET_ERROR, -(ER_LOCALE_LANG_NOT_AVAILABLE), lang_str); + } + date_lang->info.value.data_value.i = (long) flag; + date_lang->type_enum = PT_TYPE_INTEGER; + parser_free_node (this_parser, arg3); } - date_lang->info.value.data_value.i = (long) flag; } - - if (date_lang) + else if (arg3->node_type == PT_HOST_VAR) { - date_lang->type_enum = PT_TYPE_INTEGER; - parser_free_node (this_parser, arg3); - } - else + date_lang = arg3; + } + else if(this_parser->flag.is_parsing_static_sql) { - date_lang = arg3; + if (arg3->node_type == PT_EXPR && arg3->info.expr.op == PT_CAST) + { + PT_NODE * node = arg3->info.expr.cast_type; + if(node->node_type == PT_DATA_TYPE && PT_IS_SIMPLE_CHAR_STRING_TYPE(node->type_enum)) + { + date_lang = arg3; + } + } } + + if (date_lang == NULL) + { + PT_ERRORm (this_parser, arg3, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_SUPPORT_TYPE_TO_DATE_LANG); + + date_lang = arg3; + } } else { @@ -26442,7 +26461,7 @@ parser_make_date_lang (int arg_cnt, PT_NODE * arg3) } } - return date_lang; + return date_lang; } static PT_NODE * diff --git a/src/parser/parse_tree_cl.c b/src/parser/parse_tree_cl.c index 739217d03ea..38bf91c5fde 100644 --- a/src/parser/parse_tree_cl.c +++ b/src/parser/parse_tree_cl.c @@ -10573,23 +10573,63 @@ pt_print_expr (PARSER_CONTEXT * parser, PT_NODE * p) r1 = pt_print_bytes (parser, p->info.expr.arg1); q = pt_append_varchar (parser, q, r1); - flags = p->info.expr.arg3->info.value.data_value.i; - lang_id = lang_get_lang_id_from_flag (flags, &has_user_format, &has_user_lang); - if (has_user_format) + assert (p->info.expr.arg3); + if (p->info.expr.arg3->node_type == PT_HOST_VAR) { - const char *lang_name = lang_get_lang_name_from_id (lang_id); - q = pt_append_nulstring (parser, q, ", "); r1 = pt_print_bytes (parser, p->info.expr.arg2); q = pt_append_varchar (parser, q, r1); - if (lang_name != NULL && has_user_lang) + q = pt_append_nulstring (parser, q, ", "); + r1 = pt_print_bytes (parser, p->info.expr.arg3); + q = pt_append_varchar (parser, q, r1); + } + else if (p->info.expr.arg3->node_type == PT_VALUE) + { + flags = p->info.expr.arg3->info.value.data_value.i; + lang_id = lang_get_lang_id_from_flag (flags, &has_user_format, &has_user_lang); + if (has_user_format) { - q = pt_append_nulstring (parser, q, ", '"); - q = pt_append_nulstring (parser, q, lang_name); - q = pt_append_nulstring (parser, q, "'"); + const char *lang_name = lang_get_lang_name_from_id (lang_id); + + q = pt_append_nulstring (parser, q, ", "); + r1 = pt_print_bytes (parser, p->info.expr.arg2); + q = pt_append_varchar (parser, q, r1); + + if (lang_name != NULL && has_user_lang) + { + q = pt_append_nulstring (parser, q, ", '"); + q = pt_append_nulstring (parser, q, lang_name); + q = pt_append_nulstring (parser, q, "'"); + } } } + else if (parser->flag.is_parsing_static_sql) + { + assert (p->info.expr.arg3->node_type == PT_EXPR && p->info.expr.arg3->info.expr.op == PT_CAST); + assert (p->info.expr.arg3->info.expr.cast_type->node_type == PT_DATA_TYPE); + assert (PT_IS_SIMPLE_CHAR_STRING_TYPE (p->info.expr.arg3->info.expr.cast_type->type_enum)); + q = pt_append_nulstring (parser, q, ", "); + r1 = pt_print_bytes (parser, p->info.expr.arg2); + q = pt_append_varchar (parser, q, r1); + + q = pt_append_nulstring (parser, q, ", "); + r1 = pt_print_bytes (parser, p->info.expr.arg3); + q = pt_append_varchar (parser, q, r1); + } + else + { + /* + create table foo(a char(20), b varchar, c nchar(20), d nchar varying, e sequence(int)); + insert into foo values('aaa', 'bbb', n'ccc', n'ddd', {1, 2, 3, 4, 5}); + select to_char(e) from foo order by 1; + + --In a case like "select to_char(e) from foo", it enters the second step. + */ + assert ((pt_has_error (parser) || er_errid () != NO_ERROR) + || (p->info.expr.arg2 && + p->info.expr.arg2->node_type == PT_VALUE && p->info.expr.arg2->type_enum == PT_TYPE_NULL)); + } q = pt_append_nulstring (parser, q, ")"); } break; diff --git a/src/parser/parser_message.h b/src/parser/parser_message.h index 07cabb253a2..db177a312c4 100644 --- a/src/parser/parser_message.h +++ b/src/parser/parser_message.h @@ -502,6 +502,7 @@ #define MSGCAT_SEMANTIC_UNDEFINED_TABLE_COLUMN MSGCAT_SEMANTIC_NO(322) #define MSGCAT_SEMANTIC_SP_OUT_ARGS_EXISTS_IN_QUERY MSGCAT_SEMANTIC_NO(323) #define MSGCAT_SEMANTIC_SP_INTO_FIELD_EXPR_IN_NON_STATIC_SQL MSGCAT_SEMANTIC_NO(324) +#define MSGCAT_SEMANTIC_NOT_SUPPORT_TYPE_TO_DATE_LANG MSGCAT_SEMANTIC_NO(325) #define MSGCAT_SEMANTIC_NOT_SUPPORT_TYPE_TO_DATE_LANG MSGCAT_SEMANTIC_NO(325) #define MSTCAT_SEMANTIC_SP_NOT_EXIST MSGCAT_SEMANTIC_NO(326) diff --git a/src/parser/type_checking.c b/src/parser/type_checking.c index eb59e0df2cf..69f0b9d24a9 100644 --- a/src/parser/type_checking.c +++ b/src/parser/type_checking.c @@ -2413,7 +2413,7 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) case PT_TO_DATE: num = 0; - /* one overload */ + /* two overload */ /* arg1 */ sig.arg1_type.type = pt_arg_type::GENERIC; @@ -2422,21 +2422,29 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_STRING; /* arg3 */ - sig.arg3_type.type = pt_arg_type::NORMAL; - sig.arg3_type.val.type = PT_TYPE_INTEGER; - + sig.arg3_type.type = pt_arg_type::GENERIC; + sig.arg3_type.val.generic_type = PT_GENERIC_TYPE_STRING; /* return type */ sig.return_type.type = pt_arg_type::NORMAL; sig.return_type.val.type = PT_TYPE_DATE; def->overloads[num++] = sig; + /* arg1 */ + /* arg2 */ + /* arg3 */ + sig.arg3_type.type = pt_arg_type::NORMAL; + sig.arg3_type.val.type = PT_TYPE_INTEGER; + /* return type */ + def->overloads[num++] = sig; + def->overloads_count = num; break; case PT_TO_DATETIME: + case PT_TO_DATETIME_TZ: num = 0; - /* two overloads */ + /* four overloads */ /* arg1 */ sig.arg1_type.type = pt_arg_type::GENERIC; @@ -2445,12 +2453,19 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_CHAR; /* arg3 */ + sig.arg3_type.type = pt_arg_type::GENERIC; + sig.arg3_type.val.generic_type = PT_GENERIC_TYPE_STRING; + /* return type */ + sig.return_type.type = pt_arg_type::NORMAL; + sig.return_type.val.type = (op == PT_TO_DATETIME) ? PT_TYPE_DATETIME : PT_TYPE_DATETIMETZ; + def->overloads[num++] = sig; + + /* arg1 */ + /* arg2 */ + /* arg3 */ sig.arg3_type.type = pt_arg_type::NORMAL; sig.arg3_type.val.type = PT_TYPE_INTEGER; - /* return type */ - sig.return_type.type = pt_arg_type::NORMAL; - sig.return_type.val.type = PT_TYPE_DATETIME; def->overloads[num++] = sig; /* arg1 */ @@ -2460,12 +2475,19 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_NCHAR; /* arg3 */ + sig.arg3_type.type = pt_arg_type::GENERIC; + sig.arg3_type.val.generic_type = PT_GENERIC_TYPE_STRING; + /* return type */ + sig.return_type.type = pt_arg_type::NORMAL; + sig.return_type.val.type = (op == PT_TO_DATETIME) ? PT_TYPE_DATETIME : PT_TYPE_DATETIMETZ; + def->overloads[num++] = sig; + + /* arg1 */ + /* arg2 */ + /* arg3 */ sig.arg3_type.type = pt_arg_type::NORMAL; sig.arg3_type.val.type = PT_TYPE_INTEGER; - /* return type */ - sig.return_type.type = pt_arg_type::NORMAL; - sig.return_type.val.type = PT_TYPE_DATETIME; def->overloads[num++] = sig; def->overloads_count = num; @@ -2474,7 +2496,7 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) case PT_TO_TIME: num = 0; - /* two overloads */ + /* four overloads */ /* arg1 */ sig.arg1_type.type = pt_arg_type::GENERIC; @@ -2483,14 +2505,22 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_CHAR; /* arg3 */ - sig.arg3_type.type = pt_arg_type::NORMAL; - sig.arg3_type.val.type = PT_TYPE_INTEGER; + sig.arg3_type.type = pt_arg_type::GENERIC; + sig.arg3_type.val.generic_type = PT_GENERIC_TYPE_STRING; /* return type */ sig.return_type.type = pt_arg_type::NORMAL; sig.return_type.val.type = PT_TYPE_TIME; def->overloads[num++] = sig; + /* arg1 */ + /* arg2 */ + /* arg3 */ + sig.arg3_type.type = pt_arg_type::NORMAL; + sig.arg3_type.val.type = PT_TYPE_INTEGER; + /* return type */ + def->overloads[num++] = sig; + /* arg1 */ sig.arg1_type.type = pt_arg_type::GENERIC; sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_NCHAR; @@ -2498,21 +2528,29 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_NCHAR; /* arg3 */ - sig.arg3_type.type = pt_arg_type::NORMAL; - sig.arg3_type.val.type = PT_TYPE_INTEGER; - + sig.arg3_type.type = pt_arg_type::GENERIC; + sig.arg3_type.val.generic_type = PT_GENERIC_TYPE_STRING; /* return type */ sig.return_type.type = pt_arg_type::NORMAL; sig.return_type.val.type = PT_TYPE_TIME; def->overloads[num++] = sig; + /* arg1 */ + /* arg2 */ + /* arg3 */ + sig.arg3_type.type = pt_arg_type::NORMAL; + sig.arg3_type.val.type = PT_TYPE_INTEGER; + /* return type */ + def->overloads[num++] = sig; + def->overloads_count = num; break; case PT_TO_TIMESTAMP: + case PT_TO_TIMESTAMP_TZ: num = 0; - /* two overloads */ + /* four overloads */ /* arg1 */ sig.arg1_type.type = pt_arg_type::GENERIC; @@ -2521,12 +2559,19 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_CHAR; /* arg3 */ + sig.arg3_type.type = pt_arg_type::GENERIC; + sig.arg3_type.val.generic_type = PT_GENERIC_TYPE_STRING; + /* return type */ + sig.return_type.type = pt_arg_type::NORMAL; + sig.return_type.val.type = (op == PT_TO_TIMESTAMP) ? PT_TYPE_TIMESTAMP : PT_TYPE_TIMESTAMPTZ; + def->overloads[num++] = sig; + + /* arg1 */ + /* arg2 */ + /* arg3 */ sig.arg3_type.type = pt_arg_type::NORMAL; sig.arg3_type.val.type = PT_TYPE_INTEGER; - /* return type */ - sig.return_type.type = pt_arg_type::NORMAL; - sig.return_type.val.type = PT_TYPE_TIMESTAMP; def->overloads[num++] = sig; /* arg1 */ @@ -2536,12 +2581,19 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_NCHAR; /* arg3 */ + sig.arg3_type.type = pt_arg_type::GENERIC; + sig.arg3_type.val.generic_type = PT_GENERIC_TYPE_STRING; + /* return type */ + sig.return_type.type = pt_arg_type::NORMAL; + sig.return_type.val.type = (op == PT_TO_TIMESTAMP) ? PT_TYPE_TIMESTAMP : PT_TYPE_TIMESTAMPTZ; + def->overloads[num++] = sig; + + /* arg1 */ + /* arg2 */ + /* arg3 */ sig.arg3_type.type = pt_arg_type::NORMAL; sig.arg3_type.val.type = PT_TYPE_INTEGER; - /* return type */ - sig.return_type.type = pt_arg_type::NORMAL; - sig.return_type.val.type = PT_TYPE_TIMESTAMP; def->overloads[num++] = sig; def->overloads_count = num; @@ -3646,34 +3698,42 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) case PT_TO_CHAR: num = 0; - /* two overloads */ + /* four overloads */ + /* arg1, arg2, arg3, return type */ sig.arg1_type.type = pt_arg_type::GENERIC; sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_NUMBER; - sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_STRING; - - sig.arg3_type.type = pt_arg_type::NORMAL; - sig.arg3_type.val.type = PT_TYPE_INTEGER; - + sig.arg3_type.type = pt_arg_type::GENERIC; + sig.arg3_type.val.generic_type = PT_GENERIC_TYPE_STRING; sig.return_type.type = pt_arg_type::NORMAL; sig.return_type.val.type = PT_TYPE_VARCHAR; def->overloads[num++] = sig; + /* arg1 */ sig.arg1_type.type = pt_arg_type::GENERIC; sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_DATETIME; + /* arg2, arg3, return type */ + def->overloads[num++] = sig; + /* arg1, arg2, arg3, return type */ + sig.arg1_type.type = pt_arg_type::GENERIC; + sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_NUMBER; sig.arg2_type.type = pt_arg_type::GENERIC; sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_STRING; - sig.arg3_type.type = pt_arg_type::NORMAL; sig.arg3_type.val.type = PT_TYPE_INTEGER; - sig.return_type.type = pt_arg_type::NORMAL; sig.return_type.val.type = PT_TYPE_VARCHAR; def->overloads[num++] = sig; + /* arg1 */ + sig.arg1_type.type = pt_arg_type::GENERIC; + sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_DATETIME; + /* arg2, arg3, return type */ + def->overloads[num++] = sig; + def->overloads_count = num; break; @@ -4640,81 +4700,6 @@ pt_get_expression_definition (const PT_OP_TYPE op, EXPRESSION_DEFINITION * def) def->overloads_count = num; break; - case PT_TO_DATETIME_TZ: - num = 0; - - /* two overloads */ - - /* arg1 */ - sig.arg1_type.type = pt_arg_type::GENERIC; - sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_CHAR; - /* arg2 */ - sig.arg2_type.type = pt_arg_type::GENERIC; - sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_CHAR; - /* arg3 */ - sig.arg3_type.type = pt_arg_type::NORMAL; - sig.arg3_type.val.type = PT_TYPE_INTEGER; - - /* return type */ - sig.return_type.type = pt_arg_type::NORMAL; - sig.return_type.val.type = PT_TYPE_DATETIMETZ; - def->overloads[num++] = sig; - - /* arg1 */ - sig.arg1_type.type = pt_arg_type::GENERIC; - sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_NCHAR; - /* arg2 */ - sig.arg2_type.type = pt_arg_type::GENERIC; - sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_NCHAR; - /* arg3 */ - sig.arg3_type.type = pt_arg_type::NORMAL; - sig.arg3_type.val.type = PT_TYPE_INTEGER; - - /* return type */ - sig.return_type.type = pt_arg_type::NORMAL; - sig.return_type.val.type = PT_TYPE_DATETIMETZ; - def->overloads[num++] = sig; - - def->overloads_count = num; - break; - - case PT_TO_TIMESTAMP_TZ: - num = 0; - - /* two overloads */ - - /* arg1 */ - sig.arg1_type.type = pt_arg_type::GENERIC; - sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_CHAR; - /* arg2 */ - sig.arg2_type.type = pt_arg_type::GENERIC; - sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_CHAR; - /* arg3 */ - sig.arg3_type.type = pt_arg_type::NORMAL; - sig.arg3_type.val.type = PT_TYPE_INTEGER; - - /* return type */ - sig.return_type.type = pt_arg_type::NORMAL; - sig.return_type.val.type = PT_TYPE_TIMESTAMPTZ; - def->overloads[num++] = sig; - - /* arg1 */ - sig.arg1_type.type = pt_arg_type::GENERIC; - sig.arg1_type.val.generic_type = PT_GENERIC_TYPE_NCHAR; - /* arg2 */ - sig.arg2_type.type = pt_arg_type::GENERIC; - sig.arg2_type.val.generic_type = PT_GENERIC_TYPE_NCHAR; - /* arg3 */ - sig.arg3_type.type = pt_arg_type::NORMAL; - sig.arg3_type.val.type = PT_TYPE_INTEGER; - - /* return type */ - sig.return_type.type = pt_arg_type::NORMAL; - sig.return_type.val.type = PT_TYPE_TIMESTAMPTZ; - def->overloads[num++] = sig; - - def->overloads_count = num; - break; case PT_CRC32: num = 0; @@ -9204,17 +9189,22 @@ pt_eval_expr_type (PARSER_CONTEXT * parser, PT_NODE * node) bool has_user_lang = false; const char *lang_str; - assert (arg3 != NULL && arg3->node_type == PT_VALUE && arg3_type == PT_TYPE_INTEGER); - /* change locale from date_lang (set by grammar) to number_lang */ - (void) lang_get_lang_id_from_flag (arg3->info.value.data_value.i, &has_user_format, &has_user_lang); - if (!has_user_lang) + assert (arg3 != NULL && + (arg3->node_type == PT_HOST_VAR || (arg3->node_type == PT_VALUE && arg3_type == PT_TYPE_INTEGER))); + + if (arg3->node_type != PT_HOST_VAR) { - int lang_flag; - lang_str = prm_get_string_value (PRM_ID_INTL_NUMBER_LANG); - (void) lang_set_flag_from_lang (lang_str, has_user_format, has_user_lang, &lang_flag); - arg3->info.value.data_value.i = lang_flag; - arg3->info.value.db_value_is_initialized = 0; - pt_value_to_db (parser, arg3); + /* change locale from date_lang (set by grammar) to number_lang */ + (void) lang_get_lang_id_from_flag (arg3->info.value.data_value.i, &has_user_format, &has_user_lang); + if (!has_user_lang) + { + int lang_flag; + lang_str = prm_get_string_value (PRM_ID_INTL_NUMBER_LANG); + (void) lang_set_flag_from_lang (lang_str, has_user_format, has_user_lang, &lang_flag); + arg3->info.value.data_value.i = lang_flag; + arg3->info.value.db_value_is_initialized = 0; + pt_value_to_db (parser, arg3); + } } } @@ -20387,7 +20377,14 @@ pt_is_op_hv_late_bind (PT_OP_TYPE op) case PT_HOURF: case PT_MINUTEF: case PT_SECONDF: + case PT_TO_DATE: + case PT_TO_DATETIME: + case PT_TO_DATETIME_TZ: + case PT_TO_TIME: + case PT_TO_TIMESTAMP: + case PT_TO_TIMESTAMP_TZ: return true; + default: return false; } diff --git a/src/query/fetch.c b/src/query/fetch.c index 082c7e0bf49..0d6ee6914fb 100644 --- a/src/query/fetch.c +++ b/src/query/fetch.c @@ -2219,6 +2219,24 @@ fetch_peek_arith (THREAD_ENTRY * thread_p, REGU_VARIABLE * regu_var, val_descr * { PRIM_SET_NULL (arithptr->value); } + else if (!TP_IS_CHAR_TYPE (DB_VALUE_TYPE (peek_left))) + { + DB_VALUE tval; + + db_make_null (&tval); + dom_status = tp_value_cast (peek_left, &tval, &tp_Char_domain, false); + if (dom_status != DOMAIN_COMPATIBLE) + { + (void) tp_domain_status_er_set (dom_status, ARG_FILE_LINE, peek_left, arithptr->domain); + goto error; + } + if (db_to_date (&tval, peek_right, peek_third, arithptr->value) != NO_ERROR) + { + db_value_clear (&tval); + goto error; + } + db_value_clear (&tval); + } else if (db_to_date (peek_left, peek_right, peek_third, arithptr->value) != NO_ERROR) { goto error; @@ -2230,6 +2248,24 @@ fetch_peek_arith (THREAD_ENTRY * thread_p, REGU_VARIABLE * regu_var, val_descr * { PRIM_SET_NULL (arithptr->value); } + else if (!TP_IS_CHAR_TYPE (DB_VALUE_TYPE (peek_left))) + { + DB_VALUE tval; + + db_make_null (&tval); + dom_status = tp_value_cast (peek_left, &tval, &tp_Char_domain, false); + if (dom_status != DOMAIN_COMPATIBLE) + { + (void) tp_domain_status_er_set (dom_status, ARG_FILE_LINE, peek_left, arithptr->domain); + goto error; + } + if (db_to_time (&tval, peek_right, peek_third, DB_TYPE_TIME, arithptr->value) != NO_ERROR) + { + db_value_clear (&tval); + goto error; + } + db_value_clear (&tval); + } else if (db_to_time (peek_left, peek_right, peek_third, DB_TYPE_TIME, arithptr->value) != NO_ERROR) { goto error; @@ -2237,24 +2273,70 @@ fetch_peek_arith (THREAD_ENTRY * thread_p, REGU_VARIABLE * regu_var, val_descr * break; case T_TO_TIMESTAMP: + case T_TO_TIMESTAMP_TZ: if (DB_IS_NULL (peek_left)) { PRIM_SET_NULL (arithptr->value); } - else if (db_to_timestamp (peek_left, peek_right, peek_third, DB_TYPE_TIMESTAMP, arithptr->value) != NO_ERROR) + else { - goto error; + DB_TYPE db_type = (arithptr->opcode == T_TO_TIMESTAMP) ? DB_TYPE_TIMESTAMP : DB_TYPE_TIMESTAMPTZ; + if (!TP_IS_CHAR_TYPE (DB_VALUE_TYPE (peek_left))) + { + DB_VALUE tval; + + db_make_null (&tval); + dom_status = tp_value_cast (peek_left, &tval, &tp_Char_domain, false); + if (dom_status != DOMAIN_COMPATIBLE) + { + (void) tp_domain_status_er_set (dom_status, ARG_FILE_LINE, peek_left, arithptr->domain); + goto error; + } + if (db_to_timestamp (&tval, peek_right, peek_third, db_type, arithptr->value) != NO_ERROR) + { + db_value_clear (&tval); + goto error; + } + db_value_clear (&tval); + } + else if (db_to_timestamp (peek_left, peek_right, peek_third, db_type, arithptr->value) != NO_ERROR) + { + goto error; + } } break; case T_TO_DATETIME: + case T_TO_DATETIME_TZ: if (DB_IS_NULL (peek_left)) { PRIM_SET_NULL (arithptr->value); } - else if (db_to_datetime (peek_left, peek_right, peek_third, DB_TYPE_DATETIME, arithptr->value) != NO_ERROR) + else { - goto error; + DB_TYPE db_type = (arithptr->opcode == T_TO_DATETIME) ? DB_TYPE_DATETIME : DB_TYPE_DATETIMETZ; + if (!TP_IS_CHAR_TYPE (DB_VALUE_TYPE (peek_left))) + { + DB_VALUE tval; + + db_make_null (&tval); + dom_status = tp_value_cast (peek_left, &tval, &tp_Char_domain, false); + if (dom_status != DOMAIN_COMPATIBLE) + { + (void) tp_domain_status_er_set (dom_status, ARG_FILE_LINE, peek_left, arithptr->domain); + goto error; + } + if (db_to_datetime (&tval, peek_right, peek_third, db_type, arithptr->value) != NO_ERROR) + { + db_value_clear (&tval); + goto error; + } + db_value_clear (&tval); + } + else if (db_to_datetime (peek_left, peek_right, peek_third, db_type, arithptr->value) != NO_ERROR) + { + goto error; + } } break; @@ -3564,28 +3646,6 @@ fetch_peek_arith (THREAD_ENTRY * thread_p, REGU_VARIABLE * regu_var, val_descr * } break; - case T_TO_DATETIME_TZ: - if (DB_IS_NULL (peek_left)) - { - PRIM_SET_NULL (arithptr->value); - } - else if (db_to_datetime (peek_left, peek_right, peek_third, DB_TYPE_DATETIMETZ, arithptr->value) != NO_ERROR) - { - goto error; - } - break; - - case T_TO_TIMESTAMP_TZ: - if (DB_IS_NULL (peek_left)) - { - PRIM_SET_NULL (arithptr->value); - } - else if (db_to_timestamp (peek_left, peek_right, peek_third, DB_TYPE_TIMESTAMPTZ, arithptr->value) != NO_ERROR) - { - goto error; - } - break; - case T_UTC_TIMESTAMP: { DB_DATE date; diff --git a/src/query/string_opfunc.c b/src/query/string_opfunc.c index 2762338a620..2230e1ec238 100644 --- a/src/query/string_opfunc.c +++ b/src/query/string_opfunc.c @@ -13171,6 +13171,51 @@ const char *Am_Pm_name_EUCKR[][12] = { "a.m.", "p.m.", "A.m.", "P.m.", "A.M.", "P.M."}, /* TR */ }; +static inline int +check_date_lang_on_prepared (const DB_VALUE * date_lang, INTL_LANG * date_lang_id, bool * has_user_fmt) +{ + bool has_user_lang = false; + int flag; + + switch (DB_VALUE_TYPE (date_lang)) + { + case DB_TYPE_CHAR: + case DB_TYPE_VARCHAR: + case DB_TYPE_NCHAR: + case DB_TYPE_VARNCHAR: + /* We got here because we used HOST_VAR. And using HOST_VAR means we didn't omit format. */ + if (lang_get_lang_id_from_name (db_get_string (date_lang), date_lang_id)) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOCALE_LANG_NOT_AVAILABLE, 1, db_get_string (date_lang)); + return ER_LOCALE_LANG_NOT_AVAILABLE; + } + *has_user_fmt = true; + + return NO_ERROR; + + case DB_TYPE_INTEGER: + flag = db_get_int (date_lang); + if ((flag & LANG_LOADED_LOCALES_PARITY_MASK) == LANG_LOADED_LOCALES_PARITY) + { + *date_lang_id = lang_get_lang_id_from_flag (flag, has_user_fmt, &has_user_lang); + + return NO_ERROR; + } + else + { + /* We got here because we used HOST_VAR. This may be the case when the bound value is int. */ + } + + break; + + default: + break; + } + + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INVALID_DATA_TYPE, 0); + return ER_QSTR_INVALID_DATA_TYPE; +} + /* * db_to_date () - */ @@ -13213,8 +13258,11 @@ db_to_date (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB_VALU return error_status; } - assert (DB_VALUE_TYPE (date_lang) == DB_TYPE_INTEGER); - date_lang_id = lang_get_lang_id_from_flag (db_get_int (date_lang), &has_user_format, &dummy); + error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format); + if (error_status != NO_ERROR) + { + return error_status; + } if (false == is_char_string (src_str)) { @@ -13783,8 +13831,11 @@ db_to_time (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB_VALU return error_status; } - assert (DB_VALUE_TYPE (date_lang) == DB_TYPE_INTEGER); - date_lang_id = lang_get_lang_id_from_flag (db_get_int (date_lang), &has_user_format, &dummy); + error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format); + if (error_status != NO_ERROR) + { + return error_status; + } /* now return null */ if (false == is_char_string (src_str)) @@ -14354,8 +14405,11 @@ db_to_timestamp (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB return error_status; } - assert (DB_VALUE_TYPE (date_lang) == DB_TYPE_INTEGER); - date_lang_id = lang_get_lang_id_from_flag (db_get_int (date_lang), &has_user_format, &dummy); + error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format); + if (error_status != NO_ERROR) + { + return error_status; + } if (false == is_char_string (src_str)) { @@ -15265,8 +15319,11 @@ db_to_datetime (const DB_VALUE * src_str, const DB_VALUE * format_str, const DB_ return error_status; } - assert (DB_VALUE_TYPE (date_lang) == DB_TYPE_INTEGER); - date_lang_id = lang_get_lang_id_from_flag (db_get_int (date_lang), &has_user_format, &dummy); + error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format); + if (error_status != NO_ERROR) + { + return error_status; + } if (false == is_char_string (src_str)) { @@ -16719,8 +16776,11 @@ date_to_char (const DB_VALUE * src_value, const DB_VALUE * format_str, const DB_ return error_status; } - assert (DB_VALUE_TYPE (date_lang) == DB_TYPE_INTEGER); - date_lang_id = lang_get_lang_id_from_flag (db_get_int (date_lang), &has_user_format, &dummy); + error_status = check_date_lang_on_prepared (date_lang, &date_lang_id, &has_user_format); + if (error_status != NO_ERROR) + { + return error_status; + } no_user_format = (format_str == NULL) || (!has_user_format); @@ -17529,7 +17589,12 @@ number_to_char (const DB_VALUE * src_value, const DB_VALUE * format_str, const D return error_status; } - number_lang_id = lang_get_lang_id_from_flag (db_get_int (number_lang), &has_user_format, &dummy); + error_status = check_date_lang_on_prepared (number_lang, &number_lang_id, &has_user_format); + if (error_status != NO_ERROR) + { + return error_status; + } + fraction_symbol = lang_digit_fractional_symbol (number_lang_id); digit_grouping_symbol = lang_digit_grouping_symbol (number_lang_id); currency = lang_locale_currency (lang_get_lang_name_from_id (number_lang_id)); @@ -19985,7 +20050,6 @@ db_format (const DB_VALUE * value, const DB_VALUE * decimals, const DB_VALUE * n } db_make_string (&format_val, format); - error = number_to_char (num_dbval_p, &format_val, number_lang, &formatted_val, domain); if (error == NO_ERROR) {