Skip to content

Commit c21339a

Browse files
Chao Li (Evan)Commitfest Bot
authored andcommitted
Add appendStringInfoIdentifier() to avoid intermediate quoting buffers
Introduce appendStringInfoIdentifier() and appendStringInfoQualifiedIdentifier(), helper functions that append an SQL identifier directly to a StringInfo while applying quoting rules when necessary. This avoids allocating and copying through temporary palloc buffers, as currently happens with quote_identifier() when used together with appendStringInfoString(). The new functions improve both readability and efficiency of call sites that construct SQL fragments, especially those that need to build qualified names such as schema.table. Convert several existing callers in objectaddress.c, explain.c and ruleutils.c to use appendStringInfoIdentifier() / appendStringInfoQualifiedIdentifier() as examples. No functional behavior change is intended. Author: Chao Li <lic@highgo.com> Discussion: https://postgr.es/m/CAEoWx2=g2RVkxXB=JzWphgfg4QGV+spaA3PQ1rBM2iMehrVvjg@mail.gmail.com
1 parent a87987c commit c21339a

File tree

5 files changed

+133
-40
lines changed

5 files changed

+133
-40
lines changed

src/backend/catalog/objectaddress.c

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4883,8 +4883,7 @@ getObjectIdentityParts(const ObjectAddress *object,
48834883

48844884
if (attr)
48854885
{
4886-
appendStringInfo(&buffer, ".%s",
4887-
quote_identifier(attr));
4886+
appendStringInfoIdentifier(&buffer, ".", attr, NULL);
48884887
if (objname)
48894888
*objname = lappend(*objname, attr);
48904889
}
@@ -5395,8 +5394,7 @@ getObjectIdentityParts(const ObjectAddress *object,
53955394
object->objectId);
53965395
break;
53975396
}
5398-
appendStringInfoString(&buffer,
5399-
quote_identifier(nspname));
5397+
appendStringInfoIdentifier(&buffer, NULL, nspname, NULL);
54005398
if (objname)
54015399
*objname = list_make1(nspname);
54025400
break;
@@ -5739,16 +5737,12 @@ getObjectIdentityParts(const ObjectAddress *object,
57395737
defacl = (Form_pg_default_acl) GETSTRUCT(tup);
57405738

57415739
username = GetUserNameFromId(defacl->defaclrole, false);
5742-
appendStringInfo(&buffer,
5743-
"for role %s",
5744-
quote_identifier(username));
5740+
appendStringInfoIdentifier(&buffer, "for role ", username, NULL);
57455741

57465742
if (OidIsValid(defacl->defaclnamespace))
57475743
{
57485744
schema = get_namespace_name_or_temp(defacl->defaclnamespace);
5749-
appendStringInfo(&buffer,
5750-
" in schema %s",
5751-
quote_identifier(schema));
5745+
appendStringInfoIdentifier(&buffer, " in schema ", schema, NULL);
57525746
}
57535747
else
57545748
schema = NULL;

src/backend/commands/explain.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,8 +1705,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
17051705
explain_get_index_name(bitmapindexscan->indexid);
17061706

17071707
if (es->format == EXPLAIN_FORMAT_TEXT)
1708-
appendStringInfo(es->str, " on %s",
1709-
quote_identifier(indexname));
1708+
appendStringInfoIdentifier(es->str, " on ", indexname, NULL);
17101709
else
17111710
ExplainPropertyText("Index Name", indexname, es);
17121711
}

src/backend/utils/adt/ri_triggers.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,6 @@ RI_FKey_check(TriggerData *trigdata)
395395
{
396396
quoteOneName(attname,
397397
RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
398-
399398
appendStringInfo(&querybuf,
400399
"SELECT 1 FROM (SELECT %s AS r FROM %s%s x",
401400
attname, pk_only, pkrelname);
@@ -2095,7 +2094,6 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation)
20952094
HeapTuple tp;
20962095
Form_pg_collation colltup;
20972096
char *collname;
2098-
char onename[MAX_QUOTED_NAME_LEN];
20992097

21002098
/* Nothing to do if it's a noncollatable data type */
21012099
if (!OidIsValid(collation))
@@ -2111,10 +2109,8 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation)
21112109
* We qualify the name always, for simplicity and to ensure the query is
21122110
* not search-path-dependent.
21132111
*/
2114-
quoteOneName(onename, get_namespace_name(colltup->collnamespace));
2115-
appendStringInfo(buf, " COLLATE %s", onename);
2116-
quoteOneName(onename, collname);
2117-
appendStringInfo(buf, ".%s", onename);
2112+
appendStringInfoIdentifier(buf, " COLLATE ", get_namespace_name(colltup->collnamespace), NULL);
2113+
appendStringInfoIdentifier(buf, ".", collname, NULL);
21182114

