1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

In deparsing, parenthesize IS DOCUMENT where it is an operand.

isSimpleNode() and related code treated every XmlExpr as
self-delimiting.  Most of them are, because they look like function
calls with some extra decoration.  But "x IS DOCUMENT" is not: it is
a postfix test, like IS NULL.  Printed as the operand of another
operator, or as the operand of a postfix test, it was therefore not
parenthesized and could not be parsed back, or was parsed as something
unintended.  Under-parenthesization occurred even in non-pretty-print
cases, so that this could break dump/restore workflows.

To fix, treat IS DOCUMENT as neither simple nor function-like, and
give it the same treatment as other postfix tests.

Also pass the single argument of IS DOCUMENT, not the argument list,
to get_rule_expr_paren().  This avoids unnecessary parenthesization
of the argument.

Author: Henson Choi <assam258@gmail.com>
Reviewed-by: Prashant Chinnam <crprashant@hotmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAAAe_zDuPSkc8ihCctV67Lxbqock+S4wqCHHo8xxZ1t4teXo0g@mail.gmail.com
Backpatch-through: 14
This commit is contained in:
Tom Lane
2026-10-20 14:29:02 -04:00
parent 8113841f0d
commit 823474c041
3 changed files with 58 additions and 6 deletions

View File

@@ -8157,7 +8157,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_CoalesceExpr:
case T_MinMaxExpr:
case T_SQLValueFunction:
case T_XmlExpr:
case T_NextValueExpr:
case T_NullIfExpr:
case T_Aggref:
@@ -8253,6 +8252,17 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
}
/* FALLTHROUGH */
case T_XmlExpr:
/*
* Function-like (name(..)), except "x IS DOCUMENT", which is a
* postfix test like IS NULL. (The IsA check is needed because
* T_OpExpr falls through to here.)
*/
if (IsA(node, XmlExpr) && ((XmlExpr *) node)->op != IS_DOCUMENT)
return true;
/* FALLTHROUGH */
case T_SubLink:
case T_NullTest:
case T_BooleanTest:
@@ -8276,13 +8286,14 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
case T_MinMaxExpr: /* own parentheses */
case T_XmlExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */
case T_GroupingFunc: /* own parentheses */
case T_WindowFunc: /* own parentheses */
case T_CaseExpr: /* other separators */
return true;
case T_XmlExpr: /* own parentheses, except IS DOCUMENT */
return ((XmlExpr *) parentNode)->op != IS_DOCUMENT;
default:
return false;
}
@@ -8328,13 +8339,14 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
case T_MinMaxExpr: /* own parentheses */
case T_XmlExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */
case T_GroupingFunc: /* own parentheses */
case T_WindowFunc: /* own parentheses */
case T_CaseExpr: /* other separators */
return true;
case T_XmlExpr: /* own parentheses, except IS DOCUMENT */
return ((XmlExpr *) parentNode)->op != IS_DOCUMENT;
default:
return false;
}
@@ -9231,6 +9243,9 @@ get_rule_expr(Node *node, deparse_context *context,
appendStringInfoString(buf, "XMLSERIALIZE(");
break;
case IS_DOCUMENT:
/* postfix test, like IS NULL: not self-delimiting */
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, '(');
break;
}
if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
@@ -9338,7 +9353,9 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
case IS_DOCUMENT:
get_rule_expr_paren((Node *) xexpr->args, context, false, node);
Assert(list_length(xexpr->args) == 1);
get_rule_expr_paren((Node *) linitial(xexpr->args),
context, false, node);
break;
}
@@ -9348,7 +9365,11 @@ get_rule_expr(Node *node, deparse_context *context,
format_type_with_typemod(xexpr->type,
xexpr->typmod));
if (xexpr->op == IS_DOCUMENT)
{
appendStringInfoString(buf, " IS DOCUMENT");
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')');
}
else
appendStringInfoChar(buf, ')');
}
@@ -9726,9 +9747,11 @@ looks_like_function(Node *node)
case T_CoalesceExpr:
case T_MinMaxExpr:
case T_SQLValueFunction:
case T_XmlExpr:
/* these are all accepted by func_expr_common_subexpr */
return true;
case T_XmlExpr:
/* all but IS DOCUMENT, which is a postfix test */
return ((XmlExpr *) node)->op != IS_DOCUMENT;
default:
break;
}

View File

@@ -2019,6 +2019,23 @@ CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
ERROR: "tt28" is already a view
ROLLBACK;
-- xml IS DOCUMENT needs to be treated specially
create operator ||
(function = xmlconcat2, leftarg=xml, rightarg=xml);
create view tt261v as
select x is document as c1,
(x || x) is document as c2,
f = (x is document) as c3
from (values(false,null::xml)) v(f,x);
select pg_get_viewdef('tt261v', true);
pg_get_viewdef
----------------------------------------------
SELECT v.x IS DOCUMENT AS c1, +
(v.x || v.x) IS DOCUMENT AS c2, +
v.f = (v.x IS DOCUMENT) AS c3 +
FROM ( VALUES (false,NULL::xml)) v(f, x);
(1 row)
-- test restriction on non-system view expansion.
create table tt27v_tbl (a int);
create view tt27v as select a from tt27v_tbl;
@@ -2065,7 +2082,7 @@ drop cascades to view aliased_view_2
drop cascades to view aliased_view_3
drop cascades to view aliased_view_4
DROP SCHEMA testviewschm2 CASCADE;
NOTICE: drop cascades to 77 other objects
NOTICE: drop cascades to 79 other objects
DETAIL: drop cascades to table t1
drop cascades to view temporal1
drop cascades to view temporal2
@@ -2141,5 +2158,7 @@ drop cascades to view tt24v
drop cascades to view tt25v
drop cascades to view tt26v
drop cascades to table tt26
drop cascades to operator ||(xml,xml)
drop cascades to view tt261v
drop cascades to table tt27v_tbl
drop cascades to view tt27v

View File

@@ -711,6 +711,16 @@ CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
CREATE RULE "_RETURN" AS ON SELECT TO tt28 DO INSTEAD SELECT * FROM tt26;
ROLLBACK;
-- xml IS DOCUMENT needs to be treated specially
create operator ||
(function = xmlconcat2, leftarg=xml, rightarg=xml);
create view tt261v as
select x is document as c1,
(x || x) is document as c2,
f = (x is document) as c3
from (values(false,null::xml)) v(f,x);
select pg_get_viewdef('tt261v', true);
-- test restriction on non-system view expansion.
create table tt27v_tbl (a int);
create view tt27v as select a from tt27v_tbl;