public inbox for [email protected]  
help / color / mirror / Atom feed
From: Chao Li <[email protected]>
To: Álvaro Herrera <[email protected]>
Cc: Postgres hackers <[email protected]>
Subject:  Re: quoteOneName() inconsistency with quote_all_identifiers — replacement API proposed
Date: Fri, 21 Nov 2025 13:52:04 +0800
Message-ID: <CAEoWx2nPbikvjp0JBsHzbpE=u7aMkiVy-WCmPim2iX5hDF9fBw@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <CAEoWx2=vRfws6C2svUWdmNA10no+g71HABDqqKdbL43adC-VuQ@mail.gmail.com>
	<[email protected]>

On Thu, Nov 20, 2025 at 8:28 PM Álvaro Herrera <[email protected]> wrote:

> Hi,
>
> > -                appendStringInfo(&buffer, _("text search configuration
> %s"),
> > -                                 quote_qualified_identifier(nspname,
> > -
> NameStr(cfgForm->cfgname)));
> > +                appendStringInfoQualifiedIdentifier(&buffer,
> > +                                                    _("text search
> configuration "),
> > +                                                    nspname,
> NameStr(cfgForm->cfgname), NULL);
> >                  ReleaseSysCache(tup);
> >                  break;
> >              }
>
> This doesn't work from a i18n point of view.  In the original
> formulation, the translator is free to place the %s wherever it suits
> the language.  In the new one there's no such freedom: the name will be
> appended at the end.  Some existing translations:
>
> ko.po:msgid "text search configuration %s"
> ko.po-msgstr "%s 전문 검색 구성"
>
> tr.po:msgid "text search configuration %s"
> tr.po-msgstr "%s metin arama yapılandırması"
>

Thanks for the feedback. I reverted that piece of change in v3.

Best regards,
Chao Li (Evan)
---------------------
HighGo Software Co., Ltd.
https://www.highgo.com/


Attachments:

  [application/octet-stream] v3-0001-Add-appendStringInfoIdentifier-to-avoid-intermedi.patch (10.8K, 3-v3-0001-Add-appendStringInfoIdentifier-to-avoid-intermedi.patch)
  download | inline diff:
From 648a9cbd2013ac82d76585610463e1ae30e09973 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Fri, 14 Nov 2025 21:44:29 +0800
Subject: [PATCH v3 1/4] 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 <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=g2RVkxXB=JzWphgfg4QGV+spaA3PQ1rBM2iMehrVvjg@mail.gmail.com
---
 src/backend/catalog/objectaddress.c |  14 +--
 src/backend/commands/explain.c      |   3 +-
 src/backend/utils/adt/ri_triggers.c |   8 +-
 src/backend/utils/adt/ruleutils.c   | 142 +++++++++++++++++++++++-----
 src/include/utils/builtins.h        |   6 ++
 5 files changed, 133 insertions(+), 40 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c75b7131ed7..70e05059f9f 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4883,8 +4883,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				if (attr)
 				{
-					appendStringInfo(&buffer, ".%s",
-									 quote_identifier(attr));
+					appendStringInfoIdentifier(&buffer, ".", attr, NULL);
 					if (objname)
 						*objname = lappend(*objname, attr);
 				}
@@ -5395,8 +5394,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 							 object->objectId);
 					break;
 				}
-				appendStringInfoString(&buffer,
-									   quote_identifier(nspname));
+				appendStringInfoIdentifier(&buffer, NULL, nspname, NULL);
 				if (objname)
 					*objname = list_make1(nspname);
 				break;
@@ -5739,16 +5737,12 @@ getObjectIdentityParts(const ObjectAddress *object,
 				defacl = (Form_pg_default_acl) GETSTRUCT(tup);
 
 				username = GetUserNameFromId(defacl->defaclrole, false);
-				appendStringInfo(&buffer,
-								 "for role %s",
-								 quote_identifier(username));
+				appendStringInfoIdentifier(&buffer, "for role ", username, NULL);
 
 				if (OidIsValid(defacl->defaclnamespace))
 				{
 					schema = get_namespace_name_or_temp(defacl->defaclnamespace);
-					appendStringInfo(&buffer,
-									 " in schema %s",
-									 quote_identifier(schema));
+					appendStringInfoIdentifier(&buffer, " in schema ", schema, NULL);
 				}
 				else
 					schema = NULL;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 7e699f8595e..cc979737845 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1705,8 +1705,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 					explain_get_index_name(bitmapindexscan->indexid);
 
 				if (es->format == EXPLAIN_FORMAT_TEXT)
-					appendStringInfo(es->str, " on %s",
-									 quote_identifier(indexname));
+					appendStringInfoIdentifier(es->str,	" on ", indexname, NULL);
 				else
 					ExplainPropertyText("Index Name", indexname, es);
 			}
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 059fc5ebf60..9e13f526994 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -395,7 +395,6 @@ RI_FKey_check(TriggerData *trigdata)
 		{
 			quoteOneName(attname,
 						 RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
-
 			appendStringInfo(&querybuf,
 							 "SELECT 1 FROM (SELECT %s AS r FROM %s%s x",
 							 attname, pk_only, pkrelname);
@@ -2095,7 +2094,6 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation)
 	HeapTuple	tp;
 	Form_pg_collation colltup;
 	char	   *collname;
-	char		onename[MAX_QUOTED_NAME_LEN];
 
 	/* Nothing to do if it's a noncollatable data type */
 	if (!OidIsValid(collation))
@@ -2111,10 +2109,8 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation)
 	 * We qualify the name always, for simplicity and to ensure the query is
 	 * not search-path-dependent.
 	 */
-	quoteOneName(onename, get_namespace_name(colltup->collnamespace));
-	appendStringInfo(buf, " COLLATE %s", onename);
-	quoteOneName(onename, collname);
-	appendStringInfo(buf, ".%s", onename);
+	appendStringInfoIdentifier(buf, " COLLATE ", get_namespace_name(colltup->collnamespace), NULL);
+	appendStringInfoIdentifier(buf, ".", collname, NULL);
 
 	ReleaseSysCache(tp);
 }
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 556ab057e5a..1d767deb6c4 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -13052,25 +13052,17 @@ printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
 	}
 }
 
-/*
- * quote_identifier			- Quote an identifier only if needed
- *
- * When quotes are needed, we palloc the required space; slightly
- * space-wasteful but well worth it for notational simplicity.
- */
-const char *
-quote_identifier(const char *ident)
+static inline bool
+is_identifier_safe(const char *ident, int *nquotes)
 {
 	/*
 	 * Can avoid quoting if ident starts with a lowercase letter or underscore
 	 * and contains only lowercase letters, digits, and underscores, *and* is
 	 * not any SQL keyword.  Otherwise, supply quotes.
 	 */
-	int			nquotes = 0;
 	bool		safe;
-	const char *ptr;
-	char	   *result;
-	char	   *optr;
+
+	*nquotes = 0;
 
 	/*
 	 * would like to use <ctype.h> macros here, but they might yield unwanted
@@ -13078,7 +13070,7 @@ quote_identifier(const char *ident)
 	 */
 	safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
 
-	for (ptr = ident; *ptr; ptr++)
+	for (const char *ptr = ident; *ptr; ptr++)
 	{
 		char		ch = *ptr;
 
@@ -13092,7 +13084,7 @@ quote_identifier(const char *ident)
 		{
 			safe = false;
 			if (ch == '"')
-				nquotes++;
+				(*nquotes)++;
 		}
 	}
 
@@ -13115,14 +13107,20 @@ quote_identifier(const char *ident)
 			safe = false;
 	}
 
-	if (safe)
-		return ident;			/* no change needed */
+	return safe;
+}
 