21192115
ReleaseSysCache(tp);
21202116
}

src/backend/utils/adt/ruleutils.c

Lines changed: 120 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13052,33 +13052,25 @@ printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
1305213052
}
1305313053
}
1305413054

13055-
/*
13056-
* quote_identifier - Quote an identifier only if needed
13057-
*
13058-
* When quotes are needed, we palloc the required space; slightly
13059-
* space-wasteful but well worth it for notational simplicity.
13060-
*/
13061-
const char *
13062-
quote_identifier(const char *ident)
13055+
static inline bool
13056+
is_identifier_safe(const char *ident, int *nquotes)
1306313057
{
1306413058
/*
1306513059
* Can avoid quoting if ident starts with a lowercase letter or underscore
1306613060
* and contains only lowercase letters, digits, and underscores, *and* is
1306713061
* not any SQL keyword. Otherwise, supply quotes.
1306813062
*/
13069-
int nquotes = 0;
1307013063
bool safe;
13071-
const char *ptr;
13072-
char *result;
13073-
char *optr;
13064+
13065+
*nquotes = 0;
1307413066

1307513067
/*
1307613068
* would like to use <ctype.h> macros here, but they might yield unwanted
1307713069
* locale-specific results...
1307813070
*/
1307913071
safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
1308013072

13081-
for (ptr = ident; *ptr; ptr++)
13073+
for (const char *ptr = ident; *ptr; ptr++)
1308213074
{
1308313075
char ch = *ptr;
1308413076

@@ -13092,7 +13084,7 @@ quote_identifier(const char *ident)
1309213084
{
1309313085
safe = false;
1309413086
if (ch == '"')
13095-
nquotes++;
13087+
(*nquotes)++;
1309613088
}
1309713089
}
1309813090

@@ -13115,14 +13107,20 @@ quote_identifier(const char *ident)
1311513107
safe = false;
1311613108
}
1311713109

13118-
if (safe)
13119-
return ident; /* no change needed */
13110+
return safe;
13111+
}
1312013112

