LCOV - differential code coverage report
Current view: top level - contrib/postgres_fdw - postgres_fdw.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GNC CBC EUB ECB DUB DCB
Current: bed3ffbf9d952be6c7d739d068cdce44c046dfb7 vs 574581b50ac9c63dd9e4abebb731a3b67e5b50f6 Lines: 93.3 % 2590 2417 53 1 119 441 1976 1 2 35 230
Current Date: 2026-05-05 10:23:31 +0900 Functions: 100.0 % 100 100 42 58 1
Baseline: lcov-20260505-025707-baseline Branches: 74.9 % 1905 1427 118 2 358 246 1181 2 54 140
Baseline Date: 2026-05-05 10:27:06 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 91.2 % 274 250 24 249 1
(30,360] days: 86.9 % 198 172 26 169 3 1
(360..) days: 94.2 % 2118 1995 3 1 119 23 1972 1 1
Function coverage date bins:
(7,30] days: 100.0 % 11 11 11
(360..) days: 100.0 % 89 89 31 58
Branch coverage date bins:
(7,30] days: 62.6 % 206 129 77 129
(30,360] days: 73.6 % 140 103 37 103
(360..) days: 76.6 % 1561 1195 4 2 358 14 1181 2

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

Generated by: LCOV version 2.5.0-beta