-	result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
+static inline const char *
+quote_identifier_internal(const char *ident, int nquotes, char **result)
+{
+	char	   *optr;
 
-	optr = result;
+	if (*result == NULL)
+		*result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
+
+	optr = *result;
 	*optr++ = '"';
-	for (ptr = ident; *ptr; ptr++)
+	for (const char *ptr = ident; *ptr; ptr++)
 	{
 		char		ch = *ptr;
 
@@ -13133,7 +13131,107 @@ quote_identifier(const char *ident)
 	*optr++ = '"';
 	*optr = '\0';
 
-	return result;
+	return *result;
+}
+
+/*
+ * quote_identifier			- Quote an identifier only if needed
+ *
+ * When quotes are needed, we palloc the required space; slightly
+ * space-wasteful but well worth it for notational simplicity.
+ */
+const char *
+quote_identifier(const char *ident)
+{
+	int			nquotes;
+	bool		safe;
+	char	   *result = NULL;
+
+	safe = is_identifier_safe(ident, &nquotes);
+	if (safe)
+		return ident;			/* no change needed */
+
+	return quote_identifier_internal(ident, nquotes, &result);
+}
+
+/*
+ * appendStringInfoIdentifier
+ *      Append an SQL identifier to a StringInfo, quoting if required.
+ *
+ * This behaves like writing prefix + quote_identifier(ident) + suffix into
+ * the StringInfo, but emits the identifier directly into the buffer to avoid
+ * an intermediate palloc.  prefix and suffix may be NULL.
+ *
+ * The identifier is copied verbatim if it is safe to leave unquoted; otherwise
+ * it is written with double quotes and embedded double quotes are doubled.
+ * Quoting rules are local to ruleutils, so this helper is defined here rather
+ * than in stringinfo.c.
+ */
+
+void
+appendStringInfoIdentifier(StringInfo str, const char *prefix,
+						   const char *ident, const char *suffix)
+{
+	int			nquotes;
+	bool		safe;
+	int			ident_len;
+	int			prefix_len = 0;
+	int			suffix_len = 0;
+
+	safe = is_identifier_safe(ident, &nquotes);
+	if (safe)
+		ident_len = strlen(ident);
+	else
+		ident_len = strlen(ident) + nquotes + 2;	/* quotes + possible
+													 * escapes */
+
+	if (prefix != NULL)
+		prefix_len = strlen(prefix);
+
+	if (suffix != NULL)
+		suffix_len = strlen(suffix);
+
+	enlargeStringInfo(str, ident_len + prefix_len + suffix_len + 1);	/* +1 for trailing null */
+
+	if (prefix != NULL)
+		appendBinaryStringInfo(str, prefix, prefix_len);
+
+	if (safe)
+		appendBinaryStringInfo(str, ident, ident_len);
+	else
+	{
+		char	   *result = str->data + str->len;
+
+		(void) quote_identifier_internal(ident, nquotes, &result);
+		str->len += ident_len;
+		str->data[str->len] = '\0';
+	}
+
+	if (suffix != NULL)
+		appendBinaryStringInfo(str, suffix, suffix_len);
+}
+
+/*
+ * appendStringInfoQualifiedIdentifier
+ *      Append a (possibly) qualified SQL identifier to a StringInfo.
+ *
+ * Writes prefix + qualifier + '.' + ident + suffix, quoting each identifier
+ * component if needed.  If no qualifier is given, only ident (plus optional
+ * prefix/suffix) is appended.  prefix and suffix may be NULL.
+ *
+ * This is a convenience wrapper around appendStringInfoIdentifier().
+ */
+void
+appendStringInfoQualifiedIdentifier(StringInfo str, const char *prefix,
+									const char *qualifier, const char *ident,
+									const char *suffix)
+{
+	if (qualifier)
+	{
+		appendStringInfoIdentifier(str, prefix, qualifier, ".");
+		prefix = NULL;
+	}
+	appendStringInfoIdentifier(str, prefix, ident, suffix);
 }
 
 /*
@@ -13150,8 +13248,8 @@ quote_qualified_identifier(const char *qualifier,
 
 	initStringInfo(&buf);
 	if (qualifier)
-		appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
-	appendStringInfoString(&buf, quote_identifier(ident));
+		appendStringInfoIdentifier(&buf, NULL, qualifier, ".");
+	appendStringInfoIdentifier(&buf, NULL, ident, NULL);
 	return buf.data;
 }
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index ce6285a2c03..ff4ba7265c2 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -84,6 +84,12 @@ extern void generate_operator_clause(StringInfo buf,
 									 const char *leftop, Oid leftoptype,
 									 Oid opoid,
 									 const char *rightop, Oid rightoptype);
+extern void appendStringInfoIdentifier(StringInfo str, const char *prefix, const char *ident, const char *suffix);
+extern void appendStringInfoQualifiedIdentifier(StringInfo str,
+												const char *prefix,
+												const char *qualifier,
+												const char *ident,
+												const char *suffix);
 
 /* varchar.c */
 extern int	bpchartruelen(char *s, int len);
-- 
2.39.5 (Apple Git-154)



  [application/octet-stream] v3-0003-Remove-quoteOneName-and-related-buffer-sizing-mac.patch (11.5K, 4-v3-0003-Remove-quoteOneName-and-related-buffer-sizing-mac.patch)
  download | inline diff:
From 1ea040c6761a91d807c8e63887a05ac01bb93b0a Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Tue, 18 Nov 2025 17:13:07 +0800
Subject: [PATCH v3 3/4] Remove quoteOneName() and related buffer-sizing macros
 from ri_triggers.c

After the previous refactoring, quoteOneName() and its callers are no longer
needed.  Remove the function along with MAX_QUOTED_NAME_LEN,
MAX_QUOTED_REL_NAME_LEN, and quoteRelationName(), and introduce
appendRelationName() as the remaining helper for writing qualified relation
names using appendStringInfoQualifiedIdentifier().

This reduces redundant quoting code and centralizes identifier handling in
appendStringInfoIdentifier() / appendStringInfoQualifiedIdentifier(), making
RI triggers consistent with other code that generates SQL fragments.

No functional behavior change is expected.

Author: Chao Li <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=g2RVkxXB=JzWphgfg4QGV+spaA3PQ1rBM2iMehrVvjg@mail.gmail.com
---
 src/backend/utils/adt/ri_triggers.c | 112 ++++++++--------------------
 1 file changed, 30 insertions(+), 82 deletions(-)

diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 3cc4dc2b7c8..d676930aca2 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -80,9 +80,6 @@
 #define RI_PLAN_SETDEFAULT_ONDELETE		9
 #define RI_PLAN_SETDEFAULT_ONUPDATE		10
 
-#define MAX_QUOTED_NAME_LEN  (NAMEDATALEN*2+3)
-#define MAX_QUOTED_REL_NAME_LEN  (MAX_QUOTED_NAME_LEN*2)
-
 #define RIAttName(rel, attnum)	NameStr(*attnumAttName(rel, attnum))
 #define RIAttType(rel, attnum)	attnumTypeId(rel, attnum)
 #define RIAttCollation(rel, attnum) attnumCollationId(rel, attnum)
@@ -192,8 +189,7 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 							  const RI_ConstraintInfo *riinfo);
 static Datum ri_restrict(TriggerData *trigdata, bool is_no_action);
 static Datum ri_set(TriggerData *trigdata, bool is_set_null, int tgkind);
-static void quoteOneName(char *buffer, const char *name);
-static void quoteRelationName(char *buffer, Relation rel);
+static void appendRelationName(StringInfo buffer, Relation rel, const char *prefix, const char *suffix);
 static void ri_GenerateQual(StringInfo buf,
 							const char *sep,
 							const char *leftopprefix, const char *leftop, Oid leftoptype, bool quoteleftop,
@@ -357,7 +353,6 @@ RI_FKey_check(TriggerData *trigdata)
 	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 	{
 		StringInfoData querybuf;
-		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
 		const char *attname;
 		char		paramname[16];
 		const char *querysep;
@@ -390,19 +385,16 @@ RI_FKey_check(TriggerData *trigdata)
 		initStringInfo(&querybuf);
 		pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 			"" : "ONLY ";
-		quoteRelationName(pkrelname, pk_rel);
 		if (riinfo->hasperiod)
 		{
 			attname = RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]);
 			appendStringInfoIdentifier(&querybuf, "SELECT 1 FROM (SELECT ", attname, " AS r FROM ");
-			appendStringInfo(&querybuf,
-							 "%s%s x",
-							 pk_only, pkrelname);
+			appendRelationName(&querybuf, pk_rel, pk_only, " x");
 		}
 		else
 		{
-			appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
-							 pk_only, pkrelname);
+			appendStringInfoString(&querybuf, "SELECT 1 FROM ");
+			appendRelationName(&querybuf, pk_rel, pk_only, " x");
 		}
 		querysep = "WHERE";
 		for (int i = 0; i < riinfo->nkeys; i++)
@@ -526,7 +518,6 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 	{
 		StringInfoData querybuf;
-		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
 		const char *attname;
 		char		paramname[16];
 		const char *querysep;
@@ -559,19 +550,16 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 		initStringInfo(&querybuf);
 		pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 			"" : "ONLY ";
-		quoteRelationName(pkrelname, pk_rel);
 		if (riinfo->hasperiod)
 		{
 			attname = RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]);
 			appendStringInfoIdentifier(&querybuf, "SELECT 1 FROM (SELECT ", attname, " AS r FROM ");
-			appendStringInfo(&querybuf,
-							 "%s%s x",
-							 pk_only, pkrelname);
+			appendRelationName(&querybuf, pk_rel, pk_only, " x");
 		}
 		else
 		{
-			appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
-							 pk_only, pkrelname);
+			appendStringInfoString(&querybuf, "SELECT 1 FROM ");
+			appendRelationName(&querybuf, pk_rel, pk_only, " x");
 		}
 		querysep = "WHERE";
 		for (int i = 0; i < riinfo->nkeys; i++)
@@ -753,10 +741,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 	{
 		StringInfoData querybuf;
-		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
-		char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
 		const char *attname;
-		char		periodattname[MAX_QUOTED_NAME_LEN];
 		char		paramname[16];
 		const char *querysep;
 		Oid			queryoids[RI_MAX_NUMKEYS];
@@ -773,9 +758,8 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 		initStringInfo(&querybuf);
 		fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 			"" : "ONLY ";
-		quoteRelationName(fkrelname, fk_rel);
-		appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
-						 fk_only, fkrelname);
+		appendStringInfoString(&querybuf, "SELECT 1 FROM ");
+		appendRelationName(&querybuf, fk_rel, fk_only, " x");
 		querysep = "WHERE";
 		for (int i = 0; i < riinfo->nkeys; i++)
 		{
@@ -843,10 +827,8 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 			initStringInfo(&replacementsbuf);
 			appendStringInfoString(&replacementsbuf, "(SELECT pg_catalog.range_agg(r) FROM ");
 
-			quoteOneName(periodattname, RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
-			quoteRelationName(pkrelname, pk_rel);
-			appendStringInfo(&replacementsbuf, "(SELECT y.%s r FROM %s%s y",
-							 periodattname, pk_only, pkrelname);
+			appendStringInfoIdentifier(&replacementsbuf, "(SELECT y.", RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]), " r FROM ");
+			appendRelationName(&replacementsbuf, pk_rel, pk_only, " y");
 
 			/* Restrict pk rows to what matches */
 			querysep = "WHERE";
