为src/common/backend/parser下文件添加了部分注释 #1

Closed
XL_up wants to merge 9 commits from (deleted):master into master
7 changed files with 170 additions and 102 deletions

View File

@ -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.
*/
/*
* @DescriptionAnalyze 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.
* @Returnan 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);
}
}

View File

@ -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);

View File

@ -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] textThe identifier to be matched.
* @param[IN] keywordsA 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;
}
}

View File

@ -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")));
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}