LCOV - differential code coverage report
Current view: top level - contrib/postgres_fdw - postgres_fdw.c (source / functions) Coverage Total Hit LBC UBC GBC CBC
Current: 77aeca80249c9e640c811e80633a2e334a9320de vs 38afc3dcb25c45b744d4025029ce0a6c90b7059f Lines: 93.6 % 2627 2459 2 166 2 2457
Current Date: 2026-07-25 19:08:27 +0900 Functions: 100.0 % 105 105 105
Baseline: lcov-20260725-baseline Branches: 75.5 % 1885 1424 3 458 1 1423
Baseline Date: 2026-07-25 19:09:19 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 100.0 % 1 1 1
(7,30] days: 94.1 % 101 95 6 95
(30,360] days: 92.4 % 224 207 17 207
(360..) days: 93.7 % 2301 2156 2 143 2 2154
Function coverage date bins:
(7,30] days: 100.0 % 7 7 7
(30,360] days: 100.0 % 9 9 9
(360..) days: 100.0 % 89 89 89
Branch coverage date bins:
(7,30] days: 65.6 % 32 21 11 21
(30,360] days: 67.1 % 158 106 52 106
(360..) days: 76.5 % 1695 1297 3 395 1 1296

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

Generated by: LCOV version 2.0-1