@@ -939,7 +921,6 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 	{
 		StringInfoData querybuf;
-		char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
 		const char *attname;
 		char		paramname[16];
 		const char *querysep;
@@ -956,9 +937,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 		initStringInfo(&querybuf);
 		fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 			"" : "ONLY ";
-		quoteRelationName(fkrelname, fk_rel);
-		appendStringInfo(&querybuf, "DELETE FROM %s%s",
-						 fk_only, fkrelname);
+		appendStringInfoString(&querybuf, "DELETE FROM ");
+		appendRelationName(&querybuf, fk_rel, fk_only, NULL);
 		querysep = "WHERE";
 		for (int i = 0; i < riinfo->nkeys; i++)
 		{
@@ -1044,7 +1024,6 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 	{
 		StringInfoData querybuf;
 		StringInfoData qualbuf;
-		char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
 		const char *attname;
 		char		paramname[16];
 		const char *querysep;
@@ -1066,9 +1045,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 		initStringInfo(&qualbuf);
 		fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 			"" : "ONLY ";
-		quoteRelationName(fkrelname, fk_rel);
-		appendStringInfo(&querybuf, "UPDATE %s%s SET",
-						 fk_only, fkrelname);
+		appendStringInfoString(&querybuf, "UPDATE ");
+		appendRelationName(&querybuf, fk_rel, fk_only, " SET");
 		querysep = "";
 		qualsep = "WHERE";
 		for (int i = 0, j = riinfo->nkeys; i < riinfo->nkeys; i++, j++)
@@ -1232,7 +1210,6 @@ ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
 	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 	{
 		StringInfoData querybuf;
-		char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
 		const char *attname;
 		char		paramname[16];
 		const char *querysep;
@@ -1281,9 +1258,8 @@ ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
 		initStringInfo(&querybuf);
 		fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 			"" : "ONLY ";
-		quoteRelationName(fkrelname, fk_rel);
-		appendStringInfo(&querybuf, "UPDATE %s%s SET",
-						 fk_only, fkrelname);
+		appendStringInfo(&querybuf, "UPDATE ");
+		appendRelationName(&querybuf, fk_rel, fk_only, " SET");
 
 		/*
 		 * Add assignment clauses
@@ -1509,8 +1485,6 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 {
 	const RI_ConstraintInfo *riinfo;
 	StringInfoData querybuf;
-	char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
-	char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
 	const char *pkattname;
 	const char *fkattname;
 	RangeTblEntry *rte;
@@ -1613,15 +1587,13 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 		sep = ", ";
 	}
 
-	quoteRelationName(pkrelname, pk_rel);
-	quoteRelationName(fkrelname, fk_rel);
 	fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 		"" : "ONLY ";
 	pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 		"" : "ONLY ";
-	appendStringInfo(&querybuf,
-					 " FROM %s%s fk LEFT OUTER JOIN %s%s pk ON",
-					 fk_only, fkrelname, pk_only, pkrelname);
+	appendStringInfoString(&querybuf, " FROM ");
+	appendRelationName(&querybuf, fk_rel, fk_only, " fk LEFT OUTER JOIN ");
+	appendRelationName(&querybuf, pk_rel, pk_only, " pk ON");
 
 	sep = "(";
 	for (int i = 0; i < riinfo->nkeys; i++)
@@ -1799,8 +1771,6 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	const RI_ConstraintInfo *riinfo;
 	StringInfoData querybuf;
 	char	   *constraintDef;
-	char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
-	char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
 	const char *pkattname;
 	const char *fkattname;
 	const char *sep;
@@ -1845,13 +1815,12 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 		sep = ", ";
 	}
 
-	quoteRelationName(pkrelname, pk_rel);
-	quoteRelationName(fkrelname, fk_rel);
 	fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 		"" : "ONLY ";
-	appendStringInfo(&querybuf,
-					 " FROM %s%s fk JOIN %s pk ON",
-					 fk_only, fkrelname, pkrelname);
+	appendStringInfoString(&querybuf, " FROM ");
+	appendRelationName(&querybuf, fk_rel, fk_only, " fk JOIN ");
+	appendRelationName(&querybuf, pk_rel, NULL, " pk ON");
+
 	/* strcpy(pkattname, "pk."); */
 	/* strcpy(fkattname, "fk."); */
 	sep = "(";
@@ -2004,37 +1973,16 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 
 
 /*
- * quoteOneName --- safely quote a single SQL name
- *
- * buffer must be MAX_QUOTED_NAME_LEN long (includes room for \0)
- */
-static void
-quoteOneName(char *buffer, const char *name)
-{
-	/* Rather than trying to be smart, just always quote it. */
-	*buffer++ = '"';
-	while (*name)
-	{
-		if (*name == '"')
-			*buffer++ = '"';
-		*buffer++ = *name++;
-	}
-	*buffer++ = '"';
-	*buffer = '\0';
-}
+ * appendRelationName --- safely append a quoted fully qualified relation name
 
-/*
- * quoteRelationName --- safely quote a fully qualified relation name
- *
- * buffer must be MAX_QUOTED_REL_NAME_LEN long (includes room for \0)
  */
 static void
-quoteRelationName(char *buffer, Relation rel)
+appendRelationName(StringInfo buffer, Relation rel,
+				   const char *prefix, const char *suffix)
 {
-	quoteOneName(buffer, get_namespace_name(RelationGetNamespace(rel)));
-	buffer += strlen(buffer);
-	*buffer++ = '.';
-	quoteOneName(buffer, RelationGetRelationName(rel));
+	appendStringInfoQualifiedIdentifier(buffer, prefix,
+										get_namespace_name(RelationGetNamespace(rel)),
+										RelationGetRelationName(rel), suffix);
 }
 
 /*
-- 
2.39.5 (Apple Git-154)



  [application/octet-stream] v3-0004-Use-appendStringInfoIdentifier-in-more-places.patch (35.6K, 5-v3-0004-Use-appendStringInfoIdentifier-in-more-places.patch)
  download | inline diff:
From bddf5fa9b910babc8209f788710955197dcfc72c Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Wed, 19 Nov 2025 15:55:23 +0800
Subject: [PATCH v3 4/4] Use appendStringInfoIdentifier() in more places.

Author: Chao Li <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=g2RVkxXB=JzWphgfg4QGV+spaA3PQ1rBM2iMehrVvjg@mail.gmail.com
---
 src/backend/catalog/namespace.c     |   6 +-
 src/backend/catalog/objectaddress.c |  64 +++----
 src/backend/utils/adt/ri_triggers.c |   8 +-
 src/backend/utils/adt/ruleutils.c   | 260 ++++++++++++----------------
 4 files changed, 147 insertions(+), 191 deletions(-)

diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index d23474da4fb..7c3b0bb7853 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -3699,14 +3699,14 @@ NameListToQuotedString(const List *names)
 {
 	StringInfoData string;
 	ListCell   *l;
+	const char *sep = "";
 
 	initStringInfo(&string);
 
 	foreach(l, names)
 	{
-		if (l != list_head(names))
-			appendStringInfoChar(&string, '.');
-		appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
+		appendStringInfoIdentifier(&string, sep, strVal(lfirst(l)), NULL);
+		sep = ".";
 	}
 
 	return string.data;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 70e05059f9f..fa8762db10d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -5004,8 +5004,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				if (OidIsValid(con->conrelid))
 				{
-					appendStringInfo(&buffer, "%s on ",
-									 quote_identifier(NameStr(con->conname)));
+					appendStringInfoIdentifier(&buffer, NULL, NameStr(con->conname), " on ");
 					getRelationIdentity(&buffer, con->conrelid, objname,
 										false);
 					if (objname)
@@ -5020,10 +5019,10 @@ getObjectIdentityParts(const ObjectAddress *object,
 					domain.objectId = con->contypid;
 					domain.objectSubId = 0;
 
-					appendStringInfo(&buffer, "%s on %s",
-									 quote_identifier(NameStr(con->conname)),
-									 getObjectIdentityParts(&domain, objname,
-															objargs, false));
+					appendStringInfoIdentifier(&buffer, NULL, NameStr(con->conname), " on ");
+					appendStringInfoString(&buffer,
+										   getObjectIdentityParts(&domain, objname,
+																  objargs, false));
 
 					if (objname)
 						*objargs = lappend(*objargs, pstrdup(NameStr(con->conname)));
@@ -5096,8 +5095,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 					break;
 				}
 				langForm = (Form_pg_language) GETSTRUCT(langTup);
-				appendStringInfoString(&buffer,
-									   quote_identifier(NameStr(langForm->lanname)));
+				appendStringInfoIdentifier(&buffer, NULL,
+										   NameStr(langForm->lanname), NULL);
 				if (objname)
 					*objname = list_make1(pstrdup(NameStr(langForm->lanname)));
 				ReleaseSysCache(langTup);
@@ -5155,10 +5154,11 @@ getObjectIdentityParts(const ObjectAddress *object,
 						 opcForm->opcmethod);
 				amForm = (Form_pg_am) GETSTRUCT(amTup);
 
-				appendStringInfo(&buffer, "%s USING %s",
-								 quote_qualified_identifier(schema,
-															NameStr(opcForm->opcname)),
-								 quote_identifier(NameStr(amForm->amname)));
+				appendStringInfoQualifiedIdentifier(&buffer, NULL,
+													schema, NameStr(opcForm->opcname),
+													" USING ");
+				appendStringInfoIdentifier(&buffer, NULL,
+										   NameStr(amForm->amname), NULL);
 				if (objname)
 					*objname = list_make3(pstrdup(NameStr(amForm->amname)),
 										  schema,
@@ -5186,7 +5186,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 							 object->objectId);
 					break;
 				}
-				appendStringInfoString(&buffer, quote_identifier(amname));
+				appendStringInfoIdentifier(&buffer, NULL, amname, NULL);
 				if (objname)
 					*objname = list_make1(amname);
 			}
@@ -5339,8 +5339,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				rule = (Form_pg_rewrite) GETSTRUCT(tup);
 
-				appendStringInfo(&buffer, "%s on ",
-								 quote_identifier(NameStr(rule->rulename)));
+				appendStringInfoIdentifier(&buffer, NULL, NameStr(rule->rulename), " on ");
 				getRelationIdentity(&buffer, rule->ev_class, objname, false);
 				if (objname)
 					*objname = lappend(*objname, pstrdup(NameStr(rule->rulename)));
@@ -5372,8 +5371,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				trig = (Form_pg_trigger) GETSTRUCT(tup);
 
-				appendStringInfo(&buffer, "%s on ",
-								 quote_identifier(NameStr(trig->tgname)));
+				appendStringInfoIdentifier(&buffer, NULL, NameStr(trig->tgname), " on ");
 				getRelationIdentity(&buffer, trig->tgrelid, objname, false);
 				if (objname)
 					*objname = lappend(*objname, pstrdup(NameStr(trig->tgname)));
@@ -5544,8 +5542,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 					break;
 				if (objname)
 					*objname = list_make1(username);
-				appendStringInfoString(&buffer,
-									   quote_identifier(username));
+				appendStringInfoIdentifier(&buffer, NULL, username, NULL);
 				break;
 			}
 
@@ -5606,8 +5603,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 				}
 				if (objname)
 					*objname = list_make1(datname);
-				appendStringInfoString(&buffer,
-									   quote_identifier(datname));
+				appendStringInfoIdentifier(&buffer, NULL, datname, NULL);
 				break;
 			}
 
@@ -5625,8 +5621,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 				}
 				if (objname)
 					*objname = list_make1(tblspace);
-				appendStringInfoString(&buffer,
-									   quote_identifier(tblspace));
+				appendStringInfoIdentifier(&buffer, NULL, tblspace, NULL);
 				break;
 			}
 
@@ -5638,7 +5633,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 													missing_ok);
 				if (fdw)
 				{
-					appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+					appendStringInfoIdentifier(&buffer, NULL, fdw->fdwname, NULL);
 					if (objname)
 						*objname = list_make1(pstrdup(fdw->fdwname));
 				}
@@ -5653,8 +5648,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 											   missing_ok);
 				if (srv)
 				{
-					appendStringInfoString(&buffer,
-										   quote_identifier(srv->servername));
+					appendStringInfoIdentifier(&buffer, NULL, srv->servername, NULL);
 					if (objname)
 						*objname = list_make1(pstrdup(srv->servername));
 				}
@@ -5695,9 +5689,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 					*objargs = list_make1(pstrdup(srv->servername));
 				}
 
-				appendStringInfo(&buffer, "%s on server %s",
-								 quote_identifier(usename),
-								 srv->servername);
+				appendStringInfoIdentifier(&buffer, NULL, usename, " on server ");
+				appendStringInfoString(&buffer, srv->servername);
 				break;
 			}
 
@@ -5800,7 +5793,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 							 object->objectId);
 					break;
 				}
-				appendStringInfoString(&buffer, quote_identifier(extname));
+				appendStringInfoIdentifier(&buffer, NULL, extname, NULL);
 				if (objname)
 					*objname = list_make1(extname);
 				break;
@@ -5823,7 +5816,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 				}
 				trigForm = (Form_pg_event_trigger) GETSTRUCT(tup);
 				evtname = pstrdup(NameStr(trigForm->evtname));
-				appendStringInfoString(&buffer, quote_identifier(evtname));
+				appendStringInfoIdentifier(&buffer, NULL, evtname, NULL);
 				if (objname)
 					*objname = list_make1(evtname);
 				ReleaseSysCache(tup);
@@ -5878,8 +5871,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				policy = (Form_pg_policy) GETSTRUCT(tup);
 
-				appendStringInfo(&buffer, "%s on ",
-								 quote_identifier(NameStr(policy->polname)));
+				appendStringInfoIdentifier(&buffer, NULL, NameStr(policy->polname), " on ");
 				getRelationIdentity(&buffer, policy->polrelid, objname, false);
 				if (objname)
 					*objname = lappend(*objname, pstrdup(NameStr(policy->polname)));
@@ -5895,8 +5887,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 				pubname = get_publication_name(object->objectId, missing_ok);
 				if (pubname)
 				{
-					appendStringInfoString(&buffer,
-										   quote_identifier(pubname));
+					appendStringInfoIdentifier(&buffer, NULL, pubname, NULL);
 					if (objname)
 						*objname = list_make1(pubname);
 				}
@@ -5963,8 +5954,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 				subname = get_subscription_name(object->objectId, missing_ok);
 				if (subname)
 				{
-					appendStringInfoString(&buffer,
-										   quote_identifier(subname));
+					appendStringInfoIdentifier(&buffer, NULL, subname, NULL);
 					if (objname)
 						*objname = list_make1(subname);
 				}
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index d676930aca2..5b1c6547519 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -825,9 +825,11 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 
 			/* Find the remaining history */
 			initStringInfo(&replacementsbuf);
-			appendStringInfoString(&replacementsbuf, "(SELECT pg_catalog.range_agg(r) FROM ");
 
-			appendStringInfoIdentifier(&replacementsbuf, "(SELECT y.", RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]), " r FROM ");
+			appendStringInfoIdentifier(&replacementsbuf,
+									   "(SELECT pg_catalog.range_agg(r) FROM (SELECT y.",
+									   RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]),
+									   " r FROM ");
 			appendRelationName(&replacementsbuf, pk_rel, pk_only, " y");
 
 			/* Restrict pk rows to what matches */
