LCOV - code coverage report
Current view: top level - contrib/postgres_fdw - deparse.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 1790 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 62 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * deparse.c
       4              :  *                Query deparser for postgres_fdw
       5              :  *
       6              :  * This file includes functions that examine query WHERE clauses to see
       7              :  * whether they're safe to send to the remote server for execution, as
       8              :  * well as functions to construct the query text to be sent.  The latter
       9              :  * functionality is annoyingly duplicative of ruleutils.c, but there are
      10              :  * enough special considerations that it seems best to keep this separate.
      11              :  * One saving grace is that we only need deparse logic for node types that
      12              :  * we consider safe to send.
      13              :  *
      14              :  * We assume that the remote session's search_path is exactly "pg_catalog",
      15              :  * and thus we need schema-qualify all and only names outside pg_catalog.
      16              :  *
      17              :  * We do not consider that it is ever safe to send COLLATE expressions to
      18              :  * the remote server: it might not have the same collation names we do.
      19              :  * (Later we might consider it safe to send COLLATE "C", but even that would
      20              :  * fail on old remote servers.)  An expression is considered safe to send
      21              :  * only if all operator/function input collations used in it are traceable to
      22              :  * Var(s) of the foreign table.  That implies that if the remote server gets
      23              :  * a different answer than we do, the foreign table's columns are not marked
      24              :  * with collations that match the remote table's columns, which we can
      25              :  * consider to be user error.
      26              :  *
      27              :  * Portions Copyright (c) 2012-2026, PostgreSQL Global Development Group
      28              :  *
      29              :  * IDENTIFICATION
      30              :  *                contrib/postgres_fdw/deparse.c
      31              :  *
      32              :  *-------------------------------------------------------------------------
      33              :  */
      34              : #include "postgres.h"
      35              : 
      36              : #include "access/htup_details.h"
      37              : #include "access/sysattr.h"
      38              : #include "access/table.h"
      39              : #include "catalog/pg_aggregate.h"
      40              : #include "catalog/pg_authid.h"
      41              : #include "catalog/pg_collation.h"
      42              : #include "catalog/pg_database.h"
      43              : #include "catalog/pg_namespace.h"
      44              : #include "catalog/pg_operator.h"
      45              : #include "catalog/pg_opfamily.h"
      46              : #include "catalog/pg_proc.h"
      47              : #include "catalog/pg_ts_config.h"
      48              : #include "catalog/pg_ts_dict.h"
      49              : #include "catalog/pg_type.h"
      50              : #include "commands/defrem.h"
      51              : #include "nodes/nodeFuncs.h"
      52              : #include "nodes/plannodes.h"
      53              : #include "optimizer/optimizer.h"
      54              : #include "optimizer/prep.h"
      55              : #include "optimizer/tlist.h"
      56              : #include "parser/parsetree.h"
      57              : #include "postgres_fdw.h"
      58              : #include "utils/builtins.h"
      59              : #include "utils/lsyscache.h"
      60              : #include "utils/rel.h"
      61              : #include "utils/syscache.h"
      62              : #include "utils/typcache.h"
      63              : 
      64              : /*
      65              :  * Global context for foreign_expr_walker's search of an expression tree.
      66              :  */
      67              : typedef struct foreign_glob_cxt
      68              : {
      69              :         PlannerInfo *root;                      /* global planner state */
      70              :         RelOptInfo *foreignrel;         /* the foreign relation we are planning for */
      71              :         Relids          relids;                 /* relids of base relations in the underlying
      72              :                                                                  * scan */
      73              : } foreign_glob_cxt;
      74              : 
      75              : /*
      76              :  * Local (per-tree-level) context for foreign_expr_walker's search.
      77              :  * This is concerned with identifying collations used in the expression.
      78              :  */
      79              : typedef enum
      80              : {
      81              :         FDW_COLLATE_NONE,                       /* expression is of a noncollatable type, or
      82              :                                                                  * it has default collation that is not
      83              :                                                                  * traceable to a foreign Var */
      84              :         FDW_COLLATE_SAFE,                       /* collation derives from a foreign Var */
      85              :         FDW_COLLATE_UNSAFE,                     /* collation is non-default and derives from
      86              :                                                                  * something other than a foreign Var */
      87              : } FDWCollateState;
      88              : 
      89              : typedef struct foreign_loc_cxt
      90              : {
      91              :         Oid                     collation;              /* OID of current collation, if any */
      92              :         FDWCollateState state;          /* state of current collation choice */
      93              : } foreign_loc_cxt;
      94              : 
      95              : /*
      96              :  * Context for deparseExpr
      97              :  */
      98              : typedef struct deparse_expr_cxt
      99              : {
     100              :         PlannerInfo *root;                      /* global planner state */
     101              :         RelOptInfo *foreignrel;         /* the foreign relation we are planning for */
     102              :         RelOptInfo *scanrel;            /* the underlying scan relation. Same as
     103              :                                                                  * foreignrel, when that represents a join or
     104              :                                                                  * a base relation. */
     105              :         StringInfo      buf;                    /* output buffer to append to */
     106              :         List      **params_list;        /* exprs that will become remote Params */
     107              : } deparse_expr_cxt;
     108              : 
     109              : #define REL_ALIAS_PREFIX        "r"
     110              : /* Handy macro to add relation name qualification */
     111              : #define ADD_REL_QUALIFIER(buf, varno)   \
     112              :                 appendStringInfo((buf), "%s%d.", REL_ALIAS_PREFIX, (varno))
     113              : #define SUBQUERY_REL_ALIAS_PREFIX       "s"
     114              : #define SUBQUERY_COL_ALIAS_PREFIX       "c"
     115              : 
     116              : /*
     117              :  * Functions to determine whether an expression can be evaluated safely on
     118              :  * remote server.
     119              :  */
     120              : static bool foreign_expr_walker(Node *node,
     121              :                                                                 foreign_glob_cxt *glob_cxt,
     122              :                                                                 foreign_loc_cxt *outer_cxt,
     123              :                                                                 foreign_loc_cxt *case_arg_cxt);
     124              : static char *deparse_type_name(Oid type_oid, int32 typemod);
     125              : 
     126              : /*
     127              :  * Functions to construct string representation of a node tree.
     128              :  */
     129              : static void deparseTargetList(StringInfo buf,
     130              :                                                           RangeTblEntry *rte,
     131              :                                                           Index rtindex,
     132              :                                                           Relation rel,
     133              :                                                           bool is_returning,
     134              :                                                           Bitmapset *attrs_used,
     135              :                                                           bool qualify_col,
     136              :                                                           List **retrieved_attrs);
     137              : static void deparseExplicitTargetList(List *tlist,
     138              :                                                                           bool is_returning,
     139              :                                                                           List **retrieved_attrs,
     140              :                                                                           deparse_expr_cxt *context);
     141              : static void deparseSubqueryTargetList(deparse_expr_cxt *context);
     142              : static void deparseReturningList(StringInfo buf, RangeTblEntry *rte,
     143              :                                                                  Index rtindex, Relation rel,
     144              :                                                                  bool trig_after_row,
     145              :                                                                  List *withCheckOptionList,
     146              :                                                                  List *returningList,
     147              :                                                                  List **retrieved_attrs);
     148              : static void deparseColumnRef(StringInfo buf, int varno, int varattno,
     149              :                                                          RangeTblEntry *rte, bool qualify_col);
     150              : static void deparseRelation(StringInfo buf, Relation rel);
     151              : static void deparseExpr(Expr *node, deparse_expr_cxt *context);
     152              : static void deparseVar(Var *node, deparse_expr_cxt *context);
     153              : static void deparseConst(Const *node, deparse_expr_cxt *context, int showtype);
     154              : static void deparseParam(Param *node, deparse_expr_cxt *context);
     155              : static void deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context);
     156              : static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
     157              : static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
     158              : static bool isPlainForeignVar(Expr *node, deparse_expr_cxt *context);
     159              : static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
     160              : static void deparseDistinctExpr(DistinctExpr *node, deparse_expr_cxt *context);
     161              : static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node,
     162              :                                                                          deparse_expr_cxt *context);
     163              : static void deparseRelabelType(RelabelType *node, deparse_expr_cxt *context);
     164              : static void deparseArrayCoerceExpr(ArrayCoerceExpr *node, deparse_expr_cxt *context);
     165              : static void deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context);
     166              : static void deparseNullTest(NullTest *node, deparse_expr_cxt *context);
     167              : static void deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context);
     168              : static void deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context);
     169              : static void printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod,
     170              :                                                          deparse_expr_cxt *context);
     171              : static void printRemotePlaceholder(Oid paramtype, int32 paramtypmod,
     172              :                                                                    deparse_expr_cxt *context);
     173              : static void deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs,
     174              :                                                          deparse_expr_cxt *context);
     175              : static void deparseLockingClause(deparse_expr_cxt *context);
     176              : static void appendOrderByClause(List *pathkeys, bool has_final_sort,
     177              :                                                                 deparse_expr_cxt *context);
     178              : static void appendLimitClause(deparse_expr_cxt *context);
     179              : static void appendConditions(List *exprs, deparse_expr_cxt *context);
     180              : static void deparseFromExprForRel(StringInfo buf, PlannerInfo *root,
     181              :                                                                   RelOptInfo *foreignrel, bool use_alias,
     182              :                                                                   Index ignore_rel, List **ignore_conds,
     183              :                                                                   List **additional_conds,
     184              :                                                                   List **params_list);
     185              : static void appendWhereClause(List *exprs, List *additional_conds,
     186              :                                                           deparse_expr_cxt *context);
     187              : static void deparseFromExpr(List *quals, deparse_expr_cxt *context);
     188              : static void deparseRangeTblRef(StringInfo buf, PlannerInfo *root,
     189              :                                                            RelOptInfo *foreignrel, bool make_subquery,
     190              :                                                            Index ignore_rel, List **ignore_conds,
     191              :                                                            List **additional_conds, List **params_list);
     192              : static void deparseAggref(Aggref *node, deparse_expr_cxt *context);
     193              : static void appendGroupByClause(List *tlist, deparse_expr_cxt *context);
     194              : static void appendOrderBySuffix(Oid sortop, Oid sortcoltype, bool nulls_first,
     195              :                                                                 deparse_expr_cxt *context);
     196              : static void appendAggOrderBy(List *orderList, List *targetList,
     197              :                                                          deparse_expr_cxt *context);
     198              : static void appendFunctionName(Oid funcid, deparse_expr_cxt *context);
     199              : static Node *deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
     200              :                                                                         deparse_expr_cxt *context);
     201              : 
     202              : /*
     203              :  * Helper functions
     204              :  */
     205              : static bool is_subquery_var(Var *node, RelOptInfo *foreignrel,
     206              :                                                         int *relno, int *colno);
     207              : static void get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
     208              :                                                                                   int *relno, int *colno);
     209              : 
     210              : 
     211              : /*
     212              :  * Examine each qual clause in input_conds, and classify them into two groups,
     213              :  * which are returned as two lists:
     214              :  *      - remote_conds contains expressions that can be evaluated remotely
     215              :  *      - local_conds contains expressions that can't be evaluated remotely
     216              :  */
     217              : void
     218            0 : classifyConditions(PlannerInfo *root,
     219              :                                    RelOptInfo *baserel,
     220              :                                    List *input_conds,
     221              :                                    List **remote_conds,
     222              :                                    List **local_conds)
     223              : {
     224            0 :         ListCell   *lc;
     225              : 
     226            0 :         *remote_conds = NIL;
     227            0 :         *local_conds = NIL;
     228              : 
     229            0 :         foreach(lc, input_conds)
     230              :         {
     231            0 :                 RestrictInfo *ri = lfirst_node(RestrictInfo, lc);
     232              : 
     233            0 :                 if (is_foreign_expr(root, baserel, ri->clause))
     234            0 :                         *remote_conds = lappend(*remote_conds, ri);
     235              :                 else
     236            0 :                         *local_conds = lappend(*local_conds, ri);
     237            0 :         }
     238            0 : }
     239              : 
     240              : /*
     241              :  * Returns true if given expr is safe to evaluate on the foreign server.
     242              :  */
     243              : bool
     244            0 : is_foreign_expr(PlannerInfo *root,
     245              :                                 RelOptInfo *baserel,
     246              :                                 Expr *expr)
     247              : {
     248            0 :         foreign_glob_cxt glob_cxt;
     249            0 :         foreign_loc_cxt loc_cxt;
     250            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) (baserel->fdw_private);
     251              : 
     252              :         /*
     253              :          * Check that the expression consists of nodes that are safe to execute
     254              :          * remotely.
     255              :          */
     256            0 :         glob_cxt.root = root;
     257            0 :         glob_cxt.foreignrel = baserel;
     258              : 
     259              :         /*
     260              :          * For an upper relation, use relids from its underneath scan relation,
     261              :          * because the upperrel's own relids currently aren't set to anything
     262              :          * meaningful by the core code.  For other relation, use their own relids.
     263              :          */
     264            0 :         if (IS_UPPER_REL(baserel))
     265            0 :                 glob_cxt.relids = fpinfo->outerrel->relids;
     266              :         else
     267            0 :                 glob_cxt.relids = baserel->relids;
     268            0 :         loc_cxt.collation = InvalidOid;
     269            0 :         loc_cxt.state = FDW_COLLATE_NONE;
     270            0 :         if (!foreign_expr_walker((Node *) expr, &glob_cxt, &loc_cxt, NULL))
     271            0 :                 return false;
     272              : 
     273              :         /*
     274              :          * If the expression has a valid collation that does not arise from a
     275              :          * foreign var, the expression can not be sent over.
     276              :          */
     277            0 :         if (loc_cxt.state == FDW_COLLATE_UNSAFE)
     278            0 :                 return false;
     279              : 
     280              :         /*
     281              :          * An expression which includes any mutable functions can't be sent over
     282              :          * because its result is not stable.  For example, sending now() remote
     283              :          * side could cause confusion from clock offsets.  Future versions might
     284              :          * be able to make this choice with more granularity.  (We check this last
     285              :          * because it requires a lot of expensive catalog lookups.)
     286              :          */
     287            0 :         if (contain_mutable_functions((Node *) expr))
     288            0 :                 return false;
     289              : 
     290              :         /* OK to evaluate on the remote server */
     291            0 :         return true;
     292            0 : }
     293              : 
     294              : /*
     295              :  * Check if expression is safe to execute remotely, and return true if so.
     296              :  *
     297              :  * In addition, *outer_cxt is updated with collation information.
     298              :  *
     299              :  * case_arg_cxt is NULL if this subexpression is not inside a CASE-with-arg.
     300              :  * Otherwise, it points to the collation info derived from the arg expression,
     301              :  * which must be consulted by any CaseTestExpr.
     302              :  *
     303              :  * We must check that the expression contains only node types we can deparse,
     304              :  * that all types/functions/operators are safe to send (they are "shippable"),
     305              :  * and that all collations used in the expression derive from Vars of the
     306              :  * foreign table.  Because of the latter, the logic is pretty close to
     307              :  * assign_collations_walker() in parse_collate.c, though we can assume here
     308              :  * that the given expression is valid.  Note function mutability is not
     309              :  * currently considered here.
     310              :  */
     311              : static bool
     312            0 : foreign_expr_walker(Node *node,
     313              :                                         foreign_glob_cxt *glob_cxt,
     314              :                                         foreign_loc_cxt *outer_cxt,
     315              :                                         foreign_loc_cxt *case_arg_cxt)
     316              : {
     317            0 :         bool            check_type = true;
     318            0 :         PgFdwRelationInfo *fpinfo;
     319            0 :         foreign_loc_cxt inner_cxt;
     320            0 :         Oid                     collation;
     321            0 :         FDWCollateState state;
     322              : 
     323              :         /* Need do nothing for empty subexpressions */
     324            0 :         if (node == NULL)
     325            0 :                 return true;
     326              : 
     327              :         /* May need server info from baserel's fdw_private struct */
     328            0 :         fpinfo = (PgFdwRelationInfo *) (glob_cxt->foreignrel->fdw_private);
     329              : 
     330              :         /* Set up inner_cxt for possible recursion to child nodes */
     331            0 :         inner_cxt.collation = InvalidOid;
     332            0 :         inner_cxt.state = FDW_COLLATE_NONE;
     333              : 
     334            0 :         switch (nodeTag(node))
     335              :         {
     336              :                 case T_Var:
     337              :                         {
     338            0 :                                 Var                *var = (Var *) node;
     339              : 
     340              :                                 /*
     341              :                                  * If the Var is from the foreign table, we consider its
     342              :                                  * collation (if any) safe to use.  If it is from another
     343              :                                  * table, we treat its collation the same way as we would a
     344              :                                  * Param's collation, ie it's not safe for it to have a
     345              :                                  * non-default collation.
     346              :                                  */
     347            0 :                                 if (bms_is_member(var->varno, glob_cxt->relids) &&
     348            0 :                                         var->varlevelsup == 0)
     349              :                                 {
     350              :                                         /* Var belongs to foreign table */
     351              : 
     352              :                                         /*
     353              :                                          * System columns other than ctid should not be sent to
     354              :                                          * the remote, since we don't make any effort to ensure
     355              :                                          * that local and remote values match (tableoid, in
     356              :                                          * particular, almost certainly doesn't match).
     357              :                                          */
     358            0 :                                         if (var->varattno < 0 &&
     359            0 :                                                 var->varattno != SelfItemPointerAttributeNumber)
     360            0 :                                                 return false;
     361              : 
     362              :                                         /* Else check the collation */
     363            0 :                                         collation = var->varcollid;
     364            0 :                                         state = OidIsValid(collation) ? FDW_COLLATE_SAFE : FDW_COLLATE_NONE;
     365            0 :                                 }
     366              :                                 else
     367              :                                 {
     368              :                                         /* Var belongs to some other table */
     369            0 :                                         collation = var->varcollid;
     370            0 :                                         if (collation == InvalidOid ||
     371            0 :                                                 collation == DEFAULT_COLLATION_OID)
     372              :                                         {
     373              :                                                 /*
     374              :                                                  * It's noncollatable, or it's safe to combine with a
     375              :                                                  * collatable foreign Var, so set state to NONE.
     376              :                                                  */
     377            0 :                                                 state = FDW_COLLATE_NONE;
     378            0 :                                         }
     379              :                                         else
     380              :                                         {
     381              :                                                 /*
     382              :                                                  * Do not fail right away, since the Var might appear
     383              :                                                  * in a collation-insensitive context.
     384              :                                                  */
     385            0 :                                                 state = FDW_COLLATE_UNSAFE;
     386              :                                         }
     387              :                                 }
     388            0 :                         }
     389            0 :                         break;
     390              :                 case T_Const:
     391              :                         {
     392            0 :                                 Const      *c = (Const *) node;
     393              : 
     394              :                                 /*
     395              :                                  * Constants of regproc and related types can't be shipped
     396              :                                  * unless the referenced object is shippable.  But NULL's ok.
     397              :                                  * (See also the related code in dependency.c.)
     398              :                                  */
     399            0 :                                 if (!c->constisnull)
     400              :                                 {
     401            0 :                                         switch (c->consttype)
     402              :                                         {
     403              :                                                 case REGPROCOID:
     404              :                                                 case REGPROCEDUREOID:
     405            0 :                                                         if (!is_shippable(DatumGetObjectId(c->constvalue),
     406            0 :                                                                                           ProcedureRelationId, fpinfo))
     407            0 :                                                                 return false;
     408            0 :                                                         break;
     409              :                                                 case REGOPEROID:
     410              :                                                 case REGOPERATOROID:
     411            0 :                                                         if (!is_shippable(DatumGetObjectId(c->constvalue),
     412            0 :                                                                                           OperatorRelationId, fpinfo))
     413            0 :                                                                 return false;
     414            0 :                                                         break;
     415              :                                                 case REGCLASSOID:
     416            0 :                                                         if (!is_shippable(DatumGetObjectId(c->constvalue),
     417            0 :                                                                                           RelationRelationId, fpinfo))
     418            0 :                                                                 return false;
     419            0 :                                                         break;
     420              :                                                 case REGTYPEOID:
     421            0 :                                                         if (!is_shippable(DatumGetObjectId(c->constvalue),
     422            0 :                                                                                           TypeRelationId, fpinfo))
     423            0 :                                                                 return false;
     424            0 :                                                         break;
     425              :                                                 case REGCOLLATIONOID:
     426            0 :                                                         if (!is_shippable(DatumGetObjectId(c->constvalue),
     427            0 :                                                                                           CollationRelationId, fpinfo))
     428            0 :                                                                 return false;
     429            0 :                                                         break;
     430              :                                                 case REGCONFIGOID:
     431              : 
     432              :                                                         /*
     433              :                                                          * For text search objects only, we weaken the
     434              :                                                          * normal shippability criterion to allow all OIDs
     435              :                                                          * below FirstNormalObjectId.  Without this, none
     436              :                                                          * of the initdb-installed TS configurations would
     437              :                                                          * be shippable, which would be quite annoying.
     438              :                                                          */
     439            0 :                                                         if (DatumGetObjectId(c->constvalue) >= FirstNormalObjectId &&
     440            0 :                                                                 !is_shippable(DatumGetObjectId(c->constvalue),
     441            0 :                                                                                           TSConfigRelationId, fpinfo))
     442            0 :                                                                 return false;
     443            0 :                                                         break;
     444              :                                                 case REGDICTIONARYOID:
     445            0 :                                                         if (DatumGetObjectId(c->constvalue) >= FirstNormalObjectId &&
     446            0 :                                                                 !is_shippable(DatumGetObjectId(c->constvalue),
     447            0 :                                                                                           TSDictionaryRelationId, fpinfo))
     448            0 :                                                                 return false;
     449            0 :                                                         break;
     450              :                                                 case REGNAMESPACEOID:
     451            0 :                                                         if (!is_shippable(DatumGetObjectId(c->constvalue),
     452            0 :                                                                                           NamespaceRelationId, fpinfo))
     453            0 :                                                                 return false;
     454            0 :                                                         break;
     455              :                                                 case REGROLEOID:
     456            0 :                                                         if (!is_shippable(DatumGetObjectId(c->constvalue),
     457            0 :                                                                                           AuthIdRelationId, fpinfo))
     458            0 :                                                                 return false;
     459            0 :                                                         break;
     460              :                                                 case REGDATABASEOID:
     461            0 :                                                         if (!is_shippable(DatumGetObjectId(c->constvalue),
     462            0 :                                                                                           DatabaseRelationId, fpinfo))
     463            0 :                                                                 return false;
     464            0 :                                                         break;
     465              :                                         }
     466            0 :                                 }
     467              : 
     468              :                                 /*
     469              :                                  * If the constant has nondefault collation, either it's of a
     470              :                                  * non-builtin type, or it reflects folding of a CollateExpr.
     471              :                                  * It's unsafe to send to the remote unless it's used in a
     472              :                                  * non-collation-sensitive context.
     473              :                                  */
     474            0 :                                 collation = c->constcollid;
     475            0 :                                 if (collation == InvalidOid ||
     476            0 :                                         collation == DEFAULT_COLLATION_OID)
     477            0 :                                         state = FDW_COLLATE_NONE;
     478              :                                 else
     479            0 :                                         state = FDW_COLLATE_UNSAFE;
     480            0 :                         }
     481            0 :                         break;
     482              :                 case T_Param:
     483              :                         {
     484            0 :                                 Param      *p = (Param *) node;
     485              : 
     486              :                                 /*
     487              :                                  * If it's a MULTIEXPR Param, punt.  We can't tell from here
     488              :                                  * whether the referenced sublink/subplan contains any remote
     489              :                                  * Vars; if it does, handling that is too complicated to
     490              :                                  * consider supporting at present.  Fortunately, MULTIEXPR
     491              :                                  * Params are not reduced to plain PARAM_EXEC until the end of
     492              :                                  * planning, so we can easily detect this case.  (Normal
     493              :                                  * PARAM_EXEC Params are safe to ship because their values
     494              :                                  * come from somewhere else in the plan tree; but a MULTIEXPR
     495              :                                  * references a sub-select elsewhere in the same targetlist,
     496              :                                  * so we'd be on the hook to evaluate it somehow if we wanted
     497              :                                  * to handle such cases as direct foreign updates.)
     498              :                                  */
     499            0 :                                 if (p->paramkind == PARAM_MULTIEXPR)
     500            0 :                                         return false;
     501              : 
     502              :                                 /*
     503              :                                  * Collation rule is same as for Consts and non-foreign Vars.
     504              :                                  */
     505            0 :                                 collation = p->paramcollid;
     506            0 :                                 if (collation == InvalidOid ||
     507            0 :                                         collation == DEFAULT_COLLATION_OID)
     508            0 :                                         state = FDW_COLLATE_NONE;
     509              :                                 else
     510            0 :                                         state = FDW_COLLATE_UNSAFE;
     511            0 :                         }
     512            0 :                         break;
     513              :                 case T_SubscriptingRef:
     514              :                         {
     515            0 :                                 SubscriptingRef *sr = (SubscriptingRef *) node;
     516              : 
     517              :                                 /* Assignment should not be in restrictions. */
     518            0 :                                 if (sr->refassgnexpr != NULL)
     519            0 :                                         return false;
     520              : 
     521              :                                 /*
     522              :                                  * Recurse into the remaining subexpressions.  The container
     523              :                                  * subscripts will not affect collation of the SubscriptingRef
     524              :                                  * result, so do those first and reset inner_cxt afterwards.
     525              :                                  */
     526            0 :                                 if (!foreign_expr_walker((Node *) sr->refupperindexpr,
     527            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     528            0 :                                         return false;
     529            0 :                                 inner_cxt.collation = InvalidOid;
     530            0 :                                 inner_cxt.state = FDW_COLLATE_NONE;
     531            0 :                                 if (!foreign_expr_walker((Node *) sr->reflowerindexpr,
     532            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     533            0 :                                         return false;
     534            0 :                                 inner_cxt.collation = InvalidOid;
     535            0 :                                 inner_cxt.state = FDW_COLLATE_NONE;
     536            0 :                                 if (!foreign_expr_walker((Node *) sr->refexpr,
     537            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     538            0 :                                         return false;
     539              : 
     540              :                                 /*
     541              :                                  * Container subscripting typically yields same collation as
     542              :                                  * refexpr's, but in case it doesn't, use same logic as for
     543              :                                  * function nodes.
     544              :                                  */
     545            0 :                                 collation = sr->refcollid;
     546            0 :                                 if (collation == InvalidOid)
     547            0 :                                         state = FDW_COLLATE_NONE;
     548            0 :                                 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
     549            0 :                                                  collation == inner_cxt.collation)
     550            0 :                                         state = FDW_COLLATE_SAFE;
     551            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
     552            0 :                                         state = FDW_COLLATE_NONE;
     553              :                                 else
     554            0 :                                         state = FDW_COLLATE_UNSAFE;
     555            0 :                         }
     556            0 :                         break;
     557              :                 case T_FuncExpr:
     558              :                         {
     559            0 :                                 FuncExpr   *fe = (FuncExpr *) node;
     560              : 
     561              :                                 /*
     562              :                                  * If function used by the expression is not shippable, it
     563              :                                  * can't be sent to remote because it might have incompatible
     564              :                                  * semantics on remote side.
     565              :                                  */
     566            0 :                                 if (!is_shippable(fe->funcid, ProcedureRelationId, fpinfo))
     567            0 :                                         return false;
     568              : 
     569              :                                 /*
     570              :                                  * Recurse to input subexpressions.
     571              :                                  */
     572            0 :                                 if (!foreign_expr_walker((Node *) fe->args,
     573            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     574            0 :                                         return false;
     575              : 
     576              :                                 /*
     577              :                                  * If function's input collation is not derived from a foreign
     578              :                                  * Var, it can't be sent to remote.
     579              :                                  */
     580            0 :                                 if (fe->inputcollid == InvalidOid)
     581              :                                          /* OK, inputs are all noncollatable */ ;
     582            0 :                                 else if (inner_cxt.state != FDW_COLLATE_SAFE ||
     583            0 :                                                  fe->inputcollid != inner_cxt.collation)
     584            0 :                                         return false;
     585              : 
     586              :                                 /*
     587              :                                  * Detect whether node is introducing a collation not derived
     588              :                                  * from a foreign Var.  (If so, we just mark it unsafe for now
     589              :                                  * rather than immediately returning false, since the parent
     590              :                                  * node might not care.)
     591              :                                  */
     592            0 :                                 collation = fe->funccollid;
     593            0 :                                 if (collation == InvalidOid)
     594            0 :                                         state = FDW_COLLATE_NONE;
     595            0 :                                 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
     596            0 :                                                  collation == inner_cxt.collation)
     597            0 :                                         state = FDW_COLLATE_SAFE;
     598            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
     599            0 :                                         state = FDW_COLLATE_NONE;
     600              :                                 else
     601            0 :                                         state = FDW_COLLATE_UNSAFE;
     602            0 :                         }
     603            0 :                         break;
     604              :                 case T_OpExpr:
     605              :                 case T_DistinctExpr:    /* struct-equivalent to OpExpr */
     606              :                         {
     607            0 :                                 OpExpr     *oe = (OpExpr *) node;
     608              : 
     609              :                                 /*
     610              :                                  * Similarly, only shippable operators can be sent to remote.
     611              :                                  * (If the operator is shippable, we assume its underlying
     612              :                                  * function is too.)
     613              :                                  */
     614            0 :                                 if (!is_shippable(oe->opno, OperatorRelationId, fpinfo))
     615            0 :                                         return false;
     616              : 
     617              :                                 /*
     618              :                                  * Recurse to input subexpressions.
     619              :                                  */
     620            0 :                                 if (!foreign_expr_walker((Node *) oe->args,
     621            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     622            0 :                                         return false;
     623              : 
     624              :                                 /*
     625              :                                  * If operator's input collation is not derived from a foreign
     626              :                                  * Var, it can't be sent to remote.
     627              :                                  */
     628            0 :                                 if (oe->inputcollid == InvalidOid)
     629              :                                          /* OK, inputs are all noncollatable */ ;
     630            0 :                                 else if (inner_cxt.state != FDW_COLLATE_SAFE ||
     631            0 :                                                  oe->inputcollid != inner_cxt.collation)
     632            0 :                                         return false;
     633              : 
     634              :                                 /* Result-collation handling is same as for functions */
     635            0 :                                 collation = oe->opcollid;
     636            0 :                                 if (collation == InvalidOid)
     637            0 :                                         state = FDW_COLLATE_NONE;
     638            0 :                                 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
     639            0 :                                                  collation == inner_cxt.collation)
     640            0 :                                         state = FDW_COLLATE_SAFE;
     641            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
     642            0 :                                         state = FDW_COLLATE_NONE;
     643              :                                 else
     644            0 :                                         state = FDW_COLLATE_UNSAFE;
     645            0 :                         }
     646            0 :                         break;
     647              :                 case T_ScalarArrayOpExpr:
     648              :                         {
     649            0 :                                 ScalarArrayOpExpr *oe = (ScalarArrayOpExpr *) node;
     650              : 
     651              :                                 /*
     652              :                                  * Again, only shippable operators can be sent to remote.
     653              :                                  */
     654            0 :                                 if (!is_shippable(oe->opno, OperatorRelationId, fpinfo))
     655            0 :                                         return false;
     656              : 
     657              :                                 /*
     658              :                                  * Recurse to input subexpressions.
     659              :                                  */
     660            0 :                                 if (!foreign_expr_walker((Node *) oe->args,
     661            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     662            0 :                                         return false;
     663              : 
     664              :                                 /*
     665              :                                  * If operator's input collation is not derived from a foreign
     666              :                                  * Var, it can't be sent to remote.
     667              :                                  */
     668            0 :                                 if (oe->inputcollid == InvalidOid)
     669              :                                          /* OK, inputs are all noncollatable */ ;
     670            0 :                                 else if (inner_cxt.state != FDW_COLLATE_SAFE ||
     671            0 :                                                  oe->inputcollid != inner_cxt.collation)
     672            0 :                                         return false;
     673              : 
     674              :                                 /* Output is always boolean and so noncollatable. */
     675            0 :                                 collation = InvalidOid;
     676            0 :                                 state = FDW_COLLATE_NONE;
     677            0 :                         }
     678            0 :                         break;
     679              :                 case T_RelabelType:
     680              :                         {
     681            0 :                                 RelabelType *r = (RelabelType *) node;
     682              : 
     683              :                                 /*
     684              :                                  * Recurse to input subexpression.
     685              :                                  */
     686            0 :                                 if (!foreign_expr_walker((Node *) r->arg,
     687            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     688            0 :                                         return false;
     689              : 
     690              :                                 /*
     691              :                                  * RelabelType must not introduce a collation not derived from
     692              :                                  * an input foreign Var (same logic as for a real function).
     693              :                                  */
     694            0 :                                 collation = r->resultcollid;
     695            0 :                                 if (collation == InvalidOid)
     696            0 :                                         state = FDW_COLLATE_NONE;
     697            0 :                                 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
     698            0 :                                                  collation == inner_cxt.collation)
     699            0 :                                         state = FDW_COLLATE_SAFE;
     700            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
     701            0 :                                         state = FDW_COLLATE_NONE;
     702              :                                 else
     703            0 :                                         state = FDW_COLLATE_UNSAFE;
     704            0 :                         }
     705            0 :                         break;
     706              :                 case T_ArrayCoerceExpr:
     707              :                         {
     708            0 :                                 ArrayCoerceExpr *e = (ArrayCoerceExpr *) node;
     709              : 
     710              :                                 /*
     711              :                                  * Recurse to input subexpression.
     712              :                                  */
     713            0 :                                 if (!foreign_expr_walker((Node *) e->arg,
     714            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     715            0 :                                         return false;
     716              : 
     717              :                                 /*
     718              :                                  * T_ArrayCoerceExpr must not introduce a collation not
     719              :                                  * derived from an input foreign Var (same logic as for a
     720              :                                  * function).
     721              :                                  */
     722            0 :                                 collation = e->resultcollid;
     723            0 :                                 if (collation == InvalidOid)
     724            0 :                                         state = FDW_COLLATE_NONE;
     725            0 :                                 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
     726            0 :                                                  collation == inner_cxt.collation)
     727            0 :                                         state = FDW_COLLATE_SAFE;
     728            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
     729            0 :                                         state = FDW_COLLATE_NONE;
     730              :                                 else
     731            0 :                                         state = FDW_COLLATE_UNSAFE;
     732            0 :                         }
     733            0 :                         break;
     734              :                 case T_BoolExpr:
     735              :                         {
     736            0 :                                 BoolExpr   *b = (BoolExpr *) node;
     737              : 
     738              :                                 /*
     739              :                                  * Recurse to input subexpressions.
     740              :                                  */
     741            0 :                                 if (!foreign_expr_walker((Node *) b->args,
     742            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     743            0 :                                         return false;
     744              : 
     745              :                                 /* Output is always boolean and so noncollatable. */
     746            0 :                                 collation = InvalidOid;
     747            0 :                                 state = FDW_COLLATE_NONE;
     748            0 :                         }
     749            0 :                         break;
     750              :                 case T_NullTest:
     751              :                         {
     752            0 :                                 NullTest   *nt = (NullTest *) node;
     753              : 
     754              :                                 /*
     755              :                                  * Recurse to input subexpressions.
     756              :                                  */
     757            0 :                                 if (!foreign_expr_walker((Node *) nt->arg,
     758            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     759            0 :                                         return false;
     760              : 
     761              :                                 /* Output is always boolean and so noncollatable. */
     762            0 :                                 collation = InvalidOid;
     763            0 :                                 state = FDW_COLLATE_NONE;
     764            0 :                         }
     765            0 :                         break;
     766              :                 case T_CaseExpr:
     767              :                         {
     768            0 :                                 CaseExpr   *ce = (CaseExpr *) node;
     769            0 :                                 foreign_loc_cxt arg_cxt;
     770            0 :                                 foreign_loc_cxt tmp_cxt;
     771            0 :                                 ListCell   *lc;
     772              : 
     773              :                                 /*
     774              :                                  * Recurse to CASE's arg expression, if any.  Its collation
     775              :                                  * has to be saved aside for use while examining CaseTestExprs
     776              :                                  * within the WHEN expressions.
     777              :                                  */
     778            0 :                                 arg_cxt.collation = InvalidOid;
     779            0 :                                 arg_cxt.state = FDW_COLLATE_NONE;
     780            0 :                                 if (ce->arg)
     781              :                                 {
     782            0 :                                         if (!foreign_expr_walker((Node *) ce->arg,
     783            0 :                                                                                          glob_cxt, &arg_cxt, case_arg_cxt))
     784            0 :                                                 return false;
     785            0 :                                 }
     786              : 
     787              :                                 /* Examine the CaseWhen subexpressions. */
     788            0 :                                 foreach(lc, ce->args)
     789              :                                 {
     790            0 :                                         CaseWhen   *cw = lfirst_node(CaseWhen, lc);
     791              : 
     792            0 :                                         if (ce->arg)
     793              :                                         {
     794              :                                                 /*
     795              :                                                  * In a CASE-with-arg, the parser should have produced
     796              :                                                  * WHEN clauses of the form "CaseTestExpr = RHS",
     797              :                                                  * possibly with an implicit coercion inserted above
     798              :                                                  * the CaseTestExpr.  However in an expression that's
     799              :                                                  * been through the optimizer, the WHEN clause could
     800              :                                                  * be almost anything (since the equality operator
     801              :                                                  * could have been expanded into an inline function).
     802              :                                                  * In such cases forbid pushdown, because
     803              :                                                  * deparseCaseExpr can't handle it.
     804              :                                                  */
     805            0 :                                                 Node       *whenExpr = (Node *) cw->expr;
     806            0 :                                                 List       *opArgs;
     807              : 
     808            0 :                                                 if (!IsA(whenExpr, OpExpr))
     809            0 :                                                         return false;
     810              : 
     811            0 :                                                 opArgs = ((OpExpr *) whenExpr)->args;
     812            0 :                                                 if (list_length(opArgs) != 2 ||
     813            0 :                                                         !IsA(strip_implicit_coercions(linitial(opArgs)),
     814              :                                                                  CaseTestExpr))
     815            0 :                                                         return false;
     816            0 :                                         }
     817              : 
     818              :                                         /*
     819              :                                          * Recurse to WHEN expression, passing down the arg info.
     820              :                                          * Its collation doesn't affect the result (really, it
     821              :                                          * should be boolean and thus not have a collation).
     822              :                                          */
     823            0 :                                         tmp_cxt.collation = InvalidOid;
     824            0 :                                         tmp_cxt.state = FDW_COLLATE_NONE;
     825            0 :                                         if (!foreign_expr_walker((Node *) cw->expr,
     826            0 :                                                                                          glob_cxt, &tmp_cxt, &arg_cxt))
     827            0 :                                                 return false;
     828              : 
     829              :                                         /* Recurse to THEN expression. */
     830            0 :                                         if (!foreign_expr_walker((Node *) cw->result,
     831            0 :                                                                                          glob_cxt, &inner_cxt, case_arg_cxt))
     832            0 :                                                 return false;
     833            0 :                                 }
     834              : 
     835              :                                 /* Recurse to ELSE expression. */
     836            0 :                                 if (!foreign_expr_walker((Node *) ce->defresult,
     837            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     838            0 :                                         return false;
     839              : 
     840              :                                 /*
     841              :                                  * Detect whether node is introducing a collation not derived
     842              :                                  * from a foreign Var.  (If so, we just mark it unsafe for now
     843              :                                  * rather than immediately returning false, since the parent
     844              :                                  * node might not care.)  This is the same as for function
     845              :                                  * nodes, except that the input collation is derived from only
     846              :                                  * the THEN and ELSE subexpressions.
     847              :                                  */
     848            0 :                                 collation = ce->casecollid;
     849            0 :                                 if (collation == InvalidOid)
     850            0 :                                         state = FDW_COLLATE_NONE;
     851            0 :                                 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
     852            0 :                                                  collation == inner_cxt.collation)
     853            0 :                                         state = FDW_COLLATE_SAFE;
     854            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
     855            0 :                                         state = FDW_COLLATE_NONE;
     856              :                                 else
     857            0 :                                         state = FDW_COLLATE_UNSAFE;
     858            0 :                         }
     859            0 :                         break;
     860              :                 case T_CaseTestExpr:
     861              :                         {
     862            0 :                                 CaseTestExpr *c = (CaseTestExpr *) node;
     863              : 
     864              :                                 /* Punt if we seem not to be inside a CASE arg WHEN. */
     865            0 :                                 if (!case_arg_cxt)
     866            0 :                                         return false;
     867              : 
     868              :                                 /*
     869              :                                  * Otherwise, any nondefault collation attached to the
     870              :                                  * CaseTestExpr node must be derived from foreign Var(s) in
     871              :                                  * the CASE arg.
     872              :                                  */
     873            0 :                                 collation = c->collation;
     874            0 :                                 if (collation == InvalidOid)
     875            0 :                                         state = FDW_COLLATE_NONE;
     876            0 :                                 else if (case_arg_cxt->state == FDW_COLLATE_SAFE &&
     877            0 :                                                  collation == case_arg_cxt->collation)
     878            0 :                                         state = FDW_COLLATE_SAFE;
     879            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
     880            0 :                                         state = FDW_COLLATE_NONE;
     881              :                                 else
     882            0 :                                         state = FDW_COLLATE_UNSAFE;
     883            0 :                         }
     884            0 :                         break;
     885              :                 case T_ArrayExpr:
     886              :                         {
     887            0 :                                 ArrayExpr  *a = (ArrayExpr *) node;
     888              : 
     889              :                                 /*
     890              :                                  * Recurse to input subexpressions.
     891              :                                  */
     892            0 :                                 if (!foreign_expr_walker((Node *) a->elements,
     893            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
     894            0 :                                         return false;
     895              : 
     896              :                                 /*
     897              :                                  * ArrayExpr must not introduce a collation not derived from
     898              :                                  * an input foreign Var (same logic as for a function).
     899              :                                  */
     900            0 :                                 collation = a->array_collid;
     901            0 :                                 if (collation == InvalidOid)
     902            0 :                                         state = FDW_COLLATE_NONE;
     903            0 :                                 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
     904            0 :                                                  collation == inner_cxt.collation)
     905            0 :                                         state = FDW_COLLATE_SAFE;
     906            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
     907            0 :                                         state = FDW_COLLATE_NONE;
     908              :                                 else
     909            0 :                                         state = FDW_COLLATE_UNSAFE;
     910            0 :                         }
     911            0 :                         break;
     912              :                 case T_List:
     913              :                         {
     914            0 :                                 List       *l = (List *) node;
     915            0 :                                 ListCell   *lc;
     916              : 
     917              :                                 /*
     918              :                                  * Recurse to component subexpressions.
     919              :                                  */
     920            0 :                                 foreach(lc, l)
     921              :                                 {
     922            0 :                                         if (!foreign_expr_walker((Node *) lfirst(lc),
     923            0 :                                                                                          glob_cxt, &inner_cxt, case_arg_cxt))
     924            0 :                                                 return false;
     925            0 :                                 }
     926              : 
     927              :                                 /*
     928              :                                  * When processing a list, collation state just bubbles up
     929              :                                  * from the list elements.
     930              :                                  */
     931            0 :                                 collation = inner_cxt.collation;
     932            0 :                                 state = inner_cxt.state;
     933              : 
     934              :                                 /* Don't apply exprType() to the list. */
     935            0 :                                 check_type = false;
     936            0 :                         }
     937            0 :                         break;
     938              :                 case T_Aggref:
     939              :                         {
     940            0 :                                 Aggref     *agg = (Aggref *) node;
     941            0 :                                 ListCell   *lc;
     942              : 
     943              :                                 /* Not safe to pushdown when not in grouping context */
     944            0 :                                 if (!IS_UPPER_REL(glob_cxt->foreignrel))
     945            0 :                                         return false;
     946              : 
     947              :                                 /* Only non-split aggregates are pushable. */
     948            0 :                                 if (agg->aggsplit != AGGSPLIT_SIMPLE)
     949            0 :                                         return false;
     950              : 
     951              :                                 /* As usual, it must be shippable. */
     952            0 :                                 if (!is_shippable(agg->aggfnoid, ProcedureRelationId, fpinfo))
     953            0 :                                         return false;
     954              : 
     955              :                                 /*
     956              :                                  * Recurse to input args. aggdirectargs, aggorder and
     957              :                                  * aggdistinct are all present in args, so no need to check
     958              :                                  * their shippability explicitly.
     959              :                                  */
     960            0 :                                 foreach(lc, agg->args)
     961              :                                 {
     962            0 :                                         Node       *n = (Node *) lfirst(lc);
     963              : 
     964              :                                         /* If TargetEntry, extract the expression from it */
     965            0 :                                         if (IsA(n, TargetEntry))
     966              :                                         {
     967            0 :                                                 TargetEntry *tle = (TargetEntry *) n;
     968              : 
     969            0 :                                                 n = (Node *) tle->expr;
     970            0 :                                         }
     971              : 
     972            0 :                                         if (!foreign_expr_walker(n,
     973            0 :                                                                                          glob_cxt, &inner_cxt, case_arg_cxt))
     974            0 :                                                 return false;
     975            0 :                                 }
     976              : 
     977              :                                 /*
     978              :                                  * For aggorder elements, check whether the sort operator, if
     979              :                                  * specified, is shippable or not.
     980              :                                  */
     981            0 :                                 if (agg->aggorder)
     982              :                                 {
     983            0 :                                         foreach(lc, agg->aggorder)
     984              :                                         {
     985            0 :                                                 SortGroupClause *srt = (SortGroupClause *) lfirst(lc);
     986            0 :                                                 Oid                     sortcoltype;
     987            0 :                                                 TypeCacheEntry *typentry;
     988            0 :                                                 TargetEntry *tle;
     989              : 
     990            0 :                                                 tle = get_sortgroupref_tle(srt->tleSortGroupRef,
     991            0 :                                                                                                    agg->args);
     992            0 :                                                 sortcoltype = exprType((Node *) tle->expr);
     993            0 :                                                 typentry = lookup_type_cache(sortcoltype,
     994              :                                                                                                          TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
     995              :                                                 /* Check shippability of non-default sort operator. */
     996            0 :                                                 if (srt->sortop != typentry->lt_opr &&
     997            0 :                                                         srt->sortop != typentry->gt_opr &&
     998            0 :                                                         !is_shippable(srt->sortop, OperatorRelationId,
     999            0 :                                                                                   fpinfo))
    1000            0 :                                                         return false;
    1001            0 :                                         }
    1002            0 :                                 }
    1003              : 
    1004              :                                 /* Check aggregate filter */
    1005            0 :                                 if (!foreign_expr_walker((Node *) agg->aggfilter,
    1006            0 :                                                                                  glob_cxt, &inner_cxt, case_arg_cxt))
    1007            0 :                                         return false;
    1008              : 
    1009              :                                 /*
    1010              :                                  * If aggregate's input collation is not derived from a
    1011              :                                  * foreign Var, it can't be sent to remote.
    1012              :                                  */
    1013            0 :                                 if (agg->inputcollid == InvalidOid)
    1014              :                                          /* OK, inputs are all noncollatable */ ;
    1015            0 :                                 else if (inner_cxt.state != FDW_COLLATE_SAFE ||
    1016            0 :                                                  agg->inputcollid != inner_cxt.collation)
    1017            0 :                                         return false;
    1018              : 
    1019              :                                 /*
    1020              :                                  * Detect whether node is introducing a collation not derived
    1021              :                                  * from a foreign Var.  (If so, we just mark it unsafe for now
    1022              :                                  * rather than immediately returning false, since the parent
    1023              :                                  * node might not care.)
    1024              :                                  */
    1025            0 :                                 collation = agg->aggcollid;
    1026            0 :                                 if (collation == InvalidOid)
    1027            0 :                                         state = FDW_COLLATE_NONE;
    1028            0 :                                 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
    1029            0 :                                                  collation == inner_cxt.collation)
    1030            0 :                                         state = FDW_COLLATE_SAFE;
    1031            0 :                                 else if (collation == DEFAULT_COLLATION_OID)
    1032            0 :                                         state = FDW_COLLATE_NONE;
    1033              :                                 else
    1034            0 :                                         state = FDW_COLLATE_UNSAFE;
    1035            0 :                         }
    1036            0 :                         break;
    1037              :                 default:
    1038              : 
    1039              :                         /*
    1040              :                          * If it's anything else, assume it's unsafe.  This list can be
    1041              :                          * expanded later, but don't forget to add deparse support below.
    1042              :                          */
    1043            0 :                         return false;
    1044              :         }
    1045              : 
    1046              :         /*
    1047              :          * If result type of given expression is not shippable, it can't be sent
    1048              :          * to remote because it might have incompatible semantics on remote side.
    1049              :          */
    1050            0 :         if (check_type && !is_shippable(exprType(node), TypeRelationId, fpinfo))
    1051            0 :                 return false;
    1052              : 
    1053              :         /*
    1054              :          * Now, merge my collation information into my parent's state.
    1055              :          */
    1056            0 :         if (state > outer_cxt->state)
    1057              :         {
    1058              :                 /* Override previous parent state */
    1059            0 :                 outer_cxt->collation = collation;
    1060            0 :                 outer_cxt->state = state;
    1061            0 :         }
    1062            0 :         else if (state == outer_cxt->state)
    1063              :         {
    1064              :                 /* Merge, or detect error if there's a collation conflict */
    1065            0 :                 switch (state)
    1066              :                 {
    1067              :                         case FDW_COLLATE_NONE:
    1068              :                                 /* Nothing + nothing is still nothing */
    1069              :                                 break;
    1070              :                         case FDW_COLLATE_SAFE:
    1071            0 :                                 if (collation != outer_cxt->collation)
    1072              :                                 {
    1073              :                                         /*
    1074              :                                          * Non-default collation always beats default.
    1075              :                                          */
    1076            0 :                                         if (outer_cxt->collation == DEFAULT_COLLATION_OID)
    1077              :                                         {
    1078              :                                                 /* Override previous parent state */
    1079            0 :                                                 outer_cxt->collation = collation;
    1080            0 :                                         }
    1081            0 :                                         else if (collation != DEFAULT_COLLATION_OID)
    1082              :                                         {
    1083              :                                                 /*
    1084              :                                                  * Conflict; show state as indeterminate.  We don't
    1085              :                                                  * want to "return false" right away, since parent
    1086              :                                                  * node might not care about collation.
    1087              :                                                  */
    1088            0 :                                                 outer_cxt->state = FDW_COLLATE_UNSAFE;
    1089            0 :                                         }
    1090            0 :                                 }
    1091            0 :                                 break;
    1092              :                         case FDW_COLLATE_UNSAFE:
    1093              :                                 /* We're still conflicted ... */
    1094              :                                 break;
    1095              :                 }
    1096            0 :         }
    1097              : 
    1098              :         /* It looks OK */
    1099            0 :         return true;
    1100            0 : }
    1101              : 
    1102              : /*
    1103              :  * Returns true if given expr is something we'd have to send the value of
    1104              :  * to the foreign server.
    1105              :  *
    1106              :  * This should return true when the expression is a shippable node that
    1107              :  * deparseExpr would add to context->params_list.  Note that we don't care
    1108              :  * if the expression *contains* such a node, only whether one appears at top
    1109              :  * level.  We need this to detect cases where setrefs.c would recognize a
    1110              :  * false match between an fdw_exprs item (which came from the params_list)
    1111              :  * and an entry in fdw_scan_tlist (which we're considering putting the given
    1112              :  * expression into).
    1113              :  */
    1114              : bool
    1115            0 : is_foreign_param(PlannerInfo *root,
    1116              :                                  RelOptInfo *baserel,
    1117              :                                  Expr *expr)
    1118              : {
    1119            0 :         if (expr == NULL)
    1120            0 :                 return false;
    1121              : 
    1122            0 :         switch (nodeTag(expr))
    1123              :         {
    1124              :                 case T_Var:
    1125              :                         {
    1126              :                                 /* It would have to be sent unless it's a foreign Var */
    1127            0 :                                 Var                *var = (Var *) expr;
    1128            0 :                                 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) (baserel->fdw_private);
    1129            0 :                                 Relids          relids;
    1130              : 
    1131            0 :                                 if (IS_UPPER_REL(baserel))
    1132            0 :                                         relids = fpinfo->outerrel->relids;
    1133              :                                 else
    1134            0 :                                         relids = baserel->relids;
    1135              : 
    1136            0 :                                 if (bms_is_member(var->varno, relids) && var->varlevelsup == 0)
    1137            0 :                                         return false;   /* foreign Var, so not a param */
    1138              :                                 else
    1139            0 :                                         return true;    /* it'd have to be a param */
    1140              :                                 break;
    1141            0 :                         }
    1142              :                 case T_Param:
    1143              :                         /* Params always have to be sent to the foreign server */
    1144            0 :                         return true;
    1145              :                 default:
    1146            0 :                         break;
    1147              :         }
    1148            0 :         return false;
    1149            0 : }
    1150              : 
    1151              : /*
    1152              :  * Returns true if it's safe to push down the sort expression described by
    1153              :  * 'pathkey' to the foreign server.
    1154              :  */
    1155              : bool
    1156            0 : is_foreign_pathkey(PlannerInfo *root,
    1157              :                                    RelOptInfo *baserel,
    1158              :                                    PathKey *pathkey)
    1159              : {
    1160            0 :         EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
    1161            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) baserel->fdw_private;
    1162              : 
    1163              :         /*
    1164              :          * is_foreign_expr would detect volatile expressions as well, but checking
    1165              :          * ec_has_volatile here saves some cycles.
    1166              :          */
    1167            0 :         if (pathkey_ec->ec_has_volatile)
    1168            0 :                 return false;
    1169              : 
    1170              :         /* can't push down the sort if the pathkey's opfamily is not shippable */
    1171            0 :         if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId, fpinfo))
    1172            0 :                 return false;
    1173              : 
    1174              :         /* can push if a suitable EC member exists */
    1175            0 :         return (find_em_for_rel(root, pathkey_ec, baserel) != NULL);
    1176            0 : }
    1177              : 
    1178              : /*
    1179              :  * Convert type OID + typmod info into a type name we can ship to the remote
    1180              :  * server.  Someplace else had better have verified that this type name is
    1181              :  * expected to be known on the remote end.
    1182              :  *
    1183              :  * This is almost just format_type_with_typemod(), except that if left to its
    1184              :  * own devices, that function will make schema-qualification decisions based
    1185              :  * on the local search_path, which is wrong.  We must schema-qualify all
    1186              :  * type names that are not in pg_catalog.  We assume here that built-in types
    1187              :  * are all in pg_catalog and need not be qualified; otherwise, qualify.
    1188              :  */
    1189              : static char *
    1190            0 : deparse_type_name(Oid type_oid, int32 typemod)
    1191              : {
    1192            0 :         bits16          flags = FORMAT_TYPE_TYPEMOD_GIVEN;
    1193              : 
    1194            0 :         if (!is_builtin(type_oid))
    1195            0 :                 flags |= FORMAT_TYPE_FORCE_QUALIFY;
    1196              : 
    1197            0 :         return format_type_extended(type_oid, typemod, flags);
    1198            0 : }
    1199              : 
    1200              : /*
    1201              :  * Build the targetlist for given relation to be deparsed as SELECT clause.
    1202              :  *
    1203              :  * The output targetlist contains the columns that need to be fetched from the
    1204              :  * foreign server for the given relation.  If foreignrel is an upper relation,
    1205              :  * then the output targetlist can also contain expressions to be evaluated on
    1206              :  * foreign server.
    1207              :  */
    1208              : List *
    1209            0 : build_tlist_to_deparse(RelOptInfo *foreignrel)
    1210              : {
    1211            0 :         List       *tlist = NIL;
    1212            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    1213            0 :         ListCell   *lc;
    1214              : 
    1215              :         /*
    1216              :          * For an upper relation, we have already built the target list while
    1217              :          * checking shippability, so just return that.
    1218              :          */
    1219            0 :         if (IS_UPPER_REL(foreignrel))
    1220            0 :                 return fpinfo->grouped_tlist;
    1221              : 
    1222              :         /*
    1223              :          * We require columns specified in foreignrel->reltarget->exprs and those
    1224              :          * required for evaluating the local conditions.
    1225              :          */
    1226            0 :         tlist = add_to_flat_tlist(tlist,
    1227            0 :                                                           pull_var_clause((Node *) foreignrel->reltarget->exprs,
    1228              :                                                                                           PVC_RECURSE_PLACEHOLDERS));
    1229            0 :         foreach(lc, fpinfo->local_conds)
    1230              :         {
    1231            0 :                 RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
    1232              : 
    1233            0 :                 tlist = add_to_flat_tlist(tlist,
    1234            0 :                                                                   pull_var_clause((Node *) rinfo->clause,
    1235              :                                                                                                   PVC_RECURSE_PLACEHOLDERS));
    1236            0 :         }
    1237              : 
    1238            0 :         return tlist;
    1239            0 : }
    1240              : 
    1241              : /*
    1242              :  * Deparse SELECT statement for given relation into buf.
    1243              :  *
    1244              :  * tlist contains the list of desired columns to be fetched from foreign server.
    1245              :  * For a base relation fpinfo->attrs_used is used to construct SELECT clause,
    1246              :  * hence the tlist is ignored for a base relation.
    1247              :  *
    1248              :  * remote_conds is the list of conditions to be deparsed into the WHERE clause
    1249              :  * (or, in the case of upper relations, into the HAVING clause).
    1250              :  *
    1251              :  * If params_list is not NULL, it receives a list of Params and other-relation
    1252              :  * Vars used in the clauses; these values must be transmitted to the remote
    1253              :  * server as parameter values.
    1254              :  *
    1255              :  * If params_list is NULL, we're generating the query for EXPLAIN purposes,
    1256              :  * so Params and other-relation Vars should be replaced by dummy values.
    1257              :  *
    1258              :  * pathkeys is the list of pathkeys to order the result by.
    1259              :  *
    1260              :  * is_subquery is the flag to indicate whether to deparse the specified
    1261              :  * relation as a subquery.
    1262              :  *
    1263              :  * List of columns selected is returned in retrieved_attrs.
    1264              :  */
    1265              : void
    1266            0 : deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel,
    1267              :                                                 List *tlist, List *remote_conds, List *pathkeys,
    1268              :                                                 bool has_final_sort, bool has_limit, bool is_subquery,
    1269              :                                                 List **retrieved_attrs, List **params_list)
    1270              : {
    1271            0 :         deparse_expr_cxt context;
    1272            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
    1273            0 :         List       *quals;
    1274              : 
    1275              :         /*
    1276              :          * We handle relations for foreign tables, joins between those and upper
    1277              :          * relations.
    1278              :          */
    1279            0 :         Assert(IS_JOIN_REL(rel) || IS_SIMPLE_REL(rel) || IS_UPPER_REL(rel));
    1280              : 
    1281              :         /* Fill portions of context common to upper, join and base relation */
    1282            0 :         context.buf = buf;
    1283            0 :         context.root = root;
    1284            0 :         context.foreignrel = rel;
    1285            0 :         context.scanrel = IS_UPPER_REL(rel) ? fpinfo->outerrel : rel;
    1286            0 :         context.params_list = params_list;
    1287              : 
    1288              :         /* Construct SELECT clause */
    1289            0 :         deparseSelectSql(tlist, is_subquery, retrieved_attrs, &context);
    1290              : 
    1291              :         /*
    1292              :          * For upper relations, the WHERE clause is built from the remote
    1293              :          * conditions of the underlying scan relation; otherwise, we can use the
    1294              :          * supplied list of remote conditions directly.
    1295              :          */
    1296            0 :         if (IS_UPPER_REL(rel))
    1297              :         {
    1298            0 :                 PgFdwRelationInfo *ofpinfo;
    1299              : 
    1300            0 :                 ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
    1301            0 :                 quals = ofpinfo->remote_conds;
    1302            0 :         }
    1303              :         else
    1304            0 :                 quals = remote_conds;
    1305              : 
    1306              :         /* Construct FROM and WHERE clauses */
    1307            0 :         deparseFromExpr(quals, &context);
    1308              : 
    1309            0 :         if (IS_UPPER_REL(rel))
    1310              :         {
    1311              :                 /* Append GROUP BY clause */
    1312            0 :                 appendGroupByClause(tlist, &context);
    1313              : 
    1314              :                 /* Append HAVING clause */
    1315            0 :                 if (remote_conds)
    1316              :                 {
    1317            0 :                         appendStringInfoString(buf, " HAVING ");
    1318            0 :                         appendConditions(remote_conds, &context);
    1319            0 :                 }
    1320            0 :         }
    1321              : 
    1322              :         /* Add ORDER BY clause if we found any useful pathkeys */
    1323            0 :         if (pathkeys)
    1324            0 :                 appendOrderByClause(pathkeys, has_final_sort, &context);
    1325              : 
    1326              :         /* Add LIMIT clause if necessary */
    1327            0 :         if (has_limit)
    1328            0 :                 appendLimitClause(&context);
    1329              : 
    1330              :         /* Add any necessary FOR UPDATE/SHARE. */
    1331            0 :         deparseLockingClause(&context);
    1332            0 : }
    1333              : 
    1334              : /*
    1335              :  * Construct a simple SELECT statement that retrieves desired columns
    1336              :  * of the specified foreign table, and append it to "buf".  The output
    1337              :  * contains just "SELECT ... ".
    1338              :  *
    1339              :  * We also create an integer List of the columns being retrieved, which is
    1340              :  * returned to *retrieved_attrs, unless we deparse the specified relation
    1341              :  * as a subquery.
    1342              :  *
    1343              :  * tlist is the list of desired columns.  is_subquery is the flag to
    1344              :  * indicate whether to deparse the specified relation as a subquery.
    1345              :  * Read prologue of deparseSelectStmtForRel() for details.
    1346              :  */
    1347              : static void
    1348            0 : deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs,
    1349              :                                  deparse_expr_cxt *context)
    1350              : {
    1351            0 :         StringInfo      buf = context->buf;
    1352            0 :         RelOptInfo *foreignrel = context->foreignrel;
    1353            0 :         PlannerInfo *root = context->root;
    1354            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    1355              : 
    1356              :         /*
    1357              :          * Construct SELECT list
    1358              :          */
    1359            0 :         appendStringInfoString(buf, "SELECT ");
    1360              : 
    1361            0 :         if (is_subquery)
    1362              :         {
    1363              :                 /*
    1364              :                  * For a relation that is deparsed as a subquery, emit expressions
    1365              :                  * specified in the relation's reltarget.  Note that since this is for
    1366              :                  * the subquery, no need to care about *retrieved_attrs.
    1367              :                  */
    1368            0 :                 deparseSubqueryTargetList(context);
    1369            0 :         }
    1370            0 :         else if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
    1371              :         {
    1372              :                 /*
    1373              :                  * For a join or upper relation the input tlist gives the list of
    1374              :                  * columns required to be fetched from the foreign server.
    1375              :                  */
    1376            0 :                 deparseExplicitTargetList(tlist, false, retrieved_attrs, context);
    1377            0 :         }
    1378              :         else
    1379              :         {
    1380              :                 /*
    1381              :                  * For a base relation fpinfo->attrs_used gives the list of columns
    1382              :                  * required to be fetched from the foreign server.
    1383              :                  */
    1384            0 :                 RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root);
    1385              : 
    1386              :                 /*
    1387              :                  * Core code already has some lock on each rel being planned, so we
    1388              :                  * can use NoLock here.
    1389              :                  */
    1390            0 :                 Relation        rel = table_open(rte->relid, NoLock);
    1391              : 
    1392            0 :                 deparseTargetList(buf, rte, foreignrel->relid, rel, false,
    1393            0 :                                                   fpinfo->attrs_used, false, retrieved_attrs);
    1394            0 :                 table_close(rel, NoLock);
    1395            0 :         }
    1396            0 : }
    1397              : 
    1398              : /*
    1399              :  * Construct a FROM clause and, if needed, a WHERE clause, and append those to
    1400              :  * "buf".
    1401              :  *
    1402              :  * quals is the list of clauses to be included in the WHERE clause.
    1403              :  * (These may or may not include RestrictInfo decoration.)
    1404              :  */
    1405              : static void
    1406            0 : deparseFromExpr(List *quals, deparse_expr_cxt *context)
    1407              : {
    1408            0 :         StringInfo      buf = context->buf;
    1409            0 :         RelOptInfo *scanrel = context->scanrel;
    1410            0 :         List       *additional_conds = NIL;
    1411              : 
    1412              :         /* For upper relations, scanrel must be either a joinrel or a baserel */
    1413            0 :         Assert(!IS_UPPER_REL(context->foreignrel) ||
    1414              :                    IS_JOIN_REL(scanrel) || IS_SIMPLE_REL(scanrel));
    1415              : 
    1416              :         /* Construct FROM clause */
    1417            0 :         appendStringInfoString(buf, " FROM ");
    1418            0 :         deparseFromExprForRel(buf, context->root, scanrel,
    1419            0 :                                                   (bms_membership(scanrel->relids) == BMS_MULTIPLE),
    1420              :                                                   (Index) 0, NULL, &additional_conds,
    1421            0 :                                                   context->params_list);
    1422            0 :         appendWhereClause(quals, additional_conds, context);
    1423            0 :         if (additional_conds != NIL)
    1424            0 :                 list_free_deep(additional_conds);
    1425            0 : }
    1426              : 
    1427              : /*
    1428              :  * Emit a target list that retrieves the columns specified in attrs_used.
    1429              :  * This is used for both SELECT and RETURNING targetlists; the is_returning
    1430              :  * parameter is true only for a RETURNING targetlist.
    1431              :  *
    1432              :  * The tlist text is appended to buf, and we also create an integer List
    1433              :  * of the columns being retrieved, which is returned to *retrieved_attrs.
    1434              :  *
    1435              :  * If qualify_col is true, add relation alias before the column name.
    1436              :  */
    1437              : static void
    1438            0 : deparseTargetList(StringInfo buf,
    1439              :                                   RangeTblEntry *rte,
    1440              :                                   Index rtindex,
    1441              :                                   Relation rel,
    1442              :                                   bool is_returning,
    1443              :                                   Bitmapset *attrs_used,
    1444              :                                   bool qualify_col,
    1445              :                                   List **retrieved_attrs)
    1446              : {
    1447            0 :         TupleDesc       tupdesc = RelationGetDescr(rel);
    1448            0 :         bool            have_wholerow;
    1449            0 :         bool            first;
    1450            0 :         int                     i;
    1451              : 
    1452            0 :         *retrieved_attrs = NIL;
    1453              : 
    1454              :         /* If there's a whole-row reference, we'll need all the columns. */
    1455            0 :         have_wholerow = bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
    1456            0 :                                                                   attrs_used);
    1457              : 
    1458            0 :         first = true;
    1459            0 :         for (i = 1; i <= tupdesc->natts; i++)
    1460              :         {
    1461              :                 /* Ignore dropped attributes. */
    1462            0 :                 if (TupleDescCompactAttr(tupdesc, i - 1)->attisdropped)
    1463            0 :                         continue;
    1464              : 
    1465            0 :                 if (have_wholerow ||
    1466            0 :                         bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
    1467            0 :                                                   attrs_used))
    1468              :                 {
    1469            0 :                         if (!first)
    1470            0 :                                 appendStringInfoString(buf, ", ");
    1471            0 :                         else if (is_returning)
    1472            0 :                                 appendStringInfoString(buf, " RETURNING ");
    1473            0 :                         first = false;
    1474              : 
    1475            0 :                         deparseColumnRef(buf, rtindex, i, rte, qualify_col);
    1476              : 
    1477            0 :                         *retrieved_attrs = lappend_int(*retrieved_attrs, i);
    1478            0 :                 }
    1479            0 :         }
    1480              : 
    1481              :         /*
    1482              :          * Add ctid if needed.  We currently don't support retrieving any other
    1483              :          * system columns.
    1484              :          */
    1485            0 :         if (bms_is_member(SelfItemPointerAttributeNumber - FirstLowInvalidHeapAttributeNumber,
    1486            0 :                                           attrs_used))
    1487              :         {
    1488            0 :                 if (!first)
    1489            0 :                         appendStringInfoString(buf, ", ");
    1490            0 :                 else if (is_returning)
    1491            0 :                         appendStringInfoString(buf, " RETURNING ");
    1492            0 :                 first = false;
    1493              : 
    1494            0 :                 if (qualify_col)
    1495            0 :                         ADD_REL_QUALIFIER(buf, rtindex);
    1496            0 :                 appendStringInfoString(buf, "ctid");
    1497              : 
    1498            0 :                 *retrieved_attrs = lappend_int(*retrieved_attrs,
    1499              :                                                                            SelfItemPointerAttributeNumber);
    1500            0 :         }
    1501              : 
    1502              :         /* Don't generate bad syntax if no undropped columns */
    1503            0 :         if (first && !is_returning)
    1504            0 :                 appendStringInfoString(buf, "NULL");
    1505            0 : }
    1506              : 
    1507              : /*
    1508              :  * Deparse the appropriate locking clause (FOR UPDATE or FOR SHARE) for a
    1509              :  * given relation (context->scanrel).
    1510              :  */
    1511              : static void
    1512            0 : deparseLockingClause(deparse_expr_cxt *context)
    1513              : {
    1514            0 :         StringInfo      buf = context->buf;
    1515            0 :         PlannerInfo *root = context->root;
    1516            0 :         RelOptInfo *rel = context->scanrel;
    1517            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
    1518            0 :         int                     relid = -1;
    1519              : 
    1520            0 :         while ((relid = bms_next_member(rel->relids, relid)) >= 0)
    1521              :         {
    1522              :                 /*
    1523              :                  * Ignore relation if it appears in a lower subquery.  Locking clause
    1524              :                  * for such a relation is included in the subquery if necessary.
    1525              :                  */
    1526            0 :                 if (bms_is_member(relid, fpinfo->lower_subquery_rels))
    1527            0 :                         continue;
    1528              : 
    1529              :                 /*
    1530              :                  * Add FOR UPDATE/SHARE if appropriate.  We apply locking during the
    1531              :                  * initial row fetch, rather than later on as is done for local
    1532              :                  * tables. The extra roundtrips involved in trying to duplicate the
    1533              :                  * local semantics exactly don't seem worthwhile (see also comments
    1534              :                  * for RowMarkType).
    1535              :                  *
    1536              :                  * Note: because we actually run the query as a cursor, this assumes
    1537              :                  * that DECLARE CURSOR ... FOR UPDATE is supported, which it isn't
    1538              :                  * before 8.3.
    1539              :                  */
    1540            0 :                 if (bms_is_member(relid, root->all_result_relids) &&
    1541            0 :                         (root->parse->commandType == CMD_UPDATE ||
    1542            0 :                          root->parse->commandType == CMD_DELETE))
    1543              :                 {
    1544              :                         /* Relation is UPDATE/DELETE target, so use FOR UPDATE */
    1545            0 :                         appendStringInfoString(buf, " FOR UPDATE");
    1546              : 
    1547              :                         /* Add the relation alias if we are here for a join relation */
    1548            0 :                         if (IS_JOIN_REL(rel))
    1549            0 :                                 appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid);
    1550            0 :                 }
    1551              :                 else
    1552              :                 {
    1553            0 :                         PlanRowMark *rc = get_plan_rowmark(root->rowMarks, relid);
    1554              : 
    1555            0 :                         if (rc)
    1556              :                         {
    1557              :                                 /*
    1558              :                                  * Relation is specified as a FOR UPDATE/SHARE target, so
    1559              :                                  * handle that.  (But we could also see LCS_NONE, meaning this
    1560              :                                  * isn't a target relation after all.)
    1561              :                                  *
    1562              :                                  * For now, just ignore any [NO] KEY specification, since (a)
    1563              :                                  * it's not clear what that means for a remote table that we
    1564              :                                  * don't have complete information about, and (b) it wouldn't
    1565              :                                  * work anyway on older remote servers.  Likewise, we don't
    1566              :                                  * worry about NOWAIT.
    1567              :                                  */
    1568            0 :                                 switch (rc->strength)
    1569              :                                 {
    1570              :                                         case LCS_NONE:
    1571              :                                                 /* No locking needed */
    1572              :                                                 break;
    1573              :                                         case LCS_FORKEYSHARE:
    1574              :                                         case LCS_FORSHARE:
    1575            0 :                                                 appendStringInfoString(buf, " FOR SHARE");
    1576            0 :                                                 break;
    1577              :                                         case LCS_FORNOKEYUPDATE:
    1578              :                                         case LCS_FORUPDATE:
    1579            0 :                                                 appendStringInfoString(buf, " FOR UPDATE");
    1580            0 :                                                 break;
    1581              :                                 }
    1582              : 
    1583              :                                 /* Add the relation alias if we are here for a join relation */
    1584            0 :                                 if (bms_membership(rel->relids) == BMS_MULTIPLE &&
    1585            0 :                                         rc->strength != LCS_NONE)
    1586            0 :                                         appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid);
    1587            0 :                         }
    1588            0 :                 }
    1589              :         }
    1590            0 : }
    1591              : 
    1592              : /*
    1593              :  * Deparse conditions from the provided list and append them to buf.
    1594              :  *
    1595              :  * The conditions in the list are assumed to be ANDed. This function is used to
    1596              :  * deparse WHERE clauses, JOIN .. ON clauses and HAVING clauses.
    1597              :  *
    1598              :  * Depending on the caller, the list elements might be either RestrictInfos
    1599              :  * or bare clauses.
    1600              :  */
    1601              : static void
    1602            0 : appendConditions(List *exprs, deparse_expr_cxt *context)
    1603              : {
    1604            0 :         int                     nestlevel;
    1605            0 :         ListCell   *lc;
    1606            0 :         bool            is_first = true;
    1607            0 :         StringInfo      buf = context->buf;
    1608              : 
    1609              :         /* Make sure any constants in the exprs are printed portably */
    1610            0 :         nestlevel = set_transmission_modes();
    1611              : 
    1612            0 :         foreach(lc, exprs)
    1613              :         {
    1614            0 :                 Expr       *expr = (Expr *) lfirst(lc);
    1615              : 
    1616              :                 /* Extract clause from RestrictInfo, if required */
    1617            0 :                 if (IsA(expr, RestrictInfo))
    1618            0 :                         expr = ((RestrictInfo *) expr)->clause;
    1619              : 
    1620              :                 /* Connect expressions with "AND" and parenthesize each condition. */
    1621            0 :                 if (!is_first)
    1622            0 :                         appendStringInfoString(buf, " AND ");
    1623              : 
    1624            0 :                 appendStringInfoChar(buf, '(');
    1625            0 :                 deparseExpr(expr, context);
    1626            0 :                 appendStringInfoChar(buf, ')');
    1627              : 
    1628            0 :                 is_first = false;
    1629            0 :         }
    1630              : 
    1631            0 :         reset_transmission_modes(nestlevel);
    1632            0 : }
    1633              : 
    1634              : /*
    1635              :  * Append WHERE clause, containing conditions from exprs and additional_conds,
    1636              :  * to context->buf.
    1637              :  */
    1638              : static void
    1639            0 : appendWhereClause(List *exprs, List *additional_conds, deparse_expr_cxt *context)
    1640              : {
    1641            0 :         StringInfo      buf = context->buf;
    1642            0 :         bool            need_and = false;
    1643            0 :         ListCell   *lc;
    1644              : 
    1645            0 :         if (exprs != NIL || additional_conds != NIL)
    1646            0 :                 appendStringInfoString(buf, " WHERE ");
    1647              : 
    1648              :         /*
    1649              :          * If there are some filters, append them.
    1650              :          */
    1651            0 :         if (exprs != NIL)
    1652              :         {
    1653            0 :                 appendConditions(exprs, context);
    1654            0 :                 need_and = true;
    1655            0 :         }
    1656              : 
    1657              :         /*
    1658              :          * If there are some EXISTS conditions, coming from SEMI-JOINS, append
    1659              :          * them.
    1660              :          */
    1661            0 :         foreach(lc, additional_conds)
    1662              :         {
    1663            0 :                 if (need_and)
    1664            0 :                         appendStringInfoString(buf, " AND ");
    1665            0 :                 appendStringInfoString(buf, (char *) lfirst(lc));
    1666            0 :                 need_and = true;
    1667            0 :         }
    1668            0 : }
    1669              : 
    1670              : /* Output join name for given join type */
    1671              : const char *
    1672            0 : get_jointype_name(JoinType jointype)
    1673              : {
    1674            0 :         switch (jointype)
    1675              :         {
    1676              :                 case JOIN_INNER:
    1677            0 :                         return "INNER";
    1678              : 
    1679              :                 case JOIN_LEFT:
    1680            0 :                         return "LEFT";
    1681              : 
    1682              :                 case JOIN_RIGHT:
    1683            0 :                         return "RIGHT";
    1684              : 
    1685              :                 case JOIN_FULL:
    1686            0 :                         return "FULL";
    1687              : 
    1688              :                 case JOIN_SEMI:
    1689            0 :                         return "SEMI";
    1690              : 
    1691              :                 default:
    1692              :                         /* Shouldn't come here, but protect from buggy code. */
    1693            0 :                         elog(ERROR, "unsupported join type %d", jointype);
    1694            0 :         }
    1695              : 
    1696              :         /* Keep compiler happy */
    1697            0 :         return NULL;
    1698            0 : }
    1699              : 
    1700              : /*
    1701              :  * Deparse given targetlist and append it to context->buf.
    1702              :  *
    1703              :  * tlist is list of TargetEntry's which in turn contain Var nodes.
    1704              :  *
    1705              :  * retrieved_attrs is the list of continuously increasing integers starting
    1706              :  * from 1. It has same number of entries as tlist.
    1707              :  *
    1708              :  * This is used for both SELECT and RETURNING targetlists; the is_returning
    1709              :  * parameter is true only for a RETURNING targetlist.
    1710              :  */
    1711              : static void
    1712            0 : deparseExplicitTargetList(List *tlist,
    1713              :                                                   bool is_returning,
    1714              :                                                   List **retrieved_attrs,
    1715              :                                                   deparse_expr_cxt *context)
    1716              : {
    1717            0 :         ListCell   *lc;
    1718            0 :         StringInfo      buf = context->buf;
    1719            0 :         int                     i = 0;
    1720              : 
    1721            0 :         *retrieved_attrs = NIL;
    1722              : 
    1723            0 :         foreach(lc, tlist)
    1724              :         {
    1725            0 :                 TargetEntry *tle = lfirst_node(TargetEntry, lc);
    1726              : 
    1727            0 :                 if (i > 0)
    1728            0 :                         appendStringInfoString(buf, ", ");
    1729            0 :                 else if (is_returning)
    1730            0 :                         appendStringInfoString(buf, " RETURNING ");
    1731              : 
    1732            0 :                 deparseExpr((Expr *) tle->expr, context);
    1733              : 
    1734            0 :                 *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
    1735            0 :                 i++;
    1736            0 :         }
    1737              : 
    1738            0 :         if (i == 0 && !is_returning)
    1739            0 :                 appendStringInfoString(buf, "NULL");
    1740            0 : }
    1741              : 
    1742              : /*
    1743              :  * Emit expressions specified in the given relation's reltarget.
    1744              :  *
    1745              :  * This is used for deparsing the given relation as a subquery.
    1746              :  */
    1747              : static void
    1748            0 : deparseSubqueryTargetList(deparse_expr_cxt *context)
    1749              : {
    1750            0 :         StringInfo      buf = context->buf;
    1751            0 :         RelOptInfo *foreignrel = context->foreignrel;
    1752            0 :         bool            first;
    1753            0 :         ListCell   *lc;
    1754              : 
    1755              :         /* Should only be called in these cases. */
    1756            0 :         Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
    1757              : 
    1758            0 :         first = true;
    1759            0 :         foreach(lc, foreignrel->reltarget->exprs)
    1760              :         {
    1761            0 :                 Node       *node = (Node *) lfirst(lc);
    1762              : 
    1763            0 :                 if (!first)
    1764            0 :                         appendStringInfoString(buf, ", ");
    1765            0 :                 first = false;
    1766              : 
    1767            0 :                 deparseExpr((Expr *) node, context);
    1768            0 :         }
    1769              : 
    1770              :         /* Don't generate bad syntax if no expressions */
    1771            0 :         if (first)
    1772            0 :                 appendStringInfoString(buf, "NULL");
    1773            0 : }
    1774              : 
    1775              : /*
    1776              :  * Construct FROM clause for given relation
    1777              :  *
    1778              :  * The function constructs ... JOIN ... ON ... for join relation. For a base
    1779              :  * relation it just returns schema-qualified tablename, with the appropriate
    1780              :  * alias if so requested.
    1781              :  *
    1782              :  * 'ignore_rel' is either zero or the RT index of a target relation.  In the
    1783              :  * latter case the function constructs FROM clause of UPDATE or USING clause
    1784              :  * of DELETE; it deparses the join relation as if the relation never contained
    1785              :  * the target relation, and creates a List of conditions to be deparsed into
    1786              :  * the top-level WHERE clause, which is returned to *ignore_conds.
    1787              :  *
    1788              :  * 'additional_conds' is a pointer to a list of strings to be appended to
    1789              :  * the WHERE clause, coming from lower-level SEMI-JOINs.
    1790              :  */
    1791              : static void
    1792            0 : deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
    1793              :                                           bool use_alias, Index ignore_rel, List **ignore_conds,
    1794              :                                           List **additional_conds, List **params_list)
    1795              : {
    1796            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    1797              : 
    1798            0 :         if (IS_JOIN_REL(foreignrel))
    1799              :         {
    1800            0 :                 StringInfoData join_sql_o;
    1801            0 :                 StringInfoData join_sql_i;
    1802            0 :                 RelOptInfo *outerrel = fpinfo->outerrel;
    1803            0 :                 RelOptInfo *innerrel = fpinfo->innerrel;
    1804            0 :                 bool            outerrel_is_target = false;
    1805            0 :                 bool            innerrel_is_target = false;
    1806            0 :                 List       *additional_conds_i = NIL;
    1807            0 :                 List       *additional_conds_o = NIL;
    1808              : 
    1809            0 :                 if (ignore_rel > 0 && bms_is_member(ignore_rel, foreignrel->relids))
    1810              :                 {
    1811              :                         /*
    1812              :                          * If this is an inner join, add joinclauses to *ignore_conds and
    1813              :                          * set it to empty so that those can be deparsed into the WHERE
    1814              :                          * clause.  Note that since the target relation can never be
    1815              :                          * within the nullable side of an outer join, those could safely
    1816              :                          * be pulled up into the WHERE clause (see foreign_join_ok()).
    1817              :                          * Note also that since the target relation is only inner-joined
    1818              :                          * to any other relation in the query, all conditions in the join
    1819              :                          * tree mentioning the target relation could be deparsed into the
    1820              :                          * WHERE clause by doing this recursively.
    1821              :                          */
    1822            0 :                         if (fpinfo->jointype == JOIN_INNER)
    1823              :                         {
    1824            0 :                                 *ignore_conds = list_concat(*ignore_conds,
    1825            0 :                                                                                         fpinfo->joinclauses);
    1826            0 :                                 fpinfo->joinclauses = NIL;
    1827            0 :                         }
    1828              : 
    1829              :                         /*
    1830              :                          * Check if either of the input relations is the target relation.
    1831              :                          */
    1832            0 :                         if (outerrel->relid == ignore_rel)
    1833            0 :                                 outerrel_is_target = true;
    1834            0 :                         else if (innerrel->relid == ignore_rel)
    1835            0 :                                 innerrel_is_target = true;
    1836            0 :                 }
    1837              : 
    1838              :                 /* Deparse outer relation if not the target relation. */
    1839            0 :                 if (!outerrel_is_target)
    1840              :                 {
    1841            0 :                         initStringInfo(&join_sql_o);
    1842            0 :                         deparseRangeTblRef(&join_sql_o, root, outerrel,
    1843            0 :                                                            fpinfo->make_outerrel_subquery,
    1844            0 :                                                            ignore_rel, ignore_conds, &additional_conds_o,
    1845            0 :                                                            params_list);
    1846              : 
    1847              :                         /*
    1848              :                          * If inner relation is the target relation, skip deparsing it.
    1849              :                          * Note that since the join of the target relation with any other
    1850              :                          * relation in the query is an inner join and can never be within
    1851              :                          * the nullable side of an outer join, the join could be
    1852              :                          * interchanged with higher-level joins (cf. identity 1 on outer
    1853              :                          * join reordering shown in src/backend/optimizer/README), which
    1854              :                          * means it's safe to skip the target-relation deparsing here.
    1855              :                          */
    1856            0 :                         if (innerrel_is_target)
    1857              :                         {
    1858            0 :                                 Assert(fpinfo->jointype == JOIN_INNER);
    1859            0 :                                 Assert(fpinfo->joinclauses == NIL);
    1860            0 :                                 appendBinaryStringInfo(buf, join_sql_o.data, join_sql_o.len);
    1861              :                                 /* Pass EXISTS conditions to upper level */
    1862            0 :                                 if (additional_conds_o != NIL)
    1863              :                                 {
    1864            0 :                                         Assert(*additional_conds == NIL);
    1865            0 :                                         *additional_conds = additional_conds_o;
    1866            0 :                                 }
    1867            0 :                                 return;
    1868              :                         }
    1869            0 :                 }
    1870              : 
    1871              :                 /* Deparse inner relation if not the target relation. */
    1872            0 :                 if (!innerrel_is_target)
    1873              :                 {
    1874            0 :                         initStringInfo(&join_sql_i);
    1875            0 :                         deparseRangeTblRef(&join_sql_i, root, innerrel,
    1876            0 :                                                            fpinfo->make_innerrel_subquery,
    1877            0 :                                                            ignore_rel, ignore_conds, &additional_conds_i,
    1878            0 :                                                            params_list);
    1879              : 
    1880              :                         /*
    1881              :                          * SEMI-JOIN is deparsed as the EXISTS subquery. It references
    1882              :                          * outer and inner relations, so it should be evaluated as the
    1883              :                          * condition in the upper-level WHERE clause. We deparse the
    1884              :                          * condition and pass it to upper level callers as an
    1885              :                          * additional_conds list. Upper level callers are responsible for
    1886              :                          * inserting conditions from the list where appropriate.
    1887              :                          */
    1888            0 :                         if (fpinfo->jointype == JOIN_SEMI)
    1889              :                         {
    1890            0 :                                 deparse_expr_cxt context;
    1891            0 :                                 StringInfoData str;
    1892              : 
    1893              :                                 /* Construct deparsed condition from this SEMI-JOIN */
    1894            0 :                                 initStringInfo(&str);
    1895            0 :                                 appendStringInfo(&str, "EXISTS (SELECT NULL FROM %s",
    1896            0 :                                                                  join_sql_i.data);
    1897              : 
    1898            0 :                                 context.buf = &str;
    1899            0 :                                 context.foreignrel = foreignrel;
    1900            0 :                                 context.scanrel = foreignrel;
    1901            0 :                                 context.root = root;
    1902            0 :                                 context.params_list = params_list;
    1903              : 
    1904              :                                 /*
    1905              :                                  * Append SEMI-JOIN clauses and EXISTS conditions from lower
    1906              :                                  * levels to the current EXISTS subquery
    1907              :                                  */
    1908            0 :                                 appendWhereClause(fpinfo->joinclauses, additional_conds_i, &context);
    1909              : 
    1910              :                                 /*
    1911              :                                  * EXISTS conditions, coming from lower join levels, have just
    1912              :                                  * been processed.
    1913              :                                  */
    1914            0 :                                 if (additional_conds_i != NIL)
    1915              :                                 {
    1916            0 :                                         list_free_deep(additional_conds_i);
    1917            0 :                                         additional_conds_i = NIL;
    1918            0 :                                 }
    1919              : 
    1920              :                                 /* Close parentheses for EXISTS subquery */
    1921            0 :                                 appendStringInfoChar(&str, ')');
    1922              : 
    1923            0 :                                 *additional_conds = lappend(*additional_conds, str.data);
    1924            0 :                         }
    1925              : 
    1926              :                         /*
    1927              :                          * If outer relation is the target relation, skip deparsing it.
    1928              :                          * See the above note about safety.
    1929              :                          */
    1930            0 :                         if (outerrel_is_target)
    1931              :                         {
    1932            0 :                                 Assert(fpinfo->jointype == JOIN_INNER);
    1933            0 :                                 Assert(fpinfo->joinclauses == NIL);
    1934            0 :                                 appendBinaryStringInfo(buf, join_sql_i.data, join_sql_i.len);
    1935              :                                 /* Pass EXISTS conditions to the upper call */
    1936            0 :                                 if (additional_conds_i != NIL)
    1937              :                                 {
    1938            0 :                                         Assert(*additional_conds == NIL);
    1939            0 :                                         *additional_conds = additional_conds_i;
    1940            0 :                                 }
    1941            0 :                                 return;
    1942              :                         }
    1943            0 :                 }
    1944              : 
    1945              :                 /* Neither of the relations is the target relation. */
    1946            0 :                 Assert(!outerrel_is_target && !innerrel_is_target);
    1947              : 
    1948              :                 /*
    1949              :                  * For semijoin FROM clause is deparsed as an outer relation. An inner
    1950              :                  * relation and join clauses are converted to EXISTS condition and
    1951              :                  * passed to the upper level.
    1952              :                  */
    1953            0 :                 if (fpinfo->jointype == JOIN_SEMI)
    1954              :                 {
    1955            0 :                         appendBinaryStringInfo(buf, join_sql_o.data, join_sql_o.len);
    1956            0 :                 }
    1957              :                 else
    1958              :                 {
    1959              :                         /*
    1960              :                          * For a join relation FROM clause, entry is deparsed as
    1961              :                          *
    1962              :                          * ((outer relation) <join type> (inner relation) ON
    1963              :                          * (joinclauses))
    1964              :                          */
    1965            0 :                         appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data,
    1966            0 :                                                          get_jointype_name(fpinfo->jointype), join_sql_i.data);
    1967              : 
    1968              :                         /* Append join clause; (TRUE) if no join clause */
    1969            0 :                         if (fpinfo->joinclauses)
    1970              :                         {
    1971            0 :                                 deparse_expr_cxt context;
    1972              : 
    1973            0 :                                 context.buf = buf;
    1974            0 :                                 context.foreignrel = foreignrel;
    1975            0 :                                 context.scanrel = foreignrel;
    1976            0 :                                 context.root = root;
    1977            0 :                                 context.params_list = params_list;
    1978              : 
    1979            0 :                                 appendStringInfoChar(buf, '(');
    1980            0 :                                 appendConditions(fpinfo->joinclauses, &context);
    1981            0 :                                 appendStringInfoChar(buf, ')');
    1982            0 :                         }
    1983              :                         else
    1984            0 :                                 appendStringInfoString(buf, "(TRUE)");
    1985              : 
    1986              :                         /* End the FROM clause entry. */
    1987            0 :                         appendStringInfoChar(buf, ')');
    1988              :                 }
    1989              : 
    1990              :                 /*
    1991              :                  * Construct additional_conds to be passed to the upper caller from
    1992              :                  * current level additional_conds and additional_conds, coming from
    1993              :                  * inner and outer rels.
    1994              :                  */
    1995            0 :                 if (additional_conds_o != NIL)
    1996              :                 {
    1997            0 :                         *additional_conds = list_concat(*additional_conds,
    1998            0 :                                                                                         additional_conds_o);
    1999            0 :                         list_free(additional_conds_o);
    2000            0 :                 }
    2001              : 
    2002            0 :                 if (additional_conds_i != NIL)
    2003              :                 {
    2004            0 :                         *additional_conds = list_concat(*additional_conds,
    2005            0 :                                                                                         additional_conds_i);
    2006            0 :                         list_free(additional_conds_i);
    2007            0 :                 }
    2008            0 :         }
    2009              :         else
    2010              :         {
    2011            0 :                 RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root);
    2012              : 
    2013              :                 /*
    2014              :                  * Core code already has some lock on each rel being planned, so we
    2015              :                  * can use NoLock here.
    2016              :                  */
    2017            0 :                 Relation        rel = table_open(rte->relid, NoLock);
    2018              : 
    2019            0 :                 deparseRelation(buf, rel);
    2020              : 
    2021              :                 /*
    2022              :                  * Add a unique alias to avoid any conflict in relation names due to
    2023              :                  * pulled up subqueries in the query being built for a pushed down
    2024              :                  * join.
    2025              :                  */
    2026            0 :                 if (use_alias)
    2027            0 :                         appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, foreignrel->relid);
    2028              : 
    2029            0 :                 table_close(rel, NoLock);
    2030            0 :         }
    2031            0 : }
    2032              : 
    2033              : /*
    2034              :  * Append FROM clause entry for the given relation into buf.
    2035              :  * Conditions from lower-level SEMI-JOINs are appended to additional_conds
    2036              :  * and should be added to upper level WHERE clause.
    2037              :  */
    2038              : static void
    2039            0 : deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
    2040              :                                    bool make_subquery, Index ignore_rel, List **ignore_conds,
    2041              :                                    List **additional_conds, List **params_list)
    2042              : {
    2043            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    2044              : 
    2045              :         /* Should only be called in these cases. */
    2046            0 :         Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
    2047              : 
    2048            0 :         Assert(fpinfo->local_conds == NIL);
    2049              : 
    2050              :         /* If make_subquery is true, deparse the relation as a subquery. */
    2051            0 :         if (make_subquery)
    2052              :         {
    2053            0 :                 List       *retrieved_attrs;
    2054            0 :                 int                     ncols;
    2055              : 
    2056              :                 /*
    2057              :                  * The given relation shouldn't contain the target relation, because
    2058              :                  * this should only happen for input relations for a full join, and
    2059              :                  * such relations can never contain an UPDATE/DELETE target.
    2060              :                  */
    2061            0 :                 Assert(ignore_rel == 0 ||
    2062              :                            !bms_is_member(ignore_rel, foreignrel->relids));
    2063              : 
    2064              :                 /* Deparse the subquery representing the relation. */
    2065            0 :                 appendStringInfoChar(buf, '(');
    2066            0 :                 deparseSelectStmtForRel(buf, root, foreignrel, NIL,
    2067            0 :                                                                 fpinfo->remote_conds, NIL,
    2068              :                                                                 false, false, true,
    2069            0 :                                                                 &retrieved_attrs, params_list);
    2070            0 :                 appendStringInfoChar(buf, ')');
    2071              : 
    2072              :                 /* Append the relation alias. */
    2073            0 :                 appendStringInfo(buf, " %s%d", SUBQUERY_REL_ALIAS_PREFIX,
    2074            0 :                                                  fpinfo->relation_index);
    2075              : 
    2076              :                 /*
    2077              :                  * Append the column aliases if needed.  Note that the subquery emits
    2078              :                  * expressions specified in the relation's reltarget (see
    2079              :                  * deparseSubqueryTargetList).
    2080              :                  */
    2081            0 :                 ncols = list_length(foreignrel->reltarget->exprs);
    2082            0 :                 if (ncols > 0)
    2083              :                 {
    2084            0 :                         int                     i;
    2085              : 
    2086            0 :                         appendStringInfoChar(buf, '(');
    2087            0 :                         for (i = 1; i <= ncols; i++)
    2088              :                         {
    2089            0 :                                 if (i > 1)
    2090            0 :                                         appendStringInfoString(buf, ", ");
    2091              : 
    2092            0 :                                 appendStringInfo(buf, "%s%d", SUBQUERY_COL_ALIAS_PREFIX, i);
    2093            0 :                         }
    2094            0 :                         appendStringInfoChar(buf, ')');
    2095            0 :                 }
    2096            0 :         }
    2097              :         else
    2098            0 :                 deparseFromExprForRel(buf, root, foreignrel, true, ignore_rel,
    2099            0 :                                                           ignore_conds, additional_conds,
    2100            0 :                                                           params_list);
    2101            0 : }
    2102              : 
    2103              : /*
    2104              :  * deparse remote INSERT statement
    2105              :  *
    2106              :  * The statement text is appended to buf, and we also create an integer List
    2107              :  * of the columns being retrieved by WITH CHECK OPTION or RETURNING (if any),
    2108              :  * which is returned to *retrieved_attrs.
    2109              :  *
    2110              :  * This also stores end position of the VALUES clause, so that we can rebuild
    2111              :  * an INSERT for a batch of rows later.
    2112              :  */
    2113              : void
    2114            0 : deparseInsertSql(StringInfo buf, RangeTblEntry *rte,
    2115              :                                  Index rtindex, Relation rel,
    2116              :                                  List *targetAttrs, bool doNothing,
    2117              :                                  List *withCheckOptionList, List *returningList,
    2118              :                                  List **retrieved_attrs, int *values_end_len)
    2119              : {
    2120            0 :         TupleDesc       tupdesc = RelationGetDescr(rel);
    2121            0 :         AttrNumber      pindex;
    2122            0 :         bool            first;
    2123            0 :         ListCell   *lc;
    2124              : 
    2125            0 :         appendStringInfoString(buf, "INSERT INTO ");
    2126            0 :         deparseRelation(buf, rel);
    2127              : 
    2128            0 :         if (targetAttrs)
    2129              :         {
    2130            0 :                 appendStringInfoChar(buf, '(');
    2131              : 
    2132            0 :                 first = true;
    2133            0 :                 foreach(lc, targetAttrs)
    2134              :                 {
    2135            0 :                         int                     attnum = lfirst_int(lc);
    2136              : 
    2137            0 :                         if (!first)
    2138            0 :                                 appendStringInfoString(buf, ", ");
    2139            0 :                         first = false;
    2140              : 
    2141            0 :                         deparseColumnRef(buf, rtindex, attnum, rte, false);
    2142            0 :                 }
    2143              : 
    2144            0 :                 appendStringInfoString(buf, ") VALUES (");
    2145              : 
    2146            0 :                 pindex = 1;
    2147            0 :                 first = true;
    2148            0 :                 foreach(lc, targetAttrs)
    2149              :                 {
    2150            0 :                         int                     attnum = lfirst_int(lc);
    2151            0 :                         CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
    2152              : 
    2153            0 :                         if (!first)
    2154            0 :                                 appendStringInfoString(buf, ", ");
    2155            0 :                         first = false;
    2156              : 
    2157            0 :                         if (attr->attgenerated)
    2158            0 :                                 appendStringInfoString(buf, "DEFAULT");
    2159              :                         else
    2160              :                         {
    2161            0 :                                 appendStringInfo(buf, "$%d", pindex);
    2162            0 :                                 pindex++;
    2163              :                         }
    2164            0 :                 }
    2165              : 
    2166            0 :                 appendStringInfoChar(buf, ')');
    2167            0 :         }
    2168              :         else
    2169            0 :                 appendStringInfoString(buf, " DEFAULT VALUES");
    2170            0 :         *values_end_len = buf->len;
    2171              : 
    2172            0 :         if (doNothing)
    2173            0 :                 appendStringInfoString(buf, " ON CONFLICT DO NOTHING");
    2174              : 
    2175            0 :         deparseReturningList(buf, rte, rtindex, rel,
    2176            0 :                                                  rel->trigdesc && rel->trigdesc->trig_insert_after_row,
    2177            0 :                                                  withCheckOptionList, returningList, retrieved_attrs);
    2178            0 : }
    2179              : 
    2180              : /*
    2181              :  * rebuild remote INSERT statement
    2182              :  *
    2183              :  * Provided a number of rows in a batch, builds INSERT statement with the
    2184              :  * right number of parameters.
    2185              :  */
    2186              : void
    2187            0 : rebuildInsertSql(StringInfo buf, Relation rel,
    2188              :                                  char *orig_query, List *target_attrs,
    2189              :                                  int values_end_len, int num_params,
    2190              :                                  int num_rows)
    2191              : {
    2192            0 :         TupleDesc       tupdesc = RelationGetDescr(rel);
    2193            0 :         int                     i;
    2194            0 :         int                     pindex;
    2195            0 :         bool            first;
    2196            0 :         ListCell   *lc;
    2197              : 
    2198              :         /* Make sure the values_end_len is sensible */
    2199            0 :         Assert((values_end_len > 0) && (values_end_len <= strlen(orig_query)));
    2200              : 
    2201              :         /* Copy up to the end of the first record from the original query */
    2202            0 :         appendBinaryStringInfo(buf, orig_query, values_end_len);
    2203              : 
    2204              :         /*
    2205              :          * Add records to VALUES clause (we already have parameters for the first
    2206              :          * row, so start at the right offset).
    2207              :          */
    2208            0 :         pindex = num_params + 1;
    2209            0 :         for (i = 0; i < num_rows; i++)
    2210              :         {
    2211            0 :                 appendStringInfoString(buf, ", (");
    2212              : 
    2213            0 :                 first = true;
    2214            0 :                 foreach(lc, target_attrs)
    2215              :                 {
    2216            0 :                         int                     attnum = lfirst_int(lc);
    2217            0 :                         CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
    2218              : 
    2219            0 :                         if (!first)
    2220            0 :                                 appendStringInfoString(buf, ", ");
    2221            0 :                         first = false;
    2222              : 
    2223            0 :                         if (attr->attgenerated)
    2224            0 :                                 appendStringInfoString(buf, "DEFAULT");
    2225              :                         else
    2226              :                         {
    2227            0 :                                 appendStringInfo(buf, "$%d", pindex);
    2228            0 :                                 pindex++;
    2229              :                         }
    2230            0 :                 }
    2231              : 
    2232            0 :                 appendStringInfoChar(buf, ')');
    2233            0 :         }
    2234              : 
    2235              :         /* Copy stuff after VALUES clause from the original query */
    2236            0 :         appendStringInfoString(buf, orig_query + values_end_len);
    2237            0 : }
    2238              : 
    2239              : /*
    2240              :  * deparse remote UPDATE statement
    2241              :  *
    2242              :  * The statement text is appended to buf, and we also create an integer List
    2243              :  * of the columns being retrieved by WITH CHECK OPTION or RETURNING (if any),
    2244              :  * which is returned to *retrieved_attrs.
    2245              :  */
    2246              : void
    2247            0 : deparseUpdateSql(StringInfo buf, RangeTblEntry *rte,
    2248              :                                  Index rtindex, Relation rel,
    2249              :                                  List *targetAttrs,
    2250              :                                  List *withCheckOptionList, List *returningList,
    2251              :                                  List **retrieved_attrs)
    2252              : {
    2253            0 :         TupleDesc       tupdesc = RelationGetDescr(rel);
    2254            0 :         AttrNumber      pindex;
    2255            0 :         bool            first;
    2256            0 :         ListCell   *lc;
    2257              : 
    2258            0 :         appendStringInfoString(buf, "UPDATE ");
    2259            0 :         deparseRelation(buf, rel);
    2260            0 :         appendStringInfoString(buf, " SET ");
    2261              : 
    2262            0 :         pindex = 2;                                     /* ctid is always the first param */
    2263            0 :         first = true;
    2264            0 :         foreach(lc, targetAttrs)
    2265              :         {
    2266            0 :                 int                     attnum = lfirst_int(lc);
    2267            0 :                 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
    2268              : 
    2269            0 :                 if (!first)
    2270            0 :                         appendStringInfoString(buf, ", ");
    2271            0 :                 first = false;
    2272              : 
    2273            0 :                 deparseColumnRef(buf, rtindex, attnum, rte, false);
    2274            0 :                 if (attr->attgenerated)
    2275            0 :                         appendStringInfoString(buf, " = DEFAULT");
    2276              :                 else
    2277              :                 {
    2278            0 :                         appendStringInfo(buf, " = $%d", pindex);
    2279            0 :                         pindex++;
    2280              :                 }
    2281            0 :         }
    2282            0 :         appendStringInfoString(buf, " WHERE ctid = $1");
    2283              : 
    2284            0 :         deparseReturningList(buf, rte, rtindex, rel,
    2285            0 :                                                  rel->trigdesc && rel->trigdesc->trig_update_after_row,
    2286            0 :                                                  withCheckOptionList, returningList, retrieved_attrs);
    2287            0 : }
    2288              : 
    2289              : /*
    2290              :  * deparse remote UPDATE statement
    2291              :  *
    2292              :  * 'buf' is the output buffer to append the statement to
    2293              :  * 'rtindex' is the RT index of the associated target relation
    2294              :  * 'rel' is the relation descriptor for the target relation
    2295              :  * 'foreignrel' is the RelOptInfo for the target relation or the join relation
    2296              :  *              containing all base relations in the query
    2297              :  * 'targetlist' is the tlist of the underlying foreign-scan plan node
    2298              :  *              (note that this only contains new-value expressions and junk attrs)
    2299              :  * 'targetAttrs' is the target columns of the UPDATE
    2300              :  * 'remote_conds' is the qual clauses that must be evaluated remotely
    2301              :  * '*params_list' is an output list of exprs that will become remote Params
    2302              :  * 'returningList' is the RETURNING targetlist
    2303              :  * '*retrieved_attrs' is an output list of integers of columns being retrieved
    2304              :  *              by RETURNING (if any)
    2305              :  */
    2306              : void
    2307            0 : deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
    2308              :                                            Index rtindex, Relation rel,
    2309              :                                            RelOptInfo *foreignrel,
    2310              :                                            List *targetlist,
    2311              :                                            List *targetAttrs,
    2312              :                                            List *remote_conds,
    2313              :                                            List **params_list,
    2314              :                                            List *returningList,
    2315              :                                            List **retrieved_attrs)
    2316              : {
    2317            0 :         deparse_expr_cxt context;
    2318            0 :         int                     nestlevel;
    2319            0 :         bool            first;
    2320            0 :         RangeTblEntry *rte = planner_rt_fetch(rtindex, root);
    2321            0 :         ListCell   *lc,
    2322              :                            *lc2;
    2323            0 :         List       *additional_conds = NIL;
    2324              : 
    2325              :         /* Set up context struct for recursion */
    2326            0 :         context.root = root;
    2327            0 :         context.foreignrel = foreignrel;
    2328            0 :         context.scanrel = foreignrel;
    2329            0 :         context.buf = buf;
    2330            0 :         context.params_list = params_list;
    2331              : 
    2332            0 :         appendStringInfoString(buf, "UPDATE ");
    2333            0 :         deparseRelation(buf, rel);
    2334            0 :         if (foreignrel->reloptkind == RELOPT_JOINREL)
    2335            0 :                 appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
    2336            0 :         appendStringInfoString(buf, " SET ");
    2337              : 
    2338              :         /* Make sure any constants in the exprs are printed portably */
    2339            0 :         nestlevel = set_transmission_modes();
    2340              : 
    2341            0 :         first = true;
    2342            0 :         forboth(lc, targetlist, lc2, targetAttrs)
    2343              :         {
    2344            0 :                 TargetEntry *tle = lfirst_node(TargetEntry, lc);
    2345            0 :                 int                     attnum = lfirst_int(lc2);
    2346              : 
    2347              :                 /* update's new-value expressions shouldn't be resjunk */
    2348            0 :                 Assert(!tle->resjunk);
    2349              : 
    2350            0 :                 if (!first)
    2351            0 :                         appendStringInfoString(buf, ", ");
    2352            0 :                 first = false;
    2353              : 
    2354            0 :                 deparseColumnRef(buf, rtindex, attnum, rte, false);
    2355            0 :                 appendStringInfoString(buf, " = ");
    2356            0 :                 deparseExpr((Expr *) tle->expr, &context);
    2357            0 :         }
    2358              : 
    2359            0 :         reset_transmission_modes(nestlevel);
    2360              : 
    2361            0 :         if (foreignrel->reloptkind == RELOPT_JOINREL)
    2362              :         {
    2363            0 :                 List       *ignore_conds = NIL;
    2364              : 
    2365              : 
    2366            0 :                 appendStringInfoString(buf, " FROM ");
    2367            0 :                 deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
    2368            0 :                                                           &ignore_conds, &additional_conds, params_list);
    2369            0 :                 remote_conds = list_concat(remote_conds, ignore_conds);
    2370            0 :         }
    2371              : 
    2372            0 :         appendWhereClause(remote_conds, additional_conds, &context);
    2373              : 
    2374            0 :         if (additional_conds != NIL)
    2375            0 :                 list_free_deep(additional_conds);
    2376              : 
    2377            0 :         if (foreignrel->reloptkind == RELOPT_JOINREL)
    2378            0 :                 deparseExplicitTargetList(returningList, true, retrieved_attrs,
    2379              :                                                                   &context);
    2380              :         else
    2381            0 :                 deparseReturningList(buf, rte, rtindex, rel, false,
    2382            0 :                                                          NIL, returningList, retrieved_attrs);
    2383            0 : }
    2384              : 
    2385              : /*
    2386              :  * deparse remote DELETE statement
    2387              :  *
    2388              :  * The statement text is appended to buf, and we also create an integer List
    2389              :  * of the columns being retrieved by RETURNING (if any), which is returned
    2390              :  * to *retrieved_attrs.
    2391              :  */
    2392              : void
    2393            0 : deparseDeleteSql(StringInfo buf, RangeTblEntry *rte,
    2394              :                                  Index rtindex, Relation rel,
    2395              :                                  List *returningList,
    2396              :                                  List **retrieved_attrs)
    2397              : {
    2398            0 :         appendStringInfoString(buf, "DELETE FROM ");
    2399            0 :         deparseRelation(buf, rel);
    2400            0 :         appendStringInfoString(buf, " WHERE ctid = $1");
    2401              : 
    2402            0 :         deparseReturningList(buf, rte, rtindex, rel,
    2403            0 :                                                  rel->trigdesc && rel->trigdesc->trig_delete_after_row,
    2404            0 :                                                  NIL, returningList, retrieved_attrs);
    2405            0 : }
    2406              : 
    2407              : /*
    2408              :  * deparse remote DELETE statement
    2409              :  *
    2410              :  * 'buf' is the output buffer to append the statement to
    2411              :  * 'rtindex' is the RT index of the associated target relation
    2412              :  * 'rel' is the relation descriptor for the target relation
    2413              :  * 'foreignrel' is the RelOptInfo for the target relation or the join relation
    2414              :  *              containing all base relations in the query
    2415              :  * 'remote_conds' is the qual clauses that must be evaluated remotely
    2416              :  * '*params_list' is an output list of exprs that will become remote Params
    2417              :  * 'returningList' is the RETURNING targetlist
    2418              :  * '*retrieved_attrs' is an output list of integers of columns being retrieved
    2419              :  *              by RETURNING (if any)
    2420              :  */
    2421              : void
    2422            0 : deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
    2423              :                                            Index rtindex, Relation rel,
    2424              :                                            RelOptInfo *foreignrel,
    2425              :                                            List *remote_conds,
    2426              :                                            List **params_list,
    2427              :                                            List *returningList,
    2428              :                                            List **retrieved_attrs)
    2429              : {
    2430            0 :         deparse_expr_cxt context;
    2431            0 :         List       *additional_conds = NIL;
    2432              : 
    2433              :         /* Set up context struct for recursion */
    2434            0 :         context.root = root;
    2435            0 :         context.foreignrel = foreignrel;
    2436            0 :         context.scanrel = foreignrel;
    2437            0 :         context.buf = buf;
    2438            0 :         context.params_list = params_list;
    2439              : 
    2440            0 :         appendStringInfoString(buf, "DELETE FROM ");
    2441            0 :         deparseRelation(buf, rel);
    2442            0 :         if (foreignrel->reloptkind == RELOPT_JOINREL)
    2443            0 :                 appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
    2444              : 
    2445            0 :         if (foreignrel->reloptkind == RELOPT_JOINREL)
    2446              :         {
    2447            0 :                 List       *ignore_conds = NIL;
    2448              : 
    2449            0 :                 appendStringInfoString(buf, " USING ");
    2450            0 :                 deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
    2451            0 :                                                           &ignore_conds, &additional_conds, params_list);
    2452            0 :                 remote_conds = list_concat(remote_conds, ignore_conds);
    2453            0 :         }
    2454              : 
    2455            0 :         appendWhereClause(remote_conds, additional_conds, &context);
    2456              : 
    2457            0 :         if (additional_conds != NIL)
    2458            0 :                 list_free_deep(additional_conds);
    2459              : 
    2460            0 :         if (foreignrel->reloptkind == RELOPT_JOINREL)
    2461            0 :                 deparseExplicitTargetList(returningList, true, retrieved_attrs,
    2462              :                                                                   &context);
    2463              :         else
    2464            0 :                 deparseReturningList(buf, planner_rt_fetch(rtindex, root),
    2465            0 :                                                          rtindex, rel, false,
    2466            0 :                                                          NIL, returningList, retrieved_attrs);
    2467            0 : }
    2468              : 
    2469              : /*
    2470              :  * Add a RETURNING clause, if needed, to an INSERT/UPDATE/DELETE.
    2471              :  */
    2472              : static void
    2473            0 : deparseReturningList(StringInfo buf, RangeTblEntry *rte,
    2474              :                                          Index rtindex, Relation rel,
    2475              :                                          bool trig_after_row,
    2476              :                                          List *withCheckOptionList,
    2477              :                                          List *returningList,
    2478              :                                          List **retrieved_attrs)
    2479              : {
    2480            0 :         Bitmapset  *attrs_used = NULL;
    2481              : 
    2482            0 :         if (trig_after_row)
    2483              :         {
    2484              :                 /* whole-row reference acquires all non-system columns */
    2485            0 :                 attrs_used =
    2486            0 :                         bms_make_singleton(0 - FirstLowInvalidHeapAttributeNumber);
    2487            0 :         }
    2488              : 
    2489            0 :         if (withCheckOptionList != NIL)
    2490              :         {
    2491              :                 /*
    2492              :                  * We need the attrs, non-system and system, mentioned in the local
    2493              :                  * query's WITH CHECK OPTION list.
    2494              :                  *
    2495              :                  * Note: we do this to ensure that WCO constraints will be evaluated
    2496              :                  * on the data actually inserted/updated on the remote side, which
    2497              :                  * might differ from the data supplied by the core code, for example
    2498              :                  * as a result of remote triggers.
    2499              :                  */
    2500            0 :                 pull_varattnos((Node *) withCheckOptionList, rtindex,
    2501              :                                            &attrs_used);
    2502            0 :         }
    2503              : 
    2504            0 :         if (returningList != NIL)
    2505              :         {
    2506              :                 /*
    2507              :                  * We need the attrs, non-system and system, mentioned in the local
    2508              :                  * query's RETURNING list.
    2509              :                  */
    2510            0 :                 pull_varattnos((Node *) returningList, rtindex,
    2511              :                                            &attrs_used);
    2512            0 :         }
    2513              : 
    2514            0 :         if (attrs_used != NULL)
    2515            0 :                 deparseTargetList(buf, rte, rtindex, rel, true, attrs_used, false,
    2516            0 :                                                   retrieved_attrs);
    2517              :         else
    2518            0 :                 *retrieved_attrs = NIL;
    2519            0 : }
    2520              : 
    2521              : /*
    2522              :  * Construct SELECT statement to acquire size in blocks of given relation.
    2523              :  *
    2524              :  * Note: we use local definition of block size, not remote definition.
    2525              :  * This is perhaps debatable.
    2526              :  *
    2527              :  * Note: pg_relation_size() exists in 8.1 and later.
    2528              :  */
    2529              : void
    2530            0 : deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
    2531              : {
    2532            0 :         StringInfoData relname;
    2533              : 
    2534              :         /* We'll need the remote relation name as a literal. */
    2535            0 :         initStringInfo(&relname);
    2536            0 :         deparseRelation(&relname, rel);
    2537              : 
    2538            0 :         appendStringInfoString(buf, "SELECT pg_catalog.pg_relation_size(");
    2539            0 :         deparseStringLiteral(buf, relname.data);
    2540            0 :         appendStringInfo(buf, "::pg_catalog.regclass) / %d", BLCKSZ);
    2541            0 : }
    2542              : 
    2543              : /*
    2544              :  * Construct SELECT statement to acquire the number of rows and the relkind of
    2545              :  * a relation.
    2546              :  *
    2547              :  * Note: we just return the remote server's reltuples value, which might
    2548              :  * be off a good deal, but it doesn't seem worth working harder.  See
    2549              :  * comments in postgresAcquireSampleRowsFunc.
    2550              :  */
    2551              : void
    2552            0 : deparseAnalyzeInfoSql(StringInfo buf, Relation rel)
    2553              : {
    2554            0 :         StringInfoData relname;
    2555              : 
    2556              :         /* We'll need the remote relation name as a literal. */
    2557            0 :         initStringInfo(&relname);
    2558            0 :         deparseRelation(&relname, rel);
    2559              : 
    2560            0 :         appendStringInfoString(buf, "SELECT reltuples, relkind FROM pg_catalog.pg_class WHERE oid = ");
    2561            0 :         deparseStringLiteral(buf, relname.data);
    2562            0 :         appendStringInfoString(buf, "::pg_catalog.regclass");
    2563            0 : }
    2564              : 
    2565              : /*
    2566              :  * Construct SELECT statement to acquire sample rows of given relation.
    2567              :  *
    2568              :  * SELECT command is appended to buf, and list of columns retrieved
    2569              :  * is returned to *retrieved_attrs.
    2570              :  *
    2571              :  * We only support sampling methods we can decide based on server version.
    2572              :  * Allowing custom TSM modules (like tsm_system_rows) might be useful, but it
    2573              :  * would require detecting which extensions are installed, to allow automatic
    2574              :  * fall-back. Moreover, the methods may use different parameters like number
    2575              :  * of rows (and not sampling rate). So we leave this for future improvements.
    2576              :  *
    2577              :  * Using random() to sample rows on the remote server has the advantage that
    2578              :  * this works on all PostgreSQL versions (unlike TABLESAMPLE), and that it
    2579              :  * does the sampling on the remote side (without transferring everything and
    2580              :  * then discarding most rows).
    2581              :  *
    2582              :  * The disadvantage is that we still have to read all rows and evaluate the
    2583              :  * random(), while TABLESAMPLE (at least with the "system" method) may skip.
    2584              :  * It's not that different from the "bernoulli" method, though.
    2585              :  *
    2586              :  * We could also do "ORDER BY random() LIMIT x", which would always pick
    2587              :  * the expected number of rows, but it requires sorting so it may be much
    2588              :  * more expensive (particularly on large tables, which is what the
    2589              :  * remote sampling is meant to improve).
    2590              :  */
    2591              : void
    2592            0 : deparseAnalyzeSql(StringInfo buf, Relation rel,
    2593              :                                   PgFdwSamplingMethod sample_method, double sample_frac,
    2594              :                                   List **retrieved_attrs)
    2595              : {
    2596            0 :         Oid                     relid = RelationGetRelid(rel);
    2597            0 :         TupleDesc       tupdesc = RelationGetDescr(rel);
    2598            0 :         int                     i;
    2599            0 :         char       *colname;
    2600            0 :         List       *options;
    2601            0 :         ListCell   *lc;
    2602            0 :         bool            first = true;
    2603              : 
    2604            0 :         *retrieved_attrs = NIL;
    2605              : 
    2606            0 :         appendStringInfoString(buf, "SELECT ");
    2607            0 :         for (i = 0; i < tupdesc->natts; i++)
    2608              :         {
    2609              :                 /* Ignore dropped columns. */
    2610            0 :                 if (TupleDescAttr(tupdesc, i)->attisdropped)
    2611            0 :                         continue;
    2612              : 
    2613            0 :                 if (!first)
    2614            0 :                         appendStringInfoString(buf, ", ");
    2615            0 :                 first = false;
    2616              : 
    2617              :                 /* Use attribute name or column_name option. */
    2618            0 :                 colname = NameStr(TupleDescAttr(tupdesc, i)->attname);
    2619            0 :                 options = GetForeignColumnOptions(relid, i + 1);
    2620              : 
    2621            0 :                 foreach(lc, options)
    2622              :                 {
    2623            0 :                         DefElem    *def = (DefElem *) lfirst(lc);
    2624              : 
    2625            0 :                         if (strcmp(def->defname, "column_name") == 0)
    2626              :                         {
    2627            0 :                                 colname = defGetString(def);
    2628            0 :                                 break;
    2629              :                         }
    2630            0 :                 }
    2631              : 
    2632            0 :                 appendStringInfoString(buf, quote_identifier(colname));
    2633              : 
    2634            0 :                 *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
    2635            0 :         }
    2636              : 
    2637              :         /* Don't generate bad syntax for zero-column relation. */
    2638            0 :         if (first)
    2639            0 :                 appendStringInfoString(buf, "NULL");
    2640              : 
    2641              :         /*
    2642              :          * Construct FROM clause, and perhaps WHERE clause too, depending on the
    2643              :          * selected sampling method.
    2644              :          */
    2645            0 :         appendStringInfoString(buf, " FROM ");
    2646            0 :         deparseRelation(buf, rel);
    2647              : 
    2648            0 :         switch (sample_method)
    2649              :         {
    2650              :                 case ANALYZE_SAMPLE_OFF:
    2651              :                         /* nothing to do here */
    2652              :                         break;
    2653              : 
    2654              :                 case ANALYZE_SAMPLE_RANDOM:
    2655            0 :                         appendStringInfo(buf, " WHERE pg_catalog.random() < %f", sample_frac);
    2656            0 :                         break;
    2657              : 
    2658              :                 case ANALYZE_SAMPLE_SYSTEM:
    2659            0 :                         appendStringInfo(buf, " TABLESAMPLE SYSTEM(%f)", (100.0 * sample_frac));
    2660            0 :                         break;
    2661              : 
    2662              :                 case ANALYZE_SAMPLE_BERNOULLI:
    2663            0 :                         appendStringInfo(buf, " TABLESAMPLE BERNOULLI(%f)", (100.0 * sample_frac));
    2664            0 :                         break;
    2665              : 
    2666              :                 case ANALYZE_SAMPLE_AUTO:
    2667              :                         /* should have been resolved into actual method */
    2668            0 :                         elog(ERROR, "unexpected sampling method");
    2669            0 :                         break;
    2670              :         }
    2671            0 : }
    2672              : 
    2673              : /*
    2674              :  * Construct a simple "TRUNCATE rel" statement
    2675              :  */
    2676              : void
    2677            0 : deparseTruncateSql(StringInfo buf,
    2678              :                                    List *rels,
    2679              :                                    DropBehavior behavior,
    2680              :                                    bool restart_seqs)
    2681              : {
    2682            0 :         ListCell   *cell;
    2683              : 
    2684            0 :         appendStringInfoString(buf, "TRUNCATE ");
    2685              : 
    2686            0 :         foreach(cell, rels)
    2687              :         {
    2688            0 :                 Relation        rel = lfirst(cell);
    2689              : 
    2690            0 :                 if (cell != list_head(rels))
    2691            0 :                         appendStringInfoString(buf, ", ");
    2692              : 
    2693            0 :                 deparseRelation(buf, rel);
    2694            0 :         }
    2695              : 
    2696            0 :         appendStringInfo(buf, " %s IDENTITY",
    2697            0 :                                          restart_seqs ? "RESTART" : "CONTINUE");
    2698              : 
    2699            0 :         if (behavior == DROP_RESTRICT)
    2700            0 :                 appendStringInfoString(buf, " RESTRICT");
    2701            0 :         else if (behavior == DROP_CASCADE)
    2702            0 :                 appendStringInfoString(buf, " CASCADE");
    2703            0 : }
    2704              : 
    2705              : /*
    2706              :  * Construct name to use for given column, and emit it into buf.
    2707              :  * If it has a column_name FDW option, use that instead of attribute name.
    2708              :  *
    2709              :  * If qualify_col is true, qualify column name with the alias of relation.
    2710              :  */
    2711              : static void
    2712            0 : deparseColumnRef(StringInfo buf, int varno, int varattno, RangeTblEntry *rte,
    2713              :                                  bool qualify_col)
    2714              : {
    2715              :         /* We support fetching the remote side's CTID and OID. */
    2716            0 :         if (varattno == SelfItemPointerAttributeNumber)
    2717              :         {
    2718            0 :                 if (qualify_col)
    2719            0 :                         ADD_REL_QUALIFIER(buf, varno);
    2720            0 :                 appendStringInfoString(buf, "ctid");
    2721            0 :         }
    2722            0 :         else if (varattno < 0)
    2723              :         {
    2724              :                 /*
    2725              :                  * All other system attributes are fetched as 0, except for table OID,
    2726              :                  * which is fetched as the local table OID.  However, we must be
    2727              :                  * careful; the table could be beneath an outer join, in which case it
    2728              :                  * must go to NULL whenever the rest of the row does.
    2729              :                  */
    2730            0 :                 Oid                     fetchval = 0;
    2731              : 
    2732            0 :                 if (varattno == TableOidAttributeNumber)
    2733            0 :                         fetchval = rte->relid;
    2734              : 
    2735            0 :                 if (qualify_col)
    2736              :                 {
    2737            0 :                         appendStringInfoString(buf, "CASE WHEN (");
    2738            0 :                         ADD_REL_QUALIFIER(buf, varno);
    2739            0 :                         appendStringInfo(buf, "*)::text IS NOT NULL THEN %u END", fetchval);
    2740            0 :                 }
    2741              :                 else
    2742            0 :                         appendStringInfo(buf, "%u", fetchval);
    2743            0 :         }
    2744            0 :         else if (varattno == 0)
    2745              :         {
    2746              :                 /* Whole row reference */
    2747            0 :                 Relation        rel;
    2748            0 :                 Bitmapset  *attrs_used;
    2749              : 
    2750              :                 /* Required only to be passed down to deparseTargetList(). */
    2751            0 :                 List       *retrieved_attrs;
    2752              : 
    2753              :                 /*
    2754              :                  * The lock on the relation will be held by upper callers, so it's
    2755              :                  * fine to open it with no lock here.
    2756              :                  */
    2757            0 :                 rel = table_open(rte->relid, NoLock);
    2758              : 
    2759              :                 /*
    2760              :                  * The local name of the foreign table can not be recognized by the
    2761              :                  * foreign server and the table it references on foreign server might
    2762              :                  * have different column ordering or different columns than those
    2763              :                  * declared locally. Hence we have to deparse whole-row reference as
    2764              :                  * ROW(columns referenced locally). Construct this by deparsing a
    2765              :                  * "whole row" attribute.
    2766              :                  */
    2767            0 :                 attrs_used = bms_add_member(NULL,
    2768              :                                                                         0 - FirstLowInvalidHeapAttributeNumber);
    2769              : 
    2770              :                 /*
    2771              :                  * In case the whole-row reference is under an outer join then it has
    2772              :                  * to go NULL whenever the rest of the row goes NULL. Deparsing a join
    2773              :                  * query would always involve multiple relations, thus qualify_col
    2774              :                  * would be true.
    2775              :                  */
    2776            0 :                 if (qualify_col)
    2777              :                 {
    2778            0 :                         appendStringInfoString(buf, "CASE WHEN (");
    2779            0 :                         ADD_REL_QUALIFIER(buf, varno);
    2780            0 :                         appendStringInfoString(buf, "*)::text IS NOT NULL THEN ");
    2781            0 :                 }
    2782              : 
    2783            0 :                 appendStringInfoString(buf, "ROW(");
    2784            0 :                 deparseTargetList(buf, rte, varno, rel, false, attrs_used, qualify_col,
    2785              :                                                   &retrieved_attrs);
    2786            0 :                 appendStringInfoChar(buf, ')');
    2787              : 
    2788              :                 /* Complete the CASE WHEN statement started above. */
    2789            0 :                 if (qualify_col)
    2790            0 :                         appendStringInfoString(buf, " END");
    2791              : 
    2792            0 :                 table_close(rel, NoLock);
    2793            0 :                 bms_free(attrs_used);
    2794            0 :         }
    2795              :         else
    2796              :         {
    2797            0 :                 char       *colname = NULL;
    2798            0 :                 List       *options;
    2799            0 :                 ListCell   *lc;
    2800              : 
    2801              :                 /* varno must not be any of OUTER_VAR, INNER_VAR and INDEX_VAR. */
    2802            0 :                 Assert(!IS_SPECIAL_VARNO(varno));
    2803              : 
    2804              :                 /*
    2805              :                  * If it's a column of a foreign table, and it has the column_name FDW
    2806              :                  * option, use that value.
    2807              :                  */
    2808            0 :                 options = GetForeignColumnOptions(rte->relid, varattno);
    2809            0 :                 foreach(lc, options)
    2810              :                 {
    2811            0 :                         DefElem    *def = (DefElem *) lfirst(lc);
    2812              : 
    2813            0 :                         if (strcmp(def->defname, "column_name") == 0)
    2814              :                         {
    2815            0 :                                 colname = defGetString(def);
    2816            0 :                                 break;
    2817              :                         }
    2818            0 :                 }
    2819              : 
    2820              :                 /*
    2821              :                  * If it's a column of a regular table or it doesn't have column_name
    2822              :                  * FDW option, use attribute name.
    2823              :                  */
    2824            0 :                 if (colname == NULL)
    2825            0 :                         colname = get_attname(rte->relid, varattno, false);
    2826              : 
    2827            0 :                 if (qualify_col)
    2828            0 :                         ADD_REL_QUALIFIER(buf, varno);
    2829              : 
    2830            0 :                 appendStringInfoString(buf, quote_identifier(colname));
    2831            0 :         }
    2832            0 : }
    2833              : 
    2834              : /*
    2835              :  * Append remote name of specified foreign table to buf.
    2836              :  * Use value of table_name FDW option (if any) instead of relation's name.
    2837              :  * Similarly, schema_name FDW option overrides schema name.
    2838              :  */
    2839              : static void
    2840            0 : deparseRelation(StringInfo buf, Relation rel)
    2841              : {
    2842            0 :         ForeignTable *table;
    2843            0 :         const char *nspname = NULL;
    2844            0 :         const char *relname = NULL;
    2845            0 :         ListCell   *lc;
    2846              : 
    2847              :         /* obtain additional catalog information. */
    2848            0 :         table = GetForeignTable(RelationGetRelid(rel));
    2849              : 
    2850              :         /*
    2851              :          * Use value of FDW options if any, instead of the name of object itself.
    2852              :          */
    2853            0 :         foreach(lc, table->options)
    2854              :         {
    2855            0 :                 DefElem    *def = (DefElem *) lfirst(lc);
    2856              : 
    2857            0 :                 if (strcmp(def->defname, "schema_name") == 0)
    2858            0 :                         nspname = defGetString(def);
    2859            0 :                 else if (strcmp(def->defname, "table_name") == 0)
    2860            0 :                         relname = defGetString(def);
    2861            0 :         }
    2862              : 
    2863              :         /*
    2864              :          * Note: we could skip printing the schema name if it's pg_catalog, but
    2865              :          * that doesn't seem worth the trouble.
    2866              :          */
    2867            0 :         if (nspname == NULL)
    2868            0 :                 nspname = get_namespace_name(RelationGetNamespace(rel));
    2869            0 :         if (relname == NULL)
    2870            0 :                 relname = RelationGetRelationName(rel);
    2871              : 
    2872            0 :         appendStringInfo(buf, "%s.%s",
    2873            0 :                                          quote_identifier(nspname), quote_identifier(relname));
    2874            0 : }
    2875              : 
    2876              : /*
    2877              :  * Append a SQL string literal representing "val" to buf.
    2878              :  */
    2879              : void
    2880            0 : deparseStringLiteral(StringInfo buf, const char *val)
    2881              : {
    2882            0 :         const char *valptr;
    2883              : 
    2884              :         /*
    2885              :          * Rather than making assumptions about the remote server's value of
    2886              :          * standard_conforming_strings, always use E'foo' syntax if there are any
    2887              :          * backslashes.  This will fail on remote servers before 8.1, but those
    2888              :          * are long out of support.
    2889              :          */
    2890            0 :         if (strchr(val, '\\') != NULL)
    2891            0 :                 appendStringInfoChar(buf, ESCAPE_STRING_SYNTAX);
    2892            0 :         appendStringInfoChar(buf, '\'');
    2893            0 :         for (valptr = val; *valptr; valptr++)
    2894              :         {
    2895            0 :                 char            ch = *valptr;
    2896              : 
    2897            0 :                 if (SQL_STR_DOUBLE(ch, true))
    2898            0 :                         appendStringInfoChar(buf, ch);
    2899            0 :                 appendStringInfoChar(buf, ch);
    2900            0 :         }
    2901            0 :         appendStringInfoChar(buf, '\'');
    2902            0 : }
    2903              : 
    2904              : /*
    2905              :  * Deparse given expression into context->buf.
    2906              :  *
    2907              :  * This function must support all the same node types that foreign_expr_walker
    2908              :  * accepts.
    2909              :  *
    2910              :  * Note: unlike ruleutils.c, we just use a simple hard-wired parenthesization
    2911              :  * scheme: anything more complex than a Var, Const, function call or cast
    2912              :  * should be self-parenthesized.
    2913              :  */
    2914              : static void
    2915            0 : deparseExpr(Expr *node, deparse_expr_cxt *context)
    2916              : {
    2917            0 :         if (node == NULL)
    2918            0 :                 return;
    2919              : 
    2920            0 :         switch (nodeTag(node))
    2921              :         {
    2922              :                 case T_Var:
    2923            0 :                         deparseVar((Var *) node, context);
    2924            0 :                         break;
    2925              :                 case T_Const:
    2926            0 :                         deparseConst((Const *) node, context, 0);
    2927            0 :                         break;
    2928              :                 case T_Param:
    2929            0 :                         deparseParam((Param *) node, context);
    2930            0 :                         break;
    2931              :                 case T_SubscriptingRef:
    2932            0 :                         deparseSubscriptingRef((SubscriptingRef *) node, context);
    2933            0 :                         break;
    2934              :                 case T_FuncExpr:
    2935            0 :                         deparseFuncExpr((FuncExpr *) node, context);
    2936            0 :                         break;
    2937              :                 case T_OpExpr:
    2938            0 :                         deparseOpExpr((OpExpr *) node, context);
    2939            0 :                         break;
    2940              :                 case T_DistinctExpr:
    2941            0 :                         deparseDistinctExpr((DistinctExpr *) node, context);
    2942            0 :                         break;
    2943              :                 case T_ScalarArrayOpExpr:
    2944            0 :                         deparseScalarArrayOpExpr((ScalarArrayOpExpr *) node, context);
    2945            0 :                         break;
    2946              :                 case T_RelabelType:
    2947            0 :                         deparseRelabelType((RelabelType *) node, context);
    2948            0 :                         break;
    2949              :                 case T_ArrayCoerceExpr:
    2950            0 :                         deparseArrayCoerceExpr((ArrayCoerceExpr *) node, context);
    2951            0 :                         break;
    2952              :                 case T_BoolExpr:
    2953            0 :                         deparseBoolExpr((BoolExpr *) node, context);
    2954            0 :                         break;
    2955              :                 case T_NullTest:
    2956            0 :                         deparseNullTest((NullTest *) node, context);
    2957            0 :                         break;
    2958              :                 case T_CaseExpr:
    2959            0 :                         deparseCaseExpr((CaseExpr *) node, context);
    2960            0 :                         break;
    2961              :                 case T_ArrayExpr:
    2962            0 :                         deparseArrayExpr((ArrayExpr *) node, context);
    2963            0 :                         break;
    2964              :                 case T_Aggref:
    2965            0 :                         deparseAggref((Aggref *) node, context);
    2966            0 :                         break;
    2967              :                 default:
    2968            0 :                         elog(ERROR, "unsupported expression type for deparse: %d",
    2969              :                                  (int) nodeTag(node));
    2970            0 :                         break;
    2971              :         }
    2972            0 : }
    2973              : 
    2974              : /*
    2975              :  * Deparse given Var node into context->buf.
    2976              :  *
    2977              :  * If the Var belongs to the foreign relation, just print its remote name.
    2978              :  * Otherwise, it's effectively a Param (and will in fact be a Param at
    2979              :  * run time).  Handle it the same way we handle plain Params --- see
    2980              :  * deparseParam for comments.
    2981              :  */
    2982              : static void
    2983            0 : deparseVar(Var *node, deparse_expr_cxt *context)
    2984              : {
    2985            0 :         Relids          relids = context->scanrel->relids;
    2986            0 :         int                     relno;
    2987            0 :         int                     colno;
    2988              : 
    2989              :         /* Qualify columns when multiple relations are involved. */
    2990            0 :         bool            qualify_col = (bms_membership(relids) == BMS_MULTIPLE);
    2991              : 
    2992              :         /*
    2993              :          * If the Var belongs to the foreign relation that is deparsed as a
    2994              :          * subquery, use the relation and column alias to the Var provided by the
    2995              :          * subquery, instead of the remote name.
    2996              :          */
    2997            0 :         if (is_subquery_var(node, context->scanrel, &relno, &colno))
    2998              :         {
    2999            0 :                 appendStringInfo(context->buf, "%s%d.%s%d",
    3000            0 :                                                  SUBQUERY_REL_ALIAS_PREFIX, relno,
    3001            0 :                                                  SUBQUERY_COL_ALIAS_PREFIX, colno);
    3002            0 :                 return;
    3003              :         }
    3004              : 
    3005            0 :         if (bms_is_member(node->varno, relids) && node->varlevelsup == 0)
    3006            0 :                 deparseColumnRef(context->buf, node->varno, node->varattno,
    3007            0 :                                                  planner_rt_fetch(node->varno, context->root),
    3008            0 :                                                  qualify_col);
    3009              :         else
    3010              :         {
    3011              :                 /* Treat like a Param */
    3012            0 :                 if (context->params_list)
    3013              :                 {
    3014            0 :                         int                     pindex = 0;
    3015            0 :                         ListCell   *lc;
    3016              : 
    3017              :                         /* find its index in params_list */
    3018            0 :                         foreach(lc, *context->params_list)
    3019              :                         {
    3020            0 :                                 pindex++;
    3021            0 :                                 if (equal(node, (Node *) lfirst(lc)))
    3022            0 :                                         break;
    3023            0 :                         }
    3024            0 :                         if (lc == NULL)
    3025              :                         {
    3026              :                                 /* not in list, so add it */
    3027            0 :                                 pindex++;
    3028            0 :                                 *context->params_list = lappend(*context->params_list, node);
    3029            0 :                         }
    3030              : 
    3031            0 :                         printRemoteParam(pindex, node->vartype, node->vartypmod, context);
    3032            0 :                 }
    3033              :                 else
    3034              :                 {
    3035            0 :                         printRemotePlaceholder(node->vartype, node->vartypmod, context);
    3036              :                 }
    3037              :         }
    3038            0 : }
    3039              : 
    3040              : /*
    3041              :  * Deparse given constant value into context->buf.
    3042              :  *
    3043              :  * This function has to be kept in sync with ruleutils.c's get_const_expr.
    3044              :  *
    3045              :  * As in that function, showtype can be -1 to never show "::typename"
    3046              :  * decoration, +1 to always show it, or 0 to show it only if the constant
    3047              :  * wouldn't be assumed to be the right type by default.
    3048              :  *
    3049              :  * In addition, this code allows showtype to be -2 to indicate that we should
    3050              :  * not show "::typename" decoration if the constant is printed as an untyped
    3051              :  * literal or NULL (while in other cases, behaving as for showtype == 0).
    3052              :  */
    3053              : static void
    3054            0 : deparseConst(Const *node, deparse_expr_cxt *context, int showtype)
    3055              : {
    3056            0 :         StringInfo      buf = context->buf;
    3057            0 :         Oid                     typoutput;
    3058            0 :         bool            typIsVarlena;
    3059            0 :         char       *extval;
    3060            0 :         bool            isfloat = false;
    3061            0 :         bool            isstring = false;
    3062            0 :         bool            needlabel;
    3063              : 
    3064            0 :         if (node->constisnull)
    3065              :         {
    3066            0 :                 appendStringInfoString(buf, "NULL");
    3067            0 :                 if (showtype >= 0)
    3068            0 :                         appendStringInfo(buf, "::%s",
    3069            0 :                                                          deparse_type_name(node->consttype,
    3070            0 :                                                                                            node->consttypmod));
    3071            0 :                 return;
    3072              :         }
    3073              : 
    3074            0 :         getTypeOutputInfo(node->consttype,
    3075              :                                           &typoutput, &typIsVarlena);
    3076            0 :         extval = OidOutputFunctionCall(typoutput, node->constvalue);
    3077              : 
    3078            0 :         switch (node->consttype)
    3079              :         {
    3080              :                 case INT2OID:
    3081              :                 case INT4OID:
    3082              :                 case INT8OID:
    3083              :                 case OIDOID:
    3084              :                 case FLOAT4OID:
    3085              :                 case FLOAT8OID:
    3086              :                 case NUMERICOID:
    3087              :                         {
    3088              :                                 /*
    3089              :                                  * No need to quote unless it's a special value such as 'NaN'.
    3090              :                                  * See comments in get_const_expr().
    3091              :                                  */
    3092            0 :                                 if (strspn(extval, "0123456789+-eE.") == strlen(extval))
    3093              :                                 {
    3094            0 :                                         if (extval[0] == '+' || extval[0] == '-')
    3095            0 :                                                 appendStringInfo(buf, "(%s)", extval);
    3096              :                                         else
    3097            0 :                                                 appendStringInfoString(buf, extval);
    3098            0 :                                         if (strcspn(extval, "eE.") != strlen(extval))
    3099            0 :                                                 isfloat = true; /* it looks like a float */
    3100            0 :                                 }
    3101              :                                 else
    3102            0 :                                         appendStringInfo(buf, "'%s'", extval);
    3103              :                         }
    3104            0 :                         break;
    3105              :                 case BITOID:
    3106              :                 case VARBITOID:
    3107            0 :                         appendStringInfo(buf, "B'%s'", extval);
    3108            0 :                         break;
    3109              :                 case BOOLOID:
    3110            0 :                         if (strcmp(extval, "t") == 0)
    3111            0 :                                 appendStringInfoString(buf, "true");
    3112              :                         else
    3113            0 :                                 appendStringInfoString(buf, "false");
    3114            0 :                         break;
    3115              :                 default:
    3116            0 :                         deparseStringLiteral(buf, extval);
    3117            0 :                         isstring = true;
    3118            0 :                         break;
    3119              :         }
    3120              : 
    3121            0 :         pfree(extval);
    3122              : 
    3123            0 :         if (showtype == -1)
    3124            0 :                 return;                                 /* never print type label */
    3125              : 
    3126              :         /*
    3127              :          * For showtype == 0, append ::typename unless the constant will be
    3128              :          * implicitly typed as the right type when it is read in.
    3129              :          *
    3130              :          * XXX this code has to be kept in sync with the behavior of the parser,
    3131              :          * especially make_const.
    3132              :          */
    3133            0 :         switch (node->consttype)
    3134              :         {
    3135              :                 case BOOLOID:
    3136              :                 case INT4OID:
    3137              :                 case UNKNOWNOID:
    3138            0 :                         needlabel = false;
    3139            0 :                         break;
    3140              :                 case NUMERICOID:
    3141            0 :                         needlabel = !isfloat || (node->consttypmod >= 0);
    3142            0 :                         break;
    3143              :                 default:
    3144            0 :                         if (showtype == -2)
    3145              :                         {
    3146              :                                 /* label unless we printed it as an untyped string */
    3147            0 :                                 needlabel = !isstring;
    3148            0 :                         }
    3149              :                         else
    3150            0 :                                 needlabel = true;
    3151            0 :                         break;
    3152              :         }
    3153            0 :         if (needlabel || showtype > 0)
    3154            0 :                 appendStringInfo(buf, "::%s",
    3155            0 :                                                  deparse_type_name(node->consttype,
    3156            0 :                                                                                    node->consttypmod));
    3157            0 : }
    3158              : 
    3159              : /*
    3160              :  * Deparse given Param node.
    3161              :  *
    3162              :  * If we're generating the query "for real", add the Param to
    3163              :  * context->params_list if it's not already present, and then use its index
    3164              :  * in that list as the remote parameter number.  During EXPLAIN, there's
    3165              :  * no need to identify a parameter number.
    3166              :  */
    3167              : static void
    3168            0 : deparseParam(Param *node, deparse_expr_cxt *context)
    3169              : {
    3170            0 :         if (context->params_list)
    3171              :         {
    3172            0 :                 int                     pindex = 0;
    3173            0 :                 ListCell   *lc;
    3174              : 
    3175              :                 /* find its index in params_list */
    3176            0 :                 foreach(lc, *context->params_list)
    3177              :                 {
    3178            0 :                         pindex++;
    3179            0 :                         if (equal(node, (Node *) lfirst(lc)))
    3180            0 :                                 break;
    3181            0 :                 }
    3182            0 :                 if (lc == NULL)
    3183              :                 {
    3184              :                         /* not in list, so add it */
    3185            0 :                         pindex++;
    3186            0 :                         *context->params_list = lappend(*context->params_list, node);
    3187            0 :                 }
    3188              : 
    3189            0 :                 printRemoteParam(pindex, node->paramtype, node->paramtypmod, context);
    3190            0 :         }
    3191              :         else
    3192              :         {
    3193            0 :                 printRemotePlaceholder(node->paramtype, node->paramtypmod, context);
    3194              :         }
    3195            0 : }
    3196              : 
    3197              : /*
    3198              :  * Deparse a container subscript expression.
    3199              :  */
    3200              : static void
    3201            0 : deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context)
    3202              : {
    3203            0 :         StringInfo      buf = context->buf;
    3204            0 :         ListCell   *lowlist_item;
    3205            0 :         ListCell   *uplist_item;
    3206              : 
    3207              :         /* Always parenthesize the expression. */
    3208            0 :         appendStringInfoChar(buf, '(');
    3209              : 
    3210              :         /*
    3211              :          * Deparse referenced array expression first.  If that expression includes
    3212              :          * a cast, we have to parenthesize to prevent the array subscript from
    3213              :          * being taken as typename decoration.  We can avoid that in the typical
    3214              :          * case of subscripting a Var, but otherwise do it.
    3215              :          */
    3216            0 :         if (IsA(node->refexpr, Var))
    3217            0 :                 deparseExpr(node->refexpr, context);
    3218              :         else
    3219              :         {
    3220            0 :                 appendStringInfoChar(buf, '(');
    3221            0 :                 deparseExpr(node->refexpr, context);
    3222            0 :                 appendStringInfoChar(buf, ')');
    3223              :         }
    3224              : 
    3225              :         /* Deparse subscript expressions. */
    3226            0 :         lowlist_item = list_head(node->reflowerindexpr);     /* could be NULL */
    3227            0 :         foreach(uplist_item, node->refupperindexpr)
    3228              :         {
    3229            0 :                 appendStringInfoChar(buf, '[');
    3230            0 :                 if (lowlist_item)
    3231              :                 {
    3232            0 :                         deparseExpr(lfirst(lowlist_item), context);
    3233            0 :                         appendStringInfoChar(buf, ':');
    3234            0 :                         lowlist_item = lnext(node->reflowerindexpr, lowlist_item);
    3235            0 :                 }
    3236            0 :                 deparseExpr(lfirst(uplist_item), context);
    3237            0 :                 appendStringInfoChar(buf, ']');
    3238            0 :         }
    3239              : 
    3240            0 :         appendStringInfoChar(buf, ')');
    3241            0 : }
    3242              : 
    3243              : /*
    3244              :  * Deparse a function call.
    3245              :  */
    3246              : static void
    3247            0 : deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context)
    3248              : {
    3249            0 :         StringInfo      buf = context->buf;
    3250            0 :         bool            use_variadic;
    3251            0 :         bool            first;
    3252            0 :         ListCell   *arg;
    3253              : 
    3254              :         /*
    3255              :          * If the function call came from an implicit coercion, then just show the
    3256              :          * first argument.
    3257              :          */
    3258            0 :         if (node->funcformat == COERCE_IMPLICIT_CAST)
    3259              :         {
    3260            0 :                 deparseExpr((Expr *) linitial(node->args), context);
    3261            0 :                 return;
    3262              :         }
    3263              : 
    3264              :         /*
    3265              :          * If the function call came from a cast, then show the first argument
    3266              :          * plus an explicit cast operation.
    3267              :          */
    3268            0 :         if (node->funcformat == COERCE_EXPLICIT_CAST)
    3269              :         {
    3270            0 :                 Oid                     rettype = node->funcresulttype;
    3271            0 :                 int32           coercedTypmod;
    3272              : 
    3273              :                 /* Get the typmod if this is a length-coercion function */
    3274            0 :                 (void) exprIsLengthCoercion((Node *) node, &coercedTypmod);
    3275              : 
    3276            0 :                 deparseExpr((Expr *) linitial(node->args), context);
    3277            0 :                 appendStringInfo(buf, "::%s",
    3278            0 :                                                  deparse_type_name(rettype, coercedTypmod));
    3279              :                 return;
    3280            0 :         }
    3281              : 
    3282              :         /* Check if need to print VARIADIC (cf. ruleutils.c) */
    3283            0 :         use_variadic = node->funcvariadic;
    3284              : 
    3285              :         /*
    3286              :          * Normal function: display as proname(args).
    3287              :          */
    3288            0 :         appendFunctionName(node->funcid, context);
    3289            0 :         appendStringInfoChar(buf, '(');
    3290              : 
    3291              :         /* ... and all the arguments */
    3292            0 :         first = true;
    3293            0 :         foreach(arg, node->args)
    3294              :         {
    3295            0 :                 if (!first)
    3296            0 :                         appendStringInfoString(buf, ", ");
    3297            0 :                 if (use_variadic && lnext(node->args, arg) == NULL)
    3298            0 :                         appendStringInfoString(buf, "VARIADIC ");
    3299            0 :                 deparseExpr((Expr *) lfirst(arg), context);
    3300            0 :                 first = false;
    3301            0 :         }
    3302            0 :         appendStringInfoChar(buf, ')');
    3303            0 : }
    3304              : 
    3305              : /*
    3306              :  * Deparse given operator expression.   To avoid problems around
    3307              :  * priority of operations, we always parenthesize the arguments.
    3308              :  */
    3309              : static void
    3310            0 : deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
    3311              : {
    3312            0 :         StringInfo      buf = context->buf;
    3313            0 :         HeapTuple       tuple;
    3314            0 :         Form_pg_operator form;
    3315            0 :         Expr       *right;
    3316            0 :         bool            canSuppressRightConstCast = false;
    3317            0 :         char            oprkind;
    3318              : 
    3319              :         /* Retrieve information about the operator from system catalog. */
    3320            0 :         tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
    3321            0 :         if (!HeapTupleIsValid(tuple))
    3322            0 :                 elog(ERROR, "cache lookup failed for operator %u", node->opno);
    3323            0 :         form = (Form_pg_operator) GETSTRUCT(tuple);
    3324            0 :         oprkind = form->oprkind;
    3325              : 
    3326              :         /* Sanity check. */
    3327            0 :         Assert((oprkind == 'l' && list_length(node->args) == 1) ||
    3328              :                    (oprkind == 'b' && list_length(node->args) == 2));
    3329              : 
    3330            0 :         right = llast(node->args);
    3331              : 
    3332              :         /* Always parenthesize the expression. */
    3333            0 :         appendStringInfoChar(buf, '(');
    3334              : 
    3335              :         /* Deparse left operand, if any. */
    3336            0 :         if (oprkind == 'b')
    3337              :         {
    3338            0 :                 Expr       *left = linitial(node->args);
    3339            0 :                 Oid                     leftType = exprType((Node *) left);
    3340            0 :                 Oid                     rightType = exprType((Node *) right);
    3341            0 :                 bool            canSuppressLeftConstCast = false;
    3342              : 
    3343              :                 /*
    3344              :                  * When considering a binary operator, if one operand is a Const that
    3345              :                  * can be printed as a bare string literal or NULL (i.e., it will look
    3346              :                  * like type UNKNOWN to the remote parser), the Const normally
    3347              :                  * receives an explicit cast to the operator's input type.  However,
    3348              :                  * in Const-to-Var comparisons where both operands are of the same
    3349              :                  * type, we prefer to suppress the explicit cast, leaving the Const's
    3350              :                  * type resolution up to the remote parser.  The remote's resolution
    3351              :                  * heuristic will assume that an unknown input type being compared to
    3352              :                  * a known input type is of that known type as well.
    3353              :                  *
    3354              :                  * This hack allows some cases to succeed where a remote column is
    3355              :                  * declared with a different type in the local (foreign) table.  By
    3356              :                  * emitting "foreigncol = 'foo'" not "foreigncol = 'foo'::text" or the
    3357              :                  * like, we allow the remote parser to pick an "=" operator that's
    3358              :                  * compatible with whatever type the remote column really is, such as
    3359              :                  * an enum.
    3360              :                  *
    3361              :                  * We allow cast suppression to happen only when the other operand is
    3362              :                  * a plain foreign Var.  Although the remote's unknown-type heuristic
    3363              :                  * would apply to other cases just as well, we would be taking a
    3364              :                  * bigger risk that the inferred type is something unexpected.  With
    3365              :                  * this restriction, if anything goes wrong it's the user's fault for
    3366              :                  * not declaring the local column with the same type as the remote
    3367              :                  * column.
    3368              :                  */
    3369            0 :                 if (leftType == rightType)
    3370              :                 {
    3371            0 :                         if (IsA(left, Const))
    3372            0 :                                 canSuppressLeftConstCast = isPlainForeignVar(right, context);
    3373            0 :                         else if (IsA(right, Const))
    3374            0 :                                 canSuppressRightConstCast = isPlainForeignVar(left, context);
    3375            0 :                 }
    3376              : 
    3377            0 :                 if (canSuppressLeftConstCast)
    3378            0 :                         deparseConst((Const *) left, context, -2);
    3379              :                 else
    3380            0 :                         deparseExpr(left, context);
    3381              : 
    3382            0 :                 appendStringInfoChar(buf, ' ');
    3383            0 :         }
    3384              : 
    3385              :         /* Deparse operator name. */
    3386            0 :         deparseOperatorName(buf, form);
    3387              : 
    3388              :         /* Deparse right operand. */
    3389            0 :         appendStringInfoChar(buf, ' ');
    3390              : 
    3391            0 :         if (canSuppressRightConstCast)
    3392            0 :                 deparseConst((Const *) right, context, -2);
    3393              :         else
    3394            0 :                 deparseExpr(right, context);
    3395              : 
    3396            0 :         appendStringInfoChar(buf, ')');
    3397              : 
    3398            0 :         ReleaseSysCache(tuple);
    3399            0 : }
    3400              : 
    3401              : /*
    3402              :  * Will "node" deparse as a plain foreign Var?
    3403              :  */
    3404              : static bool
    3405            0 : isPlainForeignVar(Expr *node, deparse_expr_cxt *context)
    3406              : {
    3407              :         /*
    3408              :          * We allow the foreign Var to have an implicit RelabelType, mainly so
    3409              :          * that this'll work with varchar columns.  Note that deparseRelabelType
    3410              :          * will not print such a cast, so we're not breaking the restriction that
    3411              :          * the expression print as a plain Var.  We won't risk it for an implicit
    3412              :          * cast that requires a function, nor for non-implicit RelabelType; such
    3413              :          * cases seem too likely to involve semantics changes compared to what
    3414              :          * would happen on the remote side.
    3415              :          */
    3416            0 :         if (IsA(node, RelabelType) &&
    3417            0 :                 ((RelabelType *) node)->relabelformat == COERCE_IMPLICIT_CAST)
    3418            0 :                 node = ((RelabelType *) node)->arg;
    3419              : 
    3420            0 :         if (IsA(node, Var))
    3421              :         {
    3422              :                 /*
    3423              :                  * The Var must be one that'll deparse as a foreign column reference
    3424              :                  * (cf. deparseVar).
    3425              :                  */
    3426            0 :                 Var                *var = (Var *) node;
    3427            0 :                 Relids          relids = context->scanrel->relids;
    3428              : 
    3429            0 :                 if (bms_is_member(var->varno, relids) && var->varlevelsup == 0)
    3430            0 :                         return true;
    3431            0 :         }
    3432              : 
    3433            0 :         return false;
    3434            0 : }
    3435              : 
    3436              : /*
    3437              :  * Print the name of an operator.
    3438              :  */
    3439              : static void
    3440            0 : deparseOperatorName(StringInfo buf, Form_pg_operator opform)
    3441              : {
    3442            0 :         char       *opname;
    3443              : 
    3444              :         /* opname is not a SQL identifier, so we should not quote it. */
    3445            0 :         opname = NameStr(opform->oprname);
    3446              : 
    3447              :         /* Print schema name only if it's not pg_catalog */
    3448            0 :         if (opform->oprnamespace != PG_CATALOG_NAMESPACE)
    3449              :         {
    3450            0 :                 const char *opnspname;
    3451              : 
    3452            0 :                 opnspname = get_namespace_name(opform->oprnamespace);
    3453              :                 /* Print fully qualified operator name. */
    3454            0 :                 appendStringInfo(buf, "OPERATOR(%s.%s)",
    3455            0 :                                                  quote_identifier(opnspname), opname);
    3456            0 :         }
    3457              :         else
    3458              :         {
    3459              :                 /* Just print operator name. */
    3460            0 :                 appendStringInfoString(buf, opname);
    3461              :         }
    3462            0 : }
    3463              : 
    3464              : /*
    3465              :  * Deparse IS DISTINCT FROM.
    3466              :  */
    3467              : static void
    3468            0 : deparseDistinctExpr(DistinctExpr *node, deparse_expr_cxt *context)
    3469              : {
    3470            0 :         StringInfo      buf = context->buf;
    3471              : 
    3472            0 :         Assert(list_length(node->args) == 2);
    3473              : 
    3474            0 :         appendStringInfoChar(buf, '(');
    3475            0 :         deparseExpr(linitial(node->args), context);
    3476            0 :         appendStringInfoString(buf, " IS DISTINCT FROM ");
    3477            0 :         deparseExpr(lsecond(node->args), context);
    3478            0 :         appendStringInfoChar(buf, ')');
    3479            0 : }
    3480              : 
    3481              : /*
    3482              :  * Deparse given ScalarArrayOpExpr expression.  To avoid problems
    3483              :  * around priority of operations, we always parenthesize the arguments.
    3484              :  */
    3485              : static void
    3486            0 : deparseScalarArrayOpExpr(ScalarArrayOpExpr *node, deparse_expr_cxt *context)
    3487              : {
    3488            0 :         StringInfo      buf = context->buf;
    3489            0 :         HeapTuple       tuple;
    3490            0 :         Form_pg_operator form;
    3491            0 :         Expr       *arg1;
    3492            0 :         Expr       *arg2;
    3493              : 
    3494              :         /* Retrieve information about the operator from system catalog. */
    3495            0 :         tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
    3496            0 :         if (!HeapTupleIsValid(tuple))
    3497            0 :                 elog(ERROR, "cache lookup failed for operator %u", node->opno);
    3498            0 :         form = (Form_pg_operator) GETSTRUCT(tuple);
    3499              : 
    3500              :         /* Sanity check. */
    3501            0 :         Assert(list_length(node->args) == 2);
    3502              : 
    3503              :         /* Always parenthesize the expression. */
    3504            0 :         appendStringInfoChar(buf, '(');
    3505              : 
    3506              :         /* Deparse left operand. */
    3507            0 :         arg1 = linitial(node->args);
    3508            0 :         deparseExpr(arg1, context);
    3509            0 :         appendStringInfoChar(buf, ' ');
    3510              : 
    3511              :         /* Deparse operator name plus decoration. */
    3512            0 :         deparseOperatorName(buf, form);
    3513            0 :         appendStringInfo(buf, " %s (", node->useOr ? "ANY" : "ALL");
    3514              : 
    3515              :         /* Deparse right operand. */
    3516            0 :         arg2 = lsecond(node->args);
    3517            0 :         deparseExpr(arg2, context);
    3518              : 
    3519            0 :         appendStringInfoChar(buf, ')');
    3520              : 
    3521              :         /* Always parenthesize the expression. */
    3522            0 :         appendStringInfoChar(buf, ')');
    3523              : 
    3524            0 :         ReleaseSysCache(tuple);
    3525            0 : }
    3526              : 
    3527              : /*
    3528              :  * Deparse a RelabelType (binary-compatible cast) node.
    3529              :  */
    3530              : static void
    3531            0 : deparseRelabelType(RelabelType *node, deparse_expr_cxt *context)
    3532              : {
    3533            0 :         deparseExpr(node->arg, context);
    3534            0 :         if (node->relabelformat != COERCE_IMPLICIT_CAST)
    3535            0 :                 appendStringInfo(context->buf, "::%s",
    3536            0 :                                                  deparse_type_name(node->resulttype,
    3537            0 :                                                                                    node->resulttypmod));
    3538            0 : }
    3539              : 
    3540              : /*
    3541              :  * Deparse an ArrayCoerceExpr (array-type conversion) node.
    3542              :  */
    3543              : static void
    3544            0 : deparseArrayCoerceExpr(ArrayCoerceExpr *node, deparse_expr_cxt *context)
    3545              : {
    3546            0 :         deparseExpr(node->arg, context);
    3547              : 
    3548              :         /*
    3549              :          * No difference how to deparse explicit cast, but if we omit implicit
    3550              :          * cast in the query, it'll be more user-friendly
    3551              :          */
    3552            0 :         if (node->coerceformat != COERCE_IMPLICIT_CAST)
    3553            0 :                 appendStringInfo(context->buf, "::%s",
    3554            0 :                                                  deparse_type_name(node->resulttype,
    3555            0 :                                                                                    node->resulttypmod));
    3556            0 : }
    3557              : 
    3558              : /*
    3559              :  * Deparse a BoolExpr node.
    3560              :  */
    3561              : static void
    3562            0 : deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context)
    3563              : {
    3564            0 :         StringInfo      buf = context->buf;
    3565            0 :         const char *op = NULL;          /* keep compiler quiet */
    3566            0 :         bool            first;
    3567            0 :         ListCell   *lc;
    3568              : 
    3569            0 :         switch (node->boolop)
    3570              :         {
    3571              :                 case AND_EXPR:
    3572            0 :                         op = "AND";
    3573            0 :                         break;
    3574              :                 case OR_EXPR:
    3575            0 :                         op = "OR";
    3576            0 :                         break;
    3577              :                 case NOT_EXPR:
    3578            0 :                         appendStringInfoString(buf, "(NOT ");
    3579            0 :                         deparseExpr(linitial(node->args), context);
    3580            0 :                         appendStringInfoChar(buf, ')');
    3581            0 :                         return;
    3582              :         }
    3583              : 
    3584            0 :         appendStringInfoChar(buf, '(');
    3585            0 :         first = true;
    3586            0 :         foreach(lc, node->args)
    3587              :         {
    3588            0 :                 if (!first)
    3589            0 :                         appendStringInfo(buf, " %s ", op);
    3590            0 :                 deparseExpr((Expr *) lfirst(lc), context);
    3591            0 :                 first = false;
    3592            0 :         }
    3593            0 :         appendStringInfoChar(buf, ')');
    3594            0 : }
    3595              : 
    3596              : /*
    3597              :  * Deparse IS [NOT] NULL expression.
    3598              :  */
    3599              : static void
    3600            0 : deparseNullTest(NullTest *node, deparse_expr_cxt *context)
    3601              : {
    3602            0 :         StringInfo      buf = context->buf;
    3603              : 
    3604            0 :         appendStringInfoChar(buf, '(');
    3605            0 :         deparseExpr(node->arg, context);
    3606              : 
    3607              :         /*
    3608              :          * For scalar inputs, we prefer to print as IS [NOT] NULL, which is
    3609              :          * shorter and traditional.  If it's a rowtype input but we're applying a
    3610              :          * scalar test, must print IS [NOT] DISTINCT FROM NULL to be semantically
    3611              :          * correct.
    3612              :          */
    3613            0 :         if (node->argisrow || !type_is_rowtype(exprType((Node *) node->arg)))
    3614              :         {
    3615            0 :                 if (node->nulltesttype == IS_NULL)
    3616            0 :                         appendStringInfoString(buf, " IS NULL)");
    3617              :                 else
    3618            0 :                         appendStringInfoString(buf, " IS NOT NULL)");
    3619            0 :         }
    3620              :         else
    3621              :         {
    3622            0 :                 if (node->nulltesttype == IS_NULL)
    3623            0 :                         appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL)");
    3624              :                 else
    3625            0 :                         appendStringInfoString(buf, " IS DISTINCT FROM NULL)");
    3626              :         }
    3627            0 : }
    3628              : 
    3629              : /*
    3630              :  * Deparse CASE expression
    3631              :  */
    3632              : static void
    3633            0 : deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context)
    3634              : {
    3635            0 :         StringInfo      buf = context->buf;
    3636            0 :         ListCell   *lc;
    3637              : 
    3638            0 :         appendStringInfoString(buf, "(CASE");
    3639              : 
    3640              :         /* If this is a CASE arg WHEN then emit the arg expression */
    3641            0 :         if (node->arg != NULL)
    3642              :         {
    3643            0 :                 appendStringInfoChar(buf, ' ');
    3644            0 :                 deparseExpr(node->arg, context);
    3645            0 :         }
    3646              : 
    3647              :         /* Add each condition/result of the CASE clause */
    3648            0 :         foreach(lc, node->args)
    3649              :         {
    3650            0 :                 CaseWhen   *whenclause = (CaseWhen *) lfirst(lc);
    3651              : 
    3652              :                 /* WHEN */
    3653            0 :                 appendStringInfoString(buf, " WHEN ");
    3654            0 :                 if (node->arg == NULL)       /* CASE WHEN */
    3655            0 :                         deparseExpr(whenclause->expr, context);
    3656              :                 else                                    /* CASE arg WHEN */
    3657              :                 {
    3658              :                         /* Ignore the CaseTestExpr and equality operator. */
    3659            0 :                         deparseExpr(lsecond(castNode(OpExpr, whenclause->expr)->args),
    3660            0 :                                                 context);
    3661              :                 }
    3662              : 
    3663              :                 /* THEN */
    3664            0 :                 appendStringInfoString(buf, " THEN ");
    3665            0 :                 deparseExpr(whenclause->result, context);
    3666            0 :         }
    3667              : 
    3668              :         /* add ELSE if present */
    3669            0 :         if (node->defresult != NULL)
    3670              :         {
    3671            0 :                 appendStringInfoString(buf, " ELSE ");
    3672            0 :                 deparseExpr(node->defresult, context);
    3673            0 :         }
    3674              : 
    3675              :         /* append END */
    3676            0 :         appendStringInfoString(buf, " END)");
    3677            0 : }
    3678              : 
    3679              : /*
    3680              :  * Deparse ARRAY[...] construct.
    3681              :  */
    3682              : static void
    3683            0 : deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context)
    3684              : {
    3685            0 :         StringInfo      buf = context->buf;
    3686            0 :         bool            first = true;
    3687            0 :         ListCell   *lc;
    3688              : 
    3689            0 :         appendStringInfoString(buf, "ARRAY[");
    3690            0 :         foreach(lc, node->elements)
    3691              :         {
    3692            0 :                 if (!first)
    3693            0 :                         appendStringInfoString(buf, ", ");
    3694            0 :                 deparseExpr(lfirst(lc), context);
    3695            0 :                 first = false;
    3696            0 :         }
    3697            0 :         appendStringInfoChar(buf, ']');
    3698              : 
    3699              :         /* If the array is empty, we need an explicit cast to the array type. */
    3700            0 :         if (node->elements == NIL)
    3701            0 :                 appendStringInfo(buf, "::%s",
    3702            0 :                                                  deparse_type_name(node->array_typeid, -1));
    3703            0 : }
    3704              : 
    3705              : /*
    3706              :  * Deparse an Aggref node.
    3707              :  */
    3708              : static void
    3709            0 : deparseAggref(Aggref *node, deparse_expr_cxt *context)
    3710              : {
    3711            0 :         StringInfo      buf = context->buf;
    3712            0 :         bool            use_variadic;
    3713              : 
    3714              :         /* Only basic, non-split aggregation accepted. */
    3715            0 :         Assert(node->aggsplit == AGGSPLIT_SIMPLE);
    3716              : 
    3717              :         /* Check if need to print VARIADIC (cf. ruleutils.c) */
    3718            0 :         use_variadic = node->aggvariadic;
    3719              : 
    3720              :         /* Find aggregate name from aggfnoid which is a pg_proc entry */
    3721            0 :         appendFunctionName(node->aggfnoid, context);
    3722            0 :         appendStringInfoChar(buf, '(');
    3723              : 
    3724              :         /* Add DISTINCT */
    3725            0 :         appendStringInfoString(buf, (node->aggdistinct != NIL) ? "DISTINCT " : "");
    3726              : 
    3727            0 :         if (AGGKIND_IS_ORDERED_SET(node->aggkind))
    3728              :         {
    3729              :                 /* Add WITHIN GROUP (ORDER BY ..) */
    3730            0 :                 ListCell   *arg;
    3731            0 :                 bool            first = true;
    3732              : 
    3733            0 :                 Assert(!node->aggvariadic);
    3734            0 :                 Assert(node->aggorder != NIL);
    3735              : 
    3736            0 :                 foreach(arg, node->aggdirectargs)
    3737              :                 {
    3738            0 :                         if (!first)
    3739            0 :                                 appendStringInfoString(buf, ", ");
    3740            0 :                         first = false;
    3741              : 
    3742            0 :                         deparseExpr((Expr *) lfirst(arg), context);
    3743            0 :                 }
    3744              : 
    3745            0 :                 appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
    3746            0 :                 appendAggOrderBy(node->aggorder, node->args, context);
    3747            0 :         }
    3748              :         else
    3749              :         {
    3750              :                 /* aggstar can be set only in zero-argument aggregates */
    3751            0 :                 if (node->aggstar)
    3752            0 :                         appendStringInfoChar(buf, '*');
    3753              :                 else
    3754              :                 {
    3755            0 :                         ListCell   *arg;
    3756            0 :                         bool            first = true;
    3757              : 
    3758              :                         /* Add all the arguments */
    3759            0 :                         foreach(arg, node->args)
    3760              :                         {
    3761            0 :                                 TargetEntry *tle = (TargetEntry *) lfirst(arg);
    3762            0 :                                 Node       *n = (Node *) tle->expr;
    3763              : 
    3764            0 :                                 if (tle->resjunk)
    3765            0 :                                         continue;
    3766              : 
    3767            0 :                                 if (!first)
    3768            0 :                                         appendStringInfoString(buf, ", ");
    3769            0 :                                 first = false;
    3770              : 
    3771              :                                 /* Add VARIADIC */
    3772            0 :                                 if (use_variadic && lnext(node->args, arg) == NULL)
    3773            0 :                                         appendStringInfoString(buf, "VARIADIC ");
    3774              : 
    3775            0 :                                 deparseExpr((Expr *) n, context);
    3776            0 :                         }
    3777            0 :                 }
    3778              : 
    3779              :                 /* Add ORDER BY */
    3780            0 :                 if (node->aggorder != NIL)
    3781              :                 {
    3782            0 :                         appendStringInfoString(buf, " ORDER BY ");
    3783            0 :                         appendAggOrderBy(node->aggorder, node->args, context);
    3784            0 :                 }
    3785              :         }
    3786              : 
    3787              :         /* Add FILTER (WHERE ..) */
    3788            0 :         if (node->aggfilter != NULL)
    3789              :         {
    3790            0 :                 appendStringInfoString(buf, ") FILTER (WHERE ");
    3791            0 :                 deparseExpr((Expr *) node->aggfilter, context);
    3792            0 :         }
    3793              : 
    3794            0 :         appendStringInfoChar(buf, ')');
    3795            0 : }
    3796              : 
    3797              : /*
    3798              :  * Append ORDER BY within aggregate function.
    3799              :  */
    3800              : static void
    3801            0 : appendAggOrderBy(List *orderList, List *targetList, deparse_expr_cxt *context)
    3802              : {
    3803            0 :         StringInfo      buf = context->buf;
    3804            0 :         ListCell   *lc;
    3805            0 :         bool            first = true;
    3806              : 
    3807            0 :         foreach(lc, orderList)
    3808              :         {
    3809            0 :                 SortGroupClause *srt = (SortGroupClause *) lfirst(lc);
    3810            0 :                 Node       *sortexpr;
    3811              : 
    3812            0 :                 if (!first)
    3813            0 :                         appendStringInfoString(buf, ", ");
    3814            0 :                 first = false;
    3815              : 
    3816              :                 /* Deparse the sort expression proper. */
    3817            0 :                 sortexpr = deparseSortGroupClause(srt->tleSortGroupRef, targetList,
    3818            0 :                                                                                   false, context);
    3819              :                 /* Add decoration as needed. */
    3820            0 :                 appendOrderBySuffix(srt->sortop, exprType(sortexpr), srt->nulls_first,
    3821            0 :                                                         context);
    3822            0 :         }
    3823            0 : }
    3824              : 
    3825              : /*
    3826              :  * Append the ASC, DESC, USING <OPERATOR> and NULLS FIRST / NULLS LAST parts
    3827              :  * of an ORDER BY clause.
    3828              :  */
    3829              : static void
    3830            0 : appendOrderBySuffix(Oid sortop, Oid sortcoltype, bool nulls_first,
    3831              :                                         deparse_expr_cxt *context)
    3832              : {
    3833            0 :         StringInfo      buf = context->buf;
    3834            0 :         TypeCacheEntry *typentry;
    3835              : 
    3836              :         /* See whether operator is default < or > for sort expr's datatype. */
    3837            0 :         typentry = lookup_type_cache(sortcoltype,
    3838              :                                                                  TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
    3839              : 
    3840            0 :         if (sortop == typentry->lt_opr)
    3841            0 :                 appendStringInfoString(buf, " ASC");
    3842            0 :         else if (sortop == typentry->gt_opr)
    3843            0 :                 appendStringInfoString(buf, " DESC");
    3844              :         else
    3845              :         {
    3846            0 :                 HeapTuple       opertup;
    3847            0 :                 Form_pg_operator operform;
    3848              : 
    3849            0 :                 appendStringInfoString(buf, " USING ");
    3850              : 
    3851              :                 /* Append operator name. */
    3852            0 :                 opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(sortop));
    3853            0 :                 if (!HeapTupleIsValid(opertup))
    3854            0 :                         elog(ERROR, "cache lookup failed for operator %u", sortop);
    3855            0 :                 operform = (Form_pg_operator) GETSTRUCT(opertup);
    3856            0 :                 deparseOperatorName(buf, operform);
    3857            0 :                 ReleaseSysCache(opertup);
    3858            0 :         }
    3859              : 
    3860            0 :         if (nulls_first)
    3861            0 :                 appendStringInfoString(buf, " NULLS FIRST");
    3862              :         else
    3863            0 :                 appendStringInfoString(buf, " NULLS LAST");
    3864            0 : }
    3865              : 
    3866              : /*
    3867              :  * Print the representation of a parameter to be sent to the remote side.
    3868              :  *
    3869              :  * Note: we always label the Param's type explicitly rather than relying on
    3870              :  * transmitting a numeric type OID in PQsendQueryParams().  This allows us to
    3871              :  * avoid assuming that types have the same OIDs on the remote side as they
    3872              :  * do locally --- they need only have the same names.
    3873              :  */
    3874              : static void
    3875            0 : printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod,
    3876              :                                  deparse_expr_cxt *context)
    3877              : {
    3878            0 :         StringInfo      buf = context->buf;
    3879            0 :         char       *ptypename = deparse_type_name(paramtype, paramtypmod);
    3880              : 
    3881            0 :         appendStringInfo(buf, "$%d::%s", paramindex, ptypename);
    3882            0 : }
    3883              : 
    3884              : /*
    3885              :  * Print the representation of a placeholder for a parameter that will be
    3886              :  * sent to the remote side at execution time.
    3887              :  *
    3888              :  * This is used when we're just trying to EXPLAIN the remote query.
    3889              :  * We don't have the actual value of the runtime parameter yet, and we don't
    3890              :  * want the remote planner to generate a plan that depends on such a value
    3891              :  * anyway.  Thus, we can't do something simple like "$1::paramtype".
    3892              :  * Instead, we emit "((SELECT null::paramtype)::paramtype)".
    3893              :  * In all extant versions of Postgres, the planner will see that as an unknown
    3894              :  * constant value, which is what we want.  This might need adjustment if we
    3895              :  * ever make the planner flatten scalar subqueries.  Note: the reason for the
    3896              :  * apparently useless outer cast is to ensure that the representation as a
    3897              :  * whole will be parsed as an a_expr and not a select_with_parens; the latter
    3898              :  * would do the wrong thing in the context "x = ANY(...)".
    3899              :  */
    3900              : static void
    3901            0 : printRemotePlaceholder(Oid paramtype, int32 paramtypmod,
    3902              :                                            deparse_expr_cxt *context)
    3903              : {
    3904            0 :         StringInfo      buf = context->buf;
    3905            0 :         char       *ptypename = deparse_type_name(paramtype, paramtypmod);
    3906              : 
    3907            0 :         appendStringInfo(buf, "((SELECT null::%s)::%s)", ptypename, ptypename);
    3908            0 : }
    3909              : 
    3910              : /*
    3911              :  * Deparse GROUP BY clause.
    3912              :  */
    3913              : static void
    3914            0 : appendGroupByClause(List *tlist, deparse_expr_cxt *context)
    3915              : {
    3916            0 :         StringInfo      buf = context->buf;
    3917            0 :         Query      *query = context->root->parse;
    3918            0 :         ListCell   *lc;
    3919            0 :         bool            first = true;
    3920              : 
    3921              :         /* Nothing to be done, if there's no GROUP BY clause in the query. */
    3922            0 :         if (!query->groupClause)
    3923            0 :                 return;
    3924              : 
    3925            0 :         appendStringInfoString(buf, " GROUP BY ");
    3926              : 
    3927              :         /*
    3928              :          * Queries with grouping sets are not pushed down, so we don't expect
    3929              :          * grouping sets here.
    3930              :          */
    3931            0 :         Assert(!query->groupingSets);
    3932              : 
    3933              :         /*
    3934              :          * We intentionally print query->groupClause not processed_groupClause,
    3935              :          * leaving it to the remote planner to get rid of any redundant GROUP BY
    3936              :          * items again.  This is necessary in case processed_groupClause reduced
    3937              :          * to empty, and in any case the redundancy situation on the remote might
    3938              :          * be different than what we think here.
    3939              :          */
    3940            0 :         foreach(lc, query->groupClause)
    3941              :         {
    3942            0 :                 SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
    3943              : 
    3944            0 :                 if (!first)
    3945            0 :                         appendStringInfoString(buf, ", ");
    3946            0 :                 first = false;
    3947              : 
    3948            0 :                 deparseSortGroupClause(grp->tleSortGroupRef, tlist, true, context);
    3949            0 :         }
    3950            0 : }
    3951              : 
    3952              : /*
    3953              :  * Deparse ORDER BY clause defined by the given pathkeys.
    3954              :  *
    3955              :  * The clause should use Vars from context->scanrel if !has_final_sort,
    3956              :  * or from context->foreignrel's targetlist if has_final_sort.
    3957              :  *
    3958              :  * We find a suitable pathkey expression (some earlier step
    3959              :  * should have verified that there is one) and deparse it.
    3960              :  */
    3961              : static void
    3962            0 : appendOrderByClause(List *pathkeys, bool has_final_sort,
    3963              :                                         deparse_expr_cxt *context)
    3964              : {
    3965            0 :         ListCell   *lcell;
    3966            0 :         int                     nestlevel;
    3967            0 :         StringInfo      buf = context->buf;
    3968            0 :         bool            gotone = false;
    3969              : 
    3970              :         /* Make sure any constants in the exprs are printed portably */
    3971            0 :         nestlevel = set_transmission_modes();
    3972              : 
    3973            0 :         foreach(lcell, pathkeys)
    3974              :         {
    3975            0 :                 PathKey    *pathkey = lfirst(lcell);
    3976            0 :                 EquivalenceMember *em;
    3977            0 :                 Expr       *em_expr;
    3978            0 :                 Oid                     oprid;
    3979              : 
    3980            0 :                 if (has_final_sort)
    3981              :                 {
    3982              :                         /*
    3983              :                          * By construction, context->foreignrel is the input relation to
    3984              :                          * the final sort.
    3985              :                          */
    3986            0 :                         em = find_em_for_rel_target(context->root,
    3987            0 :                                                                                 pathkey->pk_eclass,
    3988            0 :                                                                                 context->foreignrel);
    3989            0 :                 }
    3990              :                 else
    3991            0 :                         em = find_em_for_rel(context->root,
    3992            0 :                                                                  pathkey->pk_eclass,
    3993            0 :                                                                  context->scanrel);
    3994              : 
    3995              :                 /*
    3996              :                  * We don't expect any error here; it would mean that shippability
    3997              :                  * wasn't verified earlier.  For the same reason, we don't recheck
    3998              :                  * shippability of the sort operator.
    3999              :                  */
    4000            0 :                 if (em == NULL)
    4001            0 :                         elog(ERROR, "could not find pathkey item to sort");
    4002              : 
    4003            0 :                 em_expr = em->em_expr;
    4004              : 
    4005              :                 /*
    4006              :                  * If the member is a Const expression then we needn't add it to the
    4007              :                  * ORDER BY clause.  This can happen in UNION ALL queries where the
    4008              :                  * union child targetlist has a Const.  Adding these would be
    4009              :                  * wasteful, but also, for INT columns, an integer literal would be
    4010              :                  * seen as an ordinal column position rather than a value to sort by.
    4011              :                  * deparseConst() does have code to handle this, but it seems less
    4012              :                  * effort on all accounts just to skip these for ORDER BY clauses.
    4013              :                  */
    4014            0 :                 if (IsA(em_expr, Const))
    4015            0 :                         continue;
    4016              : 
    4017            0 :                 if (!gotone)
    4018              :                 {
    4019            0 :                         appendStringInfoString(buf, " ORDER BY ");
    4020            0 :                         gotone = true;
    4021            0 :                 }
    4022              :                 else
    4023            0 :                         appendStringInfoString(buf, ", ");
    4024              : 
    4025              :                 /*
    4026              :                  * Lookup the operator corresponding to the compare type in the
    4027              :                  * opclass. The datatype used by the opfamily is not necessarily the
    4028              :                  * same as the expression type (for array types for example).
    4029              :                  */
    4030            0 :                 oprid = get_opfamily_member_for_cmptype(pathkey->pk_opfamily,
    4031            0 :                                                                                                 em->em_datatype,
    4032            0 :                                                                                                 em->em_datatype,
    4033            0 :                                                                                                 pathkey->pk_cmptype);
    4034            0 :                 if (!OidIsValid(oprid))
    4035            0 :                         elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
    4036              :                                  pathkey->pk_cmptype, em->em_datatype, em->em_datatype,
    4037              :                                  pathkey->pk_opfamily);
    4038              : 
    4039            0 :                 deparseExpr(em_expr, context);
    4040              : 
    4041              :                 /*
    4042              :                  * Here we need to use the expression's actual type to discover
    4043              :                  * whether the desired operator will be the default or not.
    4044              :                  */
    4045            0 :                 appendOrderBySuffix(oprid, exprType((Node *) em_expr),
    4046            0 :                                                         pathkey->pk_nulls_first, context);
    4047              : 
    4048            0 :         }
    4049            0 :         reset_transmission_modes(nestlevel);
    4050            0 : }
    4051              : 
    4052              : /*
    4053              :  * Deparse LIMIT/OFFSET clause.
    4054              :  */
    4055              : static void
    4056            0 : appendLimitClause(deparse_expr_cxt *context)
    4057              : {
    4058            0 :         PlannerInfo *root = context->root;
    4059            0 :         StringInfo      buf = context->buf;
    4060            0 :         int                     nestlevel;
    4061              : 
    4062              :         /* Make sure any constants in the exprs are printed portably */
    4063            0 :         nestlevel = set_transmission_modes();
    4064              : 
    4065            0 :         if (root->parse->limitCount)
    4066              :         {
    4067            0 :                 appendStringInfoString(buf, " LIMIT ");
    4068            0 :                 deparseExpr((Expr *) root->parse->limitCount, context);
    4069            0 :         }
    4070            0 :         if (root->parse->limitOffset)
    4071              :         {
    4072            0 :                 appendStringInfoString(buf, " OFFSET ");
    4073            0 :                 deparseExpr((Expr *) root->parse->limitOffset, context);
    4074            0 :         }
    4075              : 
    4076            0 :         reset_transmission_modes(nestlevel);
    4077            0 : }
    4078              : 
    4079              : /*
    4080              :  * appendFunctionName
    4081              :  *              Deparses function name from given function oid.
    4082              :  */
    4083              : static void
    4084            0 : appendFunctionName(Oid funcid, deparse_expr_cxt *context)
    4085              : {
    4086            0 :         StringInfo      buf = context->buf;
    4087            0 :         HeapTuple       proctup;
    4088            0 :         Form_pg_proc procform;
    4089            0 :         const char *proname;
    4090              : 
    4091            0 :         proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    4092            0 :         if (!HeapTupleIsValid(proctup))
    4093            0 :                 elog(ERROR, "cache lookup failed for function %u", funcid);
    4094            0 :         procform = (Form_pg_proc) GETSTRUCT(proctup);
    4095              : 
    4096              :         /* Print schema name only if it's not pg_catalog */
    4097            0 :         if (procform->pronamespace != PG_CATALOG_NAMESPACE)
    4098              :         {
    4099            0 :                 const char *schemaname;
    4100              : 
    4101            0 :                 schemaname = get_namespace_name(procform->pronamespace);
    4102            0 :                 appendStringInfo(buf, "%s.", quote_identifier(schemaname));
    4103            0 :         }
    4104              : 
    4105              :         /* Always print the function name */
    4106            0 :         proname = NameStr(procform->proname);
    4107            0 :         appendStringInfoString(buf, quote_identifier(proname));
    4108              : 
    4109            0 :         ReleaseSysCache(proctup);
    4110            0 : }
    4111              : 
    4112              : /*
    4113              :  * Appends a sort or group clause.
    4114              :  *
    4115              :  * Like get_rule_sortgroupclause(), returns the expression tree, so caller
    4116              :  * need not find it again.
    4117              :  */
    4118              : static Node *
    4119            0 : deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
    4120              :                                            deparse_expr_cxt *context)
    4121              : {
    4122            0 :         StringInfo      buf = context->buf;
    4123            0 :         TargetEntry *tle;
    4124            0 :         Expr       *expr;
    4125              : 
    4126            0 :         tle = get_sortgroupref_tle(ref, tlist);
    4127            0 :         expr = tle->expr;
    4128              : 
    4129            0 :         if (force_colno)
    4130              :         {
    4131              :                 /* Use column-number form when requested by caller. */
    4132            0 :                 Assert(!tle->resjunk);
    4133            0 :                 appendStringInfo(buf, "%d", tle->resno);
    4134            0 :         }
    4135            0 :         else if (expr && IsA(expr, Const))
    4136              :         {
    4137              :                 /*
    4138              :                  * Force a typecast here so that we don't emit something like "GROUP
    4139              :                  * BY 2", which will be misconstrued as a column position rather than
    4140              :                  * a constant.
    4141              :                  */
    4142            0 :                 deparseConst((Const *) expr, context, 1);
    4143            0 :         }
    4144            0 :         else if (!expr || IsA(expr, Var))
    4145            0 :                 deparseExpr(expr, context);
    4146              :         else
    4147              :         {
    4148              :                 /* Always parenthesize the expression. */
    4149            0 :                 appendStringInfoChar(buf, '(');
    4150            0 :                 deparseExpr(expr, context);
    4151            0 :                 appendStringInfoChar(buf, ')');
    4152              :         }
    4153              : 
    4154            0 :         return (Node *) expr;
    4155            0 : }
    4156              : 
    4157              : 
    4158              : /*
    4159              :  * Returns true if given Var is deparsed as a subquery output column, in
    4160              :  * which case, *relno and *colno are set to the IDs for the relation and
    4161              :  * column alias to the Var provided by the subquery.
    4162              :  */
    4163              : static bool
    4164            0 : is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
    4165              : {
    4166            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    4167            0 :         RelOptInfo *outerrel = fpinfo->outerrel;
    4168            0 :         RelOptInfo *innerrel = fpinfo->innerrel;
    4169              : 
    4170              :         /* Should only be called in these cases. */
    4171            0 :         Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
    4172              : 
    4173              :         /*
    4174              :          * If the given relation isn't a join relation, it doesn't have any lower
    4175              :          * subqueries, so the Var isn't a subquery output column.
    4176              :          */
    4177            0 :         if (!IS_JOIN_REL(foreignrel))
    4178            0 :                 return false;
    4179              : 
    4180              :         /*
    4181              :          * If the Var doesn't belong to any lower subqueries, it isn't a subquery
    4182              :          * output column.
    4183              :          */
    4184            0 :         if (!bms_is_member(node->varno, fpinfo->lower_subquery_rels))
    4185            0 :                 return false;
    4186              : 
    4187            0 :         if (bms_is_member(node->varno, outerrel->relids))
    4188              :         {
    4189              :                 /*
    4190              :                  * If outer relation is deparsed as a subquery, the Var is an output
    4191              :                  * column of the subquery; get the IDs for the relation/column alias.
    4192              :                  */
    4193            0 :                 if (fpinfo->make_outerrel_subquery)
    4194              :                 {
    4195            0 :                         get_relation_column_alias_ids(node, outerrel, relno, colno);
    4196            0 :                         return true;
    4197              :                 }
    4198              : 
    4199              :                 /* Otherwise, recurse into the outer relation. */
    4200            0 :                 return is_subquery_var(node, outerrel, relno, colno);
    4201              :         }
    4202              :         else
    4203              :         {
    4204            0 :                 Assert(bms_is_member(node->varno, innerrel->relids));
    4205              : 
    4206              :                 /*
    4207              :                  * If inner relation is deparsed as a subquery, the Var is an output
    4208              :                  * column of the subquery; get the IDs for the relation/column alias.
    4209              :                  */
    4210            0 :                 if (fpinfo->make_innerrel_subquery)
    4211              :                 {
    4212            0 :                         get_relation_column_alias_ids(node, innerrel, relno, colno);
    4213            0 :                         return true;
    4214              :                 }
    4215              : 
    4216              :                 /* Otherwise, recurse into the inner relation. */
    4217            0 :                 return is_subquery_var(node, innerrel, relno, colno);
    4218              :         }
    4219            0 : }
    4220              : 
    4221              : /*
    4222              :  * Get the IDs for the relation and column alias to given Var belonging to
    4223              :  * given relation, which are returned into *relno and *colno.
    4224              :  */
    4225              : static void
    4226            0 : get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
    4227              :                                                           int *relno, int *colno)
    4228              : {
    4229            0 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    4230            0 :         int                     i;
    4231            0 :         ListCell   *lc;
    4232              : 
    4233              :         /* Get the relation alias ID */
    4234            0 :         *relno = fpinfo->relation_index;
    4235              : 
    4236              :         /* Get the column alias ID */
    4237            0 :         i = 1;
    4238            0 :         foreach(lc, foreignrel->reltarget->exprs)
    4239              :         {
    4240            0 :                 Var                *tlvar = (Var *) lfirst(lc);
    4241              : 
    4242              :                 /*
    4243              :                  * Match reltarget entries only on varno/varattno.  Ideally there
    4244              :                  * would be some cross-check on varnullingrels, but it's unclear what
    4245              :                  * to do exactly; we don't have enough context to know what that value
    4246              :                  * should be.
    4247              :                  */
    4248            0 :                 if (IsA(tlvar, Var) &&
    4249            0 :                         tlvar->varno == node->varno &&
    4250            0 :                         tlvar->varattno == node->varattno)
    4251              :                 {
    4252            0 :                         *colno = i;
    4253            0 :                         return;
    4254              :                 }
    4255            0 :                 i++;
    4256            0 :         }
    4257              : 
    4258              :         /* Shouldn't get here */
    4259            0 :         elog(ERROR, "unexpected expression in subquery output");
    4260            0 : }
        

Generated by: LCOV version 2.3.2-1