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

Generated by: LCOV version 2.0-1