@@ -1821,8 +1823,6 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	appendRelationName(&querybuf, fk_rel, fk_only, " fk JOIN ");
 	appendRelationName(&querybuf, pk_rel, NULL, " pk ON");
 
-	/* strcpy(pkattname, "pk."); */
-	/* strcpy(fkattname, "fk."); */
 	sep = "(";
 	for (i = 0; i < riinfo->nkeys; i++)
 	{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index b212a28d9be..a83cac7f03a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -979,18 +979,17 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 		/* tgattr is first var-width field, so OK to access directly */
 		if (trigrec->tgattr.dim1 > 0)
 		{
-			int			i;
+			const char *sep = "";
 
 			appendStringInfoString(&buf, " OF ");
-			for (i = 0; i < trigrec->tgattr.dim1; i++)
+			for (int i = 0; i < trigrec->tgattr.dim1; i++)
 			{
 				char	   *attname;
 
-				if (i > 0)
-					appendStringInfoString(&buf, ", ");
 				attname = get_attname(trigrec->tgrelid,
 									  trigrec->tgattr.values[i], false);
-				appendStringInfoString(&buf, quote_identifier(attname));
+				appendStringInfoIdentifier(&buf, sep, attname, NULL);
+				sep = ", ";
 			}
 		}
 	}
@@ -1042,11 +1041,9 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 	{
 		appendStringInfoString(&buf, "REFERENCING ");
 		if (tgoldtable != NULL)
-			appendStringInfo(&buf, "OLD TABLE AS %s ",
-							 quote_identifier(tgoldtable));
+			appendStringInfoIdentifier(&buf, "OLD TABLE AS ", tgoldtable, " ");
 		if (tgnewtable != NULL)
-			appendStringInfo(&buf, "NEW TABLE AS %s ",
-							 quote_identifier(tgnewtable));
+			appendStringInfoIdentifier(&buf, "NEW TABLE AS ", tgnewtable, " ");
 	}
 
 	if (TRIGGER_FOR_ROW(trigrec->tgtype))
@@ -1387,8 +1384,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 							 generate_qualified_relation_name(indrelid),
 							 quote_identifier(NameStr(amrec->amname)));
 		else					/* currently, must be EXCLUDE constraint */
-			appendStringInfo(&buf, "EXCLUDE USING %s (",
-							 quote_identifier(NameStr(amrec->amname)));
+			appendStringInfoIdentifier(&buf, "EXCLUDE USING ", NameStr(amrec->amname), " (");
 	}
 
 	/*
@@ -1426,7 +1422,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 
 			attname = get_attname(indrelid, attnum, false);
 			if (!colno || colno == keyno + 1)
-				appendStringInfoString(&buf, quote_identifier(attname));
+				appendStringInfoIdentifier(&buf, NULL, attname, NULL);
 			get_atttypetypmodcoll(indrelid, attnum,
 								  &keycoltype, &keycoltypmod,
 								  &keycolcollation);
@@ -1536,8 +1532,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 			{
 				if (isConstraint)
 					appendStringInfoString(&buf, " USING INDEX");
-				appendStringInfo(&buf, " TABLESPACE %s",
-								 quote_identifier(get_tablespace_name(tblspc)));
+				appendStringInfoIdentifier(&buf, " TABLESPACE ",
+										   get_tablespace_name(tblspc), NULL);
 			}
 		}
 
@@ -1794,7 +1790,7 @@ pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
 
 		attname = get_attname(statextrec->stxrelid, attnum, false);
 
-		appendStringInfoString(&buf, quote_identifier(attname));
+		appendStringInfoIdentifier(&buf, NULL, attname, NULL);
 	}
 
 	context = deparse_context_for(get_relation_name(statextrec->stxrelid),
@@ -2038,7 +2034,7 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags,
 			int32		keycoltypmod;
 
 			attname = get_attname(relid, attnum, false);
-			appendStringInfoString(&buf, quote_identifier(attname));
+			appendStringInfoIdentifier(&buf, NULL, attname, NULL);
 			get_atttypetypmodcoll(relid, attnum,
 								  &keycoltype, &keycoltypmod,
 								  &keycolcollation);
@@ -2246,17 +2242,19 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 			 * we might need to let callers specify whether to put ONLY in the
 			 * command.
 			 */
-			appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
-							 generate_qualified_relation_name(conForm->conrelid),
-							 quote_identifier(NameStr(conForm->conname)));
+			appendStringInfo(&buf, "ALTER TABLE %s ",
+							 generate_qualified_relation_name(conForm->conrelid));
+			appendStringInfoIdentifier(&buf, "ADD CONSTRAINT ",
+									   NameStr(conForm->conname), " ");
 		}
 		else
 		{
 			/* Must be a domain constraint */
 			Assert(OidIsValid(conForm->contypid));
-			appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
-							 generate_qualified_type_name(conForm->contypid),
-							 quote_identifier(NameStr(conForm->conname)));
+			appendStringInfo(&buf, "ALTER DOMAIN %s ",
+							 generate_qualified_type_name(conForm->contypid));
+			appendStringInfoIdentifier(&buf, "ADD CONSTRAINT ",
+									   NameStr(conForm->conname), " ");
 		}
 	}
 
@@ -2423,6 +2421,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 					Datum	   *keys;
 					int			nKeys;
 					int			j;
+					const char *sep = "";
 
 					appendStringInfoString(&buf, " INCLUDE (");
 
@@ -2438,9 +2437,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
 						colName = get_attname(conForm->conrelid,
 											  DatumGetInt16(keys[j]), false);
-						if (j > keyatts)
-							appendStringInfoString(&buf, ", ");
-						appendStringInfoString(&buf, quote_identifier(colName));
+						appendStringInfoIdentifier(&buf, sep, colName, NULL);
+						sep = ", ";
 					}
 
 					appendStringInfoChar(&buf, ')');
@@ -2467,8 +2465,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 					 */
 					tblspc = get_rel_tablespace(indexId);
 					if (OidIsValid(tblspc))
-						appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
-										 quote_identifier(get_tablespace_name(tblspc)));
+						appendStringInfoIdentifier(&buf, " USING INDEX TABLESPACE ",
+												   get_tablespace_name(tblspc), NULL);
 				}
 
 				break;
@@ -2529,9 +2527,9 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
 					attnum = extractNotNullColumn(tup);
 
-					appendStringInfo(&buf, "NOT NULL %s",
-									 quote_identifier(get_attname(conForm->conrelid,
-																  attnum, false)));
+					appendStringInfoIdentifier(&buf, "NOT NULL ",
+											   get_attname(conForm->conrelid,
+														   attnum, false), NULL);
 					if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
 						appendStringInfoString(&buf, " NO INHERIT");
 				}
@@ -2635,11 +2633,14 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
 		colName = get_attname(relId, DatumGetInt16(keys[j]), false);
 
 		if (j == 0)
-			appendStringInfoString(buf, quote_identifier(colName));
+			appendStringInfoIdentifier(buf, NULL, colName, NULL);
 		else
