From 2f582f76b1945929ff07116cd4639747ce9bb8a1 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Sun, 19 Feb 2012 11:43:46 -0500
Subject: [PATCH] Improve pretty printing of viewdefs.

Some line feeds are added to target lists and from lists to make
them more readable. By default they wrap at 80 columns if possible,
but the wrap column is also selectable - if 0 it wraps after every
item.

Andrew Dunstan, reviewed by Hitoshi Harada.
---
 doc/src/sgml/func.sgml                     |  12 +-
 src/backend/utils/adt/ruleutils.c          | 137 ++++++++++++++++++++-
 src/include/catalog/catversion.h           |   2 +-
 src/include/catalog/pg_proc.h              |   2 +
 src/include/utils/builtins.h               |   1 +
 src/test/regress/expected/polymorphism.out |   4 +-
 src/test/regress/expected/rules.out        |  33 +++++
 src/test/regress/expected/with.out         |  25 ++--
 src/test/regress/sql/rules.sql             |   6 +
 9 files changed, 204 insertions(+), 18 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 236a60a688e..e8e637bf31b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13828,7 +13828,8 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
       <row>
        <entry><literal><function>pg_get_viewdef(<parameter>view_name</parameter>, <parameter>pretty_bool</>)</function></literal></entry>
        <entry><type>text</type></entry>
-       <entry>get underlying <command>SELECT</command> command for view (<emphasis>deprecated</emphasis>)</entry>
+       <entry>get underlying <command>SELECT</command> command for view, 
+              lines with fields are wrapped to 80 columns if pretty_bool is true (<emphasis>deprecated</emphasis>)</entry>
       </row>
       <row>
        <entry><literal><function>pg_get_viewdef(<parameter>view_oid</parameter>)</function></literal></entry>
@@ -13838,7 +13839,14 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
       <row>
        <entry><literal><function>pg_get_viewdef(<parameter>view_oid</parameter>, <parameter>pretty_bool</>)</function></literal></entry>
        <entry><type>text</type></entry>
-       <entry>get underlying <command>SELECT</command> command for view</entry>
+       <entry>get underlying <command>SELECT</command> command for view, 
+              lines with fields are wrapped to 80 columns if pretty_bool is true</entry>
+      </row>
+      <row>
+       <entry><literal><function>pg_get_viewdef(<parameter>view_oid</parameter>, <parameter>wrap_int</>)</function></literal></entry>
+       <entry><type>text</type></entry>
+       <entry>get underlying <command>SELECT</command> command for view, 
+              wrapping lines with fields as specified, pretty printing is implied</entry>
       </row>
       <row>
        <entry><literal><function>pg_options_to_table(<parameter>reloptions</parameter>)</function></literal></entry>
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9ad54c5decb..64ba8ec8917 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -73,6 +73,8 @@
 #define PRETTYFLAG_PAREN		1
 #define PRETTYFLAG_INDENT		2
 
+#define PRETTY_WRAP_DEFAULT     79
+
 /* macro to test if pretty action needed */
 #define PRETTY_PAREN(context)	((context)->prettyFlags & PRETTYFLAG_PAREN)
 #define PRETTY_INDENT(context)	((context)->prettyFlags & PRETTYFLAG_INDENT)
@@ -136,6 +138,7 @@ static SPIPlanPtr plan_getrulebyoid = NULL;
 static const char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
 static SPIPlanPtr plan_getviewrule = NULL;
 static const char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
+static int pretty_wrap = PRETTY_WRAP_DEFAULT;
 
 /* GUC parameters */
 bool		quote_all_identifiers = false;
@@ -380,6 +383,23 @@ pg_get_viewdef_ext(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags)));
 }
 
