From d6e503a493d2a54221531654d48a62fddc8ae9a2 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Wed, 9 Jun 2010 02:39:34 +0000
Subject: [PATCH] Attempt to fix EXPLAIN (FORMAT YAML) quoting to behave
 sanely.

The previous code failed to quote in many cases where quoting was necessary -
YAML has loads of special characters, including -:[]{},"'|*& - so quote much
more aggressively, and only refrain from quoting things where it seems fairly
clear that it isn't necessary.

Per report from Dean Rasheed.
---
 src/backend/commands/explain.c | 44 +++++++++++++++++++++++++++-------
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 2409a01e2dd..d6a0f65acff 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.204 2010/02/26 02:00:39 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.205 2010/06/09 02:39:34 rhaas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2152,22 +2152,48 @@ escape_json(StringInfo buf, const char *str)
 }
 
 /*
- * YAML is a superset of JSON: if we find quotable characters, we call
- * escape_json.  If not, we emit the property unquoted for better readability.
+ * YAML is a superset of JSON, so we can use JSON escaping when escaping is
+ * needed.  However, some things that need to be quoted in JSON don't require
+ * quoting in YAML, and we prefer not to quote unnecessarily, to improve
+ * readability.
+ *
+ * Unfortunately, the YAML quoting rules are ridiculously complicated -- as
+ * documented in sections 5.3 and 7.3.3 of http://yaml.org/spec/1.2/spec.html
+ * -- and it doesn't seem worth expending a large amount of energy to avoid
+ * all unnecessary quoting, so we just do something (sort of) simple: we quote
+ * any string which is empty; any string which contains characters other than
+ * alphanumerics, period, underscore, or space; or begins or ends with a
+ * space.  The exception for period is mostly so that floating-point numbers
+ * (e.g., cost values) won't be quoted.
  */
 static void
 escape_yaml(StringInfo buf, const char *str)
 {
-	const char *p;
+	bool		needs_quoting = false;
 
-	for (p = str; *p; p++)
+#define is_safe_yaml(x) \
+	(isalnum(((unsigned char) x)) || (x) == '.' || (x) == '_')
+
+	if (!is_safe_yaml(str[0]))
+		needs_quoting = true;
+	else
 	{
-		if ((unsigned char) *p < ' ' || strchr("\"\\\b\f\n\r\t", *p))
+		const char *p;
+
+		for (p = str; *p; p++)
 		{
-			escape_json(buf, str);
-			return;
+			if (*p != ' ' && !is_safe_yaml(*p))
+			{
+				needs_quoting = true;
+				break;
+			}
 		}
+		if (!*p && p[-1] == ' ')
+			needs_quoting = true;
 	}
 
-	appendStringInfo(buf, "%s", str);
+	if (needs_quoting)
+		escape_json(buf, str);
+	else
+		appendStringInfoString(buf, str);
 }
-- 
GitLab