-			appendStringInfo(buf, ", %s%s",
-							 (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
-							 quote_identifier(colName));
+		{
+			appendStringInfoString(buf, ", ");
+			appendStringInfoIdentifier(buf,
+									   (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
+									   colName, NULL);
+		}
 	}
 
 	return nKeys;
@@ -2975,8 +2976,8 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
 
 	print_function_trftypes(&buf, proctup);
 
-	appendStringInfo(&buf, " LANGUAGE %s\n",
-					 quote_identifier(get_language_name(proc->prolang, false)));
+	appendStringInfoIdentifier(&buf, " LANGUAGE ",
+							   get_language_name(proc->prolang, false), "\n");
 
 	/* Emit some miscellaneous options on one line */
 	oldlen = buf.len;
@@ -3075,8 +3076,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
 					continue;
 				*pos++ = '\0';
 
-				appendStringInfo(&buf, " SET %s TO ",
-								 quote_identifier(configitem));
+				appendStringInfoIdentifier(&buf, " SET ", configitem, " TO ");
 
 				/*
 				 * Variables that are marked GUC_LIST_QUOTE were already fully
@@ -3418,7 +3418,7 @@ print_function_arguments(StringInfo buf, HeapTuple proctup,
 
 		appendStringInfoString(buf, modename);
 		if (argname && argname[0])
-			appendStringInfo(buf, "%s ", quote_identifier(argname));
+			appendStringInfoIdentifier(buf, NULL, argname, " ");
 		appendStringInfoString(buf, format_type_be(argtype));
 		if (print_defaults && isinput && inputargno > nlackdefaults)
 		{
@@ -5402,8 +5402,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
 	/*
 	 * Build the rules definition text
 	 */
-	appendStringInfo(buf, "CREATE RULE %s AS",
-					 quote_identifier(rulename));
+	appendStringInfoIdentifier(buf, "CREATE RULE ", rulename, " AS");
 
 	if (prettyFlags & PRETTYFLAG_INDENT)
 		appendStringInfoString(buf, "\n    ON ");
@@ -5791,21 +5790,18 @@ get_with_clause(Query *query, deparse_context *context)
 		CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
 
 		appendStringInfoString(buf, sep);
-		appendStringInfoString(buf, quote_identifier(cte->ctename));
+		appendStringInfoIdentifier(buf, NULL, cte->ctename, NULL);
 		if (cte->aliascolnames)
 		{
-			bool		first = true;
 			ListCell   *col;
+			const char *colsep = "";
 
 			appendStringInfoChar(buf, '(');
 			foreach(col, cte->aliascolnames)
 			{
-				if (first)
-					first = false;
-				else
-					appendStringInfoString(buf, ", ");
-				appendStringInfoString(buf,
-									   quote_identifier(strVal(lfirst(col))));
+				appendStringInfoIdentifier(buf, colsep,
+										   strVal(lfirst(col)), NULL);
+				colsep = ", ";
 			}
 			appendStringInfoChar(buf, ')');
 		}
@@ -5834,43 +5830,35 @@ get_with_clause(Query *query, deparse_context *context)
 
 		if (cte->search_clause)
 		{
-			bool		first = true;
 			ListCell   *lc;
+			const char *colsep = "";
 
 			appendStringInfo(buf, " SEARCH %s FIRST BY ",
 							 cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
 
 			foreach(lc, cte->search_clause->search_col_list)
 			{
-				if (first)
-					first = false;
-				else
-					appendStringInfoString(buf, ", ");
-				appendStringInfoString(buf,
-									   quote_identifier(strVal(lfirst(lc))));
+				appendStringInfoIdentifier(buf, colsep, strVal(lfirst(lc)), NULL);
+				colsep = ", ";
 			}
 
-			appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
+			appendStringInfoIdentifier(buf, " SET ", cte->search_clause->search_seq_column, NULL);
 		}
 
 		if (cte->cycle_clause)
 		{
-			bool		first = true;
 			ListCell   *lc;
+			const char *sep = "";
 
 			appendStringInfoString(buf, " CYCLE ");
 
 			foreach(lc, cte->cycle_clause->cycle_col_list)
 			{
-				if (first)
-					first = false;
-				else
-					appendStringInfoString(buf, ", ");
-				appendStringInfoString(buf,
-									   quote_identifier(strVal(lfirst(lc))));
+				appendStringInfoIdentifier(buf, sep, strVal(lfirst(lc)), NULL);
+				sep = ", ";
 			}
 
-			appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
+			appendStringInfoIdentifier(buf, " SET ", cte->cycle_clause->cycle_mark_column, NULL);
 
 			{
 				Const	   *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
@@ -5886,7 +5874,7 @@ get_with_clause(Query *query, deparse_context *context)
 				}
 			}
 
-			appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
+			appendStringInfoIdentifier(buf, " USING ", cte->cycle_clause->cycle_path_column, NULL);
 		}
 
 		sep = ", ";
@@ -6022,9 +6010,7 @@ get_select_query_def(Query *query, deparse_context *context)
 					break;
 			}
 
-			appendStringInfo(buf, " OF %s",
-							 quote_identifier(get_rtable_name(rc->rti,
-															  context)));
+			appendStringInfoIdentifier(buf, " OF ", get_rtable_name(rc->rti, context), NULL);
 			if (rc->waitPolicy == LockWaitError)
 				appendStringInfoString(buf, " NOWAIT");
 			else if (rc->waitPolicy == LockWaitSkip)
@@ -6317,7 +6303,7 @@ get_target_list(List *targetList, deparse_context *context)
 		if (colname)			/* resname could be NULL */
 		{
 			if (attname == NULL || strcmp(attname, colname) != 0)
-				appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
+				appendStringInfoIdentifier(&targetbuf, " AS ", colname, NULL);
 		}
 
 		/* Restore context's output buffer */
@@ -6391,19 +6377,16 @@ get_returning_clause(Query *query, deparse_context *context)
 		/* Add WITH (OLD/NEW) options, if they're not the defaults */
 		if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
 		{
-			appendStringInfo(buf, " WITH (OLD AS %s",
-							 quote_identifier(query->returningOldAlias));
+			appendStringInfoIdentifier(buf, " WITH (OLD AS ", query->returningOldAlias, NULL);
 			have_with = true;
 		}
 		if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
 		{
 			if (have_with)
-				appendStringInfo(buf, ", NEW AS %s",
-								 quote_identifier(query->returningNewAlias));
+				appendStringInfoIdentifier(buf, ", NEW AS ", query->returningNewAlias, NULL);
 			else
 			{
-				appendStringInfo(buf, " WITH (NEW AS %s",
-								 quote_identifier(query->returningNewAlias));
+				appendStringInfoIdentifier(buf, " WITH (NEW AS ", query->returningNewAlias, NULL);
 				have_with = true;
 			}
 		}
@@ -6771,7 +6754,7 @@ get_rule_windowclause(Query *query, deparse_context *context)
 		else
 			appendStringInfoString(buf, sep);
 
-		appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
+		appendStringInfoIdentifier(buf, NULL, wc->name, " AS ");
 
 		get_rule_windowspec(wc, query->targetList, context);
 
@@ -6794,7 +6777,7 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
 	appendStringInfoChar(buf, '(');
 	if (wc->refname)
 	{
-		appendStringInfoString(buf, quote_identifier(wc->refname));
+		appendStringInfoIdentifier(buf, NULL, wc->refname, NULL);
 		needspace = true;
 	}
 	/* partition clauses are always inherited, so only print if no refname */
@@ -7021,10 +7004,8 @@ get_insert_query_def(Query *query, deparse_context *context)
 		 * Put out name of target column; look in the catalogs, not at
 		 * tle->resname, since resname will fail to track RENAME.
 		 */
-		appendStringInfoString(buf,
-							   quote_identifier(get_attname(rte->relid,
-															tle->resno,
-															false)));
+		appendStringInfoIdentifier(buf, NULL,
+								   get_attname(rte->relid, tle->resno, false), NULL);
 
 		/*
 		 * Print any indirection needed (subfields or subscripts), and strip
@@ -7117,8 +7098,7 @@ get_insert_query_def(Query *query, deparse_context *context)
 			if (!constraint)
 				elog(ERROR, "cache lookup failed for constraint %u",
 					 confl->constraint);
-			appendStringInfo(buf, " ON CONSTRAINT %s",
-							 quote_identifier(constraint));
+			appendStringInfoIdentifier(buf, " ON CONSTRAINT ", constraint, NULL);
 		}
 
 		if (confl->action == ONCONFLICT_NOTHING)
@@ -7320,10 +7300,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
 		 * Put out name of target column; look in the catalogs, not at
 		 * tle->resname, since resname will fail to track RENAME.
 		 */
-		appendStringInfoString(buf,
-							   quote_identifier(get_attname(rte->relid,
-															tle->resno,
-															false)));
+		appendStringInfoIdentifier(buf, NULL, get_attname(rte->relid, tle->resno, false), NULL);
 
 		/*
 		 * Print any indirection needed (subfields or subscripts), and strip
@@ -7511,10 +7488,10 @@ get_merge_query_def(Query *query, deparse_context *context)
 				appendStringInfoString(buf, sep);
 				sep = ", ";
 
-				appendStringInfoString(buf,
-									   quote_identifier(get_attname(rte->relid,
-																	tle->resno,
-																	false)));
+				appendStringInfoIdentifier(buf, NULL,
+										   get_attname(rte->relid,
+													   tle->resno,
+													   false), NULL);
 				strippedexprs = lappend(strippedexprs,
 										processIndirection((Node *) tle->expr,
 														   context));
@@ -7573,8 +7550,8 @@ get_utility_query_def(Query *query, deparse_context *context)
 
 		appendContextKeyword(context, "",
 							 0, PRETTYINDENT_STD, 1);
-		appendStringInfo(buf, "NOTIFY %s",
-						 quote_identifier(stmt->conditionname));
+		appendStringInfoIdentifier(buf, "NOTIFY ",
+								   stmt->conditionname, NULL);
 		if (stmt->payload)
 		{
 			appendStringInfoString(buf, ", ");
@@ -7865,11 +7842,11 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
 
 	if (refname && need_prefix)
 	{
-		appendStringInfoString(buf, quote_identifier(refname));
+		appendStringInfoIdentifier(buf, NULL, refname, NULL);
 		appendStringInfoChar(buf, '.');
 	}
 	if (attname)
-		appendStringInfoString(buf, quote_identifier(attname));
+		appendStringInfoIdentifier(buf, NULL, attname, NULL);
 	else
 	{
 		appendStringInfoChar(buf, '*');
@@ -8806,11 +8783,10 @@ get_parameter(Param *param, deparse_context *context)
 				}
 				if (should_qualify)
 				{
-					appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
-					appendStringInfoChar(context->buf, '.');
+					appendStringInfoIdentifier(context->buf, NULL, dpns->funcname, ".");
 				}
 
-				appendStringInfoString(context->buf, quote_identifier(argname));
+				appendStringInfoIdentifier(context->buf, NULL, argname, NULL);
 				return;
 			}
 		}
@@ -9391,7 +9367,7 @@ get_rule_expr(Node *node, deparse_context *context,
 			{
 				NamedArgExpr *na = (NamedArgExpr *) node;
 
-				appendStringInfo(buf, "%s => ", quote_identifier(na->name));
+				appendStringInfoIdentifier(buf, NULL, na->name, " => ");
 				get_rule_expr((Node *) na->arg, context, showimplicit);
 			}
 			break;
@@ -9679,7 +9655,7 @@ get_rule_expr(Node *node, deparse_context *context,
 				 */
 				fieldname = get_name_for_var_field((Var *) arg, fno,
 												   0, context);
-				appendStringInfo(buf, ".%s", quote_identifier(fieldname));
+				appendStringInfoIdentifier(buf, ".", fieldname, NULL);
 			}
 			break;
 
