@@ -132,7 +132,9 @@ static void deparseTargetList(StringInfo buf,
132132 Bitmapset * attrs_used ,
133133 bool qualify_col ,
134134 List * * retrieved_attrs );
135- static void deparseExplicitTargetList (List * tlist , List * * retrieved_attrs ,
135+ static void deparseExplicitTargetList (List * tlist ,
136+ bool is_returning ,
137+ List * * retrieved_attrs ,
136138 deparse_expr_cxt * context );
137139static void deparseSubqueryTargetList (deparse_expr_cxt * context );
138140static void deparseReturningList (StringInfo buf , PlannerInfo * root ,
@@ -168,11 +170,13 @@ static void deparseLockingClause(deparse_expr_cxt *context);
168170static void appendOrderByClause (List * pathkeys , deparse_expr_cxt * context );
169171static void appendConditions (List * exprs , deparse_expr_cxt * context );
170172static void deparseFromExprForRel (StringInfo buf , PlannerInfo * root ,
171- RelOptInfo * joinrel , bool use_alias , List * * params_list );
173+ RelOptInfo * foreignrel , bool use_alias ,
174+ Index ignore_rel , List * * ignore_conds ,
175+ List * * params_list );
172176static void deparseFromExpr (List * quals , deparse_expr_cxt * context );
173177static void deparseRangeTblRef (StringInfo buf , PlannerInfo * root ,
174178 RelOptInfo * foreignrel , bool make_subquery ,
175- List * * params_list );
179+ Index ignore_rel , List * * ignore_conds , List * * params_list );
176180static void deparseAggref (Aggref * node , deparse_expr_cxt * context );
177181static void appendGroupByClause (List * tlist , deparse_expr_cxt * context );
178182static void appendAggOrderBy (List * orderList , List * targetList ,
@@ -1028,7 +1032,7 @@ deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs,
10281032 * For a join or upper relation the input tlist gives the list of
10291033 * columns required to be fetched from the foreign server.
10301034 */
1031- deparseExplicitTargetList (tlist , retrieved_attrs , context );
1035+ deparseExplicitTargetList (tlist , false, retrieved_attrs , context );
10321036 }
10331037 else
10341038 {
@@ -1071,7 +1075,7 @@ deparseFromExpr(List *quals, deparse_expr_cxt *context)
10711075 appendStringInfoString (buf , " FROM " );
10721076 deparseFromExprForRel (buf , context -> root , scanrel ,
10731077 (bms_num_members (scanrel -> relids ) > 1 ),
1074- context -> params_list );
1078+ ( Index ) 0 , NULL , context -> params_list );
10751079
10761080 /* Construct WHERE clause */
10771081 if (quals != NIL )
@@ -1340,9 +1344,14 @@ get_jointype_name(JoinType jointype)
13401344 *
13411345 * retrieved_attrs is the list of continuously increasing integers starting
13421346 * from 1. It has same number of entries as tlist.
1347+ *
1348+ * This is used for both SELECT and RETURNING targetlists; the is_returning
1349+ * parameter is true only for a RETURNING targetlist.
13431350 */
13441351static void
1345- deparseExplicitTargetList (List * tlist , List * * retrieved_attrs ,
1352+ deparseExplicitTargetList (List * tlist ,
1353+ bool is_returning ,
1354+ List * * retrieved_attrs ,
13461355 deparse_expr_cxt * context )
13471356{
13481357 ListCell * lc ;
@@ -1357,13 +1366,16 @@ deparseExplicitTargetList(List *tlist, List **retrieved_attrs,
13571366
13581367 if (i > 0 )
13591368 appendStringInfoString (buf , ", " );
1369+ else if (is_returning )
1370+ appendStringInfoString (buf , " RETURNING " );
1371+
13601372 deparseExpr ((Expr * ) tle -> expr , context );
13611373
13621374 * retrieved_attrs = lappend_int (* retrieved_attrs , i + 1 );
13631375 i ++ ;
13641376 }
13651377
1366- if (i == 0 )
1378+ if (i == 0 && ! is_returning )
13671379 appendStringInfoString (buf , "NULL" );
13681380}
13691381
@@ -1406,27 +1418,107 @@ deparseSubqueryTargetList(deparse_expr_cxt *context)
14061418 * The function constructs ... JOIN ... ON ... for join relation. For a base
14071419 * relation it just returns schema-qualified tablename, with the appropriate
14081420 * alias if so requested.
1421+ *
1422+ * 'ignore_rel' is either zero or the RT index of a target relation. In the
1423+ * latter case the function constructs FROM clause of UPDATE or USING clause
1424+ * of DELETE; it deparses the join relation as if the relation never contained
1425+ * the target relation, and creates a List of conditions to be deparsed into
1426+ * the top-level WHERE clause, which is returned to *ignore_conds.
14091427 */
14101428static void
14111429deparseFromExprForRel (StringInfo buf , PlannerInfo * root , RelOptInfo * foreignrel ,
1412- bool use_alias , List * * params_list )
1430+ bool use_alias , Index ignore_rel , List * * ignore_conds ,
1431+ List * * params_list )
14131432{
14141433 PgFdwRelationInfo * fpinfo = (PgFdwRelationInfo * ) foreignrel -> fdw_private ;
14151434
14161435 if (IS_JOIN_REL (foreignrel ))
14171436 {
14181437 StringInfoData join_sql_o ;
14191438 StringInfoData join_sql_i ;
1439+ RelOptInfo * outerrel = fpinfo -> outerrel ;
1440+ RelOptInfo * innerrel = fpinfo -> innerrel ;
1441+ bool outerrel_is_target = false;
1442+ bool innerrel_is_target = false;
14201443
1421- /* Deparse outer relation */
1422- initStringInfo (& join_sql_o );
1423- deparseRangeTblRef (& join_sql_o , root , fpinfo -> outerrel ,
1424- fpinfo -> make_outerrel_subquery , params_list );
1444+ if (ignore_rel > 0 && bms_is_member (ignore_rel , foreignrel -> relids ))
1445+ {
1446+ /*
1447+ * If this is an inner join, add joinclauses to *ignore_conds and
1448+ * set it to empty so that those can be deparsed into the WHERE
1449+ * clause. Note that since the target relation can never be
1450+ * within the nullable side of an outer join, those could safely
1451+ * be pulled up into the WHERE clause (see foreign_join_ok()).
1452+ * Note also that since the target relation is only inner-joined
1453+ * to any other relation in the query, all conditions in the join
1454+ * tree mentioning the target relation could be deparsed into the
1455+ * WHERE clause by doing this recursively.
1456+ */
1457+ if (fpinfo -> jointype == JOIN_INNER )
1458+ {
1459+ * ignore_conds = list_concat (* ignore_conds ,
1460+ list_copy (fpinfo -> joinclauses ));
1461+ fpinfo -> joinclauses = NIL ;
1462+ }
14251463
1426- /* Deparse inner relation */
1427- initStringInfo (& join_sql_i );
1428- deparseRangeTblRef (& join_sql_i , root , fpinfo -> innerrel ,
1429- fpinfo -> make_innerrel_subquery , params_list );
1464+ /*
1465+ * Check if either of the input relations is the target relation.
1466+ */
1467+ if (outerrel -> relid == ignore_rel )
1468+ outerrel_is_target = true;
1469+ else if (innerrel -> relid == ignore_rel )
1470+ innerrel_is_target = true;
1471+ }
1472+
1473+ /* Deparse outer relation if not the target relation. */
1474+ if (!outerrel_is_target )
1475+ {
1476+ initStringInfo (& join_sql_o );
1477+ deparseRangeTblRef (& join_sql_o , root , outerrel ,
1478+ fpinfo -> make_outerrel_subquery ,
1479+ ignore_rel , ignore_conds , params_list );
1480+
1481+ /*
1482+ * If inner relation is the target relation, skip deparsing it.
1483+ * Note that since the join of the target relation with any other
1484+ * relation in the query is an inner join and can never be within
1485+ * the nullable side of an outer join, the join could be
1486+ * interchanged with higher-level joins (cf. identity 1 on outer
1487+ * join reordering shown in src/backend/optimizer/README), which
1488+ * means it's safe to skip the target-relation deparsing here.
1489+ */
1490+ if (innerrel_is_target )
1491+ {
1492+ Assert (fpinfo -> jointype == JOIN_INNER );
1493+ Assert (fpinfo -> joinclauses == NIL );
1494+ appendStringInfo (buf , "%s" , join_sql_o .data );
1495+ return ;
1496+ }
1497+ }
1498+
1499+ /* Deparse inner relation if not the target relation. */
1500+ if (!innerrel_is_target )
1501+ {
1502+ initStringInfo (& join_sql_i );
1503+ deparseRangeTblRef (& join_sql_i , root , innerrel ,
1504+ fpinfo -> make_innerrel_subquery ,
1505+ ignore_rel , ignore_conds , params_list );
1506+
1507+ /*
1508+ * If outer relation is the target relation, skip deparsing it.
1509+ * See the above note about safety.
1510+ */
1511+ if (outerrel_is_target )
1512+ {
1513+ Assert (fpinfo -> jointype == JOIN_INNER );
1514+ Assert (fpinfo -> joinclauses == NIL );
1515+ appendStringInfo (buf , "%s" , join_sql_i .data );
1516+ return ;
1517+ }
1518+ }
1519+
1520+ /* Neither of the relations is the target relation. */
1521+ Assert (!outerrel_is_target && !innerrel_is_target );
14301522
14311523 /*
14321524 * For a join relation FROM clause entry is deparsed as
@@ -1486,7 +1578,8 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
14861578 */
14871579static void
14881580deparseRangeTblRef (StringInfo buf , PlannerInfo * root , RelOptInfo * foreignrel ,
1489- bool make_subquery , List * * params_list )
1581+ bool make_subquery , Index ignore_rel , List * * ignore_conds ,
1582+ List * * params_list )
14901583{
14911584 PgFdwRelationInfo * fpinfo = (PgFdwRelationInfo * ) foreignrel -> fdw_private ;
14921585
@@ -1501,6 +1594,14 @@ deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
15011594 List * retrieved_attrs ;
15021595 int ncols ;
15031596
1597+ /*
1598+ * The given relation shouldn't contain the target relation, because
1599+ * this should only happen for input relations for a full join, and
1600+ * such relations can never contain an UPDATE/DELETE target.
1601+ */
1602+ Assert (ignore_rel == 0 ||
1603+ !bms_is_member (ignore_rel , foreignrel -> relids ));
1604+
15041605 /* Deparse the subquery representing the relation. */
15051606 appendStringInfoChar (buf , '(' );
15061607 deparseSelectStmtForRel (buf , root , foreignrel , NIL ,
@@ -1534,7 +1635,8 @@ deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
15341635 }
15351636 }
15361637 else
1537- deparseFromExprForRel (buf , root , foreignrel , true, params_list );
1638+ deparseFromExprForRel (buf , root , foreignrel , true, ignore_rel ,
1639+ ignore_conds , params_list );
15381640}
15391641
15401642/*
@@ -1645,35 +1747,46 @@ deparseUpdateSql(StringInfo buf, PlannerInfo *root,
16451747/*
16461748 * deparse remote UPDATE statement
16471749 *
1648- * The statement text is appended to buf, and we also create an integer List
1649- * of the columns being retrieved by RETURNING (if any), which is returned
1650- * to *retrieved_attrs.
1750+ * 'buf' is the output buffer to append the statement to
1751+ * 'rtindex' is the RT index of the associated target relation
1752+ * 'rel' is the relation descriptor for the target relation
1753+ * 'foreignrel' is the RelOptInfo for the target relation or the join relation
1754+ * containing all base relations in the query
1755+ * 'targetlist' is the tlist of the underlying foreign-scan plan node
1756+ * 'targetAttrs' is the target columns of the UPDATE
1757+ * 'remote_conds' is the qual clauses that must be evaluated remotely
1758+ * '*params_list' is an output list of exprs that will become remote Params
1759+ * 'returningList' is the RETURNING targetlist
1760+ * '*retrieved_attrs' is an output list of integers of columns being retrieved
1761+ * by RETURNING (if any)
16511762 */
16521763void
16531764deparseDirectUpdateSql (StringInfo buf , PlannerInfo * root ,
16541765 Index rtindex , Relation rel ,
1766+ RelOptInfo * foreignrel ,
16551767 List * targetlist ,
16561768 List * targetAttrs ,
16571769 List * remote_conds ,
16581770 List * * params_list ,
16591771 List * returningList ,
16601772 List * * retrieved_attrs )
16611773{
1662- RelOptInfo * baserel = root -> simple_rel_array [rtindex ];
16631774 deparse_expr_cxt context ;
16641775 int nestlevel ;
16651776 bool first ;
16661777 ListCell * lc ;
16671778
16681779 /* Set up context struct for recursion */
16691780 context .root = root ;
1670- context .foreignrel = baserel ;
1671- context .scanrel = baserel ;
1781+ context .foreignrel = foreignrel ;
1782+ context .scanrel = foreignrel ;
16721783 context .buf = buf ;
16731784 context .params_list = params_list ;
16741785
16751786 appendStringInfoString (buf , "UPDATE " );
16761787 deparseRelation (buf , rel );
1788+ if (foreignrel -> reloptkind == RELOPT_JOINREL )
1789+ appendStringInfo (buf , " %s%d" , REL_ALIAS_PREFIX , rtindex );
16771790 appendStringInfoString (buf , " SET " );
16781791
16791792 /* Make sure any constants in the exprs are printed portably */
@@ -1700,14 +1813,28 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
17001813
17011814 reset_transmission_modes (nestlevel );
17021815
1816+ if (foreignrel -> reloptkind == RELOPT_JOINREL )
1817+ {
1818+ List * ignore_conds = NIL ;
1819+
1820+ appendStringInfo (buf , " FROM " );
1821+ deparseFromExprForRel (buf , root , foreignrel , true, rtindex ,
1822+ & ignore_conds , params_list );
1823+ remote_conds = list_concat (remote_conds , ignore_conds );
1824+ }
1825+
17031826 if (remote_conds )
17041827 {
17051828 appendStringInfoString (buf , " WHERE " );
17061829 appendConditions (remote_conds , & context );
17071830 }
17081831
1709- deparseReturningList (buf , root , rtindex , rel , false,
1710- returningList , retrieved_attrs );
1832+ if (foreignrel -> reloptkind == RELOPT_JOINREL )
1833+ deparseExplicitTargetList (returningList , true, retrieved_attrs ,
1834+ & context );
1835+ else
1836+ deparseReturningList (buf , root , rtindex , rel , false,
1837+ returningList , retrieved_attrs );
17111838}
17121839
17131840/*
@@ -1735,39 +1862,62 @@ deparseDeleteSql(StringInfo buf, PlannerInfo *root,
17351862/*
17361863 * deparse remote DELETE statement
17371864 *
1738- * The statement text is appended to buf, and we also create an integer List
1739- * of the columns being retrieved by RETURNING (if any), which is returned
1740- * to *retrieved_attrs.
1865+ * 'buf' is the output buffer to append the statement to
1866+ * 'rtindex' is the RT index of the associated target relation
1867+ * 'rel' is the relation descriptor for the target relation
1868+ * 'foreignrel' is the RelOptInfo for the target relation or the join relation
1869+ * containing all base relations in the query
1870+ * 'remote_conds' is the qual clauses that must be evaluated remotely
1871+ * '*params_list' is an output list of exprs that will become remote Params
1872+ * 'returningList' is the RETURNING targetlist
1873+ * '*retrieved_attrs' is an output list of integers of columns being retrieved
1874+ * by RETURNING (if any)
17411875 */
17421876void
17431877deparseDirectDeleteSql (StringInfo buf , PlannerInfo * root ,
17441878 Index rtindex , Relation rel ,
1879+ RelOptInfo * foreignrel ,
17451880 List * remote_conds ,
17461881 List * * params_list ,
17471882 List * returningList ,
17481883 List * * retrieved_attrs )
17491884{
1750- RelOptInfo * baserel = root -> simple_rel_array [rtindex ];
17511885 deparse_expr_cxt context ;
17521886
17531887 /* Set up context struct for recursion */
17541888 context .root = root ;
1755- context .foreignrel = baserel ;
1756- context .scanrel = baserel ;
1889+ context .foreignrel = foreignrel ;
1890+ context .scanrel = foreignrel ;
17571891 context .buf = buf ;
17581892 context .params_list = params_list ;
17591893
17601894 appendStringInfoString (buf , "DELETE FROM " );
17611895 deparseRelation (buf , rel );
1896+ if (foreignrel -> reloptkind == RELOPT_JOINREL )
1897+ appendStringInfo (buf , " %s%d" , REL_ALIAS_PREFIX , rtindex );
1898+
1899+ if (foreignrel -> reloptkind == RELOPT_JOINREL )
1900+ {
1901+ List * ignore_conds = NIL ;
1902+
1903+ appendStringInfo (buf , " USING " );
1904+ deparseFromExprForRel (buf , root , foreignrel , true, rtindex ,
1905+ & ignore_conds , params_list );
1906+ remote_conds = list_concat (remote_conds , ignore_conds );
1907+ }
17621908
17631909 if (remote_conds )
17641910 {
17651911 appendStringInfoString (buf , " WHERE " );
17661912 appendConditions (remote_conds , & context );
17671913 }
17681914
1769- deparseReturningList (buf , root , rtindex , rel , false,
1770- returningList , retrieved_attrs );
1915+ if (foreignrel -> reloptkind == RELOPT_JOINREL )
1916+ deparseExplicitTargetList (returningList , true, retrieved_attrs ,
1917+ & context );
1918+ else
1919+ deparseReturningList (buf , root , rtindex , rel , false,
1920+ returningList , retrieved_attrs );
17711921}
17721922
17731923/*
0 commit comments