LCOV - code coverage report
Current view: top level - contrib/postgres_fdw - postgres_fdw.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.6 % 2236 2092
Test Date: 2026-03-03 14:15:12 Functions: 100.0 % 89 89
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * postgres_fdw.c
       4              :  *        Foreign-data wrapper for remote PostgreSQL servers
       5              :  *
       6              :  * Portions Copyright (c) 2012-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  * IDENTIFICATION
       9              :  *        contrib/postgres_fdw/postgres_fdw.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include <limits.h>
      16              : 
      17              : #include "access/htup_details.h"
      18              : #include "access/sysattr.h"
      19              : #include "access/table.h"
      20              : #include "catalog/pg_opfamily.h"
      21              : #include "commands/defrem.h"
      22              : #include "commands/explain_format.h"
      23              : #include "commands/explain_state.h"
      24              : #include "executor/execAsync.h"
      25              : #include "foreign/fdwapi.h"
      26              : #include "funcapi.h"
      27              : #include "miscadmin.h"
      28              : #include "nodes/makefuncs.h"
      29              : #include "nodes/nodeFuncs.h"
      30              : #include "optimizer/appendinfo.h"
      31              : #include "optimizer/cost.h"
      32              : #include "optimizer/inherit.h"
      33              : #include "optimizer/optimizer.h"
      34              : #include "optimizer/pathnode.h"
      35              : #include "optimizer/paths.h"
      36              : #include "optimizer/planmain.h"
      37              : #include "optimizer/prep.h"
      38              : #include "optimizer/restrictinfo.h"
      39              : #include "optimizer/tlist.h"
      40              : #include "parser/parsetree.h"
      41              : #include "postgres_fdw.h"
      42              : #include "storage/latch.h"
      43              : #include "utils/builtins.h"
      44              : #include "utils/float.h"
      45              : #include "utils/guc.h"
      46              : #include "utils/lsyscache.h"
      47              : #include "utils/memutils.h"
      48              : #include "utils/rel.h"
      49              : #include "utils/sampling.h"
      50              : #include "utils/selfuncs.h"
      51              : 
      52           18 : PG_MODULE_MAGIC_EXT(
      53              :                     .name = "postgres_fdw",
      54              :                     .version = PG_VERSION
      55              : );
      56              : 
      57              : /* Default CPU cost to start up a foreign query. */
      58              : #define DEFAULT_FDW_STARTUP_COST    100.0
      59              : 
      60              : /* Default CPU cost to process 1 row (above and beyond cpu_tuple_cost). */
      61              : #define DEFAULT_FDW_TUPLE_COST      0.2
      62              : 
      63              : /* If no remote estimates, assume a sort costs 20% extra */
      64              : #define DEFAULT_FDW_SORT_MULTIPLIER 1.2
      65              : 
      66              : /*
      67              :  * Indexes of FDW-private information stored in fdw_private lists.
      68              :  *
      69              :  * These items are indexed with the enum FdwScanPrivateIndex, so an item
      70              :  * can be fetched with list_nth().  For example, to get the SELECT statement:
      71              :  *      sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
      72              :  */
      73              : enum FdwScanPrivateIndex
      74              : {
      75              :     /* SQL statement to execute remotely (as a String node) */
      76              :     FdwScanPrivateSelectSql,
      77              :     /* Integer list of attribute numbers retrieved by the SELECT */
      78              :     FdwScanPrivateRetrievedAttrs,
      79              :     /* Integer representing the desired fetch_size */
      80              :     FdwScanPrivateFetchSize,
      81              : 
      82              :     /*
      83              :      * String describing join i.e. names of relations being joined and types
      84              :      * of join, added when the scan is join
      85              :      */
      86              :     FdwScanPrivateRelations,
      87              : };
      88              : 
      89              : /*
      90              :  * Similarly, this enum describes what's kept in the fdw_private list for
      91              :  * a ModifyTable node referencing a postgres_fdw foreign table.  We store:
      92              :  *
      93              :  * 1) INSERT/UPDATE/DELETE statement text to be sent to the remote server
      94              :  * 2) Integer list of target attribute numbers for INSERT/UPDATE
      95              :  *    (NIL for a DELETE)
      96              :  * 3) Length till the end of VALUES clause for INSERT
      97              :  *    (-1 for a DELETE/UPDATE)
      98              :  * 4) Boolean flag showing if the remote query has a RETURNING clause
      99              :  * 5) Integer list of attribute numbers retrieved by RETURNING, if any
     100              :  */
     101              : enum FdwModifyPrivateIndex
     102              : {
     103              :     /* SQL statement to execute remotely (as a String node) */
     104              :     FdwModifyPrivateUpdateSql,
     105              :     /* Integer list of target attribute numbers for INSERT/UPDATE */
     106              :     FdwModifyPrivateTargetAttnums,
     107              :     /* Length till the end of VALUES clause (as an Integer node) */
     108              :     FdwModifyPrivateLen,
     109              :     /* has-returning flag (as a Boolean node) */
     110              :     FdwModifyPrivateHasReturning,
     111              :     /* Integer list of attribute numbers retrieved by RETURNING */
     112              :     FdwModifyPrivateRetrievedAttrs,
     113              : };
     114              : 
     115              : /*
     116              :  * Similarly, this enum describes what's kept in the fdw_private list for
     117              :  * a ForeignScan node that modifies a foreign table directly.  We store:
     118              :  *
     119              :  * 1) UPDATE/DELETE statement text to be sent to the remote server
     120              :  * 2) Boolean flag showing if the remote query has a RETURNING clause
     121              :  * 3) Integer list of attribute numbers retrieved by RETURNING, if any
     122              :  * 4) Boolean flag showing if we set the command es_processed
     123              :  */
     124              : enum FdwDirectModifyPrivateIndex
     125              : {
     126              :     /* SQL statement to execute remotely (as a String node) */
     127              :     FdwDirectModifyPrivateUpdateSql,
     128              :     /* has-returning flag (as a Boolean node) */
     129              :     FdwDirectModifyPrivateHasReturning,
     130              :     /* Integer list of attribute numbers retrieved by RETURNING */
     131              :     FdwDirectModifyPrivateRetrievedAttrs,
     132              :     /* set-processed flag (as a Boolean node) */
     133              :     FdwDirectModifyPrivateSetProcessed,
     134              : };
     135              : 
     136              : /*
     137              :  * Execution state of a foreign scan using postgres_fdw.
     138              :  */
     139              : typedef struct PgFdwScanState
     140              : {
     141              :     Relation    rel;            /* relcache entry for the foreign table. NULL
     142              :                                  * for a foreign join scan. */
     143              :     TupleDesc   tupdesc;        /* tuple descriptor of scan */
     144              :     AttInMetadata *attinmeta;   /* attribute datatype conversion metadata */
     145              : 
     146              :     /* extracted fdw_private data */
     147              :     char       *query;          /* text of SELECT command */
     148              :     List       *retrieved_attrs;    /* list of retrieved attribute numbers */
     149              : 
     150              :     /* for remote query execution */
     151              :     PGconn     *conn;           /* connection for the scan */
     152              :     PgFdwConnState *conn_state; /* extra per-connection state */
     153              :     unsigned int cursor_number; /* quasi-unique ID for my cursor */
     154              :     bool        cursor_exists;  /* have we created the cursor? */
     155              :     int         numParams;      /* number of parameters passed to query */
     156              :     FmgrInfo   *param_flinfo;   /* output conversion functions for them */
     157              :     List       *param_exprs;    /* executable expressions for param values */
     158              :     const char **param_values;  /* textual values of query parameters */
     159              : 
     160              :     /* for storing result tuples */
     161              :     HeapTuple  *tuples;         /* array of currently-retrieved tuples */
     162              :     int         num_tuples;     /* # of tuples in array */
     163              :     int         next_tuple;     /* index of next one to return */
     164              : 
     165              :     /* batch-level state, for optimizing rewinds and avoiding useless fetch */
     166              :     int         fetch_ct_2;     /* Min(# of fetches done, 2) */
     167              :     bool        eof_reached;    /* true if last fetch reached EOF */
     168              : 
     169              :     /* for asynchronous execution */
     170              :     bool        async_capable;  /* engage asynchronous-capable logic? */
     171              : 
     172              :     /* working memory contexts */
     173              :     MemoryContext batch_cxt;    /* context holding current batch of tuples */
     174              :     MemoryContext temp_cxt;     /* context for per-tuple temporary data */
     175              : 
     176              :     int         fetch_size;     /* number of tuples per fetch */
     177              : } PgFdwScanState;
     178              : 
     179              : /*
     180              :  * Execution state of a foreign insert/update/delete operation.
     181              :  */
     182              : typedef struct PgFdwModifyState
     183              : {
     184              :     Relation    rel;            /* relcache entry for the foreign table */
     185              :     AttInMetadata *attinmeta;   /* attribute datatype conversion metadata */
     186              : 
     187              :     /* for remote query execution */
     188              :     PGconn     *conn;           /* connection for the scan */
     189              :     PgFdwConnState *conn_state; /* extra per-connection state */
     190              :     char       *p_name;         /* name of prepared statement, if created */
     191              : 
     192              :     /* extracted fdw_private data */
     193              :     char       *query;          /* text of INSERT/UPDATE/DELETE command */
     194              :     char       *orig_query;     /* original text of INSERT command */
     195              :     List       *target_attrs;   /* list of target attribute numbers */
     196              :     int         values_end;     /* length up to the end of VALUES */
     197              :     int         batch_size;     /* value of FDW option "batch_size" */
     198              :     bool        has_returning;  /* is there a RETURNING clause? */
     199              :     List       *retrieved_attrs;    /* attr numbers retrieved by RETURNING */
     200              : 
     201              :     /* info about parameters for prepared statement */
     202              :     AttrNumber  ctidAttno;      /* attnum of input resjunk ctid column */
     203              :     int         p_nums;         /* number of parameters to transmit */
     204              :     FmgrInfo   *p_flinfo;       /* output conversion functions for them */
     205              : 
     206              :     /* batch operation stuff */
     207              :     int         num_slots;      /* number of slots to insert */
     208              : 
     209              :     /* working memory context */
     210              :     MemoryContext temp_cxt;     /* context for per-tuple temporary data */
     211              : 
     212              :     /* for update row movement if subplan result rel */
     213              :     struct PgFdwModifyState *aux_fmstate;   /* foreign-insert state, if
     214              :                                              * created */
     215              : } PgFdwModifyState;
     216              : 
     217              : /*
     218              :  * Execution state of a foreign scan that modifies a foreign table directly.
     219              :  */
     220              : typedef struct PgFdwDirectModifyState
     221              : {
     222              :     Relation    rel;            /* relcache entry for the foreign table */
     223              :     AttInMetadata *attinmeta;   /* attribute datatype conversion metadata */
     224              : 
     225              :     /* extracted fdw_private data */
     226              :     char       *query;          /* text of UPDATE/DELETE command */
     227              :     bool        has_returning;  /* is there a RETURNING clause? */
     228              :     List       *retrieved_attrs;    /* attr numbers retrieved by RETURNING */
     229              :     bool        set_processed;  /* do we set the command es_processed? */
     230              : 
     231              :     /* for remote query execution */
     232              :     PGconn     *conn;           /* connection for the update */
     233              :     PgFdwConnState *conn_state; /* extra per-connection state */
     234              :     int         numParams;      /* number of parameters passed to query */
     235              :     FmgrInfo   *param_flinfo;   /* output conversion functions for them */
     236              :     List       *param_exprs;    /* executable expressions for param values */
     237              :     const char **param_values;  /* textual values of query parameters */
     238              : 
     239              :     /* for storing result tuples */
     240              :     PGresult   *result;         /* result for query */
     241              :     int         num_tuples;     /* # of result tuples */
     242              :     int         next_tuple;     /* index of next one to return */
     243              :     Relation    resultRel;      /* relcache entry for the target relation */
     244              :     AttrNumber *attnoMap;       /* array of attnums of input user columns */
     245              :     AttrNumber  ctidAttno;      /* attnum of input ctid column */
     246              :     AttrNumber  oidAttno;       /* attnum of input oid column */
     247              :     bool        hasSystemCols;  /* are there system columns of resultRel? */
     248              : 
     249              :     /* working memory context */
     250              :     MemoryContext temp_cxt;     /* context for per-tuple temporary data */
     251              : } PgFdwDirectModifyState;
     252              : 
     253              : /*
     254              :  * Workspace for analyzing a foreign table.
     255              :  */
     256              : typedef struct PgFdwAnalyzeState
     257              : {
     258              :     Relation    rel;            /* relcache entry for the foreign table */
     259              :     AttInMetadata *attinmeta;   /* attribute datatype conversion metadata */
     260              :     List       *retrieved_attrs;    /* attr numbers retrieved by query */
     261              : 
     262              :     /* collected sample rows */
     263              :     HeapTuple  *rows;           /* array of size targrows */
     264              :     int         targrows;       /* target # of sample rows */
     265              :     int         numrows;        /* # of sample rows collected */
     266              : 
     267              :     /* for random sampling */
     268              :     double      samplerows;     /* # of rows fetched */
     269              :     double      rowstoskip;     /* # of rows to skip before next sample */
     270              :     ReservoirStateData rstate;  /* state for reservoir sampling */
     271              : 
     272              :     /* working memory contexts */
     273              :     MemoryContext anl_cxt;      /* context for per-analyze lifespan data */
     274              :     MemoryContext temp_cxt;     /* context for per-tuple temporary data */
     275              : } PgFdwAnalyzeState;
     276              : 
     277              : /*
     278              :  * This enum describes what's kept in the fdw_private list for a ForeignPath.
     279              :  * We store:
     280              :  *
     281              :  * 1) Boolean flag showing if the remote query has the final sort
     282              :  * 2) Boolean flag showing if the remote query has the LIMIT clause
     283              :  */
     284              : enum FdwPathPrivateIndex
     285              : {
     286              :     /* has-final-sort flag (as a Boolean node) */
     287              :     FdwPathPrivateHasFinalSort,
     288              :     /* has-limit flag (as a Boolean node) */
     289              :     FdwPathPrivateHasLimit,
     290              : };
     291              : 
     292              : /* Struct for extra information passed to estimate_path_cost_size() */
     293              : typedef struct
     294              : {
     295              :     PathTarget *target;
     296              :     bool        has_final_sort;
     297              :     bool        has_limit;
     298              :     double      limit_tuples;
     299              :     int64       count_est;
     300              :     int64       offset_est;
     301              : } PgFdwPathExtraData;
     302              : 
     303              : /*
     304              :  * Identify the attribute where data conversion fails.
     305              :  */
     306              : typedef struct ConversionLocation
     307              : {
     308              :     AttrNumber  cur_attno;      /* attribute number being processed, or 0 */
     309              :     Relation    rel;            /* foreign table being processed, or NULL */
     310              :     ForeignScanState *fsstate;  /* plan node being processed, or NULL */
     311              : } ConversionLocation;
     312              : 
     313              : /* Callback argument for ec_member_matches_foreign */
     314              : typedef struct
     315              : {
     316              :     Expr       *current;        /* current expr, or NULL if not yet found */
     317              :     List       *already_used;   /* expressions already dealt with */
     318              : } ec_member_foreign_arg;
     319              : 
     320              : /*
     321              :  * SQL functions
     322              :  */
     323           14 : PG_FUNCTION_INFO_V1(postgres_fdw_handler);
     324              : 
     325              : /*
     326              :  * FDW callback routines
     327              :  */
     328              : static void postgresGetForeignRelSize(PlannerInfo *root,
     329              :                                       RelOptInfo *baserel,
     330              :                                       Oid foreigntableid);
     331              : static void postgresGetForeignPaths(PlannerInfo *root,
     332              :                                     RelOptInfo *baserel,
     333              :                                     Oid foreigntableid);
     334              : static ForeignScan *postgresGetForeignPlan(PlannerInfo *root,
     335              :                                            RelOptInfo *foreignrel,
     336              :                                            Oid foreigntableid,
     337              :                                            ForeignPath *best_path,
     338              :                                            List *tlist,
     339              :                                            List *scan_clauses,
     340              :                                            Plan *outer_plan);
     341              : static void postgresBeginForeignScan(ForeignScanState *node, int eflags);
     342              : static TupleTableSlot *postgresIterateForeignScan(ForeignScanState *node);
     343              : static void postgresReScanForeignScan(ForeignScanState *node);
     344              : static void postgresEndForeignScan(ForeignScanState *node);
     345              : static void postgresAddForeignUpdateTargets(PlannerInfo *root,
     346              :                                             Index rtindex,
     347              :                                             RangeTblEntry *target_rte,
     348              :                                             Relation target_relation);
     349              : static List *postgresPlanForeignModify(PlannerInfo *root,
     350              :                                        ModifyTable *plan,
     351              :                                        Index resultRelation,
     352              :                                        int subplan_index);
     353              : static void postgresBeginForeignModify(ModifyTableState *mtstate,
     354              :                                        ResultRelInfo *resultRelInfo,
     355              :                                        List *fdw_private,
     356              :                                        int subplan_index,
     357              :                                        int eflags);
     358              : static TupleTableSlot *postgresExecForeignInsert(EState *estate,
     359              :                                                  ResultRelInfo *resultRelInfo,
     360              :                                                  TupleTableSlot *slot,
     361              :                                                  TupleTableSlot *planSlot);
     362              : static TupleTableSlot **postgresExecForeignBatchInsert(EState *estate,
     363              :                                                        ResultRelInfo *resultRelInfo,
     364              :                                                        TupleTableSlot **slots,
     365              :                                                        TupleTableSlot **planSlots,
     366              :                                                        int *numSlots);
     367              : static int  postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo);
     368              : static TupleTableSlot *postgresExecForeignUpdate(EState *estate,
     369              :                                                  ResultRelInfo *resultRelInfo,
     370              :                                                  TupleTableSlot *slot,
     371              :                                                  TupleTableSlot *planSlot);
     372              : static TupleTableSlot *postgresExecForeignDelete(EState *estate,
     373              :                                                  ResultRelInfo *resultRelInfo,
     374              :                                                  TupleTableSlot *slot,
     375              :                                                  TupleTableSlot *planSlot);
     376              : static void postgresEndForeignModify(EState *estate,
     377              :                                      ResultRelInfo *resultRelInfo);
     378              : static void postgresBeginForeignInsert(ModifyTableState *mtstate,
     379              :                                        ResultRelInfo *resultRelInfo);
     380              : static void postgresEndForeignInsert(EState *estate,
     381              :                                      ResultRelInfo *resultRelInfo);
     382              : static int  postgresIsForeignRelUpdatable(Relation rel);
     383              : static bool postgresPlanDirectModify(PlannerInfo *root,
     384              :                                      ModifyTable *plan,
     385              :                                      Index resultRelation,
     386              :                                      int subplan_index);
     387              : static void postgresBeginDirectModify(ForeignScanState *node, int eflags);
     388              : static TupleTableSlot *postgresIterateDirectModify(ForeignScanState *node);
     389              : static void postgresEndDirectModify(ForeignScanState *node);
     390              : static void postgresExplainForeignScan(ForeignScanState *node,
     391              :                                        ExplainState *es);
     392              : static void postgresExplainForeignModify(ModifyTableState *mtstate,
     393              :                                          ResultRelInfo *rinfo,
     394              :                                          List *fdw_private,
     395              :                                          int subplan_index,
     396              :                                          ExplainState *es);
     397              : static void postgresExplainDirectModify(ForeignScanState *node,
     398              :                                         ExplainState *es);
     399              : static void postgresExecForeignTruncate(List *rels,
     400              :                                         DropBehavior behavior,
     401              :                                         bool restart_seqs);
     402              : static bool postgresAnalyzeForeignTable(Relation relation,
     403              :                                         AcquireSampleRowsFunc *func,
     404              :                                         BlockNumber *totalpages);
     405              : static List *postgresImportForeignSchema(ImportForeignSchemaStmt *stmt,
     406              :                                          Oid serverOid);
     407              : static void postgresGetForeignJoinPaths(PlannerInfo *root,
     408              :                                         RelOptInfo *joinrel,
     409              :                                         RelOptInfo *outerrel,
     410              :                                         RelOptInfo *innerrel,
     411              :                                         JoinType jointype,
     412              :                                         JoinPathExtraData *extra);
     413              : static bool postgresRecheckForeignScan(ForeignScanState *node,
     414              :                                        TupleTableSlot *slot);
     415              : static void postgresGetForeignUpperPaths(PlannerInfo *root,
     416              :                                          UpperRelationKind stage,
     417              :                                          RelOptInfo *input_rel,
     418              :                                          RelOptInfo *output_rel,
     419              :                                          void *extra);
     420              : static bool postgresIsForeignPathAsyncCapable(ForeignPath *path);
     421              : static void postgresForeignAsyncRequest(AsyncRequest *areq);
     422              : static void postgresForeignAsyncConfigureWait(AsyncRequest *areq);
     423              : static void postgresForeignAsyncNotify(AsyncRequest *areq);
     424              : 
     425              : /*
     426              :  * Helper functions
     427              :  */
     428              : static void estimate_path_cost_size(PlannerInfo *root,
     429              :                                     RelOptInfo *foreignrel,
     430              :                                     List *param_join_conds,
     431              :                                     List *pathkeys,
     432              :                                     PgFdwPathExtraData *fpextra,
     433              :                                     double *p_rows, int *p_width,
     434              :                                     int *p_disabled_nodes,
     435              :                                     Cost *p_startup_cost, Cost *p_total_cost);
     436              : static void get_remote_estimate(const char *sql,
     437              :                                 PGconn *conn,
     438              :                                 double *rows,
     439              :                                 int *width,
     440              :                                 Cost *startup_cost,
     441              :                                 Cost *total_cost);
     442              : static void adjust_foreign_grouping_path_cost(PlannerInfo *root,
     443              :                                               List *pathkeys,
     444              :                                               double retrieved_rows,
     445              :                                               double width,
     446              :                                               double limit_tuples,
     447              :                                               int *p_disabled_nodes,
     448              :                                               Cost *p_startup_cost,
     449              :                                               Cost *p_run_cost);
     450              : static bool ec_member_matches_foreign(PlannerInfo *root, RelOptInfo *rel,
     451              :                                       EquivalenceClass *ec, EquivalenceMember *em,
     452              :                                       void *arg);
     453              : static void create_cursor(ForeignScanState *node);
     454              : static void fetch_more_data(ForeignScanState *node);
     455              : static void close_cursor(PGconn *conn, unsigned int cursor_number,
     456              :                          PgFdwConnState *conn_state);
     457              : static PgFdwModifyState *create_foreign_modify(EState *estate,
     458              :                                                RangeTblEntry *rte,
     459              :                                                ResultRelInfo *resultRelInfo,
     460              :                                                CmdType operation,
     461              :                                                Plan *subplan,
     462              :                                                char *query,
     463              :                                                List *target_attrs,
     464              :                                                int values_end,
     465              :                                                bool has_returning,
     466              :                                                List *retrieved_attrs);
     467              : static TupleTableSlot **execute_foreign_modify(EState *estate,
     468              :                                                ResultRelInfo *resultRelInfo,
     469              :                                                CmdType operation,
     470              :                                                TupleTableSlot **slots,
     471              :                                                TupleTableSlot **planSlots,
     472              :                                                int *numSlots);
     473              : static void prepare_foreign_modify(PgFdwModifyState *fmstate);
     474              : static const char **convert_prep_stmt_params(PgFdwModifyState *fmstate,
     475              :                                              ItemPointer tupleid,
     476              :                                              TupleTableSlot **slots,
     477              :                                              int numSlots);
     478              : static void store_returning_result(PgFdwModifyState *fmstate,
     479              :                                    TupleTableSlot *slot, PGresult *res);
     480              : static void finish_foreign_modify(PgFdwModifyState *fmstate);
     481              : static void deallocate_query(PgFdwModifyState *fmstate);
     482              : static List *build_remote_returning(Index rtindex, Relation rel,
     483              :                                     List *returningList);
     484              : static void rebuild_fdw_scan_tlist(ForeignScan *fscan, List *tlist);
     485              : static void execute_dml_stmt(ForeignScanState *node);
     486              : static TupleTableSlot *get_returning_data(ForeignScanState *node);
     487              : static void init_returning_filter(PgFdwDirectModifyState *dmstate,
     488              :                                   List *fdw_scan_tlist,
     489              :                                   Index rtindex);
     490              : static TupleTableSlot *apply_returning_filter(PgFdwDirectModifyState *dmstate,
     491              :                                               ResultRelInfo *resultRelInfo,
     492              :                                               TupleTableSlot *slot,
     493              :                                               EState *estate);
     494              : static void prepare_query_params(PlanState *node,
     495              :                                  List *fdw_exprs,
     496              :                                  int numParams,
     497              :                                  FmgrInfo **param_flinfo,
     498              :                                  List **param_exprs,
     499              :                                  const char ***param_values);
     500              : static void process_query_params(ExprContext *econtext,
     501              :                                  FmgrInfo *param_flinfo,
     502              :                                  List *param_exprs,
     503              :                                  const char **param_values);
     504              : static int  postgresAcquireSampleRowsFunc(Relation relation, int elevel,
     505              :                                           HeapTuple *rows, int targrows,
     506              :                                           double *totalrows,
     507              :                                           double *totaldeadrows);
     508              : static void analyze_row_processor(PGresult *res, int row,
     509              :                                   PgFdwAnalyzeState *astate);
     510              : static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch);
     511              : static void fetch_more_data_begin(AsyncRequest *areq);
     512              : static void complete_pending_request(AsyncRequest *areq);
     513              : static HeapTuple make_tuple_from_result_row(PGresult *res,
     514              :                                             int row,
     515              :                                             Relation rel,
     516              :                                             AttInMetadata *attinmeta,
     517              :                                             List *retrieved_attrs,
     518              :                                             ForeignScanState *fsstate,
     519              :                                             MemoryContext temp_context);
     520              : static void conversion_error_callback(void *arg);
     521              : static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel,
     522              :                             JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel,
     523              :                             JoinPathExtraData *extra);
     524              : static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
     525              :                                 Node *havingQual);
     526              : static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
     527              :                                               RelOptInfo *rel);
     528              : static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
     529              : static void add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
     530              :                                             Path *epq_path, List *restrictlist);
     531              : static void add_foreign_grouping_paths(PlannerInfo *root,
     532              :                                        RelOptInfo *input_rel,
     533              :                                        RelOptInfo *grouped_rel,
     534              :                                        GroupPathExtraData *extra);
     535              : static void add_foreign_ordered_paths(PlannerInfo *root,
     536              :                                       RelOptInfo *input_rel,
     537              :                                       RelOptInfo *ordered_rel);
     538              : static void add_foreign_final_paths(PlannerInfo *root,
     539              :                                     RelOptInfo *input_rel,
     540              :                                     RelOptInfo *final_rel,
     541              :                                     FinalPathExtraData *extra);
     542              : static void apply_server_options(PgFdwRelationInfo *fpinfo);
     543              : static void apply_table_options(PgFdwRelationInfo *fpinfo);
     544              : static void merge_fdw_options(PgFdwRelationInfo *fpinfo,
     545              :                               const PgFdwRelationInfo *fpinfo_o,
     546              :                               const PgFdwRelationInfo *fpinfo_i);
     547              : static int  get_batch_size_option(Relation rel);
     548              : 
     549              : 
     550              : /*
     551              :  * Foreign-data wrapper handler function: return a struct with pointers
     552              :  * to my callback routines.
     553              :  */
     554              : Datum
     555          698 : postgres_fdw_handler(PG_FUNCTION_ARGS)
     556              : {
     557          698 :     FdwRoutine *routine = makeNode(FdwRoutine);
     558              : 
     559              :     /* Functions for scanning foreign tables */
     560          698 :     routine->GetForeignRelSize = postgresGetForeignRelSize;
     561          698 :     routine->GetForeignPaths = postgresGetForeignPaths;
     562          698 :     routine->GetForeignPlan = postgresGetForeignPlan;
     563          698 :     routine->BeginForeignScan = postgresBeginForeignScan;
     564          698 :     routine->IterateForeignScan = postgresIterateForeignScan;
     565          698 :     routine->ReScanForeignScan = postgresReScanForeignScan;
     566          698 :     routine->EndForeignScan = postgresEndForeignScan;
     567              : 
     568              :     /* Functions for updating foreign tables */
     569          698 :     routine->AddForeignUpdateTargets = postgresAddForeignUpdateTargets;
     570          698 :     routine->PlanForeignModify = postgresPlanForeignModify;
     571          698 :     routine->BeginForeignModify = postgresBeginForeignModify;
     572          698 :     routine->ExecForeignInsert = postgresExecForeignInsert;
     573          698 :     routine->ExecForeignBatchInsert = postgresExecForeignBatchInsert;
     574          698 :     routine->GetForeignModifyBatchSize = postgresGetForeignModifyBatchSize;
     575          698 :     routine->ExecForeignUpdate = postgresExecForeignUpdate;
     576          698 :     routine->ExecForeignDelete = postgresExecForeignDelete;
     577          698 :     routine->EndForeignModify = postgresEndForeignModify;
     578          698 :     routine->BeginForeignInsert = postgresBeginForeignInsert;
     579          698 :     routine->EndForeignInsert = postgresEndForeignInsert;
     580          698 :     routine->IsForeignRelUpdatable = postgresIsForeignRelUpdatable;
     581          698 :     routine->PlanDirectModify = postgresPlanDirectModify;
     582          698 :     routine->BeginDirectModify = postgresBeginDirectModify;
     583          698 :     routine->IterateDirectModify = postgresIterateDirectModify;
     584          698 :     routine->EndDirectModify = postgresEndDirectModify;
     585              : 
     586              :     /* Function for EvalPlanQual rechecks */
     587          698 :     routine->RecheckForeignScan = postgresRecheckForeignScan;
     588              :     /* Support functions for EXPLAIN */
     589          698 :     routine->ExplainForeignScan = postgresExplainForeignScan;
     590          698 :     routine->ExplainForeignModify = postgresExplainForeignModify;
     591          698 :     routine->ExplainDirectModify = postgresExplainDirectModify;
     592              : 
     593              :     /* Support function for TRUNCATE */
     594          698 :     routine->ExecForeignTruncate = postgresExecForeignTruncate;
     595              : 
     596              :     /* Support functions for ANALYZE */
     597          698 :     routine->AnalyzeForeignTable = postgresAnalyzeForeignTable;
     598              : 
     599              :     /* Support functions for IMPORT FOREIGN SCHEMA */
     600          698 :     routine->ImportForeignSchema = postgresImportForeignSchema;
     601              : 
     602              :     /* Support functions for join push-down */
     603          698 :     routine->GetForeignJoinPaths = postgresGetForeignJoinPaths;
     604              : 
     605              :     /* Support functions for upper relation push-down */
     606          698 :     routine->GetForeignUpperPaths = postgresGetForeignUpperPaths;
     607              : 
     608              :     /* Support functions for asynchronous execution */
     609          698 :     routine->IsForeignPathAsyncCapable = postgresIsForeignPathAsyncCapable;
     610          698 :     routine->ForeignAsyncRequest = postgresForeignAsyncRequest;
     611          698 :     routine->ForeignAsyncConfigureWait = postgresForeignAsyncConfigureWait;
     612          698 :     routine->ForeignAsyncNotify = postgresForeignAsyncNotify;
     613              : 
     614          698 :     PG_RETURN_POINTER(routine);
     615              : }
     616              : 
     617              : /*
     618              :  * postgresGetForeignRelSize
     619              :  *      Estimate # of rows and width of the result of the scan
     620              :  *
     621              :  * We should consider the effect of all baserestrictinfo clauses here, but
     622              :  * not any join clauses.
     623              :  */
     624              : static void
     625         1192 : postgresGetForeignRelSize(PlannerInfo *root,
     626              :                           RelOptInfo *baserel,
     627              :                           Oid foreigntableid)
     628              : {
     629              :     PgFdwRelationInfo *fpinfo;
     630              :     ListCell   *lc;
     631              : 
     632              :     /*
     633              :      * We use PgFdwRelationInfo to pass various information to subsequent
     634              :      * functions.
     635              :      */
     636         1192 :     fpinfo = palloc0_object(PgFdwRelationInfo);
     637         1192 :     baserel->fdw_private = fpinfo;
     638              : 
     639              :     /* Base foreign tables need to be pushed down always. */
     640         1192 :     fpinfo->pushdown_safe = true;
     641              : 
     642              :     /* Look up foreign-table catalog info. */
     643         1192 :     fpinfo->table = GetForeignTable(foreigntableid);
     644         1192 :     fpinfo->server = GetForeignServer(fpinfo->table->serverid);
     645              : 
     646              :     /*
     647              :      * Extract user-settable option values.  Note that per-table settings of
     648              :      * use_remote_estimate, fetch_size and async_capable override per-server
     649              :      * settings of them, respectively.
     650              :      */
     651         1192 :     fpinfo->use_remote_estimate = false;
     652         1192 :     fpinfo->fdw_startup_cost = DEFAULT_FDW_STARTUP_COST;
     653         1192 :     fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
     654         1192 :     fpinfo->shippable_extensions = NIL;
     655         1192 :     fpinfo->fetch_size = 100;
     656         1192 :     fpinfo->async_capable = false;
     657              : 
     658         1192 :     apply_server_options(fpinfo);
     659         1192 :     apply_table_options(fpinfo);
     660              : 
     661              :     /*
     662              :      * If the table or the server is configured to use remote estimates,
     663              :      * identify which user to do remote access as during planning.  This
     664              :      * should match what ExecCheckPermissions() does.  If we fail due to lack
     665              :      * of permissions, the query would have failed at runtime anyway.
     666              :      */
     667         1192 :     if (fpinfo->use_remote_estimate)
     668              :     {
     669              :         Oid         userid;
     670              : 
     671          315 :         userid = OidIsValid(baserel->userid) ? baserel->userid : GetUserId();
     672          315 :         fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid);
     673              :     }
     674              :     else
     675          877 :         fpinfo->user = NULL;
     676              : 
     677              :     /*
     678              :      * Identify which baserestrictinfo clauses can be sent to the remote
     679              :      * server and which can't.
     680              :      */
     681         1190 :     classifyConditions(root, baserel, baserel->baserestrictinfo,
     682              :                        &fpinfo->remote_conds, &fpinfo->local_conds);
     683              : 
     684              :     /*
     685              :      * Identify which attributes will need to be retrieved from the remote
     686              :      * server.  These include all attrs needed for joins or final output, plus
     687              :      * all attrs used in the local_conds.  (Note: if we end up using a
     688              :      * parameterized scan, it's possible that some of the join clauses will be
     689              :      * sent to the remote and thus we wouldn't really need to retrieve the
     690              :      * columns used in them.  Doesn't seem worth detecting that case though.)
     691              :      */
     692         1190 :     fpinfo->attrs_used = NULL;
     693         1190 :     pull_varattnos((Node *) baserel->reltarget->exprs, baserel->relid,
     694              :                    &fpinfo->attrs_used);
     695         1265 :     foreach(lc, fpinfo->local_conds)
     696              :     {
     697           75 :         RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
     698              : 
     699           75 :         pull_varattnos((Node *) rinfo->clause, baserel->relid,
     700              :                        &fpinfo->attrs_used);
     701              :     }
     702              : 
     703              :     /*
     704              :      * Compute the selectivity and cost of the local_conds, so we don't have
     705              :      * to do it over again for each path.  The best we can do for these
     706              :      * conditions is to estimate selectivity on the basis of local statistics.
     707              :      */
     708         2380 :     fpinfo->local_conds_sel = clauselist_selectivity(root,
     709              :                                                      fpinfo->local_conds,
     710         1190 :                                                      baserel->relid,
     711              :                                                      JOIN_INNER,
     712              :                                                      NULL);
     713              : 
     714         1190 :     cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
     715              : 
     716              :     /*
     717              :      * Set # of retrieved rows and cached relation costs to some negative
     718              :      * value, so that we can detect when they are set to some sensible values,
     719              :      * during one (usually the first) of the calls to estimate_path_cost_size.
     720              :      */
     721         1190 :     fpinfo->retrieved_rows = -1;
     722         1190 :     fpinfo->rel_startup_cost = -1;
     723         1190 :     fpinfo->rel_total_cost = -1;
     724              : 
     725              :     /*
     726              :      * If the table or the server is configured to use remote estimates,
     727              :      * connect to the foreign server and execute EXPLAIN to estimate the
     728              :      * number of rows selected by the restriction clauses, as well as the
     729              :      * average row width.  Otherwise, estimate using whatever statistics we
     730              :      * have locally, in a way similar to ordinary tables.
     731              :      */
     732         1190 :     if (fpinfo->use_remote_estimate)
     733              :     {
     734              :         /*
     735              :          * Get cost/size estimates with help of remote server.  Save the
     736              :          * values in fpinfo so we don't need to do it again to generate the
     737              :          * basic foreign path.
     738              :          */
     739          313 :         estimate_path_cost_size(root, baserel, NIL, NIL, NULL,
     740              :                                 &fpinfo->rows, &fpinfo->width,
     741              :                                 &fpinfo->disabled_nodes,
     742              :                                 &fpinfo->startup_cost, &fpinfo->total_cost);
     743              : 
     744              :         /* Report estimated baserel size to planner. */
     745          313 :         baserel->rows = fpinfo->rows;
     746          313 :         baserel->reltarget->width = fpinfo->width;
     747              :     }
     748              :     else
     749              :     {
     750              :         /*
     751              :          * If the foreign table has never been ANALYZEd, it will have
     752              :          * reltuples < 0, meaning "unknown".  We can't do much if we're not
     753              :          * allowed to consult the remote server, but we can use a hack similar
     754              :          * to plancat.c's treatment of empty relations: use a minimum size
     755              :          * estimate of 10 pages, and divide by the column-datatype-based width
     756              :          * estimate to get the corresponding number of tuples.
     757              :          */
     758          877 :         if (baserel->tuples < 0)
     759              :         {
     760          281 :             baserel->pages = 10;
     761          281 :             baserel->tuples =
     762          281 :                 (10 * BLCKSZ) / (baserel->reltarget->width +
     763              :                                  MAXALIGN(SizeofHeapTupleHeader));
     764              :         }
     765              : 
     766              :         /* Estimate baserel size as best we can with local statistics. */
     767          877 :         set_baserel_size_estimates(root, baserel);
     768              : 
     769              :         /* Fill in basically-bogus cost estimates for use later. */
     770          877 :         estimate_path_cost_size(root, baserel, NIL, NIL, NULL,
     771              :                                 &fpinfo->rows, &fpinfo->width,
     772              :                                 &fpinfo->disabled_nodes,
     773              :                                 &fpinfo->startup_cost, &fpinfo->total_cost);
     774              :     }
     775              : 
     776              :     /*
     777              :      * fpinfo->relation_name gets the numeric rangetable index of the foreign
     778              :      * table RTE.  (If this query gets EXPLAIN'd, we'll convert that to a
     779              :      * human-readable string at that time.)
     780              :      */
     781         1190 :     fpinfo->relation_name = psprintf("%u", baserel->relid);
     782              : 
     783              :     /* No outer and inner relations. */
     784         1190 :     fpinfo->make_outerrel_subquery = false;
     785         1190 :     fpinfo->make_innerrel_subquery = false;
     786         1190 :     fpinfo->lower_subquery_rels = NULL;
     787         1190 :     fpinfo->hidden_subquery_rels = NULL;
     788              :     /* Set the relation index. */
     789         1190 :     fpinfo->relation_index = baserel->relid;
     790         1190 : }
     791              : 
     792              : /*
     793              :  * get_useful_ecs_for_relation
     794              :  *      Determine which EquivalenceClasses might be involved in useful
     795              :  *      orderings of this relation.
     796              :  *
     797              :  * This function is in some respects a mirror image of the core function
     798              :  * pathkeys_useful_for_merging: for a regular table, we know what indexes
     799              :  * we have and want to test whether any of them are useful.  For a foreign
     800              :  * table, we don't know what indexes are present on the remote side but
     801              :  * want to speculate about which ones we'd like to use if they existed.
     802              :  *
     803              :  * This function returns a list of potentially-useful equivalence classes,
     804              :  * but it does not guarantee that an EquivalenceMember exists which contains
     805              :  * Vars only from the given relation.  For example, given ft1 JOIN t1 ON
     806              :  * ft1.x + t1.x = 0, this function will say that the equivalence class
     807              :  * containing ft1.x + t1.x is potentially useful.  Supposing ft1 is remote and
     808              :  * t1 is local (or on a different server), it will turn out that no useful
     809              :  * ORDER BY clause can be generated.  It's not our job to figure that out
     810              :  * here; we're only interested in identifying relevant ECs.
     811              :  */
     812              : static List *
     813          538 : get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel)
     814              : {
     815          538 :     List       *useful_eclass_list = NIL;
     816              :     ListCell   *lc;
     817              :     Relids      relids;
     818              : 
     819              :     /*
     820              :      * First, consider whether any active EC is potentially useful for a merge
     821              :      * join against this relation.
     822              :      */
     823          538 :     if (rel->has_eclass_joins)
     824              :     {
     825          700 :         foreach(lc, root->eq_classes)
     826              :         {
     827          479 :             EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc);
     828              : 
     829          479 :             if (eclass_useful_for_merging(root, cur_ec, rel))
     830          255 :                 useful_eclass_list = lappend(useful_eclass_list, cur_ec);
     831              :         }
     832              :     }
     833              : 
     834              :     /*
     835              :      * Next, consider whether there are any non-EC derivable join clauses that
     836              :      * are merge-joinable.  If the joininfo list is empty, we can exit
     837              :      * quickly.
     838              :      */
     839          538 :     if (rel->joininfo == NIL)
     840          396 :         return useful_eclass_list;
     841              : 
     842              :     /* If this is a child rel, we must use the topmost parent rel to search. */
     843          142 :     if (IS_OTHER_REL(rel))
     844              :     {
     845              :         Assert(!bms_is_empty(rel->top_parent_relids));
     846           20 :         relids = rel->top_parent_relids;
     847              :     }
     848              :     else
     849          122 :         relids = rel->relids;
     850              : 
     851              :     /* Check each join clause in turn. */
     852          345 :     foreach(lc, rel->joininfo)
     853              :     {
     854          203 :         RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
     855              : 
     856              :         /* Consider only mergejoinable clauses */
     857          203 :         if (restrictinfo->mergeopfamilies == NIL)
     858           14 :             continue;
     859              : 
     860              :         /* Make sure we've got canonical ECs. */
     861          189 :         update_mergeclause_eclasses(root, restrictinfo);
     862              : 
     863              :         /*
     864              :          * restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
     865              :          * that left_ec and right_ec will be initialized, per comments in
     866              :          * distribute_qual_to_rels.
     867              :          *
     868              :          * We want to identify which side of this merge-joinable clause
     869              :          * contains columns from the relation produced by this RelOptInfo. We
     870              :          * test for overlap, not containment, because there could be extra
     871              :          * relations on either side.  For example, suppose we've got something
     872              :          * like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
     873              :          * A.y = D.y.  The input rel might be the joinrel between A and B, and
     874              :          * we'll consider the join clause A.y = D.y. relids contains a
     875              :          * relation not involved in the join class (B) and the equivalence
     876              :          * class for the left-hand side of the clause contains a relation not
     877              :          * involved in the input rel (C).  Despite the fact that we have only
     878              :          * overlap and not containment in either direction, A.y is potentially
     879              :          * useful as a sort column.
     880              :          *
     881              :          * Note that it's even possible that relids overlaps neither side of
     882              :          * the join clause.  For example, consider A LEFT JOIN B ON A.x = B.x
     883              :          * AND A.x = 1.  The clause A.x = 1 will appear in B's joininfo list,
     884              :          * but overlaps neither side of B.  In that case, we just skip this
     885              :          * join clause, since it doesn't suggest a useful sort order for this
     886              :          * relation.
     887              :          */
     888          189 :         if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
     889           86 :             useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
     890           86 :                                                         restrictinfo->right_ec);
     891          103 :         else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
     892           94 :             useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
     893           94 :                                                         restrictinfo->left_ec);
     894              :     }
     895              : 
     896          142 :     return useful_eclass_list;
     897              : }
     898              : 
     899              : /*
     900              :  * get_useful_pathkeys_for_relation
     901              :  *      Determine which orderings of a relation might be useful.
     902              :  *
     903              :  * Getting data in sorted order can be useful either because the requested
     904              :  * order matches the final output ordering for the overall query we're
     905              :  * planning, or because it enables an efficient merge join.  Here, we try
     906              :  * to figure out which pathkeys to consider.
     907              :  */
     908              : static List *
     909         1527 : get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
     910              : {
     911         1527 :     List       *useful_pathkeys_list = NIL;
     912              :     List       *useful_eclass_list;
     913         1527 :     PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
     914         1527 :     EquivalenceClass *query_ec = NULL;
     915              :     ListCell   *lc;
     916              : 
     917              :     /*
     918              :      * Pushing the query_pathkeys to the remote server is always worth
     919              :      * considering, because it might let us avoid a local sort.
     920              :      */
     921         1527 :     fpinfo->qp_is_pushdown_safe = false;
     922         1527 :     if (root->query_pathkeys)
     923              :     {
     924          606 :         bool        query_pathkeys_ok = true;
     925              : 
     926         1144 :         foreach(lc, root->query_pathkeys)
     927              :         {
     928          775 :             PathKey    *pathkey = (PathKey *) lfirst(lc);
     929              : 
     930              :             /*
     931              :              * The planner and executor don't have any clever strategy for
     932              :              * taking data sorted by a prefix of the query's pathkeys and
     933              :              * getting it to be sorted by all of those pathkeys. We'll just
     934              :              * end up resorting the entire data set.  So, unless we can push
     935              :              * down all of the query pathkeys, forget it.
     936              :              */
     937          775 :             if (!is_foreign_pathkey(root, rel, pathkey))
     938              :             {
     939          237 :                 query_pathkeys_ok = false;
     940          237 :                 break;
     941              :             }
     942              :         }
     943              : 
     944          606 :         if (query_pathkeys_ok)
     945              :         {
     946          369 :             useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
     947          369 :             fpinfo->qp_is_pushdown_safe = true;
     948              :         }
     949              :     }
     950              : 
     951              :     /*
     952              :      * Even if we're not using remote estimates, having the remote side do the
     953              :      * sort generally won't be any worse than doing it locally, and it might
     954              :      * be much better if the remote side can generate data in the right order
     955              :      * without needing a sort at all.  However, what we're going to do next is
     956              :      * try to generate pathkeys that seem promising for possible merge joins,
     957              :      * and that's more speculative.  A wrong choice might hurt quite a bit, so
     958              :      * bail out if we can't use remote estimates.
     959              :      */
     960         1527 :     if (!fpinfo->use_remote_estimate)
     961          989 :         return useful_pathkeys_list;
     962              : 
     963              :     /* Get the list of interesting EquivalenceClasses. */
     964          538 :     useful_eclass_list = get_useful_ecs_for_relation(root, rel);
     965              : 
     966              :     /* Extract unique EC for query, if any, so we don't consider it again. */
     967          538 :     if (list_length(root->query_pathkeys) == 1)
     968              :     {
     969          177 :         PathKey    *query_pathkey = linitial(root->query_pathkeys);
     970              : 
     971          177 :         query_ec = query_pathkey->pk_eclass;
     972              :     }
     973              : 
     974              :     /*
     975              :      * As a heuristic, the only pathkeys we consider here are those of length
     976              :      * one.  It's surely possible to consider more, but since each one we
     977              :      * choose to consider will generate a round-trip to the remote side, we
     978              :      * need to be a bit cautious here.  It would sure be nice to have a local
     979              :      * cache of information about remote index definitions...
     980              :      */
     981          946 :     foreach(lc, useful_eclass_list)
     982              :     {
     983          408 :         EquivalenceClass *cur_ec = lfirst(lc);
     984              :         PathKey    *pathkey;
     985              : 
     986              :         /* If redundant with what we did above, skip it. */
     987          408 :         if (cur_ec == query_ec)
     988           81 :             continue;
     989              : 
     990              :         /* Can't push down the sort if the EC's opfamily is not shippable. */
     991          377 :         if (!is_shippable(linitial_oid(cur_ec->ec_opfamilies),
     992              :                           OperatorFamilyRelationId, fpinfo))
     993            0 :             continue;
     994              : 
     995              :         /* If no pushable expression for this rel, skip it. */
     996          377 :         if (find_em_for_rel(root, cur_ec, rel) == NULL)
     997           50 :             continue;
     998              : 
     999              :         /* Looks like we can generate a pathkey, so let's do it. */
    1000          327 :         pathkey = make_canonical_pathkey(root, cur_ec,
    1001          327 :                                          linitial_oid(cur_ec->ec_opfamilies),
    1002              :                                          COMPARE_LT,
    1003              :                                          false);
    1004          327 :         useful_pathkeys_list = lappend(useful_pathkeys_list,
    1005          327 :                                        list_make1(pathkey));
    1006              :     }
    1007              : 
    1008          538 :     return useful_pathkeys_list;
    1009              : }
    1010              : 
    1011              : /*
    1012              :  * postgresGetForeignPaths
    1013              :  *      Create possible scan paths for a scan on the foreign table
    1014              :  */
    1015              : static void
    1016         1190 : postgresGetForeignPaths(PlannerInfo *root,
    1017              :                         RelOptInfo *baserel,
    1018              :                         Oid foreigntableid)
    1019              : {
    1020         1190 :     PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) baserel->fdw_private;
    1021              :     ForeignPath *path;
    1022              :     List       *ppi_list;
    1023              :     ListCell   *lc;
    1024              : 
    1025              :     /*
    1026              :      * Create simplest ForeignScan path node and add it to baserel.  This path
    1027              :      * corresponds to SeqScan path of regular tables (though depending on what
    1028              :      * baserestrict conditions we were able to send to remote, there might
    1029              :      * actually be an indexscan happening there).  We already did all the work
    1030              :      * to estimate cost and size of this path.
    1031              :      *
    1032              :      * Although this path uses no join clauses, it could still have required
    1033              :      * parameterization due to LATERAL refs in its tlist.
    1034              :      */
    1035         1190 :     path = create_foreignscan_path(root, baserel,
    1036              :                                    NULL,    /* default pathtarget */
    1037              :                                    fpinfo->rows,
    1038              :                                    fpinfo->disabled_nodes,
    1039              :                                    fpinfo->startup_cost,
    1040              :                                    fpinfo->total_cost,
    1041              :                                    NIL, /* no pathkeys */
    1042              :                                    baserel->lateral_relids,
    1043              :                                    NULL,    /* no extra plan */
    1044              :                                    NIL, /* no fdw_restrictinfo list */
    1045              :                                    NIL);    /* no fdw_private list */
    1046         1190 :     add_path(baserel, (Path *) path);
    1047              : 
    1048              :     /* Add paths with pathkeys */
    1049         1190 :     add_paths_with_pathkeys_for_rel(root, baserel, NULL, NIL);
    1050              : 
    1051              :     /*
    1052              :      * If we're not using remote estimates, stop here.  We have no way to
    1053              :      * estimate whether any join clauses would be worth sending across, so
    1054              :      * don't bother building parameterized paths.
    1055              :      */
    1056         1190 :     if (!fpinfo->use_remote_estimate)
    1057          877 :         return;
    1058              : 
    1059              :     /*
    1060              :      * Thumb through all join clauses for the rel to identify which outer
    1061              :      * relations could supply one or more safe-to-send-to-remote join clauses.
    1062              :      * We'll build a parameterized path for each such outer relation.
    1063              :      *
    1064              :      * It's convenient to manage this by representing each candidate outer
    1065              :      * relation by the ParamPathInfo node for it.  We can then use the
    1066              :      * ppi_clauses list in the ParamPathInfo node directly as a list of the
    1067              :      * interesting join clauses for that rel.  This takes care of the
    1068              :      * possibility that there are multiple safe join clauses for such a rel,
    1069              :      * and also ensures that we account for unsafe join clauses that we'll
    1070              :      * still have to enforce locally (since the parameterized-path machinery
    1071              :      * insists that we handle all movable clauses).
    1072              :      */
    1073          313 :     ppi_list = NIL;
    1074          454 :     foreach(lc, baserel->joininfo)
    1075              :     {
    1076          141 :         RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
    1077              :         Relids      required_outer;
    1078              :         ParamPathInfo *param_info;
    1079              : 
    1080              :         /* Check if clause can be moved to this rel */
    1081          141 :         if (!join_clause_is_movable_to(rinfo, baserel))
    1082           96 :             continue;
    1083              : 
    1084              :         /* See if it is safe to send to remote */
    1085           45 :         if (!is_foreign_expr(root, baserel, rinfo->clause))
    1086            7 :             continue;
    1087              : 
    1088              :         /* Calculate required outer rels for the resulting path */
    1089           38 :         required_outer = bms_union(rinfo->clause_relids,
    1090           38 :                                    baserel->lateral_relids);
    1091              :         /* We do not want the foreign rel itself listed in required_outer */
    1092           38 :         required_outer = bms_del_member(required_outer, baserel->relid);
    1093              : 
    1094              :         /*
    1095              :          * required_outer probably can't be empty here, but if it were, we
    1096              :          * couldn't make a parameterized path.
    1097              :          */
    1098           38 :         if (bms_is_empty(required_outer))
    1099            0 :             continue;
    1100              : 
    1101              :         /* Get the ParamPathInfo */
    1102           38 :         param_info = get_baserel_parampathinfo(root, baserel,
    1103              :                                                required_outer);
    1104              :         Assert(param_info != NULL);
    1105              : 
    1106              :         /*
    1107              :          * Add it to list unless we already have it.  Testing pointer equality
    1108              :          * is OK since get_baserel_parampathinfo won't make duplicates.
    1109              :          */
    1110           38 :         ppi_list = list_append_unique_ptr(ppi_list, param_info);
    1111              :     }
    1112              : 
    1113              :     /*
    1114              :      * The above scan examined only "generic" join clauses, not those that
    1115              :      * were absorbed into EquivalenceClauses.  See if we can make anything out
    1116              :      * of EquivalenceClauses.
    1117              :      */
    1118          313 :     if (baserel->has_eclass_joins)
    1119              :     {
    1120              :         /*
    1121              :          * We repeatedly scan the eclass list looking for column references
    1122              :          * (or expressions) belonging to the foreign rel.  Each time we find
    1123              :          * one, we generate a list of equivalence joinclauses for it, and then
    1124              :          * see if any are safe to send to the remote.  Repeat till there are
    1125              :          * no more candidate EC members.
    1126              :          */
    1127              :         ec_member_foreign_arg arg;
    1128              : 
    1129          145 :         arg.already_used = NIL;
    1130              :         for (;;)
    1131          147 :         {
    1132              :             List       *clauses;
    1133              : 
    1134              :             /* Make clauses, skipping any that join to lateral_referencers */
    1135          292 :             arg.current = NULL;
    1136          292 :             clauses = generate_implied_equalities_for_column(root,
    1137              :                                                              baserel,
    1138              :                                                              ec_member_matches_foreign,
    1139              :                                                              &arg,
    1140              :                                                              baserel->lateral_referencers);
    1141              : 
    1142              :             /* Done if there are no more expressions in the foreign rel */
    1143          292 :             if (arg.current == NULL)
    1144              :             {
    1145              :                 Assert(clauses == NIL);
    1146          145 :                 break;
    1147              :             }
    1148              : 
    1149              :             /* Scan the extracted join clauses */
    1150          330 :             foreach(lc, clauses)
    1151              :             {
    1152          183 :                 RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
    1153              :                 Relids      required_outer;
    1154              :                 ParamPathInfo *param_info;
    1155              : 
    1156              :                 /* Check if clause can be moved to this rel */
    1157          183 :                 if (!join_clause_is_movable_to(rinfo, baserel))
    1158            0 :                     continue;
    1159              : 
    1160              :                 /* See if it is safe to send to remote */
    1161          183 :                 if (!is_foreign_expr(root, baserel, rinfo->clause))
    1162            7 :                     continue;
    1163              : 
    1164              :                 /* Calculate required outer rels for the resulting path */
    1165          176 :                 required_outer = bms_union(rinfo->clause_relids,
    1166          176 :                                            baserel->lateral_relids);
    1167          176 :                 required_outer = bms_del_member(required_outer, baserel->relid);
    1168          176 :                 if (bms_is_empty(required_outer))
    1169            0 :                     continue;
    1170              : 
    1171              :                 /* Get the ParamPathInfo */
    1172          176 :                 param_info = get_baserel_parampathinfo(root, baserel,
    1173              :                                                        required_outer);
    1174              :                 Assert(param_info != NULL);
    1175              : 
    1176              :                 /* Add it to list unless we already have it */
    1177          176 :                 ppi_list = list_append_unique_ptr(ppi_list, param_info);
    1178              :             }
    1179              : 
    1180              :             /* Try again, now ignoring the expression we found this time */
    1181          147 :             arg.already_used = lappend(arg.already_used, arg.current);
    1182              :         }
    1183              :     }
    1184              : 
    1185              :     /*
    1186              :      * Now build a path for each useful outer relation.
    1187              :      */
    1188          517 :     foreach(lc, ppi_list)
    1189              :     {
    1190          204 :         ParamPathInfo *param_info = (ParamPathInfo *) lfirst(lc);
    1191              :         double      rows;
    1192              :         int         width;
    1193              :         int         disabled_nodes;
    1194              :         Cost        startup_cost;
    1195              :         Cost        total_cost;
    1196              : 
    1197              :         /* Get a cost estimate from the remote */
    1198          204 :         estimate_path_cost_size(root, baserel,
    1199              :                                 param_info->ppi_clauses, NIL, NULL,
    1200              :                                 &rows, &width, &disabled_nodes,
    1201              :                                 &startup_cost, &total_cost);
    1202              : 
    1203              :         /*
    1204              :          * ppi_rows currently won't get looked at by anything, but still we
    1205              :          * may as well ensure that it matches our idea of the rowcount.
    1206              :          */
    1207          204 :         param_info->ppi_rows = rows;
    1208              : 
    1209              :         /* Make the path */
    1210          204 :         path = create_foreignscan_path(root, baserel,
    1211              :                                        NULL,    /* default pathtarget */
    1212              :                                        rows,
    1213              :                                        disabled_nodes,
    1214              :                                        startup_cost,
    1215              :                                        total_cost,
    1216              :                                        NIL, /* no pathkeys */
    1217              :                                        param_info->ppi_req_outer,
    1218              :                                        NULL,
    1219              :                                        NIL, /* no fdw_restrictinfo list */
    1220              :                                        NIL);    /* no fdw_private list */
    1221          204 :         add_path(baserel, (Path *) path);
    1222              :     }
    1223              : }
    1224              : 
    1225              : /*
    1226              :  * postgresGetForeignPlan
    1227              :  *      Create ForeignScan plan node which implements selected best path
    1228              :  */
    1229              : static ForeignScan *
    1230         1004 : postgresGetForeignPlan(PlannerInfo *root,
    1231              :                        RelOptInfo *foreignrel,
    1232              :                        Oid foreigntableid,
    1233              :                        ForeignPath *best_path,
    1234              :                        List *tlist,
    1235              :                        List *scan_clauses,
    1236              :                        Plan *outer_plan)
    1237              : {
    1238         1004 :     PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    1239              :     Index       scan_relid;
    1240              :     List       *fdw_private;
    1241         1004 :     List       *remote_exprs = NIL;
    1242         1004 :     List       *local_exprs = NIL;
    1243         1004 :     List       *params_list = NIL;
    1244         1004 :     List       *fdw_scan_tlist = NIL;
    1245         1004 :     List       *fdw_recheck_quals = NIL;
    1246              :     List       *retrieved_attrs;
    1247              :     StringInfoData sql;
    1248         1004 :     bool        has_final_sort = false;
    1249         1004 :     bool        has_limit = false;
    1250              :     ListCell   *lc;
    1251              : 
    1252              :     /*
    1253              :      * Get FDW private data created by postgresGetForeignUpperPaths(), if any.
    1254              :      */
    1255         1004 :     if (best_path->fdw_private)
    1256              :     {
    1257          151 :         has_final_sort = boolVal(list_nth(best_path->fdw_private,
    1258              :                                           FdwPathPrivateHasFinalSort));
    1259          151 :         has_limit = boolVal(list_nth(best_path->fdw_private,
    1260              :                                      FdwPathPrivateHasLimit));
    1261              :     }
    1262              : 
    1263         1004 :     if (IS_SIMPLE_REL(foreignrel))
    1264              :     {
    1265              :         /*
    1266              :          * For base relations, set scan_relid as the relid of the relation.
    1267              :          */
    1268          718 :         scan_relid = foreignrel->relid;
    1269              : 
    1270              :         /*
    1271              :          * In a base-relation scan, we must apply the given scan_clauses.
    1272              :          *
    1273              :          * Separate the scan_clauses into those that can be executed remotely
    1274              :          * and those that can't.  baserestrictinfo clauses that were
    1275              :          * previously determined to be safe or unsafe by classifyConditions
    1276              :          * are found in fpinfo->remote_conds and fpinfo->local_conds. Anything
    1277              :          * else in the scan_clauses list will be a join clause, which we have
    1278              :          * to check for remote-safety.
    1279              :          *
    1280              :          * Note: the join clauses we see here should be the exact same ones
    1281              :          * previously examined by postgresGetForeignPaths.  Possibly it'd be
    1282              :          * worth passing forward the classification work done then, rather
    1283              :          * than repeating it here.
    1284              :          *
    1285              :          * This code must match "extract_actual_clauses(scan_clauses, false)"
    1286              :          * except for the additional decision about remote versus local
    1287              :          * execution.
    1288              :          */
    1289         1079 :         foreach(lc, scan_clauses)
    1290              :         {
    1291          361 :             RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
    1292              : 
    1293              :             /* Ignore any pseudoconstants, they're dealt with elsewhere */
    1294          361 :             if (rinfo->pseudoconstant)
    1295            4 :                 continue;
    1296              : 
    1297          357 :             if (list_member_ptr(fpinfo->remote_conds, rinfo))
    1298          271 :                 remote_exprs = lappend(remote_exprs, rinfo->clause);
    1299           86 :             else if (list_member_ptr(fpinfo->local_conds, rinfo))
    1300           71 :                 local_exprs = lappend(local_exprs, rinfo->clause);
    1301           15 :             else if (is_foreign_expr(root, foreignrel, rinfo->clause))
    1302           13 :                 remote_exprs = lappend(remote_exprs, rinfo->clause);
    1303              :             else
    1304            2 :                 local_exprs = lappend(local_exprs, rinfo->clause);
    1305              :         }
    1306              : 
    1307              :         /*
    1308              :          * For a base-relation scan, we have to support EPQ recheck, which
    1309              :          * should recheck all the remote quals.
    1310              :          */
    1311          718 :         fdw_recheck_quals = remote_exprs;
    1312              :     }
    1313              :     else
    1314              :     {
    1315              :         /*
    1316              :          * Join relation or upper relation - set scan_relid to 0.
    1317              :          */
    1318          286 :         scan_relid = 0;
    1319              : 
    1320              :         /*
    1321              :          * For a join rel, baserestrictinfo is NIL and we are not considering
    1322              :          * parameterization right now, so there should be no scan_clauses for
    1323              :          * a joinrel or an upper rel either.
    1324              :          */
    1325              :         Assert(!scan_clauses);
    1326              : 
    1327              :         /*
    1328              :          * Instead we get the conditions to apply from the fdw_private
    1329              :          * structure.
    1330              :          */
    1331          286 :         remote_exprs = extract_actual_clauses(fpinfo->remote_conds, false);
    1332          286 :         local_exprs = extract_actual_clauses(fpinfo->local_conds, false);
    1333              : 
    1334              :         /*
    1335              :          * We leave fdw_recheck_quals empty in this case, since we never need
    1336              :          * to apply EPQ recheck clauses.  In the case of a joinrel, EPQ
    1337              :          * recheck is handled elsewhere --- see postgresGetForeignJoinPaths().
    1338              :          * If we're planning an upperrel (ie, remote grouping or aggregation)
    1339              :          * then there's no EPQ to do because SELECT FOR UPDATE wouldn't be
    1340              :          * allowed, and indeed we *can't* put the remote clauses into
    1341              :          * fdw_recheck_quals because the unaggregated Vars won't be available
    1342              :          * locally.
    1343              :          */
    1344              : 
    1345              :         /* Build the list of columns to be fetched from the foreign server. */
    1346          286 :         fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
    1347              : 
    1348              :         /*
    1349              :          * Ensure that the outer plan produces a tuple whose descriptor
    1350              :          * matches our scan tuple slot.  Also, remove the local conditions
    1351              :          * from outer plan's quals, lest they be evaluated twice, once by the
    1352              :          * local plan and once by the scan.
    1353              :          */
    1354          286 :         if (outer_plan)
    1355              :         {
    1356              :             /*
    1357              :              * Right now, we only consider grouping and aggregation beyond
    1358              :              * joins. Queries involving aggregates or grouping do not require
    1359              :              * EPQ mechanism, hence should not have an outer plan here.
    1360              :              */
    1361              :             Assert(!IS_UPPER_REL(foreignrel));
    1362              : 
    1363              :             /*
    1364              :              * First, update the plan's qual list if possible.  In some cases
    1365              :              * the quals might be enforced below the topmost plan level, in
    1366              :              * which case we'll fail to remove them; it's not worth working
    1367              :              * harder than this.
    1368              :              */
    1369           29 :             foreach(lc, local_exprs)
    1370              :             {
    1371            3 :                 Node       *qual = lfirst(lc);
    1372              : 
    1373            3 :                 outer_plan->qual = list_delete(outer_plan->qual, qual);
    1374              : 
    1375              :                 /*
    1376              :                  * For an inner join the local conditions of foreign scan plan
    1377              :                  * can be part of the joinquals as well.  (They might also be
    1378              :                  * in the mergequals or hashquals, but we can't touch those
    1379              :                  * without breaking the plan.)
    1380              :                  */
    1381            3 :                 if (IsA(outer_plan, NestLoop) ||
    1382            1 :                     IsA(outer_plan, MergeJoin) ||
    1383            1 :                     IsA(outer_plan, HashJoin))
    1384              :                 {
    1385            2 :                     Join       *join_plan = (Join *) outer_plan;
    1386              : 
    1387            2 :                     if (join_plan->jointype == JOIN_INNER)
    1388            2 :                         join_plan->joinqual = list_delete(join_plan->joinqual,
    1389              :                                                           qual);
    1390              :                 }
    1391              :             }
    1392              : 
    1393              :             /*
    1394              :              * Now fix the subplan's tlist --- this might result in inserting
    1395              :              * a Result node atop the plan tree.
    1396              :              */
    1397           26 :             outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist,
    1398           26 :                                                 best_path->path.parallel_safe);
    1399              :         }
    1400              :     }
    1401              : 
    1402              :     /*
    1403              :      * Build the query string to be sent for execution, and identify
    1404              :      * expressions to be sent as parameters.
    1405              :      */
    1406         1004 :     initStringInfo(&sql);
    1407         1004 :     deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
    1408              :                             remote_exprs, best_path->path.pathkeys,
    1409              :                             has_final_sort, has_limit, false,
    1410              :                             &retrieved_attrs, &params_list);
    1411              : 
    1412              :     /* Remember remote_exprs for possible use by postgresPlanDirectModify */
    1413         1004 :     fpinfo->final_remote_exprs = remote_exprs;
    1414              : 
    1415              :     /*
    1416              :      * Build the fdw_private list that will be available to the executor.
    1417              :      * Items in the list must match order in enum FdwScanPrivateIndex.
    1418              :      */
    1419         1004 :     fdw_private = list_make3(makeString(sql.data),
    1420              :                              retrieved_attrs,
    1421              :                              makeInteger(fpinfo->fetch_size));
    1422         1004 :     if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
    1423          286 :         fdw_private = lappend(fdw_private,
    1424          286 :                               makeString(fpinfo->relation_name));
    1425              : 
    1426              :     /*
    1427              :      * Create the ForeignScan node for the given relation.
    1428              :      *
    1429              :      * Note that the remote parameter expressions are stored in the fdw_exprs
    1430              :      * field of the finished plan node; we can't keep them in private state
    1431              :      * because then they wouldn't be subject to later planner processing.
    1432              :      */
    1433         1004 :     return make_foreignscan(tlist,
    1434              :                             local_exprs,
    1435              :                             scan_relid,
    1436              :                             params_list,
    1437              :                             fdw_private,
    1438              :                             fdw_scan_tlist,
    1439              :                             fdw_recheck_quals,
    1440              :                             outer_plan);
    1441              : }
    1442              : 
    1443              : /*
    1444              :  * Construct a tuple descriptor for the scan tuples handled by a foreign join.
    1445              :  */
    1446              : static TupleDesc
    1447          164 : get_tupdesc_for_join_scan_tuples(ForeignScanState *node)
    1448              : {
    1449          164 :     ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
    1450          164 :     EState     *estate = node->ss.ps.state;
    1451              :     TupleDesc   tupdesc;
    1452              : 
    1453              :     /*
    1454              :      * The core code has already set up a scan tuple slot based on
    1455              :      * fsplan->fdw_scan_tlist, and this slot's tupdesc is mostly good enough,
    1456              :      * but there's one case where it isn't.  If we have any whole-row row
    1457              :      * identifier Vars, they may have vartype RECORD, and we need to replace
    1458              :      * that with the associated table's actual composite type.  This ensures
    1459              :      * that when we read those ROW() expression values from the remote server,
    1460              :      * we can convert them to a composite type the local server knows.
    1461              :      */
    1462          164 :     tupdesc = CreateTupleDescCopy(node->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
    1463          685 :     for (int i = 0; i < tupdesc->natts; i++)
    1464              :     {
    1465          521 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
    1466              :         Var        *var;
    1467              :         RangeTblEntry *rte;
    1468              :         Oid         reltype;
    1469              : 
    1470              :         /* Nothing to do if it's not a generic RECORD attribute */
    1471          521 :         if (att->atttypid != RECORDOID || att->atttypmod >= 0)
    1472          518 :             continue;
    1473              : 
    1474              :         /*
    1475              :          * If we can't identify the referenced table, do nothing.  This'll
    1476              :          * likely lead to failure later, but perhaps we can muddle through.
    1477              :          */
    1478            3 :         var = (Var *) list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
    1479              :                                     i)->expr;
    1480            3 :         if (!IsA(var, Var) || var->varattno != 0)
    1481            0 :             continue;
    1482            3 :         rte = list_nth(estate->es_range_table, var->varno - 1);
    1483            3 :         if (rte->rtekind != RTE_RELATION)
    1484            0 :             continue;
    1485            3 :         reltype = get_rel_type_id(rte->relid);
    1486            3 :         if (!OidIsValid(reltype))
    1487            0 :             continue;
    1488            3 :         att->atttypid = reltype;
    1489              :         /* shouldn't need to change anything else */
    1490              :     }
    1491          164 :     return tupdesc;
    1492              : }
    1493              : 
    1494              : /*
    1495              :  * postgresBeginForeignScan
    1496              :  *      Initiate an executor scan of a foreign PostgreSQL table.
    1497              :  */
    1498              : static void
    1499          898 : postgresBeginForeignScan(ForeignScanState *node, int eflags)
    1500              : {
    1501          898 :     ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
    1502          898 :     EState     *estate = node->ss.ps.state;
    1503              :     PgFdwScanState *fsstate;
    1504              :     RangeTblEntry *rte;
    1505              :     Oid         userid;
    1506              :     ForeignTable *table;
    1507              :     UserMapping *user;
    1508              :     int         rtindex;
    1509              :     int         numParams;
    1510              : 
    1511              :     /*
    1512              :      * Do nothing in EXPLAIN (no ANALYZE) case.  node->fdw_state stays NULL.
    1513              :      */
    1514          898 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
    1515          386 :         return;
    1516              : 
    1517              :     /*
    1518              :      * We'll save private state in node->fdw_state.
    1519              :      */
    1520          512 :     fsstate = palloc0_object(PgFdwScanState);
    1521          512 :     node->fdw_state = fsstate;
    1522              : 
    1523              :     /*
    1524              :      * Identify which user to do the remote access as.  This should match what
    1525              :      * ExecCheckPermissions() does.
    1526              :      */
    1527          512 :     userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
    1528          512 :     if (fsplan->scan.scanrelid > 0)
    1529          348 :         rtindex = fsplan->scan.scanrelid;
    1530              :     else
    1531          164 :         rtindex = bms_next_member(fsplan->fs_base_relids, -1);
    1532          512 :     rte = exec_rt_fetch(rtindex, estate);
    1533              : 
    1534              :     /* Get info about foreign table. */
    1535          512 :     table = GetForeignTable(rte->relid);
    1536          512 :     user = GetUserMapping(userid, table->serverid);
    1537              : 
    1538              :     /*
    1539              :      * Get connection to the foreign server.  Connection manager will
    1540              :      * establish new connection if necessary.
    1541              :      */
    1542          512 :     fsstate->conn = GetConnection(user, false, &fsstate->conn_state);
    1543              : 
    1544              :     /* Assign a unique ID for my cursor */
    1545          504 :     fsstate->cursor_number = GetCursorNumber(fsstate->conn);
    1546          504 :     fsstate->cursor_exists = false;
    1547              : 
    1548              :     /* Get private info created by planner functions. */
    1549          504 :     fsstate->query = strVal(list_nth(fsplan->fdw_private,
    1550              :                                      FdwScanPrivateSelectSql));
    1551          504 :     fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
    1552              :                                                  FdwScanPrivateRetrievedAttrs);
    1553          504 :     fsstate->fetch_size = intVal(list_nth(fsplan->fdw_private,
    1554              :                                           FdwScanPrivateFetchSize));
    1555              : 
    1556              :     /* Create contexts for batches of tuples and per-tuple temp workspace. */
    1557          504 :     fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
    1558              :                                                "postgres_fdw tuple data",
    1559              :                                                ALLOCSET_DEFAULT_SIZES);
    1560          504 :     fsstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
    1561              :                                               "postgres_fdw temporary data",
    1562              :                                               ALLOCSET_SMALL_SIZES);
    1563              : 
    1564              :     /*
    1565              :      * Get info we'll need for converting data fetched from the foreign server
    1566              :      * into local representation and error reporting during that process.
    1567              :      */
    1568          504 :     if (fsplan->scan.scanrelid > 0)
    1569              :     {
    1570          341 :         fsstate->rel = node->ss.ss_currentRelation;
    1571          341 :         fsstate->tupdesc = RelationGetDescr(fsstate->rel);
    1572              :     }
    1573              :     else
    1574              :     {
    1575          163 :         fsstate->rel = NULL;
    1576          163 :         fsstate->tupdesc = get_tupdesc_for_join_scan_tuples(node);
    1577              :     }
    1578              : 
    1579          504 :     fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
    1580              : 
    1581              :     /*
    1582              :      * Prepare for processing of parameters used in remote query, if any.
    1583              :      */
    1584          504 :     numParams = list_length(fsplan->fdw_exprs);
    1585          504 :     fsstate->numParams = numParams;
    1586          504 :     if (numParams > 0)
    1587           23 :         prepare_query_params((PlanState *) node,
    1588              :                              fsplan->fdw_exprs,
    1589              :                              numParams,
    1590              :                              &fsstate->param_flinfo,
    1591              :                              &fsstate->param_exprs,
    1592              :                              &fsstate->param_values);
    1593              : 
    1594              :     /* Set the async-capable flag */
    1595          504 :     fsstate->async_capable = node->ss.ps.async_capable;
    1596              : }
    1597              : 
    1598              : /*
    1599              :  * postgresIterateForeignScan
    1600              :  *      Retrieve next row from the result set, or clear tuple slot to indicate
    1601              :  *      EOF.
    1602              :  */
    1603              : static TupleTableSlot *
    1604        70891 : postgresIterateForeignScan(ForeignScanState *node)
    1605              : {
    1606        70891 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    1607        70891 :     TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
    1608              : 
    1609              :     /*
    1610              :      * In sync mode, if this is the first call after Begin or ReScan, we need
    1611              :      * to create the cursor on the remote side.  In async mode, we would have
    1612              :      * already created the cursor before we get here, even if this is the
    1613              :      * first call after Begin or ReScan.
    1614              :      */
    1615        70891 :     if (!fsstate->cursor_exists)
    1616          764 :         create_cursor(node);
    1617              : 
    1618              :     /*
    1619              :      * Get some more tuples, if we've run out.
    1620              :      */
    1621        70888 :     if (fsstate->next_tuple >= fsstate->num_tuples)
    1622              :     {
    1623              :         /* In async mode, just clear tuple slot. */
    1624         2046 :         if (fsstate->async_capable)
    1625           32 :             return ExecClearTuple(slot);
    1626              :         /* No point in another fetch if we already detected EOF, though. */
    1627         2014 :         if (!fsstate->eof_reached)
    1628         1341 :             fetch_more_data(node);
    1629              :         /* If we didn't get any tuples, must be end of data. */
    1630         2009 :         if (fsstate->next_tuple >= fsstate->num_tuples)
    1631          744 :             return ExecClearTuple(slot);
    1632              :     }
    1633              : 
    1634              :     /*
    1635              :      * Return the next tuple.
    1636              :      */
    1637        70107 :     ExecStoreHeapTuple(fsstate->tuples[fsstate->next_tuple++],
    1638              :                        slot,
    1639              :                        false);
    1640              : 
    1641        70107 :     return slot;
    1642              : }
    1643              : 
    1644              : /*
    1645              :  * postgresReScanForeignScan
    1646              :  *      Restart the scan.
    1647              :  */
    1648              : static void
    1649          407 : postgresReScanForeignScan(ForeignScanState *node)
    1650              : {
    1651          407 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    1652              :     char        sql[64];
    1653              :     PGresult   *res;
    1654              : 
    1655              :     /* If we haven't created the cursor yet, nothing to do. */
    1656          407 :     if (!fsstate->cursor_exists)
    1657           50 :         return;
    1658              : 
    1659              :     /*
    1660              :      * If the node is async-capable, and an asynchronous fetch for it has
    1661              :      * begun, the asynchronous fetch might not have yet completed.  Check if
    1662              :      * the node is async-capable, and an asynchronous fetch for it is still in
    1663              :      * progress; if so, complete the asynchronous fetch before restarting the
    1664              :      * scan.
    1665              :      */
    1666          369 :     if (fsstate->async_capable &&
    1667           21 :         fsstate->conn_state->pendingAreq &&
    1668            2 :         fsstate->conn_state->pendingAreq->requestee == (PlanState *) node)
    1669            1 :         fetch_more_data(node);
    1670              : 
    1671              :     /*
    1672              :      * If any internal parameters affecting this node have changed, we'd
    1673              :      * better destroy and recreate the cursor.  Otherwise, if the remote
    1674              :      * server is v14 or older, rewinding it should be good enough; if not,
    1675              :      * rewind is only allowed for scrollable cursors, but we don't have a way
    1676              :      * to check the scrollability of it, so destroy and recreate it in any
    1677              :      * case.  If we've only fetched zero or one batch, we needn't even rewind
    1678              :      * the cursor, just rescan what we have.
    1679              :      */
    1680          369 :     if (node->ss.ps.chgParam != NULL)
    1681              :     {
    1682          338 :         fsstate->cursor_exists = false;
    1683          338 :         snprintf(sql, sizeof(sql), "CLOSE c%u",
    1684              :                  fsstate->cursor_number);
    1685              :     }
    1686           31 :     else if (fsstate->fetch_ct_2 > 1)
    1687              :     {
    1688           19 :         if (PQserverVersion(fsstate->conn) < 150000)
    1689            0 :             snprintf(sql, sizeof(sql), "MOVE BACKWARD ALL IN c%u",
    1690              :                      fsstate->cursor_number);
    1691              :         else
    1692              :         {
    1693           19 :             fsstate->cursor_exists = false;
    1694           19 :             snprintf(sql, sizeof(sql), "CLOSE c%u",
    1695              :                      fsstate->cursor_number);
    1696              :         }
    1697              :     }
    1698              :     else
    1699              :     {
    1700              :         /* Easy: just rescan what we already have in memory, if anything */
    1701           12 :         fsstate->next_tuple = 0;
    1702           12 :         return;
    1703              :     }
    1704              : 
    1705          357 :     res = pgfdw_exec_query(fsstate->conn, sql, fsstate->conn_state);
    1706          357 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
    1707            0 :         pgfdw_report_error(res, fsstate->conn, sql);
    1708          357 :     PQclear(res);
    1709              : 
    1710              :     /* Now force a fresh FETCH. */
    1711          357 :     fsstate->tuples = NULL;
    1712          357 :     fsstate->num_tuples = 0;
    1713          357 :     fsstate->next_tuple = 0;
    1714          357 :     fsstate->fetch_ct_2 = 0;
    1715          357 :     fsstate->eof_reached = false;
    1716              : }
    1717              : 
    1718              : /*
    1719              :  * postgresEndForeignScan
    1720              :  *      Finish scanning foreign table and dispose objects used for this scan
    1721              :  */
    1722              : static void
    1723          869 : postgresEndForeignScan(ForeignScanState *node)
    1724              : {
    1725          869 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    1726              : 
    1727              :     /* if fsstate is NULL, we are in EXPLAIN; nothing to do */
    1728          869 :     if (fsstate == NULL)
    1729          386 :         return;
    1730              : 
    1731              :     /* Close the cursor if open, to prevent accumulation of cursors */
    1732          483 :     if (fsstate->cursor_exists)
    1733          455 :         close_cursor(fsstate->conn, fsstate->cursor_number,
    1734              :                      fsstate->conn_state);
    1735              : 
    1736              :     /* Release remote connection */
    1737          483 :     ReleaseConnection(fsstate->conn);
    1738          483 :     fsstate->conn = NULL;
    1739              : 
    1740              :     /* MemoryContexts will be deleted automatically. */
    1741              : }
    1742              : 
    1743              : /*
    1744              :  * postgresAddForeignUpdateTargets
    1745              :  *      Add resjunk column(s) needed for update/delete on a foreign table
    1746              :  */
    1747              : static void
    1748          189 : postgresAddForeignUpdateTargets(PlannerInfo *root,
    1749              :                                 Index rtindex,
    1750              :                                 RangeTblEntry *target_rte,
    1751              :                                 Relation target_relation)
    1752              : {
    1753              :     Var        *var;
    1754              : 
    1755              :     /*
    1756              :      * In postgres_fdw, what we need is the ctid, same as for a regular table.
    1757              :      */
    1758              : 
    1759              :     /* Make a Var representing the desired value */
    1760          189 :     var = makeVar(rtindex,
    1761              :                   SelfItemPointerAttributeNumber,
    1762              :                   TIDOID,
    1763              :                   -1,
    1764              :                   InvalidOid,
    1765              :                   0);
    1766              : 
    1767              :     /* Register it as a row-identity column needed by this target rel */
    1768          189 :     add_row_identity_var(root, var, rtindex, "ctid");
    1769          189 : }
    1770              : 
    1771              : /*
    1772              :  * postgresPlanForeignModify
    1773              :  *      Plan an insert/update/delete operation on a foreign table
    1774              :  */
    1775              : static List *
    1776          170 : postgresPlanForeignModify(PlannerInfo *root,
    1777              :                           ModifyTable *plan,
    1778              :                           Index resultRelation,
    1779              :                           int subplan_index)
    1780              : {
    1781          170 :     CmdType     operation = plan->operation;
    1782          170 :     RangeTblEntry *rte = planner_rt_fetch(resultRelation, root);
    1783              :     Relation    rel;
    1784              :     StringInfoData sql;
    1785          170 :     List       *targetAttrs = NIL;
    1786          170 :     List       *withCheckOptionList = NIL;
    1787          170 :     List       *returningList = NIL;
    1788          170 :     List       *retrieved_attrs = NIL;
    1789          170 :     bool        doNothing = false;
    1790          170 :     int         values_end_len = -1;
    1791              : 
    1792          170 :     initStringInfo(&sql);
    1793              : 
    1794              :     /*
    1795              :      * Core code already has some lock on each rel being planned, so we can
    1796              :      * use NoLock here.
    1797              :      */
    1798          170 :     rel = table_open(rte->relid, NoLock);
    1799              : 
    1800              :     /*
    1801              :      * In an INSERT, we transmit all columns that are defined in the foreign
    1802              :      * table.  In an UPDATE, if there are BEFORE ROW UPDATE triggers on the
    1803              :      * foreign table, we transmit all columns like INSERT; else we transmit
    1804              :      * only columns that were explicitly targets of the UPDATE, so as to avoid
    1805              :      * unnecessary data transmission.  (We can't do that for INSERT since we
    1806              :      * would miss sending default values for columns not listed in the source
    1807              :      * statement, and for UPDATE if there are BEFORE ROW UPDATE triggers since
    1808              :      * those triggers might change values for non-target columns, in which
    1809              :      * case we would miss sending changed values for those columns.)
    1810              :      */
    1811          170 :     if (operation == CMD_INSERT ||
    1812           60 :         (operation == CMD_UPDATE &&
    1813           60 :          rel->trigdesc &&
    1814           18 :          rel->trigdesc->trig_update_before_row))
    1815          103 :     {
    1816          103 :         TupleDesc   tupdesc = RelationGetDescr(rel);
    1817              :         int         attnum;
    1818              : 
    1819          434 :         for (attnum = 1; attnum <= tupdesc->natts; attnum++)
    1820              :         {
    1821          331 :             CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
    1822              : 
    1823          331 :             if (!attr->attisdropped)
    1824          314 :                 targetAttrs = lappend_int(targetAttrs, attnum);
    1825              :         }
    1826              :     }
    1827           67 :     else if (operation == CMD_UPDATE)
    1828              :     {
    1829              :         int         col;
    1830           45 :         RelOptInfo *rel = find_base_rel(root, resultRelation);
    1831           45 :         Bitmapset  *allUpdatedCols = get_rel_all_updated_cols(root, rel);
    1832              : 
    1833           45 :         col = -1;
    1834          100 :         while ((col = bms_next_member(allUpdatedCols, col)) >= 0)
    1835              :         {
    1836              :             /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
    1837           55 :             AttrNumber  attno = col + FirstLowInvalidHeapAttributeNumber;
    1838              : 
    1839           55 :             if (attno <= InvalidAttrNumber) /* shouldn't happen */
    1840            0 :                 elog(ERROR, "system-column update is not supported");
    1841           55 :             targetAttrs = lappend_int(targetAttrs, attno);
    1842              :         }
    1843              :     }
    1844              : 
    1845              :     /*
    1846              :      * Extract the relevant WITH CHECK OPTION list if any.
    1847              :      */
    1848          170 :     if (plan->withCheckOptionLists)
    1849           16 :         withCheckOptionList = (List *) list_nth(plan->withCheckOptionLists,
    1850              :                                                 subplan_index);
    1851              : 
    1852              :     /*
    1853              :      * Extract the relevant RETURNING list if any.
    1854              :      */
    1855          170 :     if (plan->returningLists)
    1856           32 :         returningList = (List *) list_nth(plan->returningLists, subplan_index);
    1857              : 
    1858              :     /*
    1859              :      * ON CONFLICT DO NOTHING/SELECT/UPDATE with inference specification
    1860              :      * should have already been rejected in the optimizer, as presently there
    1861              :      * is no way to recognize an arbiter index on a foreign table.  Only DO
    1862              :      * NOTHING is supported without an inference specification.
    1863              :      */
    1864          170 :     if (plan->onConflictAction == ONCONFLICT_NOTHING)
    1865            1 :         doNothing = true;
    1866          169 :     else if (plan->onConflictAction != ONCONFLICT_NONE)
    1867            0 :         elog(ERROR, "unexpected ON CONFLICT specification: %d",
    1868              :              (int) plan->onConflictAction);
    1869              : 
    1870              :     /*
    1871              :      * Construct the SQL command string.
    1872              :      */
    1873          170 :     switch (operation)
    1874              :     {
    1875           88 :         case CMD_INSERT:
    1876           88 :             deparseInsertSql(&sql, rte, resultRelation, rel,
    1877              :                              targetAttrs, doNothing,
    1878              :                              withCheckOptionList, returningList,
    1879              :                              &retrieved_attrs, &values_end_len);
    1880           88 :             break;
    1881           60 :         case CMD_UPDATE:
    1882           60 :             deparseUpdateSql(&sql, rte, resultRelation, rel,
    1883              :                              targetAttrs,
    1884              :                              withCheckOptionList, returningList,
    1885              :                              &retrieved_attrs);
    1886           60 :             break;
    1887           22 :         case CMD_DELETE:
    1888           22 :             deparseDeleteSql(&sql, rte, resultRelation, rel,
    1889              :                              returningList,
    1890              :                              &retrieved_attrs);
    1891           22 :             break;
    1892            0 :         default:
    1893            0 :             elog(ERROR, "unexpected operation: %d", (int) operation);
    1894              :             break;
    1895              :     }
    1896              : 
    1897          170 :     table_close(rel, NoLock);
    1898              : 
    1899              :     /*
    1900              :      * Build the fdw_private list that will be available to the executor.
    1901              :      * Items in the list must match enum FdwModifyPrivateIndex, above.
    1902              :      */
    1903          170 :     return list_make5(makeString(sql.data),
    1904              :                       targetAttrs,
    1905              :                       makeInteger(values_end_len),
    1906              :                       makeBoolean((retrieved_attrs != NIL)),
    1907              :                       retrieved_attrs);
    1908              : }
    1909              : 
    1910              : /*
    1911              :  * postgresBeginForeignModify
    1912              :  *      Begin an insert/update/delete operation on a foreign table
    1913              :  */
    1914              : static void
    1915          170 : postgresBeginForeignModify(ModifyTableState *mtstate,
    1916              :                            ResultRelInfo *resultRelInfo,
    1917              :                            List *fdw_private,
    1918              :                            int subplan_index,
    1919              :                            int eflags)
    1920              : {
    1921              :     PgFdwModifyState *fmstate;
    1922              :     char       *query;
    1923              :     List       *target_attrs;
    1924              :     bool        has_returning;
    1925              :     int         values_end_len;
    1926              :     List       *retrieved_attrs;
    1927              :     RangeTblEntry *rte;
    1928              : 
    1929              :     /*
    1930              :      * Do nothing in EXPLAIN (no ANALYZE) case.  resultRelInfo->ri_FdwState
    1931              :      * stays NULL.
    1932              :      */
    1933          170 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
    1934           46 :         return;
    1935              : 
    1936              :     /* Deconstruct fdw_private data. */
    1937          124 :     query = strVal(list_nth(fdw_private,
    1938              :                             FdwModifyPrivateUpdateSql));
    1939          124 :     target_attrs = (List *) list_nth(fdw_private,
    1940              :                                      FdwModifyPrivateTargetAttnums);
    1941          124 :     values_end_len = intVal(list_nth(fdw_private,
    1942              :                                      FdwModifyPrivateLen));
    1943          124 :     has_returning = boolVal(list_nth(fdw_private,
    1944              :                                      FdwModifyPrivateHasReturning));
    1945          124 :     retrieved_attrs = (List *) list_nth(fdw_private,
    1946              :                                         FdwModifyPrivateRetrievedAttrs);
    1947              : 
    1948              :     /* Find RTE. */
    1949          124 :     rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex,
    1950              :                         mtstate->ps.state);
    1951              : 
    1952              :     /* Construct an execution state. */
    1953          124 :     fmstate = create_foreign_modify(mtstate->ps.state,
    1954              :                                     rte,
    1955              :                                     resultRelInfo,
    1956              :                                     mtstate->operation,
    1957          124 :                                     outerPlanState(mtstate)->plan,
    1958              :                                     query,
    1959              :                                     target_attrs,
    1960              :                                     values_end_len,
    1961              :                                     has_returning,
    1962              :                                     retrieved_attrs);
    1963              : 
    1964          124 :     resultRelInfo->ri_FdwState = fmstate;
    1965              : }
    1966              : 
    1967              : /*
    1968              :  * postgresExecForeignInsert
    1969              :  *      Insert one row into a foreign table
    1970              :  */
    1971              : static TupleTableSlot *
    1972          892 : postgresExecForeignInsert(EState *estate,
    1973              :                           ResultRelInfo *resultRelInfo,
    1974              :                           TupleTableSlot *slot,
    1975              :                           TupleTableSlot *planSlot)
    1976              : {
    1977          892 :     PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
    1978              :     TupleTableSlot **rslot;
    1979          892 :     int         numSlots = 1;
    1980              : 
    1981              :     /*
    1982              :      * If the fmstate has aux_fmstate set, use the aux_fmstate (see
    1983              :      * postgresBeginForeignInsert())
    1984              :      */
    1985          892 :     if (fmstate->aux_fmstate)
    1986            0 :         resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
    1987          892 :     rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
    1988              :                                    &slot, &planSlot, &numSlots);
    1989              :     /* Revert that change */
    1990          888 :     if (fmstate->aux_fmstate)
    1991            0 :         resultRelInfo->ri_FdwState = fmstate;
    1992              : 
    1993          888 :     return rslot ? *rslot : NULL;
    1994              : }
    1995              : 
    1996              : /*
    1997              :  * postgresExecForeignBatchInsert
    1998              :  *      Insert multiple rows into a foreign table
    1999              :  */
    2000              : static TupleTableSlot **
    2001           42 : postgresExecForeignBatchInsert(EState *estate,
    2002              :                                ResultRelInfo *resultRelInfo,
    2003              :                                TupleTableSlot **slots,
    2004              :                                TupleTableSlot **planSlots,
    2005              :                                int *numSlots)
    2006              : {
    2007           42 :     PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
    2008              :     TupleTableSlot **rslot;
    2009              : 
    2010              :     /*
    2011              :      * If the fmstate has aux_fmstate set, use the aux_fmstate (see
    2012              :      * postgresBeginForeignInsert())
    2013              :      */
    2014           42 :     if (fmstate->aux_fmstate)
    2015            0 :         resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
    2016           42 :     rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
    2017              :                                    slots, planSlots, numSlots);
    2018              :     /* Revert that change */
    2019           41 :     if (fmstate->aux_fmstate)
    2020            0 :         resultRelInfo->ri_FdwState = fmstate;
    2021              : 
    2022           41 :     return rslot;
    2023              : }
    2024              : 
    2025              : /*
    2026              :  * postgresGetForeignModifyBatchSize
    2027              :  *      Determine the maximum number of tuples that can be inserted in bulk
    2028              :  *
    2029              :  * Returns the batch size specified for server or table. When batching is not
    2030              :  * allowed (e.g. for tables with BEFORE/AFTER ROW triggers or with RETURNING
    2031              :  * clause), returns 1.
    2032              :  */
    2033              : static int
    2034          146 : postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
    2035              : {
    2036              :     int         batch_size;
    2037          146 :     PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
    2038              : 
    2039              :     /* should be called only once */
    2040              :     Assert(resultRelInfo->ri_BatchSize == 0);
    2041              : 
    2042              :     /*
    2043              :      * Should never get called when the insert is being performed on a table
    2044              :      * that is also among the target relations of an UPDATE operation, because
    2045              :      * postgresBeginForeignInsert() currently rejects such insert attempts.
    2046              :      */
    2047              :     Assert(fmstate == NULL || fmstate->aux_fmstate == NULL);
    2048              : 
    2049              :     /*
    2050              :      * In EXPLAIN without ANALYZE, ri_FdwState is NULL, so we have to lookup
    2051              :      * the option directly in server/table options. Otherwise just use the
    2052              :      * value we determined earlier.
    2053              :      */
    2054          146 :     if (fmstate)
    2055          133 :         batch_size = fmstate->batch_size;
    2056              :     else
    2057           13 :         batch_size = get_batch_size_option(resultRelInfo->ri_RelationDesc);
    2058              : 
    2059              :     /*
    2060              :      * Disable batching when we have to use RETURNING, there are any
    2061              :      * BEFORE/AFTER ROW INSERT triggers on the foreign table, or there are any
    2062              :      * WITH CHECK OPTION constraints from parent views.
    2063              :      *
    2064              :      * When there are any BEFORE ROW INSERT triggers on the table, we can't
    2065              :      * support it, because such triggers might query the table we're inserting
    2066              :      * into and act differently if the tuples that have already been processed
    2067              :      * and prepared for insertion are not there.
    2068              :      */
    2069          146 :     if (resultRelInfo->ri_projectReturning != NULL ||
    2070          125 :         resultRelInfo->ri_WithCheckOptions != NIL ||
    2071          116 :         (resultRelInfo->ri_TrigDesc &&
    2072           14 :          (resultRelInfo->ri_TrigDesc->trig_insert_before_row ||
    2073            1 :           resultRelInfo->ri_TrigDesc->trig_insert_after_row)))
    2074           44 :         return 1;
    2075              : 
    2076              :     /*
    2077              :      * If the foreign table has no columns, disable batching as the INSERT
    2078              :      * syntax doesn't allow batching multiple empty rows into a zero-column
    2079              :      * table in a single statement.  This is needed for COPY FROM, in which
    2080              :      * case fmstate must be non-NULL.
    2081              :      */
    2082          102 :     if (fmstate && list_length(fmstate->target_attrs) == 0)
    2083            1 :         return 1;
    2084              : 
    2085              :     /*
    2086              :      * Otherwise use the batch size specified for server/table. The number of
    2087              :      * parameters in a batch is limited to 65535 (uint16), so make sure we
    2088              :      * don't exceed this limit by using the maximum batch_size possible.
    2089              :      */
    2090          101 :     if (fmstate && fmstate->p_nums > 0)
    2091           93 :         batch_size = Min(batch_size, PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);
    2092              : 
    2093          101 :     return batch_size;
    2094              : }
    2095              : 
    2096              : /*
    2097              :  * postgresExecForeignUpdate
    2098              :  *      Update one row in a foreign table
    2099              :  */
    2100              : static TupleTableSlot *
    2101           95 : postgresExecForeignUpdate(EState *estate,
    2102              :                           ResultRelInfo *resultRelInfo,
    2103              :                           TupleTableSlot *slot,
    2104              :                           TupleTableSlot *planSlot)
    2105              : {
    2106              :     TupleTableSlot **rslot;
    2107           95 :     int         numSlots = 1;
    2108              : 
    2109           95 :     rslot = execute_foreign_modify(estate, resultRelInfo, CMD_UPDATE,
    2110              :                                    &slot, &planSlot, &numSlots);
    2111              : 
    2112           95 :     return rslot ? rslot[0] : NULL;
    2113              : }
    2114              : 
    2115              : /*
    2116              :  * postgresExecForeignDelete
    2117              :  *      Delete one row from a foreign table
    2118              :  */
    2119              : static TupleTableSlot *
    2120           23 : postgresExecForeignDelete(EState *estate,
    2121              :                           ResultRelInfo *resultRelInfo,
    2122              :                           TupleTableSlot *slot,
    2123              :                           TupleTableSlot *planSlot)
    2124              : {
    2125              :     TupleTableSlot **rslot;
    2126           23 :     int         numSlots = 1;
    2127              : 
    2128           23 :     rslot = execute_foreign_modify(estate, resultRelInfo, CMD_DELETE,
    2129              :                                    &slot, &planSlot, &numSlots);
    2130              : 
    2131           23 :     return rslot ? rslot[0] : NULL;
    2132              : }
    2133              : 
    2134              : /*
    2135              :  * postgresEndForeignModify
    2136              :  *      Finish an insert/update/delete operation on a foreign table
    2137              :  */
    2138              : static void
    2139          156 : postgresEndForeignModify(EState *estate,
    2140              :                          ResultRelInfo *resultRelInfo)
    2141              : {
    2142          156 :     PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
    2143              : 
    2144              :     /* If fmstate is NULL, we are in EXPLAIN; nothing to do */
    2145          156 :     if (fmstate == NULL)
    2146           46 :         return;
    2147              : 
    2148              :     /* Destroy the execution state */
    2149          110 :     finish_foreign_modify(fmstate);
    2150              : }
    2151              : 
    2152              : /*
    2153              :  * postgresBeginForeignInsert
    2154              :  *      Begin an insert operation on a foreign table
    2155              :  */
    2156              : static void
    2157           64 : postgresBeginForeignInsert(ModifyTableState *mtstate,
    2158              :                            ResultRelInfo *resultRelInfo)
    2159              : {
    2160              :     PgFdwModifyState *fmstate;
    2161           64 :     ModifyTable *plan = castNode(ModifyTable, mtstate->ps.plan);
    2162           64 :     EState     *estate = mtstate->ps.state;
    2163              :     Index       resultRelation;
    2164           64 :     Relation    rel = resultRelInfo->ri_RelationDesc;
    2165              :     RangeTblEntry *rte;
    2166           64 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    2167              :     int         attnum;
    2168              :     int         values_end_len;
    2169              :     StringInfoData sql;
    2170           64 :     List       *targetAttrs = NIL;
    2171           64 :     List       *retrieved_attrs = NIL;
    2172           64 :     bool        doNothing = false;
    2173              : 
    2174              :     /*
    2175              :      * If the foreign table we are about to insert routed rows into is also an
    2176              :      * UPDATE subplan result rel that will be updated later, proceeding with
    2177              :      * the INSERT will result in the later UPDATE incorrectly modifying those
    2178              :      * routed rows, so prevent the INSERT --- it would be nice if we could
    2179              :      * handle this case; but for now, throw an error for safety.
    2180              :      */
    2181           64 :     if (plan && plan->operation == CMD_UPDATE &&
    2182            9 :         (resultRelInfo->ri_usesFdwDirectModify ||
    2183            5 :          resultRelInfo->ri_FdwState))
    2184            6 :         ereport(ERROR,
    2185              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2186              :                  errmsg("cannot route tuples into foreign table to be updated \"%s\"",
    2187              :                         RelationGetRelationName(rel))));
    2188              : 
    2189           58 :     initStringInfo(&sql);
    2190              : 
    2191              :     /* We transmit all columns that are defined in the foreign table. */
    2192          173 :     for (attnum = 1; attnum <= tupdesc->natts; attnum++)
    2193              :     {
    2194          115 :         CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
    2195              : 
    2196          115 :         if (!attr->attisdropped)
    2197          113 :             targetAttrs = lappend_int(targetAttrs, attnum);
    2198              :     }
    2199              : 
    2200              :     /* Check if we add the ON CONFLICT clause to the remote query. */
    2201           58 :     if (plan)
    2202              :     {
    2203           34 :         OnConflictAction onConflictAction = plan->onConflictAction;
    2204              : 
    2205              :         /* We only support DO NOTHING without an inference specification. */
    2206           34 :         if (onConflictAction == ONCONFLICT_NOTHING)
    2207            2 :             doNothing = true;
    2208           32 :         else if (onConflictAction != ONCONFLICT_NONE)
    2209            0 :             elog(ERROR, "unexpected ON CONFLICT specification: %d",
    2210              :                  (int) onConflictAction);
    2211              :     }
    2212              : 
    2213              :     /*
    2214              :      * If the foreign table is a partition that doesn't have a corresponding
    2215              :      * RTE entry, we need to create a new RTE describing the foreign table for
    2216              :      * use by deparseInsertSql and create_foreign_modify() below, after first
    2217              :      * copying the parent's RTE and modifying some fields to describe the
    2218              :      * foreign partition to work on. However, if this is invoked by UPDATE,
    2219              :      * the existing RTE may already correspond to this partition if it is one
    2220              :      * of the UPDATE subplan target rels; in that case, we can just use the
    2221              :      * existing RTE as-is.
    2222              :      */
    2223           58 :     if (resultRelInfo->ri_RangeTableIndex == 0)
    2224              :     {
    2225           40 :         ResultRelInfo *rootResultRelInfo = resultRelInfo->ri_RootResultRelInfo;
    2226              : 
    2227           40 :         rte = exec_rt_fetch(rootResultRelInfo->ri_RangeTableIndex, estate);
    2228           40 :         rte = copyObject(rte);
    2229           40 :         rte->relid = RelationGetRelid(rel);
    2230           40 :         rte->relkind = RELKIND_FOREIGN_TABLE;
    2231              : 
    2232              :         /*
    2233              :          * For UPDATE, we must use the RT index of the first subplan target
    2234              :          * rel's RTE, because the core code would have built expressions for
    2235              :          * the partition, such as RETURNING, using that RT index as varno of
    2236              :          * Vars contained in those expressions.
    2237              :          */
    2238           40 :         if (plan && plan->operation == CMD_UPDATE &&
    2239            3 :             rootResultRelInfo->ri_RangeTableIndex == plan->rootRelation)
    2240            3 :             resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
    2241              :         else
    2242           37 :             resultRelation = rootResultRelInfo->ri_RangeTableIndex;
    2243              :     }
    2244              :     else
    2245              :     {
    2246           18 :         resultRelation = resultRelInfo->ri_RangeTableIndex;
    2247           18 :         rte = exec_rt_fetch(resultRelation, estate);
    2248              :     }
    2249              : 
    2250              :     /* Construct the SQL command string. */
    2251           58 :     deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing,
    2252              :                      resultRelInfo->ri_WithCheckOptions,
    2253              :                      resultRelInfo->ri_returningList,
    2254              :                      &retrieved_attrs, &values_end_len);
    2255              : 
    2256              :     /* Construct an execution state. */
    2257           58 :     fmstate = create_foreign_modify(mtstate->ps.state,
    2258              :                                     rte,
    2259              :                                     resultRelInfo,
    2260              :                                     CMD_INSERT,
    2261              :                                     NULL,
    2262              :                                     sql.data,
    2263              :                                     targetAttrs,
    2264              :                                     values_end_len,
    2265              :                                     retrieved_attrs != NIL,
    2266              :                                     retrieved_attrs);
    2267              : 
    2268              :     /*
    2269              :      * If the given resultRelInfo already has PgFdwModifyState set, it means
    2270              :      * the foreign table is an UPDATE subplan result rel; in which case, store
    2271              :      * the resulting state into the aux_fmstate of the PgFdwModifyState.
    2272              :      */
    2273           58 :     if (resultRelInfo->ri_FdwState)
    2274              :     {
    2275              :         Assert(plan && plan->operation == CMD_UPDATE);
    2276              :         Assert(resultRelInfo->ri_usesFdwDirectModify == false);
    2277            0 :         ((PgFdwModifyState *) resultRelInfo->ri_FdwState)->aux_fmstate = fmstate;
    2278              :     }
    2279              :     else
    2280           58 :         resultRelInfo->ri_FdwState = fmstate;
    2281           58 : }
    2282              : 
    2283              : /*
    2284              :  * postgresEndForeignInsert
    2285              :  *      Finish an insert operation on a foreign table
    2286              :  */
    2287              : static void
    2288           50 : postgresEndForeignInsert(EState *estate,
    2289              :                          ResultRelInfo *resultRelInfo)
    2290              : {
    2291           50 :     PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
    2292              : 
    2293              :     Assert(fmstate != NULL);
    2294              : 
    2295              :     /*
    2296              :      * If the fmstate has aux_fmstate set, get the aux_fmstate (see
    2297              :      * postgresBeginForeignInsert())
    2298              :      */
    2299           50 :     if (fmstate->aux_fmstate)
    2300            0 :         fmstate = fmstate->aux_fmstate;
    2301              : 
    2302              :     /* Destroy the execution state */
    2303           50 :     finish_foreign_modify(fmstate);
    2304           50 : }
    2305              : 
    2306              : /*
    2307              :  * postgresIsForeignRelUpdatable
    2308              :  *      Determine whether a foreign table supports INSERT, UPDATE and/or
    2309              :  *      DELETE.
    2310              :  */
    2311              : static int
    2312          338 : postgresIsForeignRelUpdatable(Relation rel)
    2313              : {
    2314              :     bool        updatable;
    2315              :     ForeignTable *table;
    2316              :     ForeignServer *server;
    2317              :     ListCell   *lc;
    2318              : 
    2319              :     /*
    2320              :      * By default, all postgres_fdw foreign tables are assumed updatable. This
    2321              :      * can be overridden by a per-server setting, which in turn can be
    2322              :      * overridden by a per-table setting.
    2323              :      */
    2324          338 :     updatable = true;
    2325              : 
    2326          338 :     table = GetForeignTable(RelationGetRelid(rel));
    2327          338 :     server = GetForeignServer(table->serverid);
    2328              : 
    2329         1511 :     foreach(lc, server->options)
    2330              :     {
    2331         1173 :         DefElem    *def = (DefElem *) lfirst(lc);
    2332              : 
    2333         1173 :         if (strcmp(def->defname, "updatable") == 0)
    2334            0 :             updatable = defGetBoolean(def);
    2335              :     }
    2336          814 :     foreach(lc, table->options)
    2337              :     {
    2338          476 :         DefElem    *def = (DefElem *) lfirst(lc);
    2339              : 
    2340          476 :         if (strcmp(def->defname, "updatable") == 0)
    2341            0 :             updatable = defGetBoolean(def);
    2342              :     }
    2343              : 
    2344              :     /*
    2345              :      * Currently "updatable" means support for INSERT, UPDATE and DELETE.
    2346              :      */
    2347              :     return updatable ?
    2348          338 :         (1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE) : 0;
    2349              : }
    2350              : 
    2351              : /*
    2352              :  * postgresRecheckForeignScan
    2353              :  *      Execute a local join execution plan for a foreign join
    2354              :  */
    2355              : static bool
    2356            5 : postgresRecheckForeignScan(ForeignScanState *node, TupleTableSlot *slot)
    2357              : {
    2358            5 :     Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
    2359            5 :     PlanState  *outerPlan = outerPlanState(node);
    2360              :     TupleTableSlot *result;
    2361              : 
    2362              :     /* For base foreign relations, it suffices to set fdw_recheck_quals */
    2363            5 :     if (scanrelid > 0)
    2364            3 :         return true;
    2365              : 
    2366              :     Assert(outerPlan != NULL);
    2367              : 
    2368              :     /* Execute a local join execution plan */
    2369            2 :     result = ExecProcNode(outerPlan);
    2370            2 :     if (TupIsNull(result))
    2371            1 :         return false;
    2372              : 
    2373              :     /* Store result in the given slot */
    2374            1 :     ExecCopySlot(slot, result);
    2375              : 
    2376            1 :     return true;
    2377              : }
    2378              : 
    2379              : /*
    2380              :  * find_modifytable_subplan
    2381              :  *      Helper routine for postgresPlanDirectModify to find the
    2382              :  *      ModifyTable subplan node that scans the specified RTI.
    2383              :  *
    2384              :  * Returns NULL if the subplan couldn't be identified.  That's not a fatal
    2385              :  * error condition, we just abandon trying to do the update directly.
    2386              :  */
    2387              : static ForeignScan *
    2388          131 : find_modifytable_subplan(PlannerInfo *root,
    2389              :                          ModifyTable *plan,
    2390              :                          Index rtindex,
    2391              :                          int subplan_index)
    2392              : {
    2393          131 :     Plan       *subplan = outerPlan(plan);
    2394              : 
    2395              :     /*
    2396              :      * The cases we support are (1) the desired ForeignScan is the immediate
    2397              :      * child of ModifyTable, or (2) it is the subplan_index'th child of an
    2398              :      * Append node that is the immediate child of ModifyTable.  There is no
    2399              :      * point in looking further down, as that would mean that local joins are
    2400              :      * involved, so we can't do the update directly.
    2401              :      *
    2402              :      * There could be a Result atop the Append too, acting to compute the
    2403              :      * UPDATE targetlist values.  We ignore that here; the tlist will be
    2404              :      * checked by our caller.
    2405              :      *
    2406              :      * In principle we could examine all the children of the Append, but it's
    2407              :      * currently unlikely that the core planner would generate such a plan
    2408              :      * with the children out-of-order.  Moreover, such a search risks costing
    2409              :      * O(N^2) time when there are a lot of children.
    2410              :      */
    2411          131 :     if (IsA(subplan, Append))
    2412              :     {
    2413           33 :         Append     *appendplan = (Append *) subplan;
    2414              : 
    2415           33 :         if (subplan_index < list_length(appendplan->appendplans))
    2416           33 :             subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
    2417              :     }
    2418           98 :     else if (IsA(subplan, Result) &&
    2419            6 :              outerPlan(subplan) != NULL &&
    2420            5 :              IsA(outerPlan(subplan), Append))
    2421              :     {
    2422            5 :         Append     *appendplan = (Append *) outerPlan(subplan);
    2423              : 
    2424            5 :         if (subplan_index < list_length(appendplan->appendplans))
    2425            5 :             subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
    2426              :     }
    2427              : 
    2428              :     /* Now, have we got a ForeignScan on the desired rel? */
    2429          131 :     if (IsA(subplan, ForeignScan))
    2430              :     {
    2431          114 :         ForeignScan *fscan = (ForeignScan *) subplan;
    2432              : 
    2433          114 :         if (bms_is_member(rtindex, fscan->fs_base_relids))
    2434          114 :             return fscan;
    2435              :     }
    2436              : 
    2437           17 :     return NULL;
    2438              : }
    2439              : 
    2440              : /*
    2441              :  * postgresPlanDirectModify
    2442              :  *      Consider a direct foreign table modification
    2443              :  *
    2444              :  * Decide whether it is safe to modify a foreign table directly, and if so,
    2445              :  * rewrite subplan accordingly.
    2446              :  */
    2447              : static bool
    2448          195 : postgresPlanDirectModify(PlannerInfo *root,
    2449              :                          ModifyTable *plan,
    2450              :                          Index resultRelation,
    2451              :                          int subplan_index)
    2452              : {
    2453          195 :     CmdType     operation = plan->operation;
    2454              :     RelOptInfo *foreignrel;
    2455              :     RangeTblEntry *rte;
    2456              :     PgFdwRelationInfo *fpinfo;
    2457              :     Relation    rel;
    2458              :     StringInfoData sql;
    2459              :     ForeignScan *fscan;
    2460          195 :     List       *processed_tlist = NIL;
    2461          195 :     List       *targetAttrs = NIL;
    2462              :     List       *remote_exprs;
    2463          195 :     List       *params_list = NIL;
    2464          195 :     List       *returningList = NIL;
    2465          195 :     List       *retrieved_attrs = NIL;
    2466              : 
    2467              :     /*
    2468              :      * Decide whether it is safe to modify a foreign table directly.
    2469              :      */
    2470              : 
    2471              :     /*
    2472              :      * The table modification must be an UPDATE or DELETE.
    2473              :      */
    2474          195 :     if (operation != CMD_UPDATE && operation != CMD_DELETE)
    2475           64 :         return false;
    2476              : 
    2477              :     /*
    2478              :      * Try to locate the ForeignScan subplan that's scanning resultRelation.
    2479              :      */
    2480          131 :     fscan = find_modifytable_subplan(root, plan, resultRelation, subplan_index);
    2481          131 :     if (!fscan)
    2482           17 :         return false;
    2483              : 
    2484              :     /*
    2485              :      * It's unsafe to modify a foreign table directly if there are any quals
    2486              :      * that should be evaluated locally.
    2487              :      */
    2488          114 :     if (fscan->scan.plan.qual != NIL)
    2489            5 :         return false;
    2490              : 
    2491              :     /* Safe to fetch data about the target foreign rel */
    2492          109 :     if (fscan->scan.scanrelid == 0)
    2493              :     {
    2494           10 :         foreignrel = find_join_rel(root, fscan->fs_relids);
    2495              :         /* We should have a rel for this foreign join. */
    2496              :         Assert(foreignrel);
    2497              :     }
    2498              :     else
    2499           99 :         foreignrel = root->simple_rel_array[resultRelation];
    2500          109 :     rte = root->simple_rte_array[resultRelation];
    2501          109 :     fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    2502              : 
    2503              :     /*
    2504              :      * It's unsafe to update a foreign table directly, if any expressions to
    2505              :      * assign to the target columns are unsafe to evaluate remotely.
    2506              :      */
    2507          109 :     if (operation == CMD_UPDATE)
    2508              :     {
    2509              :         ListCell   *lc,
    2510              :                    *lc2;
    2511              : 
    2512              :         /*
    2513              :          * The expressions of concern are the first N columns of the processed
    2514              :          * targetlist, where N is the length of the rel's update_colnos.
    2515              :          */
    2516           50 :         get_translated_update_targetlist(root, resultRelation,
    2517              :                                          &processed_tlist, &targetAttrs);
    2518          103 :         forboth(lc, processed_tlist, lc2, targetAttrs)
    2519              :         {
    2520           58 :             TargetEntry *tle = lfirst_node(TargetEntry, lc);
    2521           58 :             AttrNumber  attno = lfirst_int(lc2);
    2522              : 
    2523              :             /* update's new-value expressions shouldn't be resjunk */
    2524              :             Assert(!tle->resjunk);
    2525              : 
    2526           58 :             if (attno <= InvalidAttrNumber) /* shouldn't happen */
    2527            0 :                 elog(ERROR, "system-column update is not supported");
    2528              : 
    2529           58 :             if (!is_foreign_expr(root, foreignrel, (Expr *) tle->expr))
    2530            5 :                 return false;
    2531              :         }
    2532              :     }
    2533              : 
    2534              :     /*
    2535              :      * Ok, rewrite subplan so as to modify the foreign table directly.
    2536              :      */
    2537          104 :     initStringInfo(&sql);
    2538              : 
    2539              :     /*
    2540              :      * Core code already has some lock on each rel being planned, so we can
    2541              :      * use NoLock here.
    2542              :      */
    2543          104 :     rel = table_open(rte->relid, NoLock);
    2544              : 
    2545              :     /*
    2546              :      * Recall the qual clauses that must be evaluated remotely.  (These are
    2547              :      * bare clauses not RestrictInfos, but deparse.c's appendConditions()
    2548              :      * doesn't care.)
    2549              :      */
    2550          104 :     remote_exprs = fpinfo->final_remote_exprs;
    2551              : 
    2552              :     /*
    2553              :      * Extract the relevant RETURNING list if any.
    2554              :      */
    2555          104 :     if (plan->returningLists)
    2556              :     {
    2557           35 :         returningList = (List *) list_nth(plan->returningLists, subplan_index);
    2558              : 
    2559              :         /*
    2560              :          * When performing an UPDATE/DELETE .. RETURNING on a join directly,
    2561              :          * we fetch from the foreign server any Vars specified in RETURNING
    2562              :          * that refer not only to the target relation but to non-target
    2563              :          * relations.  So we'll deparse them into the RETURNING clause of the
    2564              :          * remote query; use a targetlist consisting of them instead, which
    2565              :          * will be adjusted to be new fdw_scan_tlist of the foreign-scan plan
    2566              :          * node below.
    2567              :          */
    2568           35 :         if (fscan->scan.scanrelid == 0)
    2569            4 :             returningList = build_remote_returning(resultRelation, rel,
    2570              :                                                    returningList);
    2571              :     }
    2572              : 
    2573              :     /*
    2574              :      * Construct the SQL command string.
    2575              :      */
    2576          104 :     switch (operation)
    2577              :     {
    2578           45 :         case CMD_UPDATE:
    2579           45 :             deparseDirectUpdateSql(&sql, root, resultRelation, rel,
    2580              :                                    foreignrel,
    2581              :                                    processed_tlist,
    2582              :                                    targetAttrs,
    2583              :                                    remote_exprs, &params_list,
    2584              :                                    returningList, &retrieved_attrs);
    2585           45 :             break;
    2586           59 :         case CMD_DELETE:
    2587           59 :             deparseDirectDeleteSql(&sql, root, resultRelation, rel,
    2588              :                                    foreignrel,
    2589              :                                    remote_exprs, &params_list,
    2590              :                                    returningList, &retrieved_attrs);
    2591           59 :             break;
    2592            0 :         default:
    2593            0 :             elog(ERROR, "unexpected operation: %d", (int) operation);
    2594              :             break;
    2595              :     }
    2596              : 
    2597              :     /*
    2598              :      * Update the operation and target relation info.
    2599              :      */
    2600          104 :     fscan->operation = operation;
    2601          104 :     fscan->resultRelation = resultRelation;
    2602              : 
    2603              :     /*
    2604              :      * Update the fdw_exprs list that will be available to the executor.
    2605              :      */
    2606          104 :     fscan->fdw_exprs = params_list;
    2607              : 
    2608              :     /*
    2609              :      * Update the fdw_private list that will be available to the executor.
    2610              :      * Items in the list must match enum FdwDirectModifyPrivateIndex, above.
    2611              :      */
    2612          104 :     fscan->fdw_private = list_make4(makeString(sql.data),
    2613              :                                     makeBoolean((retrieved_attrs != NIL)),
    2614              :                                     retrieved_attrs,
    2615              :                                     makeBoolean(plan->canSetTag));
    2616              : 
    2617              :     /*
    2618              :      * Update the foreign-join-related fields.
    2619              :      */
    2620          104 :     if (fscan->scan.scanrelid == 0)
    2621              :     {
    2622              :         /* No need for the outer subplan. */
    2623            8 :         fscan->scan.plan.lefttree = NULL;
    2624              : 
    2625              :         /* Build new fdw_scan_tlist if UPDATE/DELETE .. RETURNING. */
    2626            8 :         if (returningList)
    2627            2 :             rebuild_fdw_scan_tlist(fscan, returningList);
    2628              :     }
    2629              : 
    2630              :     /*
    2631              :      * Finally, unset the async-capable flag if it is set, as we currently
    2632              :      * don't support asynchronous execution of direct modifications.
    2633              :      */
    2634          104 :     if (fscan->scan.plan.async_capable)
    2635            8 :         fscan->scan.plan.async_capable = false;
    2636              : 
    2637          104 :     table_close(rel, NoLock);
    2638          104 :     return true;
    2639              : }
    2640              : 
    2641              : /*
    2642              :  * postgresBeginDirectModify
    2643              :  *      Prepare a direct foreign table modification
    2644              :  */
    2645              : static void
    2646          104 : postgresBeginDirectModify(ForeignScanState *node, int eflags)
    2647              : {
    2648          104 :     ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
    2649          104 :     EState     *estate = node->ss.ps.state;
    2650              :     PgFdwDirectModifyState *dmstate;
    2651              :     Index       rtindex;
    2652              :     Oid         userid;
    2653              :     ForeignTable *table;
    2654              :     UserMapping *user;
    2655              :     int         numParams;
    2656              : 
    2657              :     /*
    2658              :      * Do nothing in EXPLAIN (no ANALYZE) case.  node->fdw_state stays NULL.
    2659              :      */
    2660          104 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
    2661           32 :         return;
    2662              : 
    2663              :     /*
    2664              :      * We'll save private state in node->fdw_state.
    2665              :      */
    2666           72 :     dmstate = palloc0_object(PgFdwDirectModifyState);
    2667           72 :     node->fdw_state = dmstate;
    2668              : 
    2669              :     /*
    2670              :      * Identify which user to do the remote access as.  This should match what
    2671              :      * ExecCheckPermissions() does.
    2672              :      */
    2673           72 :     userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
    2674              : 
    2675              :     /* Get info about foreign table. */
    2676           72 :     rtindex = node->resultRelInfo->ri_RangeTableIndex;
    2677           72 :     if (fsplan->scan.scanrelid == 0)
    2678            4 :         dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags);
    2679              :     else
    2680           68 :         dmstate->rel = node->ss.ss_currentRelation;
    2681           72 :     table = GetForeignTable(RelationGetRelid(dmstate->rel));
    2682           72 :     user = GetUserMapping(userid, table->serverid);
    2683              : 
    2684              :     /*
    2685              :      * Get connection to the foreign server.  Connection manager will
    2686              :      * establish new connection if necessary.
    2687              :      */
    2688           72 :     dmstate->conn = GetConnection(user, false, &dmstate->conn_state);
    2689              : 
    2690              :     /* Update the foreign-join-related fields. */
    2691           72 :     if (fsplan->scan.scanrelid == 0)
    2692              :     {
    2693              :         /* Save info about foreign table. */
    2694            4 :         dmstate->resultRel = dmstate->rel;
    2695              : 
    2696              :         /*
    2697              :          * Set dmstate->rel to NULL to teach get_returning_data() and
    2698              :          * make_tuple_from_result_row() that columns fetched from the remote
    2699              :          * server are described by fdw_scan_tlist of the foreign-scan plan
    2700              :          * node, not the tuple descriptor for the target relation.
    2701              :          */
    2702            4 :         dmstate->rel = NULL;
    2703              :     }
    2704              : 
    2705              :     /* Initialize state variable */
    2706           72 :     dmstate->num_tuples = -1;    /* -1 means not set yet */
    2707              : 
    2708              :     /* Get private info created by planner functions. */
    2709           72 :     dmstate->query = strVal(list_nth(fsplan->fdw_private,
    2710              :                                      FdwDirectModifyPrivateUpdateSql));
    2711           72 :     dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
    2712              :                                               FdwDirectModifyPrivateHasReturning));
    2713           72 :     dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
    2714              :                                                  FdwDirectModifyPrivateRetrievedAttrs);
    2715           72 :     dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
    2716              :                                               FdwDirectModifyPrivateSetProcessed));
    2717              : 
    2718              :     /* Create context for per-tuple temp workspace. */
    2719           72 :     dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
    2720              :                                               "postgres_fdw temporary data",
    2721              :                                               ALLOCSET_SMALL_SIZES);
    2722              : 
    2723              :     /* Prepare for input conversion of RETURNING results. */
    2724           72 :     if (dmstate->has_returning)
    2725              :     {
    2726              :         TupleDesc   tupdesc;
    2727              : 
    2728           16 :         if (fsplan->scan.scanrelid == 0)
    2729            1 :             tupdesc = get_tupdesc_for_join_scan_tuples(node);
    2730              :         else
    2731           15 :             tupdesc = RelationGetDescr(dmstate->rel);
    2732              : 
    2733           16 :         dmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
    2734              : 
    2735              :         /*
    2736              :          * When performing an UPDATE/DELETE .. RETURNING on a join directly,
    2737              :          * initialize a filter to extract an updated/deleted tuple from a scan
    2738              :          * tuple.
    2739              :          */
    2740           16 :         if (fsplan->scan.scanrelid == 0)
    2741            1 :             init_returning_filter(dmstate, fsplan->fdw_scan_tlist, rtindex);
    2742              :     }
    2743              : 
    2744              :     /*
    2745              :      * Prepare for processing of parameters used in remote query, if any.
    2746              :      */
    2747           72 :     numParams = list_length(fsplan->fdw_exprs);
    2748           72 :     dmstate->numParams = numParams;
    2749           72 :     if (numParams > 0)
    2750            0 :         prepare_query_params((PlanState *) node,
    2751              :                              fsplan->fdw_exprs,
    2752              :                              numParams,
    2753              :                              &dmstate->param_flinfo,
    2754              :                              &dmstate->param_exprs,
    2755              :                              &dmstate->param_values);
    2756              : }
    2757              : 
    2758              : /*
    2759              :  * postgresIterateDirectModify
    2760              :  *      Execute a direct foreign table modification
    2761              :  */
    2762              : static TupleTableSlot *
    2763          418 : postgresIterateDirectModify(ForeignScanState *node)
    2764              : {
    2765          418 :     PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state;
    2766          418 :     EState     *estate = node->ss.ps.state;
    2767          418 :     ResultRelInfo *resultRelInfo = node->resultRelInfo;
    2768              : 
    2769              :     /*
    2770              :      * If this is the first call after Begin, execute the statement.
    2771              :      */
    2772          418 :     if (dmstate->num_tuples == -1)
    2773           71 :         execute_dml_stmt(node);
    2774              : 
    2775              :     /*
    2776              :      * If the local query doesn't specify RETURNING, just clear tuple slot.
    2777              :      */
    2778          414 :     if (!resultRelInfo->ri_projectReturning)
    2779              :     {
    2780           50 :         TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
    2781           50 :         Instrumentation *instr = node->ss.ps.instrument;
    2782              : 
    2783              :         Assert(!dmstate->has_returning);
    2784              : 
    2785              :         /* Increment the command es_processed count if necessary. */
    2786           50 :         if (dmstate->set_processed)
    2787           50 :             estate->es_processed += dmstate->num_tuples;
    2788              : 
    2789              :         /* Increment the tuple count for EXPLAIN ANALYZE if necessary. */
    2790           50 :         if (instr)
    2791            0 :             instr->tuplecount += dmstate->num_tuples;
    2792              : 
    2793           50 :         return ExecClearTuple(slot);
    2794              :     }
    2795              : 
    2796              :     /*
    2797              :      * Get the next RETURNING tuple.
    2798              :      */
    2799          364 :     return get_returning_data(node);
    2800              : }
    2801              : 
    2802              : /*
    2803              :  * postgresEndDirectModify
    2804              :  *      Finish a direct foreign table modification
    2805              :  */
    2806              : static void
    2807           96 : postgresEndDirectModify(ForeignScanState *node)
    2808              : {
    2809           96 :     PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state;
    2810              : 
    2811              :     /* if dmstate is NULL, we are in EXPLAIN; nothing to do */
    2812           96 :     if (dmstate == NULL)
    2813           32 :         return;
    2814              : 
    2815              :     /* Release PGresult */
    2816           64 :     PQclear(dmstate->result);
    2817              : 
    2818              :     /* Release remote connection */
    2819           64 :     ReleaseConnection(dmstate->conn);
    2820           64 :     dmstate->conn = NULL;
    2821              : 
    2822              :     /* MemoryContext will be deleted automatically. */
    2823              : }
    2824              : 
    2825              : /*
    2826              :  * postgresExplainForeignScan
    2827              :  *      Produce extra output for EXPLAIN of a ForeignScan on a foreign table
    2828              :  */
    2829              : static void
    2830          396 : postgresExplainForeignScan(ForeignScanState *node, ExplainState *es)
    2831              : {
    2832          396 :     ForeignScan *plan = castNode(ForeignScan, node->ss.ps.plan);
    2833          396 :     List       *fdw_private = plan->fdw_private;
    2834              : 
    2835              :     /*
    2836              :      * Identify foreign scans that are really joins or upper relations.  The
    2837              :      * input looks something like "(1) LEFT JOIN (2)", and we must replace the
    2838              :      * digit string(s), which are RT indexes, with the correct relation names.
    2839              :      * We do that here, not when the plan is created, because we can't know
    2840              :      * what aliases ruleutils.c will assign at plan creation time.
    2841              :      */
    2842          396 :     if (list_length(fdw_private) > FdwScanPrivateRelations)
    2843              :     {
    2844              :         StringInfoData relations;
    2845              :         char       *rawrelations;
    2846              :         char       *ptr;
    2847              :         int         minrti,
    2848              :                     rtoffset;
    2849              : 
    2850          123 :         rawrelations = strVal(list_nth(fdw_private, FdwScanPrivateRelations));
    2851              : 
    2852              :         /*
    2853              :          * A difficulty with using a string representation of RT indexes is
    2854              :          * that setrefs.c won't update the string when flattening the
    2855              :          * rangetable.  To find out what rtoffset was applied, identify the
    2856              :          * minimum RT index appearing in the string and compare it to the
    2857              :          * minimum member of plan->fs_base_relids.  (We expect all the relids
    2858              :          * in the join will have been offset by the same amount; the Asserts
    2859              :          * below should catch it if that ever changes.)
    2860              :          */
    2861          123 :         minrti = INT_MAX;
    2862          123 :         ptr = rawrelations;
    2863         2915 :         while (*ptr)
    2864              :         {
    2865         2792 :             if (isdigit((unsigned char) *ptr))
    2866              :             {
    2867          243 :                 int         rti = strtol(ptr, &ptr, 10);
    2868              : 
    2869          243 :                 if (rti < minrti)
    2870          135 :                     minrti = rti;
    2871              :             }
    2872              :             else
    2873         2549 :                 ptr++;
    2874              :         }
    2875          123 :         rtoffset = bms_next_member(plan->fs_base_relids, -1) - minrti;
    2876              : 
    2877              :         /* Now we can translate the string */
    2878          123 :         initStringInfo(&relations);
    2879          123 :         ptr = rawrelations;
    2880         2915 :         while (*ptr)
    2881              :         {
    2882         2792 :             if (isdigit((unsigned char) *ptr))
    2883              :             {
    2884          243 :                 int         rti = strtol(ptr, &ptr, 10);
    2885              :                 RangeTblEntry *rte;
    2886              :                 char       *relname;
    2887              :                 char       *refname;
    2888              : 
    2889          243 :                 rti += rtoffset;
    2890              :                 Assert(bms_is_member(rti, plan->fs_base_relids));
    2891          243 :                 rte = rt_fetch(rti, es->rtable);
    2892              :                 Assert(rte->rtekind == RTE_RELATION);
    2893              :                 /* This logic should agree with explain.c's ExplainTargetRel */
    2894          243 :                 relname = get_rel_name(rte->relid);
    2895          243 :                 if (es->verbose)
    2896              :                 {
    2897              :                     char       *namespace;
    2898              : 
    2899          230 :                     namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid));
    2900          230 :                     appendStringInfo(&relations, "%s.%s",
    2901              :                                      quote_identifier(namespace),
    2902              :                                      quote_identifier(relname));
    2903              :                 }
    2904              :                 else
    2905           13 :                     appendStringInfoString(&relations,
    2906              :                                            quote_identifier(relname));
    2907          243 :                 refname = (char *) list_nth(es->rtable_names, rti - 1);
    2908          243 :                 if (refname == NULL)
    2909            0 :                     refname = rte->eref->aliasname;
    2910          243 :                 if (strcmp(refname, relname) != 0)
    2911          149 :                     appendStringInfo(&relations, " %s",
    2912              :                                      quote_identifier(refname));
    2913              :             }
    2914              :             else
    2915         2549 :                 appendStringInfoChar(&relations, *ptr++);
    2916              :         }
    2917          123 :         ExplainPropertyText("Relations", relations.data, es);
    2918              :     }
    2919              : 
    2920              :     /*
    2921              :      * Add remote query, when VERBOSE option is specified.
    2922              :      */
    2923          396 :     if (es->verbose)
    2924              :     {
    2925              :         char       *sql;
    2926              : 
    2927          360 :         sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
    2928          360 :         ExplainPropertyText("Remote SQL", sql, es);
    2929              :     }
    2930          396 : }
    2931              : 
    2932              : /*
    2933              :  * postgresExplainForeignModify
    2934              :  *      Produce extra output for EXPLAIN of a ModifyTable on a foreign table
    2935              :  */
    2936              : static void
    2937           46 : postgresExplainForeignModify(ModifyTableState *mtstate,
    2938              :                              ResultRelInfo *rinfo,
    2939              :                              List *fdw_private,
    2940              :                              int subplan_index,
    2941              :                              ExplainState *es)
    2942              : {
    2943           46 :     if (es->verbose)
    2944              :     {
    2945           46 :         char       *sql = strVal(list_nth(fdw_private,
    2946              :                                           FdwModifyPrivateUpdateSql));
    2947              : 
    2948           46 :         ExplainPropertyText("Remote SQL", sql, es);
    2949              : 
    2950              :         /*
    2951              :          * For INSERT we should always have batch size >= 1, but UPDATE and
    2952              :          * DELETE don't support batching so don't show the property.
    2953              :          */
    2954           46 :         if (rinfo->ri_BatchSize > 0)
    2955           13 :             ExplainPropertyInteger("Batch Size", NULL, rinfo->ri_BatchSize, es);
    2956              :     }
    2957           46 : }
    2958              : 
    2959              : /*
    2960              :  * postgresExplainDirectModify
    2961              :  *      Produce extra output for EXPLAIN of a ForeignScan that modifies a
    2962              :  *      foreign table directly
    2963              :  */
    2964              : static void
    2965           32 : postgresExplainDirectModify(ForeignScanState *node, ExplainState *es)
    2966              : {
    2967              :     List       *fdw_private;
    2968              :     char       *sql;
    2969              : 
    2970           32 :     if (es->verbose)
    2971              :     {
    2972           32 :         fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
    2973           32 :         sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql));
    2974           32 :         ExplainPropertyText("Remote SQL", sql, es);
    2975              :     }
    2976           32 : }
    2977              : 
    2978              : /*
    2979              :  * postgresExecForeignTruncate
    2980              :  *      Truncate one or more foreign tables
    2981              :  */
    2982              : static void
    2983           15 : postgresExecForeignTruncate(List *rels,
    2984              :                             DropBehavior behavior,
    2985              :                             bool restart_seqs)
    2986              : {
    2987           15 :     Oid         serverid = InvalidOid;
    2988           15 :     UserMapping *user = NULL;
    2989           15 :     PGconn     *conn = NULL;
    2990              :     StringInfoData sql;
    2991              :     ListCell   *lc;
    2992           15 :     bool        server_truncatable = true;
    2993              : 
    2994              :     /*
    2995              :      * By default, all postgres_fdw foreign tables are assumed truncatable.
    2996              :      * This can be overridden by a per-server setting, which in turn can be
    2997              :      * overridden by a per-table setting.
    2998              :      */
    2999           29 :     foreach(lc, rels)
    3000              :     {
    3001           17 :         ForeignServer *server = NULL;
    3002           17 :         Relation    rel = lfirst(lc);
    3003           17 :         ForeignTable *table = GetForeignTable(RelationGetRelid(rel));
    3004              :         ListCell   *cell;
    3005              :         bool        truncatable;
    3006              : 
    3007              :         /*
    3008              :          * First time through, determine whether the foreign server allows
    3009              :          * truncates. Since all specified foreign tables are assumed to belong
    3010              :          * to the same foreign server, this result can be used for other
    3011              :          * foreign tables.
    3012              :          */
    3013           17 :         if (!OidIsValid(serverid))
    3014              :         {
    3015           15 :             serverid = table->serverid;
    3016           15 :             server = GetForeignServer(serverid);
    3017              : 
    3018           60 :             foreach(cell, server->options)
    3019              :             {
    3020           48 :                 DefElem    *defel = (DefElem *) lfirst(cell);
    3021              : 
    3022           48 :                 if (strcmp(defel->defname, "truncatable") == 0)
    3023              :                 {
    3024            3 :                     server_truncatable = defGetBoolean(defel);
    3025            3 :                     break;
    3026              :                 }
    3027              :             }
    3028              :         }
    3029              : 
    3030              :         /*
    3031              :          * Confirm that all specified foreign tables belong to the same
    3032              :          * foreign server.
    3033              :          */
    3034              :         Assert(table->serverid == serverid);
    3035              : 
    3036              :         /* Determine whether this foreign table allows truncations */
    3037           17 :         truncatable = server_truncatable;
    3038           34 :         foreach(cell, table->options)
    3039              :         {
    3040           24 :             DefElem    *defel = (DefElem *) lfirst(cell);
    3041              : 
    3042           24 :             if (strcmp(defel->defname, "truncatable") == 0)
    3043              :             {
    3044            7 :                 truncatable = defGetBoolean(defel);
    3045            7 :                 break;
    3046              :             }
    3047              :         }
    3048              : 
    3049           17 :         if (!truncatable)
    3050            3 :             ereport(ERROR,
    3051              :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    3052              :                      errmsg("foreign table \"%s\" does not allow truncates",
    3053              :                             RelationGetRelationName(rel))));
    3054              :     }
    3055              :     Assert(OidIsValid(serverid));
    3056              : 
    3057              :     /*
    3058              :      * Get connection to the foreign server.  Connection manager will
    3059              :      * establish new connection if necessary.
    3060              :      */
    3061           12 :     user = GetUserMapping(GetUserId(), serverid);
    3062           12 :     conn = GetConnection(user, false, NULL);
    3063              : 
    3064              :     /* Construct the TRUNCATE command string */
    3065           12 :     initStringInfo(&sql);
    3066           12 :     deparseTruncateSql(&sql, rels, behavior, restart_seqs);
    3067              : 
    3068              :     /* Issue the TRUNCATE command to remote server */
    3069           12 :     do_sql_command(conn, sql.data);
    3070              : 
    3071           11 :     pfree(sql.data);
    3072           11 : }
    3073              : 
    3074              : /*
    3075              :  * estimate_path_cost_size
    3076              :  *      Get cost and size estimates for a foreign scan on given foreign relation
    3077              :  *      either a base relation or a join between foreign relations or an upper
    3078              :  *      relation containing foreign relations.
    3079              :  *
    3080              :  * param_join_conds are the parameterization clauses with outer relations.
    3081              :  * pathkeys specify the expected sort order if any for given path being costed.
    3082              :  * fpextra specifies additional post-scan/join-processing steps such as the
    3083              :  * final sort and the LIMIT restriction.
    3084              :  *
    3085              :  * The function returns the cost and size estimates in p_rows, p_width,
    3086              :  * p_disabled_nodes, p_startup_cost and p_total_cost variables.
    3087              :  */
    3088              : static void
    3089         2717 : estimate_path_cost_size(PlannerInfo *root,
    3090              :                         RelOptInfo *foreignrel,
    3091              :                         List *param_join_conds,
    3092              :                         List *pathkeys,
    3093              :                         PgFdwPathExtraData *fpextra,
    3094              :                         double *p_rows, int *p_width,
    3095              :                         int *p_disabled_nodes,
    3096              :                         Cost *p_startup_cost, Cost *p_total_cost)
    3097              : {
    3098         2717 :     PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
    3099              :     double      rows;
    3100              :     double      retrieved_rows;
    3101              :     int         width;
    3102         2717 :     int         disabled_nodes = 0;
    3103              :     Cost        startup_cost;
    3104              :     Cost        total_cost;
    3105              : 
    3106              :     /* Make sure the core code has set up the relation's reltarget */
    3107              :     Assert(foreignrel->reltarget);
    3108              : 
    3109              :     /*
    3110              :      * If the table or the server is configured to use remote estimates,
    3111              :      * connect to the foreign server and execute EXPLAIN to estimate the
    3112              :      * number of rows selected by the restriction+join clauses.  Otherwise,
    3113              :      * estimate rows using whatever statistics we have locally, in a way
    3114              :      * similar to ordinary tables.
    3115              :      */
    3116         2717 :     if (fpinfo->use_remote_estimate)
    3117              :     {
    3118              :         List       *remote_param_join_conds;
    3119              :         List       *local_param_join_conds;
    3120              :         StringInfoData sql;
    3121              :         PGconn     *conn;
    3122              :         Selectivity local_sel;
    3123              :         QualCost    local_cost;
    3124         1319 :         List       *fdw_scan_tlist = NIL;
    3125              :         List       *remote_conds;
    3126              : 
    3127              :         /* Required only to be passed to deparseSelectStmtForRel */
    3128              :         List       *retrieved_attrs;
    3129              : 
    3130              :         /*
    3131              :          * param_join_conds might contain both clauses that are safe to send
    3132              :          * across, and clauses that aren't.
    3133              :          */
    3134         1319 :         classifyConditions(root, foreignrel, param_join_conds,
    3135              :                            &remote_param_join_conds, &local_param_join_conds);
    3136              : 
    3137              :         /* Build the list of columns to be fetched from the foreign server. */
    3138         1319 :         if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
    3139          527 :             fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
    3140              :         else
    3141          792 :             fdw_scan_tlist = NIL;
    3142              : 
    3143              :         /*
    3144              :          * The complete list of remote conditions includes everything from
    3145              :          * baserestrictinfo plus any extra join_conds relevant to this
    3146              :          * particular path.
    3147              :          */
    3148         1319 :         remote_conds = list_concat(remote_param_join_conds,
    3149         1319 :                                    fpinfo->remote_conds);
    3150              : 
    3151              :         /*
    3152              :          * Construct EXPLAIN query including the desired SELECT, FROM, and
    3153              :          * WHERE clauses. Params and other-relation Vars are replaced by dummy
    3154              :          * values, so don't request params_list.
    3155              :          */
    3156         1319 :         initStringInfo(&sql);
    3157         1319 :         appendStringInfoString(&sql, "EXPLAIN ");
    3158         1401 :         deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
    3159              :                                 remote_conds, pathkeys,
    3160           41 :                                 fpextra ? fpextra->has_final_sort : false,
    3161           41 :                                 fpextra ? fpextra->has_limit : false,
    3162              :                                 false, &retrieved_attrs, NULL);
    3163              : 
    3164              :         /* Get the remote estimate */
    3165         1319 :         conn = GetConnection(fpinfo->user, false, NULL);
    3166         1319 :         get_remote_estimate(sql.data, conn, &rows, &width,
    3167              :                             &startup_cost, &total_cost);
    3168         1319 :         ReleaseConnection(conn);
    3169              : 
    3170         1319 :         retrieved_rows = rows;
    3171              : 
    3172              :         /* Factor in the selectivity of the locally-checked quals */
    3173         1319 :         local_sel = clauselist_selectivity(root,
    3174              :                                            local_param_join_conds,
    3175         1319 :                                            foreignrel->relid,
    3176              :                                            JOIN_INNER,
    3177              :                                            NULL);
    3178         1319 :         local_sel *= fpinfo->local_conds_sel;
    3179              : 
    3180         1319 :         rows = clamp_row_est(rows * local_sel);
    3181              : 
    3182              :         /* Add in the eval cost of the locally-checked quals */
    3183         1319 :         startup_cost += fpinfo->local_conds_cost.startup;
    3184         1319 :         total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
    3185         1319 :         cost_qual_eval(&local_cost, local_param_join_conds, root);
    3186         1319 :         startup_cost += local_cost.startup;
    3187         1319 :         total_cost += local_cost.per_tuple * retrieved_rows;
    3188              : 
    3189              :         /*
    3190              :          * Add in tlist eval cost for each output row.  In case of an
    3191              :          * aggregate, some of the tlist expressions such as grouping
    3192              :          * expressions will be evaluated remotely, so adjust the costs.
    3193              :          */
    3194         1319 :         startup_cost += foreignrel->reltarget->cost.startup;
    3195         1319 :         total_cost += foreignrel->reltarget->cost.startup;
    3196         1319 :         total_cost += foreignrel->reltarget->cost.per_tuple * rows;
    3197         1319 :         if (IS_UPPER_REL(foreignrel))
    3198              :         {
    3199              :             QualCost    tlist_cost;
    3200              : 
    3201           40 :             cost_qual_eval(&tlist_cost, fdw_scan_tlist, root);
    3202           40 :             startup_cost -= tlist_cost.startup;
    3203           40 :             total_cost -= tlist_cost.startup;
    3204           40 :             total_cost -= tlist_cost.per_tuple * rows;
    3205              :         }
    3206              :     }
    3207              :     else
    3208              :     {
    3209         1398 :         Cost        run_cost = 0;
    3210              : 
    3211              :         /*
    3212              :          * We don't support join conditions in this mode (hence, no
    3213              :          * parameterized paths can be made).
    3214              :          */
    3215              :         Assert(param_join_conds == NIL);
    3216              : 
    3217              :         /*
    3218              :          * We will come here again and again with different set of pathkeys or
    3219              :          * additional post-scan/join-processing steps that caller wants to
    3220              :          * cost.  We don't need to calculate the cost/size estimates for the
    3221              :          * underlying scan, join, or grouping each time.  Instead, use those
    3222              :          * estimates if we have cached them already.
    3223              :          */
    3224         1398 :         if (fpinfo->rel_startup_cost >= 0 && fpinfo->rel_total_cost >= 0)
    3225              :         {
    3226              :             Assert(fpinfo->retrieved_rows >= 0);
    3227              : 
    3228          310 :             rows = fpinfo->rows;
    3229          310 :             retrieved_rows = fpinfo->retrieved_rows;
    3230          310 :             width = fpinfo->width;
    3231          310 :             startup_cost = fpinfo->rel_startup_cost;
    3232          310 :             run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
    3233              : 
    3234              :             /*
    3235              :              * If we estimate the costs of a foreign scan or a foreign join
    3236              :              * with additional post-scan/join-processing steps, the scan or
    3237              :              * join costs obtained from the cache wouldn't yet contain the
    3238              :              * eval costs for the final scan/join target, which would've been
    3239              :              * updated by apply_scanjoin_target_to_paths(); add the eval costs
    3240              :              * now.
    3241              :              */
    3242          310 :             if (fpextra && !IS_UPPER_REL(foreignrel))
    3243              :             {
    3244              :                 /* Shouldn't get here unless we have LIMIT */
    3245              :                 Assert(fpextra->has_limit);
    3246              :                 Assert(foreignrel->reloptkind == RELOPT_BASEREL ||
    3247              :                        foreignrel->reloptkind == RELOPT_JOINREL);
    3248           90 :                 startup_cost += foreignrel->reltarget->cost.startup;
    3249           90 :                 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
    3250              :             }
    3251              :         }
    3252         1088 :         else if (IS_JOIN_REL(foreignrel))
    3253          112 :         {
    3254              :             PgFdwRelationInfo *fpinfo_i;
    3255              :             PgFdwRelationInfo *fpinfo_o;
    3256              :             QualCost    join_cost;
    3257              :             QualCost    remote_conds_cost;
    3258              :             double      nrows;
    3259              : 
    3260              :             /* Use rows/width estimates made by the core code. */
    3261          112 :             rows = foreignrel->rows;
    3262          112 :             width = foreignrel->reltarget->width;
    3263              : 
    3264              :             /* For join we expect inner and outer relations set */
    3265              :             Assert(fpinfo->innerrel && fpinfo->outerrel);
    3266              : 
    3267          112 :             fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
    3268          112 :             fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
    3269              : 
    3270              :             /* Estimate of number of rows in cross product */
    3271          112 :             nrows = fpinfo_i->rows * fpinfo_o->rows;
    3272              : 
    3273              :             /*
    3274              :              * Back into an estimate of the number of retrieved rows.  Just in
    3275              :              * case this is nuts, clamp to at most nrows.
    3276              :              */
    3277          112 :             retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
    3278          112 :             retrieved_rows = Min(retrieved_rows, nrows);
    3279              : 
    3280              :             /*
    3281              :              * The cost of foreign join is estimated as cost of generating
    3282              :              * rows for the joining relations + cost for applying quals on the
    3283              :              * rows.
    3284              :              */
    3285              : 
    3286              :             /*
    3287              :              * Calculate the cost of clauses pushed down to the foreign server
    3288              :              */
    3289          112 :             cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
    3290              :             /* Calculate the cost of applying join clauses */
    3291          112 :             cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
    3292              : 
    3293              :             /*
    3294              :              * Startup cost includes startup cost of joining relations and the
    3295              :              * startup cost for join and other clauses. We do not include the
    3296              :              * startup cost specific to join strategy (e.g. setting up hash
    3297              :              * tables) since we do not know what strategy the foreign server
    3298              :              * is going to use.
    3299              :              */
    3300          112 :             startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
    3301          112 :             startup_cost += join_cost.startup;
    3302          112 :             startup_cost += remote_conds_cost.startup;
    3303          112 :             startup_cost += fpinfo->local_conds_cost.startup;
    3304              : 
    3305              :             /*
    3306              :              * Run time cost includes:
    3307              :              *
    3308              :              * 1. Run time cost (total_cost - startup_cost) of relations being
    3309              :              * joined
    3310              :              *
    3311              :              * 2. Run time cost of applying join clauses on the cross product
    3312              :              * of the joining relations.
    3313              :              *
    3314              :              * 3. Run time cost of applying pushed down other clauses on the
    3315              :              * result of join
    3316              :              *
    3317              :              * 4. Run time cost of applying nonpushable other clauses locally
    3318              :              * on the result fetched from the foreign server.
    3319              :              */
    3320          112 :             run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
    3321          112 :             run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
    3322          112 :             run_cost += nrows * join_cost.per_tuple;
    3323          112 :             nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
    3324          112 :             run_cost += nrows * remote_conds_cost.per_tuple;
    3325          112 :             run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
    3326              : 
    3327              :             /* Add in tlist eval cost for each output row */
    3328          112 :             startup_cost += foreignrel->reltarget->cost.startup;
    3329          112 :             run_cost += foreignrel->reltarget->cost.per_tuple * rows;
    3330              :         }
    3331          976 :         else if (IS_UPPER_REL(foreignrel))
    3332           99 :         {
    3333           99 :             RelOptInfo *outerrel = fpinfo->outerrel;
    3334              :             PgFdwRelationInfo *ofpinfo;
    3335           99 :             AggClauseCosts aggcosts = {0};
    3336              :             double      input_rows;
    3337              :             int         numGroupCols;
    3338           99 :             double      numGroups = 1;
    3339              : 
    3340              :             /* The upper relation should have its outer relation set */
    3341              :             Assert(outerrel);
    3342              :             /* and that outer relation should have its reltarget set */
    3343              :             Assert(outerrel->reltarget);
    3344              : 
    3345              :             /*
    3346              :              * This cost model is mixture of costing done for sorted and
    3347              :              * hashed aggregates in cost_agg().  We are not sure which
    3348              :              * strategy will be considered at remote side, thus for
    3349              :              * simplicity, we put all startup related costs in startup_cost
    3350              :              * and all finalization and run cost are added in total_cost.
    3351              :              */
    3352              : 
    3353           99 :             ofpinfo = (PgFdwRelationInfo *) outerrel->fdw_private;
    3354              : 
    3355              :             /* Get rows from input rel */
    3356           99 :             input_rows = ofpinfo->rows;
    3357              : 
    3358              :             /* Collect statistics about aggregates for estimating costs. */
    3359           99 :             if (root->parse->hasAggs)
    3360              :             {
    3361           95 :                 get_agg_clause_costs(root, AGGSPLIT_SIMPLE, &aggcosts);
    3362              :             }
    3363              : 
    3364              :             /* Get number of grouping columns and possible number of groups */
    3365           99 :             numGroupCols = list_length(root->processed_groupClause);
    3366           99 :             numGroups = estimate_num_groups(root,
    3367              :                                             get_sortgrouplist_exprs(root->processed_groupClause,
    3368              :                                                                     fpinfo->grouped_tlist),
    3369              :                                             input_rows, NULL, NULL);
    3370              : 
    3371              :             /*
    3372              :              * Get the retrieved_rows and rows estimates.  If there are HAVING
    3373              :              * quals, account for their selectivity.
    3374              :              */
    3375           99 :             if (root->hasHavingQual)
    3376              :             {
    3377              :                 /* Factor in the selectivity of the remotely-checked quals */
    3378              :                 retrieved_rows =
    3379           14 :                     clamp_row_est(numGroups *
    3380           14 :                                   clauselist_selectivity(root,
    3381              :                                                          fpinfo->remote_conds,
    3382              :                                                          0,
    3383              :                                                          JOIN_INNER,
    3384              :                                                          NULL));
    3385              :                 /* Factor in the selectivity of the locally-checked quals */
    3386           14 :                 rows = clamp_row_est(retrieved_rows * fpinfo->local_conds_sel);
    3387              :             }
    3388              :             else
    3389              :             {
    3390           85 :                 rows = retrieved_rows = numGroups;
    3391              :             }
    3392              : 
    3393              :             /* Use width estimate made by the core code. */
    3394           99 :             width = foreignrel->reltarget->width;
    3395              : 
    3396              :             /*-----
    3397              :              * Startup cost includes:
    3398              :              *    1. Startup cost for underneath input relation, adjusted for
    3399              :              *       tlist replacement by apply_scanjoin_target_to_paths()
    3400              :              *    2. Cost of performing aggregation, per cost_agg()
    3401              :              *-----
    3402              :              */
    3403           99 :             startup_cost = ofpinfo->rel_startup_cost;
    3404           99 :             startup_cost += outerrel->reltarget->cost.startup;
    3405           99 :             startup_cost += aggcosts.transCost.startup;
    3406           99 :             startup_cost += aggcosts.transCost.per_tuple * input_rows;
    3407           99 :             startup_cost += aggcosts.finalCost.startup;
    3408           99 :             startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
    3409              : 
    3410              :             /*-----
    3411              :              * Run time cost includes:
    3412              :              *    1. Run time cost of underneath input relation, adjusted for
    3413              :              *       tlist replacement by apply_scanjoin_target_to_paths()
    3414              :              *    2. Run time cost of performing aggregation, per cost_agg()
    3415              :              *-----
    3416              :              */
    3417           99 :             run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
    3418           99 :             run_cost += outerrel->reltarget->cost.per_tuple * input_rows;
    3419           99 :             run_cost += aggcosts.finalCost.per_tuple * numGroups;
    3420           99 :             run_cost += cpu_tuple_cost * numGroups;
    3421              : 
    3422              :             /* Account for the eval cost of HAVING quals, if any */
    3423           99 :             if (root->hasHavingQual)
    3424              :             {
    3425              :                 QualCost    remote_cost;
    3426              : 
    3427              :                 /* Add in the eval cost of the remotely-checked quals */
    3428           14 :                 cost_qual_eval(&remote_cost, fpinfo->remote_conds, root);
    3429           14 :                 startup_cost += remote_cost.startup;
    3430           14 :                 run_cost += remote_cost.per_tuple * numGroups;
    3431              :                 /* Add in the eval cost of the locally-checked quals */
    3432           14 :                 startup_cost += fpinfo->local_conds_cost.startup;
    3433           14 :                 run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
    3434              :             }
    3435              : 
    3436              :             /* Add in tlist eval cost for each output row */
    3437           99 :             startup_cost += foreignrel->reltarget->cost.startup;
    3438           99 :             run_cost += foreignrel->reltarget->cost.per_tuple * rows;
    3439              :         }
    3440              :         else
    3441              :         {
    3442              :             Cost        cpu_per_tuple;
    3443              : 
    3444              :             /* Use rows/width estimates made by set_baserel_size_estimates. */
    3445          877 :             rows = foreignrel->rows;
    3446          877 :             width = foreignrel->reltarget->width;
    3447              : 
    3448              :             /*
    3449              :              * Back into an estimate of the number of retrieved rows.  Just in
    3450              :              * case this is nuts, clamp to at most foreignrel->tuples.
    3451              :              */
    3452          877 :             retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
    3453          877 :             retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
    3454              : 
    3455              :             /*
    3456              :              * Cost as though this were a seqscan, which is pessimistic.  We
    3457              :              * effectively imagine the local_conds are being evaluated
    3458              :              * remotely, too.
    3459              :              */
    3460          877 :             startup_cost = 0;
    3461          877 :             run_cost = 0;
    3462          877 :             run_cost += seq_page_cost * foreignrel->pages;
    3463              : 
    3464          877 :             startup_cost += foreignrel->baserestrictcost.startup;
    3465          877 :             cpu_per_tuple = cpu_tuple_cost + foreignrel->baserestrictcost.per_tuple;
    3466          877 :             run_cost += cpu_per_tuple * foreignrel->tuples;
    3467              : 
    3468              :             /* Add in tlist eval cost for each output row */
    3469          877 :             startup_cost += foreignrel->reltarget->cost.startup;
    3470          877 :             run_cost += foreignrel->reltarget->cost.per_tuple * rows;
    3471              :         }
    3472              : 
    3473              :         /*
    3474              :          * Without remote estimates, we have no real way to estimate the cost
    3475              :          * of generating sorted output.  It could be free if the query plan
    3476              :          * the remote side would have chosen generates properly-sorted output
    3477              :          * anyway, but in most cases it will cost something.  Estimate a value
    3478              :          * high enough that we won't pick the sorted path when the ordering
    3479              :          * isn't locally useful, but low enough that we'll err on the side of
    3480              :          * pushing down the ORDER BY clause when it's useful to do so.
    3481              :          */
    3482         1398 :         if (pathkeys != NIL)
    3483              :         {
    3484          254 :             if (IS_UPPER_REL(foreignrel))
    3485              :             {
    3486              :                 Assert(foreignrel->reloptkind == RELOPT_UPPER_REL &&
    3487              :                        fpinfo->stage == UPPERREL_GROUP_AGG);
    3488              : 
    3489              :                 /*
    3490              :                  * We can only get here when this function is called from
    3491              :                  * add_foreign_ordered_paths() or add_foreign_final_paths();
    3492              :                  * in which cases, the passed-in fpextra should not be NULL.
    3493              :                  */
    3494              :                 Assert(fpextra);
    3495           30 :                 adjust_foreign_grouping_path_cost(root, pathkeys,
    3496              :                                                   retrieved_rows, width,
    3497              :                                                   fpextra->limit_tuples,
    3498              :                                                   &disabled_nodes,
    3499              :                                                   &startup_cost, &run_cost);
    3500              :             }
    3501              :             else
    3502              :             {
    3503          224 :                 startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
    3504          224 :                 run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
    3505              :             }
    3506              :         }
    3507              : 
    3508         1398 :         total_cost = startup_cost + run_cost;
    3509              : 
    3510              :         /* Adjust the cost estimates if we have LIMIT */
    3511         1398 :         if (fpextra && fpextra->has_limit)
    3512              :         {
    3513           92 :             adjust_limit_rows_costs(&rows, &startup_cost, &total_cost,
    3514              :                                     fpextra->offset_est, fpextra->count_est);
    3515           92 :             retrieved_rows = rows;
    3516              :         }
    3517              :     }
    3518              : 
    3519              :     /*
    3520              :      * If this includes the final sort step, the given target, which will be
    3521              :      * applied to the resulting path, might have different expressions from
    3522              :      * the foreignrel's reltarget (see make_sort_input_target()); adjust tlist
    3523              :      * eval costs.
    3524              :      */
    3525         2717 :     if (fpextra && fpextra->has_final_sort &&
    3526          109 :         fpextra->target != foreignrel->reltarget)
    3527              :     {
    3528            6 :         QualCost    oldcost = foreignrel->reltarget->cost;
    3529            6 :         QualCost    newcost = fpextra->target->cost;
    3530              : 
    3531            6 :         startup_cost += newcost.startup - oldcost.startup;
    3532            6 :         total_cost += newcost.startup - oldcost.startup;
    3533            6 :         total_cost += (newcost.per_tuple - oldcost.per_tuple) * rows;
    3534              :     }
    3535              : 
    3536              :     /*
    3537              :      * Cache the retrieved rows and cost estimates for scans, joins, or
    3538              :      * groupings without any parameterization, pathkeys, or additional
    3539              :      * post-scan/join-processing steps, before adding the costs for
    3540              :      * transferring data from the foreign server.  These estimates are useful
    3541              :      * for costing remote joins involving this relation or costing other
    3542              :      * remote operations on this relation such as remote sorts and remote
    3543              :      * LIMIT restrictions, when the costs can not be obtained from the foreign
    3544              :      * server.  This function will be called at least once for every foreign
    3545              :      * relation without any parameterization, pathkeys, or additional
    3546              :      * post-scan/join-processing steps.
    3547              :      */
    3548         2717 :     if (pathkeys == NIL && param_join_conds == NIL && fpextra == NULL)
    3549              :     {
    3550         1654 :         fpinfo->retrieved_rows = retrieved_rows;
    3551         1654 :         fpinfo->rel_startup_cost = startup_cost;
    3552         1654 :         fpinfo->rel_total_cost = total_cost;
    3553              :     }
    3554              : 
    3555              :     /*
    3556              :      * Add some additional cost factors to account for connection overhead
    3557              :      * (fdw_startup_cost), transferring data across the network
    3558              :      * (fdw_tuple_cost per retrieved row), and local manipulation of the data
    3559              :      * (cpu_tuple_cost per retrieved row).
    3560              :      */
    3561         2717 :     startup_cost += fpinfo->fdw_startup_cost;
    3562         2717 :     total_cost += fpinfo->fdw_startup_cost;
    3563         2717 :     total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
    3564         2717 :     total_cost += cpu_tuple_cost * retrieved_rows;
    3565              : 
    3566              :     /*
    3567              :      * If we have LIMIT, we should prefer performing the restriction remotely
    3568              :      * rather than locally, as the former avoids extra row fetches from the
    3569              :      * remote that the latter might cause.  But since the core code doesn't
    3570              :      * account for such fetches when estimating the costs of the local
    3571              :      * restriction (see create_limit_path()), there would be no difference
    3572              :      * between the costs of the local restriction and the costs of the remote
    3573              :      * restriction estimated above if we don't use remote estimates (except
    3574              :      * for the case where the foreignrel is a grouping relation, the given
    3575              :      * pathkeys is not NIL, and the effects of a bounded sort for that rel is
    3576              :      * accounted for in costing the remote restriction).  Tweak the costs of
    3577              :      * the remote restriction to ensure we'll prefer it if LIMIT is a useful
    3578              :      * one.
    3579              :      */
    3580         2717 :     if (!fpinfo->use_remote_estimate &&
    3581          122 :         fpextra && fpextra->has_limit &&
    3582           92 :         fpextra->limit_tuples > 0 &&
    3583           92 :         fpextra->limit_tuples < fpinfo->rows)
    3584              :     {
    3585              :         Assert(fpinfo->rows > 0);
    3586           86 :         total_cost -= (total_cost - startup_cost) * 0.05 *
    3587           86 :             (fpinfo->rows - fpextra->limit_tuples) / fpinfo->rows;
    3588              :     }
    3589              : 
    3590              :     /* Return results. */
    3591         2717 :     *p_rows = rows;
    3592         2717 :     *p_width = width;
    3593         2717 :     *p_disabled_nodes = disabled_nodes;
    3594         2717 :     *p_startup_cost = startup_cost;
    3595         2717 :     *p_total_cost = total_cost;
    3596         2717 : }
    3597              : 
    3598              : /*
    3599              :  * Estimate costs of executing a SQL statement remotely.
    3600              :  * The given "sql" must be an EXPLAIN command.
    3601              :  */
    3602              : static void
    3603         1319 : get_remote_estimate(const char *sql, PGconn *conn,
    3604              :                     double *rows, int *width,
    3605              :                     Cost *startup_cost, Cost *total_cost)
    3606              : {
    3607              :     PGresult   *res;
    3608              :     char       *line;
    3609              :     char       *p;
    3610              :     int         n;
    3611              : 
    3612              :     /*
    3613              :      * Execute EXPLAIN remotely.
    3614              :      */
    3615         1319 :     res = pgfdw_exec_query(conn, sql, NULL);
    3616         1319 :     if (PQresultStatus(res) != PGRES_TUPLES_OK)
    3617            0 :         pgfdw_report_error(res, conn, sql);
    3618              : 
    3619              :     /*
    3620              :      * Extract cost numbers for topmost plan node.  Note we search for a left
    3621              :      * paren from the end of the line to avoid being confused by other uses of
    3622              :      * parentheses.
    3623              :      */
    3624         1319 :     line = PQgetvalue(res, 0, 0);
    3625         1319 :     p = strrchr(line, '(');
    3626         1319 :     if (p == NULL)
    3627            0 :         elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
    3628         1319 :     n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
    3629              :                startup_cost, total_cost, rows, width);
    3630         1319 :     if (n != 4)
    3631            0 :         elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
    3632         1319 :     PQclear(res);
    3633         1319 : }
    3634              : 
    3635              : /*
    3636              :  * Adjust the cost estimates of a foreign grouping path to include the cost of
    3637              :  * generating properly-sorted output.
    3638              :  */
    3639              : static void
    3640           30 : adjust_foreign_grouping_path_cost(PlannerInfo *root,
    3641              :                                   List *pathkeys,
    3642              :                                   double retrieved_rows,
    3643              :                                   double width,
    3644              :                                   double limit_tuples,
    3645              :                                   int *p_disabled_nodes,
    3646              :                                   Cost *p_startup_cost,
    3647              :                                   Cost *p_run_cost)
    3648              : {
    3649              :     /*
    3650              :      * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
    3651              :      * side is unlikely to generate properly-sorted output, so it would need
    3652              :      * an explicit sort; adjust the given costs with cost_sort().  Likewise,
    3653              :      * if the GROUP BY clause is sort-able but isn't a superset of the given
    3654              :      * pathkeys, adjust the costs with that function.  Otherwise, adjust the
    3655              :      * costs by applying the same heuristic as for the scan or join case.
    3656              :      */
    3657           30 :     if (!grouping_is_sortable(root->processed_groupClause) ||
    3658           30 :         !pathkeys_contained_in(pathkeys, root->group_pathkeys))
    3659           22 :     {
    3660              :         Path        sort_path;  /* dummy for result of cost_sort */
    3661              : 
    3662           22 :         cost_sort(&sort_path,
    3663              :                   root,
    3664              :                   pathkeys,
    3665              :                   0,
    3666           22 :                   *p_startup_cost + *p_run_cost,
    3667              :                   retrieved_rows,
    3668              :                   width,
    3669              :                   0.0,
    3670              :                   work_mem,
    3671              :                   limit_tuples);
    3672              : 
    3673           22 :         *p_startup_cost = sort_path.startup_cost;
    3674           22 :         *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
    3675              :     }
    3676              :     else
    3677              :     {
    3678              :         /*
    3679              :          * The default extra cost seems too large for foreign-grouping cases;
    3680              :          * add 1/4th of that default.
    3681              :          */
    3682            8 :         double      sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
    3683              :                                              - 1.0) * 0.25;
    3684              : 
    3685            8 :         *p_startup_cost *= sort_multiplier;
    3686            8 :         *p_run_cost *= sort_multiplier;
    3687              :     }
    3688           30 : }
    3689              : 
    3690              : /*
    3691              :  * Detect whether we want to process an EquivalenceClass member.
    3692              :  *
    3693              :  * This is a callback for use by generate_implied_equalities_for_column.
    3694              :  */
    3695              : static bool
    3696          310 : ec_member_matches_foreign(PlannerInfo *root, RelOptInfo *rel,
    3697              :                           EquivalenceClass *ec, EquivalenceMember *em,
    3698              :                           void *arg)
    3699              : {
    3700          310 :     ec_member_foreign_arg *state = (ec_member_foreign_arg *) arg;
    3701          310 :     Expr       *expr = em->em_expr;
    3702              : 
    3703              :     /*
    3704              :      * If we've identified what we're processing in the current scan, we only
    3705              :      * want to match that expression.
    3706              :      */
    3707          310 :     if (state->current != NULL)
    3708            0 :         return equal(expr, state->current);
    3709              : 
    3710              :     /*
    3711              :      * Otherwise, ignore anything we've already processed.
    3712              :      */
    3713          310 :     if (list_member(state->already_used, expr))
    3714          163 :         return false;
    3715              : 
    3716              :     /* This is the new target to process. */
    3717          147 :     state->current = expr;
    3718          147 :     return true;
    3719              : }
    3720              : 
    3721              : /*
    3722              :  * Create cursor for node's query with current parameter values.
    3723              :  */
    3724              : static void
    3725          832 : create_cursor(ForeignScanState *node)
    3726              : {
    3727          832 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    3728          832 :     ExprContext *econtext = node->ss.ps.ps_ExprContext;
    3729          832 :     int         numParams = fsstate->numParams;
    3730          832 :     const char **values = fsstate->param_values;
    3731          832 :     PGconn     *conn = fsstate->conn;
    3732              :     StringInfoData buf;
    3733              :     PGresult   *res;
    3734              : 
    3735              :     /* First, process a pending asynchronous request, if any. */
    3736          832 :     if (fsstate->conn_state->pendingAreq)
    3737            1 :         process_pending_request(fsstate->conn_state->pendingAreq);
    3738              : 
    3739              :     /*
    3740              :      * Construct array of query parameter values in text format.  We do the
    3741              :      * conversions in the short-lived per-tuple context, so as not to cause a
    3742              :      * memory leak over repeated scans.
    3743              :      */
    3744          832 :     if (numParams > 0)
    3745              :     {
    3746              :         MemoryContext oldcontext;
    3747              : 
    3748          350 :         oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
    3749              : 
    3750          350 :         process_query_params(econtext,
    3751              :                              fsstate->param_flinfo,
    3752              :                              fsstate->param_exprs,
    3753              :                              values);
    3754              : 
    3755          350 :         MemoryContextSwitchTo(oldcontext);
    3756              :     }
    3757              : 
    3758              :     /* Construct the DECLARE CURSOR command */
    3759          832 :     initStringInfo(&buf);
    3760          832 :     appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
    3761              :                      fsstate->cursor_number, fsstate->query);
    3762              : 
    3763              :     /*
    3764              :      * Notice that we pass NULL for paramTypes, thus forcing the remote server
    3765              :      * to infer types for all parameters.  Since we explicitly cast every
    3766              :      * parameter (see deparse.c), the "inference" is trivial and will produce
    3767              :      * the desired result.  This allows us to avoid assuming that the remote
    3768              :      * server has the same OIDs we do for the parameters' types.
    3769              :      */
    3770          832 :     if (!PQsendQueryParams(conn, buf.data, numParams,
    3771              :                            NULL, values, NULL, NULL, 0))
    3772            0 :         pgfdw_report_error(NULL, conn, buf.data);
    3773              : 
    3774              :     /*
    3775              :      * Get the result, and check for success.
    3776              :      */
    3777          832 :     res = pgfdw_get_result(conn);
    3778          831 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
    3779            3 :         pgfdw_report_error(res, conn, fsstate->query);
    3780          828 :     PQclear(res);
    3781              : 
    3782              :     /* Mark the cursor as created, and show no tuples have been retrieved */
    3783          828 :     fsstate->cursor_exists = true;
    3784          828 :     fsstate->tuples = NULL;
    3785          828 :     fsstate->num_tuples = 0;
    3786          828 :     fsstate->next_tuple = 0;
    3787          828 :     fsstate->fetch_ct_2 = 0;
    3788          828 :     fsstate->eof_reached = false;
    3789              : 
    3790              :     /* Clean up */
    3791          828 :     pfree(buf.data);
    3792          828 : }
    3793              : 
    3794              : /*
    3795              :  * Fetch some more rows from the node's cursor.
    3796              :  */
    3797              : static void
    3798         1499 : fetch_more_data(ForeignScanState *node)
    3799              : {
    3800         1499 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    3801         1499 :     PGconn     *conn = fsstate->conn;
    3802              :     PGresult   *res;
    3803              :     int         numrows;
    3804              :     int         i;
    3805              :     MemoryContext oldcontext;
    3806              : 
    3807              :     /*
    3808              :      * We'll store the tuples in the batch_cxt.  First, flush the previous
    3809              :      * batch.
    3810              :      */
    3811         1499 :     fsstate->tuples = NULL;
    3812         1499 :     MemoryContextReset(fsstate->batch_cxt);
    3813         1499 :     oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
    3814              : 
    3815         1499 :     if (fsstate->async_capable)
    3816              :     {
    3817              :         Assert(fsstate->conn_state->pendingAreq);
    3818              : 
    3819              :         /*
    3820              :          * The query was already sent by an earlier call to
    3821              :          * fetch_more_data_begin.  So now we just fetch the result.
    3822              :          */
    3823          158 :         res = pgfdw_get_result(conn);
    3824              :         /* On error, report the original query, not the FETCH. */
    3825          158 :         if (PQresultStatus(res) != PGRES_TUPLES_OK)
    3826            0 :             pgfdw_report_error(res, conn, fsstate->query);
    3827              : 
    3828              :         /* Reset per-connection state */
    3829          158 :         fsstate->conn_state->pendingAreq = NULL;
    3830              :     }
    3831              :     else
    3832              :     {
    3833              :         char        sql[64];
    3834              : 
    3835              :         /* This is a regular synchronous fetch. */
    3836         1341 :         snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
    3837              :                  fsstate->fetch_size, fsstate->cursor_number);
    3838              : 
    3839         1341 :         res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
    3840              :         /* On error, report the original query, not the FETCH. */
    3841         1341 :         if (PQresultStatus(res) != PGRES_TUPLES_OK)
    3842            1 :             pgfdw_report_error(res, conn, fsstate->query);
    3843              :     }
    3844              : 
    3845              :     /* Convert the data into HeapTuples */
    3846         1498 :     numrows = PQntuples(res);
    3847         1498 :     fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
    3848         1498 :     fsstate->num_tuples = numrows;
    3849         1498 :     fsstate->next_tuple = 0;
    3850              : 
    3851        72726 :     for (i = 0; i < numrows; i++)
    3852              :     {
    3853              :         Assert(IsA(node->ss.ps.plan, ForeignScan));
    3854              : 
    3855        71228 :         fsstate->tuples[i] =
    3856        71232 :             make_tuple_from_result_row(res, i,
    3857              :                                        fsstate->rel,
    3858              :                                        fsstate->attinmeta,
    3859              :                                        fsstate->retrieved_attrs,
    3860              :                                        node,
    3861              :                                        fsstate->temp_cxt);
    3862              :     }
    3863              : 
    3864              :     /* Update fetch_ct_2 */
    3865         1494 :     if (fsstate->fetch_ct_2 < 2)
    3866          942 :         fsstate->fetch_ct_2++;
    3867              : 
    3868              :     /* Must be EOF if we didn't get as many tuples as we asked for. */
    3869         1494 :     fsstate->eof_reached = (numrows < fsstate->fetch_size);
    3870              : 
    3871         1494 :     PQclear(res);
    3872              : 
    3873         1494 :     MemoryContextSwitchTo(oldcontext);
    3874         1494 : }
    3875              : 
    3876              : /*
    3877              :  * Force assorted GUC parameters to settings that ensure that we'll output
    3878              :  * data values in a form that is unambiguous to the remote server.
    3879              :  *
    3880              :  * This is rather expensive and annoying to do once per row, but there's
    3881              :  * little choice if we want to be sure values are transmitted accurately;
    3882              :  * we can't leave the settings in place between rows for fear of affecting
    3883              :  * user-visible computations.
    3884              :  *
    3885              :  * We use the equivalent of a function SET option to allow the settings to
    3886              :  * persist only until the caller calls reset_transmission_modes().  If an
    3887              :  * error is thrown in between, guc.c will take care of undoing the settings.
    3888              :  *
    3889              :  * The return value is the nestlevel that must be passed to
    3890              :  * reset_transmission_modes() to undo things.
    3891              :  */
    3892              : int
    3893         4248 : set_transmission_modes(void)
    3894              : {
    3895         4248 :     int         nestlevel = NewGUCNestLevel();
    3896              : 
    3897              :     /*
    3898              :      * The values set here should match what pg_dump does.  See also
    3899              :      * configure_remote_session in connection.c.
    3900              :      */
    3901         4248 :     if (DateStyle != USE_ISO_DATES)
    3902         4246 :         (void) set_config_option("datestyle", "ISO",
    3903              :                                  PGC_USERSET, PGC_S_SESSION,
    3904              :                                  GUC_ACTION_SAVE, true, 0, false);
    3905         4248 :     if (IntervalStyle != INTSTYLE_POSTGRES)
    3906         4246 :         (void) set_config_option("intervalstyle", "postgres",
    3907              :                                  PGC_USERSET, PGC_S_SESSION,
    3908              :                                  GUC_ACTION_SAVE, true, 0, false);
    3909         4248 :     if (extra_float_digits < 3)
    3910         4246 :         (void) set_config_option("extra_float_digits", "3",
    3911              :                                  PGC_USERSET, PGC_S_SESSION,
    3912              :                                  GUC_ACTION_SAVE, true, 0, false);
    3913              : 
    3914              :     /*
    3915              :      * In addition force restrictive search_path, in case there are any
    3916              :      * regproc or similar constants to be printed.
    3917              :      */
    3918         4248 :     (void) set_config_option("search_path", "pg_catalog",
    3919              :                              PGC_USERSET, PGC_S_SESSION,
    3920              :                              GUC_ACTION_SAVE, true, 0, false);
    3921              : 
    3922         4248 :     return nestlevel;
    3923              : }
    3924              : 
    3925              : /*
    3926              :  * Undo the effects of set_transmission_modes().
    3927              :  */
    3928              : void
    3929         4248 : reset_transmission_modes(int nestlevel)
    3930              : {
    3931         4248 :     AtEOXact_GUC(true, nestlevel);
    3932         4248 : }
    3933              : 
    3934              : /*
    3935              :  * Utility routine to close a cursor.
    3936              :  */
    3937              : static void
    3938          501 : close_cursor(PGconn *conn, unsigned int cursor_number,
    3939              :              PgFdwConnState *conn_state)
    3940              : {
    3941              :     char        sql[64];
    3942              :     PGresult   *res;
    3943              : 
    3944          501 :     snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
    3945          501 :     res = pgfdw_exec_query(conn, sql, conn_state);
    3946          501 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
    3947            0 :         pgfdw_report_error(res, conn, sql);
    3948          501 :     PQclear(res);
    3949          501 : }
    3950              : 
    3951              : /*
    3952              :  * create_foreign_modify
    3953              :  *      Construct an execution state of a foreign insert/update/delete
    3954              :  *      operation
    3955              :  */
    3956              : static PgFdwModifyState *
    3957          182 : create_foreign_modify(EState *estate,
    3958              :                       RangeTblEntry *rte,
    3959              :                       ResultRelInfo *resultRelInfo,
    3960              :                       CmdType operation,
    3961              :                       Plan *subplan,
    3962              :                       char *query,
    3963              :                       List *target_attrs,
    3964              :                       int values_end,
    3965              :                       bool has_returning,
    3966              :                       List *retrieved_attrs)
    3967              : {
    3968              :     PgFdwModifyState *fmstate;
    3969          182 :     Relation    rel = resultRelInfo->ri_RelationDesc;
    3970          182 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    3971              :     Oid         userid;
    3972              :     ForeignTable *table;
    3973              :     UserMapping *user;
    3974              :     AttrNumber  n_params;
    3975              :     Oid         typefnoid;
    3976              :     bool        isvarlena;
    3977              :     ListCell   *lc;
    3978              : 
    3979              :     /* Begin constructing PgFdwModifyState. */
    3980          182 :     fmstate = palloc0_object(PgFdwModifyState);
    3981          182 :     fmstate->rel = rel;
    3982              : 
    3983              :     /* Identify which user to do the remote access as. */
    3984          182 :     userid = ExecGetResultRelCheckAsUser(resultRelInfo, estate);
    3985              : 
    3986              :     /* Get info about foreign table. */
    3987          182 :     table = GetForeignTable(RelationGetRelid(rel));
    3988          182 :     user = GetUserMapping(userid, table->serverid);
    3989              : 
    3990              :     /* Open connection; report that we'll create a prepared statement. */
    3991          182 :     fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
    3992          182 :     fmstate->p_name = NULL;      /* prepared statement not made yet */
    3993              : 
    3994              :     /* Set up remote query information. */
    3995          182 :     fmstate->query = query;
    3996          182 :     if (operation == CMD_INSERT)
    3997              :     {
    3998          133 :         fmstate->query = pstrdup(fmstate->query);
    3999          133 :         fmstate->orig_query = pstrdup(fmstate->query);
    4000              :     }
    4001          182 :     fmstate->target_attrs = target_attrs;
    4002          182 :     fmstate->values_end = values_end;
    4003          182 :     fmstate->has_returning = has_returning;
    4004          182 :     fmstate->retrieved_attrs = retrieved_attrs;
    4005              : 
    4006              :     /* Create context for per-tuple temp workspace. */
    4007          182 :     fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
    4008              :                                               "postgres_fdw temporary data",
    4009              :                                               ALLOCSET_SMALL_SIZES);
    4010              : 
    4011              :     /* Prepare for input conversion of RETURNING results. */
    4012          182 :     if (fmstate->has_returning)
    4013           62 :         fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
    4014              : 
    4015              :     /* Prepare for output conversion of parameters used in prepared stmt. */
    4016          182 :     n_params = list_length(fmstate->target_attrs) + 1;
    4017          182 :     fmstate->p_flinfo = palloc0_array(FmgrInfo, n_params);
    4018          182 :     fmstate->p_nums = 0;
    4019              : 
    4020          182 :     if (operation == CMD_UPDATE || operation == CMD_DELETE)
    4021              :     {
    4022              :         Assert(subplan != NULL);
    4023              : 
    4024              :         /* Find the ctid resjunk column in the subplan's result */
    4025           49 :         fmstate->ctidAttno = ExecFindJunkAttributeInTlist(subplan->targetlist,
    4026              :                                                           "ctid");
    4027           49 :         if (!AttributeNumberIsValid(fmstate->ctidAttno))
    4028            0 :             elog(ERROR, "could not find junk ctid column");
    4029              : 
    4030              :         /* First transmittable parameter will be ctid */
    4031           49 :         getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
    4032           49 :         fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
    4033           49 :         fmstate->p_nums++;
    4034              :     }
    4035              : 
    4036          182 :     if (operation == CMD_INSERT || operation == CMD_UPDATE)
    4037              :     {
    4038              :         /* Set up for remaining transmittable parameters */
    4039          568 :         foreach(lc, fmstate->target_attrs)
    4040              :         {
    4041          399 :             int         attnum = lfirst_int(lc);
    4042          399 :             Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
    4043              : 
    4044              :             Assert(!attr->attisdropped);
    4045              : 
    4046              :             /* Ignore generated columns; they are set to DEFAULT */
    4047          399 :             if (attr->attgenerated)
    4048            8 :                 continue;
    4049          391 :             getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
    4050          391 :             fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
    4051          391 :             fmstate->p_nums++;
    4052              :         }
    4053              :     }
    4054              : 
    4055              :     Assert(fmstate->p_nums <= n_params);
    4056              : 
    4057              :     /* Set batch_size from foreign server/table options. */
    4058          182 :     if (operation == CMD_INSERT)
    4059          133 :         fmstate->batch_size = get_batch_size_option(rel);
    4060              : 
    4061          182 :     fmstate->num_slots = 1;
    4062              : 
    4063              :     /* Initialize auxiliary state */
    4064          182 :     fmstate->aux_fmstate = NULL;
    4065              : 
    4066          182 :     return fmstate;
    4067              : }
    4068              : 
    4069              : /*
    4070              :  * execute_foreign_modify
    4071              :  *      Perform foreign-table modification as required, and fetch RETURNING
    4072              :  *      result if any.  (This is the shared guts of postgresExecForeignInsert,
    4073              :  *      postgresExecForeignBatchInsert, postgresExecForeignUpdate, and
    4074              :  *      postgresExecForeignDelete.)
    4075              :  */
    4076              : static TupleTableSlot **
    4077         1052 : execute_foreign_modify(EState *estate,
    4078              :                        ResultRelInfo *resultRelInfo,
    4079              :                        CmdType operation,
    4080              :                        TupleTableSlot **slots,
    4081              :                        TupleTableSlot **planSlots,
    4082              :                        int *numSlots)
    4083              : {
    4084         1052 :     PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
    4085         1052 :     ItemPointer ctid = NULL;
    4086              :     const char **p_values;
    4087              :     PGresult   *res;
    4088              :     int         n_rows;
    4089              :     StringInfoData sql;
    4090              : 
    4091              :     /* The operation should be INSERT, UPDATE, or DELETE */
    4092              :     Assert(operation == CMD_INSERT ||
    4093              :            operation == CMD_UPDATE ||
    4094              :            operation == CMD_DELETE);
    4095              : 
    4096              :     /* First, process a pending asynchronous request, if any. */
    4097         1052 :     if (fmstate->conn_state->pendingAreq)
    4098            1 :         process_pending_request(fmstate->conn_state->pendingAreq);
    4099              : 
    4100              :     /*
    4101              :      * If the existing query was deparsed and prepared for a different number
    4102              :      * of rows, rebuild it for the proper number.
    4103              :      */
    4104         1052 :     if (operation == CMD_INSERT && fmstate->num_slots != *numSlots)
    4105              :     {
    4106              :         /* Destroy the prepared statement created previously */
    4107           26 :         if (fmstate->p_name)
    4108           11 :             deallocate_query(fmstate);
    4109              : 
    4110              :         /* Build INSERT string with numSlots records in its VALUES clause. */
    4111           26 :         initStringInfo(&sql);
    4112           26 :         rebuildInsertSql(&sql, fmstate->rel,
    4113              :                          fmstate->orig_query, fmstate->target_attrs,
    4114              :                          fmstate->values_end, fmstate->p_nums,
    4115           26 :                          *numSlots - 1);
    4116           26 :         pfree(fmstate->query);
    4117           26 :         fmstate->query = sql.data;
    4118           26 :         fmstate->num_slots = *numSlots;
    4119              :     }
    4120              : 
    4121              :     /* Set up the prepared statement on the remote server, if we didn't yet */
    4122         1052 :     if (!fmstate->p_name)
    4123          187 :         prepare_foreign_modify(fmstate);
    4124              : 
    4125              :     /*
    4126              :      * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
    4127              :      */
    4128         1052 :     if (operation == CMD_UPDATE || operation == CMD_DELETE)
    4129              :     {
    4130              :         Datum       datum;
    4131              :         bool        isNull;
    4132              : 
    4133          118 :         datum = ExecGetJunkAttribute(planSlots[0],
    4134          118 :                                      fmstate->ctidAttno,
    4135              :                                      &isNull);
    4136              :         /* shouldn't ever get a null result... */
    4137          118 :         if (isNull)
    4138            0 :             elog(ERROR, "ctid is NULL");
    4139          118 :         ctid = (ItemPointer) DatumGetPointer(datum);
    4140              :     }
    4141              : 
    4142              :     /* Convert parameters needed by prepared statement to text form */
    4143         1052 :     p_values = convert_prep_stmt_params(fmstate, ctid, slots, *numSlots);
    4144              : 
    4145              :     /*
    4146              :      * Execute the prepared statement.
    4147              :      */
    4148         1052 :     if (!PQsendQueryPrepared(fmstate->conn,
    4149         1052 :                              fmstate->p_name,
    4150         1052 :                              fmstate->p_nums * (*numSlots),
    4151              :                              p_values,
    4152              :                              NULL,
    4153              :                              NULL,
    4154              :                              0))
    4155            0 :         pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
    4156              : 
    4157              :     /*
    4158              :      * Get the result, and check for success.
    4159              :      */
    4160         1052 :     res = pgfdw_get_result(fmstate->conn);
    4161         2104 :     if (PQresultStatus(res) !=
    4162         1052 :         (fmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
    4163            5 :         pgfdw_report_error(res, fmstate->conn, fmstate->query);
    4164              : 
    4165              :     /* Check number of rows affected, and fetch RETURNING tuple if any */
    4166         1047 :     if (fmstate->has_returning)
    4167              :     {
    4168              :         Assert(*numSlots == 1);
    4169          108 :         n_rows = PQntuples(res);
    4170          108 :         if (n_rows > 0)
    4171          107 :             store_returning_result(fmstate, slots[0], res);
    4172              :     }
    4173              :     else
    4174          939 :         n_rows = atoi(PQcmdTuples(res));
    4175              : 
    4176              :     /* And clean up */
    4177         1047 :     PQclear(res);
    4178              : 
    4179         1047 :     MemoryContextReset(fmstate->temp_cxt);
    4180              : 
    4181         1047 :     *numSlots = n_rows;
    4182              : 
    4183              :     /*
    4184              :      * Return NULL if nothing was inserted/updated/deleted on the remote end
    4185              :      */
    4186         1047 :     return (n_rows > 0) ? slots : NULL;
    4187              : }
    4188              : 
    4189              : /*
    4190              :  * prepare_foreign_modify
    4191              :  *      Establish a prepared statement for execution of INSERT/UPDATE/DELETE
    4192              :  */
    4193              : static void
    4194          187 : prepare_foreign_modify(PgFdwModifyState *fmstate)
    4195              : {
    4196              :     char        prep_name[NAMEDATALEN];
    4197              :     char       *p_name;
    4198              :     PGresult   *res;
    4199              : 
    4200              :     /*
    4201              :      * The caller would already have processed a pending asynchronous request
    4202              :      * if any, so no need to do it here.
    4203              :      */
    4204              : 
    4205              :     /* Construct name we'll use for the prepared statement. */
    4206          187 :     snprintf(prep_name, sizeof(prep_name), "pgsql_fdw_prep_%u",
    4207              :              GetPrepStmtNumber(fmstate->conn));
    4208          187 :     p_name = pstrdup(prep_name);
    4209              : 
    4210              :     /*
    4211              :      * We intentionally do not specify parameter types here, but leave the
    4212              :      * remote server to derive them by default.  This avoids possible problems
    4213              :      * with the remote server using different type OIDs than we do.  All of
    4214              :      * the prepared statements we use in this module are simple enough that
    4215              :      * the remote server will make the right choices.
    4216              :      */
    4217          187 :     if (!PQsendPrepare(fmstate->conn,
    4218              :                        p_name,
    4219          187 :                        fmstate->query,
    4220              :                        0,
    4221              :                        NULL))
    4222            0 :         pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
    4223              : 
    4224              :     /*
    4225              :      * Get the result, and check for success.
    4226              :      */
    4227          187 :     res = pgfdw_get_result(fmstate->conn);
    4228          187 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
    4229            0 :         pgfdw_report_error(res, fmstate->conn, fmstate->query);
    4230          187 :     PQclear(res);
    4231              : 
    4232              :     /* This action shows that the prepare has been done. */
    4233          187 :     fmstate->p_name = p_name;
    4234          187 : }
    4235              : 
    4236              : /*
    4237              :  * convert_prep_stmt_params
    4238              :  *      Create array of text strings representing parameter values
    4239              :  *
    4240              :  * tupleid is ctid to send, or NULL if none
    4241              :  * slot is slot to get remaining parameters from, or NULL if none
    4242              :  *
    4243              :  * Data is constructed in temp_cxt; caller should reset that after use.
    4244              :  */
    4245              : static const char **
    4246         1052 : convert_prep_stmt_params(PgFdwModifyState *fmstate,
    4247              :                          ItemPointer tupleid,
    4248              :                          TupleTableSlot **slots,
    4249              :                          int numSlots)
    4250              : {
    4251              :     const char **p_values;
    4252              :     int         i;
    4253              :     int         j;
    4254         1052 :     int         pindex = 0;
    4255              :     MemoryContext oldcontext;
    4256              : 
    4257         1052 :     oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
    4258              : 
    4259         1052 :     p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
    4260              : 
    4261              :     /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
    4262              :     Assert(!(tupleid != NULL && numSlots > 1));
    4263              : 
    4264              :     /* 1st parameter should be ctid, if it's in use */
    4265         1052 :     if (tupleid != NULL)
    4266              :     {
    4267              :         Assert(numSlots == 1);
    4268              :         /* don't need set_transmission_modes for TID output */
    4269          118 :         p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
    4270              :                                               PointerGetDatum(tupleid));
    4271          118 :         pindex++;
    4272              :     }
    4273              : 
    4274              :     /* get following parameters from slots */
    4275         1052 :     if (slots != NULL && fmstate->target_attrs != NIL)
    4276              :     {
    4277         1026 :         TupleDesc   tupdesc = RelationGetDescr(fmstate->rel);
    4278              :         int         nestlevel;
    4279              :         ListCell   *lc;
    4280              : 
    4281         1026 :         nestlevel = set_transmission_modes();
    4282              : 
    4283         2174 :         for (i = 0; i < numSlots; i++)
    4284              :         {
    4285         1148 :             j = (tupleid != NULL) ? 1 : 0;
    4286         4797 :             foreach(lc, fmstate->target_attrs)
    4287              :             {
    4288         3649 :                 int         attnum = lfirst_int(lc);
    4289         3649 :                 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
    4290              :                 Datum       value;
    4291              :                 bool        isnull;
    4292              : 
    4293              :                 /* Ignore generated columns; they are set to DEFAULT */
    4294         3649 :                 if (attr->attgenerated)
    4295           14 :                     continue;
    4296         3635 :                 value = slot_getattr(slots[i], attnum, &isnull);
    4297         3635 :                 if (isnull)
    4298          583 :                     p_values[pindex] = NULL;
    4299              :                 else
    4300         3052 :                     p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[j],
    4301              :                                                           value);
    4302         3635 :                 pindex++;
    4303         3635 :                 j++;
    4304              :             }
    4305              :         }
    4306              : 
    4307         1026 :         reset_transmission_modes(nestlevel);
    4308              :     }
    4309              : 
    4310              :     Assert(pindex == fmstate->p_nums * numSlots);
    4311              : 
    4312         1052 :     MemoryContextSwitchTo(oldcontext);
    4313              : 
    4314         1052 :     return p_values;
    4315              : }
    4316              : 
    4317              : /*
    4318              :  * store_returning_result
    4319              :  *      Store the result of a RETURNING clause
    4320              :  */
    4321              : static void
    4322          107 : store_returning_result(PgFdwModifyState *fmstate,
    4323              :                        TupleTableSlot *slot, PGresult *res)
    4324              : {
    4325              :     HeapTuple   newtup;
    4326              : 
    4327          107 :     newtup = make_tuple_from_result_row(res, 0,
    4328              :                                         fmstate->rel,
    4329              :                                         fmstate->attinmeta,
    4330              :                                         fmstate->retrieved_attrs,
    4331              :                                         NULL,
    4332              :                                         fmstate->temp_cxt);
    4333              : 
    4334              :     /*
    4335              :      * The returning slot will not necessarily be suitable to store heaptuples
    4336              :      * directly, so allow for conversion.
    4337              :      */
    4338          107 :     ExecForceStoreHeapTuple(newtup, slot, true);
    4339          107 : }
    4340              : 
    4341              : /*
    4342              :  * finish_foreign_modify
    4343              :  *      Release resources for a foreign insert/update/delete operation
    4344              :  */
    4345              : static void
    4346          160 : finish_foreign_modify(PgFdwModifyState *fmstate)
    4347              : {
    4348              :     Assert(fmstate != NULL);
    4349              : 
    4350              :     /* If we created a prepared statement, destroy it */
    4351          160 :     deallocate_query(fmstate);
    4352              : 
    4353              :     /* Release remote connection */
    4354          160 :     ReleaseConnection(fmstate->conn);
    4355          160 :     fmstate->conn = NULL;
    4356          160 : }
    4357              : 
    4358              : /*
    4359              :  * deallocate_query
    4360              :  *      Deallocate a prepared statement for a foreign insert/update/delete
    4361              :  *      operation
    4362              :  */
    4363              : static void
    4364          171 : deallocate_query(PgFdwModifyState *fmstate)
    4365              : {
    4366              :     char        sql[64];
    4367              :     PGresult   *res;
    4368              : 
    4369              :     /* do nothing if the query is not allocated */
    4370          171 :     if (!fmstate->p_name)
    4371            4 :         return;
    4372              : 
    4373          167 :     snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
    4374          167 :     res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
    4375          167 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
    4376            0 :         pgfdw_report_error(res, fmstate->conn, sql);
    4377          167 :     PQclear(res);
    4378          167 :     pfree(fmstate->p_name);
    4379          167 :     fmstate->p_name = NULL;
    4380              : }
    4381              : 
    4382              : /*
    4383              :  * build_remote_returning
    4384              :  *      Build a RETURNING targetlist of a remote query for performing an
    4385              :  *      UPDATE/DELETE .. RETURNING on a join directly
    4386              :  */
    4387              : static List *
    4388            4 : build_remote_returning(Index rtindex, Relation rel, List *returningList)
    4389              : {
    4390            4 :     bool        have_wholerow = false;
    4391            4 :     List       *tlist = NIL;
    4392              :     List       *vars;
    4393              :     ListCell   *lc;
    4394              : 
    4395              :     Assert(returningList);
    4396              : 
    4397            4 :     vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
    4398              : 
    4399              :     /*
    4400              :      * If there's a whole-row reference to the target relation, then we'll
    4401              :      * need all the columns of the relation.
    4402              :      */
    4403            4 :     foreach(lc, vars)
    4404              :     {
    4405            2 :         Var        *var = (Var *) lfirst(lc);
    4406              : 
    4407            2 :         if (IsA(var, Var) &&
    4408            2 :             var->varno == rtindex &&
    4409            2 :             var->varattno == InvalidAttrNumber)
    4410              :         {
    4411            2 :             have_wholerow = true;
    4412            2 :             break;
    4413              :         }
    4414              :     }
    4415              : 
    4416            4 :     if (have_wholerow)
    4417              :     {
    4418            2 :         TupleDesc   tupdesc = RelationGetDescr(rel);
    4419              :         int         i;
    4420              : 
    4421           20 :         for (i = 1; i <= tupdesc->natts; i++)
    4422              :         {
    4423           18 :             Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
    4424              :             Var        *var;
    4425              : 
    4426              :             /* Ignore dropped attributes. */
    4427           18 :             if (attr->attisdropped)
    4428            2 :                 continue;
    4429              : 
    4430           16 :             var = makeVar(rtindex,
    4431              :                           i,
    4432              :                           attr->atttypid,
    4433              :                           attr->atttypmod,
    4434              :                           attr->attcollation,
    4435              :                           0);
    4436              : 
    4437           16 :             tlist = lappend(tlist,
    4438           16 :                             makeTargetEntry((Expr *) var,
    4439           16 :                                             list_length(tlist) + 1,
    4440              :                                             NULL,
    4441              :                                             false));
    4442              :         }
    4443              :     }
    4444              : 
    4445              :     /* Now add any remaining columns to tlist. */
    4446           30 :     foreach(lc, vars)
    4447              :     {
    4448           26 :         Var        *var = (Var *) lfirst(lc);
    4449              : 
    4450              :         /*
    4451              :          * No need for whole-row references to the target relation.  We don't
    4452              :          * need system columns other than ctid and oid either, since those are
    4453              :          * set locally.
    4454              :          */
    4455           26 :         if (IsA(var, Var) &&
    4456           26 :             var->varno == rtindex &&
    4457           18 :             var->varattno <= InvalidAttrNumber &&
    4458            2 :             var->varattno != SelfItemPointerAttributeNumber)
    4459            2 :             continue;           /* don't need it */
    4460              : 
    4461           24 :         if (tlist_member((Expr *) var, tlist))
    4462           16 :             continue;           /* already got it */
    4463              : 
    4464            8 :         tlist = lappend(tlist,
    4465            8 :                         makeTargetEntry((Expr *) var,
    4466            8 :                                         list_length(tlist) + 1,
    4467              :                                         NULL,
    4468              :                                         false));
    4469              :     }
    4470              : 
    4471            4 :     list_free(vars);
    4472              : 
    4473            4 :     return tlist;
    4474              : }
    4475              : 
    4476              : /*
    4477              :  * rebuild_fdw_scan_tlist
    4478              :  *      Build new fdw_scan_tlist of given foreign-scan plan node from given
    4479              :  *      tlist
    4480              :  *
    4481              :  * There might be columns that the fdw_scan_tlist of the given foreign-scan
    4482              :  * plan node contains that the given tlist doesn't.  The fdw_scan_tlist would
    4483              :  * have contained resjunk columns such as 'ctid' of the target relation and
    4484              :  * 'wholerow' of non-target relations, but the tlist might not contain them,
    4485              :  * for example.  So, adjust the tlist so it contains all the columns specified
    4486              :  * in the fdw_scan_tlist; else setrefs.c will get confused.
    4487              :  */
    4488              : static void
    4489            2 : rebuild_fdw_scan_tlist(ForeignScan *fscan, List *tlist)
    4490              : {
    4491            2 :     List       *new_tlist = tlist;
    4492            2 :     List       *old_tlist = fscan->fdw_scan_tlist;
    4493              :     ListCell   *lc;
    4494              : 
    4495           16 :     foreach(lc, old_tlist)
    4496              :     {
    4497           14 :         TargetEntry *tle = (TargetEntry *) lfirst(lc);
    4498              : 
    4499           14 :         if (tlist_member(tle->expr, new_tlist))
    4500            8 :             continue;           /* already got it */
    4501              : 
    4502            6 :         new_tlist = lappend(new_tlist,
    4503            6 :                             makeTargetEntry(tle->expr,
    4504            6 :                                             list_length(new_tlist) + 1,
    4505              :                                             NULL,
    4506              :                                             false));
    4507              :     }
    4508            2 :     fscan->fdw_scan_tlist = new_tlist;
    4509            2 : }
    4510              : 
    4511              : /*
    4512              :  * Execute a direct UPDATE/DELETE statement.
    4513              :  */
    4514              : static void
    4515           71 : execute_dml_stmt(ForeignScanState *node)
    4516              : {
    4517           71 :     PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state;
    4518           71 :     ExprContext *econtext = node->ss.ps.ps_ExprContext;
    4519           71 :     int         numParams = dmstate->numParams;
    4520           71 :     const char **values = dmstate->param_values;
    4521              : 
    4522              :     /* First, process a pending asynchronous request, if any. */
    4523           71 :     if (dmstate->conn_state->pendingAreq)
    4524            1 :         process_pending_request(dmstate->conn_state->pendingAreq);
    4525              : 
    4526              :     /*
    4527              :      * Construct array of query parameter values in text format.
    4528              :      */
    4529           71 :     if (numParams > 0)
    4530            0 :         process_query_params(econtext,
    4531              :                              dmstate->param_flinfo,
    4532              :                              dmstate->param_exprs,
    4533              :                              values);
    4534              : 
    4535              :     /*
    4536              :      * Notice that we pass NULL for paramTypes, thus forcing the remote server
    4537              :      * to infer types for all parameters.  Since we explicitly cast every
    4538              :      * parameter (see deparse.c), the "inference" is trivial and will produce
    4539              :      * the desired result.  This allows us to avoid assuming that the remote
    4540              :      * server has the same OIDs we do for the parameters' types.
    4541              :      */
    4542           71 :     if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
    4543              :                            NULL, values, NULL, NULL, 0))
    4544            0 :         pgfdw_report_error(NULL, dmstate->conn, dmstate->query);
    4545              : 
    4546              :     /*
    4547              :      * Get the result, and check for success.
    4548              :      */
    4549           71 :     dmstate->result = pgfdw_get_result(dmstate->conn);
    4550          142 :     if (PQresultStatus(dmstate->result) !=
    4551           71 :         (dmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
    4552            4 :         pgfdw_report_error(dmstate->result, dmstate->conn,
    4553            4 :                            dmstate->query);
    4554              : 
    4555              :     /*
    4556              :      * The result potentially needs to survive across multiple executor row
    4557              :      * cycles, so move it to the context where the dmstate is.
    4558              :      */
    4559           67 :     dmstate->result = libpqsrv_PGresultSetParent(dmstate->result,
    4560              :                                                  GetMemoryChunkContext(dmstate));
    4561              : 
    4562              :     /* Get the number of rows affected. */
    4563           67 :     if (dmstate->has_returning)
    4564           14 :         dmstate->num_tuples = PQntuples(dmstate->result);
    4565              :     else
    4566           53 :         dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
    4567           67 : }
    4568              : 
    4569              : /*
    4570              :  * Get the result of a RETURNING clause.
    4571              :  */
    4572              : static TupleTableSlot *
    4573          364 : get_returning_data(ForeignScanState *node)
    4574              : {
    4575          364 :     PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state;
    4576          364 :     EState     *estate = node->ss.ps.state;
    4577          364 :     ResultRelInfo *resultRelInfo = node->resultRelInfo;
    4578          364 :     TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
    4579              :     TupleTableSlot *resultSlot;
    4580              : 
    4581              :     Assert(resultRelInfo->ri_projectReturning);
    4582              : 
    4583              :     /* If we didn't get any tuples, must be end of data. */
    4584          364 :     if (dmstate->next_tuple >= dmstate->num_tuples)
    4585           17 :         return ExecClearTuple(slot);
    4586              : 
    4587              :     /* Increment the command es_processed count if necessary. */
    4588          347 :     if (dmstate->set_processed)
    4589          346 :         estate->es_processed += 1;
    4590              : 
    4591              :     /*
    4592              :      * Store a RETURNING tuple.  If has_returning is false, just emit a dummy
    4593              :      * tuple.  (has_returning is false when the local query is of the form
    4594              :      * "UPDATE/DELETE .. RETURNING 1" for example.)
    4595              :      */
    4596          347 :     if (!dmstate->has_returning)
    4597              :     {
    4598           12 :         ExecStoreAllNullTuple(slot);
    4599           12 :         resultSlot = slot;
    4600              :     }
    4601              :     else
    4602              :     {
    4603              :         HeapTuple   newtup;
    4604              : 
    4605          335 :         newtup = make_tuple_from_result_row(dmstate->result,
    4606              :                                             dmstate->next_tuple,
    4607              :                                             dmstate->rel,
    4608              :                                             dmstate->attinmeta,
    4609              :                                             dmstate->retrieved_attrs,
    4610              :                                             node,
    4611              :                                             dmstate->temp_cxt);
    4612          335 :         ExecStoreHeapTuple(newtup, slot, false);
    4613              :         /* Get the updated/deleted tuple. */
    4614          335 :         if (dmstate->rel)
    4615          319 :             resultSlot = slot;
    4616              :         else
    4617           16 :             resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
    4618              :     }
    4619          347 :     dmstate->next_tuple++;
    4620              : 
    4621              :     /* Make slot available for evaluation of the local query RETURNING list. */
    4622          347 :     resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
    4623              :         resultSlot;
    4624              : 
    4625          347 :     return slot;
    4626              : }
    4627              : 
    4628              : /*
    4629              :  * Initialize a filter to extract an updated/deleted tuple from a scan tuple.
    4630              :  */
    4631              : static void
    4632            1 : init_returning_filter(PgFdwDirectModifyState *dmstate,
    4633              :                       List *fdw_scan_tlist,
    4634              :                       Index rtindex)
    4635              : {
    4636            1 :     TupleDesc   resultTupType = RelationGetDescr(dmstate->resultRel);
    4637              :     ListCell   *lc;
    4638              :     int         i;
    4639              : 
    4640              :     /*
    4641              :      * Calculate the mapping between the fdw_scan_tlist's entries and the
    4642              :      * result tuple's attributes.
    4643              :      *
    4644              :      * The "map" is an array of indexes of the result tuple's attributes in
    4645              :      * fdw_scan_tlist, i.e., one entry for every attribute of the result
    4646              :      * tuple.  We store zero for any attributes that don't have the
    4647              :      * corresponding entries in that list, marking that a NULL is needed in
    4648              :      * the result tuple.
    4649              :      *
    4650              :      * Also get the indexes of the entries for ctid and oid if any.
    4651              :      */
    4652            1 :     dmstate->attnoMap = (AttrNumber *)
    4653            1 :         palloc0(resultTupType->natts * sizeof(AttrNumber));
    4654              : 
    4655            1 :     dmstate->ctidAttno = dmstate->oidAttno = 0;
    4656              : 
    4657            1 :     i = 1;
    4658            1 :     dmstate->hasSystemCols = false;
    4659           16 :     foreach(lc, fdw_scan_tlist)
    4660              :     {
    4661           15 :         TargetEntry *tle = (TargetEntry *) lfirst(lc);
    4662           15 :         Var        *var = (Var *) tle->expr;
    4663              : 
    4664              :         Assert(IsA(var, Var));
    4665              : 
    4666              :         /*
    4667              :          * If the Var is a column of the target relation to be retrieved from
    4668              :          * the foreign server, get the index of the entry.
    4669              :          */
    4670           25 :         if (var->varno == rtindex &&
    4671           10 :             list_member_int(dmstate->retrieved_attrs, i))
    4672              :         {
    4673            8 :             int         attrno = var->varattno;
    4674              : 
    4675            8 :             if (attrno < 0)
    4676              :             {
    4677              :                 /*
    4678              :                  * We don't retrieve system columns other than ctid and oid.
    4679              :                  */
    4680            0 :                 if (attrno == SelfItemPointerAttributeNumber)
    4681            0 :                     dmstate->ctidAttno = i;
    4682              :                 else
    4683              :                     Assert(false);
    4684            0 :                 dmstate->hasSystemCols = true;
    4685              :             }
    4686              :             else
    4687              :             {
    4688              :                 /*
    4689              :                  * We don't retrieve whole-row references to the target
    4690              :                  * relation either.
    4691              :                  */
    4692              :                 Assert(attrno > 0);
    4693              : 
    4694            8 :                 dmstate->attnoMap[attrno - 1] = i;
    4695              :             }
    4696              :         }
    4697           15 :         i++;
    4698              :     }
    4699            1 : }
    4700              : 
    4701              : /*
    4702              :  * Extract and return an updated/deleted tuple from a scan tuple.
    4703              :  */
    4704              : static TupleTableSlot *
    4705           16 : apply_returning_filter(PgFdwDirectModifyState *dmstate,
    4706              :                        ResultRelInfo *resultRelInfo,
    4707              :                        TupleTableSlot *slot,
    4708              :                        EState *estate)
    4709              : {
    4710           16 :     TupleDesc   resultTupType = RelationGetDescr(dmstate->resultRel);
    4711              :     TupleTableSlot *resultSlot;
    4712              :     Datum      *values;
    4713              :     bool       *isnull;
    4714              :     Datum      *old_values;
    4715              :     bool       *old_isnull;
    4716              :     int         i;
    4717              : 
    4718              :     /*
    4719              :      * Use the return tuple slot as a place to store the result tuple.
    4720              :      */
    4721           16 :     resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
    4722              : 
    4723              :     /*
    4724              :      * Extract all the values of the scan tuple.
    4725              :      */
    4726           16 :     slot_getallattrs(slot);
    4727           16 :     old_values = slot->tts_values;
    4728           16 :     old_isnull = slot->tts_isnull;
    4729              : 
    4730              :     /*
    4731              :      * Prepare to build the result tuple.
    4732              :      */
    4733           16 :     ExecClearTuple(resultSlot);
    4734           16 :     values = resultSlot->tts_values;
    4735           16 :     isnull = resultSlot->tts_isnull;
    4736              : 
    4737              :     /*
    4738              :      * Transpose data into proper fields of the result tuple.
    4739              :      */
    4740          160 :     for (i = 0; i < resultTupType->natts; i++)
    4741              :     {
    4742          144 :         int         j = dmstate->attnoMap[i];
    4743              : 
    4744          144 :         if (j == 0)
    4745              :         {
    4746           16 :             values[i] = (Datum) 0;
    4747           16 :             isnull[i] = true;
    4748              :         }
    4749              :         else
    4750              :         {
    4751          128 :             values[i] = old_values[j - 1];
    4752          128 :             isnull[i] = old_isnull[j - 1];
    4753              :         }
    4754              :     }
    4755              : 
    4756              :     /*
    4757              :      * Build the virtual tuple.
    4758              :      */
    4759           16 :     ExecStoreVirtualTuple(resultSlot);
    4760              : 
    4761              :     /*
    4762              :      * If we have any system columns to return, materialize a heap tuple in
    4763              :      * the slot from column values set above and install system columns in
    4764              :      * that tuple.
    4765              :      */
    4766           16 :     if (dmstate->hasSystemCols)
    4767              :     {
    4768            0 :         HeapTuple   resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
    4769              : 
    4770              :         /* ctid */
    4771            0 :         if (dmstate->ctidAttno)
    4772              :         {
    4773            0 :             ItemPointer ctid = NULL;
    4774              : 
    4775            0 :             ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
    4776            0 :             resultTup->t_self = *ctid;
    4777              :         }
    4778              : 
    4779              :         /*
    4780              :          * And remaining columns
    4781              :          *
    4782              :          * Note: since we currently don't allow the target relation to appear
    4783              :          * on the nullable side of an outer join, any system columns wouldn't
    4784              :          * go to NULL.
    4785              :          *
    4786              :          * Note: no need to care about tableoid here because it will be
    4787              :          * initialized in ExecProcessReturning().
    4788              :          */
    4789            0 :         HeapTupleHeaderSetXmin(resultTup->t_data, InvalidTransactionId);
    4790            0 :         HeapTupleHeaderSetXmax(resultTup->t_data, InvalidTransactionId);
    4791            0 :         HeapTupleHeaderSetCmin(resultTup->t_data, InvalidTransactionId);
    4792              :     }
    4793              : 
    4794              :     /*
    4795              :      * And return the result tuple.
    4796              :      */
    4797           16 :     return resultSlot;
    4798              : }
    4799              : 
    4800              : /*
    4801              :  * Prepare for processing of parameters used in remote query.
    4802              :  */
    4803              : static void
    4804           23 : prepare_query_params(PlanState *node,
    4805              :                      List *fdw_exprs,
    4806              :                      int numParams,
    4807              :                      FmgrInfo **param_flinfo,
    4808              :                      List **param_exprs,
    4809              :                      const char ***param_values)
    4810              : {
    4811              :     int         i;
    4812              :     ListCell   *lc;
    4813              : 
    4814              :     Assert(numParams > 0);
    4815              : 
    4816              :     /* Prepare for output conversion of parameters used in remote query. */
    4817           23 :     *param_flinfo = palloc0_array(FmgrInfo, numParams);
    4818              : 
    4819           23 :     i = 0;
    4820           47 :     foreach(lc, fdw_exprs)
    4821              :     {
    4822           24 :         Node       *param_expr = (Node *) lfirst(lc);
    4823              :         Oid         typefnoid;
    4824              :         bool        isvarlena;
    4825              : 
    4826           24 :         getTypeOutputInfo(exprType(param_expr), &typefnoid, &isvarlena);
    4827           24 :         fmgr_info(typefnoid, &(*param_flinfo)[i]);
    4828           24 :         i++;
    4829              :     }
    4830              : 
    4831              :     /*
    4832              :      * Prepare remote-parameter expressions for evaluation.  (Note: in
    4833              :      * practice, we expect that all these expressions will be just Params, so
    4834              :      * we could possibly do something more efficient than using the full
    4835              :      * expression-eval machinery for this.  But probably there would be little
    4836              :      * benefit, and it'd require postgres_fdw to know more than is desirable
    4837              :      * about Param evaluation.)
    4838              :      */
    4839           23 :     *param_exprs = ExecInitExprList(fdw_exprs, node);
    4840              : 
    4841              :     /* Allocate buffer for text form of query parameters. */
    4842           23 :     *param_values = (const char **) palloc0(numParams * sizeof(char *));
    4843           23 : }
    4844              : 
    4845              : /*
    4846              :  * Construct array of query parameter values in text format.
    4847              :  */
    4848              : static void
    4849          350 : process_query_params(ExprContext *econtext,
    4850              :                      FmgrInfo *param_flinfo,
    4851              :                      List *param_exprs,
    4852              :                      const char **param_values)
    4853              : {
    4854              :     int         nestlevel;
    4855              :     int         i;
    4856              :     ListCell   *lc;
    4857              : 
    4858          350 :     nestlevel = set_transmission_modes();
    4859              : 
    4860          350 :     i = 0;
    4861          900 :     foreach(lc, param_exprs)
    4862              :     {
    4863          550 :         ExprState  *expr_state = (ExprState *) lfirst(lc);
    4864              :         Datum       expr_value;
    4865              :         bool        isNull;
    4866              : 
    4867              :         /* Evaluate the parameter expression */
    4868          550 :         expr_value = ExecEvalExpr(expr_state, econtext, &isNull);
    4869              : 
    4870              :         /*
    4871              :          * Get string representation of each parameter value by invoking
    4872              :          * type-specific output function, unless the value is null.
    4873              :          */
    4874          550 :         if (isNull)
    4875            0 :             param_values[i] = NULL;
    4876              :         else
    4877          550 :             param_values[i] = OutputFunctionCall(&param_flinfo[i], expr_value);
    4878              : 
    4879          550 :         i++;
    4880              :     }
    4881              : 
    4882          350 :     reset_transmission_modes(nestlevel);
    4883          350 : }
    4884              : 
    4885              : /*
    4886              :  * postgresAnalyzeForeignTable
    4887              :  *      Test whether analyzing this foreign table is supported
    4888              :  */
    4889              : static bool
    4890           47 : postgresAnalyzeForeignTable(Relation relation,
    4891              :                             AcquireSampleRowsFunc *func,
    4892              :                             BlockNumber *totalpages)
    4893              : {
    4894              :     ForeignTable *table;
    4895              :     UserMapping *user;
    4896              :     PGconn     *conn;
    4897              :     StringInfoData sql;
    4898              :     PGresult   *res;
    4899              : 
    4900              :     /* Return the row-analysis function pointer */
    4901           47 :     *func = postgresAcquireSampleRowsFunc;
    4902              : 
    4903              :     /*
    4904              :      * Now we have to get the number of pages.  It's annoying that the ANALYZE
    4905              :      * API requires us to return that now, because it forces some duplication
    4906              :      * of effort between this routine and postgresAcquireSampleRowsFunc.  But
    4907              :      * it's probably not worth redefining that API at this point.
    4908              :      */
    4909              : 
    4910              :     /*
    4911              :      * Get the connection to use.  We do the remote access as the table's
    4912              :      * owner, even if the ANALYZE was started by some other user.
    4913              :      */
    4914           47 :     table = GetForeignTable(RelationGetRelid(relation));
    4915           47 :     user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
    4916           47 :     conn = GetConnection(user, false, NULL);
    4917              : 
    4918              :     /*
    4919              :      * Construct command to get page count for relation.
    4920              :      */
    4921           47 :     initStringInfo(&sql);
    4922           47 :     deparseAnalyzeSizeSql(&sql, relation);
    4923              : 
    4924           47 :     res = pgfdw_exec_query(conn, sql.data, NULL);
    4925           47 :     if (PQresultStatus(res) != PGRES_TUPLES_OK)
    4926            0 :         pgfdw_report_error(res, conn, sql.data);
    4927              : 
    4928           47 :     if (PQntuples(res) != 1 || PQnfields(res) != 1)
    4929            0 :         elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
    4930           47 :     *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
    4931           47 :     PQclear(res);
    4932              : 
    4933           47 :     ReleaseConnection(conn);
    4934              : 
    4935           47 :     return true;
    4936              : }
    4937              : 
    4938              : /*
    4939              :  * postgresGetAnalyzeInfoForForeignTable
    4940              :  *      Count tuples in foreign table (just get pg_class.reltuples).
    4941              :  *
    4942              :  * can_tablesample determines if the remote relation supports acquiring the
    4943              :  * sample using TABLESAMPLE.
    4944              :  */
    4945              : static double
    4946           46 : postgresGetAnalyzeInfoForForeignTable(Relation relation, bool *can_tablesample)
    4947              : {
    4948              :     ForeignTable *table;
    4949              :     UserMapping *user;
    4950              :     PGconn     *conn;
    4951              :     StringInfoData sql;
    4952              :     PGresult   *res;
    4953              :     double      reltuples;
    4954              :     char        relkind;
    4955              : 
    4956              :     /* assume the remote relation does not support TABLESAMPLE */
    4957           46 :     *can_tablesample = false;
    4958              : 
    4959              :     /*
    4960              :      * Get the connection to use.  We do the remote access as the table's
    4961              :      * owner, even if the ANALYZE was started by some other user.
    4962              :      */
    4963           46 :     table = GetForeignTable(RelationGetRelid(relation));
    4964           46 :     user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
    4965           46 :     conn = GetConnection(user, false, NULL);
    4966              : 
    4967              :     /*
    4968              :      * Construct command to get page count for relation.
    4969              :      */
    4970           46 :     initStringInfo(&sql);
    4971           46 :     deparseAnalyzeInfoSql(&sql, relation);
    4972              : 
    4973           46 :     res = pgfdw_exec_query(conn, sql.data, NULL);
    4974           46 :     if (PQresultStatus(res) != PGRES_TUPLES_OK)
    4975            0 :         pgfdw_report_error(res, conn, sql.data);
    4976              : 
    4977           46 :     if (PQntuples(res) != 1 || PQnfields(res) != 2)
    4978            0 :         elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
    4979           46 :     reltuples = strtod(PQgetvalue(res, 0, 0), NULL);
    4980           46 :     relkind = *(PQgetvalue(res, 0, 1));
    4981           46 :     PQclear(res);
    4982              : 
    4983           46 :     ReleaseConnection(conn);
    4984              : 
    4985              :     /* TABLESAMPLE is supported only for regular tables and matviews */
    4986            0 :     *can_tablesample = (relkind == RELKIND_RELATION ||
    4987           46 :                         relkind == RELKIND_MATVIEW ||
    4988           46 :                         relkind == RELKIND_PARTITIONED_TABLE);
    4989              : 
    4990           46 :     return reltuples;
    4991              : }
    4992              : 
    4993              : /*
    4994              :  * Acquire a random sample of rows from foreign table managed by postgres_fdw.
    4995              :  *
    4996              :  * Selected rows are returned in the caller-allocated array rows[],
    4997              :  * which must have at least targrows entries.
    4998              :  * The actual number of rows selected is returned as the function result.
    4999              :  * We also count the total number of rows in the table and return it into
    5000              :  * *totalrows.  Note that *totaldeadrows is always set to 0.
    5001              :  *
    5002              :  * Note that the returned list of rows is not always in order by physical
    5003              :  * position in the table.  Therefore, correlation estimates derived later
    5004              :  * may be meaningless, but it's OK because we don't use the estimates
    5005              :  * currently (the planner only pays attention to correlation for indexscans).
    5006              :  */
    5007              : static int
    5008           47 : postgresAcquireSampleRowsFunc(Relation relation, int elevel,
    5009              :                               HeapTuple *rows, int targrows,
    5010              :                               double *totalrows,
    5011              :                               double *totaldeadrows)
    5012              : {
    5013              :     PgFdwAnalyzeState astate;
    5014              :     ForeignTable *table;
    5015              :     ForeignServer *server;
    5016              :     UserMapping *user;
    5017              :     PGconn     *conn;
    5018              :     int         server_version_num;
    5019           47 :     PgFdwSamplingMethod method = ANALYZE_SAMPLE_AUTO;   /* auto is default */
    5020           47 :     double      sample_frac = -1.0;
    5021           47 :     double      reltuples = -1.0;
    5022              :     unsigned int cursor_number;
    5023              :     StringInfoData sql;
    5024              :     PGresult   *res;
    5025              :     char        fetch_sql[64];
    5026              :     int         fetch_size;
    5027              :     ListCell   *lc;
    5028              : 
    5029              :     /* Initialize workspace state */
    5030           47 :     astate.rel = relation;
    5031           47 :     astate.attinmeta = TupleDescGetAttInMetadata(RelationGetDescr(relation));
    5032              : 
    5033           47 :     astate.rows = rows;
    5034           47 :     astate.targrows = targrows;
    5035           47 :     astate.numrows = 0;
    5036           47 :     astate.samplerows = 0;
    5037           47 :     astate.rowstoskip = -1;     /* -1 means not set yet */
    5038           47 :     reservoir_init_selection_state(&astate.rstate, targrows);
    5039              : 
    5040              :     /* Remember ANALYZE context, and create a per-tuple temp context */
    5041           47 :     astate.anl_cxt = CurrentMemoryContext;
    5042           47 :     astate.temp_cxt = AllocSetContextCreate(CurrentMemoryContext,
    5043              :                                             "postgres_fdw temporary data",
    5044              :                                             ALLOCSET_SMALL_SIZES);
    5045              : 
    5046              :     /*
    5047              :      * Get the connection to use.  We do the remote access as the table's
    5048              :      * owner, even if the ANALYZE was started by some other user.
    5049              :      */
    5050           47 :     table = GetForeignTable(RelationGetRelid(relation));
    5051           47 :     server = GetForeignServer(table->serverid);
    5052           47 :     user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
    5053           47 :     conn = GetConnection(user, false, NULL);
    5054              : 
    5055              :     /* We'll need server version, so fetch it now. */
    5056           47 :     server_version_num = PQserverVersion(conn);
    5057              : 
    5058              :     /*
    5059              :      * What sampling method should we use?
    5060              :      */
    5061          214 :     foreach(lc, server->options)
    5062              :     {
    5063          172 :         DefElem    *def = (DefElem *) lfirst(lc);
    5064              : 
    5065          172 :         if (strcmp(def->defname, "analyze_sampling") == 0)
    5066              :         {
    5067            5 :             char       *value = defGetString(def);
    5068              : 
    5069            5 :             if (strcmp(value, "off") == 0)
    5070            1 :                 method = ANALYZE_SAMPLE_OFF;
    5071            4 :             else if (strcmp(value, "auto") == 0)
    5072            1 :                 method = ANALYZE_SAMPLE_AUTO;
    5073            3 :             else if (strcmp(value, "random") == 0)
    5074            1 :                 method = ANALYZE_SAMPLE_RANDOM;
    5075            2 :             else if (strcmp(value, "system") == 0)
    5076            1 :                 method = ANALYZE_SAMPLE_SYSTEM;
    5077            1 :             else if (strcmp(value, "bernoulli") == 0)
    5078            1 :                 method = ANALYZE_SAMPLE_BERNOULLI;
    5079              : 
    5080            5 :             break;
    5081              :         }
    5082              :     }
    5083              : 
    5084          108 :     foreach(lc, table->options)
    5085              :     {
    5086           61 :         DefElem    *def = (DefElem *) lfirst(lc);
    5087              : 
    5088           61 :         if (strcmp(def->defname, "analyze_sampling") == 0)
    5089              :         {
    5090            0 :             char       *value = defGetString(def);
    5091              : 
    5092            0 :             if (strcmp(value, "off") == 0)
    5093            0 :                 method = ANALYZE_SAMPLE_OFF;
    5094            0 :             else if (strcmp(value, "auto") == 0)
    5095            0 :                 method = ANALYZE_SAMPLE_AUTO;
    5096            0 :             else if (strcmp(value, "random") == 0)
    5097            0 :                 method = ANALYZE_SAMPLE_RANDOM;
    5098            0 :             else if (strcmp(value, "system") == 0)
    5099            0 :                 method = ANALYZE_SAMPLE_SYSTEM;
    5100            0 :             else if (strcmp(value, "bernoulli") == 0)
    5101            0 :                 method = ANALYZE_SAMPLE_BERNOULLI;
    5102              : 
    5103            0 :             break;
    5104              :         }
    5105              :     }
    5106              : 
    5107              :     /*
    5108              :      * Error-out if explicitly required one of the TABLESAMPLE methods, but
    5109              :      * the server does not support it.
    5110              :      */
    5111           47 :     if ((server_version_num < 95000) &&
    5112            0 :         (method == ANALYZE_SAMPLE_SYSTEM ||
    5113              :          method == ANALYZE_SAMPLE_BERNOULLI))
    5114            0 :         ereport(ERROR,
    5115              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5116              :                  errmsg("remote server does not support TABLESAMPLE feature")));
    5117              : 
    5118              :     /*
    5119              :      * If we've decided to do remote sampling, calculate the sampling rate. We
    5120              :      * need to get the number of tuples from the remote server, but skip that
    5121              :      * network round-trip if not needed.
    5122              :      */
    5123           47 :     if (method != ANALYZE_SAMPLE_OFF)
    5124              :     {
    5125              :         bool        can_tablesample;
    5126              : 
    5127           46 :         reltuples = postgresGetAnalyzeInfoForForeignTable(relation,
    5128              :                                                           &can_tablesample);
    5129              : 
    5130              :         /*
    5131              :          * Make sure we're not choosing TABLESAMPLE when the remote relation
    5132              :          * does not support that. But only do this for "auto" - if the user
    5133              :          * explicitly requested BERNOULLI/SYSTEM, it's better to fail.
    5134              :          */
    5135           46 :         if (!can_tablesample && (method == ANALYZE_SAMPLE_AUTO))
    5136            0 :             method = ANALYZE_SAMPLE_RANDOM;
    5137              : 
    5138              :         /*
    5139              :          * Remote's reltuples could be 0 or -1 if the table has never been
    5140              :          * vacuumed/analyzed.  In that case, disable sampling after all.
    5141              :          */
    5142           46 :         if ((reltuples <= 0) || (targrows >= reltuples))
    5143           46 :             method = ANALYZE_SAMPLE_OFF;
    5144              :         else
    5145              :         {
    5146              :             /*
    5147              :              * All supported sampling methods require sampling rate, not
    5148              :              * target rows directly, so we calculate that using the remote
    5149              :              * reltuples value. That's imperfect, because it might be off a
    5150              :              * good deal, but that's not something we can (or should) address
    5151              :              * here.
    5152              :              *
    5153              :              * If reltuples is too low (i.e. when table grew), we'll end up
    5154              :              * sampling more rows - but then we'll apply the local sampling,
    5155              :              * so we get the expected sample size. This is the same outcome as
    5156              :              * without remote sampling.
    5157              :              *
    5158              :              * If reltuples is too high (e.g. after bulk DELETE), we will end
    5159              :              * up sampling too few rows.
    5160              :              *
    5161              :              * We can't really do much better here - we could try sampling a
    5162              :              * bit more rows, but we don't know how off the reltuples value is
    5163              :              * so how much is "a bit more"?
    5164              :              *
    5165              :              * Furthermore, the targrows value for partitions is determined
    5166              :              * based on table size (relpages), which can be off in different
    5167              :              * ways too. Adjusting the sampling rate here might make the issue
    5168              :              * worse.
    5169              :              */
    5170            0 :             sample_frac = targrows / reltuples;
    5171              : 
    5172              :             /*
    5173              :              * We should never get sampling rate outside the valid range
    5174              :              * (between 0.0 and 1.0), because those cases should be covered by
    5175              :              * the previous branch that sets ANALYZE_SAMPLE_OFF.
    5176              :              */
    5177              :             Assert(sample_frac >= 0.0 && sample_frac <= 1.0);
    5178              :         }
    5179              :     }
    5180              : 
    5181              :     /*
    5182              :      * For "auto" method, pick the one we believe is best. For servers with
    5183              :      * TABLESAMPLE support we pick BERNOULLI, for old servers we fall-back to
    5184              :      * random() to at least reduce network transfer.
    5185              :      */
    5186           47 :     if (method == ANALYZE_SAMPLE_AUTO)
    5187              :     {
    5188            0 :         if (server_version_num < 95000)
    5189            0 :             method = ANALYZE_SAMPLE_RANDOM;
    5190              :         else
    5191            0 :             method = ANALYZE_SAMPLE_BERNOULLI;
    5192              :     }
    5193              : 
    5194              :     /*
    5195              :      * Construct cursor that retrieves whole rows from remote.
    5196              :      */
    5197           47 :     cursor_number = GetCursorNumber(conn);
    5198           47 :     initStringInfo(&sql);
    5199           47 :     appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
    5200              : 
    5201           47 :     deparseAnalyzeSql(&sql, relation, method, sample_frac, &astate.retrieved_attrs);
    5202              : 
    5203           47 :     res = pgfdw_exec_query(conn, sql.data, NULL);
    5204           47 :     if (PQresultStatus(res) != PGRES_COMMAND_OK)
    5205            0 :         pgfdw_report_error(res, conn, sql.data);
    5206           47 :     PQclear(res);
    5207              : 
    5208              :     /*
    5209              :      * Determine the fetch size.  The default is arbitrary, but shouldn't be
    5210              :      * enormous.
    5211              :      */
    5212           47 :     fetch_size = 100;
    5213          219 :     foreach(lc, server->options)
    5214              :     {
    5215          172 :         DefElem    *def = (DefElem *) lfirst(lc);
    5216              : 
    5217          172 :         if (strcmp(def->defname, "fetch_size") == 0)
    5218              :         {
    5219            0 :             (void) parse_int(defGetString(def), &fetch_size, 0, NULL);
    5220            0 :             break;
    5221              :         }
    5222              :     }
    5223          108 :     foreach(lc, table->options)
    5224              :     {
    5225           61 :         DefElem    *def = (DefElem *) lfirst(lc);
    5226              : 
    5227           61 :         if (strcmp(def->defname, "fetch_size") == 0)
    5228              :         {
    5229            0 :             (void) parse_int(defGetString(def), &fetch_size, 0, NULL);
    5230            0 :             break;
    5231              :         }
    5232              :     }
    5233              : 
    5234              :     /* Construct command to fetch rows from remote. */
    5235           47 :     snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
    5236              :              fetch_size, cursor_number);
    5237              : 
    5238              :     /* Retrieve and process rows a batch at a time. */
    5239              :     for (;;)
    5240          222 :     {
    5241              :         int         numrows;
    5242              :         int         i;
    5243              : 
    5244              :         /* Allow users to cancel long query */
    5245          269 :         CHECK_FOR_INTERRUPTS();
    5246              : 
    5247              :         /*
    5248              :          * XXX possible future improvement: if rowstoskip is large, we could
    5249              :          * issue a MOVE rather than physically fetching the rows, then just
    5250              :          * adjust rowstoskip and samplerows appropriately.
    5251              :          */
    5252              : 
    5253              :         /* Fetch some rows */
    5254          269 :         res = pgfdw_exec_query(conn, fetch_sql, NULL);
    5255              :         /* On error, report the original query, not the FETCH. */
    5256          269 :         if (PQresultStatus(res) != PGRES_TUPLES_OK)
    5257            0 :             pgfdw_report_error(res, conn, sql.data);
    5258              : 
    5259              :         /* Process whatever we got. */
    5260          269 :         numrows = PQntuples(res);
    5261        22996 :         for (i = 0; i < numrows; i++)
    5262        22728 :             analyze_row_processor(res, i, &astate);
    5263              : 
    5264          268 :         PQclear(res);
    5265              : 
    5266              :         /* Must be EOF if we didn't get all the rows requested. */
    5267          268 :         if (numrows < fetch_size)
    5268           46 :             break;
    5269              :     }
    5270              : 
    5271              :     /* Close the cursor, just to be tidy. */
    5272           46 :     close_cursor(conn, cursor_number, NULL);
    5273              : 
    5274           46 :     ReleaseConnection(conn);
    5275              : 
    5276              :     /* We assume that we have no dead tuple. */
    5277           46 :     *totaldeadrows = 0.0;
    5278              : 
    5279              :     /*
    5280              :      * Without sampling, we've retrieved all living tuples from foreign
    5281              :      * server, so report that as totalrows.  Otherwise use the reltuples
    5282              :      * estimate we got from the remote side.
    5283              :      */
    5284           46 :     if (method == ANALYZE_SAMPLE_OFF)
    5285           46 :         *totalrows = astate.samplerows;
    5286              :     else
    5287            0 :         *totalrows = reltuples;
    5288              : 
    5289              :     /*
    5290              :      * Emit some interesting relation info
    5291              :      */
    5292           46 :     ereport(elevel,
    5293              :             (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
    5294              :                     RelationGetRelationName(relation),
    5295              :                     *totalrows, astate.numrows)));
    5296              : 
    5297           46 :     return astate.numrows;
    5298              : }
    5299              : 
    5300              : /*
    5301              :  * Collect sample rows from the result of query.
    5302              :  *   - Use all tuples in sample until target # of samples are collected.
    5303              :  *   - Subsequently, replace already-sampled tuples randomly.
    5304              :  */
    5305              : static void
    5306        22728 : analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
    5307              : {
    5308        22728 :     int         targrows = astate->targrows;
    5309              :     int         pos;            /* array index to store tuple in */
    5310              :     MemoryContext oldcontext;
    5311              : 
    5312              :     /* Always increment sample row counter. */
    5313        22728 :     astate->samplerows += 1;
    5314              : 
    5315              :     /*
    5316              :      * Determine the slot where this sample row should be stored.  Set pos to
    5317              :      * negative value to indicate the row should be skipped.
    5318              :      */
    5319        22728 :     if (astate->numrows < targrows)
    5320              :     {
    5321              :         /* First targrows rows are always included into the sample */
    5322        22728 :         pos = astate->numrows++;
    5323              :     }
    5324              :     else
    5325              :     {
    5326              :         /*
    5327              :          * Now we start replacing tuples in the sample until we reach the end
    5328              :          * of the relation.  Same algorithm as in acquire_sample_rows in
    5329              :          * analyze.c; see Jeff Vitter's paper.
    5330              :          */
    5331            0 :         if (astate->rowstoskip < 0)
    5332            0 :             astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
    5333              : 
    5334            0 :         if (astate->rowstoskip <= 0)
    5335              :         {
    5336              :             /* Choose a random reservoir element to replace. */
    5337            0 :             pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
    5338              :             Assert(pos >= 0 && pos < targrows);
    5339            0 :             heap_freetuple(astate->rows[pos]);
    5340              :         }
    5341              :         else
    5342              :         {
    5343              :             /* Skip this tuple. */
    5344            0 :             pos = -1;
    5345              :         }
    5346              : 
    5347            0 :         astate->rowstoskip -= 1;
    5348              :     }
    5349              : 
    5350        22728 :     if (pos >= 0)
    5351              :     {
    5352              :         /*
    5353              :          * Create sample tuple from current result row, and store it in the
    5354              :          * position determined above.  The tuple has to be created in anl_cxt.
    5355              :          */
    5356        22728 :         oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
    5357              : 
    5358        22728 :         astate->rows[pos] = make_tuple_from_result_row(res, row,
    5359              :                                                        astate->rel,
    5360              :                                                        astate->attinmeta,
    5361              :                                                        astate->retrieved_attrs,
    5362              :                                                        NULL,
    5363              :                                                        astate->temp_cxt);
    5364              : 
    5365        22727 :         MemoryContextSwitchTo(oldcontext);
    5366              :     }
    5367        22727 : }
    5368              : 
    5369              : /*
    5370              :  * Import a foreign schema
    5371              :  */
    5372              : static List *
    5373           10 : postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
    5374              : {
    5375           10 :     List       *commands = NIL;
    5376           10 :     bool        import_collate = true;
    5377           10 :     bool        import_default = false;
    5378           10 :     bool        import_generated = true;
    5379           10 :     bool        import_not_null = true;
    5380              :     ForeignServer *server;
    5381              :     UserMapping *mapping;
    5382              :     PGconn     *conn;
    5383              :     StringInfoData buf;
    5384              :     PGresult   *res;
    5385              :     int         numrows,
    5386              :                 i;
    5387              :     ListCell   *lc;
    5388              : 
    5389              :     /* Parse statement options */
    5390           14 :     foreach(lc, stmt->options)
    5391              :     {
    5392            4 :         DefElem    *def = (DefElem *) lfirst(lc);
    5393              : 
    5394            4 :         if (strcmp(def->defname, "import_collate") == 0)
    5395            1 :             import_collate = defGetBoolean(def);
    5396            3 :         else if (strcmp(def->defname, "import_default") == 0)
    5397            1 :             import_default = defGetBoolean(def);
    5398            2 :         else if (strcmp(def->defname, "import_generated") == 0)
    5399            1 :             import_generated = defGetBoolean(def);
    5400            1 :         else if (strcmp(def->defname, "import_not_null") == 0)
    5401            1 :             import_not_null = defGetBoolean(def);
    5402              :         else
    5403            0 :             ereport(ERROR,
    5404              :                     (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
    5405              :                      errmsg("invalid option \"%s\"", def->defname)));
    5406              :     }
    5407              : 
    5408              :     /*
    5409              :      * Get connection to the foreign server.  Connection manager will
    5410              :      * establish new connection if necessary.
    5411              :      */
    5412           10 :     server = GetForeignServer(serverOid);
    5413           10 :     mapping = GetUserMapping(GetUserId(), server->serverid);
    5414           10 :     conn = GetConnection(mapping, false, NULL);
    5415              : 
    5416              :     /* Don't attempt to import collation if remote server hasn't got it */
    5417           10 :     if (PQserverVersion(conn) < 90100)
    5418            0 :         import_collate = false;
    5419              : 
    5420              :     /* Create workspace for strings */
    5421           10 :     initStringInfo(&buf);
    5422              : 
    5423              :     /* Check that the schema really exists */
    5424           10 :     appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
    5425           10 :     deparseStringLiteral(&buf, stmt->remote_schema);
    5426              : 
    5427           10 :     res = pgfdw_exec_query(conn, buf.data, NULL);
    5428           10 :     if (PQresultStatus(res) != PGRES_TUPLES_OK)
    5429            0 :         pgfdw_report_error(res, conn, buf.data);
    5430              : 
    5431           10 :     if (PQntuples(res) != 1)
    5432            1 :         ereport(ERROR,
    5433              :                 (errcode(ERRCODE_FDW_SCHEMA_NOT_FOUND),
    5434              :                  errmsg("schema \"%s\" is not present on foreign server \"%s\"",
    5435              :                         stmt->remote_schema, server->servername)));
    5436              : 
    5437            9 :     PQclear(res);
    5438            9 :     resetStringInfo(&buf);
    5439              : 
    5440              :     /*
    5441              :      * Fetch all table data from this schema, possibly restricted by EXCEPT or
    5442              :      * LIMIT TO.  (We don't actually need to pay any attention to EXCEPT/LIMIT
    5443              :      * TO here, because the core code will filter the statements we return
    5444              :      * according to those lists anyway.  But it should save a few cycles to
    5445              :      * not process excluded tables in the first place.)
    5446              :      *
    5447              :      * Import table data for partitions only when they are explicitly
    5448              :      * specified in LIMIT TO clause. Otherwise ignore them and only include
    5449              :      * the definitions of the root partitioned tables to allow access to the
    5450              :      * complete remote data set locally in the schema imported.
    5451              :      *
    5452              :      * Note: because we run the connection with search_path restricted to
    5453              :      * pg_catalog, the format_type() and pg_get_expr() outputs will always
    5454              :      * include a schema name for types/functions in other schemas, which is
    5455              :      * what we want.
    5456              :      */
    5457            9 :     appendStringInfoString(&buf,
    5458              :                            "SELECT relname, "
    5459              :                            "  attname, "
    5460              :                            "  format_type(atttypid, atttypmod), "
    5461              :                            "  attnotnull, "
    5462              :                            "  pg_get_expr(adbin, adrelid), ");
    5463              : 
    5464              :     /* Generated columns are supported since Postgres 12 */
    5465            9 :     if (PQserverVersion(conn) >= 120000)
    5466            9 :         appendStringInfoString(&buf,
    5467              :                                "  attgenerated, ");
    5468              :     else
    5469            0 :         appendStringInfoString(&buf,
    5470              :                                "  NULL, ");
    5471              : 
    5472            9 :     if (import_collate)
    5473            8 :         appendStringInfoString(&buf,
    5474              :                                "  collname, "
    5475              :                                "  collnsp.nspname ");
    5476              :     else
    5477            1 :         appendStringInfoString(&buf,
    5478              :                                "  NULL, NULL ");
    5479              : 
    5480            9 :     appendStringInfoString(&buf,
    5481              :                            "FROM pg_class c "
    5482              :                            "  JOIN pg_namespace n ON "
    5483              :                            "    relnamespace = n.oid "
    5484              :                            "  LEFT JOIN pg_attribute a ON "
    5485              :                            "    attrelid = c.oid AND attnum > 0 "
    5486              :                            "      AND NOT attisdropped "
    5487              :                            "  LEFT JOIN pg_attrdef ad ON "
    5488              :                            "    adrelid = c.oid AND adnum = attnum ");
    5489              : 
    5490            9 :     if (import_collate)
    5491            8 :         appendStringInfoString(&buf,
    5492              :                                "  LEFT JOIN pg_collation coll ON "
    5493              :                                "    coll.oid = attcollation "
    5494              :                                "  LEFT JOIN pg_namespace collnsp ON "
    5495              :                                "    collnsp.oid = collnamespace ");
    5496              : 
    5497            9 :     appendStringInfoString(&buf,
    5498              :                            "WHERE c.relkind IN ("
    5499              :                            CppAsString2(RELKIND_RELATION) ","
    5500              :                            CppAsString2(RELKIND_VIEW) ","
    5501              :                            CppAsString2(RELKIND_FOREIGN_TABLE) ","
    5502              :                            CppAsString2(RELKIND_MATVIEW) ","
    5503              :                            CppAsString2(RELKIND_PARTITIONED_TABLE) ") "
    5504              :                            "  AND n.nspname = ");
    5505            9 :     deparseStringLiteral(&buf, stmt->remote_schema);
    5506              : 
    5507              :     /* Partitions are supported since Postgres 10 */
    5508            9 :     if (PQserverVersion(conn) >= 100000 &&
    5509            9 :         stmt->list_type != FDW_IMPORT_SCHEMA_LIMIT_TO)
    5510            5 :         appendStringInfoString(&buf, " AND NOT c.relispartition ");
    5511              : 
    5512              :     /* Apply restrictions for LIMIT TO and EXCEPT */
    5513            9 :     if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
    5514            5 :         stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
    5515              :     {
    5516            5 :         bool        first_item = true;
    5517              : 
    5518            5 :         appendStringInfoString(&buf, " AND c.relname ");
    5519            5 :         if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
    5520            1 :             appendStringInfoString(&buf, "NOT ");
    5521            5 :         appendStringInfoString(&buf, "IN (");
    5522              : 
    5523              :         /* Append list of table names within IN clause */
    5524           15 :         foreach(lc, stmt->table_list)
    5525              :         {
    5526           10 :             RangeVar   *rv = (RangeVar *) lfirst(lc);
    5527              : 
    5528           10 :             if (first_item)
    5529            5 :                 first_item = false;
    5530              :             else
    5531            5 :                 appendStringInfoString(&buf, ", ");
    5532           10 :             deparseStringLiteral(&buf, rv->relname);
    5533              :         }
    5534            5 :         appendStringInfoChar(&buf, ')');
    5535              :     }
    5536              : 
    5537              :     /* Append ORDER BY at the end of query to ensure output ordering */
    5538            9 :     appendStringInfoString(&buf, " ORDER BY c.relname, a.attnum");
    5539              : 
    5540              :     /* Fetch the data */
    5541            9 :     res = pgfdw_exec_query(conn, buf.data, NULL);
    5542            9 :     if (PQresultStatus(res) != PGRES_TUPLES_OK)
    5543            0 :         pgfdw_report_error(res, conn, buf.data);
    5544              : 
    5545              :     /* Process results */
    5546            9 :     numrows = PQntuples(res);
    5547              :     /* note: incrementation of i happens in inner loop's while() test */
    5548           47 :     for (i = 0; i < numrows;)
    5549              :     {
    5550           38 :         char       *tablename = PQgetvalue(res, i, 0);
    5551           38 :         bool        first_item = true;
    5552              : 
    5553           38 :         resetStringInfo(&buf);
    5554           38 :         appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
    5555              :                          quote_identifier(tablename));
    5556              : 
    5557              :         /* Scan all rows for this table */
    5558              :         do
    5559              :         {
    5560              :             char       *attname;
    5561              :             char       *typename;
    5562              :             char       *attnotnull;
    5563              :             char       *attgenerated;
    5564              :             char       *attdefault;
    5565              :             char       *collname;
    5566              :             char       *collnamespace;
    5567              : 
    5568              :             /* If table has no columns, we'll see nulls here */
    5569           75 :             if (PQgetisnull(res, i, 1))
    5570            5 :                 continue;
    5571              : 
    5572           70 :             attname = PQgetvalue(res, i, 1);
    5573           70 :             typename = PQgetvalue(res, i, 2);
    5574           70 :             attnotnull = PQgetvalue(res, i, 3);
    5575           70 :             attdefault = PQgetisnull(res, i, 4) ? NULL :
    5576           15 :                 PQgetvalue(res, i, 4);
    5577           70 :             attgenerated = PQgetisnull(res, i, 5) ? NULL :
    5578           70 :                 PQgetvalue(res, i, 5);
    5579           70 :             collname = PQgetisnull(res, i, 6) ? NULL :
    5580           19 :                 PQgetvalue(res, i, 6);
    5581           70 :             collnamespace = PQgetisnull(res, i, 7) ? NULL :
    5582           19 :                 PQgetvalue(res, i, 7);
    5583              : 
    5584           70 :             if (first_item)
    5585           33 :                 first_item = false;
    5586              :             else
    5587           37 :                 appendStringInfoString(&buf, ",\n");
    5588              : 
    5589              :             /* Print column name and type */
    5590           70 :             appendStringInfo(&buf, "  %s %s",
    5591              :                              quote_identifier(attname),
    5592              :                              typename);
    5593              : 
    5594              :             /*
    5595              :              * Add column_name option so that renaming the foreign table's
    5596              :              * column doesn't break the association to the underlying column.
    5597              :              */
    5598           70 :             appendStringInfoString(&buf, " OPTIONS (column_name ");
    5599           70 :             deparseStringLiteral(&buf, attname);
    5600           70 :             appendStringInfoChar(&buf, ')');
    5601              : 
    5602              :             /* Add COLLATE if needed */
    5603           70 :             if (import_collate && collname != NULL && collnamespace != NULL)
    5604           19 :                 appendStringInfo(&buf, " COLLATE %s.%s",
    5605              :                                  quote_identifier(collnamespace),
    5606              :                                  quote_identifier(collname));
    5607              : 
    5608              :             /* Add DEFAULT if needed */
    5609           70 :             if (import_default && attdefault != NULL &&
    5610            3 :                 (!attgenerated || !attgenerated[0]))
    5611            2 :                 appendStringInfo(&buf, " DEFAULT %s", attdefault);
    5612              : 
    5613              :             /* Add GENERATED if needed */
    5614           70 :             if (import_generated && attgenerated != NULL &&
    5615           57 :                 attgenerated[0] == ATTRIBUTE_GENERATED_STORED)
    5616              :             {
    5617              :                 Assert(attdefault != NULL);
    5618            4 :                 appendStringInfo(&buf,
    5619              :                                  " GENERATED ALWAYS AS (%s) STORED",
    5620              :                                  attdefault);
    5621              :             }
    5622              : 
    5623              :             /* Add NOT NULL if needed */
    5624           70 :             if (import_not_null && attnotnull[0] == 't')
    5625            4 :                 appendStringInfoString(&buf, " NOT NULL");
    5626              :         }
    5627           75 :         while (++i < numrows &&
    5628           66 :                strcmp(PQgetvalue(res, i, 0), tablename) == 0);
    5629              : 
    5630              :         /*
    5631              :          * Add server name and table-level options.  We specify remote schema
    5632              :          * and table name as options (the latter to ensure that renaming the
    5633              :          * foreign table doesn't break the association).
    5634              :          */
    5635           38 :         appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
    5636           38 :                          quote_identifier(server->servername));
    5637              : 
    5638           38 :         appendStringInfoString(&buf, "schema_name ");
    5639           38 :         deparseStringLiteral(&buf, stmt->remote_schema);
    5640           38 :         appendStringInfoString(&buf, ", table_name ");
    5641           38 :         deparseStringLiteral(&buf, tablename);
    5642              : 
    5643           38 :         appendStringInfoString(&buf, ");");
    5644              : 
    5645           38 :         commands = lappend(commands, pstrdup(buf.data));
    5646              :     }
    5647            9 :     PQclear(res);
    5648              : 
    5649            9 :     ReleaseConnection(conn);
    5650              : 
    5651            9 :     return commands;
    5652              : }
    5653              : 
    5654              : /*
    5655              :  * Check if reltarget is safe enough to push down semi-join.  Reltarget is not
    5656              :  * safe, if it contains references to inner rel relids, which do not belong to
    5657              :  * outer rel.
    5658              :  */
    5659              : static bool
    5660           64 : semijoin_target_ok(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel)
    5661              : {
    5662              :     List       *vars;
    5663              :     ListCell   *lc;
    5664           64 :     bool        ok = true;
    5665              : 
    5666              :     Assert(joinrel->reltarget);
    5667              : 
    5668           64 :     vars = pull_var_clause((Node *) joinrel->reltarget->exprs, PVC_INCLUDE_PLACEHOLDERS);
    5669              : 
    5670          443 :     foreach(lc, vars)
    5671              :     {
    5672          394 :         Var        *var = (Var *) lfirst(lc);
    5673              : 
    5674          394 :         if (!IsA(var, Var))
    5675            0 :             continue;
    5676              : 
    5677          394 :         if (bms_is_member(var->varno, innerrel->relids))
    5678              :         {
    5679              :             /*
    5680              :              * The planner can create semi-join, which refers to inner rel
    5681              :              * vars in its target list. However, we deparse semi-join as an
    5682              :              * exists() subquery, so can't handle references to inner rel in
    5683              :              * the target list.
    5684              :              */
    5685              :             Assert(!bms_is_member(var->varno, outerrel->relids));
    5686           15 :             ok = false;
    5687           15 :             break;
    5688              :         }
    5689              :     }
    5690           64 :     return ok;
    5691              : }
    5692              : 
    5693              : /*
    5694              :  * Assess whether the join between inner and outer relations can be pushed down
    5695              :  * to the foreign server. As a side effect, save information we obtain in this
    5696              :  * function to PgFdwRelationInfo passed in.
    5697              :  */
    5698              : static bool
    5699          396 : foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
    5700              :                 RelOptInfo *outerrel, RelOptInfo *innerrel,
    5701              :                 JoinPathExtraData *extra)
    5702              : {
    5703              :     PgFdwRelationInfo *fpinfo;
    5704              :     PgFdwRelationInfo *fpinfo_o;
    5705              :     PgFdwRelationInfo *fpinfo_i;
    5706              :     ListCell   *lc;
    5707              :     List       *joinclauses;
    5708              : 
    5709              :     /*
    5710              :      * We support pushing down INNER, LEFT, RIGHT, FULL OUTER and SEMI joins.
    5711              :      * Constructing queries representing ANTI joins is hard, hence not
    5712              :      * considered right now.
    5713              :      */
    5714          396 :     if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
    5715          129 :         jointype != JOIN_RIGHT && jointype != JOIN_FULL &&
    5716              :         jointype != JOIN_SEMI)
    5717           19 :         return false;
    5718              : 
    5719              :     /*
    5720              :      * We can't push down semi-join if its reltarget is not safe
    5721              :      */
    5722          377 :     if ((jointype == JOIN_SEMI) && !semijoin_target_ok(root, joinrel, outerrel, innerrel))
    5723           15 :         return false;
    5724              : 
    5725              :     /*
    5726              :      * If either of the joining relations is marked as unsafe to pushdown, the
    5727              :      * join can not be pushed down.
    5728              :      */
    5729          362 :     fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
    5730          362 :     fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
    5731          362 :     fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
    5732          362 :     if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
    5733          357 :         !fpinfo_i || !fpinfo_i->pushdown_safe)
    5734            5 :         return false;
    5735              : 
    5736              :     /*
    5737              :      * If joining relations have local conditions, those conditions are
    5738              :      * required to be applied before joining the relations. Hence the join can
    5739              :      * not be pushed down.
    5740              :      */
    5741          357 :     if (fpinfo_o->local_conds || fpinfo_i->local_conds)
    5742            9 :         return false;
    5743              : 
    5744              :     /*
    5745              :      * Merge FDW options.  We might be tempted to do this after we have deemed
    5746              :      * the foreign join to be OK.  But we must do this beforehand so that we
    5747              :      * know which quals can be evaluated on the foreign server, which might
    5748              :      * depend on shippable_extensions.
    5749              :      */
    5750          348 :     fpinfo->server = fpinfo_o->server;
    5751          348 :     merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
    5752              : 
    5753              :     /*
    5754              :      * Separate restrict list into join quals and pushed-down (other) quals.
    5755              :      *
    5756              :      * Join quals belonging to an outer join must all be shippable, else we
    5757              :      * cannot execute the join remotely.  Add such quals to 'joinclauses'.
    5758              :      *
    5759              :      * Add other quals to fpinfo->remote_conds if they are shippable, else to
    5760              :      * fpinfo->local_conds.  In an inner join it's okay to execute conditions
    5761              :      * either locally or remotely; the same is true for pushed-down conditions
    5762              :      * at an outer join.
    5763              :      *
    5764              :      * Note we might return failure after having already scribbled on
    5765              :      * fpinfo->remote_conds and fpinfo->local_conds.  That's okay because we
    5766              :      * won't consult those lists again if we deem the join unshippable.
    5767              :      */
    5768          348 :     joinclauses = NIL;
    5769          691 :     foreach(lc, extra->restrictlist)
    5770              :     {
    5771          346 :         RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
    5772          346 :         bool        is_remote_clause = is_foreign_expr(root, joinrel,
    5773              :                                                        rinfo->clause);
    5774              : 
    5775          346 :         if (IS_OUTER_JOIN(jointype) &&
    5776          129 :             !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
    5777              :         {
    5778          113 :             if (!is_remote_clause)
    5779            3 :                 return false;
    5780          110 :             joinclauses = lappend(joinclauses, rinfo);
    5781              :         }
    5782              :         else
    5783              :         {
    5784          233 :             if (is_remote_clause)
    5785          221 :                 fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
    5786              :             else
    5787           12 :                 fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
    5788              :         }
    5789              :     }
    5790              : 
    5791              :     /*
    5792              :      * deparseExplicitTargetList() isn't smart enough to handle anything other
    5793              :      * than a Var.  In particular, if there's some PlaceHolderVar that would
    5794              :      * need to be evaluated within this join tree (because there's an upper
    5795              :      * reference to a quantity that may go to NULL as a result of an outer
    5796              :      * join), then we can't try to push the join down because we'll fail when
    5797              :      * we get to deparseExplicitTargetList().  However, a PlaceHolderVar that
    5798              :      * needs to be evaluated *at the top* of this join tree is OK, because we
    5799              :      * can do that locally after fetching the results from the remote side.
    5800              :      */
    5801          348 :     foreach(lc, root->placeholder_list)
    5802              :     {
    5803           11 :         PlaceHolderInfo *phinfo = lfirst(lc);
    5804              :         Relids      relids;
    5805              : 
    5806              :         /* PlaceHolderInfo refers to parent relids, not child relids. */
    5807           11 :         relids = IS_OTHER_REL(joinrel) ?
    5808           22 :             joinrel->top_parent_relids : joinrel->relids;
    5809              : 
    5810           22 :         if (bms_is_subset(phinfo->ph_eval_at, relids) &&
    5811           11 :             bms_nonempty_difference(relids, phinfo->ph_eval_at))
    5812            8 :             return false;
    5813              :     }
    5814              : 
    5815              :     /* Save the join clauses, for later use. */
    5816          337 :     fpinfo->joinclauses = joinclauses;
    5817              : 
    5818          337 :     fpinfo->outerrel = outerrel;
    5819          337 :     fpinfo->innerrel = innerrel;
    5820          337 :     fpinfo->jointype = jointype;
    5821              : 
    5822              :     /*
    5823              :      * By default, both the input relations are not required to be deparsed as
    5824              :      * subqueries, but there might be some relations covered by the input
    5825              :      * relations that are required to be deparsed as subqueries, so save the
    5826              :      * relids of those relations for later use by the deparser.
    5827              :      */
    5828          337 :     fpinfo->make_outerrel_subquery = false;
    5829          337 :     fpinfo->make_innerrel_subquery = false;
    5830              :     Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
    5831              :     Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
    5832          674 :     fpinfo->lower_subquery_rels = bms_union(fpinfo_o->lower_subquery_rels,
    5833          337 :                                             fpinfo_i->lower_subquery_rels);
    5834          674 :     fpinfo->hidden_subquery_rels = bms_union(fpinfo_o->hidden_subquery_rels,
    5835          337 :                                              fpinfo_i->hidden_subquery_rels);
    5836              : 
    5837              :     /*
    5838              :      * Pull the other remote conditions from the joining relations into join
    5839              :      * clauses or other remote clauses (remote_conds) of this relation
    5840              :      * wherever possible. This avoids building subqueries at every join step.
    5841              :      *
    5842              :      * For an inner join, clauses from both the relations are added to the
    5843              :      * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
    5844              :      * the outer side are added to remote_conds since those can be evaluated
    5845              :      * after the join is evaluated. The clauses from inner side are added to
    5846              :      * the joinclauses, since they need to be evaluated while constructing the
    5847              :      * join.
    5848              :      *
    5849              :      * For SEMI-JOIN clauses from inner relation can not be added to
    5850              :      * remote_conds, but should be treated as join clauses (as they are
    5851              :      * deparsed to EXISTS subquery, where inner relation can be referred). A
    5852              :      * list of relation ids, which can't be referred to from higher levels, is
    5853              :      * preserved as a hidden_subquery_rels list.
    5854              :      *
    5855              :      * For a FULL OUTER JOIN, the other clauses from either relation can not
    5856              :      * be added to the joinclauses or remote_conds, since each relation acts
    5857              :      * as an outer relation for the other.
    5858              :      *
    5859              :      * The joining sides can not have local conditions, thus no need to test
    5860              :      * shippability of the clauses being pulled up.
    5861              :      */
    5862          337 :     switch (jointype)
    5863              :     {
    5864          191 :         case JOIN_INNER:
    5865          382 :             fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
    5866          191 :                                                fpinfo_i->remote_conds);
    5867          382 :             fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
    5868          191 :                                                fpinfo_o->remote_conds);
    5869          191 :             break;
    5870              : 
    5871           60 :         case JOIN_LEFT:
    5872              : 
    5873              :             /*
    5874              :              * When semi-join is involved in the inner or outer part of the
    5875              :              * left join, it's deparsed as a subquery, and we can't refer to
    5876              :              * its vars on the upper level.
    5877              :              */
    5878           60 :             if (bms_is_empty(fpinfo_i->hidden_subquery_rels))
    5879           56 :                 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
    5880           56 :                                                   fpinfo_i->remote_conds);
    5881           60 :             if (bms_is_empty(fpinfo_o->hidden_subquery_rels))
    5882           60 :                 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
    5883           60 :                                                    fpinfo_o->remote_conds);
    5884           60 :             break;
    5885              : 
    5886            0 :         case JOIN_RIGHT:
    5887              : 
    5888              :             /*
    5889              :              * When semi-join is involved in the inner or outer part of the
    5890              :              * right join, it's deparsed as a subquery, and we can't refer to
    5891              :              * its vars on the upper level.
    5892              :              */
    5893            0 :             if (bms_is_empty(fpinfo_o->hidden_subquery_rels))
    5894            0 :                 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
    5895            0 :                                                   fpinfo_o->remote_conds);
    5896            0 :             if (bms_is_empty(fpinfo_i->hidden_subquery_rels))
    5897            0 :                 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
    5898            0 :                                                    fpinfo_i->remote_conds);
    5899            0 :             break;
    5900              : 
    5901           44 :         case JOIN_SEMI:
    5902           88 :             fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
    5903           44 :                                               fpinfo_i->remote_conds);
    5904           88 :             fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
    5905           44 :                                               fpinfo->remote_conds);
    5906           44 :             fpinfo->remote_conds = list_copy(fpinfo_o->remote_conds);
    5907           88 :             fpinfo->hidden_subquery_rels = bms_union(fpinfo->hidden_subquery_rels,
    5908           44 :                                                      innerrel->relids);
    5909           44 :             break;
    5910              : 
    5911           42 :         case JOIN_FULL:
    5912              : 
    5913              :             /*
    5914              :              * In this case, if any of the input relations has conditions, we
    5915              :              * need to deparse that relation as a subquery so that the
    5916              :              * conditions can be evaluated before the join.  Remember it in
    5917              :              * the fpinfo of this relation so that the deparser can take
    5918              :              * appropriate action.  Also, save the relids of base relations
    5919              :              * covered by that relation for later use by the deparser.
    5920              :              */
    5921           42 :             if (fpinfo_o->remote_conds)
    5922              :             {
    5923           14 :                 fpinfo->make_outerrel_subquery = true;
    5924           14 :                 fpinfo->lower_subquery_rels =
    5925           14 :                     bms_add_members(fpinfo->lower_subquery_rels,
    5926           14 :                                     outerrel->relids);
    5927              :             }
    5928           42 :             if (fpinfo_i->remote_conds)
    5929              :             {
    5930           14 :                 fpinfo->make_innerrel_subquery = true;
    5931           14 :                 fpinfo->lower_subquery_rels =
    5932           14 :                     bms_add_members(fpinfo->lower_subquery_rels,
    5933           14 :                                     innerrel->relids);
    5934              :             }
    5935           42 :             break;
    5936              : 
    5937            0 :         default:
    5938              :             /* Should not happen, we have just checked this above */
    5939            0 :             elog(ERROR, "unsupported join type %d", jointype);
    5940              :     }
    5941              : 
    5942              :     /*
    5943              :      * For an inner join, all restrictions can be treated alike. Treating the
    5944              :      * pushed down conditions as join conditions allows a top level full outer
    5945              :      * join to be deparsed without requiring subqueries.
    5946              :      */
    5947          337 :     if (jointype == JOIN_INNER)
    5948              :     {
    5949              :         Assert(!fpinfo->joinclauses);
    5950          191 :         fpinfo->joinclauses = fpinfo->remote_conds;
    5951          191 :         fpinfo->remote_conds = NIL;
    5952              :     }
    5953          146 :     else if (jointype == JOIN_LEFT || jointype == JOIN_RIGHT || jointype == JOIN_FULL)
    5954              :     {
    5955              :         /*
    5956              :          * Conditions, generated from semi-joins, should be evaluated before
    5957              :          * LEFT/RIGHT/FULL join.
    5958              :          */
    5959          102 :         if (!bms_is_empty(fpinfo_o->hidden_subquery_rels))
    5960              :         {
    5961            0 :             fpinfo->make_outerrel_subquery = true;
    5962            0 :             fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, outerrel->relids);
    5963              :         }
    5964              : 
    5965          102 :         if (!bms_is_empty(fpinfo_i->hidden_subquery_rels))
    5966              :         {
    5967            4 :             fpinfo->make_innerrel_subquery = true;
    5968            4 :             fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, innerrel->relids);
    5969              :         }
    5970              :     }
    5971              : 
    5972              :     /* Mark that this join can be pushed down safely */
    5973          337 :     fpinfo->pushdown_safe = true;
    5974              : 
    5975              :     /* Get user mapping */
    5976          337 :     if (fpinfo->use_remote_estimate)
    5977              :     {
    5978          225 :         if (fpinfo_o->use_remote_estimate)
    5979          159 :             fpinfo->user = fpinfo_o->user;
    5980              :         else
    5981           66 :             fpinfo->user = fpinfo_i->user;
    5982              :     }
    5983              :     else
    5984          112 :         fpinfo->user = NULL;
    5985              : 
    5986              :     /*
    5987              :      * Set # of retrieved rows and cached relation costs to some negative
    5988              :      * value, so that we can detect when they are set to some sensible values,
    5989              :      * during one (usually the first) of the calls to estimate_path_cost_size.
    5990              :      */
    5991          337 :     fpinfo->retrieved_rows = -1;
    5992          337 :     fpinfo->rel_startup_cost = -1;
    5993          337 :     fpinfo->rel_total_cost = -1;
    5994              : 
    5995              :     /*
    5996              :      * Set the string describing this join relation to be used in EXPLAIN
    5997              :      * output of corresponding ForeignScan.  Note that the decoration we add
    5998              :      * to the base relation names mustn't include any digits, or it'll confuse
    5999              :      * postgresExplainForeignScan.
    6000              :      */
    6001          337 :     fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
    6002              :                                      fpinfo_o->relation_name,
    6003              :                                      get_jointype_name(fpinfo->jointype),
    6004              :                                      fpinfo_i->relation_name);
    6005              : 
    6006              :     /*
    6007              :      * Set the relation index.  This is defined as the position of this
    6008              :      * joinrel in the join_rel_list list plus the length of the rtable list.
    6009              :      * Note that since this joinrel is at the end of the join_rel_list list
    6010              :      * when we are called, we can get the position by list_length.
    6011              :      */
    6012              :     Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
    6013          337 :     fpinfo->relation_index =
    6014          337 :         list_length(root->parse->rtable) + list_length(root->join_rel_list);
    6015              : 
    6016          337 :     return true;
    6017              : }
    6018              : 
    6019              : static void
    6020         1527 : add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
    6021              :                                 Path *epq_path, List *restrictlist)
    6022              : {
    6023         1527 :     List       *useful_pathkeys_list = NIL; /* List of all pathkeys */
    6024              :     ListCell   *lc;
    6025              : 
    6026         1527 :     useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
    6027              : 
    6028              :     /*
    6029              :      * Before creating sorted paths, arrange for the passed-in EPQ path, if
    6030              :      * any, to return columns needed by the parent ForeignScan node so that
    6031              :      * they will propagate up through Sort nodes injected below, if necessary.
    6032              :      */
    6033         1527 :     if (epq_path != NULL && useful_pathkeys_list != NIL)
    6034              :     {
    6035           34 :         PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
    6036           34 :         PathTarget *target = copy_pathtarget(epq_path->pathtarget);
    6037              : 
    6038              :         /* Include columns required for evaluating PHVs in the tlist. */
    6039           34 :         add_new_columns_to_pathtarget(target,
    6040           34 :                                       pull_var_clause((Node *) target->exprs,
    6041              :                                                       PVC_RECURSE_PLACEHOLDERS));
    6042              : 
    6043              :         /* Include columns required for evaluating the local conditions. */
    6044           37 :         foreach(lc, fpinfo->local_conds)
    6045              :         {
    6046            3 :             RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
    6047              : 
    6048            3 :             add_new_columns_to_pathtarget(target,
    6049            3 :                                           pull_var_clause((Node *) rinfo->clause,
    6050              :                                                           PVC_RECURSE_PLACEHOLDERS));
    6051              :         }
    6052              : 
    6053              :         /*
    6054              :          * If we have added any new columns, adjust the tlist of the EPQ path.
    6055              :          *
    6056              :          * Note: the plan created using this path will only be used to execute
    6057              :          * EPQ checks, where accuracy of the plan cost and width estimates
    6058              :          * would not be important, so we do not do set_pathtarget_cost_width()
    6059              :          * for the new pathtarget here.  See also postgresGetForeignPlan().
    6060              :          */
    6061           34 :         if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
    6062              :         {
    6063              :             /* The EPQ path is a join path, so it is projection-capable. */
    6064              :             Assert(is_projection_capable_path(epq_path));
    6065              : 
    6066              :             /*
    6067              :              * Use create_projection_path() here, so as to avoid modifying it
    6068              :              * in place.
    6069              :              */
    6070            4 :             epq_path = (Path *) create_projection_path(root,
    6071              :                                                        rel,
    6072              :                                                        epq_path,
    6073              :                                                        target);
    6074              :         }
    6075              :     }
    6076              : 
    6077              :     /* Create one path for each set of pathkeys we found above. */
    6078         2223 :     foreach(lc, useful_pathkeys_list)
    6079              :     {
    6080              :         double      rows;
    6081              :         int         width;
    6082              :         int         disabled_nodes;
    6083              :         Cost        startup_cost;
    6084              :         Cost        total_cost;
    6085          696 :         List       *useful_pathkeys = lfirst(lc);
    6086              :         Path       *sorted_epq_path;
    6087              : 
    6088          696 :         estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
    6089              :                                 &rows, &width, &disabled_nodes,
    6090              :                                 &startup_cost, &total_cost);
    6091              : 
    6092              :         /*
    6093              :          * The EPQ path must be at least as well sorted as the path itself, in
    6094              :          * case it gets used as input to a mergejoin.
    6095              :          */
    6096          696 :         sorted_epq_path = epq_path;
    6097          696 :         if (sorted_epq_path != NULL &&
    6098           34 :             !pathkeys_contained_in(useful_pathkeys,
    6099              :                                    sorted_epq_path->pathkeys))
    6100              :             sorted_epq_path = (Path *)
    6101           26 :                 create_sort_path(root,
    6102              :                                  rel,
    6103              :                                  sorted_epq_path,
    6104              :                                  useful_pathkeys,
    6105              :                                  -1.0);
    6106              : 
    6107          696 :         if (IS_SIMPLE_REL(rel))
    6108          427 :             add_path(rel, (Path *)
    6109          427 :                      create_foreignscan_path(root, rel,
    6110              :                                              NULL,
    6111              :                                              rows,
    6112              :                                              disabled_nodes,
    6113              :                                              startup_cost,
    6114              :                                              total_cost,
    6115              :                                              useful_pathkeys,
    6116              :                                              rel->lateral_relids,
    6117              :                                              sorted_epq_path,
    6118              :                                              NIL,   /* no fdw_restrictinfo
    6119              :                                                      * list */
    6120              :                                              NIL));
    6121              :         else
    6122          269 :             add_path(rel, (Path *)
    6123          269 :                      create_foreign_join_path(root, rel,
    6124              :                                               NULL,
    6125              :                                               rows,
    6126              :                                               disabled_nodes,
    6127              :                                               startup_cost,
    6128              :                                               total_cost,
    6129              :                                               useful_pathkeys,
    6130              :                                               rel->lateral_relids,
    6131              :                                               sorted_epq_path,
    6132              :                                               restrictlist,
    6133              :                                               NIL));
    6134              :     }
    6135         1527 : }
    6136              : 
    6137              : /*
    6138              :  * Parse options from foreign server and apply them to fpinfo.
    6139              :  *
    6140              :  * New options might also require tweaking merge_fdw_options().
    6141              :  */
    6142              : static void
    6143         1192 : apply_server_options(PgFdwRelationInfo *fpinfo)
    6144              : {
    6145              :     ListCell   *lc;
    6146              : 
    6147         5027 :     foreach(lc, fpinfo->server->options)
    6148              :     {
    6149         3835 :         DefElem    *def = (DefElem *) lfirst(lc);
    6150              : 
    6151         3835 :         if (strcmp(def->defname, "use_remote_estimate") == 0)
    6152          116 :             fpinfo->use_remote_estimate = defGetBoolean(def);
    6153         3719 :         else if (strcmp(def->defname, "fdw_startup_cost") == 0)
    6154            6 :             (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
    6155              :                               NULL);
    6156         3713 :         else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
    6157            2 :             (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
    6158              :                               NULL);
    6159         3711 :         else if (strcmp(def->defname, "extensions") == 0)
    6160          928 :             fpinfo->shippable_extensions =
    6161          928 :                 ExtractExtensionList(defGetString(def), false);
    6162         2783 :         else if (strcmp(def->defname, "fetch_size") == 0)
    6163            0 :             (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
    6164         2783 :         else if (strcmp(def->defname, "async_capable") == 0)
    6165          121 :             fpinfo->async_capable = defGetBoolean(def);
    6166              :     }
    6167         1192 : }
    6168              : 
    6169              : /*
    6170              :  * Parse options from foreign table and apply them to fpinfo.
    6171              :  *
    6172              :  * New options might also require tweaking merge_fdw_options().
    6173              :  */
    6174              : static void
    6175         1192 : apply_table_options(PgFdwRelationInfo *fpinfo)
    6176              : {
    6177              :     ListCell   *lc;
    6178              : 
    6179         3469 :     foreach(lc, fpinfo->table->options)
    6180              :     {
    6181         2277 :         DefElem    *def = (DefElem *) lfirst(lc);
    6182              : 
    6183         2277 :         if (strcmp(def->defname, "use_remote_estimate") == 0)
    6184          348 :             fpinfo->use_remote_estimate = defGetBoolean(def);
    6185         1929 :         else if (strcmp(def->defname, "fetch_size") == 0)
    6186            0 :             (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
    6187         1929 :         else if (strcmp(def->defname, "async_capable") == 0)
    6188            0 :             fpinfo->async_capable = defGetBoolean(def);
    6189              :     }
    6190         1192 : }
    6191              : 
    6192              : /*
    6193              :  * Merge FDW options from input relations into a new set of options for a join
    6194              :  * or an upper rel.
    6195              :  *
    6196              :  * For a join relation, FDW-specific information about the inner and outer
    6197              :  * relations is provided using fpinfo_i and fpinfo_o.  For an upper relation,
    6198              :  * fpinfo_o provides the information for the input relation; fpinfo_i is
    6199              :  * expected to NULL.
    6200              :  */
    6201              : static void
    6202          794 : merge_fdw_options(PgFdwRelationInfo *fpinfo,
    6203              :                   const PgFdwRelationInfo *fpinfo_o,
    6204              :                   const PgFdwRelationInfo *fpinfo_i)
    6205              : {
    6206              :     /* We must always have fpinfo_o. */
    6207              :     Assert(fpinfo_o);
    6208              : 
    6209              :     /* fpinfo_i may be NULL, but if present the servers must both match. */
    6210              :     Assert(!fpinfo_i ||
    6211              :            fpinfo_i->server->serverid == fpinfo_o->server->serverid);
    6212              : 
    6213              :     /*
    6214              :      * Copy the server specific FDW options.  (For a join, both relations come
    6215              :      * from the same server, so the server options should have the same value
    6216              :      * for both relations.)
    6217              :      */
    6218          794 :     fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
    6219          794 :     fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
    6220          794 :     fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
    6221          794 :     fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
    6222          794 :     fpinfo->fetch_size = fpinfo_o->fetch_size;
    6223          794 :     fpinfo->async_capable = fpinfo_o->async_capable;
    6224              : 
    6225              :     /* Merge the table level options from either side of the join. */
    6226          794 :     if (fpinfo_i)
    6227              :     {
    6228              :         /*
    6229              :          * We'll prefer to use remote estimates for this join if any table
    6230              :          * from either side of the join is using remote estimates.  This is
    6231              :          * most likely going to be preferred since they're already willing to
    6232              :          * pay the price of a round trip to get the remote EXPLAIN.  In any
    6233              :          * case it's not entirely clear how we might otherwise handle this
    6234              :          * best.
    6235              :          */
    6236          532 :         fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
    6237          184 :             fpinfo_i->use_remote_estimate;
    6238              : 
    6239              :         /*
    6240              :          * Set fetch size to maximum of the joining sides, since we are
    6241              :          * expecting the rows returned by the join to be proportional to the
    6242              :          * relation sizes.
    6243              :          */
    6244          348 :         fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
    6245              : 
    6246              :         /*
    6247              :          * We'll prefer to consider this join async-capable if any table from
    6248              :          * either side of the join is considered async-capable.  This would be
    6249              :          * reasonable because in that case the foreign server would have its
    6250              :          * own resources to scan that table asynchronously, and the join could
    6251              :          * also be computed asynchronously using the resources.
    6252              :          */
    6253          688 :         fpinfo->async_capable = fpinfo_o->async_capable ||
    6254          688 :             fpinfo_i->async_capable;
    6255              :     }
    6256          794 : }
    6257              : 
    6258              : /*
    6259              :  * postgresGetForeignJoinPaths
    6260              :  *      Add possible ForeignPath to joinrel, if join is safe to push down.
    6261              :  */
    6262              : static void
    6263         1360 : postgresGetForeignJoinPaths(PlannerInfo *root,
    6264              :                             RelOptInfo *joinrel,
    6265              :                             RelOptInfo *outerrel,
    6266              :                             RelOptInfo *innerrel,
    6267              :                             JoinType jointype,
    6268              :                             JoinPathExtraData *extra)
    6269              : {
    6270              :     PgFdwRelationInfo *fpinfo;
    6271              :     ForeignPath *joinpath;
    6272              :     double      rows;
    6273              :     int         width;
    6274              :     int         disabled_nodes;
    6275              :     Cost        startup_cost;
    6276              :     Cost        total_cost;
    6277              :     Path       *epq_path;       /* Path to create plan to be executed when
    6278              :                                  * EvalPlanQual gets triggered. */
    6279              : 
    6280              :     /*
    6281              :      * Skip if this join combination has been considered already.
    6282              :      */
    6283         1360 :     if (joinrel->fdw_private)
    6284         1023 :         return;
    6285              : 
    6286              :     /*
    6287              :      * This code does not work for joins with lateral references, since those
    6288              :      * must have parameterized paths, which we don't generate yet.
    6289              :      */
    6290          400 :     if (!bms_is_empty(joinrel->lateral_relids))
    6291            4 :         return;
    6292              : 
    6293              :     /*
    6294              :      * Create unfinished PgFdwRelationInfo entry which is used to indicate
    6295              :      * that the join relation is already considered, so that we won't waste
    6296              :      * time in judging safety of join pushdown and adding the same paths again
    6297              :      * if found safe. Once we know that this join can be pushed down, we fill
    6298              :      * the entry.
    6299              :      */
    6300          396 :     fpinfo = palloc0_object(PgFdwRelationInfo);
    6301          396 :     fpinfo->pushdown_safe = false;
    6302          396 :     joinrel->fdw_private = fpinfo;
    6303              :     /* attrs_used is only for base relations. */
    6304          396 :     fpinfo->attrs_used = NULL;
    6305              : 
    6306              :     /*
    6307              :      * If there is a possibility that EvalPlanQual will be executed, we need
    6308              :      * to be able to reconstruct the row using scans of the base relations.
    6309              :      * GetExistingLocalJoinPath will find a suitable path for this purpose in
    6310              :      * the path list of the joinrel, if one exists.  We must be careful to
    6311              :      * call it before adding any ForeignPath, since the ForeignPath might
    6312              :      * dominate the only suitable local path available.  We also do it before
    6313              :      * calling foreign_join_ok(), since that function updates fpinfo and marks
    6314              :      * it as pushable if the join is found to be pushable.
    6315              :      */
    6316          396 :     if (root->parse->commandType == CMD_DELETE ||
    6317          382 :         root->parse->commandType == CMD_UPDATE ||
    6318          356 :         root->rowMarks)
    6319              :     {
    6320           78 :         epq_path = GetExistingLocalJoinPath(joinrel);
    6321           78 :         if (!epq_path)
    6322              :         {
    6323            0 :             elog(DEBUG3, "could not push down foreign join because a local path suitable for EPQ checks was not found");
    6324            0 :             return;
    6325              :         }
    6326              :     }
    6327              :     else
    6328          318 :         epq_path = NULL;
    6329              : 
    6330          396 :     if (!foreign_join_ok(root, joinrel, jointype, outerrel, innerrel, extra))
    6331              :     {
    6332              :         /* Free path required for EPQ if we copied one; we don't need it now */
    6333           59 :         if (epq_path)
    6334            2 :             pfree(epq_path);
    6335           59 :         return;
    6336              :     }
    6337              : 
    6338              :     /*
    6339              :      * Compute the selectivity and cost of the local_conds, so we don't have
    6340              :      * to do it over again for each path. The best we can do for these
    6341              :      * conditions is to estimate selectivity on the basis of local statistics.
    6342              :      * The local conditions are applied after the join has been computed on
    6343              :      * the remote side like quals in WHERE clause, so pass jointype as
    6344              :      * JOIN_INNER.
    6345              :      */
    6346          337 :     fpinfo->local_conds_sel = clauselist_selectivity(root,
    6347              :                                                      fpinfo->local_conds,
    6348              :                                                      0,
    6349              :                                                      JOIN_INNER,
    6350              :                                                      NULL);
    6351          337 :     cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
    6352              : 
    6353              :     /*
    6354              :      * If we are going to estimate costs locally, estimate the join clause
    6355              :      * selectivity here while we have special join info.
    6356              :      */
    6357          337 :     if (!fpinfo->use_remote_estimate)
    6358          112 :         fpinfo->joinclause_sel = clauselist_selectivity(root, fpinfo->joinclauses,
    6359              :                                                         0, fpinfo->jointype,
    6360              :                                                         extra->sjinfo);
    6361              : 
    6362              :     /* Estimate costs for bare join relation */
    6363          337 :     estimate_path_cost_size(root, joinrel, NIL, NIL, NULL,
    6364              :                             &rows, &width, &disabled_nodes,
    6365              :                             &startup_cost, &total_cost);
    6366              :     /* Now update this information in the joinrel */
    6367          337 :     joinrel->rows = rows;
    6368          337 :     joinrel->reltarget->width = width;
    6369          337 :     fpinfo->rows = rows;
    6370          337 :     fpinfo->width = width;
    6371          337 :     fpinfo->disabled_nodes = disabled_nodes;
    6372          337 :     fpinfo->startup_cost = startup_cost;
    6373          337 :     fpinfo->total_cost = total_cost;
    6374              : 
    6375              :     /*
    6376              :      * Create a new join path and add it to the joinrel which represents a
    6377              :      * join between foreign tables.
    6378              :      */
    6379          337 :     joinpath = create_foreign_join_path(root,
    6380              :                                         joinrel,
    6381              :                                         NULL,   /* default pathtarget */
    6382              :                                         rows,
    6383              :                                         disabled_nodes,
    6384              :                                         startup_cost,
    6385              :                                         total_cost,
    6386              :                                         NIL,    /* no pathkeys */
    6387              :                                         joinrel->lateral_relids,
    6388              :                                         epq_path,
    6389              :                                         extra->restrictlist,
    6390              :                                         NIL);   /* no fdw_private */
    6391              : 
    6392              :     /* Add generated path into joinrel by add_path(). */
    6393          337 :     add_path(joinrel, (Path *) joinpath);
    6394              : 
    6395              :     /* Consider pathkeys for the join relation */
    6396          337 :     add_paths_with_pathkeys_for_rel(root, joinrel, epq_path,
    6397              :                                     extra->restrictlist);
    6398              : 
    6399              :     /* XXX Consider parameterized paths for the join relation */
    6400              : }
    6401              : 
    6402              : /*
    6403              :  * Assess whether the aggregation, grouping and having operations can be pushed
    6404              :  * down to the foreign server.  As a side effect, save information we obtain in
    6405              :  * this function to PgFdwRelationInfo of the input relation.
    6406              :  */
    6407              : static bool
    6408          161 : foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
    6409              :                     Node *havingQual)
    6410              : {
    6411          161 :     Query      *query = root->parse;
    6412          161 :     PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
    6413          161 :     PathTarget *grouping_target = grouped_rel->reltarget;
    6414              :     PgFdwRelationInfo *ofpinfo;
    6415              :     ListCell   *lc;
    6416              :     int         i;
    6417          161 :     List       *tlist = NIL;
    6418              : 
    6419              :     /* We currently don't support pushing Grouping Sets. */
    6420          161 :     if (query->groupingSets)
    6421            6 :         return false;
    6422              : 
    6423              :     /* Get the fpinfo of the underlying scan relation. */
    6424          155 :     ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
    6425              : 
    6426              :     /*
    6427              :      * If underlying scan relation has any local conditions, those conditions
    6428              :      * are required to be applied before performing aggregation.  Hence the
    6429              :      * aggregate cannot be pushed down.
    6430              :      */
    6431          155 :     if (ofpinfo->local_conds)
    6432            9 :         return false;
    6433              : 
    6434              :     /*
    6435              :      * Examine grouping expressions, as well as other expressions we'd need to
    6436              :      * compute, and check whether they are safe to push down to the foreign
    6437              :      * server.  All GROUP BY expressions will be part of the grouping target
    6438              :      * and thus there is no need to search for them separately.  Add grouping
    6439              :      * expressions into target list which will be passed to foreign server.
    6440              :      *
    6441              :      * A tricky fine point is that we must not put any expression into the
    6442              :      * target list that is just a foreign param (that is, something that
    6443              :      * deparse.c would conclude has to be sent to the foreign server).  If we
    6444              :      * do, the expression will also appear in the fdw_exprs list of the plan
    6445              :      * node, and setrefs.c will get confused and decide that the fdw_exprs
    6446              :      * entry is actually a reference to the fdw_scan_tlist entry, resulting in
    6447              :      * a broken plan.  Somewhat oddly, it's OK if the expression contains such
    6448              :      * a node, as long as it's not at top level; then no match is possible.
    6449              :      */
    6450          146 :     i = 0;
    6451          427 :     foreach(lc, grouping_target->exprs)
    6452              :     {
    6453          299 :         Expr       *expr = (Expr *) lfirst(lc);
    6454          299 :         Index       sgref = get_pathtarget_sortgroupref(grouping_target, i);
    6455              :         ListCell   *l;
    6456              : 
    6457              :         /*
    6458              :          * Check whether this expression is part of GROUP BY clause.  Note we
    6459              :          * check the whole GROUP BY clause not just processed_groupClause,
    6460              :          * because we will ship all of it, cf. appendGroupByClause.
    6461              :          */
    6462          299 :         if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
    6463           92 :         {
    6464              :             TargetEntry *tle;
    6465              : 
    6466              :             /*
    6467              :              * If any GROUP BY expression is not shippable, then we cannot
    6468              :              * push down aggregation to the foreign server.
    6469              :              */
    6470           95 :             if (!is_foreign_expr(root, grouped_rel, expr))
    6471           18 :                 return false;
    6472              : 
    6473              :             /*
    6474              :              * If it would be a foreign param, we can't put it into the tlist,
    6475              :              * so we have to fail.
    6476              :              */
    6477           94 :             if (is_foreign_param(root, grouped_rel, expr))
    6478            2 :                 return false;
    6479              : 
    6480              :             /*
    6481              :              * Pushable, so add to tlist.  We need to create a TLE for this
    6482              :              * expression and apply the sortgroupref to it.  We cannot use
    6483              :              * add_to_flat_tlist() here because that avoids making duplicate
    6484              :              * entries in the tlist.  If there are duplicate entries with
    6485              :              * distinct sortgrouprefs, we have to duplicate that situation in
    6486              :              * the output tlist.
    6487              :              */
    6488           92 :             tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
    6489           92 :             tle->ressortgroupref = sgref;
    6490           92 :             tlist = lappend(tlist, tle);
    6491              :         }
    6492              :         else
    6493              :         {
    6494              :             /*
    6495              :              * Non-grouping expression we need to compute.  Can we ship it
    6496              :              * as-is to the foreign server?
    6497              :              */
    6498          204 :             if (is_foreign_expr(root, grouped_rel, expr) &&
    6499          183 :                 !is_foreign_param(root, grouped_rel, expr))
    6500          181 :             {
    6501              :                 /* Yes, so add to tlist as-is; OK to suppress duplicates */
    6502          181 :                 tlist = add_to_flat_tlist(tlist, list_make1(expr));
    6503              :             }
    6504              :             else
    6505              :             {
    6506              :                 /* Not pushable as a whole; extract its Vars and aggregates */
    6507              :                 List       *aggvars;
    6508              : 
    6509           23 :                 aggvars = pull_var_clause((Node *) expr,
    6510              :                                           PVC_INCLUDE_AGGREGATES);
    6511              : 
    6512              :                 /*
    6513              :                  * If any aggregate expression is not shippable, then we
    6514              :                  * cannot push down aggregation to the foreign server.  (We
    6515              :                  * don't have to check is_foreign_param, since that certainly
    6516              :                  * won't return true for any such expression.)
    6517              :                  */
    6518           23 :                 if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
    6519           15 :                     return false;
    6520              : 
    6521              :                 /*
    6522              :                  * Add aggregates, if any, into the targetlist.  Plain Vars
    6523              :                  * outside an aggregate can be ignored, because they should be
    6524              :                  * either same as some GROUP BY column or part of some GROUP
    6525              :                  * BY expression.  In either case, they are already part of
    6526              :                  * the targetlist and thus no need to add them again.  In fact
    6527              :                  * including plain Vars in the tlist when they do not match a
    6528              :                  * GROUP BY column would cause the foreign server to complain
    6529              :                  * that the shipped query is invalid.
    6530              :                  */
    6531           14 :                 foreach(l, aggvars)
    6532              :                 {
    6533            6 :                     Expr       *aggref = (Expr *) lfirst(l);
    6534              : 
    6535            6 :                     if (IsA(aggref, Aggref))
    6536            4 :                         tlist = add_to_flat_tlist(tlist, list_make1(aggref));
    6537              :                 }
    6538              :             }
    6539              :         }
    6540              : 
    6541          281 :         i++;
    6542              :     }
    6543              : 
    6544              :     /*
    6545              :      * Classify the pushable and non-pushable HAVING clauses and save them in
    6546              :      * remote_conds and local_conds of the grouped rel's fpinfo.
    6547              :      */
    6548          128 :     if (havingQual)
    6549              :     {
    6550           34 :         foreach(lc, (List *) havingQual)
    6551              :         {
    6552           19 :             Expr       *expr = (Expr *) lfirst(lc);
    6553              :             RestrictInfo *rinfo;
    6554              : 
    6555              :             /*
    6556              :              * Currently, the core code doesn't wrap havingQuals in
    6557              :              * RestrictInfos, so we must make our own.
    6558              :              */
    6559              :             Assert(!IsA(expr, RestrictInfo));
    6560           19 :             rinfo = make_restrictinfo(root,
    6561              :                                       expr,
    6562              :                                       true,
    6563              :                                       false,
    6564              :                                       false,
    6565              :                                       false,
    6566              :                                       root->qual_security_level,
    6567              :                                       grouped_rel->relids,
    6568              :                                       NULL,
    6569              :                                       NULL);
    6570           19 :             if (is_foreign_expr(root, grouped_rel, expr))
    6571           16 :                 fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
    6572              :             else
    6573            3 :                 fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
    6574              :         }
    6575              :     }
    6576              : 
    6577              :     /*
    6578              :      * If there are any local conditions, pull Vars and aggregates from it and
    6579              :      * check whether they are safe to pushdown or not.
    6580              :      */
    6581          128 :     if (fpinfo->local_conds)
    6582              :     {
    6583            3 :         List       *aggvars = NIL;
    6584              : 
    6585            6 :         foreach(lc, fpinfo->local_conds)
    6586              :         {
    6587            3 :             RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
    6588              : 
    6589            3 :             aggvars = list_concat(aggvars,
    6590            3 :                                   pull_var_clause((Node *) rinfo->clause,
    6591              :                                                   PVC_INCLUDE_AGGREGATES));
    6592              :         }
    6593              : 
    6594            7 :         foreach(lc, aggvars)
    6595              :         {
    6596            5 :             Expr       *expr = (Expr *) lfirst(lc);
    6597              : 
    6598              :             /*
    6599              :              * If aggregates within local conditions are not safe to push
    6600              :              * down, then we cannot push down the query.  Vars are already
    6601              :              * part of GROUP BY clause which are checked above, so no need to
    6602              :              * access them again here.  Again, we need not check
    6603              :              * is_foreign_param for a foreign aggregate.
    6604              :              */
    6605            5 :             if (IsA(expr, Aggref))
    6606              :             {
    6607            5 :                 if (!is_foreign_expr(root, grouped_rel, expr))
    6608            1 :                     return false;
    6609              : 
    6610            4 :                 tlist = add_to_flat_tlist(tlist, list_make1(expr));
    6611              :             }
    6612              :         }
    6613              :     }
    6614              : 
    6615              :     /* Store generated targetlist */
    6616          127 :     fpinfo->grouped_tlist = tlist;
    6617              : 
    6618              :     /* Safe to pushdown */
    6619          127 :     fpinfo->pushdown_safe = true;
    6620              : 
    6621              :     /*
    6622              :      * Set # of retrieved rows and cached relation costs to some negative
    6623              :      * value, so that we can detect when they are set to some sensible values,
    6624              :      * during one (usually the first) of the calls to estimate_path_cost_size.
    6625              :      */
    6626          127 :     fpinfo->retrieved_rows = -1;
    6627          127 :     fpinfo->rel_startup_cost = -1;
    6628          127 :     fpinfo->rel_total_cost = -1;
    6629              : 
    6630              :     /*
    6631              :      * Set the string describing this grouped relation to be used in EXPLAIN
    6632              :      * output of corresponding ForeignScan.  Note that the decoration we add
    6633              :      * to the base relation name mustn't include any digits, or it'll confuse
    6634              :      * postgresExplainForeignScan.
    6635              :      */
    6636          127 :     fpinfo->relation_name = psprintf("Aggregate on (%s)",
    6637              :                                      ofpinfo->relation_name);
    6638              : 
    6639          127 :     return true;
    6640              : }
    6641              : 
    6642              : /*
    6643              :  * postgresGetForeignUpperPaths
    6644              :  *      Add paths for post-join operations like aggregation, grouping etc. if
    6645              :  *      corresponding operations are safe to push down.
    6646              :  */
    6647              : static void
    6648          966 : postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage,
    6649              :                              RelOptInfo *input_rel, RelOptInfo *output_rel,
    6650              :                              void *extra)
    6651              : {
    6652              :     PgFdwRelationInfo *fpinfo;
    6653              : 
    6654              :     /*
    6655              :      * If input rel is not safe to pushdown, then simply return as we cannot
    6656              :      * perform any post-join operations on the foreign server.
    6657              :      */
    6658          966 :     if (!input_rel->fdw_private ||
    6659          900 :         !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
    6660          122 :         return;
    6661              : 
    6662              :     /* Ignore stages we don't support; and skip any duplicate calls. */
    6663          844 :     if ((stage != UPPERREL_GROUP_AGG &&
    6664          533 :          stage != UPPERREL_ORDERED &&
    6665          827 :          stage != UPPERREL_FINAL) ||
    6666          827 :         output_rel->fdw_private)
    6667           17 :         return;
    6668              : 
    6669          827 :     fpinfo = palloc0_object(PgFdwRelationInfo);
    6670          827 :     fpinfo->pushdown_safe = false;
    6671          827 :     fpinfo->stage = stage;
    6672          827 :     output_rel->fdw_private = fpinfo;
    6673              : 
    6674          827 :     switch (stage)
    6675              :     {
    6676          161 :         case UPPERREL_GROUP_AGG:
    6677          161 :             add_foreign_grouping_paths(root, input_rel, output_rel,
    6678              :                                        (GroupPathExtraData *) extra);
    6679          161 :             break;
    6680          150 :         case UPPERREL_ORDERED:
    6681          150 :             add_foreign_ordered_paths(root, input_rel, output_rel);
    6682          150 :             break;
    6683          516 :         case UPPERREL_FINAL:
    6684          516 :             add_foreign_final_paths(root, input_rel, output_rel,
    6685              :                                     (FinalPathExtraData *) extra);
    6686          516 :             break;
    6687            0 :         default:
    6688            0 :             elog(ERROR, "unexpected upper relation: %d", (int) stage);
    6689              :             break;
    6690              :     }
    6691              : }
    6692              : 
    6693              : /*
    6694              :  * add_foreign_grouping_paths
    6695              :  *      Add foreign path for grouping and/or aggregation.
    6696              :  *
    6697              :  * Given input_rel represents the underlying scan.  The paths are added to the
    6698              :  * given grouped_rel.
    6699              :  */
    6700              : static void
    6701          161 : add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
    6702              :                            RelOptInfo *grouped_rel,
    6703              :                            GroupPathExtraData *extra)
    6704              : {
    6705          161 :     Query      *parse = root->parse;
    6706          161 :     PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
    6707          161 :     PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
    6708              :     ForeignPath *grouppath;
    6709              :     double      rows;
    6710              :     int         width;
    6711              :     int         disabled_nodes;
    6712              :     Cost        startup_cost;
    6713              :     Cost        total_cost;
    6714              : 
    6715              :     /* Nothing to be done, if there is no grouping or aggregation required. */
    6716          161 :     if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
    6717            0 :         !root->hasHavingQual)
    6718           34 :         return;
    6719              : 
    6720              :     Assert(extra->patype == PARTITIONWISE_AGGREGATE_NONE ||
    6721              :            extra->patype == PARTITIONWISE_AGGREGATE_FULL);
    6722              : 
    6723              :     /* save the input_rel as outerrel in fpinfo */
    6724          161 :     fpinfo->outerrel = input_rel;
    6725              : 
    6726              :     /*
    6727              :      * Copy foreign table, foreign server, user mapping, FDW options etc.
    6728              :      * details from the input relation's fpinfo.
    6729              :      */
    6730          161 :     fpinfo->table = ifpinfo->table;
    6731          161 :     fpinfo->server = ifpinfo->server;
    6732          161 :     fpinfo->user = ifpinfo->user;
    6733          161 :     merge_fdw_options(fpinfo, ifpinfo, NULL);
    6734              : 
    6735              :     /*
    6736              :      * Assess if it is safe to push down aggregation and grouping.
    6737              :      *
    6738              :      * Use HAVING qual from extra. In case of child partition, it will have
    6739              :      * translated Vars.
    6740              :      */
    6741          161 :     if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
    6742           34 :         return;
    6743              : 
    6744              :     /*
    6745              :      * Compute the selectivity and cost of the local_conds, so we don't have
    6746              :      * to do it over again for each path.  (Currently we create just a single
    6747              :      * path here, but in future it would be possible that we build more paths
    6748              :      * such as pre-sorted paths as in postgresGetForeignPaths and
    6749              :      * postgresGetForeignJoinPaths.)  The best we can do for these conditions
    6750              :      * is to estimate selectivity on the basis of local statistics.
    6751              :      */
    6752          127 :     fpinfo->local_conds_sel = clauselist_selectivity(root,
    6753              :                                                      fpinfo->local_conds,
    6754              :                                                      0,
    6755              :                                                      JOIN_INNER,
    6756              :                                                      NULL);
    6757              : 
    6758          127 :     cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
    6759              : 
    6760              :     /* Estimate the cost of push down */
    6761          127 :     estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
    6762              :                             &rows, &width, &disabled_nodes,
    6763              :                             &startup_cost, &total_cost);
    6764              : 
    6765              :     /* Now update this information in the fpinfo */
    6766          127 :     fpinfo->rows = rows;
    6767          127 :     fpinfo->width = width;
    6768          127 :     fpinfo->disabled_nodes = disabled_nodes;
    6769          127 :     fpinfo->startup_cost = startup_cost;
    6770          127 :     fpinfo->total_cost = total_cost;
    6771              : 
    6772              :     /* Create and add foreign path to the grouping relation. */
    6773          127 :     grouppath = create_foreign_upper_path(root,
    6774              :                                           grouped_rel,
    6775          127 :                                           grouped_rel->reltarget,
    6776              :                                           rows,
    6777              :                                           disabled_nodes,
    6778              :                                           startup_cost,
    6779              :                                           total_cost,
    6780              :                                           NIL,  /* no pathkeys */
    6781              :                                           NULL,
    6782              :                                           NIL,  /* no fdw_restrictinfo list */
    6783              :                                           NIL); /* no fdw_private */
    6784              : 
    6785              :     /* Add generated path into grouped_rel by add_path(). */
    6786          127 :     add_path(grouped_rel, (Path *) grouppath);
    6787              : }
    6788              : 
    6789              : /*
    6790              :  * add_foreign_ordered_paths
    6791              :  *      Add foreign paths for performing the final sort remotely.
    6792              :  *
    6793              :  * Given input_rel contains the source-data Paths.  The paths are added to the
    6794              :  * given ordered_rel.
    6795              :  */
    6796              : static void
    6797          150 : add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
    6798              :                           RelOptInfo *ordered_rel)
    6799              : {
    6800          150 :     Query      *parse = root->parse;
    6801          150 :     PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
    6802          150 :     PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
    6803              :     PgFdwPathExtraData *fpextra;
    6804              :     double      rows;
    6805              :     int         width;
    6806              :     int         disabled_nodes;
    6807              :     Cost        startup_cost;
    6808              :     Cost        total_cost;
    6809              :     List       *fdw_private;
    6810              :     ForeignPath *ordered_path;
    6811              :     ListCell   *lc;
    6812              : 
    6813              :     /* Shouldn't get here unless the query has ORDER BY */
    6814              :     Assert(parse->sortClause);
    6815              : 
    6816              :     /* We don't support cases where there are any SRFs in the targetlist */
    6817          150 :     if (parse->hasTargetSRFs)
    6818          108 :         return;
    6819              : 
    6820              :     /* Save the input_rel as outerrel in fpinfo */
    6821          150 :     fpinfo->outerrel = input_rel;
    6822              : 
    6823              :     /*
    6824              :      * Copy foreign table, foreign server, user mapping, FDW options etc.
    6825              :      * details from the input relation's fpinfo.
    6826              :      */
    6827          150 :     fpinfo->table = ifpinfo->table;
    6828          150 :     fpinfo->server = ifpinfo->server;
    6829          150 :     fpinfo->user = ifpinfo->user;
    6830          150 :     merge_fdw_options(fpinfo, ifpinfo, NULL);
    6831              : 
    6832              :     /*
    6833              :      * If the input_rel is a base or join relation, we would already have
    6834              :      * considered pushing down the final sort to the remote server when
    6835              :      * creating pre-sorted foreign paths for that relation, because the
    6836              :      * query_pathkeys is set to the root->sort_pathkeys in that case (see
    6837              :      * standard_qp_callback()).
    6838              :      */
    6839          150 :     if (input_rel->reloptkind == RELOPT_BASEREL ||
    6840          109 :         input_rel->reloptkind == RELOPT_JOINREL)
    6841              :     {
    6842              :         Assert(root->query_pathkeys == root->sort_pathkeys);
    6843              : 
    6844              :         /* Safe to push down if the query_pathkeys is safe to push down */
    6845          104 :         fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
    6846              : 
    6847          104 :         return;
    6848              :     }
    6849              : 
    6850              :     /* The input_rel should be a grouping relation */
    6851              :     Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
    6852              :            ifpinfo->stage == UPPERREL_GROUP_AGG);
    6853              : 
    6854              :     /*
    6855              :      * We try to create a path below by extending a simple foreign path for
    6856              :      * the underlying grouping relation to perform the final sort remotely,
    6857              :      * which is stored into the fdw_private list of the resulting path.
    6858              :      */
    6859              : 
    6860              :     /* Assess if it is safe to push down the final sort */
    6861           94 :     foreach(lc, root->sort_pathkeys)
    6862              :     {
    6863           52 :         PathKey    *pathkey = (PathKey *) lfirst(lc);
    6864           52 :         EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
    6865              : 
    6866              :         /*
    6867              :          * is_foreign_expr would detect volatile expressions as well, but
    6868              :          * checking ec_has_volatile here saves some cycles.
    6869              :          */
    6870           52 :         if (pathkey_ec->ec_has_volatile)
    6871            4 :             return;
    6872              : 
    6873              :         /*
    6874              :          * Can't push down the sort if pathkey's opfamily is not shippable.
    6875              :          */
    6876           48 :         if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId,
    6877              :                           fpinfo))
    6878            0 :             return;
    6879              : 
    6880              :         /*
    6881              :          * The EC must contain a shippable EM that is computed in input_rel's
    6882              :          * reltarget, else we can't push down the sort.
    6883              :          */
    6884           48 :         if (find_em_for_rel_target(root,
    6885              :                                    pathkey_ec,
    6886              :                                    input_rel) == NULL)
    6887            0 :             return;
    6888              :     }
    6889              : 
    6890              :     /* Safe to push down */
    6891           42 :     fpinfo->pushdown_safe = true;
    6892              : 
    6893              :     /* Construct PgFdwPathExtraData */
    6894           42 :     fpextra = palloc0_object(PgFdwPathExtraData);
    6895           42 :     fpextra->target = root->upper_targets[UPPERREL_ORDERED];
    6896           42 :     fpextra->has_final_sort = true;
    6897              : 
    6898              :     /* Estimate the costs of performing the final sort remotely */
    6899           42 :     estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
    6900              :                             &rows, &width, &disabled_nodes,
    6901              :                             &startup_cost, &total_cost);
    6902              : 
    6903              :     /*
    6904              :      * Build the fdw_private list that will be used by postgresGetForeignPlan.
    6905              :      * Items in the list must match order in enum FdwPathPrivateIndex.
    6906              :      */
    6907           42 :     fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
    6908              : 
    6909              :     /* Create foreign ordering path */
    6910           42 :     ordered_path = create_foreign_upper_path(root,
    6911              :                                              input_rel,
    6912           42 :                                              root->upper_targets[UPPERREL_ORDERED],
    6913              :                                              rows,
    6914              :                                              disabled_nodes,
    6915              :                                              startup_cost,
    6916              :                                              total_cost,
    6917              :                                              root->sort_pathkeys,
    6918              :                                              NULL,  /* no extra plan */
    6919              :                                              NIL,   /* no fdw_restrictinfo
    6920              :                                                      * list */
    6921              :                                              fdw_private);
    6922              : 
    6923              :     /* and add it to the ordered_rel */
    6924           42 :     add_path(ordered_rel, (Path *) ordered_path);
    6925              : }
    6926              : 
    6927              : /*
    6928              :  * add_foreign_final_paths
    6929              :  *      Add foreign paths for performing the final processing remotely.
    6930              :  *
    6931              :  * Given input_rel contains the source-data Paths.  The paths are added to the
    6932              :  * given final_rel.
    6933              :  */
    6934              : static void
    6935          516 : add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel,
    6936              :                         RelOptInfo *final_rel,
    6937              :                         FinalPathExtraData *extra)
    6938              : {
    6939          516 :     Query      *parse = root->parse;
    6940          516 :     PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
    6941          516 :     PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
    6942          516 :     bool        has_final_sort = false;
    6943          516 :     List       *pathkeys = NIL;
    6944              :     PgFdwPathExtraData *fpextra;
    6945          516 :     bool        save_use_remote_estimate = false;
    6946              :     double      rows;
    6947              :     int         width;
    6948              :     int         disabled_nodes;
    6949              :     Cost        startup_cost;
    6950              :     Cost        total_cost;
    6951              :     List       *fdw_private;
    6952              :     ForeignPath *final_path;
    6953              : 
    6954              :     /*
    6955              :      * Currently, we only support this for SELECT commands
    6956              :      */
    6957          516 :     if (parse->commandType != CMD_SELECT)
    6958          395 :         return;
    6959              : 
    6960              :     /*
    6961              :      * No work if there is no FOR UPDATE/SHARE clause and if there is no need
    6962              :      * to add a LIMIT node
    6963              :      */
    6964          403 :     if (!parse->rowMarks && !extra->limit_needed)
    6965          268 :         return;
    6966              : 
    6967              :     /* We don't support cases where there are any SRFs in the targetlist */
    6968          135 :     if (parse->hasTargetSRFs)
    6969            0 :         return;
    6970              : 
    6971              :     /* Save the input_rel as outerrel in fpinfo */
    6972          135 :     fpinfo->outerrel = input_rel;
    6973              : 
    6974              :     /*
    6975              :      * Copy foreign table, foreign server, user mapping, FDW options etc.
    6976              :      * details from the input relation's fpinfo.
    6977              :      */
    6978          135 :     fpinfo->table = ifpinfo->table;
    6979          135 :     fpinfo->server = ifpinfo->server;
    6980          135 :     fpinfo->user = ifpinfo->user;
    6981          135 :     merge_fdw_options(fpinfo, ifpinfo, NULL);
    6982              : 
    6983              :     /*
    6984              :      * If there is no need to add a LIMIT node, there might be a ForeignPath
    6985              :      * in the input_rel's pathlist that implements all behavior of the query.
    6986              :      * Note: we would already have accounted for the query's FOR UPDATE/SHARE
    6987              :      * (if any) before we get here.
    6988              :      */
    6989          135 :     if (!extra->limit_needed)
    6990              :     {
    6991              :         ListCell   *lc;
    6992              : 
    6993              :         Assert(parse->rowMarks);
    6994              : 
    6995              :         /*
    6996              :          * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
    6997              :          * so the input_rel should be a base, join, or ordered relation; and
    6998              :          * if it's an ordered relation, its input relation should be a base or
    6999              :          * join relation.
    7000              :          */
    7001              :         Assert(input_rel->reloptkind == RELOPT_BASEREL ||
    7002              :                input_rel->reloptkind == RELOPT_JOINREL ||
    7003              :                (input_rel->reloptkind == RELOPT_UPPER_REL &&
    7004              :                 ifpinfo->stage == UPPERREL_ORDERED &&
    7005              :                 (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
    7006              :                  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
    7007              : 
    7008            4 :         foreach(lc, input_rel->pathlist)
    7009              :         {
    7010            4 :             Path       *path = (Path *) lfirst(lc);
    7011              : 
    7012              :             /*
    7013              :              * apply_scanjoin_target_to_paths() uses create_projection_path()
    7014              :              * to adjust each of its input paths if needed, whereas
    7015              :              * create_ordered_paths() uses apply_projection_to_path() to do
    7016              :              * that.  So the former might have put a ProjectionPath on top of
    7017              :              * the ForeignPath; look through ProjectionPath and see if the
    7018              :              * path underneath it is ForeignPath.
    7019              :              */
    7020            4 :             if (IsA(path, ForeignPath) ||
    7021            0 :                 (IsA(path, ProjectionPath) &&
    7022            0 :                  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
    7023              :             {
    7024              :                 /*
    7025              :                  * Create foreign final path; this gets rid of a
    7026              :                  * no-longer-needed outer plan (if any), which makes the
    7027              :                  * EXPLAIN output look cleaner
    7028              :                  */
    7029            4 :                 final_path = create_foreign_upper_path(root,
    7030              :                                                        path->parent,
    7031              :                                                        path->pathtarget,
    7032              :                                                        path->rows,
    7033              :                                                        path->disabled_nodes,
    7034              :                                                        path->startup_cost,
    7035              :                                                        path->total_cost,
    7036              :                                                        path->pathkeys,
    7037              :                                                        NULL,    /* no extra plan */
    7038              :                                                        NIL, /* no fdw_restrictinfo
    7039              :                                                              * list */
    7040              :                                                        NIL);    /* no fdw_private */
    7041              : 
    7042              :                 /* and add it to the final_rel */
    7043            4 :                 add_path(final_rel, (Path *) final_path);
    7044              : 
    7045              :                 /* Safe to push down */
    7046            4 :                 fpinfo->pushdown_safe = true;
    7047              : 
    7048            4 :                 return;
    7049              :             }
    7050              :         }
    7051              : 
    7052              :         /*
    7053              :          * If we get here it means no ForeignPaths; since we would already
    7054              :          * have considered pushing down all operations for the query to the
    7055              :          * remote server, give up on it.
    7056              :          */
    7057            0 :         return;
    7058              :     }
    7059              : 
    7060              :     Assert(extra->limit_needed);
    7061              : 
    7062              :     /*
    7063              :      * If the input_rel is an ordered relation, replace the input_rel with its
    7064              :      * input relation
    7065              :      */
    7066          131 :     if (input_rel->reloptkind == RELOPT_UPPER_REL &&
    7067           74 :         ifpinfo->stage == UPPERREL_ORDERED)
    7068              :     {
    7069           74 :         input_rel = ifpinfo->outerrel;
    7070           74 :         ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
    7071           74 :         has_final_sort = true;
    7072           74 :         pathkeys = root->sort_pathkeys;
    7073              :     }
    7074              : 
    7075              :     /* The input_rel should be a base, join, or grouping relation */
    7076              :     Assert(input_rel->reloptkind == RELOPT_BASEREL ||
    7077              :            input_rel->reloptkind == RELOPT_JOINREL ||
    7078              :            (input_rel->reloptkind == RELOPT_UPPER_REL &&
    7079              :             ifpinfo->stage == UPPERREL_GROUP_AGG));
    7080              : 
    7081              :     /*
    7082              :      * We try to create a path below by extending a simple foreign path for
    7083              :      * the underlying base, join, or grouping relation to perform the final
    7084              :      * sort (if has_final_sort) and the LIMIT restriction remotely, which is
    7085              :      * stored into the fdw_private list of the resulting path.  (We
    7086              :      * re-estimate the costs of sorting the underlying relation, if
    7087              :      * has_final_sort.)
    7088              :      */
    7089              : 
    7090              :     /*
    7091              :      * Assess if it is safe to push down the LIMIT and OFFSET to the remote
    7092              :      * server
    7093              :      */
    7094              : 
    7095              :     /*
    7096              :      * If the underlying relation has any local conditions, the LIMIT/OFFSET
    7097              :      * cannot be pushed down.
    7098              :      */
    7099          131 :     if (ifpinfo->local_conds)
    7100            8 :         return;
    7101              : 
    7102              :     /*
    7103              :      * If the query has FETCH FIRST .. WITH TIES, 1) it must have ORDER BY as
    7104              :      * well, which is used to determine which additional rows tie for the last
    7105              :      * place in the result set, and 2) ORDER BY must already have been
    7106              :      * determined to be safe to push down before we get here.  So in that case
    7107              :      * the FETCH clause is safe to push down with ORDER BY if the remote
    7108              :      * server is v13 or later, but if not, the remote query will fail entirely
    7109              :      * for lack of support for it.  Since we do not currently have a way to do
    7110              :      * a remote-version check (without accessing the remote server), disable
    7111              :      * pushing the FETCH clause for now.
    7112              :      */
    7113          123 :     if (parse->limitOption == LIMIT_OPTION_WITH_TIES)
    7114            2 :         return;
    7115              : 
    7116              :     /*
    7117              :      * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
    7118              :      * not safe to remote.
    7119              :      */
    7120          121 :     if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
    7121          121 :         !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
    7122            0 :         return;
    7123              : 
    7124              :     /* Safe to push down */
    7125          121 :     fpinfo->pushdown_safe = true;
    7126              : 
    7127              :     /* Construct PgFdwPathExtraData */
    7128          121 :     fpextra = palloc0_object(PgFdwPathExtraData);
    7129          121 :     fpextra->target = root->upper_targets[UPPERREL_FINAL];
    7130          121 :     fpextra->has_final_sort = has_final_sort;
    7131          121 :     fpextra->has_limit = extra->limit_needed;
    7132          121 :     fpextra->limit_tuples = extra->limit_tuples;
    7133          121 :     fpextra->count_est = extra->count_est;
    7134          121 :     fpextra->offset_est = extra->offset_est;
    7135              : 
    7136              :     /*
    7137              :      * Estimate the costs of performing the final sort and the LIMIT
    7138              :      * restriction remotely.  If has_final_sort is false, we wouldn't need to
    7139              :      * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
    7140              :      * roughly estimated using the costs we already have for the underlying
    7141              :      * relation, in the same way as when use_remote_estimate is false.  Since
    7142              :      * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
    7143              :      * false in that case.
    7144              :      */
    7145          121 :     if (!fpextra->has_final_sort)
    7146              :     {
    7147           54 :         save_use_remote_estimate = ifpinfo->use_remote_estimate;
    7148           54 :         ifpinfo->use_remote_estimate = false;
    7149              :     }
    7150          121 :     estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
    7151              :                             &rows, &width, &disabled_nodes,
    7152              :                             &startup_cost, &total_cost);
    7153          121 :     if (!fpextra->has_final_sort)
    7154           54 :         ifpinfo->use_remote_estimate = save_use_remote_estimate;
    7155              : 
    7156              :     /*
    7157              :      * Build the fdw_private list that will be used by postgresGetForeignPlan.
    7158              :      * Items in the list must match order in enum FdwPathPrivateIndex.
    7159              :      */
    7160          121 :     fdw_private = list_make2(makeBoolean(has_final_sort),
    7161              :                              makeBoolean(extra->limit_needed));
    7162              : 
    7163              :     /*
    7164              :      * Create foreign final path; this gets rid of a no-longer-needed outer
    7165              :      * plan (if any), which makes the EXPLAIN output look cleaner
    7166              :      */
    7167          121 :     final_path = create_foreign_upper_path(root,
    7168              :                                            input_rel,
    7169          121 :                                            root->upper_targets[UPPERREL_FINAL],
    7170              :                                            rows,
    7171              :                                            disabled_nodes,
    7172              :                                            startup_cost,
    7173              :                                            total_cost,
    7174              :                                            pathkeys,
    7175              :                                            NULL,    /* no extra plan */
    7176              :                                            NIL, /* no fdw_restrictinfo list */
    7177              :                                            fdw_private);
    7178              : 
    7179              :     /* and add it to the final_rel */
    7180          121 :     add_path(final_rel, (Path *) final_path);
    7181              : }
    7182              : 
    7183              : /*
    7184              :  * postgresIsForeignPathAsyncCapable
    7185              :  *      Check whether a given ForeignPath node is async-capable.
    7186              :  */
    7187              : static bool
    7188          237 : postgresIsForeignPathAsyncCapable(ForeignPath *path)
    7189              : {
    7190          237 :     RelOptInfo *rel = ((Path *) path)->parent;
    7191          237 :     PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
    7192              : 
    7193          237 :     return fpinfo->async_capable;
    7194              : }
    7195              : 
    7196              : /*
    7197              :  * postgresForeignAsyncRequest
    7198              :  *      Asynchronously request next tuple from a foreign PostgreSQL table.
    7199              :  */
    7200              : static void
    7201         6175 : postgresForeignAsyncRequest(AsyncRequest *areq)
    7202              : {
    7203         6175 :     produce_tuple_asynchronously(areq, true);
    7204         6175 : }
    7205              : 
    7206              : /*
    7207              :  * postgresForeignAsyncConfigureWait
    7208              :  *      Configure a file descriptor event for which we wish to wait.
    7209              :  */
    7210              : static void
    7211          214 : postgresForeignAsyncConfigureWait(AsyncRequest *areq)
    7212              : {
    7213          214 :     ForeignScanState *node = (ForeignScanState *) areq->requestee;
    7214          214 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    7215          214 :     AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
    7216          214 :     AppendState *requestor = (AppendState *) areq->requestor;
    7217          214 :     WaitEventSet *set = requestor->as_eventset;
    7218              : 
    7219              :     /* This should not be called unless callback_pending */
    7220              :     Assert(areq->callback_pending);
    7221              : 
    7222              :     /*
    7223              :      * If process_pending_request() has been invoked on the given request
    7224              :      * before we get here, we might have some tuples already; in which case
    7225              :      * complete the request
    7226              :      */
    7227          214 :     if (fsstate->next_tuple < fsstate->num_tuples)
    7228              :     {
    7229            5 :         complete_pending_request(areq);
    7230            5 :         if (areq->request_complete)
    7231            3 :             return;
    7232              :         Assert(areq->callback_pending);
    7233              :     }
    7234              : 
    7235              :     /* We must have run out of tuples */
    7236              :     Assert(fsstate->next_tuple >= fsstate->num_tuples);
    7237              : 
    7238              :     /* The core code would have registered postmaster death event */
    7239              :     Assert(GetNumRegisteredWaitEvents(set) >= 1);
    7240              : 
    7241              :     /* Begin an asynchronous data fetch if not already done */
    7242          211 :     if (!pendingAreq)
    7243            4 :         fetch_more_data_begin(areq);
    7244          207 :     else if (pendingAreq->requestor != areq->requestor)
    7245              :     {
    7246              :         /*
    7247              :          * This is the case when the in-process request was made by another
    7248              :          * Append.  Note that it might be useless to process the request made
    7249              :          * by that Append, because the query might not need tuples from that
    7250              :          * Append anymore; so we avoid processing it to begin a fetch for the
    7251              :          * given request if possible.  If there are any child subplans of the
    7252              :          * same parent that are ready for new requests, skip the given
    7253              :          * request.  Likewise, if there are any configured events other than
    7254              :          * the postmaster death event, skip it.  Otherwise, process the
    7255              :          * in-process request, then begin a fetch to configure the event
    7256              :          * below, because we might otherwise end up with no configured events
    7257              :          * other than the postmaster death event.
    7258              :          */
    7259            8 :         if (!bms_is_empty(requestor->as_needrequest))
    7260            0 :             return;
    7261            8 :         if (GetNumRegisteredWaitEvents(set) > 1)
    7262            6 :             return;
    7263            2 :         process_pending_request(pendingAreq);
    7264            2 :         fetch_more_data_begin(areq);
    7265              :     }
    7266          199 :     else if (pendingAreq->requestee != areq->requestee)
    7267              :     {
    7268              :         /*
    7269              :          * This is the case when the in-process request was made by the same
    7270              :          * parent but for a different child.  Since we configure only the
    7271              :          * event for the request made for that child, skip the given request.
    7272              :          */
    7273            8 :         return;
    7274              :     }
    7275              :     else
    7276              :         Assert(pendingAreq == areq);
    7277              : 
    7278          196 :     AddWaitEventToSet(set, WL_SOCKET_READABLE, PQsocket(fsstate->conn),
    7279              :                       NULL, areq);
    7280              : }
    7281              : 
    7282              : /*
    7283              :  * postgresForeignAsyncNotify
    7284              :  *      Fetch some more tuples from a file descriptor that becomes ready,
    7285              :  *      requesting next tuple.
    7286              :  */
    7287              : static void
    7288          148 : postgresForeignAsyncNotify(AsyncRequest *areq)
    7289              : {
    7290          148 :     ForeignScanState *node = (ForeignScanState *) areq->requestee;
    7291          148 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    7292              : 
    7293              :     /* The core code would have initialized the callback_pending flag */
    7294              :     Assert(!areq->callback_pending);
    7295              : 
    7296              :     /*
    7297              :      * If process_pending_request() has been invoked on the given request
    7298              :      * before we get here, we might have some tuples already; in which case
    7299              :      * produce the next tuple
    7300              :      */
    7301          148 :     if (fsstate->next_tuple < fsstate->num_tuples)
    7302              :     {
    7303            0 :         produce_tuple_asynchronously(areq, true);
    7304            0 :         return;
    7305              :     }
    7306              : 
    7307              :     /* We must have run out of tuples */
    7308              :     Assert(fsstate->next_tuple >= fsstate->num_tuples);
    7309              : 
    7310              :     /* The request should be currently in-process */
    7311              :     Assert(fsstate->conn_state->pendingAreq == areq);
    7312              : 
    7313              :     /* On error, report the original query, not the FETCH. */
    7314          148 :     if (!PQconsumeInput(fsstate->conn))
    7315            0 :         pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
    7316              : 
    7317          148 :     fetch_more_data(node);
    7318              : 
    7319          148 :     produce_tuple_asynchronously(areq, true);
    7320              : }
    7321              : 
    7322              : /*
    7323              :  * Asynchronously produce next tuple from a foreign PostgreSQL table.
    7324              :  */
    7325              : static void
    7326         6328 : produce_tuple_asynchronously(AsyncRequest *areq, bool fetch)
    7327              : {
    7328         6328 :     ForeignScanState *node = (ForeignScanState *) areq->requestee;
    7329         6328 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    7330         6328 :     AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
    7331              :     TupleTableSlot *result;
    7332              : 
    7333              :     /* This should not be called if the request is currently in-process */
    7334              :     Assert(areq != pendingAreq);
    7335              : 
    7336              :     /* Fetch some more tuples, if we've run out */
    7337         6328 :     if (fsstate->next_tuple >= fsstate->num_tuples)
    7338              :     {
    7339              :         /* No point in another fetch if we already detected EOF, though */
    7340          189 :         if (!fsstate->eof_reached)
    7341              :         {
    7342              :             /* Mark the request as pending for a callback */
    7343          129 :             ExecAsyncRequestPending(areq);
    7344              :             /* Begin another fetch if requested and if no pending request */
    7345          129 :             if (fetch && !pendingAreq)
    7346          124 :                 fetch_more_data_begin(areq);
    7347              :         }
    7348              :         else
    7349              :         {
    7350              :             /* There's nothing more to do; just return a NULL pointer */
    7351           60 :             result = NULL;
    7352              :             /* Mark the request as complete */
    7353           60 :             ExecAsyncRequestDone(areq, result);
    7354              :         }
    7355          189 :         return;
    7356              :     }
    7357              : 
    7358              :     /* Get a tuple from the ForeignScan node */
    7359         6139 :     result = areq->requestee->ExecProcNodeReal(areq->requestee);
    7360         6139 :     if (!TupIsNull(result))
    7361              :     {
    7362              :         /* Mark the request as complete */
    7363         6107 :         ExecAsyncRequestDone(areq, result);
    7364         6107 :         return;
    7365              :     }
    7366              : 
    7367              :     /* We must have run out of tuples */
    7368              :     Assert(fsstate->next_tuple >= fsstate->num_tuples);
    7369              : 
    7370              :     /* Fetch some more tuples, if we've not detected EOF yet */
    7371           32 :     if (!fsstate->eof_reached)
    7372              :     {
    7373              :         /* Mark the request as pending for a callback */
    7374           32 :         ExecAsyncRequestPending(areq);
    7375              :         /* Begin another fetch if requested and if no pending request */
    7376           32 :         if (fetch && !pendingAreq)
    7377           30 :             fetch_more_data_begin(areq);
    7378              :     }
    7379              :     else
    7380              :     {
    7381              :         /* There's nothing more to do; just return a NULL pointer */
    7382            0 :         result = NULL;
    7383              :         /* Mark the request as complete */
    7384            0 :         ExecAsyncRequestDone(areq, result);
    7385              :     }
    7386              : }
    7387              : 
    7388              : /*
    7389              :  * Begin an asynchronous data fetch.
    7390              :  *
    7391              :  * Note: this function assumes there is no currently-in-progress asynchronous
    7392              :  * data fetch.
    7393              :  *
    7394              :  * Note: fetch_more_data must be called to fetch the result.
    7395              :  */
    7396              : static void
    7397          160 : fetch_more_data_begin(AsyncRequest *areq)
    7398              : {
    7399          160 :     ForeignScanState *node = (ForeignScanState *) areq->requestee;
    7400          160 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    7401              :     char        sql[64];
    7402              : 
    7403              :     Assert(!fsstate->conn_state->pendingAreq);
    7404              : 
    7405              :     /* Create the cursor synchronously. */
    7406          160 :     if (!fsstate->cursor_exists)
    7407           68 :         create_cursor(node);
    7408              : 
    7409              :     /* We will send this query, but not wait for the response. */
    7410          159 :     snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
    7411              :              fsstate->fetch_size, fsstate->cursor_number);
    7412              : 
    7413          159 :     if (!PQsendQuery(fsstate->conn, sql))
    7414            0 :         pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
    7415              : 
    7416              :     /* Remember that the request is in process */
    7417          159 :     fsstate->conn_state->pendingAreq = areq;
    7418          159 : }
    7419              : 
    7420              : /*
    7421              :  * Process a pending asynchronous request.
    7422              :  */
    7423              : void
    7424            9 : process_pending_request(AsyncRequest *areq)
    7425              : {
    7426            9 :     ForeignScanState *node = (ForeignScanState *) areq->requestee;
    7427            9 :     PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
    7428              : 
    7429              :     /* The request would have been pending for a callback */
    7430              :     Assert(areq->callback_pending);
    7431              : 
    7432              :     /* The request should be currently in-process */
    7433              :     Assert(fsstate->conn_state->pendingAreq == areq);
    7434              : 
    7435            9 :     fetch_more_data(node);
    7436              : 
    7437              :     /*
    7438              :      * If we didn't get any tuples, must be end of data; complete the request
    7439              :      * now.  Otherwise, we postpone completing the request until we are called
    7440              :      * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
    7441              :      */
    7442            9 :     if (fsstate->next_tuple >= fsstate->num_tuples)
    7443              :     {
    7444              :         /* Unlike AsyncNotify, we unset callback_pending ourselves */
    7445            0 :         areq->callback_pending = false;
    7446              :         /* Mark the request as complete */
    7447            0 :         ExecAsyncRequestDone(areq, NULL);
    7448              :         /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
    7449            0 :         ExecAsyncResponse(areq);
    7450              :     }
    7451            9 : }
    7452              : 
    7453              : /*
    7454              :  * Complete a pending asynchronous request.
    7455              :  */
    7456              : static void
    7457            5 : complete_pending_request(AsyncRequest *areq)
    7458              : {
    7459              :     /* The request would have been pending for a callback */
    7460              :     Assert(areq->callback_pending);
    7461              : 
    7462              :     /* Unlike AsyncNotify, we unset callback_pending ourselves */
    7463            5 :     areq->callback_pending = false;
    7464              : 
    7465              :     /* We begin a fetch afterwards if necessary; don't fetch */
    7466            5 :     produce_tuple_asynchronously(areq, false);
    7467              : 
    7468              :     /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
    7469            5 :     ExecAsyncResponse(areq);
    7470              : 
    7471              :     /* Also, we do instrumentation ourselves, if required */
    7472            5 :     if (areq->requestee->instrument)
    7473            1 :         InstrUpdateTupleCount(areq->requestee->instrument,
    7474            1 :                               TupIsNull(areq->result) ? 0.0 : 1.0);
    7475            5 : }
    7476              : 
    7477              : /*
    7478              :  * Create a tuple from the specified row of the PGresult.
    7479              :  *
    7480              :  * rel is the local representation of the foreign table, attinmeta is
    7481              :  * conversion data for the rel's tupdesc, and retrieved_attrs is an
    7482              :  * integer list of the table column numbers present in the PGresult.
    7483              :  * fsstate is the ForeignScan plan node's execution state.
    7484              :  * temp_context is a working context that can be reset after each tuple.
    7485              :  *
    7486              :  * Note: either rel or fsstate, but not both, can be NULL.  rel is NULL
    7487              :  * if we're processing a remote join, while fsstate is NULL in a non-query
    7488              :  * context such as ANALYZE, or if we're processing a non-scan query node.
    7489              :  */
    7490              : static HeapTuple
    7491        94402 : make_tuple_from_result_row(PGresult *res,
    7492              :                            int row,
    7493              :                            Relation rel,
    7494              :                            AttInMetadata *attinmeta,
    7495              :                            List *retrieved_attrs,
    7496              :                            ForeignScanState *fsstate,
    7497              :                            MemoryContext temp_context)
    7498              : {
    7499              :     HeapTuple   tuple;
    7500              :     TupleDesc   tupdesc;
    7501              :     Datum      *values;
    7502              :     bool       *nulls;
    7503        94402 :     ItemPointer ctid = NULL;
    7504              :     ConversionLocation errpos;
    7505              :     ErrorContextCallback errcallback;
    7506              :     MemoryContext oldcontext;
    7507              :     ListCell   *lc;
    7508              :     int         j;
    7509              : 
    7510              :     Assert(row < PQntuples(res));
    7511              : 
    7512              :     /*
    7513              :      * Do the following work in a temp context that we reset after each tuple.
    7514              :      * This cleans up not only the data we have direct access to, but any
    7515              :      * cruft the I/O functions might leak.
    7516              :      */
    7517        94402 :     oldcontext = MemoryContextSwitchTo(temp_context);
    7518              : 
    7519              :     /*
    7520              :      * Get the tuple descriptor for the row.  Use the rel's tupdesc if rel is
    7521              :      * provided, otherwise look to the scan node's ScanTupleSlot.
    7522              :      */
    7523        94402 :     if (rel)
    7524        58451 :         tupdesc = RelationGetDescr(rel);
    7525              :     else
    7526              :     {
    7527              :         Assert(fsstate);
    7528        35951 :         tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
    7529              :     }
    7530              : 
    7531        94402 :     values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
    7532        94402 :     nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
    7533              :     /* Initialize to nulls for any columns not present in result */
    7534        94402 :     memset(nulls, true, tupdesc->natts * sizeof(bool));
    7535              : 
    7536              :     /*
    7537              :      * Set up and install callback to report where conversion error occurs.
    7538              :      */
    7539        94402 :     errpos.cur_attno = 0;
    7540        94402 :     errpos.rel = rel;
    7541        94402 :     errpos.fsstate = fsstate;
    7542        94402 :     errcallback.callback = conversion_error_callback;
    7543        94402 :     errcallback.arg = &errpos;
    7544        94402 :     errcallback.previous = error_context_stack;
    7545        94402 :     error_context_stack = &errcallback;
    7546              : 
    7547              :     /*
    7548              :      * i indexes columns in the relation, j indexes columns in the PGresult.
    7549              :      */
    7550        94402 :     j = 0;
    7551       354001 :     foreach(lc, retrieved_attrs)
    7552              :     {
    7553       259604 :         int         i = lfirst_int(lc);
    7554              :         char       *valstr;
    7555              : 
    7556              :         /* fetch next column's textual value */
    7557       259604 :         if (PQgetisnull(res, row, j))
    7558        10753 :             valstr = NULL;
    7559              :         else
    7560       248851 :             valstr = PQgetvalue(res, row, j);
    7561              : 
    7562              :         /*
    7563              :          * convert value to internal representation
    7564              :          *
    7565              :          * Note: we ignore system columns other than ctid and oid in result
    7566              :          */
    7567       259604 :         errpos.cur_attno = i;
    7568       259604 :         if (i > 0)
    7569              :         {
    7570              :             /* ordinary column */
    7571              :             Assert(i <= tupdesc->natts);
    7572       256487 :             nulls[i - 1] = (valstr == NULL);
    7573              :             /* Apply the input function even to nulls, to support domains */
    7574       256482 :             values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
    7575              :                                               valstr,
    7576       256487 :                                               attinmeta->attioparams[i - 1],
    7577       256487 :                                               attinmeta->atttypmods[i - 1]);
    7578              :         }
    7579         3117 :         else if (i == SelfItemPointerAttributeNumber)
    7580              :         {
    7581              :             /* ctid */
    7582         3117 :             if (valstr != NULL)
    7583              :             {
    7584              :                 Datum       datum;
    7585              : 
    7586         3117 :                 datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
    7587         3117 :                 ctid = (ItemPointer) DatumGetPointer(datum);
    7588              :             }
    7589              :         }
    7590       259599 :         errpos.cur_attno = 0;
    7591              : 
    7592       259599 :         j++;
    7593              :     }
    7594              : 
    7595              :     /* Uninstall error context callback. */
    7596        94397 :     error_context_stack = errcallback.previous;
    7597              : 
    7598              :     /*
    7599              :      * Check we got the expected number of columns.  Note: j == 0 and
    7600              :      * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
    7601              :      */
    7602        94397 :     if (j > 0 && j != PQnfields(res))
    7603            0 :         elog(ERROR, "remote query result does not match the foreign table");
    7604              : 
    7605              :     /*
    7606              :      * Build the result tuple in caller's memory context.
    7607              :      */
    7608        94397 :     MemoryContextSwitchTo(oldcontext);
    7609              : 
    7610        94397 :     tuple = heap_form_tuple(tupdesc, values, nulls);
    7611              : 
    7612              :     /*
    7613              :      * If we have a CTID to return, install it in both t_self and t_ctid.
    7614              :      * t_self is the normal place, but if the tuple is converted to a
    7615              :      * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
    7616              :      * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
    7617              :      */
    7618        94397 :     if (ctid)
    7619         3117 :         tuple->t_self = tuple->t_data->t_ctid = *ctid;
    7620              : 
    7621              :     /*
    7622              :      * Stomp on the xmin, xmax, and cmin fields from the tuple created by
    7623              :      * heap_form_tuple.  heap_form_tuple actually creates the tuple with
    7624              :      * DatumTupleFields, not HeapTupleFields, but the executor expects
    7625              :      * HeapTupleFields and will happily extract system columns on that
    7626              :      * assumption.  If we don't do this then, for example, the tuple length
    7627              :      * ends up in the xmin field, which isn't what we want.
    7628              :      */
    7629        94397 :     HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId);
    7630        94397 :     HeapTupleHeaderSetXmin(tuple->t_data, InvalidTransactionId);
    7631        94397 :     HeapTupleHeaderSetCmin(tuple->t_data, InvalidTransactionId);
    7632              : 
    7633              :     /* Clean up */
    7634        94397 :     MemoryContextReset(temp_context);
    7635              : 
    7636        94397 :     return tuple;
    7637              : }
    7638              : 
    7639              : /*
    7640              :  * Callback function which is called when error occurs during column value
    7641              :  * conversion.  Print names of column and relation.
    7642              :  *
    7643              :  * Note that this function mustn't do any catalog lookups, since we are in
    7644              :  * an already-failed transaction.  Fortunately, we can get the needed info
    7645              :  * from the relation or the query's rangetable instead.
    7646              :  */
    7647              : static void
    7648            5 : conversion_error_callback(void *arg)
    7649              : {
    7650            5 :     ConversionLocation *errpos = (ConversionLocation *) arg;
    7651            5 :     Relation    rel = errpos->rel;
    7652            5 :     ForeignScanState *fsstate = errpos->fsstate;
    7653            5 :     const char *attname = NULL;
    7654            5 :     const char *relname = NULL;
    7655            5 :     bool        is_wholerow = false;
    7656              : 
    7657              :     /*
    7658              :      * If we're in a scan node, always use aliases from the rangetable, for
    7659              :      * consistency between the simple-relation and remote-join cases.  Look at
    7660              :      * the relation's tupdesc only if we're not in a scan node.
    7661              :      */
    7662            5 :     if (fsstate)
    7663              :     {
    7664              :         /* ForeignScan case */
    7665            4 :         ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
    7666            4 :         int         varno = 0;
    7667            4 :         AttrNumber  colno = 0;
    7668              : 
    7669            4 :         if (fsplan->scan.scanrelid > 0)
    7670              :         {
    7671              :             /* error occurred in a scan against a foreign table */
    7672            1 :             varno = fsplan->scan.scanrelid;
    7673            1 :             colno = errpos->cur_attno;
    7674              :         }
    7675              :         else
    7676              :         {
    7677              :             /* error occurred in a scan against a foreign join */
    7678              :             TargetEntry *tle;
    7679              : 
    7680            3 :             tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
    7681              :                                 errpos->cur_attno - 1);
    7682              : 
    7683              :             /*
    7684              :              * Target list can have Vars and expressions.  For Vars, we can
    7685              :              * get some information, however for expressions we can't.  Thus
    7686              :              * for expressions, just show generic context message.
    7687              :              */
    7688            3 :             if (IsA(tle->expr, Var))
    7689              :             {
    7690            2 :                 Var        *var = (Var *) tle->expr;
    7691              : 
    7692            2 :                 varno = var->varno;
    7693            2 :                 colno = var->varattno;
    7694              :             }
    7695              :         }
    7696              : 
    7697            4 :         if (varno > 0)
    7698              :         {
    7699            3 :             EState     *estate = fsstate->ss.ps.state;
    7700            3 :             RangeTblEntry *rte = exec_rt_fetch(varno, estate);
    7701              : 
    7702            3 :             relname = rte->eref->aliasname;
    7703              : 
    7704            3 :             if (colno == 0)
    7705            1 :                 is_wholerow = true;
    7706            2 :             else if (colno > 0 && colno <= list_length(rte->eref->colnames))
    7707            2 :                 attname = strVal(list_nth(rte->eref->colnames, colno - 1));
    7708            0 :             else if (colno == SelfItemPointerAttributeNumber)
    7709            0 :                 attname = "ctid";
    7710              :         }
    7711              :     }
    7712            1 :     else if (rel)
    7713              :     {
    7714              :         /* Non-ForeignScan case (we should always have a rel here) */
    7715            1 :         TupleDesc   tupdesc = RelationGetDescr(rel);
    7716              : 
    7717            1 :         relname = RelationGetRelationName(rel);
    7718            1 :         if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
    7719            1 :         {
    7720            1 :             Form_pg_attribute attr = TupleDescAttr(tupdesc,
    7721            1 :                                                    errpos->cur_attno - 1);
    7722              : 
    7723            1 :             attname = NameStr(attr->attname);
    7724              :         }
    7725            0 :         else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
    7726            0 :             attname = "ctid";
    7727              :     }
    7728              : 
    7729            5 :     if (relname && is_wholerow)
    7730            1 :         errcontext("whole-row reference to foreign table \"%s\"", relname);
    7731            4 :     else if (relname && attname)
    7732            3 :         errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
    7733              :     else
    7734            1 :         errcontext("processing expression at position %d in select list",
    7735            1 :                    errpos->cur_attno);
    7736            5 : }
    7737              : 
    7738              : /*
    7739              :  * Given an EquivalenceClass and a foreign relation, find an EC member
    7740              :  * that can be used to sort the relation remotely according to a pathkey
    7741              :  * using this EC.
    7742              :  *
    7743              :  * If there is more than one suitable candidate, return an arbitrary
    7744              :  * one of them.  If there is none, return NULL.
    7745              :  *
    7746              :  * This checks that the EC member expression uses only Vars from the given
    7747              :  * rel and is shippable.  Caller must separately verify that the pathkey's
    7748              :  * ordering operator is shippable.
    7749              :  */
    7750              : EquivalenceMember *
    7751         1821 : find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
    7752              : {
    7753         1821 :     PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
    7754              :     EquivalenceMemberIterator it;
    7755              :     EquivalenceMember *em;
    7756              : 
    7757         1821 :     setup_eclass_member_iterator(&it, ec, rel->relids);
    7758         3048 :     while ((em = eclass_member_iterator_next(&it)) != NULL)
    7759              :     {
    7760              :         /*
    7761              :          * Note we require !bms_is_empty, else we'd accept constant
    7762              :          * expressions which are not suitable for the purpose.
    7763              :          */
    7764         2768 :         if (bms_is_subset(em->em_relids, rel->relids) &&
    7765         3139 :             !bms_is_empty(em->em_relids) &&
    7766         3126 :             bms_is_empty(bms_intersect(em->em_relids, fpinfo->hidden_subquery_rels)) &&
    7767         1557 :             is_foreign_expr(root, rel, em->em_expr))
    7768         1541 :             return em;
    7769              :     }
    7770              : 
    7771          280 :     return NULL;
    7772              : }
    7773              : 
    7774              : /*
    7775              :  * Find an EquivalenceClass member that is to be computed as a sort column
    7776              :  * in the given rel's reltarget, and is shippable.
    7777              :  *
    7778              :  * If there is more than one suitable candidate, return an arbitrary
    7779              :  * one of them.  If there is none, return NULL.
    7780              :  *
    7781              :  * This checks that the EC member expression uses only Vars from the given
    7782              :  * rel and is shippable.  Caller must separately verify that the pathkey's
    7783              :  * ordering operator is shippable.
    7784              :  */
    7785              : EquivalenceMember *
    7786          255 : find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
    7787              :                        RelOptInfo *rel)
    7788              : {
    7789          255 :     PathTarget *target = rel->reltarget;
    7790              :     ListCell   *lc1;
    7791              :     int         i;
    7792              : 
    7793          255 :     i = 0;
    7794          425 :     foreach(lc1, target->exprs)
    7795              :     {
    7796          425 :         Expr       *expr = (Expr *) lfirst(lc1);
    7797          425 :         Index       sgref = get_pathtarget_sortgroupref(target, i);
    7798              :         ListCell   *lc2;
    7799              : 
    7800              :         /* Ignore non-sort expressions */
    7801          765 :         if (sgref == 0 ||
    7802          340 :             get_sortgroupref_clause_noerr(sgref,
    7803          340 :                                           root->parse->sortClause) == NULL)
    7804              :         {
    7805           93 :             i++;
    7806           93 :             continue;
    7807              :         }
    7808              : 
    7809              :         /* We ignore binary-compatible relabeling on both ends */
    7810          332 :         while (expr && IsA(expr, RelabelType))
    7811            0 :             expr = ((RelabelType *) expr)->arg;
    7812              : 
    7813              :         /*
    7814              :          * Locate an EquivalenceClass member matching this expr, if any.
    7815              :          * Ignore child members.
    7816              :          */
    7817          413 :         foreach(lc2, ec->ec_members)
    7818              :         {
    7819          336 :             EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
    7820              :             Expr       *em_expr;
    7821              : 
    7822              :             /* Don't match constants */
    7823          336 :             if (em->em_is_const)
    7824            0 :                 continue;
    7825              : 
    7826              :             /* Child members should not exist in ec_members */
    7827              :             Assert(!em->em_is_child);
    7828              : 
    7829              :             /* Match if same expression (after stripping relabel) */
    7830          336 :             em_expr = em->em_expr;
    7831          348 :             while (em_expr && IsA(em_expr, RelabelType))
    7832           12 :                 em_expr = ((RelabelType *) em_expr)->arg;
    7833              : 
    7834          336 :             if (!equal(em_expr, expr))
    7835           81 :                 continue;
    7836              : 
    7837              :             /* Check that expression (including relabels!) is shippable */
    7838          255 :             if (is_foreign_expr(root, rel, em->em_expr))
    7839          255 :                 return em;
    7840              :         }
    7841              : 
    7842           77 :         i++;
    7843              :     }
    7844              : 
    7845            0 :     return NULL;
    7846              : }
    7847              : 
    7848              : /*
    7849              :  * Determine batch size for a given foreign table. The option specified for
    7850              :  * a table has precedence.
    7851              :  */
    7852              : static int
    7853          146 : get_batch_size_option(Relation rel)
    7854              : {
    7855          146 :     Oid         foreigntableid = RelationGetRelid(rel);
    7856              :     ForeignTable *table;
    7857              :     ForeignServer *server;
    7858              :     List       *options;
    7859              :     ListCell   *lc;
    7860              : 
    7861              :     /* we use 1 by default, which means "no batching" */
    7862          146 :     int         batch_size = 1;
    7863              : 
    7864              :     /*
    7865              :      * Load options for table and server. We append server options after table
    7866              :      * options, because table options take precedence.
    7867              :      */
    7868          146 :     table = GetForeignTable(foreigntableid);
    7869          146 :     server = GetForeignServer(table->serverid);
    7870              : 
    7871          146 :     options = NIL;
    7872          146 :     options = list_concat(options, table->options);
    7873          146 :     options = list_concat(options, server->options);
    7874              : 
    7875              :     /* See if either table or server specifies batch_size. */
    7876          766 :     foreach(lc, options)
    7877              :     {
    7878          655 :         DefElem    *def = (DefElem *) lfirst(lc);
    7879              : 
    7880          655 :         if (strcmp(def->defname, "batch_size") == 0)
    7881              :         {
    7882           35 :             (void) parse_int(defGetString(def), &batch_size, 0, NULL);
    7883           35 :             break;
    7884              :         }
    7885              :     }
    7886              : 
    7887          146 :     return batch_size;
    7888              : }
        

Generated by: LCOV version 2.0-1