@@ -10131,8 +10107,8 @@ get_rule_expr(Node *node, deparse_context *context,
 				}
 				if (xexpr->name)
 				{
-					appendStringInfo(buf, "NAME %s",
-									 quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
+					appendStringInfoIdentifier(buf, "NAME ",
+											   map_xml_name_to_sql_identifier(xexpr->name), NULL);
 					needcomma = true;
 				}
 				if (xexpr->named_args)
@@ -10152,8 +10128,8 @@ get_rule_expr(Node *node, deparse_context *context,
 						if (needcomma)
 							appendStringInfoString(buf, ", ");
 						get_rule_expr((Node *) e, context, true);
-						appendStringInfo(buf, " AS %s",
-										 quote_identifier(map_xml_name_to_sql_identifier(argname)));
+						appendStringInfoIdentifier(buf, " AS ",
+												   map_xml_name_to_sql_identifier(argname), NULL);
 						needcomma = true;
 					}
 					if (xexpr->op != IS_XMLFOREST)
@@ -10369,8 +10345,8 @@ get_rule_expr(Node *node, deparse_context *context,
 				CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
 
 				if (cexpr->cursor_name)
-					appendStringInfo(buf, "CURRENT OF %s",
-									 quote_identifier(cexpr->cursor_name));
+					appendStringInfoIdentifier(buf, "CURRENT OF ",
+											   cexpr->cursor_name, NULL);
 				else
 					appendStringInfo(buf, "CURRENT OF $%d",
 									 cexpr->cursor_param);
@@ -10604,8 +10580,8 @@ get_rule_expr(Node *node, deparse_context *context,
 						needcomma = true;
 
 						get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
-						appendStringInfo(buf, " AS %s",
-										 quote_identifier(lfirst_node(String, lc1)->sval));
+						appendStringInfoIdentifier(buf, " AS ",
+												   lfirst_node(String, lc1)->sval, NULL);
 					}
 				}
 
@@ -11135,7 +11111,7 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
 			if (wc->winref == wfunc->winref)
 			{
 				if (wc->name)
-					appendStringInfoString(buf, quote_identifier(wc->name));
+					appendStringInfoIdentifier(buf, NULL, wc->name, NULL);
 				else
 					get_rule_windowspec(wc, context->targetList, context);
 				break;
@@ -11161,7 +11137,7 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
 
 				if (wagg->winref == wfunc->winref)
 				{
-					appendStringInfoString(buf, quote_identifier(wagg->winname));
+					appendStringInfoIdentifier(buf, NULL, wagg->winname, NULL);
 					break;
 				}
 			}
@@ -12001,8 +11977,8 @@ get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
 			if (ns_node != NULL)
 			{
 				get_rule_expr(expr, context, showimplicit);
-				appendStringInfo(buf, " AS %s",
-								 quote_identifier(strVal(ns_node)));
+				appendStringInfoIdentifier(buf, " AS ",
+										   strVal(ns_node), NULL);
 			}
 			else
 			{
@@ -12088,7 +12064,7 @@ get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
 		appendStringInfoChar(context->buf, ' ');
 		appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
 		get_const_expr(scan->path->value, context, -1);
-		appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
+		appendStringInfoIdentifier(context->buf, " AS ", scan->path->name, NULL);
 		get_json_table_columns(tf, scan, context, showimplicit);
 	}
 	else if (IsA(plan, JsonTableSiblingJoin))
@@ -12231,7 +12207,7 @@ get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
 
 	get_const_expr(root->path->value, context, -1);
 
-	appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
+	appendStringInfoIdentifier(buf, " AS ", root->path->name, NULL);
 
 	if (jexpr->passing_values)
 	{
@@ -12255,9 +12231,7 @@ get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
 			appendContextKeyword(context, "", 0, 0, 0);
 
 			get_rule_expr((Node *) lfirst(lc2), context, false);
-			appendStringInfo(buf, " AS %s",
-							 quote_identifier((lfirst_node(String, lc1))->sval)
-				);
+			appendStringInfoIdentifier(buf, " AS ", lfirst_node(String, lc1)->sval, NULL);
 		}
 
 		if (PRETTY_INDENT(context))
@@ -12533,7 +12507,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 				appendStringInfoChar(buf, ')');
 				break;
 			case RTE_CTE:
-				appendStringInfoString(buf, quote_identifier(rte->ctename));
+				appendStringInfoIdentifier(buf, NULL, rte->ctename, NULL);
 				break;
 			default:
 				elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
@@ -12620,7 +12594,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 		if (j->usingClause)
 		{
 			ListCell   *lc;
-			bool		first = true;
+			const char *sep = "";
 
 			appendStringInfoString(buf, " USING (");
 			/* Use the assigned names, not what's in usingClause */
@@ -12628,17 +12602,13 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 			{
 				char	   *colname = (char *) lfirst(lc);
 
-				if (first)
-					first = false;
-				else
-					appendStringInfoString(buf, ", ");
-				appendStringInfoString(buf, quote_identifier(colname));
+				appendStringInfoIdentifier(buf, sep, colname, NULL);
+				sep = ", ";
 			}
 			appendStringInfoChar(buf, ')');
 
 			if (j->join_using_alias)
-				appendStringInfo(buf, " AS %s",
-								 quote_identifier(j->join_using_alias->aliasname));
+				appendStringInfoIdentifier(buf, " AS ", j->join_using_alias->aliasname, NULL);
 		}
 		else if (j->quals)
 		{
@@ -12668,9 +12638,9 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 			 * subtleties we don't want.  However, we might print a different
 			 * alias name than was there originally.
 			 */
-			appendStringInfo(buf, " %s",
-							 quote_identifier(get_rtable_name(j->rtindex,
-															  context)));
+			appendStringInfoIdentifier(buf, " ",
+									   get_rtable_name(j->rtindex,
+													   context), NULL);
 			get_column_alias_list(colinfo, context);
 		}
 	}
@@ -12745,9 +12715,9 @@ get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
 	}
 
 	if (printalias)
-		appendStringInfo(context->buf, "%s%s",
-						 use_as ? " AS " : " ",
-						 quote_identifier(refname));
+		appendStringInfoIdentifier(context->buf,
+								   use_as ? " AS " : " ",
+								   refname, NULL);
 }
 
 /*
@@ -12777,7 +12747,7 @@ get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
 		}
 		else
 			appendStringInfoString(buf, ", ");
-		appendStringInfoString(buf, quote_identifier(colname));
+		appendStringInfoIdentifier(buf, NULL, colname, NULL);
 	}
 	if (!first)
 		appendStringInfoChar(buf, ')');
@@ -12910,13 +12880,11 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
 		/* Okay, we need the opclass name.  Do we need to qualify it? */
 		opcname = NameStr(opcrec->opcname);
 		if (OpclassIsVisible(opclass))
-			appendStringInfo(buf, " %s", quote_identifier(opcname));
+			appendStringInfoIdentifier(buf, " ", opcname, NULL);
 		else
 		{
 			nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
-			appendStringInfo(buf, " %s.%s",
-							 quote_identifier(nspname),
-							 quote_identifier(opcname));
+			appendStringInfoQualifiedIdentifier(buf, NULL, nspname, opcname, NULL);
 		}
 	}
 	ReleaseSysCache(ht_opc);
@@ -12980,7 +12948,7 @@ processIndirection(Node *node, deparse_context *context)
 			Assert(list_length(fstore->fieldnums) == 1);
 			fieldname = get_attname(typrelid,
 									linitial_int(fstore->fieldnums), false);
