LCOV - differential code coverage report
Current view: top level - contrib/postgres_fdw - postgres_fdw.c (source / functions) Coverage Total Hit UNC UBC GBC GNC CBC DUB DCB
Current: ba12a202ce1b5581dc0ed149cf3f637d7897ad5d vs 2866d8c7dbfc9d882a7d80fef93fbbe763709932 Lines: 93.9 % 2784 2614 9 161 3 212 2399 4 55
Current Date: 2026-08-27 14:31:44 +0300 Functions: 100.0 % 109 109 26 83 2
Baseline: lcov-20260827-baseline Branches: 75.3 % 2041 1537 56 448 4 146 1387
Baseline Date: 2026-08-27 14:31:58 +0300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 100.0 % 10 10 2 8
(7,30] days: 95.6 % 226 216 9 1 210 6
(30,360] days: 93.3 % 298 278 20 278
(360..) days: 93.8 % 2250 2110 140 3 2107
Function coverage date bins:
(7,30] days: 100.0 % 6 6 6
(30,360] days: 100.0 % 15 15 2 13
(360..) days: 100.0 % 88 88 18 70
Branch coverage date bins:
(1,7] days: 50.0 % 10 5 5 5
(7,30] days: 72.0 % 214 154 56 4 146 8
(30,360] days: 67.4 % 172 116 56 116
(360..) days: 76.7 % 1645 1262 383 4 1258

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

Generated by: LCOV version 2.0-1