diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 2baa4184..3f64e808 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -152,31 +152,55 @@ static const char* NOKEYUPDATE_KEYSHARE_ERRMSG = ""; * transformation, while utility-type statements are simply hung off * a dummy CMD_UTILITY Query node. */ + + /* + * @Description:Analyze the raw syntax tree, do semantic analysis and output query tree. + * @Param[IN] parseTree: a raw parse tree(abstract syntax tree). + * @Param[IN] sourceText: The source text of the query statement. + * @Param[IN] numParams: number of params. + * @Param[IN] paramTypes: stores the type OID of each parameter. + * @Return:an analyzed query tree(of Query class). + */ Query* parse_analyze( Node* parseTree, const char* sourceText, Oid* paramTypes, int numParams, bool isFirstNode, bool isCreateView) { + // Initialization of ParseState object. ParseState* pstate = make_parsestate(NULL); + // Declaration of the query tree. Query* query = NULL; - /* required as of 8.4 */ + // sourceText can't be NULL. AssertEreport(sourceText != NULL, MOD_OPT, "para cannot be NULL"); pstate->p_sourcetext = sourceText; if (numParams > 0) { + /* + * parse_fixed_parameters() was defined in parse_param.cpp. + * + * This function converts the reference contained in the query into a + * fixed parameter structure, that is, pass in the reference parameters + * of the query statement and constructs a FixedParamState structure + * for it. + */ parse_fixed_parameters(pstate, paramTypes, numParams); } PUSH_SKIP_UNIQUE_SQL_HOOK(); + + // Converts the raw parse tree into a query tree. query = transformTopLevelStmt(pstate, parseTree, isFirstNode, isCreateView); - POP_SKIP_UNIQUE_SQL_HOOK(); - + + POP_SKIP_UNIQUE_SQL_HOOK(); + /* it's unsafe to deal with plugins hooks as dynamic lib may be released */ if (post_parse_analyze_hook && !(g_instance.status > NoShutdown)) { (*post_parse_analyze_hook)(pstate, query); } pfree_ext(pstate->p_ref_hook_state); + + // release the ParseState object pstate. free_parsestate(pstate); /* For plpy CTAS query. CTAS is a recursive call. CREATE query is the first rewrited. @@ -185,7 +209,8 @@ Query* parse_analyze( */ query->fixed_paramTypes = paramTypes; query->fixed_numParams = numParams; - + + // return the analyzed raw parse tree, that is, the query tree. return query; } @@ -228,6 +253,7 @@ Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** pa /* * parse_sub_analyze * Entry point for recursively analyzing a sub-statement. + * 递归分析子查询的入口函数 */ Query* parse_sub_analyze(Node* parseTree, ParseState* parentParseState, CommonTableExpr* parentCTE, bool locked_from_parent, bool resolve_unknowns) @@ -4732,4 +4758,4 @@ static bool checkAllowedTableCombination(ParseState* pstate) Assert(has_ustore || has_else); return !(has_ustore && has_else); -} +} \ No newline at end of file diff --git a/src/common/backend/parser/keywords.cpp b/src/common/backend/parser/keywords.cpp index 785635e2..c0184514 100644 --- a/src/common/backend/parser/keywords.cpp +++ b/src/common/backend/parser/keywords.cpp @@ -21,9 +21,11 @@ #define PG_KEYWORD(a, b, c) {a, b, c}, +// A table that stores keywords information. +// This table will be used in keyword-matching process. const ScanKeyword ScanKeywords[] = { #include "parser/kwlist.h" }; +// The number of table items, which is also the total number of keywords const int NumScanKeywords = lengthof(ScanKeywords); - diff --git a/src/common/backend/parser/kwlookup.cpp b/src/common/backend/parser/kwlookup.cpp index ba028bae..899c96a1 100644 --- a/src/common/backend/parser/kwlookup.cpp +++ b/src/common/backend/parser/kwlookup.cpp @@ -28,7 +28,7 @@ * * Returns a pointer to the ScanKeyword table entry, or NULL if no match. * - * The match is done case-insensitively. Note that we deliberately use a + * The match is done case-insensitively. Note that we deliberately use a * dumbed-down case conversion that will only translate 'A'-'Z' into 'a'-'z', * even if we are in a locale where tolower() would produce more or different * translations. This is to conform to the SQL99 spec, which says that @@ -36,13 +36,20 @@ * receive a different case-normalization mapping. */ const ScanKeyword* ScanKeywordLookup(const char* text, const ScanKeyword* keywords, int num_keywords) -{ +{ /* + * @param[IN] text:The identifier to be matched. + * @param[IN] keywords:A pointer to the keyword table. + * @param[IN] num_keywords: The number of keywords table items (keywords). + * @param[OUT]:A pointer to a keywords table item. Null is returned if the match fails. + */ + int len, i; - char word[NAMEDATALEN] = {0}; - const ScanKeyword* low = NULL; - const ScanKeyword* high = NULL; + char word[NAMEDATALEN] = {0}; + const ScanKeyword* low = NULL; // Pointer used in the binary search. + const ScanKeyword* high = NULL; // Pointer used in the binary search. - if (text == NULL) { + // The input is NULL, no match is made, and NULL is returned. + if (text == NULL) { return NULL; } @@ -64,7 +71,7 @@ const ScanKeyword* ScanKeywordLookup(const char* text, const ScanKeyword* keywor } word[i] = ch; } - word[len] = '\0'; + word[len] = '\0'; // The converted text should ends with '\0'. /* * Now do a binary search using plain strcmp() comparison. @@ -85,6 +92,6 @@ const ScanKeyword* ScanKeywordLookup(const char* text, const ScanKeyword* keywor high = middle - 1; } } - + // The binary search fails and returns a null value. return NULL; -} +} \ No newline at end of file diff --git a/src/common/backend/parser/parse_oper.cpp b/src/common/backend/parser/parse_oper.cpp index cf35bc26..98c16b33 100644 --- a/src/common/backend/parser/parse_oper.cpp +++ b/src/common/backend/parser/parse_oper.cpp @@ -2,6 +2,7 @@ * * parse_oper.cpp * handle operator things for parser + * 处理表达式中的操作符 * * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -45,21 +46,24 @@ */ /* If your search_path is longer than this, sucks to be you ... */ +// search_path的最大长度 #define MAX_CACHED_PATH_LEN 16 typedef struct OprCacheKey { - char oprname[NAMEDATALEN]; + char oprname[NAMEDATALEN]; // operator名称 Oid left_arg; /* Left input OID, or 0 if prefix op */ + // 左侧操作符的OID,如果这是一个前缀操作符则此项为0. Oid right_arg; /* Right input OID, or 0 if postfix op */ - Oid search_path[MAX_CACHED_PATH_LEN]; + // 右侧操作符的OID,如果这是一个后缀操作符则此项为0. + Oid search_path[MAX_CACHED_PATH_LEN]; // 搜索路径 bool use_a_style_coercion; } OprCacheKey; typedef struct OprCacheEntry { /* the hash lookup key MUST BE FIRST */ - OprCacheKey key; - - Oid opr_oid; /* OID of the resolved operator */ + OprCacheKey key; // 哈希查找值必须是第一项 + /* OID of the resolved operator */ + Oid opr_oid; // 解决后的OID } OprCacheEntry; static Oid binary_oper_exact(List* opname, Oid arg1, Oid arg2, bool use_a_style_coercion); @@ -90,12 +94,18 @@ static void make_oper_cache_entry(OprCacheKey* key, Oid opr_oid); Oid LookupOperName(ParseState* pstate, List* opername, Oid oprleft, Oid oprright, bool noError, int location) { Oid result; - + // Search the operator Oid according to the value of OprCacheKey.left_arg.oprname, + // OprCacheKey.left_arg and OprCacheKey.right_arg. + // Returns result if the lookup is successful. result = OpernameGetOprid(opername, oprleft, oprright); if (OidIsValid(result)) return result; /* we don't use op_error here because only an exact match is wanted */ + + // The lookup fails, and passed-in param noError = false, + // Indicates that the caller allows a lookup failure to be treated as an error. + // This if-branch is used to report the error messages. if (!noError) { char oprkind; @@ -166,9 +176,9 @@ void get_sort_group_operators( { TypeCacheEntry* typentry = NULL; int cache_flags; - Oid lt_opr; - Oid eq_opr; - Oid gt_opr; + Oid lt_opr; // Oid of '<' + Oid eq_opr; // Oid of '=' + Oid gt_opr; // Oid of '>' bool hashable = false; /* @@ -183,9 +193,9 @@ void get_sort_group_operators( cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR; typentry = lookup_type_cache(argtype, cache_flags); - lt_opr = typentry->lt_opr; - eq_opr = typentry->eq_opr; - gt_opr = typentry->gt_opr; + lt_opr = typentry->lt_opr; + eq_opr = typentry->eq_opr; + gt_opr = typentry->gt_opr; hashable = OidIsValid(typentry->hash_proc); /* Report errors if needed */ @@ -681,7 +691,8 @@ static void op_error( } /* - * Operator expression construction. + * make_op + * Operator expression construction. * * Transform operator expression ensuring type compatibility. * This is where some type conversion happens. @@ -691,19 +702,25 @@ static void op_error( */ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int location, bool inNumeric) { - Oid ltypeId, rtypeId; - Operator tup; - Form_pg_operator opform; - Oid actual_arg_types[2]; - Oid declared_arg_types[2]; - int nargs; - List* args = NIL; - Oid rettype; - OpExpr* result = NULL; + // Enter the operator name and the structure tree of the left and right operators, + // and then construct the expression structure for them. + + Oid ltypeId, rtypeId; // Oid of left operator and right operator + Operator tup; // The Operator structure to get before constructing OpExpr + Form_pg_operator opform; // Related to type conversion between Pg_Operator and Operator + Oid actual_arg_types[2]; // Oid of the actual left, right operator determined by ltree, rtree + Oid declared_arg_types[2]; // Oid declared in Pg + int nargs; // the number of left and right operators(0/1/2). + List* args = NIL; // tree structure representing the left and right operators + Oid rettype; // Oid of the operator type returned + OpExpr* result = NULL; // The expression structure returned /* Select the operator */ + + // Get the OID of the left and right operators, and then obtain + // the Operator structure of the left and right operators accordingly if (rtree == NULL) { - /* right operator */ + /* right operator */ ltypeId = exprType(ltree); rtypeId = InvalidOid; tup = right_oper(pstate, opname, ltypeId, false, location); @@ -724,7 +741,7 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo } tup = oper(pstate, opname, ltypeId, rtypeId, false, location, inNumeric); } - + // Related to type conversion between Pg_Operator and Operator opform = check_operator_is_shell(opname, pstate, location, tup); /* Do typecasting and build the expression tree */ @@ -732,13 +749,13 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo /* right operator */ args = list_make1(ltree); actual_arg_types[0] = ltypeId; - declared_arg_types[0] = opform->oprleft; - nargs = 1; + declared_arg_types[0] = opform->oprleft; + nargs = 1; } else if (ltree == NULL) { /* left operator */ - args = list_make1(rtree); - actual_arg_types[0] = rtypeId; - declared_arg_types[0] = opform->oprright; + args = list_make1(rtree); + actual_arg_types[0] = rtypeId; + declared_arg_types[0] = opform->oprright; nargs = 1; } else { /* otherwise, binary operator */ @@ -747,7 +764,7 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; - nargs = 2; + nargs = 2; // Both the left operator and the right operator are valid. } /* @@ -755,6 +772,7 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo * possibly adjusting return type or declared_arg_types (which will be * used as the cast destination by make_fn_arguments) */ + // Oid of the operator type returned rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, nargs, opform->oprresult, false); /* perform the necessary typecasting of arguments */ @@ -762,17 +780,17 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo /* and build the expression node */ result = makeNode(OpExpr); - result->opno = oprid(tup); - result->opfuncid = opform->oprcode; - result->opresulttype = rettype; + result->opno = oprid(tup); + result->opfuncid = opform->oprcode; + result->opresulttype = rettype; result->opretset = get_func_retset(opform->oprcode); /* opcollid and inputcollid will be set by parse_collate.c */ - result->args = args; - result->location = location; + result->args = args; + result->location = location; - ReleaseSysCache(tup); + ReleaseSysCache(tup); // Release the cache space - return (Expr*)result; + return (Expr*)result; // Returned as an Expr* type } /* @@ -1026,4 +1044,4 @@ void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) if (hash_search(u_sess->parser_cxt.opr_cache_hash, (void*)&hentry->key, HASH_REMOVE, NULL) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("hash table corrupted"))); } -} +} \ No newline at end of file diff --git a/src/common/backend/parser/parse_param.cpp b/src/common/backend/parser/parse_param.cpp index 661ec4a8..c9de7007 100644 --- a/src/common/backend/parser/parse_param.cpp +++ b/src/common/backend/parser/parse_param.cpp @@ -3,7 +3,7 @@ * parse_param.cpp * handle parameters in parser * - * This code covers two cases that are used within the core backend: + * This code covers two cases that are used within the core backend: * * a fixed list of parameters with known types * * an expandable list of parameters whose types can optionally * be determined from context @@ -33,7 +33,8 @@ #include "utils/builtins.h" #include "utils/lsyscache.h" -typedef struct FixedParamState { +// Stmt of fixed parameters. +typedef struct FixedParamState { Oid* paramTypes; /* array of parameter type OIDs */ int numParams; /* number of array entries */ } FixedParamState; @@ -44,6 +45,7 @@ typedef struct FixedParamState { * hasn't been seen, while UNKNOWNOID means the parameter has been used but * its type is not yet known. */ +// Stmt of variable parameters. typedef struct VarParamState { Oid** paramTypes; /* array of parameter type OIDs */ int* numParams; /* number of array entries */ @@ -64,10 +66,12 @@ static bool check_parameter_resolution_walker(Node* node, ParseState* pstate); */ void parse_fixed_parameters(ParseState* pstate, Oid* paramTypes, int numParams) { + // palloc for FixedParamState. FixedParamState* parstate = (FixedParamState*)palloc(sizeof(FixedParamState)); - + // Construct the FixedParamState structure. parstate->paramTypes = paramTypes; parstate->numParams = numParams; + pstate->p_ref_hook_state = (void*)parstate; pstate->p_paramref_hook = fixed_paramref_hook; /* no need to use p_coerce_param_hook */ @@ -78,10 +82,12 @@ void parse_fixed_parameters(ParseState* pstate, Oid* paramTypes, int numParams) */ void parse_variable_parameters(ParseState* pstate, Oid** paramTypes, int* numParams) { + // palloc for VarParamState VarParamState* parstate = (VarParamState*)palloc(sizeof(VarParamState)); - + // Construct the VarParamState structure. parstate->paramTypes = paramTypes; parstate->numParams = numParams; + pstate->p_ref_hook_state = (void*)parstate; pstate->p_paramref_hook = variable_paramref_hook; pstate->p_coerce_param_hook = variable_coerce_param_hook; @@ -265,10 +271,10 @@ static bool check_parameter_resolution_walker(Node* node, ParseState* pstate) if (node == NULL) { return false; } - if (IsA(node, Param)) { - Param* param = (Param*)node; + if (IsA(node, Param)) { // If the current node is an object of the Param class + Param* param = (Param*)node; - if (param->paramkind == PARAM_EXTERN) { + if (param->paramkind == PARAM_EXTERN) { // If param is external VarParamState* parstate = (VarParamState*)pstate->p_ref_hook_state; int paramno = param->paramid; @@ -288,9 +294,9 @@ static bool check_parameter_resolution_walker(Node* node, ParseState* pstate) } return false; } - if (IsA(node, Query)) { + if (IsA(node, Query)) { // If the current node is an object of the Query class /* Recurse into RTE subquery or not-yet-planned sublink subquery */ return query_tree_walker((Query*)node, (bool (*)())check_parameter_resolution_walker, (void*)pstate, 0); } return expression_tree_walker(node, (bool (*)())check_parameter_resolution_walker, (void*)pstate); -} +} \ No newline at end of file diff --git a/src/common/backend/parser/parser.cpp b/src/common/backend/parser/parser.cpp index d91fb17c..d7541e83 100644 --- a/src/common/backend/parser/parser.cpp +++ b/src/common/backend/parser/parser.cpp @@ -9,7 +9,6 @@ * the grammar are "raw" parsetrees that still need to be analyzed by * analyze.c and related files. * - * * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * @@ -40,15 +39,15 @@ static void resetCreateFuncFlag() /* * raw_parser - * Given a query in string form, do lexical and grammatical analysis. + * Given a query in string form, do lexical and grammatical analysis. * * Returns a list of raw (un-analyzed) parse trees. */ List* raw_parser(const char* str, List** query_string_locationlist) { - core_yyscan_t yyscanner; - base_yy_extra_type yyextra; - int yyresult; + core_yyscan_t yyscanner; + base_yy_extra_type yyextra; // An intermediate variable that holds the returned syntax tree. + int yyresult; // The calling result of base_yyparse(). /* reset u_sess->parser_cxt.stmt_contains_operator_plus */ resetOperatorPlusFlag(); @@ -58,7 +57,7 @@ List* raw_parser(const char* str, List** query_string_locationlist) /* reset u_sess->parser_cxt.isCreateFuncOrProc */ resetCreateFuncFlag(); - + /* initialize the flex scanner */ yyscanner = scanner_init(str, &yyextra.core_yy_extra, ScanKeywords, NumScanKeywords); @@ -89,6 +88,7 @@ List* raw_parser(const char* str, List** query_string_locationlist) } } + // Returns the generated syntax tree. return yyextra.parsetree; } @@ -127,7 +127,7 @@ List* raw_parser(const char* str, List** query_string_locationlist) * words. Furthermore it's not clear how to do it without re-introducing * scanner backtrack, which would cost more performance than this filter * layer does. - * + * * The filter also provides a convenient place to translate between * the core_YYSTYPE and YYSTYPE representations (which are really the * same thing anyway, but notationally they're different). @@ -489,8 +489,8 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) /* * @Description: Check whether its a empty query with only comments and semicolon. - * @Param query_string: the query need check. - * @retrun:true or false. + * @Param[IN] query_string: the query need check. + * @return:the bool value of the check result. */ static bool is_empty_query(char* query_string) { @@ -513,6 +513,7 @@ static bool is_empty_query(char* query_string) end_comment_postion = strstr(query_string, end_comment); query_string = end_comment_postion + 2; while (isspace((unsigned char)*query_string)) { + // Trim the spaces after comments. query_string++; } } @@ -537,11 +538,12 @@ static bool is_empty_query(char* query_string) char** get_next_snippet( char** query_string_single, const char* query_string, List* query_string_locationlist, int* stmt_num) { - int query_string_location_start = 0; - int query_string_location_end = -1; - char* query_string_single_p = NULL; - int single_query_string_len = 0; - + int query_string_location_start = 0; // The starting position of the query statement. + int query_string_location_end = -1; // The ending position of the query statement. + char* query_string_single_p = NULL; // An intermediate variable used to copy the string. + int single_query_string_len = 0; // The length of the query statement. + + // Count the number of query statements. int stmt_count = list_length(query_string_locationlist); /* Malloc memory for single query here just for the first time. */ @@ -558,11 +560,13 @@ char** get_next_snippet( * Notice : The locationlist only store the end postion of each single query but not any * start postion. */ + // Calculate the starting position of the specified query statement. if (*stmt_num == 0) { query_string_location_start = 0; } else { query_string_location_start = list_nth_int(query_string_locationlist, *stmt_num - 1) + 1; } + // Calculate the ending position of the specified query statement. query_string_location_end = list_nth_int(query_string_locationlist, (*stmt_num)++); /* Malloc memory for each single query string. */ @@ -583,10 +587,10 @@ char** get_next_snippet( */ if (is_empty_query(query_string_single[*stmt_num - 1])) { continue; - } else { + } else { // The obtained query statement is not a null query, exit the loop, and return this statement. break; } } return query_string_single; -} +} \ No newline at end of file diff --git a/src/common/backend/parser/scansup.cpp b/src/common/backend/parser/scansup.cpp index dc11a225..3ee8ba1c 100644 --- a/src/common/backend/parser/scansup.cpp +++ b/src/common/backend/parser/scansup.cpp @@ -21,30 +21,29 @@ #include "parser/scansup.h" #include "mb/pg_wchar.h" -/* ---------------- - * scanstr - * - * if the string passed in has escaped codes, map the escape codes to actual - * chars +/* + * scanstr() --- if the string passed in has escaped codes, map the escape + * codes to actual chars. * * the string returned is palloc'd and should eventually be pfree'd by the * caller! - * ---------------- */ char* scanstr(const char* s) { char* newStr = NULL; int len, i, j; - if (s == NULL || s[0] == '\0') + if (s == NULL || s[0] == '\0') // The input string is empty. return pstrdup(""); len = strlen(s); - newStr = (char*)palloc(len + 1); /* string cannot get longer */ + // The new string constructed does not exceed the length of the original string. + // That is because the processes of the escape code would get the string shorter. + newStr = (char*)palloc(len + 1); /* string cannot get longer */ for (i = 0, j = 0; i < len; i++) { - if (s[i] == '\'') { + if (s[i] == '\'') { /* * Note: if scanner is working right, unescaped quotes can only * appear in pairs, so there should be another character. @@ -52,11 +51,11 @@ char* scanstr(const char* s) i++; newStr[j] = s[i]; } else if (s[i] == '\\') { - i++; + i++; // "\" and a next character are translated together into an escape symbol. switch (s[i]) { case 'b': newStr[j] = '\b'; - break; + break; case 'f': newStr[j] = '\f'; break; @@ -76,18 +75,20 @@ char* scanstr(const char* s) case '4': case '5': case '6': - case '7': { + case '7': { + // case '0'~'7', represents this is the highest bit of an octal number. int k; unsigned long octVal = 0; - + // Converts the next consecutive digits of up to three 0-7 digits into an octal number. for (k = 0; s[i + k] >= '0' && s[i + k] <= '7' && k < 3; k++){ octVal = (octVal << 3) + (s[i + k] - '0'); } i += k - 1; - newStr[j] = ((char)octVal); + // According to the octal numeric value corresponding to the ASCII code to determine the specific characters. + newStr[j] = ((char)octVal); } break; default: - newStr[j] = s[i]; + newStr[j] = s[i]; break; } /* switch */ } /* s[i] == '\\' */ @@ -96,7 +97,7 @@ char* scanstr(const char* s) } j++; } - newStr[j] = '\0'; + newStr[j] = '\0'; // The new string constructed should end with '\0'. return newStr; } @@ -120,6 +121,8 @@ char* downcase_truncate_identifier(const char* ident, int len, bool warn) bool enc_is_single_byte = false; result = (char*)palloc(len + 1); + // Gets the maximum byte of the database encoding. + // enc_is_single_byte = TRUE IF it is single-byte encoding. enc_is_single_byte = pg_database_encoding_max_length() == 1; /* @@ -131,7 +134,7 @@ char* downcase_truncate_identifier(const char* ident, int len, bool warn) * the high bit set, as long as they aren't part of a multi-byte character, * and use an ASCII-only downcasing for 7-bit characters. */ - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) { // Implementation of downcasing. unsigned char ch = (unsigned char)ident[i]; if (ch >= 'A' && ch <= 'Z') { @@ -143,7 +146,8 @@ char* downcase_truncate_identifier(const char* ident, int len, bool warn) } result[i] = '\0'; - if (i >= NAMEDATALEN) + // The length exceeds the identifier's maximum length, so truncate. + if (i >= NAMEDATALEN) truncate_identifier(result, i, warn); return result; @@ -152,15 +156,13 @@ char* downcase_truncate_identifier(const char* ident, int len, bool warn) /* * truncate_identifier() --- truncate an identifier to NAMEDATALEN-1 bytes. * - * The given string is modified in-place, if necessary. A warning is - * issued if requested. - * * We require the caller to pass in the string length since this saves a * strlen() call in some common usages. */ void truncate_identifier(char* ident, int len, bool warn) { if (len >= NAMEDATALEN) { + // Calculate the appropriate length after truncation. len = pg_mbcliplen(ident, len, NAMEDATALEN - 1); if (warn) { /* @@ -169,14 +171,17 @@ void truncate_identifier(char* ident, int len, bool warn) */ char buf[NAMEDATALEN]; errno_t rc; - + rc = memcpy_s(buf, NAMEDATALEN, ident, len); + securec_check(rc, "\0", "\0"); buf[len] = '\0'; + ereport(NOTICE, (errcode(ERRCODE_NAME_TOO_LONG), errmsg("identifier \"%s\" will be truncated to \"%s\"", ident, buf))); } - ident[len] = '\0'; + // Truncate by setting the end character of the identifier to '\0'. + ident[len] = '\0'; } } @@ -197,4 +202,4 @@ bool scanner_isspace(char ch) } return false; -} +} \ No newline at end of file