From 73cc3fd408f40fa4b7cd991027d21d9fe4c31c2e 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 1/6] [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 --- msg/en_US.utf8/cubrid.msg | 5 +- msg/ko_KR.utf8/cubrid.msg | 5 +- src/base/error_code.h | 4 +- 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 ++++++++++++-- 11 files changed, 386 insertions(+), 223 deletions(-) diff --git a/msg/en_US.utf8/cubrid.msg b/msg/en_US.utf8/cubrid.msg index 356a9aa4b5c..01f06b7c7ec 100644 --- a/msg/en_US.utf8/cubrid.msg +++ b/msg/en_US.utf8/cubrid.msg @@ -1451,7 +1451,9 @@ Check the path of the key file (_keys) and make sure that it includes proper key 1361 Invalid result cache for subquery. -1362 Last Error +1362 Locale '%1$s' is unavailable. + +1363 Last Error $set 6 MSGCAT_SET_INTERNAL 1 Error in error subsystem (line %1$d): @@ -1945,6 +1947,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. $set 9 MSGCAT_SET_PARSER_RUNTIME 1 Out of virtual memory: unable to allocate %1$d bytes. diff --git a/msg/ko_KR.utf8/cubrid.msg b/msg/ko_KR.utf8/cubrid.msg index 6f51c6139f7..cfa1ec39fef 100644 --- a/msg/ko_KR.utf8/cubrid.msg +++ b/msg/ko_KR.utf8/cubrid.msg @@ -1451,7 +1451,9 @@ $ LOADDB 1361 부질의 캐시가 잘못되었습니다. -1362 마지막 에러 +1362 언어 '%1$s'에 대한 로캘은 사용 가능하지 않습니다. + +1363 마지막 에러 $set 6 MSGCAT_SET_INTERNAL 1 에러 서브 시스템에 에러 발생(라인 %1$d): @@ -1945,6 +1947,7 @@ $set 8 MSGCAT_SET_PARSER_SEMANTIC 322 테이블 컬럼 '%1$s.%2$s'이 정의되지 않았습니다 323 Stored procedure/function '%1$s' 이(가) OUT 또는 IN OUT 인수를 가지고 있습니다. 324 '%1$s'은 레코드 변수가 아닙니다. +325 언어를 지정하는 인수는 문자열 리터럴이어야 합니다. $set 9 MSGCAT_SET_PARSER_RUNTIME 1 가상 메모리 없음: %1$d 바이트를 할당할 수 없습니다. diff --git a/src/base/error_code.h b/src/base/error_code.h index 90ac9e97e98..114e687e1a4 100644 --- a/src/base/error_code.h +++ b/src/base/error_code.h @@ -1741,7 +1741,9 @@ #define ER_QPROC_RESULT_CACHE_INVALID -1361 -#define ER_LAST_ERROR -1362 +#define ER_LOCALE_LANG_NOT_AVAILABLE -1362 + +#define ER_LAST_ERROR -1363 /* * CAUTION! 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 17c1fc46d8b..b7522b7c32e 100644 --- a/src/parser/csql_grammar.y +++ b/src/parser/csql_grammar.y @@ -26158,32 +26158,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 { @@ -26207,7 +26226,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 dcc75354c58..412d43d2c7d 100644 --- a/src/parser/parse_tree_cl.c +++ b/src/parser/parse_tree_cl.c @@ -10504,23 +10504,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 64bb3b9a8c3..efe01b11c33 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) /* Message id in the set MSGCAT_SET_PARSER_RUNTIME */ #define MSGCAT_RUNTIME_NO(n) n 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 69d49c6645d..d78d38d411c 100644 --- a/src/query/fetch.c +++ b/src/query/fetch.c @@ -2218,6 +2218,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; @@ -2229,6 +2247,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; @@ -2236,24 +2272,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; @@ -3563,28 +3645,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) { From 35419404fd2fb32885d63f5beaccc808fba4bcd4 Mon Sep 17 00:00:00 2001 From: Yeunjun Lee Date: Mon, 9 Dec 2024 15:52:46 +0900 Subject: [PATCH 2/6] [CBRD-24741] Provides a way to revive the cub_server process in case of abnormal shutdown (#5674) http://jira.cubrid.org/browse/CBRD-24741 --- src/base/system_parameter.c | 2 +- src/connection/connection_defs.h | 1 + src/connection/connection_globals.h | 22 + src/connection/connection_sr.c | 69 +++- src/connection/connection_sr.h | 3 + src/executables/commdb.c | 32 ++ src/executables/master.c | 94 ++++- src/executables/master_request.c | 71 ++++ src/executables/master_request.h | 1 + src/executables/master_server_monitor.cpp | 477 ++++++++++++++++++++++ src/executables/master_server_monitor.hpp | 175 ++++++++ src/executables/server.c | 47 +-- src/executables/util_service.c | 18 + src/executables/utility.h | 3 + util/CMakeLists.txt | 1 + 15 files changed, 951 insertions(+), 65 deletions(-) create mode 100644 src/executables/master_server_monitor.cpp create mode 100644 src/executables/master_server_monitor.hpp diff --git a/src/base/system_parameter.c b/src/base/system_parameter.c index 898ec19c865..0c9c4130599 100644 --- a/src/base/system_parameter.c +++ b/src/base/system_parameter.c @@ -3626,7 +3626,7 @@ SYSPRM_PARAM prm_Def[] = { (DUP_PRM_FUNC) NULL}, {PRM_ID_AUTO_RESTART_SERVER, PRM_NAME_AUTO_RESTART_SERVER, - (PRM_FOR_SERVER | PRM_USER_CHANGE), + (PRM_FOR_CLIENT | PRM_USER_CHANGE), PRM_BOOLEAN, &prm_auto_restart_server_flag, (void *) &prm_auto_restart_server_default, diff --git a/src/connection/connection_defs.h b/src/connection/connection_defs.h index 994196c70cf..fb36d7f9003 100644 --- a/src/connection/connection_defs.h +++ b/src/connection/connection_defs.h @@ -107,6 +107,7 @@ enum css_client_request CANCEL_SHUTDOWN = 15, GET_SHUTDOWN_TIME = 16, KILL_SERVER_IMMEDIATE = 17, + SHUTDOWN_REVIVING_SERVER = 18, GET_REPL_LIST = 20, /* REPL: get the info. for a process */ GET_ALL_LIST = 21, /* REPL: get the info. for all processes */ GET_REPL_COUNT = 22, /* REPL: get the # of repl processes */ diff --git a/src/connection/connection_globals.h b/src/connection/connection_globals.h index 20cb63c7618..06973ebb487 100644 --- a/src/connection/connection_globals.h +++ b/src/connection/connection_globals.h @@ -32,6 +32,8 @@ #define CSS_MAX_CLIENT_COUNT 4000 +#define CSS_SERVER_PROC_REGISTER_INITIALIZER {-1, "", 0, "", ""} + typedef bool (*CSS_CHECK_CLIENT_TYPE) (BOOT_CLIENT_TYPE client_type); typedef int (*CSS_GET_MAX_CONN_NUM) (void); @@ -57,6 +59,26 @@ typedef struct css_conn_rule_info int num_curr_conn; } CSS_CONN_RULE_INFO; +/* + * server register resource message body + */ + +/* process register */ +typedef struct css_server_proc_register CSS_SERVER_PROC_REGISTER; +struct css_server_proc_register +{ + static constexpr int CSS_SERVER_MAX_SZ_SERVER_NAME = 256; + static constexpr int CSS_SERVER_MAX_SZ_PROC_EXEC_PATH = 128; + static constexpr int CSS_SERVER_MAX_SZ_PROC_ARGS = 1024; + + int pid; + char server_name[CSS_SERVER_MAX_SZ_SERVER_NAME]; + int server_name_length; + + char exec_path[CSS_SERVER_MAX_SZ_PROC_EXEC_PATH]; + char args[CSS_SERVER_MAX_SZ_PROC_ARGS]; +}; + extern int css_Service_id; extern const char *css_Service_name; diff --git a/src/connection/connection_sr.c b/src/connection/connection_sr.c index 260a07670f9..d76848d12f8 100644 --- a/src/connection/connection_sr.c +++ b/src/connection/connection_sr.c @@ -151,6 +151,9 @@ CSS_THREAD_FN css_Request_handler = NULL; /* This will handle closed connection errors */ CSS_THREAD_FN css_Connection_error_handler = NULL; +static char css_Server_exec_path[PATH_MAX]; +static char **css_Server_argv; + #define CSS_CONN_IDX(conn_arg) ((conn_arg) - css_Conn_array) #define CSS_FREE_CONN_MSG "Free count = %d, head = %d" @@ -1073,6 +1076,32 @@ css_common_connect (CSS_CONN_ENTRY * conn, unsigned short *rid, return NULL; } +/* + * css_set_proc_register() - make a server proc register. + * return: + * server_name(in): + * server_name_lenth(in): + * proc_register(out): + */ +static void +css_set_proc_register (const char *server_name, int server_name_length, CSS_SERVER_PROC_REGISTER * proc_register) +{ + char *p, *last; + char **argv; + + memcpy (proc_register->server_name, server_name, server_name_length); + proc_register->server_name_length = server_name_length; + proc_register->pid = getpid (); + strncpy_bufsize (proc_register->exec_path, css_Server_exec_path); + + p = (char *) proc_register->args; + last = p + proc_register->CSS_SERVER_MAX_SZ_PROC_ARGS; + for (argv = css_Server_argv; *argv; argv++) + { + p += snprintf (p, MAX ((last - p), 0), "%s ", *argv); + } +} + /* * css_connect_to_master_server() - Connect to the master from the server. * return: connection entry if success, or NULL @@ -1093,6 +1122,9 @@ css_connect_to_master_server (int master_port_id, const char *server_name, int n std::string pname; int datagram_fd, socket_fd; #endif + const char *data; + int data_length; + CSS_SERVER_PROC_REGISTER proc_register = CSS_SERVER_PROC_REGISTER_INITIALIZER; css_Service_id = master_port_id; if (GETHOSTNAME (hname, CUB_MAXHOSTNAMELEN) != 0) @@ -1108,18 +1140,27 @@ css_connect_to_master_server (int master_port_id, const char *server_name, int n } /* select the connection protocol */ + + // TODO : When supporting the Windows environment, It will be modified to send the same data + // (proc_register) for the Windows protocol (SERVER_REQUEST_NEW) as well. + if (css_Server_use_new_connection_protocol) { // Windows connection_protocol = SERVER_REQUEST_NEW; + data = server_name; + data_length = name_length; } else { // Linux and Unix connection_protocol = SERVER_REQUEST; + css_set_proc_register (server_name, name_length, &proc_register); + data = (const char *) &proc_register; + data_length = sizeof (proc_register); } - if (css_common_connect (conn, &rid, hname, connection_protocol, server_name, name_length, master_port_id) == NULL) + if (css_common_connect (conn, &rid, hname, connection_protocol, data, data_length, master_port_id) == NULL) { goto fail_end; } @@ -3122,3 +3163,29 @@ css_free_user_access_status (void) return; } + +/* + * css_set_exec_path () - + * return: none + * + * exec_path(in): + */ +void +css_set_exec_path (char *exec_path) +{ + assert (exec_path != NULL); + strncpy (css_Server_exec_path, exec_path, sizeof (css_Server_exec_path) - 1); +} + +/* + * css_set_argv () - + * return: none + * + * argv(in): + */ +void +css_set_argv (char **argv) +{ + assert (argv != NULL); + css_Server_argv = argv; +} diff --git a/src/connection/connection_sr.h b/src/connection/connection_sr.h index f34f0960020..b639786fdf5 100644 --- a/src/connection/connection_sr.h +++ b/src/connection/connection_sr.h @@ -192,4 +192,7 @@ extern void css_set_user_access_status (const char *db_user, const char *host, c extern void css_get_user_access_status (int num_user, LAST_ACCESS_STATUS ** access_status_array); extern void css_free_user_access_status (void); +extern void css_set_exec_path (char *exec_path); +extern void css_set_argv (char **argv); + #endif /* _CONNECTION_SR_H_ */ diff --git a/src/executables/commdb.c b/src/executables/commdb.c index 95860490dd4..cfd1f99a81f 100644 --- a/src/executables/commdb.c +++ b/src/executables/commdb.c @@ -135,6 +135,8 @@ static bool commdb_Arg_deact_confirm_no_server = false; static char *commdb_Arg_host_name = NULL; static bool commdb_Arg_ha_start_util_process = false; static char *commdb_Arg_ha_util_process_args = NULL; +static bool commdb_Arg_shutdown_reviving_server = false; +static char *commdb_Arg_shutdown_reviving_server_name = NULL; /* * send_request_no_args() - send request without argument @@ -1063,6 +1065,18 @@ process_ha_start_util_process (CSS_CONN_ENTRY * conn, char *args) return error; } +/* + * process_shutdown_reviving_server() - shutdown reviving server + * return: none + * server_name(in) : server name + * conn(in) : connection info + */ +static void +process_shutdown_reviving_server (CSS_CONN_ENTRY * conn, char *server_name) +{ + send_request_one_arg (conn, SHUTDOWN_REVIVING_SERVER, server_name, (int) strlen (server_name) + 1); +} + /* * process_batch_command() - process user command in batch mode * return: none @@ -1179,6 +1193,11 @@ process_batch_command (CSS_CONN_ENTRY * conn) return process_ha_start_util_process (conn, (char *) commdb_Arg_ha_util_process_args); } + if (commdb_Arg_shutdown_reviving_server) + { + process_shutdown_reviving_server (conn, (char *) commdb_Arg_shutdown_reviving_server_name); + } + return NO_ERROR; } @@ -1221,6 +1240,7 @@ main (int argc, char **argv) {COMMDB_HOST_L, 1, 0, COMMDB_HOST_S}, {COMMDB_HA_ADMIN_INFO_L, 0, 0, COMMDB_HA_ADMIN_INFO_S}, {COMMDB_HA_START_UTIL_PROCESS_L, 1, 0, COMMDB_HA_START_UTIL_PROCESS_S}, + {COMMDB_SHUTDOWN_REVIVING_SERVER_L, 1, 0, COMMDB_SHUTDOWN_REVIVING_SERVER_S}, {0, 0, 0, 0} }; @@ -1369,6 +1389,14 @@ main (int argc, char **argv) commdb_Arg_ha_util_process_args = strdup (optarg); commdb_Arg_ha_start_util_process = true; break; + case COMMDB_SHUTDOWN_REVIVING_SERVER_S: + if (commdb_Arg_shutdown_reviving_server_name != NULL) + { + free_and_init (commdb_Arg_shutdown_reviving_server_name); + } + commdb_Arg_shutdown_reviving_server_name = strdup (optarg); + commdb_Arg_shutdown_reviving_server = true; + break; default: util_log_write_errid (MSGCAT_UTIL_GENERIC_INVALID_ARGUMENT); goto usage; @@ -1439,6 +1467,10 @@ main (int argc, char **argv) { free_and_init (commdb_Arg_ha_util_process_args); } + if (commdb_Arg_shutdown_reviving_server_name != NULL) + { + free_and_init (commdb_Arg_shutdown_reviving_server_name); + } return status; } diff --git a/src/executables/master.c b/src/executables/master.c index 6249bce0a55..030350db9a5 100644 --- a/src/executables/master.c +++ b/src/executables/master.c @@ -59,6 +59,7 @@ #include "error_manager.h" #include "connection_globals.h" #include "connection_cl.h" +#include "system_parameter.h" #if defined(WINDOWS) #include "wintcp.h" #else /* ! WINDOWS */ @@ -73,6 +74,7 @@ #include "message_catalog.h" #include "dbi.h" #include "util_func.h" +#include "master_server_monitor.hpp" static void css_master_error (const char *error_string); static int css_master_timeout (void); @@ -80,8 +82,7 @@ static int css_master_init (int cport, SOCKET * clientfd); static void css_reject_client_request (CSS_CONN_ENTRY * conn, unsigned short rid, int reason); static void css_reject_server_request (CSS_CONN_ENTRY * conn, int reason); static void css_accept_server_request (CSS_CONN_ENTRY * conn, int reason); -static void css_accept_new_request (CSS_CONN_ENTRY * conn, unsigned short rid, char *server_name, - int server_name_length); +static void css_accept_new_request (CSS_CONN_ENTRY * conn, unsigned short rid, char *buffer); static void css_accept_old_request (CSS_CONN_ENTRY * conn, unsigned short rid, SOCKET_QUEUE_ENTRY * entry, char *server_name, int server_name_length); static void css_register_new_server (CSS_CONN_ENTRY * conn, unsigned short rid); @@ -319,39 +320,53 @@ css_accept_server_request (CSS_CONN_ENTRY * conn, int reason) * return: none * conn(in) * rid(in) - * server_name(in) - * server_name_length(in) + * buffer(in) + * */ static void -css_accept_new_request (CSS_CONN_ENTRY * conn, unsigned short rid, char *server_name, int server_name_length) +css_accept_new_request (CSS_CONN_ENTRY * conn, unsigned short rid, char *buffer) { char *datagram; + char *server_name; int datagram_length; SOCKET server_fd = INVALID_SOCKET; int length; + int server_name_length; CSS_CONN_ENTRY *datagram_conn; SOCKET_QUEUE_ENTRY *entry; + CSS_SERVER_PROC_REGISTER *proc_register = (CSS_SERVER_PROC_REGISTER *) buffer; datagram = NULL; datagram_length = 0; + css_accept_server_request (conn, SERVER_REQUEST_ACCEPTED); + if (css_receive_data (conn, rid, &datagram, &datagram_length, -1) == NO_ERRORS) { + if (datagram != NULL && css_tcp_master_datagram (datagram, &server_fd)) { datagram_conn = css_make_conn (server_fd); #if defined(DEBUG) css_Active_server_count++; #endif - css_add_request_to_socket_queue (datagram_conn, false, server_name, server_fd, READ_WRITE, 0, + css_add_request_to_socket_queue (datagram_conn, false, proc_register->server_name, server_fd, READ_WRITE, 0, &css_Master_socket_anchor); - length = (int) strlen (server_name) + 1; + + // Note : server_name is usually packed(appended) information of server_name, version_string, env_var, pid, + // packed from css_pack_server_name(). Since there are some cases that returns server_name and server_name_length + // as NULL, we need to check if server_name is packed information or not. + length = (int) strlen (proc_register->server_name) + 1; + server_name_length = proc_register->server_name_length; + + assert (length <= DB_MAX_IDENTIFIER_LENGTH); + if (length < server_name_length) { - entry = css_return_entry_of_server (server_name, css_Master_socket_anchor); + entry = css_return_entry_of_server (proc_register->server_name, css_Master_socket_anchor); if (entry != NULL) { - server_name += length; + server_name = proc_register->server_name + length; entry->version_string = (char *) malloc (strlen (server_name) + 1); if (entry->version_string != NULL) { @@ -374,6 +389,19 @@ css_accept_new_request (CSS_CONN_ENTRY * conn, unsigned short rid, char *server_ } } } + + if (!entry->ha_mode) + { +#if !defined(WINDOWS) + if (auto_Restart_server) + { + /* *INDENT-OFF* */ + master_Server_monitor->produce_job (server_monitor::job_type::REGISTER_SERVER, proc_register->pid, + proc_register->exec_path, proc_register->args, proc_register->server_name); + /* *INDENT-ON* */ + } +#endif + } } } if (datagram) @@ -440,20 +468,26 @@ css_accept_old_request (CSS_CONN_ENTRY * conn, unsigned short rid, SOCKET_QUEUE_ static void css_register_new_server (CSS_CONN_ENTRY * conn, unsigned short rid) { - int name_length; - char *server_name = NULL; + int data_length; + char *data = NULL; SOCKET_QUEUE_ENTRY *entry; - /* read server name */ - if (css_receive_data (conn, rid, &server_name, &name_length, -1) == NO_ERRORS) + // Note: css_register_new_server() is used in two situations: + // 1. When a client requests to connect a cub_server to cub_master, which is already registered. + // 2. When a new cub_server requests to register itself to cub_master. + // For the first situation, css_register_new_server() receives the server name as data. + // For the second situation, css_register_new_server() receives CSS_SERVER_PROC_REGISTER as data. + // css_register_new_server determines which case it is by checking if the entry is NULL or not. + + if (css_receive_data (conn, rid, &data, &data_length, -1) == NO_ERRORS) { - entry = css_return_entry_of_server (server_name, css_Master_socket_anchor); + entry = css_return_entry_of_server (data, css_Master_socket_anchor); if (entry != NULL) { if (IS_INVALID_SOCKET (entry->fd)) { /* accept a server that was auto-started */ - css_accept_old_request (conn, rid, entry, server_name, name_length); + css_accept_old_request (conn, rid, entry, data, data_length); } else { @@ -468,18 +502,17 @@ css_register_new_server (CSS_CONN_ENTRY * conn, unsigned short rid) #if defined(DEBUG) css_Active_server_count++; #endif - css_add_request_to_socket_queue (conn, false, server_name, conn->fd, READ_WRITE, 0, - &css_Master_socket_anchor); + css_add_request_to_socket_queue (conn, false, data, conn->fd, READ_WRITE, 0, &css_Master_socket_anchor); #else /* ! WINDOWS */ /* accept a request from a new server */ - css_accept_new_request (conn, rid, server_name, name_length); + css_accept_new_request (conn, rid, data); #endif /* ! WINDOWS */ } } - if (server_name != NULL) + if (data != NULL) { - free_and_init (server_name); + free_and_init (data); } #if !defined(WINDOWS) @@ -980,6 +1013,15 @@ css_check_master_socket_input (int *count, fd_set * fd_var) css_Active_server_count--; } #endif + +#if !defined(WINDOWS) + if (auto_Restart_server) + { + /* *INDENT-OFF* */ + master_Server_monitor->produce_job (server_monitor::job_type::REVIVE_SERVER, -1, "", "", temp->name); + /* *INDENT-ON* */ + } +#endif css_remove_entry_by_conn (temp->conn_ptr, &css_Master_socket_anchor); } } @@ -1221,6 +1263,18 @@ main (int argc, char **argv) goto cleanup; } } + + auto_Restart_server = prm_get_bool_value (PRM_ID_AUTO_RESTART_SERVER); + + // Since master_Server_monitor is a module for restarting abnormally terminated cub_server, + // it is initialized only when the 'auto_restart_server' parameter is set to true. + + if (auto_Restart_server) + { + // *INDENT-OFF* + master_Server_monitor.reset (new server_monitor ()); + // *INDENT-ON* + } #endif conn = css_make_conn (css_Master_socket_fd[0]); diff --git a/src/executables/master_request.c b/src/executables/master_request.c index 20ae2620516..683e05952f9 100644 --- a/src/executables/master_request.c +++ b/src/executables/master_request.c @@ -56,6 +56,7 @@ #include "master_util.h" #include "master_request.h" #include "master_heartbeat.h" +#include "master_server_monitor.hpp" #if defined (SUPPRESS_STRLEN_WARNING) #define strlen(s1) ((int) strlen(s1)) @@ -530,6 +531,14 @@ css_process_kill_slave (CSS_CONN_ENTRY * conn, unsigned short request_id, char * snprintf (buffer, MASTER_TO_SRV_MSG_SIZE, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MASTER, MASTER_MSG_SERVER_STATUS), server_name, timeout); +#if !defined(WINDOWS) + if (auto_Restart_server) + { + /* *INDENT-OFF* */ + master_Server_monitor->produce_job (server_monitor::job_type::UNREGISTER_SERVER, -1, "", "", server_name); + /* *INDENT-ON* */ + } +#endif css_process_start_shutdown (temp, timeout * 60, buffer); } snprintf (buffer, MASTER_TO_SRV_MSG_SIZE, @@ -597,6 +606,54 @@ css_process_kill_immediate (CSS_CONN_ENTRY * conn, unsigned short request_id, ch } } +/* + * css_process_start_shutdown_by_name() + * return: none + * server_name(in/out) + */ +void +css_process_start_shutdown_by_name (char *server_name) +{ +#if !defined(WINDOWS) + SOCKET_QUEUE_ENTRY *temp; + char buffer[MASTER_TO_SRV_MSG_SIZE]; + + (void) pthread_mutex_lock (&css_Master_socket_anchor_lock); + + for (temp = css_Master_socket_anchor; temp; temp = temp->next) + { + if ((temp->name != NULL) && (strcmp (temp->name, server_name) == 0)) + { + /* Send a shutdown request to the specified cub_server with a timeout of 0. + * Buffer will be unused in the receiving function (css_process_shutdown_request). */ + css_process_start_shutdown (temp, 0, buffer); + } + } + (void) pthread_mutex_unlock (&css_Master_socket_anchor_lock); +#endif +} + + +/* + * css_process_shutdown_reviving_server() + * return: none + * conn(in) + * request_id(in) + * server_name(in/out) + */ +static void +css_process_shutdown_reviving_server (CSS_CONN_ENTRY * conn, unsigned short request_id, char *server_name) +{ +#if !defined(WINDOWS) + if (auto_Restart_server) + { + /* *INDENT-OFF* */ + master_Server_monitor->produce_job (server_monitor::job_type::SHUTDOWN_SERVER, -1, "", "", server_name); + /* *INDENT-ON* */ + } +#endif +} + /* * css_send_term_signal() - send a signal to the target process * return: none @@ -709,6 +766,14 @@ css_process_shutdown (char *time_buffer) memset (buffer, 0, sizeof (buffer)); snprintf (buffer, MASTER_TO_SRV_MSG_SIZE, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_MASTER, MASTER_MSG_GOING_DOWN), timeout); +#if !defined(WINDOWS) + if (auto_Restart_server) + { + /* INDENT-OFF */ + master_Server_monitor.reset (); + /* INDENT-ON */ + } +#endif for (temp = css_Master_socket_anchor; temp; temp = temp->next) { @@ -1921,6 +1986,12 @@ css_process_info_request (CSS_CONN_ENTRY * conn) css_process_kill_immediate (conn, request_id, buffer); } break; + case SHUTDOWN_REVIVING_SERVER: + if (buffer != NULL) + { + css_process_shutdown_reviving_server (conn, request_id, buffer); + } + break; case GET_ALL_COUNT: css_process_all_count_info (conn, request_id); break; diff --git a/src/executables/master_request.h b/src/executables/master_request.h index f55fc605c03..2141b8d6080 100644 --- a/src/executables/master_request.h +++ b/src/executables/master_request.h @@ -57,4 +57,5 @@ extern SOCKET_QUEUE_ENTRY *css_add_request_to_socket_queue (CSS_CONN_ENTRY * con SOCKET fd, int fd_type, int pid, SOCKET_QUEUE_ENTRY ** anchor_p); extern SOCKET_QUEUE_ENTRY *css_return_entry_by_conn (CSS_CONN_ENTRY * conn_p, SOCKET_QUEUE_ENTRY ** anchor_p); +extern void css_process_start_shutdown_by_name (char *server_name); #endif /* _MASTER_REQUEST_H_ */ diff --git a/src/executables/master_server_monitor.cpp b/src/executables/master_server_monitor.cpp new file mode 100644 index 00000000000..92da0ee0cf4 --- /dev/null +++ b/src/executables/master_server_monitor.cpp @@ -0,0 +1,477 @@ +/* + * + * Copyright 2016 CUBRID Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// +// master_server_monitor.cpp - Server Revive monitoring module +// + +#include +#include +#include +#include + +#include "master_server_monitor.hpp" + +std::unique_ptr master_Server_monitor = nullptr; +bool auto_Restart_server = false; + +server_monitor::server_monitor () +{ + + m_server_entry_map = std::unordered_map (); + + { + std::lock_guard lock (m_server_monitor_mutex); + m_thread_shutdown = false; + } + + start_monitoring_thread (); + _er_log_debug (ARG_FILE_LINE, "[Server Monitor] Monitoring started."); +} + +// In server_monitor destructor, it should guarentee that +// m_monitoring_thread is terminated before m_monitor_list is deleted. +server_monitor::~server_monitor () +{ + stop_monitoring_thread (); + _er_log_debug (ARG_FILE_LINE, "[Server Monitor] Monitoring finished."); +} + +void +server_monitor::start_monitoring_thread () +{ + m_monitoring_thread = std::make_unique (&server_monitor::server_monitor_thread_worker, this); +} + +void +server_monitor::stop_monitoring_thread () +{ + { + std::lock_guard lock (m_server_monitor_mutex); + m_thread_shutdown = true; + } + m_monitor_cv_consumer.notify_all(); + + m_monitoring_thread->join(); +} + +void +server_monitor::server_monitor_thread_worker () +{ + job job; + + while (true) + { + { + std::unique_lock lock (m_server_monitor_mutex); + + m_monitor_cv_consumer.wait (lock, [this] + { + return !m_job_queue.empty () || m_thread_shutdown; + }); + + if (m_thread_shutdown) + { + break; + } + else + { + assert (!m_job_queue.empty ()); + consume_job (job); + } + } + process_job (job); + } +} + +void +server_monitor::register_server_entry (int pid, const std::string &exec_path, const std::string &args, + const std::string &server_name + ) +{ + auto entry = m_server_entry_map.find (server_name); + + if (entry != m_server_entry_map.end ()) + { + entry->second.set_pid (pid); + entry->second.set_need_revive (false); + entry->second.set_last_revived_time (std::chrono::steady_clock::now ()); + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Server entry has been registered. (pid : %d)", + server_name.c_str(), pid); + } + else + { + m_server_entry_map.emplace (std::move (server_name), server_entry (pid, exec_path, args, + std::chrono::steady_clock::time_point ())); + + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Server entry has been registered newly. (pid : %d)", + server_name.c_str(), pid); + } +} + +void +server_monitor::remove_server_entry (const std::string &server_name) +{ + auto entry = m_server_entry_map.find (server_name); + assert (entry != m_server_entry_map.end ()); + + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Server entry has been unregistered. (pid : %d)", + server_name.c_str(), entry->second.get_pid()); + + m_server_entry_map.erase (entry); +} + +void +server_monitor::revive_server (const std::string &server_name) +{ + int error_code; + + // Unacceptable revive time difference is set to be 120 seconds + // as the timediff of server restart mechanism of heartbeat. + constexpr int SERVER_MONITOR_UNACCEPTABLE_REVIVE_TIMEDIFF_IN_SECS = 120; + std::chrono::steady_clock::time_point tv; + int out_pid; + + auto entry = m_server_entry_map.find (server_name); + + if (entry != m_server_entry_map.end ()) + { + entry->second.set_need_revive (true); + + tv = std::chrono::steady_clock::now (); + auto timediff = std::chrono::duration_cast (tv - + entry->second.get_last_revived_time()).count(); + bool revived_before = entry->second.get_last_revived_time () != std::chrono::steady_clock::time_point (); + + // If the server is abnormally terminated and revived within a short period of time, it is considered as a repeated failure. + // For HA server, heartbeat handle this case as demoting the server from master to slave and keep trying to revive the server. + // However, in this case, the server_monitor will not try to revive the server due to following reasons. + // 1. preventing repeated creation of core files. + // 2. The service cannot be recovered even if revived if the server abnormally terminates again within a short time. + + // TODO: Consider retry count for repeated failure case, and give up reviving the server after several retries. + // TODO: The timediff value continues to increase if REVIVE_SERVER handling is repeated. Thus, the if condition will always be + // true after the first evaluation. Therefore, evaluating the timediff only once when producing the REVIVE_SERVER job is needed. + // (Currently, it is impossible since last_revived_time is stored in server_entry, which is not synchronized structure between monitor and main thread.) + + if (!revived_before || timediff > SERVER_MONITOR_UNACCEPTABLE_REVIVE_TIMEDIFF_IN_SECS) + { + out_pid = try_revive_server (entry->second.get_exec_path(), entry->second.get_argv()); + if (out_pid == -1) + { + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Failed to fork server process. Server monitor try to revive server again.", + entry->first.c_str()); + produce_job_internal (job_type::REVIVE_SERVER, -1, "", "", entry->first); + } + else + { + entry->second.set_pid (out_pid); + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Server monitor is waiting for server to be registered. (pid : %d)", + entry->first.c_str(), entry->second.get_pid()); + produce_job_internal (job_type::CONFIRM_REVIVE_SERVER, -1, "", "", + entry->first); + } + return; + } + else + { + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Server process failures occurred again within a short period of time(%d sec). It will no longer be revived automatically. (pid : %d)", + entry->first.c_str(), SERVER_MONITOR_UNACCEPTABLE_REVIVE_TIMEDIFF_IN_SECS, entry->second.get_pid()); + m_server_entry_map.erase (entry); + return; + } + } +} + +void +server_monitor::check_server_revived (const std::string &server_name) +{ + int error_code; + auto entry = m_server_entry_map.find (server_name); + + if (entry != m_server_entry_map.end ()) + { + error_code = kill (entry->second.get_pid (), 0); + if (error_code) + { + if (errno == ESRCH) + { + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Revived server process can not be found. Server monitor will try to revive server again. (pid : %d)", + entry->first.c_str(), entry->second.get_pid()); + produce_job_internal (job_type::REVIVE_SERVER, -1, "", "", entry->first); + } + else + { + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Failed to revive server due to unknown error from kill() function. It will no longer be revived automatically. (pid : %d)", + entry->first.c_str(), entry->second.get_pid()); + kill (entry->second.get_pid (), SIGKILL); + m_server_entry_map.erase (entry); + } + } + else if (entry->second.get_need_revive ()) + { + // Server revive confirm interval is set to be 1 second to avoid busy waiting. + constexpr int SERVER_MONITOR_CONFIRM_REVIVE_INTERVAL_IN_SECS = 1; + + std::this_thread::sleep_for (std::chrono::seconds (SERVER_MONITOR_CONFIRM_REVIVE_INTERVAL_IN_SECS)); + + produce_job_internal (job_type::CONFIRM_REVIVE_SERVER, -1, "", "", + entry->first); + + } + else + { + _er_log_debug (ARG_FILE_LINE, "[Server Monitor] [%s] Server revive success. (pid : %d)", + entry->first.c_str(), + entry->second.get_pid()); + } + return; + } +} + +int +server_monitor::try_revive_server (const std::string &exec_path, char *const *argv) +{ + pid_t pid; + + pid = fork (); + if (pid < 0) + { + return -1; + } + else if (pid == 0) + { + execv (exec_path.c_str(), argv); + } + else + { + return pid; + } +} +void +server_monitor::shutdown_server (const std::string &server_name) +{ + int rv; + auto entry = m_server_entry_map.find (server_name); + + if (entry != m_server_entry_map.end ()) + { + + if (entry->second.get_need_revive ()) + { + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Server is shutdown. Reviving the server will not be tried.", + server_name.c_str()); + } + else + { + css_process_start_shutdown_by_name (const_cast (server_name.c_str())); + + _er_log_debug (ARG_FILE_LINE, + "[Server Monitor] [%s] Server is already revived. Server monitor will terminate the server. (pid : %d)", + server_name.c_str(), entry->second.get_pid()); + } + m_server_entry_map.erase (entry); + } +} + +void +server_monitor::produce_job_internal (job_type job_type, int pid, const std::string &exec_path, + const std::string &args, const std::string &server_name) +{ + std::lock_guard lock (m_server_monitor_mutex); + m_job_queue.emplace (job_type, pid, exec_path, args, server_name); +} + +void +server_monitor::produce_job (job_type job_type, int pid, const std::string &exec_path, + const std::string &args, const std::string &server_name) +{ + produce_job_internal (job_type, pid, exec_path, args, server_name); + m_monitor_cv_consumer.notify_all(); +} + +void +server_monitor::consume_job (job &consume_job) +{ + consume_job = std::move (m_job_queue.front ()); + m_job_queue.pop (); +} + +void +server_monitor::process_job (job &consume_job) +{ + switch (consume_job.get_job_type ()) + { + case job_type::REGISTER_SERVER: + register_server_entry (consume_job.get_pid(), consume_job.get_exec_path(), consume_job.get_args(), + consume_job.get_server_name()); + + break; + case job_type::UNREGISTER_SERVER: + remove_server_entry (consume_job.get_server_name()); + break; + case job_type::REVIVE_SERVER: + revive_server (consume_job.get_server_name()); + break; + case job_type::CONFIRM_REVIVE_SERVER: + check_server_revived (consume_job.get_server_name()); + break; + case job_type::SHUTDOWN_SERVER: + shutdown_server (consume_job.get_server_name()); + break; + case job_type::JOB_MAX: + default: + assert (false); + break; + } +} + +server_monitor::job:: +job (job_type job_type, int pid, const std::string &exec_path, const std::string &args, + const std::string &server_name) + : m_job_type {job_type} + , m_pid {pid} + , m_exec_path {exec_path} + , m_args {args} + , m_server_name {server_name} +{ +} + +server_monitor::job_type +server_monitor::job::get_job_type () const +{ + return m_job_type; +} + +int +server_monitor::job::get_pid () const +{ + return m_pid; +} + +std::string +server_monitor::job::get_exec_path () const +{ + return m_exec_path; +} + +std::string +server_monitor::job::get_args () const +{ + return m_args; +} + +std::string +server_monitor::job::get_server_name () const +{ + return m_server_name; +} + +server_monitor::server_entry:: +server_entry (int pid, const std::string &exec_path, const std::string &args, + std::chrono::steady_clock::time_point revive_time) + : m_pid {pid} + , m_exec_path {exec_path} + , m_need_revive {false} + , m_last_revived_time {revive_time} +{ + if (args.size() > 0) + { + proc_make_arg (args); + } +} + +int +server_monitor::server_entry::get_pid () const +{ + return m_pid; +} + +std::string +server_monitor::server_entry::get_exec_path () const +{ + return m_exec_path; +} + +char *const * +server_monitor::server_entry::get_argv () const +{ + return m_argv.get (); +} + +bool +server_monitor::server_entry::get_need_revive () const +{ + return m_need_revive; +} + +std::chrono::steady_clock::time_point +server_monitor::server_entry::get_last_revived_time () const +{ + return m_last_revived_time; +} + +void +server_monitor::server_entry::set_pid (int pid) +{ + m_pid = pid; +} + +void +server_monitor::server_entry::set_exec_path (const std::string &exec_path) +{ + m_exec_path = exec_path; +} + +void +server_monitor::server_entry::set_need_revive (bool need_revive) +{ + m_need_revive = need_revive; +} + +void +server_monitor::server_entry::set_last_revived_time (std::chrono::steady_clock::time_point revive_time) +{ + m_last_revived_time = revive_time; +} + +void +server_monitor::server_entry::proc_make_arg (const std::string &args) +{ + //argv is type of std::unique_ptr + m_argv = std::make_unique (args.size () + 1); + std::istringstream iss (args); + std::string arg; + int i = 0; + while (std::getline (iss, arg, ' ')) + { + m_argv[i] = new char[arg.size () + 1]; + std::copy (arg.begin (), arg.end (), m_argv[i]); + i++; + } + m_argv[args.size()] = nullptr; +} diff --git a/src/executables/master_server_monitor.hpp b/src/executables/master_server_monitor.hpp new file mode 100644 index 00000000000..f8ca8dd3c2e --- /dev/null +++ b/src/executables/master_server_monitor.hpp @@ -0,0 +1,175 @@ +/* + * + * Copyright 2016 CUBRID Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * master_server_monitor.hpp - + */ + +#ifndef _MASTER_SERVER_MONITOR_HPP_ +#define _MASTER_SERVER_MONITOR_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "connection_globals.h" +#include "master_request.h" + +class server_monitor +{ + + public: + + enum class job_type + { + REGISTER_SERVER = 0, + UNREGISTER_SERVER = 1, + REVIVE_SERVER = 2, + CONFIRM_REVIVE_SERVER = 3, + SHUTDOWN_SERVER = 4, + JOB_MAX + }; + + private: + + class job + { + + public: + + job (job_type job_type, int pid, const std::string &exec_path, const std::string &args, + const std::string &server_name); + job () : m_job_type (job_type::JOB_MAX) {}; + ~job () {}; + + job (const job &) = default; + job (job &&) = default; + + job &operator= (const job &) = delete; + job &operator= (job &&) = default; + + job_type get_job_type () const; + int get_pid () const; + std::string get_exec_path () const; + std::string get_args () const; + std::string get_server_name () const; + + private: + + job_type m_job_type; // job type + int m_pid; // process ID of server process + std::string m_exec_path; // executable path of server process + std::string m_args; // arguments of server process + std::string m_server_name; // server name + }; + + public: + + server_monitor (); + ~server_monitor (); + + server_monitor (const server_monitor &) = delete; + server_monitor (server_monitor &&) = delete; + + server_monitor &operator = (const server_monitor &) = delete; + server_monitor &operator = (server_monitor &&) = delete; + + void register_server_entry (int pid, const std::string &exec_path, const std::string &args, + const std::string &server_name); + void remove_server_entry (const std::string &server_name); + void revive_server (const std::string &server_name); + int try_revive_server (const std::string &exec_path, char *const *argv); + void check_server_revived (const std::string &server_name); + void shutdown_server (const std::string &server_name); + + void produce_job_internal (job_type job_type, int pid, const std::string &exec_path, const std::string &args, + const std::string &server_name); + void produce_job (job_type job_type, int pid, const std::string &exec_path, const std::string &args, + const std::string &server_name); + + void consume_job (job &consune_job); + void process_job (job &consume_job); + + private: + + class server_entry + { + public: + server_entry (int pid, const std::string &exec_path, const std::string &args, + std::chrono::steady_clock::time_point revive_time); + ~server_entry () {}; + + server_entry (const server_entry &) = delete; + server_entry (server_entry &&) = default; + + server_entry &operator= (const server_entry &) = delete; + server_entry &operator= (server_entry &&other) + { + if (this != &other) + { + m_pid = other.m_pid; + m_need_revive = other.m_need_revive; + m_last_revived_time = other.m_last_revived_time; + m_exec_path = other.m_exec_path; + m_argv = std::move (other.m_argv); + } + return *this; + } + + int get_pid () const; + std::string get_exec_path () const; + char *const *get_argv () const; + bool get_need_revive () const; + std::chrono::steady_clock::time_point get_last_revived_time () const; + + void set_pid (int pid); + void set_exec_path (const std::string &exec_path); + void set_need_revive (bool need_revive); + void set_last_revived_time (std::chrono::steady_clock::time_point revive_time); + + void proc_make_arg (const std::string &args); + + private: + int m_pid; // process ID of server process + std::string m_exec_path; // executable path of server process + std::unique_ptr m_argv; // arguments of server process + volatile bool m_need_revive; // need to be revived by monitoring thread + std::chrono::steady_clock::time_point m_last_revived_time; // last revived time + }; + + std::unordered_map m_server_entry_map; // map of server entries + std::unique_ptr m_monitoring_thread; // monitoring thread + std::queue m_job_queue; // job queue for monitoring thread + bool m_thread_shutdown; // flag to shutdown monitoring thread + std::mutex m_server_monitor_mutex; // lock that syncs m_job_queue and m_thread_shutdown + std::condition_variable m_monitor_cv_consumer; // condition variable for m_job_queue empty check + + void start_monitoring_thread (); + void stop_monitoring_thread (); + void server_monitor_thread_worker (); +}; + +extern std::unique_ptr master_Server_monitor; +extern bool auto_Restart_server; + +#endif diff --git a/src/executables/server.c b/src/executables/server.c index 431e24a4c1b..c021d456ac6 100644 --- a/src/executables/server.c +++ b/src/executables/server.c @@ -50,6 +50,7 @@ #include "system_parameter.h" #include "perf_monitor.h" #include "util_func.h" +#include "connection_sr.h" #if defined(WINDOWS) #include "wintcp.h" #else /* !defined (WINDOWS) */ @@ -206,8 +207,6 @@ CreateMiniDump (struct _EXCEPTION_POINTERS *pException, char *db_name) static void crash_handler (int signo, siginfo_t * siginfo, void *dummyp) { - int pid; - if (signo != SIGABRT && siginfo != NULL && siginfo->si_code <= 0) { register_fatal_signal_handler (signo); @@ -218,47 +217,6 @@ crash_handler (int signo, siginfo_t * siginfo, void *dummyp) { return; } - - if (!BO_IS_SERVER_RESTARTED () || !prm_get_bool_value (PRM_ID_AUTO_RESTART_SERVER)) - { - return; - } - - pid = fork (); - if (pid == 0) - { - char err_log[PATH_MAX]; - int ppid; - int fd, fd_max; - - fd_max = css_get_max_socket_fds (); - - for (fd = 3; fd < fd_max; fd++) - { - close (fd); - } - - ppid = getppid (); - while (1) - { - if (kill (ppid, 0) < 0) - { - break; - } - sleep (1); - } - - unmask_signal (signo); - - if (prm_get_string_value (PRM_ID_ER_LOG_FILE) != NULL) - { - snprintf (err_log, PATH_MAX, "%s.%d", prm_get_string_value (PRM_ID_ER_LOG_FILE), ppid); - rename (prm_get_string_value (PRM_ID_ER_LOG_FILE), err_log); - } - - execl (executable_path, executable_path, database_name, NULL); - exit (0); - } } #if !defined (NDEBUG) @@ -354,6 +312,9 @@ main (int argc, char **argv) hb_set_exec_path (executable_path); hb_set_argv (argv); + css_set_exec_path (executable_path); + css_set_argv (argv); + /* create a new session */ setsid (); #endif diff --git a/src/executables/util_service.c b/src/executables/util_service.c index fd09356dfb4..270286b9e48 100644 --- a/src/executables/util_service.c +++ b/src/executables/util_service.c @@ -298,6 +298,7 @@ static bool is_terminated_process (const int pid); static char *make_exec_abspath (char *buf, int buf_len, char *cmd); static const char *command_string (int command_type); static bool is_server_running (const char *type, const char *server_name, int pid); +static int shutdown_reviving_server (const char *server_name); static int is_broker_running (void); static int is_gateway_running (void); static UTIL_MANAGER_SERVER_STATUS_E is_manager_running (unsigned int sleep_time); @@ -1605,6 +1606,22 @@ is_server_running (const char *type, const char *server_name, int pid) } } +/* + * shutdown_reviving_server - + * + * return: + * + * type(in): + * server_name(in): + * pid(in): + */ +static int +shutdown_reviving_server (const char *server_name) +{ + const char *args[] = { UTIL_COMMDB_NAME, COMMDB_SHUTDOWN_REVIVING_SERVER, server_name, NULL }; + return proc_execute (UTIL_COMMDB_NAME, args, true, false, false, NULL); +} + /* * process_server - * @@ -1845,6 +1862,7 @@ process_server (int command_type, int argc, char **argv, bool show_usage, bool c status = ER_GENERIC_ERROR; print_message (stdout, MSGCAT_UTIL_GENERIC_NOT_RUNNING_2S, PRINT_SERVER_NAME, token); util_log_write_errid (MSGCAT_UTIL_GENERIC_NOT_RUNNING_2S, PRINT_SERVER_NAME, token); + shutdown_reviving_server (token); } } break; diff --git a/src/executables/utility.h b/src/executables/utility.h index e0a9931e85f..c1630f4c299 100644 --- a/src/executables/utility.h +++ b/src/executables/utility.h @@ -959,6 +959,7 @@ typedef struct _ha_config #define COMMDB_HA_ADMIN_INFO "--admin-info" #define COMMDB_VERBOSE_OUTPUT "--verbose" #define COMMDB_HA_START_UTIL_PROCESS "-t" +#define COMMDB_SHUTDOWN_REVIVING_SERVER "--shutdown-reviving-server" #define ACLDB_RELOAD "-r" @@ -1531,6 +1532,8 @@ typedef struct _ha_config #define COMMDB_HA_ADMIN_INFO_L "admin-info" #define COMMDB_HA_START_UTIL_PROCESS_S 't' #define COMMDB_HA_START_UTIL_PROCESS_L "start-ha-util-process" +#define COMMDB_SHUTDOWN_REVIVING_SERVER_S 12116 +#define COMMDB_SHUTDOWN_REVIVING_SERVER_L "shutdown-reviving-server" /* paramdump option list */ #define PARAMDUMP_OUTPUT_FILE_S 'o' diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 4cccf7b8414..3d8f74f76f6 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -96,6 +96,7 @@ set(CUB_MASTER_SOURCES ) if(UNIX) list(APPEND CUB_MASTER_SOURCES ${EXECUTABLES_DIR}/master_heartbeat.c) + list(APPEND CUB_MASTER_SOURCES ${EXECUTABLES_DIR}/master_server_monitor.cpp) SET_SOURCE_FILES_PROPERTIES( ${CUB_MASTER_SOURCES} PROPERTIES LANGUAGE CXX From a16f31d24fb8a739d4a45d16b3b81b8edaddc0c5 Mon Sep 17 00:00:00 2001 From: Kisoo Han Date: Tue, 10 Dec 2024 09:20:26 +0900 Subject: [PATCH 3/6] [CBRD-25706] change URLs for external libraries & git workflow (#5675) change URL for external libraries and git workflow --- .github/workflows/check.yml | 6 +++--- 3rdparty/CMakeLists.txt | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 21f9f727606..7239ea92758 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -99,7 +99,7 @@ jobs: sudo apt-get -yq install astyle # indent 2.2.11, the lastest version (2.2.12) make a different result - wget https://ftp.gnu.org/gnu/indent/indent-2.2.11.tar.gz + wget https://github.com/CUBRID/3rdparty/raw/develop/indent/indent-2.2.11.tar.gz tar xf indent-2.2.11.tar.gz cd indent-2.2.11 ./configure @@ -107,7 +107,7 @@ jobs: cd .. # download google-java-format-1.7-all-deps.jar - wget https://github.com/google/google-java-format/releases/download/google-java-format-1.7/google-java-format-1.7-all-deps.jar + wget https://github.com/CUBRID/3rdparty/raw/develop/google-java-format/google-java-format-1.7-all-deps.jar working-directory: ${{ env.working-directory }} - uses: actions/setup-java@v3 with: @@ -140,7 +140,7 @@ jobs: # Download cppcheck-2.4.1 source wd=`pwd` cd .. - wget https://github.com/danmar/cppcheck/archive/2.4.1.tar.gz + wget https://github.com/CUBRID/3rdparty/raw/develop/cppcheck/2.4.1.tar.gz tar xf 2.4.1.tar.gz # build cppcheck-2.4.1 diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 5c55fc3928d..52e81a13b03 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -15,15 +15,15 @@ # limitations under the License. # -set(WITH_FLEX_URL "https://github.com/westes/flex/files/981163/flex-2.6.4.tar.gz") # flex -set(WITH_BISON_URL "https://ftp.gnu.org/gnu/bison/bison-3.4.1.tar.gz") # bison -set(WITH_LIBEXPAT_URL "https://github.com/libexpat/libexpat/releases/download/R_2_2_5/expat-2.2.5.tar.bz2") # expat library sources URL -set(WITH_LIBJANSSON_URL "https://github.com/akheron/jansson/releases/download/v2.10/jansson-2.10.tar.gz") # jansson library sources URL +set(WITH_FLEX_URL "https://github.com/CUBRID/3rdparty/raw/develop/flex/flex-2.6.4.tar.gz") # flex +set(WITH_BISON_URL "https://github.com/CUBRID/3rdparty/raw/develop/bison/bison-3.4.1.tar.gz") # bison +set(WITH_LIBEXPAT_URL "https://github.com/CUBRID/3rdparty/raw/develop/expat/expat-2.2.5.tar.bz2") # expat library sources URL +set(WITH_LIBJANSSON_URL "https://github.com/CUBRID/3rdparty/raw/develop/jansson/jansson-2.10.tar.gz") # jansson library sources URL set(WITH_LIBEDIT_URL "https://github.com/CUBRID/libedit/archive/refs/tags/csql_v1.1.tar.gz") # editline library sources URL -set(WITH_RAPIDJSON_URL "https://github.com/Tencent/rapidjson/archive/v1.1.0.tar.gz") # rapidjson library sources URL -set(WITH_LIBOPENSSL_URL "https://www.openssl.org/source/old/1.1.1/openssl-1.1.1f.tar.gz") # openssl library sources URL -set(WITH_LIBUNIXODBC_URL "https://ftp.osuosl.org/pub/blfs/conglomeration/unixODBC/unixODBC-2.3.9.tar.gz") # unixODBC library sources URL -set(WITH_LIBTBB_URL "https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2021.11.0.tar.gz") # intel oneTBB library sources URL +set(WITH_RAPIDJSON_URL "https://github.com/CUBRID/3rdparty/raw/develop/rapidjson/v1.1.0.tar.gz") # rapidjson library sources URL +set(WITH_LIBOPENSSL_URL "https://github.com/CUBRID/3rdparty/raw/develop/openssl/openssl-1.1.1f.tar.gz") # openssl library sources URL +set(WITH_LIBUNIXODBC_URL "https://github.com/CUBRID/3rdparty/raw/develop/unixODBC/unixODBC-2.3.9.tar.gz") # unixODBC library sources URL +set(WITH_LIBTBB_URL "https://github.com/CUBRID/3rdparty/raw/develop/tbb/v2021.11.0.tar.gz") # intel oneTBB library sources URL # URL_HASH is a hash value used to verify the integrity of a file downloaded from a specified URL. # It is optional but highly recommended to set this value. If not set, the file will be downloaded From 77260b2766ea41c796366dd6ad44ae1ceef8fe36 Mon Sep 17 00:00:00 2001 From: Byungwook Kim Date: Tue, 10 Dec 2024 11:17:46 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[CBRD-25713]=20CHAR=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=B5=9C=EB=8C=80=20=EA=B8=B8=EC=9D=B4=202048=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=9C=ED=95=9C=20(#5658)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit http://jira.cubrid.org/browse/CBRD-25713 --- src/compat/dbtype_def.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compat/dbtype_def.h b/src/compat/dbtype_def.h index 2dc2a34583b..a1e71ccd864 100644 --- a/src/compat/dbtype_def.h +++ b/src/compat/dbtype_def.h @@ -537,10 +537,11 @@ extern "C" /* The lower limit for a number that can be represented by a numeric type */ #define DB_NUMERIC_UNDERFLOW_LIMIT 1e-38 -/* The maximum precision of CHAR(n) domain that can be specified for an INTL_UTF8_MAX_CHAR_SIZE. - * We may need to define this functionally as the maximum precision will depend on the size multiplier of the codeset. +/* + * Change the maximum length of CHAR type to 2048 for memory allocation and performance. + * Refer to CBRD-25713 */ -#define DB_MAX_CHAR_PRECISION (DB_MAX_STRING_LENGTH/4) +#define DB_MAX_CHAR_PRECISION 2048 /* The maximum precision that can be specified for a CHARACTER VARYING domain.*/ #define DB_MAX_VARCHAR_PRECISION DB_MAX_STRING_LENGTH From e560ed5639295c0dce104c7266600da55b9d39bf Mon Sep 17 00:00:00 2001 From: "Na, Hyunik" <67700070+hyunikn@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:26:42 +0900 Subject: [PATCH 5/6] [CBRD-25713] limit max length of CHAR type to 2048 (#5692) limit max length of CHAR type to 2048 --- .../src/main/java/com/cubrid/plcsql/compiler/type/TypeChar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_engine/pl_server/src/main/java/com/cubrid/plcsql/compiler/type/TypeChar.java b/pl_engine/pl_server/src/main/java/com/cubrid/plcsql/compiler/type/TypeChar.java index e5101817ef3..fb5ca0c0f08 100644 --- a/pl_engine/pl_server/src/main/java/com/cubrid/plcsql/compiler/type/TypeChar.java +++ b/pl_engine/pl_server/src/main/java/com/cubrid/plcsql/compiler/type/TypeChar.java @@ -34,7 +34,7 @@ public class TypeChar extends Type { - public static final int MAX_LEN = 268435455; + public static final int MAX_LEN = 2048; public final int length; From 6ea4644635cfeaffaab4263a4f679f15522da47b Mon Sep 17 00:00:00 2001 From: Byungwook Kim Date: Wed, 11 Dec 2024 17:56:51 +0900 Subject: [PATCH 6/6] [CBRD-25713] revert to MAX CHAR Length (#5702) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit http://jira.cubrid.org/browse/CBRD-25713 테스트 케이스에 반영하기 위해 임시로 예전 길이로 변경합니다. --- src/compat/dbtype_def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compat/dbtype_def.h b/src/compat/dbtype_def.h index a1e71ccd864..9334f59e4c6 100644 --- a/src/compat/dbtype_def.h +++ b/src/compat/dbtype_def.h @@ -541,7 +541,7 @@ extern "C" * Change the maximum length of CHAR type to 2048 for memory allocation and performance. * Refer to CBRD-25713 */ -#define DB_MAX_CHAR_PRECISION 2048 +#define DB_MAX_CHAR_PRECISION (DB_MAX_STRING_LENGTH/4) /* The maximum precision that can be specified for a CHARACTER VARYING domain.*/ #define DB_MAX_VARCHAR_PRECISION DB_MAX_STRING_LENGTH