+Datum
+pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
+{
+	/* By OID */
+	Oid			viewoid = PG_GETARG_OID(0);
+	int		    wrap = PG_GETARG_INT32(1);
+	int			prettyFlags;
+	char       *result;
+
+	/* calling this implies we want pretty printing */
+	prettyFlags = PRETTYFLAG_PAREN | PRETTYFLAG_INDENT;
+	pretty_wrap = wrap;
+	result = pg_get_viewdef_worker(viewoid, prettyFlags);
+	pretty_wrap = PRETTY_WRAP_DEFAULT;
+	PG_RETURN_TEXT_P(string_to_text(result));
+}
+
 Datum
 pg_get_viewdef_name(PG_FUNCTION_ARGS)
 {
@@ -3013,6 +3033,7 @@ get_target_list(List *targetList, deparse_context *context,
 	char	   *sep;
 	int			colno;
 	ListCell   *l;
+	bool        last_was_multiline = false;
 
 	sep = " ";
 	colno = 0;
@@ -3021,6 +3042,10 @@ get_target_list(List *targetList, deparse_context *context,
 		TargetEntry *tle = (TargetEntry *) lfirst(l);
 		char	   *colname;
 		char	   *attname;
+		StringInfoData targetbuf;
+		int         leading_nl_pos =  -1;
+		char       *trailing_nl;
+		int         pos;
 
 		if (tle->resjunk)
 			continue;			/* ignore junk entries */
@@ -3029,6 +3054,15 @@ get_target_list(List *targetList, deparse_context *context,
 		sep = ", ";
 		colno++;
 
+		/*
+		 * Put the new field spec into targetbuf so we can
+		 * decide after we've got it whether or not it needs
+		 * to go on a new line.
+		 */
+
+		initStringInfo(&targetbuf);
+		context->buf = &targetbuf;
+
 		/*
 		 * We special-case Var nodes rather than using get_rule_expr. This is
 		 * needed because get_rule_expr will display a whole-row Var as
@@ -3063,8 +3097,66 @@ get_target_list(List *targetList, deparse_context *context,
 		if (colname)			/* resname could be NULL */
 		{
 			if (attname == NULL || strcmp(attname, colname) != 0)
-				appendStringInfo(buf, " AS %s", quote_identifier(colname));
+				appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
+		}
+
+		/* Restore context buffer */
+
+		context->buf = buf;
+
+		/* Does the new field start with whitespace plus a new line? */
+
+		for (pos=0; pos < targetbuf.len; pos++)
+		{
+			if (targetbuf.data[pos] == '\n')
+			{
+				leading_nl_pos = pos;
+				break;
+			}
+			if (targetbuf.data[pos] > ' ')
+				break;
+		}
+
+		/* Locate the start of the current  line in the buffer */
+
+		trailing_nl = (strrchr(buf->data,'\n'));
+		if (trailing_nl == NULL)
+			trailing_nl = buf->data;
+		else 
+			trailing_nl++;
+
+		/*
+		 * If the field we're adding is the first in the list, or it already 
+		 * has a leading newline, or wrap mode is disabled (pretty_wrap < 0), 
+		 * don't add anything.
+		 * Otherwise, add a newline, plus some  indentation, if either the 
+		 * new field would cause an overflow or the last field used more than
+		 * one line.
+		 */
+
+		if (colno > 1 &&
+			leading_nl_pos == -1 && 
+			pretty_wrap >= 0 &&
+			((strlen(trailing_nl) + strlen(targetbuf.data) > pretty_wrap) ||
+			 last_was_multiline))
+		{
+			appendContextKeyword(context, "", -PRETTYINDENT_STD, 
+								 PRETTYINDENT_STD, PRETTYINDENT_VAR);
 		}
+
+		/* Add the new field */
+
+		appendStringInfoString(buf, targetbuf.data);
+
+
+		/* Keep track of this field's status for next iteration */
+
+		last_was_multiline = 
+			(strchr(targetbuf.data + leading_nl_pos + 1,'\n') != NULL);
+
+		/* cleanup */
+
+		pfree (targetbuf.data);
 	}
 }
 
@@ -6445,11 +6537,52 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context)
 			appendContextKeyword(context, prefix,
 								 -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
 			first = false;
+
+			get_from_clause_item(jtnode, query, context);
 		}
 		else
+		{
+			StringInfoData targetbuf;
+			char          *trailing_nl;
+
 			appendStringInfoString(buf, ", ");
+			
+			initStringInfo(&targetbuf);
+			context->buf = &targetbuf;
+
+			get_from_clause_item(jtnode, query, context);
+
+			context->buf = buf;
+
+			/* Locate the start of the current  line in the buffer */
+
+			trailing_nl = (strrchr(buf->data,'\n'));
+			if (trailing_nl == NULL)
+				trailing_nl = buf->data;
+			else 
+				trailing_nl++;
+			
+			/*
+			 * Add a newline, plus some  indentation, if pretty_wrap is on and the 
+			 * new from-clause item would cause an overflow.
+			 */
+			
+			if (pretty_wrap >= 0 &&
+				(strlen(trailing_nl) + strlen(targetbuf.data) > pretty_wrap))
+			{
+				appendContextKeyword(context, "", -PRETTYINDENT_STD, 
+									 PRETTYINDENT_STD, PRETTYINDENT_VAR);
+			}
+
+			/* Add the new item */
+
+			appendStringInfoString(buf, targetbuf.data);
+			
+			/* cleanup */
+
+			pfree (targetbuf.data);
+		}
 
-		get_from_clause_item(jtnode, query, context);
 	}
 }
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 7a54a74757e..6100472d94a 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201202141
+#define CATALOG_VERSION_NO	201202191
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index fb2923f94db..8700d0d958a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3743,6 +3743,8 @@ DATA(insert OID = 2505 (  pg_get_viewdef	   PGNSP PGUID 12 1 0 0 0 f f f f t f s
 DESCR("select statement of a view with pretty-print option");
 DATA(insert OID = 2506 (  pg_get_viewdef	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 25 "26 16" _null_ _null_ _null_ _null_	pg_get_viewdef_ext _null_ _null_ _null_ ));
 DESCR("select statement of a view with pretty-print option");
+DATA(insert OID = 3159 (  pg_get_viewdef	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 25 "26 23" _null_ _null_ _null_ _null_	pg_get_viewdef_wrap _null_ _null_ _null_ ));
+DESCR("select statement of a view with pretty-printing and specified line wrapping");
 DATA(insert OID = 2507 (  pg_get_indexdef	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 25 "26 23 16" _null_ _null_ _null_ _null_	pg_get_indexdef_ext _null_ _null_ _null_ ));
 DESCR("index description (full create statement or single expression) with pretty-print option");
 DATA(insert OID = 2508 (  pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 25 "26 16" _null_ _null_ _null_ _null_	pg_get_constraintdef_ext _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2c331ce5eb9..fe253bcc7cd 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -624,6 +624,7 @@ extern Datum pg_get_ruledef(PG_FUNCTION_ARGS);
 extern Datum pg_get_ruledef_ext(PG_FUNCTION_ARGS);
 extern Datum pg_get_viewdef(PG_FUNCTION_ARGS);
 extern Datum pg_get_viewdef_ext(PG_FUNCTION_ARGS);
+extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS);
 extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS);
 extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS);
 extern Datum pg_get_indexdef(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index cb3d756290c..1e879532f87 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -1381,7 +1381,9 @@ select * from dfview;
  c3     | bigint |           | plain   | 
  c4     | bigint |           | plain   | 
 View definition:
- SELECT int8_tbl.q1, int8_tbl.q2, dfunc(int8_tbl.q1, int8_tbl.q2, flag := int8_tbl.q1 > int8_tbl.q2) AS c3, dfunc(int8_tbl.q1, flag := int8_tbl.q1 < int8_tbl.q2, b := int8_tbl.q2) AS c4
+ SELECT int8_tbl.q1, int8_tbl.q2, 
+    dfunc(int8_tbl.q1, int8_tbl.q2, flag := int8_tbl.q1 > int8_tbl.q2) AS c3, 
+    dfunc(int8_tbl.q1, flag := int8_tbl.q1 < int8_tbl.q2, b := int8_tbl.q2) AS c4
    FROM int8_tbl;
 
 drop view dfview;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f67b8dc3f43..0275a0e120e 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1568,3 +1568,36 @@ select * from only t1_2;
  19
 (10 rows)
 
+-- test various flavors of pg_get_viewdef()
+select pg_get_viewdef('shoe'::regclass) as unpretty;
+                                                                                                             unpretty                                                                                                              
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, (sh.slminlen * un.un_fact) AS slminlen_cm, sh.slmaxlen, (sh.slmaxlen * un.un_fact) AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE (sh.slunit = un.un_name);
+(1 row)
+
+select pg_get_viewdef('shoe'::regclass,true) as pretty;
+                           pretty                            
+-------------------------------------------------------------
+  SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, +
+     sh.slminlen * un.un_fact AS slminlen_cm, sh.slmaxlen,  +
+     sh.slmaxlen * un.un_fact AS slmaxlen_cm, sh.slunit     +
+    FROM shoe_data sh, unit un                              +
+   WHERE sh.slunit = un.un_name;
+(1 row)
+
+select pg_get_viewdef('shoe'::regclass,0) as prettier;
+                   prettier                    
+-----------------------------------------------
+  SELECT sh.shoename,                         +
+     sh.sh_avail,                             +
+     sh.slcolor,                              +
+     sh.slminlen,                             +
+     sh.slminlen * un.un_fact AS slminlen_cm, +
+     sh.slmaxlen,                             +
+     sh.slmaxlen * un.un_fact AS slmaxlen_cm, +
+     sh.slunit                                +
+    FROM shoe_data sh,                        +
+     unit un                                  +
+   WHERE sh.slunit = un.un_name;
+(1 row)
+
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index a58739b68ef..fae92cd37bf 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -277,18 +277,19 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass);
 (1 row)
 
 SELECT pg_get_viewdef('vsubdepartment'::regclass, true);
-                                    pg_get_viewdef                                    
---------------------------------------------------------------------------------------
-  WITH RECURSIVE subdepartment AS (                                                  +
-                  SELECT department.id, department.parent_department, department.name+
-                    FROM department                                                  +
-                   WHERE department.name = 'A'::text                                 +
-         UNION ALL                                                                   +
-                  SELECT d.id, d.parent_department, d.name                           +
-                    FROM department d, subdepartment sd                              +
-                   WHERE d.parent_department = sd.id                                 +
-         )                                                                           +
-  SELECT subdepartment.id, subdepartment.parent_department, subdepartment.name       +
+                                pg_get_viewdef                                 
+-------------------------------------------------------------------------------
+  WITH RECURSIVE subdepartment AS (                                           +
+                  SELECT department.id, department.parent_department,         +
+                     department.name                                          +
+                    FROM department                                           +
+                   WHERE department.name = 'A'::text                          +
+         UNION ALL                                                            +
+                  SELECT d.id, d.parent_department, d.name                    +
+                    FROM department d, subdepartment sd                       +
+                   WHERE d.parent_department = sd.id                          +
+         )                                                                    +
+  SELECT subdepartment.id, subdepartment.parent_department, subdepartment.name+
     FROM subdepartment;
 (1 row)
 
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 16dc106ab07..ea665a3c8bb 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -927,3 +927,9 @@ update t1 set a = 4 where a = 5;
 select * from only t1;
 select * from only t1_1;
 select * from only t1_2;
+
+-- test various flavors of pg_get_viewdef()
+
+select pg_get_viewdef('shoe'::regclass) as unpretty;
+select pg_get_viewdef('shoe'::regclass,true) as pretty;
+select pg_get_viewdef('shoe'::regclass,0) as prettier;
-- 
GitLab