-			appendStringInfo(buf, ".%s", quote_identifier(fieldname));
+			appendStringInfoIdentifier(buf, ".", fieldname, NULL);
 
 			/*
 			 * We ignore arg since it should be an uninteresting reference to
@@ -13535,7 +13503,7 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
 	else
 	{
 		nspname = get_namespace_name_or_temp(operform->oprnamespace);
-		appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
+		appendStringInfoIdentifier(&buf, "OPERATOR(", nspname, ".");
 	}
 
 	appendStringInfoString(&buf, oprname);
@@ -13641,8 +13609,7 @@ add_cast_to(StringInfo buf, Oid typid)
 	typname = NameStr(typform->typname);
 	nspname = get_namespace_name_or_temp(typform->typnamespace);
 
-	appendStringInfo(buf, "::%s.%s",
-					 quote_identifier(nspname), quote_identifier(typname));
+	appendStringInfoQualifiedIdentifier(buf, "::", nspname, typname, NULL);
 
 	ReleaseSysCache(typetup);
 }
@@ -13739,12 +13706,12 @@ get_reloptions(StringInfo buf, Datum reloptions)
 {
 	Datum	   *options;
 	int			noptions;
-	int			i;
+	const char *sep = "";
 
 	deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
 							  &options, NULL, &noptions);
 
-	for (i = 0; i < noptions; i++)
+	for (int i = 0; i < noptions; i++)
 	{
 		char	   *option = TextDatumGetCString(options[i]);
 		char	   *name;
@@ -13765,9 +13732,8 @@ get_reloptions(StringInfo buf, Datum reloptions)
 		else
 			value = "";
 
-		if (i > 0)
-			appendStringInfoString(buf, ", ");
-		appendStringInfo(buf, "%s=", quote_identifier(name));
+		appendStringInfoIdentifier(buf, sep, name, "=");
+		sep = ", ";
 
 		/*
 		 * In general we need to quote the value; but to avoid unnecessary
-- 
2.39.5 (Apple Git-154)



  [application/octet-stream] v3-0002-Use-appendStringInfoIdentifier-throughout-ri_trig.patch (21.9K, 6-v3-0002-Use-appendStringInfoIdentifier-throughout-ri_trig.patch)
  download | inline diff:
From 8043be01f1883ff419c1a732be73cbe05021dbf3 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <[email protected]>
Date: Mon, 17 Nov 2025 12:09:12 +0800
Subject: [PATCH v3 2/4] Use appendStringInfoIdentifier() throughout
 ri_triggers.c

Replace most uses of quoteOneName() and manual stack buffers in
ri_triggers.c with appendStringInfoIdentifier() and related infrastructure.

This simplifies the construction of SQL queries generated by RI triggers and
eliminates the need for MAX_QUOTED_NAME_LEN / stack-allocated intermediate
buffers.  It also removes several code paths where identifiers were quoted
manually, making the quoting rules consistent with ruleutils.c and the GUC
quote_all_identifiers.

This commit also adjusts generate_operator_clause() to support prefixed
arguments and identifier quoting directly, reducing the number of places
where callers need to inject string concatenation logic.

No user-visible behavior change is intended; the generated SQL should be
equivalent to the previous version.

Author: Chao Li <[email protected]>
Discussion: https://postgr.es/m/CAEoWx2=g2RVkxXB=JzWphgfg4QGV+spaA3PQ1rBM2iMehrVvjg@mail.gmail.com
---
 src/backend/commands/matview.c      |   4 +-
 src/backend/utils/adt/ri_triggers.c | 188 +++++++++++++---------------
 src/backend/utils/adt/ruleutils.c   |  31 +++--
 src/include/utils/builtins.h        |   4 +-
 4 files changed, 115 insertions(+), 112 deletions(-)

diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index ef7c0d624f1..f1ad5a8e059 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -797,9 +797,9 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
 													 NameStr(attr->attname));
 
 				generate_operator_clause(&querybuf,
-										 leftop, attrtype,
+										 NULL, leftop, attrtype, false,
 										 op,
-										 rightop, attrtype);
+										 NULL, rightop, attrtype, false);
 
 				foundUniqueIndex = true;
 			}
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 9e13f526994..3cc4dc2b7c8 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -196,9 +196,9 @@ static void quoteOneName(char *buffer, const char *name);
 static void quoteRelationName(char *buffer, Relation rel);
 static void ri_GenerateQual(StringInfo buf,
 							const char *sep,
-							const char *leftop, Oid leftoptype,
+							const char *leftopprefix, const char *leftop, Oid leftoptype, bool quoteleftop,
 							Oid opoid,
-							const char *rightop, Oid rightoptype);
+							const char *rightopprefix, const char *rightop, Oid rightoptype, bool quoterightop);
 static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
 static int	ri_NullCheck(TupleDesc tupDesc, TupleTableSlot *slot,
 						 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
@@ -358,7 +358,7 @@ RI_FKey_check(TriggerData *trigdata)
 	{
 		StringInfoData querybuf;
 		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
-		char		attname[MAX_QUOTED_NAME_LEN];
+		const char *attname;
 		char		paramname[16];
 		const char *querysep;
 		Oid			queryoids[RI_MAX_NUMKEYS];
@@ -393,11 +393,11 @@ RI_FKey_check(TriggerData *trigdata)
 		quoteRelationName(pkrelname, pk_rel);
 		if (riinfo->hasperiod)
 		{
-			quoteOneName(attname,
-						 RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
+			attname = RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]);
+			appendStringInfoIdentifier(&querybuf, "SELECT 1 FROM (SELECT ", attname, " AS r FROM ");
 			appendStringInfo(&querybuf,
-							 "SELECT 1 FROM (SELECT %s AS r FROM %s%s x",
-							 attname, pk_only, pkrelname);
+							 "%s%s x",
+							 pk_only, pkrelname);
 		}
 		else
 		{
@@ -410,13 +410,12 @@ RI_FKey_check(TriggerData *trigdata)
 			Oid			pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
 			Oid			fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
 
-			quoteOneName(attname,
-						 RIAttName(pk_rel, riinfo->pk_attnums[i]));
+			attname = RIAttName(pk_rel, riinfo->pk_attnums[i]);
 			sprintf(paramname, "$%d", i + 1);
 			ri_GenerateQual(&querybuf, querysep,
-							attname, pk_type,
+							NULL, attname, pk_type, true,
 							riinfo->pf_eq_oprs[i],
-							paramname, fk_type);
+							NULL, paramname, fk_type, false);
 			querysep = "AND";
 			queryoids[i] = fk_type;
 		}
@@ -428,9 +427,9 @@ RI_FKey_check(TriggerData *trigdata)
 			appendStringInfoString(&querybuf, ") x1 HAVING ");
 			sprintf(paramname, "$%d", riinfo->nkeys);
 			ri_GenerateQual(&querybuf, "",
-							paramname, fk_type,
+							NULL, paramname, fk_type, false,
 							riinfo->agged_period_contained_by_oper,
-							"pg_catalog.range_agg", ANYMULTIRANGEOID);
+							NULL, "pg_catalog.range_agg", ANYMULTIRANGEOID, false);
 			appendStringInfoString(&querybuf, "(x1.r)");
 		}
 
@@ -528,7 +527,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 	{
 		StringInfoData querybuf;
 		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
-		char		attname[MAX_QUOTED_NAME_LEN];
+		const char *attname;
 		char		paramname[16];
 		const char *querysep;
 		const char *pk_only;
@@ -563,11 +562,11 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 		quoteRelationName(pkrelname, pk_rel);
 		if (riinfo->hasperiod)
 		{
-			quoteOneName(attname, RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
-
+			attname = RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]);
+			appendStringInfoIdentifier(&querybuf, "SELECT 1 FROM (SELECT ", attname, " AS r FROM ");
 			appendStringInfo(&querybuf,
-							 "SELECT 1 FROM (SELECT %s AS r FROM %s%s x",
-							 attname, pk_only, pkrelname);
+							 "%s%s x",
+							 pk_only, pkrelname);
 		}
 		else
 		{
@@ -579,13 +578,12 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 		{
 			Oid			pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
 
-			quoteOneName(attname,
-						 RIAttName(pk_rel, riinfo->pk_attnums[i]));
+			attname = RIAttName(pk_rel, riinfo->pk_attnums[i]);
 			sprintf(paramname, "$%d", i + 1);
 			ri_GenerateQual(&querybuf, querysep,
-							attname, pk_type,
+							NULL, attname, pk_type, true,
 							riinfo->pp_eq_oprs[i],
-							paramname, pk_type);
+							NULL, paramname, pk_type, false);
 			querysep = "AND";
 			queryoids[i] = pk_type;
 		}
@@ -597,9 +595,9 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 			appendStringInfoString(&querybuf, ") x1 HAVING ");
 			sprintf(paramname, "$%d", riinfo->nkeys);
 			ri_GenerateQual(&querybuf, "",
-							paramname, fk_type,
+							NULL, paramname, fk_type, false,
 							riinfo->agged_period_contained_by_oper,
-							"pg_catalog.range_agg", ANYMULTIRANGEOID);
+							NULL, "pg_catalog.range_agg", ANYMULTIRANGEOID, false);
 			appendStringInfoString(&querybuf, "(x1.r)");
 		}
 
@@ -757,7 +755,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 		StringInfoData querybuf;
 		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
 		char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
-		char		attname[MAX_QUOTED_NAME_LEN];
+		const char *attname;
 		char		periodattname[MAX_QUOTED_NAME_LEN];
 		char		paramname[16];
 		const char *querysep;
@@ -784,13 +782,12 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 			Oid			pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
 			Oid			fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
 
-			quoteOneName(attname,
-						 RIAttName(fk_rel, riinfo->fk_attnums[i]));
+			attname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
 			sprintf(paramname, "$%d", i + 1);
 			ri_GenerateQual(&querybuf, querysep,
-							paramname, pk_type,
+							NULL, paramname, pk_type, false,
 							riinfo->pf_eq_oprs[i],
-							attname, fk_type);
+							NULL, attname, fk_type, true);
 			querysep = "AND";
 			queryoids[i] = pk_type;
 		}
@@ -828,7 +825,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 			char	   *pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 				"" : "ONLY ";
 
-			quoteOneName(attname, RIAttName(fk_rel, riinfo->fk_attnums[riinfo->nkeys - 1]));
+			attname = RIAttName(fk_rel, riinfo->fk_attnums[riinfo->nkeys - 1]);
 			sprintf(paramname, "$%d", riinfo->nkeys);
 
 			appendStringInfoString(&querybuf, " AND NOT coalesce(");
@@ -837,9 +834,9 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 			initStringInfo(&intersectbuf);
 			appendStringInfoChar(&intersectbuf, '(');
 			ri_GenerateQual(&intersectbuf, "",
-							attname, fk_period_type,
+							NULL, attname, fk_period_type, true,
 							riinfo->period_intersect_oper,
-							paramname, pk_period_type);
+							NULL, paramname, pk_period_type, false);
 			appendStringInfoChar(&intersectbuf, ')');
 
 			/* Find the remaining history */
@@ -857,22 +854,21 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
 			{
 				Oid			pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
 
-				quoteOneName(attname,
-							 RIAttName(pk_rel, riinfo->pk_attnums[i]));
+				attname = RIAttName(pk_rel, riinfo->pk_attnums[i]);
 				sprintf(paramname, "$%d", i + 1);
 				ri_GenerateQual(&replacementsbuf, querysep,
-								paramname, pk_type,
+								NULL, paramname, pk_type, false,
 								riinfo->pp_eq_oprs[i],
-								attname, pk_type);
+								NULL, attname, pk_type, true);
 				querysep = "AND";
 				queryoids[i] = pk_type;
 			}
 			appendStringInfoString(&replacementsbuf, " FOR KEY SHARE OF y) y2)");
 
 			ri_GenerateQual(&querybuf, "",
-							intersectbuf.data, fk_period_type,
+							NULL, intersectbuf.data, fk_period_type, false,
 							riinfo->agged_period_contained_by_oper,
-							replacementsbuf.data, ANYMULTIRANGEOID);
+							NULL, replacementsbuf.data, ANYMULTIRANGEOID, false);
 			/* end of coalesce: */
 			appendStringInfoString(&querybuf, ", false)");
 		}
@@ -944,7 +940,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 	{
 		StringInfoData querybuf;
 		char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
-		char		attname[MAX_QUOTED_NAME_LEN];
+		const char *attname;
 		char		paramname[16];
 		const char *querysep;
 		Oid			queryoids[RI_MAX_NUMKEYS];
@@ -969,13 +965,12 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			Oid			pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
 			Oid			fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
 
-			quoteOneName(attname,
-						 RIAttName(fk_rel, riinfo->fk_attnums[i]));
+			attname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
 			sprintf(paramname, "$%d", i + 1);
 			ri_GenerateQual(&querybuf, querysep,
-							paramname, pk_type,
+							NULL, paramname, pk_type, false,
 							riinfo->pf_eq_oprs[i],
-							attname, fk_type);
+							NULL, attname, fk_type, true);
 			querysep = "AND";
 			queryoids[i] = pk_type;
 		}
@@ -1050,7 +1045,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 		StringInfoData querybuf;
 		StringInfoData qualbuf;
 		char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
-		char		attname[MAX_QUOTED_NAME_LEN];
+		const char *attname;
 		char		paramname[16];
 		const char *querysep;
 		const char *qualsep;
@@ -1081,16 +1076,15 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			Oid			pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
 			Oid			fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
 
-			quoteOneName(attname,
-						 RIAttName(fk_rel, riinfo->fk_attnums[i]));
-			appendStringInfo(&querybuf,
-							 "%s %s = $%d",
-							 querysep, attname, i + 1);
+			attname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
+			appendStringInfoString(&querybuf, querysep);
+			appendStringInfoIdentifier(&querybuf, " ", attname, " = ");
+			appendStringInfo(&querybuf, "$%d", i + 1);
 			sprintf(paramname, "$%d", j + 1);
 			ri_GenerateQual(&qualbuf, qualsep,
-							paramname, pk_type,
+							NULL, paramname, pk_type, false,
 							riinfo->pf_eq_oprs[i],
-							attname, fk_type);
+							NULL, attname, fk_type, true);
 			querysep = ",";
 			qualsep = "AND";
 			queryoids[i] = pk_type;