13121-
result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13113+
static inline const char *
13114+
quote_identifier_internal(const char *ident, int nquotes, char **result)
13115+
{
13116+
char *optr;
1312213117

13123-
optr = result;
13118+
if (*result == NULL)
13119+
*result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
13120+
13121+
optr = *result;
1312413122
*optr++ = '"';
13125-
for (ptr = ident; *ptr; ptr++)
13123+
for (const char *ptr = ident; *ptr; ptr++)
1312613124
{
1312713125
char ch = *ptr;
1312813126

@@ -13133,7 +13131,107 @@ quote_identifier(const char *ident)
1313313131
*optr++ = '"';
1313413132
*optr = '\0';
1313513133

13136-
return result;
13134+
return *result;
13135+
}
13136+
13137+
/*
13138+
* quote_identifier - Quote an identifier only if needed
13139+
*
13140+
* When quotes are needed, we palloc the required space; slightly
13141+
* space-wasteful but well worth it for notational simplicity.
13142+
*/
13143+
const char *
13144+
quote_identifier(const char *ident)
13145+
{
13146+
int nquotes;
13147+
bool safe;
13148+
char *result = NULL;
13149+
13150+
safe = is_identifier_safe(ident, &nquotes);
13151+
if (safe)
13152+
return ident; /* no change needed */
13153+
13154+
return quote_identifier_internal(ident, nquotes, &result);
13155+
}
13156+
13157+
/*
13158+
* appendStringInfoIdentifier
13159+
* Append an SQL identifier to a StringInfo, quoting if required.
13160+
*
13161+
* This behaves like writing prefix + quote_identifier(ident) + suffix into
13162+
* the StringInfo, but emits the identifier directly into the buffer to avoid
13163+
* an intermediate palloc. prefix and suffix may be NULL.
13164+
*
13165+
* The identifier is copied verbatim if it is safe to leave unquoted; otherwise
13166+
* it is written with double quotes and embedded double quotes are doubled.
13167+
* Quoting rules are local to ruleutils, so this helper is defined here rather
13168+
* than in stringinfo.c.
13169+
*/
13170+
13171+
void
13172+
appendStringInfoIdentifier(StringInfo str, const char *prefix,
13173+
const char *ident, const char *suffix)
13174+
{
13175+
int nquotes;
13176+
bool safe;
13177+
int ident_len;
13178+
int prefix_len = 0;
13179+
int suffix_len = 0;
13180+
13181+
safe = is_identifier_safe(ident, &nquotes);
13182+
if (safe)
13183+
ident_len = strlen(ident);
13184+
else
13185+
ident_len = strlen(ident) + nquotes + 2; /* quotes + possible
13186+
* escapes */
13187+
13188+
if (prefix != NULL)
13189+
prefix_len = strlen(prefix);
13190+
13191+
if (suffix != NULL)
13192+
suffix_len = strlen(suffix);
13193+
13194+
enlargeStringInfo(str, ident_len + prefix_len + suffix_len + 1); /* +1 for trailing null */
13195+
13196+
if (prefix != NULL)
13197+
appendBinaryStringInfo(str, prefix, prefix_len);
13198+
13199+
if (safe)
13200+
appendBinaryStringInfo(str, ident, ident_len);
13201+
else
13202+
{
13203+
char *result = str->data + str->len;
13204+
13205+
(void) quote_identifier_internal(ident, nquotes, &result);
13206+
str->len += ident_len;
13207+
str->data[str->len] = '\0';
13208+
}
13209+
13210+
if (suffix != NULL)
13211+
appendBinaryStringInfo(str, suffix, suffix_len);
13212+
}
13213+
13214+
/*
13215+
* appendStringInfoQualifiedIdentifier
13216+
* Append a (possibly) qualified SQL identifier to a StringInfo.
13217+
*
13218+
* Writes prefix + qualifier + '.' + ident + suffix, quoting each identifier
13219+
* component if needed. If no qualifier is given, only ident (plus optional
13220+
* prefix/suffix) is appended. prefix and suffix may be NULL.
13221+
*
13222+
* This is a convenience wrapper around appendStringInfoIdentifier().
13223+
*/
13224+
void
13225+
appendStringInfoQualifiedIdentifier(StringInfo str, const char *prefix,
13226+
const char *qualifier, const char *ident,
13227+
const char *suffix)
13228+
{
13229+
if (qualifier)
13230+
{
13231+
appendStringInfoIdentifier(str, prefix, qualifier, ".");
13232+
prefix = NULL;
13233+
}
13234+
appendStringInfoIdentifier(str, prefix, ident, suffix);
1313713235
}
1313813236

1313913237
/*
@@ -13150,8 +13248,8 @@ quote_qualified_identifier(const char *qualifier,
1315013248

1315113249
initStringInfo(&buf);
1315213250
if (qualifier)
13153-
appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
13154-
appendStringInfoString(&buf, quote_identifier(ident));
13251+
appendStringInfoIdentifier(&buf, NULL, qualifier, ".");
13252+
appendStringInfoIdentifier(&buf, NULL, ident, NULL);
1315513253
return buf.data;
1315613254
}
1315713255

src/include/utils/builtins.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ extern void generate_operator_clause(StringInfo buf,
8484
const char *leftop, Oid leftoptype,
8585
Oid opoid,
8686
const char *rightop, Oid rightoptype);
87+
extern void appendStringInfoIdentifier(StringInfo str, const char *prefix, const char *ident, const char *suffix);
88+
extern void appendStringInfoQualifiedIdentifier(StringInfo str,
89+
const char *prefix,
90+
const char *qualifier,
91+
const char *ident,
92+
const char *suffix);
8793

8894
/* varchar.c */
8995
extern int bpchartruelen(char *s, int len);

0 commit comments

Comments
 (0)