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:01 -04:00
parent 61bb221489
commit cdc6c25f97
3 changed files with 58 additions and 6 deletions

View File

@@ -8913,7 +8913,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_CoalesceExpr: case T_CoalesceExpr:
case T_MinMaxExpr: case T_MinMaxExpr:
case T_SQLValueFunction: case T_SQLValueFunction:
case T_XmlExpr:
case T_NextValueExpr: case T_NextValueExpr:
case T_NullIfExpr: case T_NullIfExpr:
case T_Aggref: case T_Aggref:
@@ -9015,6 +9014,17 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
} }
pg_fallthrough; pg_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;
pg_fallthrough;
case T_SubLink: case T_SubLink:
case T_NullTest: case T_NullTest:
case T_BooleanTest: case T_BooleanTest:
@@ -9039,13 +9049,14 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_RowExpr: /* other separators */ case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */ case T_CoalesceExpr: /* own parentheses */
case T_MinMaxExpr: /* own parentheses */ case T_MinMaxExpr: /* own parentheses */
case T_XmlExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */ case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */ case T_Aggref: /* own parentheses */
case T_GroupingFunc: /* own parentheses */ case T_GroupingFunc: /* own parentheses */
case T_WindowFunc: /* own parentheses */ case T_WindowFunc: /* own parentheses */
case T_CaseExpr: /* other separators */ case T_CaseExpr: /* other separators */
return true; return true;
case T_XmlExpr: /* own parentheses, except IS DOCUMENT */
return ((XmlExpr *) parentNode)->op != IS_DOCUMENT;
default: default:
return false; return false;
} }
@@ -9091,7 +9102,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_RowExpr: /* other separators */ case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */ case T_CoalesceExpr: /* own parentheses */
case T_MinMaxExpr: /* own parentheses */ case T_MinMaxExpr: /* own parentheses */
case T_XmlExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */ case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */ case T_Aggref: /* own parentheses */
case T_GroupingFunc: /* own parentheses */ case T_GroupingFunc: /* own parentheses */
@@ -9099,6 +9109,8 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_CaseExpr: /* other separators */ case T_CaseExpr: /* other separators */
case T_JsonExpr: /* own parentheses */ case T_JsonExpr: /* own parentheses */
return true; return true;
case T_XmlExpr: /* own parentheses, except IS DOCUMENT */
return ((XmlExpr *) parentNode)->op != IS_DOCUMENT;
default: default:
return false; return false;
} }
@@ -10151,6 +10163,9 @@ get_rule_expr(Node *node, deparse_context *context,
appendStringInfoString(buf, "XMLSERIALIZE("); appendStringInfoString(buf, "XMLSERIALIZE(");
break; break;
case IS_DOCUMENT: case IS_DOCUMENT:
/* postfix test, like IS NULL: not self-delimiting */
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, '(');
break; break;
} }
if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE) if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
@@ -10258,7 +10273,9 @@ get_rule_expr(Node *node, deparse_context *context,
} }
break; break;
case IS_DOCUMENT: 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; break;
} }
} }
@@ -10274,7 +10291,11 @@ get_rule_expr(Node *node, deparse_context *context,
} }
if (xexpr->op == IS_DOCUMENT) if (xexpr->op == IS_DOCUMENT)
{
appendStringInfoString(buf, " IS DOCUMENT"); appendStringInfoString(buf, " IS DOCUMENT");
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')');
}
else else
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
} }
@@ -10776,10 +10797,12 @@ looks_like_function(Node *node)
case T_CoalesceExpr: case T_CoalesceExpr:
case T_MinMaxExpr: case T_MinMaxExpr:
case T_SQLValueFunction: case T_SQLValueFunction:
case T_XmlExpr:
case T_JsonExpr: case T_JsonExpr:
/* these are all accepted by func_expr_common_subexpr */ /* these are all accepted by func_expr_common_subexpr */
return true; return true;
case T_XmlExpr:
/* all but IS DOCUMENT, which is a postfix test */
return ((XmlExpr *) node)->op != IS_DOCUMENT;
default: default:
break; break;
} }

View File

@@ -2276,6 +2276,23 @@ select pg_get_viewdef('tt26v', true);
FROM ( VALUES (1,2,3)) v(x, y, z); FROM ( VALUES (1,2,3)) v(x, y, z);
(1 row) (1 row)
-- 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 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);
(1 row)
-- test restriction on non-system view expansion. -- test restriction on non-system view expansion.
create table tt27v_tbl (a int); create table tt27v_tbl (a int);
create view tt27v as select a from tt27v_tbl; create view tt27v as select a from tt27v_tbl;
@@ -2322,7 +2339,7 @@ drop cascades to view aliased_view_2
drop cascades to view aliased_view_3 drop cascades to view aliased_view_3
drop cascades to view aliased_view_4 drop cascades to view aliased_view_4
DROP SCHEMA testviewschm2 CASCADE; DROP SCHEMA testviewschm2 CASCADE;
NOTICE: drop cascades to 80 other objects NOTICE: drop cascades to 82 other objects
DETAIL: drop cascades to table t1 DETAIL: drop cascades to table t1
drop cascades to view temporal1 drop cascades to view temporal1
drop cascades to view temporal2 drop cascades to view temporal2
@@ -2401,5 +2418,7 @@ drop cascades to view tt23v
drop cascades to view tt24v drop cascades to view tt24v
drop cascades to view tt25v drop cascades to view tt25v
drop cascades to view tt26v drop cascades to view tt26v
drop cascades to operator ||(xml,xml)
drop cascades to view tt261v
drop cascades to table tt27v_tbl drop cascades to table tt27v_tbl
drop cascades to view tt27v drop cascades to view tt27v

View File

@@ -832,6 +832,16 @@ select x + y + z as c1,
from (values(1,2,3)) v(x,y,z); from (values(1,2,3)) v(x,y,z);
select pg_get_viewdef('tt26v', true); select pg_get_viewdef('tt26v', true);
-- 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. -- test restriction on non-system view expansion.
create table tt27v_tbl (a int); create table tt27v_tbl (a int);
create view tt27v as select a from tt27v_tbl; create view tt27v as select a from tt27v_tbl;