@@ -1239,7 +1233,7 @@ ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
 	{
 		StringInfoData querybuf;
 		char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
-		char		attname[MAX_QUOTED_NAME_LEN];
+		const char *attname;
 		char		paramname[16];
 		const char *querysep;
 		const char *qualsep;
@@ -1297,11 +1291,10 @@ ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
 		querysep = "";
 		for (int i = 0; i < num_cols_to_set; i++)
 		{
-			quoteOneName(attname, RIAttName(fk_rel, set_cols[i]));
-			appendStringInfo(&querybuf,
-							 "%s %s = %s",
-							 querysep, attname,
-							 is_set_null ? "NULL" : "DEFAULT");
+			attname = RIAttName(fk_rel, set_cols[i]);
+			appendStringInfoString(&querybuf, querysep);
+			appendStringInfoIdentifier(&querybuf, " ", attname, " = ");
+			appendStringInfoString(&querybuf, is_set_null ? "NULL" : "DEFAULT");
 			querysep = ",";
 		}
 
@@ -1314,14 +1307,13 @@ ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
 			Oid			pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
 			Oid			fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
 
-			quoteOneName(attname,
-						 RIAttName(fk_rel, riinfo->fk_attnums[i]));
+			attname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
 
 			sprintf(paramname, "$%d", i + 1);
 			ri_GenerateQual(&querybuf, qualsep,
-							paramname, pk_type,
+							NULL, paramname, pk_type, false,
 							riinfo->pf_eq_oprs[i],
-							attname, fk_type);
+							NULL, attname, fk_type, true);
 			qualsep = "AND";
 			queryoids[i] = pk_type;
 		}
@@ -1519,8 +1511,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	StringInfoData querybuf;
 	char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
 	char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
-	char		pkattname[MAX_QUOTED_NAME_LEN + 3];
-	char		fkattname[MAX_QUOTED_NAME_LEN + 3];
+	const char *pkattname;
+	const char *fkattname;
 	RangeTblEntry *rte;
 	RTEPermissionInfo *pk_perminfo;
 	RTEPermissionInfo *fk_perminfo;
@@ -1615,9 +1607,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	sep = "";
 	for (int i = 0; i < riinfo->nkeys; i++)
 	{
-		quoteOneName(fkattname,
-					 RIAttName(fk_rel, riinfo->fk_attnums[i]));
-		appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+		fkattname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
+		appendStringInfoString(&querybuf, sep);
+		appendStringInfoIdentifier(&querybuf, "fk.", fkattname, NULL);
 		sep = ", ";
 	}
 
@@ -1631,8 +1623,6 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 					 " FROM %s%s fk LEFT OUTER JOIN %s%s pk ON",
 					 fk_only, fkrelname, pk_only, pkrelname);
 
-	strcpy(pkattname, "pk.");
-	strcpy(fkattname, "fk.");
 	sep = "(";
 	for (int i = 0; i < riinfo->nkeys; i++)
 	{
@@ -1641,14 +1631,12 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 		Oid			pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
 		Oid			fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
 
-		quoteOneName(pkattname + 3,
-					 RIAttName(pk_rel, riinfo->pk_attnums[i]));
-		quoteOneName(fkattname + 3,
-					 RIAttName(fk_rel, riinfo->fk_attnums[i]));
+		pkattname = RIAttName(pk_rel, riinfo->pk_attnums[i]);
+		fkattname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
 		ri_GenerateQual(&querybuf, sep,
-						pkattname, pk_type,
+						"pk.", pkattname, pk_type, true,
 						riinfo->pf_eq_oprs[i],
-						fkattname, fk_type);
+						"fk.", fkattname, fk_type, true);
 		if (pk_coll != fk_coll)
 			ri_GenerateQualCollation(&querybuf, pk_coll);
 		sep = "AND";
@@ -1658,16 +1646,15 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	 * It's sufficient to test any one pk attribute for null to detect a join
 	 * failure.
 	 */
-	quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
-	appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
+	pkattname = RIAttName(pk_rel, riinfo->pk_attnums[0]);
+	appendStringInfoIdentifier(&querybuf, ") WHERE pk.", pkattname, " IS NULL AND (");
 
 	sep = "";
 	for (int i = 0; i < riinfo->nkeys; i++)
 	{
-		quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
-		appendStringInfo(&querybuf,
-						 "%sfk.%s IS NOT NULL",
-						 sep, fkattname);
+		fkattname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
+		appendStringInfoString(&querybuf, sep);
+		appendStringInfoIdentifier(&querybuf, "fk.", fkattname, " IS NOT NULL");
 		switch (riinfo->confmatchtype)
 		{
 			case FKCONSTR_MATCH_SIMPLE:
@@ -1814,8 +1801,8 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	char	   *constraintDef;
 	char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
 	char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
-	char		pkattname[MAX_QUOTED_NAME_LEN + 3];
-	char		fkattname[MAX_QUOTED_NAME_LEN + 3];
+	const char *pkattname;
+	const char *fkattname;
 	const char *sep;
 	const char *fk_only;
 	int			save_nestlevel;
@@ -1852,9 +1839,9 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	sep = "";
 	for (i = 0; i < riinfo->nkeys; i++)
 	{
-		quoteOneName(fkattname,
-					 RIAttName(fk_rel, riinfo->fk_attnums[i]));
-		appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+		fkattname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
+		appendStringInfoString(&querybuf, sep);
+		appendStringInfoIdentifier(&querybuf, "fk.", fkattname, NULL);
 		sep = ", ";
 	}
 
@@ -1865,8 +1852,8 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	appendStringInfo(&querybuf,
 					 " FROM %s%s fk JOIN %s pk ON",
 					 fk_only, fkrelname, pkrelname);
-	strcpy(pkattname, "pk.");
-	strcpy(fkattname, "fk.");
+	/* strcpy(pkattname, "pk."); */
+	/* strcpy(fkattname, "fk."); */
 	sep = "(";
 	for (i = 0; i < riinfo->nkeys; i++)
 	{
@@ -1875,14 +1862,12 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 		Oid			pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
 		Oid			fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
 
-		quoteOneName(pkattname + 3,
-					 RIAttName(pk_rel, riinfo->pk_attnums[i]));
-		quoteOneName(fkattname + 3,
-					 RIAttName(fk_rel, riinfo->fk_attnums[i]));
+		pkattname = RIAttName(pk_rel, riinfo->pk_attnums[i]);
+		fkattname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
 		ri_GenerateQual(&querybuf, sep,
-						pkattname, pk_type,
+						"pk.", pkattname, pk_type, true,
 						riinfo->pf_eq_oprs[i],
-						fkattname, fk_type);
+						"fk.", fkattname, fk_type, true);
 		if (pk_coll != fk_coll)
 			ri_GenerateQualCollation(&querybuf, pk_coll);
 		sep = "AND";
@@ -1903,10 +1888,9 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	sep = "";
 	for (i = 0; i < riinfo->nkeys; i++)
 	{
-		quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
-		appendStringInfo(&querybuf,
-						 "%sfk.%s IS NOT NULL",
-						 sep, fkattname);
+		fkattname = RIAttName(fk_rel, riinfo->fk_attnums[i]);
+		appendStringInfoString(&querybuf, sep);
+		appendStringInfoIdentifier(&querybuf, "fk.", fkattname, " IS NOT NULL");
 		switch (riinfo->confmatchtype)
 		{
 			case FKCONSTR_MATCH_SIMPLE:
@@ -2064,13 +2048,15 @@ quoteRelationName(char *buffer, Relation rel)
 static void
 ri_GenerateQual(StringInfo buf,
 				const char *sep,
-				const char *leftop, Oid leftoptype,
+				const char *leftopprefix, const char *leftop, Oid leftoptype, bool quoteleftop,
 				Oid opoid,
-				const char *rightop, Oid rightoptype)
+				const char *rightopprefix, const char *rightop, Oid rightoptype, bool quoterightop)
 {
 	appendStringInfo(buf, " %s ", sep);
-	generate_operator_clause(buf, leftop, leftoptype, opoid,
-							 rightop, rightoptype);
+	generate_operator_clause(buf,
+							 leftopprefix, leftop, leftoptype, quoteleftop,
+							 opoid,
+							 rightopprefix, rightop, rightoptype, quoterightop);
 }
 
 /*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 1d767deb6c4..b212a28d9be 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -13570,9 +13570,9 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
  */
 void
 generate_operator_clause(StringInfo buf,
-						 const char *leftop, Oid leftoptype,
+						 const char *leftopprefix, const char *leftop, Oid leftoptype, bool quoteleftop,
 						 Oid opoid,
-						 const char *rightop, Oid rightoptype)
+						 const char *rightopprefix, const char *rightop, Oid rightoptype, bool quoterightop)
 {
 	HeapTuple	opertup;
 	Form_pg_operator operform;
@@ -13585,15 +13585,32 @@ generate_operator_clause(StringInfo buf,
 	operform = (Form_pg_operator) GETSTRUCT(opertup);
 	Assert(operform->oprkind == 'b');
 	oprname = NameStr(operform->oprname);
-
 	nspname = get_namespace_name(operform->oprnamespace);
 
-	appendStringInfoString(buf, leftop);
+	if (quoteleftop)
+		appendStringInfoIdentifier(buf, leftopprefix, leftop, NULL);
+	else
+	{
+		if (leftopprefix)
+			appendStringInfoString(buf, leftopprefix);
+		appendStringInfoString(buf, leftop);
+	}
+
 	if (leftoptype != operform->oprleft)
 		add_cast_to(buf, operform->oprleft);
-	appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
-	appendStringInfoString(buf, oprname);
-	appendStringInfo(buf, ") %s", rightop);
+
+	appendStringInfoIdentifier(buf, " OPERATOR(", nspname, ".");
+	appendStringInfo(buf, "%s) ", oprname);
+
+	if (quoterightop)
+		appendStringInfoIdentifier(buf, rightopprefix, rightop, NULL);
+	else
+	{
+		if (rightopprefix)
+			appendStringInfoString(buf, rightopprefix);
+		appendStringInfoString(buf, rightop);
+	}
+
 	if (rightoptype != operform->oprright)
 		add_cast_to(buf, operform->oprright);
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index ff4ba7265c2..4bd04e1a57a 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -81,9 +81,9 @@ extern const char *quote_identifier(const char *ident);
 extern char *quote_qualified_identifier(const char *qualifier,
 										const char *ident);
 extern void generate_operator_clause(StringInfo buf,
-									 const char *leftop, Oid leftoptype,
+									 const char *leftopprefix, const char *leftop, Oid leftoptype, bool quoteleftop,
 									 Oid opoid,
-									 const char *rightop, Oid rightoptype);
+									 const char *rightopprefix, const char *rightop, Oid rightoptype, bool quoterightop);
 extern void appendStringInfoIdentifier(StringInfo str, const char *prefix, const char *ident, const char *suffix);
 extern void appendStringInfoQualifiedIdentifier(StringInfo str,
 												const char *prefix,
-- 
2.39.5 (Apple Git-154)



view thread (8+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected]
  Subject: Re:  Re: quoteOneName() inconsistency with quote_all_identifiers — replacement API proposed
  In-Reply-To: <CAEoWx2nPbikvjp0JBsHzbpE=u7aMkiVy-WCmPim2iX5hDF9fBw@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox