From 2f582f76b1945929ff07116cd4639747ce9bb8a1 Mon Sep 17 00:00:00 2001 From: Andrew Dunstan 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 236a60a688..e8e637bf31 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); pg_get_viewdef(view_name, pretty_bool) text - get underlying SELECT command for view (deprecated) + get underlying SELECT command for view, + lines with fields are wrapped to 80 columns if pretty_bool is true (deprecated) pg_get_viewdef(view_oid) @@ -13838,7 +13839,14 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); pg_get_viewdef(view_oid, pretty_bool) text - get underlying SELECT command for view + get underlying SELECT command for view, + lines with fields are wrapped to 80 columns if pretty_bool is true + + + pg_get_viewdef(view_oid, wrap_int) + text + get underlying SELECT command for view, + wrapping lines with fields as specified, pretty printing is implied pg_options_to_table(reloptions) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 9ad54c5dec..64ba8ec891 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 7a54a74757..6100472d94 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 fb2923f94d..8700d0d958 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 2c331ce5eb..fe253bcc7c 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 cb3d756290..1e879532f8 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 f67b8dc3f4..0275a0e120 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 a58739b68e..fae92cd37b 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 16dc106ab0..ea665a3c8b 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; -- 2.40.0