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

Generated by: LCOV version 2.0-1