PostgreSQL Source Code  git master
postgres_fdw.c File Reference
#include "postgres.h"
#include <limits.h>
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/pg_opfamily.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "executor/execAsync.h"
#include "foreign/fdwapi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/cost.h"
#include "optimizer/inherit.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "postgres_fdw.h"
#include "storage/latch.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
#include "utils/selfuncs.h"
Include dependency graph for postgres_fdw.c:

Go to the source code of this file.

Data Structures

struct  PgFdwScanState
 
struct  PgFdwModifyState
 
struct  PgFdwDirectModifyState
 
struct  PgFdwAnalyzeState
 
struct  PgFdwPathExtraData
 
struct  ConversionLocation
 
struct  ec_member_foreign_arg
 

Macros

#define DEFAULT_FDW_STARTUP_COST   100.0
 
#define DEFAULT_FDW_TUPLE_COST   0.2
 
#define DEFAULT_FDW_SORT_MULTIPLIER   1.2
 

Typedefs

typedef struct PgFdwScanState PgFdwScanState
 
typedef struct PgFdwModifyState PgFdwModifyState
 
typedef struct PgFdwDirectModifyState PgFdwDirectModifyState
 
typedef struct PgFdwAnalyzeState PgFdwAnalyzeState
 
typedef struct ConversionLocation ConversionLocation
 

Enumerations

enum  FdwScanPrivateIndex { FdwScanPrivateSelectSql , FdwScanPrivateRetrievedAttrs , FdwScanPrivateFetchSize , FdwScanPrivateRelations }
 
enum  FdwModifyPrivateIndex {
  FdwModifyPrivateUpdateSql , FdwModifyPrivateTargetAttnums , FdwModifyPrivateLen , FdwModifyPrivateHasReturning ,
  FdwModifyPrivateRetrievedAttrs
}
 
enum  FdwDirectModifyPrivateIndex { FdwDirectModifyPrivateUpdateSql , FdwDirectModifyPrivateHasReturning , FdwDirectModifyPrivateRetrievedAttrs , FdwDirectModifyPrivateSetProcessed }
 
enum  FdwPathPrivateIndex { FdwPathPrivateHasFinalSort , FdwPathPrivateHasLimit }
 

Functions

 PG_FUNCTION_INFO_V1 (postgres_fdw_handler)
 
static void postgresGetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static void postgresGetForeignPaths (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static ForeignScanpostgresGetForeignPlan (PlannerInfo *root, RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
 
static void postgresBeginForeignScan (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateForeignScan (ForeignScanState *node)
 
static void postgresReScanForeignScan (ForeignScanState *node)
 
static void postgresEndForeignScan (ForeignScanState *node)
 
static void postgresAddForeignUpdateTargets (PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation)
 
static ListpostgresPlanForeignModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginForeignModify (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
 
static TupleTableSlotpostgresExecForeignInsert (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlot ** postgresExecForeignBatchInsert (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
 
static int postgresGetForeignModifyBatchSize (ResultRelInfo *resultRelInfo)
 
static TupleTableSlotpostgresExecForeignUpdate (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlotpostgresExecForeignDelete (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static void postgresEndForeignModify (EState *estate, ResultRelInfo *resultRelInfo)
 
static void postgresBeginForeignInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static void postgresEndForeignInsert (EState *estate, ResultRelInfo *resultRelInfo)
 
static int postgresIsForeignRelUpdatable (Relation rel)
 
static bool postgresPlanDirectModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginDirectModify (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateDirectModify (ForeignScanState *node)
 
static void postgresEndDirectModify (ForeignScanState *node)
 
static void postgresExplainForeignScan (ForeignScanState *node, ExplainState *es)
 
static void postgresExplainForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
 
static void postgresExplainDirectModify (ForeignScanState *node, ExplainState *es)
 
static void postgresExecForeignTruncate (List *rels, DropBehavior behavior, bool restart_seqs)
 
static bool postgresAnalyzeForeignTable (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
 
static ListpostgresImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid)
 
static void postgresGetForeignJoinPaths (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static bool postgresRecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot)
 
static void postgresGetForeignUpperPaths (PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra)
 
static bool postgresIsForeignPathAsyncCapable (ForeignPath *path)
 
static void postgresForeignAsyncRequest (AsyncRequest *areq)
 
static void postgresForeignAsyncConfigureWait (AsyncRequest *areq)
 
static void postgresForeignAsyncNotify (AsyncRequest *areq)
 
static void estimate_path_cost_size (PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, int *p_disabled_nodes, Cost *p_startup_cost, Cost *p_total_cost)
 
static void get_remote_estimate (const char *sql, PGconn *conn, double *rows, int *width, Cost *startup_cost, Cost *total_cost)
 
static void adjust_foreign_grouping_path_cost (PlannerInfo *root, List *pathkeys, double retrieved_rows, double width, double limit_tuples, int *disabled_nodes, Cost *p_startup_cost, Cost *p_run_cost)
 
static bool ec_member_matches_foreign (PlannerInfo *root, RelOptInfo *rel, EquivalenceClass *ec, EquivalenceMember *em, void *arg)
 
static void create_cursor (ForeignScanState *node)
 
static void fetch_more_data (ForeignScanState *node)
 
static void close_cursor (PGconn *conn, unsigned int cursor_number, PgFdwConnState *conn_state)
 
static PgFdwModifyStatecreate_foreign_modify (EState *estate, RangeTblEntry *rte, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, int values_end, bool has_returning, List *retrieved_attrs)
 
static TupleTableSlot ** execute_foreign_modify (EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
 
static void prepare_foreign_modify (PgFdwModifyState *fmstate)
 
static const char ** convert_prep_stmt_params (PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot **slots, int numSlots)
 
static void store_returning_result (PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
 
static void finish_foreign_modify (PgFdwModifyState *fmstate)
 
static void deallocate_query (PgFdwModifyState *fmstate)
 
static Listbuild_remote_returning (Index rtindex, Relation rel, List *returningList)
 
static void rebuild_fdw_scan_tlist (ForeignScan *fscan, List *tlist)
 
static void execute_dml_stmt (ForeignScanState *node)
 
static TupleTableSlotget_returning_data (ForeignScanState *node)
 
static void init_returning_filter (PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
 
static TupleTableSlotapply_returning_filter (PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
 
static void prepare_query_params (PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
 
static void process_query_params (ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
 
static int postgresAcquireSampleRowsFunc (Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
 
static void analyze_row_processor (PGresult *res, int row, PgFdwAnalyzeState *astate)
 
static void produce_tuple_asynchronously (AsyncRequest *areq, bool fetch)
 
static void fetch_more_data_begin (AsyncRequest *areq)
 
static void complete_pending_request (AsyncRequest *areq)
 
static HeapTuple make_tuple_from_result_row (PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
 
static void conversion_error_callback (void *arg)
 
static bool foreign_join_ok (PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra)
 
static bool foreign_grouping_ok (PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
 
static Listget_useful_pathkeys_for_relation (PlannerInfo *root, RelOptInfo *rel)
 
static Listget_useful_ecs_for_relation (PlannerInfo *root, RelOptInfo *rel)
 
static void add_paths_with_pathkeys_for_rel (PlannerInfo *root, RelOptInfo *rel, Path *epq_path, List *restrictlist)
 
static void add_foreign_grouping_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra)
 
static void add_foreign_ordered_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel)
 
static void add_foreign_final_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *final_rel, FinalPathExtraData *extra)
 
static void apply_server_options (PgFdwRelationInfo *fpinfo)
 
static void apply_table_options (PgFdwRelationInfo *fpinfo)
 
static void merge_fdw_options (PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
 
static int get_batch_size_option (Relation rel)
 
Datum postgres_fdw_handler (PG_FUNCTION_ARGS)
 
static TupleDesc get_tupdesc_for_join_scan_tuples (ForeignScanState *node)
 
static ForeignScanfind_modifytable_subplan (PlannerInfo *root, ModifyTable *plan, Index rtindex, int subplan_index)
 
int set_transmission_modes (void)
 
void reset_transmission_modes (int nestlevel)
 
static double postgresGetAnalyzeInfoForForeignTable (Relation relation, bool *can_tablesample)
 
static bool semijoin_target_ok (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel)
 
void process_pending_request (AsyncRequest *areq)
 
EquivalenceMemberfind_em_for_rel (PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 
EquivalenceMemberfind_em_for_rel_target (PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 

Variables

 PG_MODULE_MAGIC
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 60 of file postgres_fdw.c.

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 54 of file postgres_fdw.c.

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.2

Definition at line 57 of file postgres_fdw.c.

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 120 of file postgres_fdw.c.

121 {
122  /* SQL statement to execute remotely (as a String node) */
124  /* has-returning flag (as a Boolean node) */
126  /* Integer list of attribute numbers retrieved by RETURNING */
128  /* set-processed flag (as a Boolean node) */
130 };
@ FdwDirectModifyPrivateSetProcessed
Definition: postgres_fdw.c:129
@ FdwDirectModifyPrivateHasReturning
Definition: postgres_fdw.c:125
@ FdwDirectModifyPrivateRetrievedAttrs
Definition: postgres_fdw.c:127
@ FdwDirectModifyPrivateUpdateSql
Definition: postgres_fdw.c:123

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 97 of file postgres_fdw.c.

98 {
99  /* SQL statement to execute remotely (as a String node) */
101  /* Integer list of target attribute numbers for INSERT/UPDATE */
103  /* Length till the end of VALUES clause (as an Integer node) */
105  /* has-returning flag (as a Boolean node) */
107  /* Integer list of attribute numbers retrieved by RETURNING */
109 };
@ FdwModifyPrivateLen
Definition: postgres_fdw.c:104
@ FdwModifyPrivateUpdateSql
Definition: postgres_fdw.c:100
@ FdwModifyPrivateTargetAttnums
Definition: postgres_fdw.c:102
@ FdwModifyPrivateRetrievedAttrs
Definition: postgres_fdw.c:108
@ FdwModifyPrivateHasReturning
Definition: postgres_fdw.c:106

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 280 of file postgres_fdw.c.

281 {
282  /* has-final-sort flag (as a Boolean node) */
284  /* has-limit flag (as a Boolean node) */
286 };
@ FdwPathPrivateHasLimit
Definition: postgres_fdw.c:285
@ FdwPathPrivateHasFinalSort
Definition: postgres_fdw.c:283

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 69 of file postgres_fdw.c.

70 {
71  /* SQL statement to execute remotely (as a String node) */
73  /* Integer list of attribute numbers retrieved by the SELECT */
75  /* Integer representing the desired fetch_size */
77 
78  /*
79  * String describing join i.e. names of relations being joined and types
80  * of join, added when the scan is join
81  */
83 };
@ FdwScanPrivateRetrievedAttrs
Definition: postgres_fdw.c:74
@ FdwScanPrivateSelectSql
Definition: postgres_fdw.c:72
@ FdwScanPrivateFetchSize
Definition: postgres_fdw.c:76
@ FdwScanPrivateRelations
Definition: postgres_fdw.c:82

Function Documentation

◆ add_foreign_final_paths()

static void add_foreign_final_paths ( PlannerInfo root,
RelOptInfo input_rel,
RelOptInfo final_rel,
FinalPathExtraData extra 
)
static

Definition at line 7013 of file postgres_fdw.c.

7016 {
7017  Query *parse = root->parse;
7018  PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
7019  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
7020  bool has_final_sort = false;
7021  List *pathkeys = NIL;
7022  PgFdwPathExtraData *fpextra;
7023  bool save_use_remote_estimate = false;
7024  double rows;
7025  int width;
7026  int disabled_nodes;
7027  Cost startup_cost;
7028  Cost total_cost;
7029  List *fdw_private;
7030  ForeignPath *final_path;
7031 
7032  /*
7033  * Currently, we only support this for SELECT commands
7034  */
7035  if (parse->commandType != CMD_SELECT)
7036  return;
7037 
7038  /*
7039  * No work if there is no FOR UPDATE/SHARE clause and if there is no need
7040  * to add a LIMIT node
7041  */
7042  if (!parse->rowMarks && !extra->limit_needed)
7043  return;
7044 
7045  /* We don't support cases where there are any SRFs in the targetlist */
7046  if (parse->hasTargetSRFs)
7047  return;
7048 
7049  /* Save the input_rel as outerrel in fpinfo */
7050  fpinfo->outerrel = input_rel;
7051 
7052  /*
7053  * Copy foreign table, foreign server, user mapping, FDW options etc.
7054  * details from the input relation's fpinfo.
7055  */
7056  fpinfo->table = ifpinfo->table;
7057  fpinfo->server = ifpinfo->server;
7058  fpinfo->user = ifpinfo->user;
7059  merge_fdw_options(fpinfo, ifpinfo, NULL);
7060 
7061  /*
7062  * If there is no need to add a LIMIT node, there might be a ForeignPath
7063  * in the input_rel's pathlist that implements all behavior of the query.
7064  * Note: we would already have accounted for the query's FOR UPDATE/SHARE
7065  * (if any) before we get here.
7066  */
7067  if (!extra->limit_needed)
7068  {
7069  ListCell *lc;
7070 
7071  Assert(parse->rowMarks);
7072 
7073  /*
7074  * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
7075  * so the input_rel should be a base, join, or ordered relation; and
7076  * if it's an ordered relation, its input relation should be a base or
7077  * join relation.
7078  */
7079  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7080  input_rel->reloptkind == RELOPT_JOINREL ||
7081  (input_rel->reloptkind == RELOPT_UPPER_REL &&
7082  ifpinfo->stage == UPPERREL_ORDERED &&
7083  (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
7084  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
7085 
7086  foreach(lc, input_rel->pathlist)
7087  {
7088  Path *path = (Path *) lfirst(lc);
7089 
7090  /*
7091  * apply_scanjoin_target_to_paths() uses create_projection_path()
7092  * to adjust each of its input paths if needed, whereas
7093  * create_ordered_paths() uses apply_projection_to_path() to do
7094  * that. So the former might have put a ProjectionPath on top of
7095  * the ForeignPath; look through ProjectionPath and see if the
7096  * path underneath it is ForeignPath.
7097  */
7098  if (IsA(path, ForeignPath) ||
7099  (IsA(path, ProjectionPath) &&
7100  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
7101  {
7102  /*
7103  * Create foreign final path; this gets rid of a
7104  * no-longer-needed outer plan (if any), which makes the
7105  * EXPLAIN output look cleaner
7106  */
7107  final_path = create_foreign_upper_path(root,
7108  path->parent,
7109  path->pathtarget,
7110  path->rows,
7111  path->disabled_nodes,
7112  path->startup_cost,
7113  path->total_cost,
7114  path->pathkeys,
7115  NULL, /* no extra plan */
7116  NIL, /* no fdw_restrictinfo
7117  * list */
7118  NIL); /* no fdw_private */
7119 
7120  /* and add it to the final_rel */
7121  add_path(final_rel, (Path *) final_path);
7122 
7123  /* Safe to push down */
7124  fpinfo->pushdown_safe = true;
7125 
7126  return;
7127  }
7128  }
7129 
7130  /*
7131  * If we get here it means no ForeignPaths; since we would already
7132  * have considered pushing down all operations for the query to the
7133  * remote server, give up on it.
7134  */
7135  return;
7136  }
7137 
7138  Assert(extra->limit_needed);
7139 
7140  /*
7141  * If the input_rel is an ordered relation, replace the input_rel with its
7142  * input relation
7143  */
7144  if (input_rel->reloptkind == RELOPT_UPPER_REL &&
7145  ifpinfo->stage == UPPERREL_ORDERED)
7146  {
7147  input_rel = ifpinfo->outerrel;
7148  ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
7149  has_final_sort = true;
7150  pathkeys = root->sort_pathkeys;
7151  }
7152 
7153  /* The input_rel should be a base, join, or grouping relation */
7154  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7155  input_rel->reloptkind == RELOPT_JOINREL ||
7156  (input_rel->reloptkind == RELOPT_UPPER_REL &&
7157  ifpinfo->stage == UPPERREL_GROUP_AGG));
7158 
7159  /*
7160  * We try to create a path below by extending a simple foreign path for
7161  * the underlying base, join, or grouping relation to perform the final
7162  * sort (if has_final_sort) and the LIMIT restriction remotely, which is
7163  * stored into the fdw_private list of the resulting path. (We
7164  * re-estimate the costs of sorting the underlying relation, if
7165  * has_final_sort.)
7166  */
7167 
7168  /*
7169  * Assess if it is safe to push down the LIMIT and OFFSET to the remote
7170  * server
7171  */
7172 
7173  /*
7174  * If the underlying relation has any local conditions, the LIMIT/OFFSET
7175  * cannot be pushed down.
7176  */
7177  if (ifpinfo->local_conds)
7178  return;
7179 
7180  /*
7181  * If the query has FETCH FIRST .. WITH TIES, 1) it must have ORDER BY as
7182  * well, which is used to determine which additional rows tie for the last
7183  * place in the result set, and 2) ORDER BY must already have been
7184  * determined to be safe to push down before we get here. So in that case
7185  * the FETCH clause is safe to push down with ORDER BY if the remote
7186  * server is v13 or later, but if not, the remote query will fail entirely
7187  * for lack of support for it. Since we do not currently have a way to do
7188  * a remote-version check (without accessing the remote server), disable
7189  * pushing the FETCH clause for now.
7190  */
7191  if (parse->limitOption == LIMIT_OPTION_WITH_TIES)
7192  return;
7193 
7194  /*
7195  * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
7196  * not safe to remote.
7197  */
7198  if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
7199  !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
7200  return;
7201 
7202  /* Safe to push down */
7203  fpinfo->pushdown_safe = true;
7204 
7205  /* Construct PgFdwPathExtraData */
7206  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
7207  fpextra->target = root->upper_targets[UPPERREL_FINAL];
7208  fpextra->has_final_sort = has_final_sort;
7209  fpextra->has_limit = extra->limit_needed;
7210  fpextra->limit_tuples = extra->limit_tuples;
7211  fpextra->count_est = extra->count_est;
7212  fpextra->offset_est = extra->offset_est;
7213 
7214  /*
7215  * Estimate the costs of performing the final sort and the LIMIT
7216  * restriction remotely. If has_final_sort is false, we wouldn't need to
7217  * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
7218  * roughly estimated using the costs we already have for the underlying
7219  * relation, in the same way as when use_remote_estimate is false. Since
7220  * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
7221  * false in that case.
7222  */
7223  if (!fpextra->has_final_sort)
7224  {
7225  save_use_remote_estimate = ifpinfo->use_remote_estimate;
7226  ifpinfo->use_remote_estimate = false;
7227  }
7228  estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
7229  &rows, &width, &disabled_nodes,
7230  &startup_cost, &total_cost);
7231  if (!fpextra->has_final_sort)
7232  ifpinfo->use_remote_estimate = save_use_remote_estimate;
7233 
7234  /*
7235  * Build the fdw_private list that will be used by postgresGetForeignPlan.
7236  * Items in the list must match order in enum FdwPathPrivateIndex.
7237  */
7238  fdw_private = list_make2(makeBoolean(has_final_sort),
7239  makeBoolean(extra->limit_needed));
7240 
7241  /*
7242  * Create foreign final path; this gets rid of a no-longer-needed outer
7243  * plan (if any), which makes the EXPLAIN output look cleaner
7244  */
7245  final_path = create_foreign_upper_path(root,
7246  input_rel,
7247  root->upper_targets[UPPERREL_FINAL],
7248  rows,
7249  disabled_nodes,
7250  startup_cost,
7251  total_cost,
7252  pathkeys,
7253  NULL, /* no extra plan */
7254  NIL, /* no fdw_restrictinfo list */
7255  fdw_private);
7256 
7257  /* and add it to the final_rel */
7258  add_path(final_rel, (Path *) final_path);
7259 }
#define Assert(condition)
Definition: c.h:837
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:242
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:308
void * palloc0(Size size)
Definition: mcxt.c:1347
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
double Cost
Definition: nodes.h:251
@ CMD_SELECT
Definition: nodes.h:265
@ LIMIT_OPTION_WITH_TIES
Definition: nodes.h:432
ForeignPath * create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, int disabled_nodes, Cost startup_cost, Cost total_cost, List *pathkeys, Path *fdw_outerpath, List *fdw_restrictinfo, List *fdw_private)
Definition: pathnode.c:2409
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:461
@ UPPERREL_GROUP_AGG
Definition: pathnodes.h:74
@ UPPERREL_FINAL
Definition: pathnodes.h:79
@ UPPERREL_ORDERED
Definition: pathnodes.h:78
@ RELOPT_BASEREL
Definition: pathnodes.h:827
@ RELOPT_UPPER_REL
Definition: pathnodes.h:831
@ RELOPT_JOINREL
Definition: pathnodes.h:828
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
#define list_make2(x1, x2)
Definition: pg_list.h:214
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, int *p_disabled_nodes, Cost *p_startup_cost, Cost *p_total_cost)
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
tree ctl root
Definition: radixtree.h:1886
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:717
Cardinality limit_tuples
Definition: pathnodes.h:3326
Definition: pg_list.h:54
List * pathkeys
Definition: pathnodes.h:1675
Cardinality rows
Definition: pathnodes.h:1669
Cost startup_cost
Definition: pathnodes.h:1671
int disabled_nodes
Definition: pathnodes.h:1670
Cost total_cost
Definition: pathnodes.h:1672
PathTarget * target
Definition: postgres_fdw.c:291
RelOptInfo * outerrel
Definition: postgres_fdw.h:103
ForeignTable * table
Definition: postgres_fdw.h:86
UserMapping * user
Definition: postgres_fdw.h:88
ForeignServer * server
Definition: postgres_fdw.h:87
UpperRelationKind stage
Definition: postgres_fdw.h:110
List * pathlist
Definition: pathnodes.h:898
RelOptKind reloptkind
Definition: pathnodes.h:865
Boolean * makeBoolean(bool val)
Definition: value.c:49

References add_path(), Assert, CMD_SELECT, PgFdwPathExtraData::count_est, FinalPathExtraData::count_est, create_foreign_upper_path(), Path::disabled_nodes, estimate_path_cost_size(), PgFdwPathExtraData::has_final_sort, PgFdwPathExtraData::has_limit, is_foreign_expr(), IsA, lfirst, FinalPathExtraData::limit_needed, LIMIT_OPTION_WITH_TIES, PgFdwPathExtraData::limit_tuples, FinalPathExtraData::limit_tuples, list_make2, PgFdwRelationInfo::local_conds, makeBoolean(), merge_fdw_options(), NIL, PgFdwPathExtraData::offset_est, FinalPathExtraData::offset_est, PgFdwRelationInfo::outerrel, palloc0(), parse(), Path::pathkeys, RelOptInfo::pathlist, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, root, Path::rows, PgFdwRelationInfo::server, PgFdwRelationInfo::stage, Path::startup_cost, subpath(), PgFdwRelationInfo::table, PgFdwPathExtraData::target, Path::total_cost, UPPERREL_FINAL, UPPERREL_GROUP_AGG, UPPERREL_ORDERED, PgFdwRelationInfo::use_remote_estimate, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignUpperPaths().

◆ add_foreign_grouping_paths()

static void add_foreign_grouping_paths ( PlannerInfo root,
RelOptInfo input_rel,
RelOptInfo grouped_rel,
GroupPathExtraData extra 
)
static

Definition at line 6779 of file postgres_fdw.c.

6782 {
6783  Query *parse = root->parse;
6784  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6785  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
6786  ForeignPath *grouppath;
6787  double rows;
6788  int width;
6789  int disabled_nodes;
6790  Cost startup_cost;
6791  Cost total_cost;
6792 
6793  /* Nothing to be done, if there is no grouping or aggregation required. */
6794  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
6795  !root->hasHavingQual)
6796  return;
6797 
6800 
6801  /* save the input_rel as outerrel in fpinfo */
6802  fpinfo->outerrel = input_rel;
6803 
6804  /*
6805  * Copy foreign table, foreign server, user mapping, FDW options etc.
6806  * details from the input relation's fpinfo.
6807  */
6808  fpinfo->table = ifpinfo->table;
6809  fpinfo->server = ifpinfo->server;
6810  fpinfo->user = ifpinfo->user;
6811  merge_fdw_options(fpinfo, ifpinfo, NULL);
6812 
6813  /*
6814  * Assess if it is safe to push down aggregation and grouping.
6815  *
6816  * Use HAVING qual from extra. In case of child partition, it will have
6817  * translated Vars.
6818  */
6819  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
6820  return;
6821 
6822  /*
6823  * Compute the selectivity and cost of the local_conds, so we don't have
6824  * to do it over again for each path. (Currently we create just a single
6825  * path here, but in future it would be possible that we build more paths
6826  * such as pre-sorted paths as in postgresGetForeignPaths and
6827  * postgresGetForeignJoinPaths.) The best we can do for these conditions
6828  * is to estimate selectivity on the basis of local statistics.
6829  */
6831  fpinfo->local_conds,
6832  0,
6833  JOIN_INNER,
6834  NULL);
6835 
6836  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6837 
6838  /* Estimate the cost of push down */
6839  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
6840  &rows, &width, &disabled_nodes,
6841  &startup_cost, &total_cost);
6842 
6843  /* Now update this information in the fpinfo */
6844  fpinfo->rows = rows;
6845  fpinfo->width = width;
6846  fpinfo->disabled_nodes = disabled_nodes;
6847  fpinfo->startup_cost = startup_cost;
6848  fpinfo->total_cost = total_cost;
6849 
6850  /* Create and add foreign path to the grouping relation. */
6851  grouppath = create_foreign_upper_path(root,
6852  grouped_rel,
6853  grouped_rel->reltarget,
6854  rows,
6855  disabled_nodes,
6856  startup_cost,
6857  total_cost,
6858  NIL, /* no pathkeys */
6859  NULL,
6860  NIL, /* no fdw_restrictinfo list */
6861  NIL); /* no fdw_private */
6862 
6863  /* Add generated path into grouped_rel by add_path(). */
6864  add_path(grouped_rel, (Path *) grouppath);
6865 }
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:100
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:4732
@ JOIN_INNER
Definition: nodes.h:293
@ PARTITIONWISE_AGGREGATE_FULL
Definition: pathnodes.h:3281
@ PARTITIONWISE_AGGREGATE_NONE
Definition: pathnodes.h:3280
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
PartitionwiseAggregateType patype
Definition: pathnodes.h:3310
Selectivity local_conds_sel
Definition: postgres_fdw.h:57
QualCost local_conds_cost
Definition: postgres_fdw.h:56
struct PathTarget * reltarget
Definition: pathnodes.h:893

References add_path(), Assert, clauselist_selectivity(), cost_qual_eval(), create_foreign_upper_path(), PgFdwRelationInfo::disabled_nodes, estimate_path_cost_size(), foreign_grouping_ok(), GroupPathExtraData::havingQual, JOIN_INNER, PgFdwRelationInfo::local_conds, PgFdwRelationInfo::local_conds_cost, PgFdwRelationInfo::local_conds_sel, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, parse(), PARTITIONWISE_AGGREGATE_FULL, PARTITIONWISE_AGGREGATE_NONE, GroupPathExtraData::patype, RelOptInfo::reltarget, root, PgFdwRelationInfo::rows, PgFdwRelationInfo::server, PgFdwRelationInfo::startup_cost, PgFdwRelationInfo::table, PgFdwRelationInfo::total_cost, PgFdwRelationInfo::user, and PgFdwRelationInfo::width.

Referenced by postgresGetForeignUpperPaths().

◆ add_foreign_ordered_paths()

static void add_foreign_ordered_paths ( PlannerInfo root,
RelOptInfo input_rel,
RelOptInfo ordered_rel 
)
static

Definition at line 6875 of file postgres_fdw.c.

6877 {
6878  Query *parse = root->parse;
6879  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6880  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
6881  PgFdwPathExtraData *fpextra;
6882  double rows;
6883  int width;
6884  int disabled_nodes;
6885  Cost startup_cost;
6886  Cost total_cost;
6887  List *fdw_private;
6888  ForeignPath *ordered_path;
6889  ListCell *lc;
6890 
6891  /* Shouldn't get here unless the query has ORDER BY */
6892  Assert(parse->sortClause);
6893 
6894  /* We don't support cases where there are any SRFs in the targetlist */
6895  if (parse->hasTargetSRFs)
6896  return;
6897 
6898  /* Save the input_rel as outerrel in fpinfo */
6899  fpinfo->outerrel = input_rel;
6900 
6901  /*
6902  * Copy foreign table, foreign server, user mapping, FDW options etc.
6903  * details from the input relation's fpinfo.
6904  */
6905  fpinfo->table = ifpinfo->table;
6906  fpinfo->server = ifpinfo->server;
6907  fpinfo->user = ifpinfo->user;
6908  merge_fdw_options(fpinfo, ifpinfo, NULL);
6909 
6910  /*
6911  * If the input_rel is a base or join relation, we would already have
6912  * considered pushing down the final sort to the remote server when
6913  * creating pre-sorted foreign paths for that relation, because the
6914  * query_pathkeys is set to the root->sort_pathkeys in that case (see
6915  * standard_qp_callback()).
6916  */
6917  if (input_rel->reloptkind == RELOPT_BASEREL ||
6918  input_rel->reloptkind == RELOPT_JOINREL)
6919  {
6920  Assert(root->query_pathkeys == root->sort_pathkeys);
6921 
6922  /* Safe to push down if the query_pathkeys is safe to push down */
6923  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
6924 
6925  return;
6926  }
6927 
6928  /* The input_rel should be a grouping relation */
6929  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
6930  ifpinfo->stage == UPPERREL_GROUP_AGG);
6931 
6932  /*
6933  * We try to create a path below by extending a simple foreign path for
6934  * the underlying grouping relation to perform the final sort remotely,
6935  * which is stored into the fdw_private list of the resulting path.
6936  */
6937 
6938  /* Assess if it is safe to push down the final sort */
6939  foreach(lc, root->sort_pathkeys)
6940  {
6941  PathKey *pathkey = (PathKey *) lfirst(lc);
6942  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6943 
6944  /*
6945  * is_foreign_expr would detect volatile expressions as well, but
6946  * checking ec_has_volatile here saves some cycles.
6947  */
6948  if (pathkey_ec->ec_has_volatile)
6949  return;
6950 
6951  /*
6952  * Can't push down the sort if pathkey's opfamily is not shippable.
6953  */
6954  if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId,
6955  fpinfo))
6956  return;
6957 
6958  /*
6959  * The EC must contain a shippable EM that is computed in input_rel's
6960  * reltarget, else we can't push down the sort.
6961  */
6963  pathkey_ec,
6964  input_rel) == NULL)
6965  return;
6966  }
6967 
6968  /* Safe to push down */
6969  fpinfo->pushdown_safe = true;
6970 
6971  /* Construct PgFdwPathExtraData */
6972  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6973  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6974  fpextra->has_final_sort = true;
6975 
6976  /* Estimate the costs of performing the final sort remotely */
6977  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6978  &rows, &width, &disabled_nodes,
6979  &startup_cost, &total_cost);
6980 
6981  /*
6982  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6983  * Items in the list must match order in enum FdwPathPrivateIndex.
6984  */
6985  fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
6986 
6987  /* Create foreign ordering path */
6988  ordered_path = create_foreign_upper_path(root,
6989  input_rel,
6990  root->upper_targets[UPPERREL_ORDERED],
6991  rows,
6992  disabled_nodes,
6993  startup_cost,
6994  total_cost,
6995  root->sort_pathkeys,
6996  NULL, /* no extra plan */
6997  NIL, /* no fdw_restrictinfo
6998  * list */
6999  fdw_private);
7000 
7001  /* and add it to the ordered_rel */
7002  add_path(ordered_rel, (Path *) ordered_path);
7003 }
EquivalenceMember * find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
bool is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
Definition: shippable.c:162
Oid pk_opfamily
Definition: pathnodes.h:1478

References add_path(), Assert, create_foreign_upper_path(), EquivalenceClass::ec_has_volatile, estimate_path_cost_size(), find_em_for_rel_target(), PgFdwPathExtraData::has_final_sort, is_shippable(), lfirst, list_make2, makeBoolean(), merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, palloc0(), parse(), PathKey::pk_opfamily, PgFdwRelationInfo::pushdown_safe, PgFdwRelationInfo::qp_is_pushdown_safe, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, root, PgFdwRelationInfo::server, PgFdwRelationInfo::stage, PgFdwRelationInfo::table, PgFdwPathExtraData::target, UPPERREL_GROUP_AGG, UPPERREL_ORDERED, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignUpperPaths().

◆ add_paths_with_pathkeys_for_rel()

static void add_paths_with_pathkeys_for_rel ( PlannerInfo root,
RelOptInfo rel,
Path epq_path,
List restrictlist 
)
static

Definition at line 6098 of file postgres_fdw.c.

6100 {
6101  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6102  ListCell *lc;
6103 
6104  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
6105 
6106  /*
6107  * Before creating sorted paths, arrange for the passed-in EPQ path, if
6108  * any, to return columns needed by the parent ForeignScan node so that
6109  * they will propagate up through Sort nodes injected below, if necessary.
6110  */
6111  if (epq_path != NULL && useful_pathkeys_list != NIL)
6112  {
6113  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6114  PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6115 
6116  /* Include columns required for evaluating PHVs in the tlist. */
6118  pull_var_clause((Node *) target->exprs,
6120 
6121  /* Include columns required for evaluating the local conditions. */
6122  foreach(lc, fpinfo->local_conds)
6123  {
6124  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6125 
6127  pull_var_clause((Node *) rinfo->clause,
6129  }
6130 
6131  /*
6132  * If we have added any new columns, adjust the tlist of the EPQ path.
6133  *
6134  * Note: the plan created using this path will only be used to execute
6135  * EPQ checks, where accuracy of the plan cost and width estimates
6136  * would not be important, so we do not do set_pathtarget_cost_width()
6137  * for the new pathtarget here. See also postgresGetForeignPlan().
6138  */
6139  if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6140  {
6141  /* The EPQ path is a join path, so it is projection-capable. */
6143 
6144  /*
6145  * Use create_projection_path() here, so as to avoid modifying it
6146  * in place.
6147  */
6148  epq_path = (Path *) create_projection_path(root,
6149  rel,
6150  epq_path,
6151  target);
6152  }
6153  }
6154 
6155  /* Create one path for each set of pathkeys we found above. */
6156  foreach(lc, useful_pathkeys_list)
6157  {
6158  double rows;
6159  int width;
6160  int disabled_nodes;
6161  Cost startup_cost;
6162  Cost total_cost;
6163  List *useful_pathkeys = lfirst(lc);
6164  Path *sorted_epq_path;
6165 
6166  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
6167  &rows, &width, &disabled_nodes,
6168  &startup_cost, &total_cost);
6169 
6170  /*
6171  * The EPQ path must be at least as well sorted as the path itself, in
6172  * case it gets used as input to a mergejoin.
6173  */
6174  sorted_epq_path = epq_path;
6175  if (sorted_epq_path != NULL &&
6176  !pathkeys_contained_in(useful_pathkeys,
6177  sorted_epq_path->pathkeys))
6178  sorted_epq_path = (Path *)
6180  rel,
6181  sorted_epq_path,
6182  useful_pathkeys,
6183  -1.0);
6184 
6185  if (IS_SIMPLE_REL(rel))
6186  add_path(rel, (Path *)
6188  NULL,
6189  rows,
6190  disabled_nodes,
6191  startup_cost,
6192  total_cost,
6193  useful_pathkeys,
6194  rel->lateral_relids,
6195  sorted_epq_path,
6196  NIL, /* no fdw_restrictinfo
6197  * list */
6198  NIL));
6199  else
6200  add_path(rel, (Path *)
6202  NULL,
6203  rows,
6204  disabled_nodes,
6205  startup_cost,
6206  total_cost,
6207  useful_pathkeys,
6208  rel->lateral_relids,
6209  sorted_epq_path,
6210  restrictlist,
6211  NIL));
6212  }
6213 }
bool is_projection_capable_path(Path *path)
Definition: createplan.c:7294
#define PVC_RECURSE_PLACEHOLDERS
Definition: optimizer.h:192
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:343
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:3082
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2763
ForeignPath * create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, int disabled_nodes, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_restrictinfo, List *fdw_private)
Definition: pathnode.c:2355
ForeignPath * create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, int disabled_nodes, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_restrictinfo, List *fdw_private)
Definition: pathnode.c:2307
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:839
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:905
Definition: nodes.h:129
Relids lateral_relids
Definition: pathnodes.h:913
Expr * clause
Definition: pathnodes.h:2574
PathTarget * copy_pathtarget(PathTarget *src)
Definition: tlist.c:657
void add_new_columns_to_pathtarget(PathTarget *target, List *exprs)
Definition: tlist.c:752
List * pull_var_clause(Node *node, int flags)
Definition: var.c:609

References add_new_columns_to_pathtarget(), add_path(), Assert, RestrictInfo::clause, copy_pathtarget(), create_foreign_join_path(), create_foreignscan_path(), create_projection_path(), create_sort_path(), estimate_path_cost_size(), get_useful_pathkeys_for_relation(), is_projection_capable_path(), IS_SIMPLE_REL, RelOptInfo::lateral_relids, lfirst, lfirst_node, list_length(), PgFdwRelationInfo::local_conds, NIL, Path::pathkeys, pathkeys_contained_in(), pull_var_clause(), PVC_RECURSE_PLACEHOLDERS, and root.

Referenced by postgresGetForeignJoinPaths(), and postgresGetForeignPaths().

◆ adjust_foreign_grouping_path_cost()

static void adjust_foreign_grouping_path_cost ( PlannerInfo root,
List pathkeys,
double  retrieved_rows,
double  width,
double  limit_tuples,
int *  disabled_nodes,
Cost p_startup_cost,
Cost p_run_cost 
)
static

Definition at line 3643 of file postgres_fdw.c.

3651 {
3652  /*
3653  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3654  * side is unlikely to generate properly-sorted output, so it would need
3655  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3656  * if the GROUP BY clause is sort-able but isn't a superset of the given
3657  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3658  * costs by applying the same heuristic as for the scan or join case.
3659  */
3660  if (!grouping_is_sortable(root->processed_groupClause) ||
3661  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3662  {
3663  Path sort_path; /* dummy for result of cost_sort */
3664 
3665  cost_sort(&sort_path,
3666  root,
3667  pathkeys,
3668  0,
3669  *p_startup_cost + *p_run_cost,
3670  retrieved_rows,
3671  width,
3672  0.0,
3673  work_mem,
3674  limit_tuples);
3675 
3676  *p_startup_cost = sort_path.startup_cost;
3677  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3678  }
3679  else
3680  {
3681  /*
3682  * The default extra cost seems too large for foreign-grouping cases;
3683  * add 1/4th of that default.
3684  */
3685  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3686  - 1.0) * 0.25;
3687 
3688  *p_startup_cost *= sort_multiplier;
3689  *p_run_cost *= sort_multiplier;
3690  }
3691 }
void cost_sort(Path *path, PlannerInfo *root, List *pathkeys, int input_disabled_nodes, Cost input_cost, double tuples, int width, Cost comparison_cost, int sort_mem, double limit_tuples)
Definition: costsize.c:2144
int work_mem
Definition: globals.c:130
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:60
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:540

References cost_sort(), DEFAULT_FDW_SORT_MULTIPLIER, grouping_is_sortable(), pathkeys_contained_in(), root, Path::startup_cost, Path::total_cost, and work_mem.

Referenced by estimate_path_cost_size().

◆ analyze_row_processor()

static void analyze_row_processor ( PGresult res,
int  row,
PgFdwAnalyzeState astate 
)
static

Definition at line 5388 of file postgres_fdw.c.

5389 {
5390  int targrows = astate->targrows;
5391  int pos; /* array index to store tuple in */
5392  MemoryContext oldcontext;
5393 
5394  /* Always increment sample row counter. */
5395  astate->samplerows += 1;
5396 
5397  /*
5398  * Determine the slot where this sample row should be stored. Set pos to
5399  * negative value to indicate the row should be skipped.
5400  */
5401  if (astate->numrows < targrows)
5402  {
5403  /* First targrows rows are always included into the sample */
5404  pos = astate->numrows++;
5405  }
5406  else
5407  {
5408  /*
5409  * Now we start replacing tuples in the sample until we reach the end
5410  * of the relation. Same algorithm as in acquire_sample_rows in
5411  * analyze.c; see Jeff Vitter's paper.
5412  */
5413  if (astate->rowstoskip < 0)
5414  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5415 
5416  if (astate->rowstoskip <= 0)
5417  {
5418  /* Choose a random reservoir element to replace. */
5419  pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5420  Assert(pos >= 0 && pos < targrows);
5421  heap_freetuple(astate->rows[pos]);
5422  }
5423  else
5424  {
5425  /* Skip this tuple. */
5426  pos = -1;
5427  }
5428 
5429  astate->rowstoskip -= 1;
5430  }
5431 
5432  if (pos >= 0)
5433  {
5434  /*
5435  * Create sample tuple from current result row, and store it in the
5436  * position determined above. The tuple has to be created in anl_cxt.
5437  */
5438  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5439 
5440  astate->rows[pos] = make_tuple_from_result_row(res, row,
5441  astate->rel,
5442  astate->attinmeta,
5443  astate->retrieved_attrs,
5444  NULL,
5445  astate->temp_cxt);
5446 
5447  MemoryContextSwitchTo(oldcontext);
5448  }
5449 }
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
MemoryContextSwitchTo(old_ctx)
double sampler_random_fract(pg_prng_state *randstate)
Definition: sampling.c:241
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:147
ReservoirStateData rstate
Definition: postgres_fdw.c:266
AttInMetadata * attinmeta
Definition: postgres_fdw.c:255
MemoryContext anl_cxt
Definition: postgres_fdw.c:269
HeapTuple * rows
Definition: postgres_fdw.c:259
MemoryContext temp_cxt
Definition: postgres_fdw.c:270
pg_prng_state randstate
Definition: sampling.h:49

References PgFdwAnalyzeState::anl_cxt, Assert, PgFdwAnalyzeState::attinmeta, heap_freetuple(), make_tuple_from_result_row(), MemoryContextSwitchTo(), PgFdwAnalyzeState::numrows, ReservoirStateData::randstate, PgFdwAnalyzeState::rel, res, reservoir_get_next_S(), PgFdwAnalyzeState::retrieved_attrs, PgFdwAnalyzeState::rows, PgFdwAnalyzeState::rowstoskip, PgFdwAnalyzeState::rstate, sampler_random_fract(), PgFdwAnalyzeState::samplerows, PgFdwAnalyzeState::targrows, and PgFdwAnalyzeState::temp_cxt.

Referenced by postgresAcquireSampleRowsFunc().

◆ apply_returning_filter()

static TupleTableSlot * apply_returning_filter ( PgFdwDirectModifyState dmstate,
ResultRelInfo resultRelInfo,
TupleTableSlot slot,
EState estate 
)
static

Definition at line 4757 of file postgres_fdw.c.

4761 {
4762  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4763  TupleTableSlot *resultSlot;
4764  Datum *values;
4765  bool *isnull;
4766  Datum *old_values;
4767  bool *old_isnull;
4768  int i;
4769 
4770  /*
4771  * Use the return tuple slot as a place to store the result tuple.
4772  */
4773  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4774 
4775  /*
4776  * Extract all the values of the scan tuple.
4777  */
4778  slot_getallattrs(slot);
4779  old_values = slot->tts_values;
4780  old_isnull = slot->tts_isnull;
4781 
4782  /*
4783  * Prepare to build the result tuple.
4784  */
4785  ExecClearTuple(resultSlot);
4786  values = resultSlot->tts_values;
4787  isnull = resultSlot->tts_isnull;
4788 
4789  /*
4790  * Transpose data into proper fields of the result tuple.
4791  */
4792  for (i = 0; i < resultTupType->natts; i++)
4793  {
4794  int j = dmstate->attnoMap[i];
4795 
4796  if (j == 0)
4797  {
4798  values[i] = (Datum) 0;
4799  isnull[i] = true;
4800  }
4801  else
4802  {
4803  values[i] = old_values[j - 1];
4804  isnull[i] = old_isnull[j - 1];
4805  }
4806  }
4807 
4808  /*
4809  * Build the virtual tuple.
4810  */
4811  ExecStoreVirtualTuple(resultSlot);
4812 
4813  /*
4814  * If we have any system columns to return, materialize a heap tuple in
4815  * the slot from column values set above and install system columns in
4816  * that tuple.
4817  */
4818  if (dmstate->hasSystemCols)
4819  {
4820  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4821 
4822  /* ctid */
4823  if (dmstate->ctidAttno)
4824  {
4825  ItemPointer ctid = NULL;
4826 
4827  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4828  resultTup->t_self = *ctid;
4829  }
4830 
4831  /*
4832  * And remaining columns
4833  *
4834  * Note: since we currently don't allow the target relation to appear
4835  * on the nullable side of an outer join, any system columns wouldn't
4836  * go to NULL.
4837  *
4838  * Note: no need to care about tableoid here because it will be
4839  * initialized in ExecProcessReturning().
4840  */
4844  }
4845 
4846  /*
4847  * And return the result tuple.
4848  */
4849  return resultSlot;
4850 }
static Datum values[MAXATTR]
Definition: bootstrap.c:151
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1639
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1731
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1184
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:315
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:376
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:393
int j
Definition: isn.c:73
int i
Definition: isn.c:72
ItemPointerData * ItemPointer
Definition: itemptr.h:49
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define RelationGetDescr(relation)
Definition: rel.h:531
ItemPointerData t_self
Definition: htup.h:65
HeapTupleHeader t_data
Definition: htup.h:68
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
#define InvalidTransactionId
Definition: transam.h:31
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:368

References PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, DatumGetPointer(), ExecClearTuple(), ExecFetchSlotHeapTuple(), ExecGetReturningSlot(), ExecStoreVirtualTuple(), PgFdwDirectModifyState::hasSystemCols, HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, i, InvalidTransactionId, j, TupleDescData::natts, RelationGetDescr, PgFdwDirectModifyState::resultRel, slot_getallattrs(), HeapTupleData::t_data, HeapTupleData::t_self, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, and values.

Referenced by get_returning_data().

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 6221 of file postgres_fdw.c.

6222 {
6223  ListCell *lc;
6224 
6225  foreach(lc, fpinfo->server->options)
6226  {
6227  DefElem *def = (DefElem *) lfirst(lc);
6228 
6229  if (strcmp(def->defname, "use_remote_estimate") == 0)
6230  fpinfo->use_remote_estimate = defGetBoolean(def);
6231  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
6232  (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
6233  NULL);
6234  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
6235  (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
6236  NULL);
6237  else if (strcmp(def->defname, "extensions") == 0)
6238  fpinfo->shippable_extensions =
6239  ExtractExtensionList(defGetString(def), false);
6240  else if (strcmp(def->defname, "fetch_size") == 0)
6241  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6242  else if (strcmp(def->defname, "async_capable") == 0)
6243  fpinfo->async_capable = defGetBoolean(def);
6244  }
6245 }
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:444
bool defGetBoolean(DefElem *def)
Definition: define.c:107
char * defGetString(DefElem *def)
Definition: define.c:48
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:2871
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:2961
char * defname
Definition: parsenodes.h:817
List * options
Definition: foreign.h:42
List * shippable_extensions
Definition: postgres_fdw.h:82

References PgFdwRelationInfo::async_capable, defGetBoolean(), defGetString(), DefElem::defname, ExtractExtensionList(), PgFdwRelationInfo::fdw_startup_cost, PgFdwRelationInfo::fdw_tuple_cost, PgFdwRelationInfo::fetch_size, lfirst, ForeignServer::options, parse_int(), parse_real(), PgFdwRelationInfo::server, PgFdwRelationInfo::shippable_extensions, and PgFdwRelationInfo::use_remote_estimate.

Referenced by postgresGetForeignRelSize().

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 6253 of file postgres_fdw.c.

6254 {
6255  ListCell *lc;
6256 
6257  foreach(lc, fpinfo->table->options)
6258  {
6259  DefElem *def = (DefElem *) lfirst(lc);
6260 
6261  if (strcmp(def->defname, "use_remote_estimate") == 0)
6262  fpinfo->use_remote_estimate = defGetBoolean(def);
6263  else if (strcmp(def->defname, "fetch_size") == 0)
6264  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6265  else if (strcmp(def->defname, "async_capable") == 0)
6266  fpinfo->async_capable = defGetBoolean(def);
6267  }
6268 }
List * options
Definition: foreign.h:57

References PgFdwRelationInfo::async_capable, defGetBoolean(), defGetString(), DefElem::defname, PgFdwRelationInfo::fetch_size, lfirst, ForeignTable::options, parse_int(), PgFdwRelationInfo::table, and PgFdwRelationInfo::use_remote_estimate.

Referenced by postgresGetForeignRelSize().

◆ build_remote_returning()

static List * build_remote_returning ( Index  rtindex,
Relation  rel,
List returningList 
)
static

Definition at line 4430 of file postgres_fdw.c.

4431 {
4432  bool have_wholerow = false;
4433  List *tlist = NIL;
4434  List *vars;
4435  ListCell *lc;
4436 
4437  Assert(returningList);
4438 
4439  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4440 
4441  /*
4442  * If there's a whole-row reference to the target relation, then we'll
4443  * need all the columns of the relation.
4444  */
4445  foreach(lc, vars)
4446  {
4447  Var *var = (Var *) lfirst(lc);
4448 
4449  if (IsA(var, Var) &&
4450  var->varno == rtindex &&
4451  var->varattno == InvalidAttrNumber)
4452  {
4453  have_wholerow = true;
4454  break;
4455  }
4456  }
4457 
4458  if (have_wholerow)
4459  {
4460  TupleDesc tupdesc = RelationGetDescr(rel);
4461  int i;
4462 
4463  for (i = 1; i <= tupdesc->natts; i++)
4464  {
4465  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4466  Var *var;
4467 
4468  /* Ignore dropped attributes. */
4469  if (attr->attisdropped)
4470  continue;
4471 
4472  var = makeVar(rtindex,
4473  i,
4474  attr->atttypid,
4475  attr->atttypmod,
4476  attr->attcollation,
4477  0);
4478 
4479  tlist = lappend(tlist,
4480  makeTargetEntry((Expr *) var,
4481  list_length(tlist) + 1,
4482  NULL,
4483  false));
4484  }
4485  }
4486 
4487  /* Now add any remaining columns to tlist. */
4488  foreach(lc, vars)
4489  {
4490  Var *var = (Var *) lfirst(lc);
4491 
4492  /*
4493  * No need for whole-row references to the target relation. We don't
4494  * need system columns other than ctid and oid either, since those are
4495  * set locally.
4496  */
4497  if (IsA(var, Var) &&
4498  var->varno == rtindex &&
4499  var->varattno <= InvalidAttrNumber &&
4501  continue; /* don't need it */
4502 
4503  if (tlist_member((Expr *) var, tlist))
4504  continue; /* already got it */
4505 
4506  tlist = lappend(tlist,
4507  makeTargetEntry((Expr *) var,
4508  list_length(tlist) + 1,
4509  NULL,
4510  false));
4511  }
4512 
4513  list_free(vars);
4514 
4515  return tlist;
4516 }
#define InvalidAttrNumber
Definition: attnum.h:23
List * lappend(List *list, void *datum)
Definition: list.c:339
void list_free(List *list)
Definition: list.c:1546
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:240
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
#define PVC_INCLUDE_PLACEHOLDERS
Definition: optimizer.h:191
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
Definition: primnodes.h:248
AttrNumber varattno
Definition: primnodes.h:260
int varno
Definition: primnodes.h:255
Definition: regcomp.c:282
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:79
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

References Assert, i, InvalidAttrNumber, IsA, lappend(), lfirst, list_free(), list_length(), makeTargetEntry(), makeVar(), TupleDescData::natts, NIL, pull_var_clause(), PVC_INCLUDE_PLACEHOLDERS, RelationGetDescr, SelfItemPointerAttributeNumber, tlist_member(), TupleDescAttr, Var::varattno, and Var::varno.

Referenced by postgresPlanDirectModify().

◆ close_cursor()

static void close_cursor ( PGconn conn,
unsigned int  cursor_number,
PgFdwConnState conn_state 
)
static

Definition at line 3952 of file postgres_fdw.c.

3954 {
3955  char sql[64];
3956  PGresult *res;
3957 
3958  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3959 
3960  /*
3961  * We don't use a PG_TRY block here, so be careful not to throw error
3962  * without releasing the PGresult.
3963  */
3964  res = pgfdw_exec_query(conn, sql, conn_state);
3966  pgfdw_report_error(ERROR, res, conn, true, sql);
3967  PQclear(res);
3968 }
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:885
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:849
static unsigned int cursor_number
Definition: connection.c:80
#define ERROR
Definition: elog.h:39
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3411
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:120
#define snprintf
Definition: port.h:238
PGconn * conn
Definition: streamutil.c:53

References conn, cursor_number, ERROR, pgfdw_exec_query(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), res, and snprintf.

Referenced by postgresAcquireSampleRowsFunc(), and postgresEndForeignScan().

◆ complete_pending_request()

static void complete_pending_request ( AsyncRequest areq)
static

Definition at line 7535 of file postgres_fdw.c.

7536 {
7537  /* The request would have been pending for a callback */
7538  Assert(areq->callback_pending);
7539 
7540  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7541  areq->callback_pending = false;
7542 
7543  /* We begin a fetch afterwards if necessary; don't fetch */
7544  produce_tuple_asynchronously(areq, false);
7545 
7546  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7547  ExecAsyncResponse(areq);
7548 
7549  /* Also, we do instrumentation ourselves, if required */
7550  if (areq->requestee->instrument)
7552  TupIsNull(areq->result) ? 0.0 : 1.0);
7553 }
void ExecAsyncResponse(AsyncRequest *areq)
Definition: execAsync.c:117
void InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
Definition: instrument.c:132
static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch)
TupleTableSlot * result
Definition: execnodes.h:616
bool callback_pending
Definition: execnodes.h:614
struct PlanState * requestee
Definition: execnodes.h:612
Instrumentation * instrument
Definition: execnodes.h:1137
#define TupIsNull(slot)
Definition: tuptable.h:306

References Assert, AsyncRequest::callback_pending, ExecAsyncResponse(), PlanState::instrument, InstrUpdateTupleCount(), produce_tuple_asynchronously(), AsyncRequest::requestee, AsyncRequest::result, and TupIsNull.

Referenced by postgresForeignAsyncConfigureWait().

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

Definition at line 7726 of file postgres_fdw.c.

7727 {
7729  Relation rel = errpos->rel;
7730  ForeignScanState *fsstate = errpos->fsstate;
7731  const char *attname = NULL;
7732  const char *relname = NULL;
7733  bool is_wholerow = false;
7734 
7735  /*
7736  * If we're in a scan node, always use aliases from the rangetable, for
7737  * consistency between the simple-relation and remote-join cases. Look at
7738  * the relation's tupdesc only if we're not in a scan node.
7739  */
7740  if (fsstate)
7741  {
7742  /* ForeignScan case */
7743  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7744  int varno = 0;
7745  AttrNumber colno = 0;
7746 
7747  if (fsplan->scan.scanrelid > 0)
7748  {
7749  /* error occurred in a scan against a foreign table */
7750  varno = fsplan->scan.scanrelid;
7751  colno = errpos->cur_attno;
7752  }
7753  else
7754  {
7755  /* error occurred in a scan against a foreign join */
7756  TargetEntry *tle;
7757 
7758  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7759  errpos->cur_attno - 1);
7760 
7761  /*
7762  * Target list can have Vars and expressions. For Vars, we can
7763  * get some information, however for expressions we can't. Thus
7764  * for expressions, just show generic context message.
7765  */
7766  if (IsA(tle->expr, Var))
7767  {
7768  Var *var = (Var *) tle->expr;
7769 
7770  varno = var->varno;
7771  colno = var->varattno;
7772  }
7773  }
7774 
7775  if (varno > 0)
7776  {
7777  EState *estate = fsstate->ss.ps.state;
7778  RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7779 
7780  relname = rte->eref->aliasname;
7781 
7782  if (colno == 0)
7783  is_wholerow = true;
7784  else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7785  attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7786  else if (colno == SelfItemPointerAttributeNumber)
7787  attname = "ctid";
7788  }
7789  }
7790  else if (rel)
7791  {
7792  /* Non-ForeignScan case (we should always have a rel here) */
7793  TupleDesc tupdesc = RelationGetDescr(rel);
7794 
7796  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7797  {
7798  Form_pg_attribute attr = TupleDescAttr(tupdesc,
7799  errpos->cur_attno - 1);
7800 
7801  attname = NameStr(attr->attname);
7802  }
7803  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7804  attname = "ctid";
7805  }
7806 
7807  if (relname && is_wholerow)
7808  errcontext("whole-row reference to foreign table \"%s\"", relname);
7809  else if (relname && attname)
7810  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7811  else
7812  errcontext("processing expression at position %d in select list",
7813  errpos->cur_attno);
7814 }
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:725
#define errcontext
Definition: elog.h:196
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:598
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
NameData attname
Definition: pg_attribute.h:41
void * arg
NameData relname
Definition: pg_class.h:38
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
#define RelationGetRelationName(relation)
Definition: rel.h:539
AttrNumber cur_attno
Definition: postgres_fdw.c:304
ForeignScanState * fsstate
Definition: postgres_fdw.c:306
ScanState ss
Definition: execnodes.h:2075
List * fdw_scan_tlist
Definition: plannodes.h:718
Plan * plan
Definition: execnodes.h:1127
EState * state
Definition: execnodes.h:1129
PlanState ps
Definition: execnodes.h:1574
Index scanrelid
Definition: plannodes.h:390
Expr * expr
Definition: primnodes.h:2190
#define strVal(v)
Definition: value.h:82

References arg, attname, castNode, ConversionLocation::cur_attno, errcontext, exec_rt_fetch(), TargetEntry::expr, ForeignScan::fdw_scan_tlist, ConversionLocation::fsstate, IsA, list_length(), list_nth(), list_nth_node, NameStr, TupleDescData::natts, PlanState::plan, ScanState::ps, ConversionLocation::rel, RelationGetDescr, RelationGetRelationName, relname, ForeignScan::scan, Scan::scanrelid, SelfItemPointerAttributeNumber, ForeignScanState::ss, PlanState::state, strVal, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by make_tuple_from_result_row().

◆ convert_prep_stmt_params()

static const char ** convert_prep_stmt_params ( PgFdwModifyState fmstate,
ItemPointer  tupleid,
TupleTableSlot **  slots,
int  numSlots 
)
static

Definition at line 4271 of file postgres_fdw.c.

4275 {
4276  const char **p_values;
4277  int i;
4278  int j;
4279  int pindex = 0;
4280  MemoryContext oldcontext;
4281 
4282  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4283 
4284  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4285 
4286  /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4287  Assert(!(tupleid != NULL && numSlots > 1));
4288 
4289  /* 1st parameter should be ctid, if it's in use */
4290  if (tupleid != NULL)
4291  {
4292  Assert(numSlots == 1);
4293  /* don't need set_transmission_modes for TID output */
4294  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
4295  PointerGetDatum(tupleid));
4296  pindex++;
4297  }
4298 
4299  /* get following parameters from slots */
4300  if (slots != NULL && fmstate->target_attrs != NIL)
4301  {
4302  TupleDesc tupdesc = RelationGetDescr(fmstate->rel);
4303  int nestlevel;
4304  ListCell *lc;
4305 
4306  nestlevel = set_transmission_modes();
4307 
4308  for (i = 0; i < numSlots; i++)
4309  {
4310  j = (tupleid != NULL) ? 1 : 0;
4311  foreach(lc, fmstate->target_attrs)
4312  {
4313  int attnum = lfirst_int(lc);
4314  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4315  Datum value;
4316  bool isnull;
4317 
4318  /* Ignore generated columns; they are set to DEFAULT */
4319  if (attr->attgenerated)
4320  continue;
4321  value = slot_getattr(slots[i], attnum, &isnull);
4322  if (isnull)
4323  p_values[pindex] = NULL;
4324  else
4325  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[j],
4326  value);
4327  pindex++;
4328  j++;
4329  }
4330  }
4331 
4332  reset_transmission_modes(nestlevel);
4333  }
4334 
4335  Assert(pindex == fmstate->p_nums * numSlots);
4336 
4337  MemoryContextSwitchTo(oldcontext);
4338 
4339  return p_values;
4340 }
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
static struct @160 value
void * palloc(Size size)
Definition: mcxt.c:1317
int16 attnum
Definition: pg_attribute.h:74
#define lfirst_int(lc)
Definition: pg_list.h:173
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
void reset_transmission_modes(int nestlevel)
int set_transmission_modes(void)
MemoryContext temp_cxt
Definition: postgres_fdw.c:206
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:200
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:395

References Assert, attnum, i, j, lfirst_int, MemoryContextSwitchTo(), NIL, OutputFunctionCall(), PgFdwModifyState::p_flinfo, PgFdwModifyState::p_nums, palloc(), PointerGetDatum(), PgFdwModifyState::rel, RelationGetDescr, reset_transmission_modes(), set_transmission_modes(), slot_getattr(), PgFdwModifyState::target_attrs, PgFdwModifyState::temp_cxt, TupleDescAttr, and value.

Referenced by execute_foreign_modify().

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

Definition at line 3728 of file postgres_fdw.c.

3729 {
3730  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3731  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3732  int numParams = fsstate->numParams;
3733  const char **values = fsstate->param_values;
3734  PGconn *conn = fsstate->conn;
3736  PGresult *res;
3737 
3738  /* First, process a pending asynchronous request, if any. */
3739  if (fsstate->conn_state->pendingAreq)
3741 
3742  /*
3743  * Construct array of query parameter values in text format. We do the
3744  * conversions in the short-lived per-tuple context, so as not to cause a
3745  * memory leak over repeated scans.
3746  */
3747  if (numParams > 0)
3748  {
3749  MemoryContext oldcontext;
3750 
3751  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3752 
3753  process_query_params(econtext,
3754  fsstate->param_flinfo,
3755  fsstate->param_exprs,
3756  values);
3757 
3758  MemoryContextSwitchTo(oldcontext);
3759  }
3760 
3761  /* Construct the DECLARE CURSOR command */
3762  initStringInfo(&buf);
3763  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3764  fsstate->cursor_number, fsstate->query);
3765 
3766  /*
3767  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3768  * to infer types for all parameters. Since we explicitly cast every
3769  * parameter (see deparse.c), the "inference" is trivial and will produce
3770  * the desired result. This allows us to avoid assuming that the remote
3771  * server has the same OIDs we do for the parameters' types.
3772  */
3773  if (!PQsendQueryParams(conn, buf.data, numParams,
3774  NULL, values, NULL, NULL, 0))
3775  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3776 
3777  /*
3778  * Get the result, and check for success.
3779  *
3780  * We don't use a PG_TRY block here, so be careful not to throw error
3781  * without releasing the PGresult.
3782  */
3785  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3786  PQclear(res);
3787 
3788  /* Mark the cursor as created, and show no tuples have been retrieved */
3789  fsstate->cursor_exists = true;
3790  fsstate->tuples = NULL;
3791  fsstate->num_tuples = 0;
3792  fsstate->next_tuple = 0;
3793  fsstate->fetch_ct_2 = 0;
3794  fsstate->eof_reached = false;
3795 
3796  /* Clean up */
3797  pfree(buf.data);
3798 }
PGresult * pgfdw_get_result(PGconn *conn)
Definition: connection.c:866
int PQsendQueryParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition: fe-exec.c:1492
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
void pfree(void *pointer)
Definition: mcxt.c:1521
static char * buf
Definition: pg_test_fsync.c:72
void process_pending_request(AsyncRequest *areq)
static void process_query_params(ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:94
void initStringInfo(StringInfo str)
Definition: stringinfo.c:56
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:139
FmgrInfo * param_flinfo
Definition: postgres_fdw.c:152
const char ** param_values
Definition: postgres_fdw.c:154
unsigned int cursor_number
Definition: postgres_fdw.c:149
List * param_exprs
Definition: postgres_fdw.c:153
PgFdwConnState * conn_state
Definition: postgres_fdw.c:148
HeapTuple * tuples
Definition: postgres_fdw.c:157
ExprContext * ps_ExprContext
Definition: execnodes.h:1166

References appendStringInfo(), buf, PgFdwScanState::conn, conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, ERROR, ForeignScanState::fdw_state, PgFdwScanState::fetch_ct_2, if(), initStringInfo(), MemoryContextSwitchTo(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, PgFdwScanState::numParams, PgFdwScanState::param_exprs, PgFdwScanState::param_flinfo, PgFdwScanState::param_values, PgFdwConnState::pendingAreq, pfree(), pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), PQsendQueryParams(), process_pending_request(), process_query_params(), ScanState::ps, PlanState::ps_ExprContext, PgFdwScanState::query, res, ForeignScanState::ss, PgFdwScanState::tuples, and values.

Referenced by fetch_more_data_begin(), and postgresIterateForeignScan().

◆ create_foreign_modify()

static PgFdwModifyState * create_foreign_modify ( EState estate,
RangeTblEntry rte,
ResultRelInfo resultRelInfo,
CmdType  operation,
Plan subplan,
char *  query,
List target_attrs,
int  values_end,
bool  has_returning,
List retrieved_attrs 
)
static

Definition at line 3976 of file postgres_fdw.c.

3986 {
3987  PgFdwModifyState *fmstate;
3988  Relation rel = resultRelInfo->ri_RelationDesc;
3989  TupleDesc tupdesc = RelationGetDescr(rel);
3990  Oid userid;
3991  ForeignTable *table;
3992  UserMapping *user;
3993  AttrNumber n_params;
3994  Oid typefnoid;
3995  bool isvarlena;
3996  ListCell *lc;
3997 
3998  /* Begin constructing PgFdwModifyState. */
3999  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
4000  fmstate->rel = rel;
4001 
4002  /* Identify which user to do the remote access as. */
4003  userid = ExecGetResultRelCheckAsUser(resultRelInfo, estate);
4004 
4005  /* Get info about foreign table. */
4006  table = GetForeignTable(RelationGetRelid(rel));
4007  user = GetUserMapping(userid, table->serverid);
4008 
4009  /* Open connection; report that we'll create a prepared statement. */
4010  fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
4011  fmstate->p_name = NULL; /* prepared statement not made yet */
4012 
4013  /* Set up remote query information. */
4014  fmstate->query = query;
4015  if (operation == CMD_INSERT)
4016  {
4017  fmstate->query = pstrdup(fmstate->query);
4018  fmstate->orig_query = pstrdup(fmstate->query);
4019  }
4020  fmstate->target_attrs = target_attrs;
4021  fmstate->values_end = values_end;
4022  fmstate->has_returning = has_returning;
4023  fmstate->retrieved_attrs = retrieved_attrs;
4024 
4025  /* Create context for per-tuple temp workspace. */
4026  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
4027  "postgres_fdw temporary data",
4029 
4030  /* Prepare for input conversion of RETURNING results. */
4031  if (fmstate->has_returning)
4032  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
4033 
4034  /* Prepare for output conversion of parameters used in prepared stmt. */
4035  n_params = list_length(fmstate->target_attrs) + 1;
4036  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
4037  fmstate->p_nums = 0;
4038 
4039  if (operation == CMD_UPDATE || operation == CMD_DELETE)
4040  {
4041  Assert(subplan != NULL);
4042 
4043  /* Find the ctid resjunk column in the subplan's result */
4045  "ctid");
4046  if (!AttributeNumberIsValid(fmstate->ctidAttno))
4047  elog(ERROR, "could not find junk ctid column");
4048 
4049  /* First transmittable parameter will be ctid */
4050  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
4051  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4052  fmstate->p_nums++;
4053  }
4054 
4055  if (operation == CMD_INSERT || operation == CMD_UPDATE)
4056  {
4057  /* Set up for remaining transmittable parameters */
4058  foreach(lc, fmstate->target_attrs)
4059  {
4060  int attnum = lfirst_int(lc);
4061  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4062 
4063  Assert(!attr->attisdropped);
4064 
4065  /* Ignore generated columns; they are set to DEFAULT */
4066  if (attr->attgenerated)
4067  continue;
4068  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
4069  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4070  fmstate->p_nums++;
4071  }
4072  }
4073 
4074  Assert(fmstate->p_nums <= n_params);
4075 
4076  /* Set batch_size from foreign server/table options. */
4077  if (operation == CMD_INSERT)
4078  fmstate->batch_size = get_batch_size_option(rel);
4079 
4080  fmstate->num_slots = 1;
4081 
4082  /* Initialize auxiliary state */
4083  fmstate->aux_fmstate = NULL;
4084 
4085  return fmstate;
4086 }
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
Definition: connection.c:191
#define elog(elevel,...)
Definition: elog.h:225
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2173
Oid ExecGetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate)
Definition: execUtils.c:1397
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:254
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:200
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2907
char * pstrdup(const char *in)
Definition: mcxt.c:1696
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
static char * user
Definition: pg_regress.c:119
unsigned int Oid
Definition: postgres_ext.h:31
static int get_batch_size_option(Relation rel)
#define RelationGetRelid(relation)
Definition: rel.h:505
MemoryContext es_query_cxt
Definition: execnodes.h:675
Definition: fmgr.h:57
Oid serverid
Definition: foreign.h:56
AttInMetadata * attinmeta
Definition: postgres_fdw.c:181
PgFdwConnState * conn_state
Definition: postgres_fdw.c:185
AttrNumber ctidAttno
Definition: postgres_fdw.c:198
struct PgFdwModifyState * aux_fmstate
Definition: postgres_fdw.c:209
List * retrieved_attrs
Definition: postgres_fdw.c:195
List * targetlist
Definition: plannodes.h:153
Relation ri_RelationDesc
Definition: execnodes.h:459

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, Assert, PgFdwModifyState::attinmeta, attnum, AttributeNumberIsValid, PgFdwModifyState::aux_fmstate, PgFdwModifyState::batch_size, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, PgFdwModifyState::conn_state, PgFdwModifyState::ctidAttno, elog, ERROR, EState::es_query_cxt, ExecFindJunkAttributeInTlist(), ExecGetResultRelCheckAsUser(), fmgr_info(), get_batch_size_option(), GetConnection(), GetForeignTable(), getTypeOutputInfo(), GetUserMapping(), PgFdwModifyState::has_returning, lfirst_int, list_length(), PgFdwModifyState::num_slots, PgFdwModifyState::orig_query, PgFdwModifyState::p_flinfo, PgFdwModifyState::p_name, PgFdwModifyState::p_nums, palloc0(), pstrdup(), PgFdwModifyState::query, PgFdwModifyState::rel, RelationGetDescr, RelationGetRelid, PgFdwModifyState::retrieved_attrs, ResultRelInfo::ri_RelationDesc, ForeignTable::serverid, PgFdwModifyState::target_attrs, Plan::targetlist, PgFdwModifyState::temp_cxt, TupleDescAttr, TupleDescGetAttInMetadata(), user, and PgFdwModifyState::values_end.

Referenced by postgresBeginForeignInsert(), and postgresBeginForeignModify().

◆ deallocate_query()

static void deallocate_query ( PgFdwModifyState fmstate)
static

Definition at line 4401 of file postgres_fdw.c.

4402 {
4403  char sql[64];
4404  PGresult *res;
4405 
4406  /* do nothing if the query is not allocated */
4407  if (!fmstate->p_name)
4408  return;
4409 
4410  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4411 
4412  /*
4413  * We don't use a PG_TRY block here, so be careful not to throw error
4414  * without releasing the PGresult.
4415  */
4416  res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4418  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
4419  PQclear(res);
4420  pfree(fmstate->p_name);
4421  fmstate->p_name = NULL;
4422 }

References PgFdwModifyState::conn, PgFdwModifyState::conn_state, ERROR, PgFdwModifyState::p_name, pfree(), pgfdw_exec_query(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), res, and snprintf.

Referenced by execute_foreign_modify(), and finish_foreign_modify().

◆ ec_member_matches_foreign()

static bool ec_member_matches_foreign ( PlannerInfo root,
RelOptInfo rel,
EquivalenceClass ec,
EquivalenceMember em,
void *  arg 
)
static

Definition at line 3699 of file postgres_fdw.c.

3702 {
3704  Expr *expr = em->em_expr;
3705 
3706  /*
3707  * If we've identified what we're processing in the current scan, we only
3708  * want to match that expression.
3709  */
3710  if (state->current != NULL)
3711  return equal(expr, state->current);
3712 
3713  /*
3714  * Otherwise, ignore anything we've already processed.
3715  */
3716  if (list_member(state->already_used, expr))
3717  return false;
3718 
3719  /* This is the new target to process. */
3720  state->current = expr;
3721  return true;
3722 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
bool list_member(const List *list, const void *datum)
Definition: list.c:661
Definition: regguts.h:323

References arg, EquivalenceMember::em_expr, equal(), and list_member().

Referenced by postgresGetForeignPaths().

◆ estimate_path_cost_size()

static void estimate_path_cost_size ( PlannerInfo root,
RelOptInfo foreignrel,
List param_join_conds,
List pathkeys,
PgFdwPathExtraData fpextra,
double *  p_rows,
int *  p_width,
int *  p_disabled_nodes,
Cost p_startup_cost,
Cost p_total_cost 
)
static

Definition at line 3089 of file postgres_fdw.c.

3097 {
3098  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
3099  double rows;
3100  double retrieved_rows;
3101  int width;
3102  int disabled_nodes = 0;
3103  Cost startup_cost;
3104  Cost total_cost;
3105 
3106  /* Make sure the core code has set up the relation's reltarget */
3107  Assert(foreignrel->reltarget);
3108 
3109  /*
3110  * If the table or the server is configured to use remote estimates,
3111  * connect to the foreign server and execute EXPLAIN to estimate the
3112  * number of rows selected by the restriction+join clauses. Otherwise,
3113  * estimate rows using whatever statistics we have locally, in a way
3114  * similar to ordinary tables.
3115  */
3116  if (fpinfo->use_remote_estimate)
3117  {
3118  List *remote_param_join_conds;
3119  List *local_param_join_conds;
3120  StringInfoData sql;
3121  PGconn *conn;
3122  Selectivity local_sel;
3123  QualCost local_cost;
3124  List *fdw_scan_tlist = NIL;
3125  List *remote_conds;
3126 
3127  /* Required only to be passed to deparseSelectStmtForRel */
3128  List *retrieved_attrs;
3129 
3130  /*
3131  * param_join_conds might contain both clauses that are safe to send
3132  * across, and clauses that aren't.
3133  */
3134  classifyConditions(root, foreignrel, param_join_conds,
3135  &remote_param_join_conds, &local_param_join_conds);
3136 
3137  /* Build the list of columns to be fetched from the foreign server. */
3138  if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
3139  fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
3140  else
3141  fdw_scan_tlist = NIL;
3142 
3143  /*
3144  * The complete list of remote conditions includes everything from
3145  * baserestrictinfo plus any extra join_conds relevant to this
3146  * particular path.
3147  */
3148  remote_conds = list_concat(remote_param_join_conds,
3149  fpinfo->remote_conds);
3150 
3151  /*
3152  * Construct EXPLAIN query including the desired SELECT, FROM, and
3153  * WHERE clauses. Params and other-relation Vars are replaced by dummy
3154  * values, so don't request params_list.
3155  */
3156  initStringInfo(&sql);
3157  appendStringInfoString(&sql, "EXPLAIN ");
3158  deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
3159  remote_conds, pathkeys,
3160  fpextra ? fpextra->has_final_sort : false,
3161  fpextra ? fpextra->has_limit : false,
3162  false, &retrieved_attrs, NULL);
3163 
3164  /* Get the remote estimate */
3165  conn = GetConnection(fpinfo->user, false, NULL);
3166  get_remote_estimate(sql.data, conn, &rows, &width,
3167  &startup_cost, &total_cost);
3169 
3170  retrieved_rows = rows;
3171 
3172  /* Factor in the selectivity of the locally-checked quals */
3173  local_sel = clauselist_selectivity(root,
3174  local_param_join_conds,
3175  foreignrel->relid,
3176  JOIN_INNER,
3177  NULL);
3178  local_sel *= fpinfo->local_conds_sel;
3179 
3180  rows = clamp_row_est(rows * local_sel);
3181 
3182  /* Add in the eval cost of the locally-checked quals */
3183  startup_cost += fpinfo->local_conds_cost.startup;
3184  total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3185  cost_qual_eval(&local_cost, local_param_join_conds, root);
3186  startup_cost += local_cost.startup;
3187  total_cost += local_cost.per_tuple * retrieved_rows;
3188 
3189  /*
3190  * Add in tlist eval cost for each output row. In case of an
3191  * aggregate, some of the tlist expressions such as grouping
3192  * expressions will be evaluated remotely, so adjust the costs.
3193  */
3194  startup_cost += foreignrel->reltarget->cost.startup;
3195  total_cost += foreignrel->reltarget->cost.startup;
3196  total_cost += foreignrel->reltarget->cost.per_tuple * rows;
3197  if (IS_UPPER_REL(foreignrel))
3198  {
3199  QualCost tlist_cost;
3200 
3201  cost_qual_eval(&tlist_cost, fdw_scan_tlist, root);
3202  startup_cost -= tlist_cost.startup;
3203  total_cost -= tlist_cost.startup;
3204  total_cost -= tlist_cost.per_tuple * rows;
3205  }
3206  }
3207  else
3208  {
3209  Cost run_cost = 0;
3210 
3211  /*
3212  * We don't support join conditions in this mode (hence, no
3213  * parameterized paths can be made).
3214  */
3215  Assert(param_join_conds == NIL);
3216 
3217  /*
3218  * We will come here again and again with different set of pathkeys or
3219  * additional post-scan/join-processing steps that caller wants to
3220  * cost. We don't need to calculate the cost/size estimates for the
3221  * underlying scan, join, or grouping each time. Instead, use those
3222  * estimates if we have cached them already.
3223  */
3224  if (fpinfo->rel_startup_cost >= 0 && fpinfo->rel_total_cost >= 0)
3225  {
3226  Assert(fpinfo->retrieved_rows >= 0);
3227 
3228  rows = fpinfo->rows;
3229  retrieved_rows = fpinfo->retrieved_rows;
3230  width = fpinfo->width;
3231  startup_cost = fpinfo->rel_startup_cost;
3232  run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
3233 
3234  /*
3235  * If we estimate the costs of a foreign scan or a foreign join
3236  * with additional post-scan/join-processing steps, the scan or
3237  * join costs obtained from the cache wouldn't yet contain the
3238  * eval costs for the final scan/join target, which would've been
3239  * updated by apply_scanjoin_target_to_paths(); add the eval costs
3240  * now.
3241  */
3242  if (fpextra && !IS_UPPER_REL(foreignrel))
3243  {
3244  /* Shouldn't get here unless we have LIMIT */
3245  Assert(fpextra->has_limit);
3246  Assert(foreignrel->reloptkind == RELOPT_BASEREL ||
3247  foreignrel->reloptkind == RELOPT_JOINREL);
3248  startup_cost += foreignrel->reltarget->cost.startup;
3249  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3250  }
3251  }
3252  else if (IS_JOIN_REL(foreignrel))
3253  {
3254  PgFdwRelationInfo *fpinfo_i;
3255  PgFdwRelationInfo *fpinfo_o;
3256  QualCost join_cost;
3257  QualCost remote_conds_cost;
3258  double nrows;
3259 
3260  /* Use rows/width estimates made by the core code. */
3261  rows = foreignrel->rows;
3262  width = foreignrel->reltarget->width;
3263 
3264  /* For join we expect inner and outer relations set */
3265  Assert(fpinfo->innerrel && fpinfo->outerrel);
3266 
3267  fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
3268  fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
3269 
3270  /* Estimate of number of rows in cross product */
3271  nrows = fpinfo_i->rows * fpinfo_o->rows;
3272 
3273  /*
3274  * Back into an estimate of the number of retrieved rows. Just in
3275  * case this is nuts, clamp to at most nrows.
3276  */
3277  retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3278  retrieved_rows = Min(retrieved_rows, nrows);
3279 
3280  /*
3281  * The cost of foreign join is estimated as cost of generating
3282  * rows for the joining relations + cost for applying quals on the
3283  * rows.
3284  */
3285 
3286  /*
3287  * Calculate the cost of clauses pushed down to the foreign server
3288  */
3289  cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
3290  /* Calculate the cost of applying join clauses */
3291  cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
3292 
3293  /*
3294  * Startup cost includes startup cost of joining relations and the
3295  * startup cost for join and other clauses. We do not include the
3296  * startup cost specific to join strategy (e.g. setting up hash
3297  * tables) since we do not know what strategy the foreign server
3298  * is going to use.
3299  */
3300  startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
3301  startup_cost += join_cost.startup;
3302  startup_cost += remote_conds_cost.startup;
3303  startup_cost += fpinfo->local_conds_cost.startup;
3304 
3305  /*
3306  * Run time cost includes:
3307  *
3308  * 1. Run time cost (total_cost - startup_cost) of relations being
3309  * joined
3310  *
3311  * 2. Run time cost of applying join clauses on the cross product
3312  * of the joining relations.
3313  *
3314  * 3. Run time cost of applying pushed down other clauses on the
3315  * result of join
3316  *
3317  * 4. Run time cost of applying nonpushable other clauses locally
3318  * on the result fetched from the foreign server.
3319  */
3320  run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
3321  run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
3322  run_cost += nrows * join_cost.per_tuple;
3323  nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
3324  run_cost += nrows * remote_conds_cost.per_tuple;
3325  run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3326 
3327  /* Add in tlist eval cost for each output row */
3328  startup_cost += foreignrel->reltarget->cost.startup;
3329  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3330  }
3331  else if (IS_UPPER_REL(foreignrel))
3332  {
3333  RelOptInfo *outerrel = fpinfo->outerrel;
3334  PgFdwRelationInfo *ofpinfo;
3335  AggClauseCosts aggcosts;
3336  double input_rows;
3337  int numGroupCols;
3338  double numGroups = 1;
3339 
3340  /* The upper relation should have its outer relation set */
3341  Assert(outerrel);
3342  /* and that outer relation should have its reltarget set */
3343  Assert(outerrel->reltarget);
3344 
3345  /*
3346  * This cost model is mixture of costing done for sorted and
3347  * hashed aggregates in cost_agg(). We are not sure which
3348  * strategy will be considered at remote side, thus for
3349  * simplicity, we put all startup related costs in startup_cost
3350  * and all finalization and run cost are added in total_cost.
3351  */
3352 
3353  ofpinfo = (PgFdwRelationInfo *) outerrel->fdw_private;
3354 
3355  /* Get rows from input rel */
3356  input_rows = ofpinfo->rows;
3357 
3358  /* Collect statistics about aggregates for estimating costs. */
3359  MemSet(&aggcosts, 0, sizeof(AggClauseCosts));
3360  if (root->parse->hasAggs)
3361  {
3363  }
3364 
3365  /* Get number of grouping columns and possible number of groups */
3366  numGroupCols = list_length(root->processed_groupClause);
3367  numGroups = estimate_num_groups(root,
3368  get_sortgrouplist_exprs(root->processed_groupClause,
3369  fpinfo->grouped_tlist),
3370  input_rows, NULL, NULL);
3371 
3372  /*
3373  * Get the retrieved_rows and rows estimates. If there are HAVING
3374  * quals, account for their selectivity.
3375  */
3376  if (root->hasHavingQual)
3377  {
3378  /* Factor in the selectivity of the remotely-checked quals */
3379  retrieved_rows =
3380  clamp_row_est(numGroups *
3382  fpinfo->remote_conds,
3383  0,
3384  JOIN_INNER,
3385  NULL));
3386  /* Factor in the selectivity of the locally-checked quals */
3387  rows = clamp_row_est(retrieved_rows * fpinfo->local_conds_sel);
3388  }
3389  else
3390  {
3391  rows = retrieved_rows = numGroups;
3392  }
3393 
3394  /* Use width estimate made by the core code. */
3395  width = foreignrel->reltarget->width;
3396 
3397  /*-----
3398  * Startup cost includes:
3399  * 1. Startup cost for underneath input relation, adjusted for
3400  * tlist replacement by apply_scanjoin_target_to_paths()
3401  * 2. Cost of performing aggregation, per cost_agg()
3402  *-----
3403  */
3404  startup_cost = ofpinfo->rel_startup_cost;
3405  startup_cost += outerrel->reltarget->cost.startup;
3406  startup_cost += aggcosts.transCost.startup;
3407  startup_cost += aggcosts.transCost.per_tuple * input_rows;
3408  startup_cost += aggcosts.finalCost.startup;
3409  startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
3410 
3411  /*-----
3412  * Run time cost includes:
3413  * 1. Run time cost of underneath input relation, adjusted for
3414  * tlist replacement by apply_scanjoin_target_to_paths()
3415  * 2. Run time cost of performing aggregation, per cost_agg()
3416  *-----
3417  */
3418  run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
3419  run_cost += outerrel->reltarget->cost.per_tuple * input_rows;
3420  run_cost += aggcosts.finalCost.per_tuple * numGroups;
3421  run_cost += cpu_tuple_cost * numGroups;
3422 
3423  /* Account for the eval cost of HAVING quals, if any */
3424  if (root->hasHavingQual)
3425  {
3426  QualCost remote_cost;
3427 
3428  /* Add in the eval cost of the remotely-checked quals */
3429  cost_qual_eval(&remote_cost, fpinfo->remote_conds, root);
3430  startup_cost += remote_cost.startup;
3431  run_cost += remote_cost.per_tuple * numGroups;
3432  /* Add in the eval cost of the locally-checked quals */
3433  startup_cost += fpinfo->local_conds_cost.startup;
3434  run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3435  }
3436 
3437  /* Add in tlist eval cost for each output row */
3438  startup_cost += foreignrel->reltarget->cost.startup;
3439  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3440  }
3441  else
3442  {
3443  Cost cpu_per_tuple;
3444 
3445  /* Use rows/width estimates made by set_baserel_size_estimates. */
3446  rows = foreignrel->rows;
3447  width = foreignrel->reltarget->width;
3448 
3449  /*
3450  * Back into an estimate of the number of retrieved rows. Just in
3451  * case this is nuts, clamp to at most foreignrel->tuples.
3452  */
3453  retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3454  retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
3455 
3456  /*
3457  * Cost as though this were a seqscan, which is pessimistic. We
3458  * effectively imagine the local_conds are being evaluated
3459  * remotely, too.
3460  */
3461  startup_cost = 0;
3462  run_cost = 0;
3463  run_cost += seq_page_cost * foreignrel->pages;
3464 
3465  startup_cost += foreignrel->baserestrictcost.startup;
3466  cpu_per_tuple = cpu_tuple_cost + foreignrel->baserestrictcost.per_tuple;
3467  run_cost += cpu_per_tuple * foreignrel->tuples;
3468 
3469  /* Add in tlist eval cost for each output row */
3470  startup_cost += foreignrel->reltarget->cost.startup;
3471  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3472  }
3473 
3474  /*
3475  * Without remote estimates, we have no real way to estimate the cost
3476  * of generating sorted output. It could be free if the query plan
3477  * the remote side would have chosen generates properly-sorted output
3478  * anyway, but in most cases it will cost something. Estimate a value
3479  * high enough that we won't pick the sorted path when the ordering
3480  * isn't locally useful, but low enough that we'll err on the side of
3481  * pushing down the ORDER BY clause when it's useful to do so.
3482  */
3483  if (pathkeys != NIL)
3484  {
3485  if (IS_UPPER_REL(foreignrel))
3486  {
3487  Assert(foreignrel->reloptkind == RELOPT_UPPER_REL &&
3488  fpinfo->stage == UPPERREL_GROUP_AGG);
3490  retrieved_rows, width,
3491  fpextra->limit_tuples,
3492  &disabled_nodes,
3493  &startup_cost, &run_cost);
3494  }
3495  else
3496  {
3497  startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3498  run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3499  }
3500  }
3501 
3502  total_cost = startup_cost + run_cost;
3503 
3504  /* Adjust the cost estimates if we have LIMIT */
3505  if (fpextra && fpextra->has_limit)
3506  {
3507  adjust_limit_rows_costs(&rows, &startup_cost, &total_cost,
3508  fpextra->offset_est, fpextra->count_est);
3509  retrieved_rows = rows;
3510  }
3511  }
3512 
3513  /*
3514  * If this includes the final sort step, the given target, which will be
3515  * applied to the resulting path, might have different expressions from
3516  * the foreignrel's reltarget (see make_sort_input_target()); adjust tlist
3517  * eval costs.
3518  */
3519  if (fpextra && fpextra->has_final_sort &&
3520  fpextra->target != foreignrel->reltarget)
3521  {
3522  QualCost oldcost = foreignrel->reltarget->cost;
3523  QualCost newcost = fpextra->target->cost;
3524 
3525  startup_cost += newcost.startup - oldcost.startup;
3526  total_cost += newcost.startup - oldcost.startup;
3527  total_cost += (newcost.per_tuple - oldcost.per_tuple) * rows;
3528  }
3529 
3530  /*
3531  * Cache the retrieved rows and cost estimates for scans, joins, or
3532  * groupings without any parameterization, pathkeys, or additional
3533  * post-scan/join-processing steps, before adding the costs for
3534  * transferring data from the foreign server. These estimates are useful
3535  * for costing remote joins involving this relation or costing other
3536  * remote operations on this relation such as remote sorts and remote
3537  * LIMIT restrictions, when the costs can not be obtained from the foreign
3538  * server. This function will be called at least once for every foreign
3539  * relation without any parameterization, pathkeys, or additional
3540  * post-scan/join-processing steps.
3541  */
3542  if (pathkeys == NIL && param_join_conds == NIL && fpextra == NULL)
3543  {
3544  fpinfo->retrieved_rows = retrieved_rows;
3545  fpinfo->rel_startup_cost = startup_cost;
3546  fpinfo->rel_total_cost = total_cost;
3547  }
3548 
3549  /*
3550  * Add some additional cost factors to account for connection overhead
3551  * (fdw_startup_cost), transferring data across the network
3552  * (fdw_tuple_cost per retrieved row), and local manipulation of the data
3553  * (cpu_tuple_cost per retrieved row).
3554  */
3555  startup_cost += fpinfo->fdw_startup_cost;
3556  total_cost += fpinfo->fdw_startup_cost;
3557  total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
3558  total_cost += cpu_tuple_cost * retrieved_rows;
3559 
3560  /*
3561  * If we have LIMIT, we should prefer performing the restriction remotely
3562  * rather than locally, as the former avoids extra row fetches from the
3563  * remote that the latter might cause. But since the core code doesn't
3564  * account for such fetches when estimating the costs of the local
3565  * restriction (see create_limit_path()), there would be no difference
3566  * between the costs of the local restriction and the costs of the remote
3567  * restriction estimated above if we don't use remote estimates (except
3568  * for the case where the foreignrel is a grouping relation, the given
3569  * pathkeys is not NIL, and the effects of a bounded sort for that rel is
3570  * accounted for in costing the remote restriction). Tweak the costs of
3571  * the remote restriction to ensure we'll prefer it if LIMIT is a useful
3572  * one.
3573  */
3574  if (!fpinfo->use_remote_estimate &&
3575  fpextra && fpextra->has_limit &&
3576  fpextra->limit_tuples > 0 &&
3577  fpextra->limit_tuples < fpinfo->rows)
3578  {
3579  Assert(fpinfo->rows > 0);
3580  total_cost -= (total_cost - startup_cost) * 0.05 *
3581  (fpinfo->rows - fpextra->limit_tuples) / fpinfo->rows;
3582  }
3583 
3584  /* Return results. */
3585  *p_rows = rows;
3586  *p_width = width;
3587  *p_disabled_nodes = disabled_nodes;
3588  *p_startup_cost = startup_cost;
3589  *p_total_cost = total_cost;
3590 }
#define Min(x, y)
Definition: c.h:983
#define MemSet(start, val, len)
Definition: c.h:999
void ReleaseConnection(PGconn *conn)
Definition: connection.c:799
double cpu_operator_cost
Definition: costsize.c:134
double cpu_tuple_cost
Definition: costsize.c:132
double seq_page_cost
Definition: costsize.c:130
double clamp_row_est(double nrows)
Definition: costsize.c:213
void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, bool has_final_sort, bool has_limit, bool is_subquery, List **retrieved_attrs, List **params_list)
Definition: deparse.c:1231
List * build_tlist_to_deparse(RelOptInfo *foreignrel)
Definition: deparse.c:1174
void classifyConditions(PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds, List **local_conds)
Definition: deparse.c:216
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
double Selectivity
Definition: nodes.h:250
@ AGGSPLIT_SIMPLE
Definition: nodes.h:377
void adjust_limit_rows_costs(double *rows, Cost *startup_cost, Cost *total_cost, int64 offset_est, int64 count_est)
Definition: pathnode.c:3978
#define IS_JOIN_REL(rel)
Definition: pathnodes.h:844
#define IS_UPPER_REL(rel)
Definition: pathnodes.h:849
static void adjust_foreign_grouping_path_cost(PlannerInfo *root, List *pathkeys, double retrieved_rows, double width, double limit_tuples, int *disabled_nodes, Cost *p_startup_cost, Cost *p_run_cost)
static void get_remote_estimate(const char *sql, PGconn *conn, double *rows, int *width, Cost *startup_cost, Cost *total_cost)
void get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
Definition: prepagg.c:559
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset, EstimationInfo *estinfo)
Definition: selfuncs.c:3420
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:179
QualCost finalCost
Definition: pathnodes.h:61
QualCost transCost
Definition: pathnodes.h:60
QualCost cost
Definition: pathnodes.h:1548
Selectivity joinclause_sel
Definition: postgres_fdw.h:60
RelOptInfo * innerrel
Definition: postgres_fdw.h:104
Cost per_tuple
Definition: pathnodes.h:48
Cost startup
Definition: pathnodes.h:47
Index relid
Definition: pathnodes.h:918
Cardinality tuples
Definition: pathnodes.h:949
BlockNumber pages
Definition: pathnodes.h:948
QualCost baserestrictcost
Definition: pathnodes.h:987
Cardinality rows
Definition: pathnodes.h:877
List * get_sortgrouplist_exprs(List *sgClauses, List *targetList)
Definition: tlist.c:392

References adjust_foreign_grouping_path_cost(), adjust_limit_rows_costs(), AGGSPLIT_SIMPLE, appendStringInfoString(), Assert, RelOptInfo::baserestrictcost, build_tlist_to_deparse(), clamp_row_est(), classifyConditions(), clauselist_selectivity(), conn, PathTarget::cost, cost_qual_eval(), PgFdwPathExtraData::count_est, cpu_operator_cost, cpu_tuple_cost, StringInfoData::data, DEFAULT_FDW_SORT_MULTIPLIER, deparseSelectStmtForRel(), estimate_num_groups(), PgFdwRelationInfo::fdw_startup_cost, PgFdwRelationInfo::fdw_tuple_cost, AggClauseCosts::finalCost, get_agg_clause_costs(), get_remote_estimate(), get_sortgrouplist_exprs(), GetConnection(), PgFdwRelationInfo::grouped_tlist, PgFdwPathExtraData::has_final_sort, PgFdwPathExtraData::has_limit, initStringInfo(), PgFdwRelationInfo::innerrel, IS_JOIN_REL, IS_UPPER_REL, JOIN_INNER, PgFdwRelationInfo::joinclause_sel, PgFdwRelationInfo::joinclauses, PgFdwPathExtraData::limit_tuples, list_concat(), list_length(), PgFdwRelationInfo::local_conds_cost, PgFdwRelationInfo::local_conds_sel, MemSet, Min, NIL, PgFdwPathExtraData::offset_est, PgFdwRelationInfo::outerrel, RelOptInfo::pages, QualCost::per_tuple, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, ReleaseConnection(), RelOptInfo::relid, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, PgFdwRelationInfo::retrieved_rows, root, PgFdwRelationInfo::rows, RelOptInfo::rows, seq_page_cost, PgFdwRelationInfo::stage, QualCost::startup, PgFdwPathExtraData::target, AggClauseCosts::transCost, RelOptInfo::tuples, UPPERREL_GROUP_AGG, PgFdwRelationInfo::use_remote_estimate, PgFdwRelationInfo::user, PgFdwRelationInfo::width, and PathTarget::width.

Referenced by add_foreign_final_paths(), add_foreign_grouping_paths(), add_foreign_ordered_paths(), add_paths_with_pathkeys_for_rel(), postgresGetForeignJoinPaths(), postgresGetForeignPaths(), and postgresGetForeignRelSize().

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

Definition at line 4557 of file postgres_fdw.c.

4558 {
4560  ExprContext *econtext = node->ss.ps.ps_ExprContext;
4561  int numParams = dmstate->numParams;
4562  const char **values = dmstate->param_values;
4563 
4564  /* First, process a pending asynchronous request, if any. */
4565  if (dmstate->conn_state->pendingAreq)
4567 
4568  /*
4569  * Construct array of query parameter values in text format.
4570  */
4571  if (numParams > 0)
4572  process_query_params(econtext,
4573  dmstate->param_flinfo,
4574  dmstate->param_exprs,
4575  values);
4576 
4577  /*
4578  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4579  * to infer types for all parameters. Since we explicitly cast every
4580  * parameter (see deparse.c), the "inference" is trivial and will produce
4581  * the desired result. This allows us to avoid assuming that the remote
4582  * server has the same OIDs we do for the parameters' types.
4583  */
4584  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4585  NULL, values, NULL, NULL, 0))
4586  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4587 
4588  /*
4589  * Get the result, and check for success.
4590  *
4591  * We don't use a PG_TRY block here, so be careful not to throw error
4592  * without releasing the PGresult.
4593  */
4594  dmstate->result = pgfdw_get_result(dmstate->conn);
4595  if (PQresultStatus(dmstate->result) !=
4597  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4598  dmstate->query);
4599 
4600  /* Get the number of rows affected. */
4601  if (dmstate->has_returning)
4602  dmstate->num_tuples = PQntuples(dmstate->result);
4603  else
4604  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4605 }
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3822
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3481
@ PGRES_TUPLES_OK
Definition: libpq-fe.h:123
PgFdwConnState * conn_state
Definition: postgres_fdw.c:229
const char ** param_values
Definition: postgres_fdw.c:233

References PgFdwDirectModifyState::conn, PgFdwDirectModifyState::conn_state, ERROR, ForeignScanState::fdw_state, PgFdwDirectModifyState::has_returning, if(), PgFdwDirectModifyState::num_tuples, PgFdwDirectModifyState::numParams, PgFdwDirectModifyState::param_exprs, PgFdwDirectModifyState::param_flinfo, PgFdwDirectModifyState::param_values, PgFdwConnState::pendingAreq, pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQcmdTuples(), PQntuples(), PQresultStatus(), PQsendQueryParams(), process_pending_request(), process_query_params(), ScanState::ps, PlanState::ps_ExprContext, PgFdwDirectModifyState::query, PgFdwDirectModifyState::result, ForeignScanState::ss, and values.

Referenced by postgresIterateDirectModify().

◆ execute_foreign_modify()

static TupleTableSlot ** execute_foreign_modify ( EState estate,
ResultRelInfo resultRelInfo,
CmdType  operation,
TupleTableSlot **  slots,
TupleTableSlot **  planSlots,
int *  numSlots 
)
static

Definition at line 4096 of file postgres_fdw.c.

4102 {
4103  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
4104  ItemPointer ctid = NULL;
4105  const char **p_values;
4106  PGresult *res;
4107  int n_rows;
4108  StringInfoData sql;
4109 
4110  /* The operation should be INSERT, UPDATE, or DELETE */
4111  Assert(operation == CMD_INSERT ||
4112  operation == CMD_UPDATE ||
4113  operation == CMD_DELETE);
4114 
4115  /* First, process a pending asynchronous request, if any. */
4116  if (fmstate->conn_state->pendingAreq)
4118 
4119  /*
4120  * If the existing query was deparsed and prepared for a different number
4121  * of rows, rebuild it for the proper number.
4122  */
4123  if (operation == CMD_INSERT && fmstate->num_slots != *numSlots)
4124  {
4125  /* Destroy the prepared statement created previously */
4126  if (fmstate->p_name)
4127  deallocate_query(fmstate);
4128 
4129  /* Build INSERT string with numSlots records in its VALUES clause. */
4130  initStringInfo(&sql);
4131  rebuildInsertSql(&sql, fmstate->rel,
4132  fmstate->orig_query, fmstate->target_attrs,
4133  fmstate->values_end, fmstate->p_nums,
4134  *numSlots - 1);
4135  pfree(fmstate->query);
4136  fmstate->query = sql.data;
4137  fmstate->num_slots = *numSlots;
4138  }
4139 
4140  /* Set up the prepared statement on the remote server, if we didn't yet */
4141  if (!fmstate->p_name)
4142  prepare_foreign_modify(fmstate);
4143 
4144  /*
4145  * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
4146  */
4147  if (operation == CMD_UPDATE || operation == CMD_DELETE)
4148  {
4149  Datum datum;
4150  bool isNull;
4151 
4152  datum = ExecGetJunkAttribute(planSlots[0],
4153  fmstate->ctidAttno,
4154  &isNull);
4155  /* shouldn't ever get a null result... */
4156  if (isNull)
4157  elog(ERROR, "ctid is NULL");
4158  ctid = (ItemPointer) DatumGetPointer(datum);
4159  }
4160 
4161  /* Convert parameters needed by prepared statement to text form */
4162  p_values = convert_prep_stmt_params(fmstate, ctid, slots, *numSlots);
4163 
4164  /*
4165  * Execute the prepared statement.
4166  */
4167  if (!PQsendQueryPrepared(fmstate->conn,
4168  fmstate->p_name,
4169  fmstate->p_nums * (*numSlots),
4170  p_values,
4171  NULL,
4172  NULL,
4173  0))
4174  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
4175 
4176  /*
4177  * Get the result, and check for success.
4178  *
4179  * We don't use a PG_TRY block here, so be careful not to throw error
4180  * without releasing the PGresult.
4181  */
4182  res = pgfdw_get_result(fmstate->conn);
4183  if (PQresultStatus(res) !=
4185  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
4186 
4187  /* Check number of rows affected, and fetch RETURNING tuple if any */
4188  if (fmstate->has_returning)
4189  {
4190  Assert(*numSlots == 1);
4191  n_rows = PQntuples(res);
4192  if (n_rows > 0)
4193  store_returning_result(fmstate, slots[0], res);
4194  }
4195  else
4196  n_rows = atoi(PQcmdTuples(res));
4197 
4198  /* And clean up */
4199  PQclear(res);
4200 
4201  MemoryContextReset(fmstate->temp_cxt);
4202 
4203  *numSlots = n_rows;
4204 
4205  /*
4206  * Return NULL if nothing was inserted/updated/deleted on the remote end
4207  */
4208  return (n_rows > 0) ? slots : NULL;
4209 }
void rebuildInsertSql(StringInfo buf, Relation rel, char *orig_query, List *target_attrs, int values_end_len, int num_params, int num_rows)
Definition: deparse.c:2154
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: executor.h:190
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition: fe-exec.c:1633
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
static const char ** convert_prep_stmt_params(PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot **slots, int numSlots)
static void store_returning_result(PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
static void deallocate_query(PgFdwModifyState *fmstate)
static void prepare_foreign_modify(PgFdwModifyState *fmstate)
void * ri_FdwState
Definition: execnodes.h:512

References Assert, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, PgFdwModifyState::conn_state, convert_prep_stmt_params(), PgFdwModifyState::ctidAttno, DatumGetPointer(), deallocate_query(), elog, ERROR, ExecGetJunkAttribute(), PgFdwModifyState::has_returning, initStringInfo(), MemoryContextReset(), PgFdwModifyState::num_slots, PgFdwModifyState::orig_query, PgFdwModifyState::p_name, PgFdwModifyState::p_nums, PgFdwConnState::pendingAreq, pfree(), pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQcmdTuples(), PQntuples(), PQresultStatus(), PQsendQueryPrepared(), prepare_foreign_modify(), process_pending_request(), PgFdwModifyState::query, rebuildInsertSql(), PgFdwModifyState::rel, res, ResultRelInfo::ri_FdwState, store_returning_result(), PgFdwModifyState::target_attrs, PgFdwModifyState::temp_cxt, and PgFdwModifyState::values_end.

Referenced by postgresExecForeignBatchInsert(), postgresExecForeignDelete(), postgresExecForeignInsert(), and postgresExecForeignUpdate().

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3804 of file postgres_fdw.c.

3805 {
3806  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3807  PGresult *volatile res = NULL;
3808  MemoryContext oldcontext;
3809 
3810  /*
3811  * We'll store the tuples in the batch_cxt. First, flush the previous
3812  * batch.
3813  */
3814  fsstate->tuples = NULL;
3815  MemoryContextReset(fsstate->batch_cxt);
3816  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3817 
3818  /* PGresult must be released before leaving this function. */
3819  PG_TRY();
3820  {
3821  PGconn *conn = fsstate->conn;
3822  int numrows;
3823  int i;
3824 
3825  if (fsstate->async_capable)
3826  {
3827  Assert(fsstate->conn_state->pendingAreq);
3828 
3829  /*
3830  * The query was already sent by an earlier call to
3831  * fetch_more_data_begin. So now we just fetch the result.
3832  */
3834  /* On error, report the original query, not the FETCH. */
3836  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3837 
3838  /* Reset per-connection state */
3839  fsstate->conn_state->pendingAreq = NULL;
3840  }
3841  else
3842  {
3843  char sql[64];
3844 
3845  /* This is a regular synchronous fetch. */
3846  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3847  fsstate->fetch_size, fsstate->cursor_number);
3848 
3849  res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
3850  /* On error, report the original query, not the FETCH. */
3852  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3853  }
3854 
3855  /* Convert the data into HeapTuples */
3856  numrows = PQntuples(res);
3857  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3858  fsstate->num_tuples = numrows;
3859  fsstate->next_tuple = 0;
3860 
3861  for (i = 0; i < numrows; i++)
3862  {
3863  Assert(IsA(node->ss.ps.plan, ForeignScan));
3864 
3865  fsstate->tuples[i] =
3867  fsstate->rel,
3868  fsstate->attinmeta,
3869  fsstate->retrieved_attrs,
3870  node,
3871  fsstate->temp_cxt);
3872  }
3873 
3874  /* Update fetch_ct_2 */
3875  if (fsstate->fetch_ct_2 < 2)
3876  fsstate->fetch_ct_2++;
3877 
3878  /* Must be EOF if we didn't get as many tuples as we asked for. */
3879  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3880  }
3881  PG_FINALLY();
3882  {
3883  PQclear(res);
3884  }
3885  PG_END_TRY();
3886 
3887  MemoryContextSwitchTo(oldcontext);
3888 }
#define PG_TRY(...)
Definition: elog.h:371
#define PG_END_TRY(...)
Definition: elog.h:396
#define PG_FINALLY(...)
Definition: elog.h:388
List * retrieved_attrs
Definition: postgres_fdw.c:144
AttInMetadata * attinmeta
Definition: postgres_fdw.c:140
MemoryContext batch_cxt
Definition: postgres_fdw.c:169
MemoryContext temp_cxt
Definition: postgres_fdw.c:170

References Assert, PgFdwScanState::async_capable, PgFdwScanState::attinmeta, PgFdwScanState::batch_cxt, PgFdwScanState::conn, conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, ERROR, ForeignScanState::fdw_state, PgFdwScanState::fetch_ct_2, PgFdwScanState::fetch_size, i, IsA, make_tuple_from_result_row(), MemoryContextReset(), MemoryContextSwitchTo(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, palloc0(), PgFdwConnState::pendingAreq, PG_END_TRY, PG_FINALLY, PG_TRY, pgfdw_exec_query(), pgfdw_get_result(), pgfdw_report_error(), PGRES_TUPLES_OK, PlanState::plan, PQclear(), PQntuples(), PQresultStatus(), ScanState::ps, PgFdwScanState::query, PgFdwScanState::rel, res, PgFdwScanState::retrieved_attrs, snprintf, ForeignScanState::ss, PgFdwScanState::temp_cxt, and PgFdwScanState::tuples.

Referenced by postgresForeignAsyncNotify(), postgresIterateForeignScan(), postgresReScanForeignScan(), and process_pending_request().

◆ fetch_more_data_begin()

static void fetch_more_data_begin ( AsyncRequest areq)
static

Definition at line 7475 of file postgres_fdw.c.

7476 {
7477  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7478  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7479  char sql[64];
7480 
7481  Assert(!fsstate->conn_state->pendingAreq);
7482 
7483  /* Create the cursor synchronously. */
7484  if (!fsstate->cursor_exists)
7485  create_cursor(node);
7486 
7487  /* We will send this query, but not wait for the response. */
7488  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7489  fsstate->fetch_size, fsstate->cursor_number);
7490 
7491  if (!PQsendQuery(fsstate->conn, sql))
7492  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7493 
7494  /* Remember that the request is in process */
7495  fsstate->conn_state->pendingAreq = areq;
7496 }
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1416
static void create_cursor(ForeignScanState *node)

References Assert, create_cursor(), ERROR, ForeignScanState::fdw_state, pgfdw_report_error(), PQsendQuery(), AsyncRequest::requestee, and snprintf.

Referenced by postgresForeignAsyncConfigureWait(), and produce_tuple_asynchronously().

◆ find_em_for_rel()

EquivalenceMember* find_em_for_rel ( PlannerInfo root,
EquivalenceClass ec,
RelOptInfo rel 
)

Definition at line 7829 of file postgres_fdw.c.

7830 {
7831  ListCell *lc;
7832 
7833  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7834 
7835  foreach(lc, ec->ec_members)
7836  {
7838 
7839  /*
7840  * Note we require !bms_is_empty, else we'd accept constant
7841  * expressions which are not suitable for the purpose.
7842  */
7843  if (bms_is_subset(em->em_relids, rel->relids) &&
7844  !bms_is_empty(em->em_relids) &&
7846  is_foreign_expr(root, rel, em->em_expr))
7847  return em;
7848  }
7849 
7850  return NULL;
7851 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:412
Bitmapset * bms_intersect(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:292
#define bms_is_empty(a)
Definition: bitmapset.h:118
Relids hidden_subquery_rels
Definition: postgres_fdw.h:122
Relids relids
Definition: pathnodes.h:871

References bms_intersect(), bms_is_empty, bms_is_subset(), EquivalenceClass::ec_members, EquivalenceMember::em_expr, EquivalenceMember::em_relids, PgFdwRelationInfo::hidden_subquery_rels, is_foreign_expr(), lfirst, RelOptInfo::relids, and root.

Referenced by appendOrderByClause(), get_useful_pathkeys_for_relation(), and is_foreign_pathkey().

◆ find_em_for_rel_target()

EquivalenceMember* find_em_for_rel_target ( PlannerInfo root,
EquivalenceClass ec,
RelOptInfo rel 
)

Definition at line 7865 of file postgres_fdw.c.

7867 {
7868  PathTarget *target = rel->reltarget;
7869  ListCell *lc1;
7870  int i;
7871 
7872  i = 0;
7873  foreach(lc1, target->exprs)
7874  {
7875  Expr *expr = (Expr *) lfirst(lc1);
7876  Index sgref = get_pathtarget_sortgroupref(target, i);
7877  ListCell *lc2;
7878 
7879  /* Ignore non-sort expressions */
7880  if (sgref == 0 ||
7882  root->parse->sortClause) == NULL)
7883  {
7884  i++;
7885  continue;
7886  }
7887 
7888  /* We ignore binary-compatible relabeling on both ends */
7889  while (expr && IsA(expr, RelabelType))
7890  expr = ((RelabelType *) expr)->arg;
7891 
7892  /* Locate an EquivalenceClass member matching this expr, if any */
7893  foreach(lc2, ec->ec_members)
7894  {
7896  Expr *em_expr;
7897 
7898  /* Don't match constants */
7899  if (em->em_is_const)
7900  continue;
7901 
7902  /* Ignore child members */
7903  if (em->em_is_child)
7904  continue;
7905 
7906  /* Match if same expression (after stripping relabel) */
7907  em_expr = em->em_expr;
7908  while (em_expr && IsA(em_expr, RelabelType))
7909  em_expr = ((RelabelType *) em_expr)->arg;
7910 
7911  if (!equal(em_expr, expr))
7912  continue;
7913 
7914  /* Check that expression (including relabels!) is shippable */
7915  if (is_foreign_expr(root, rel, em->em_expr))
7916  return em;
7917  }
7918 
7919  i++;
7920  }
7921 
7922  return NULL;
7923 }
unsigned int Index
Definition: c.h:593
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1558
List * exprs
Definition: pathnodes.h:1542
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:443

References arg, EquivalenceClass::ec_members, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, EquivalenceMember::em_is_const, equal(), PathTarget::exprs, get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), i, is_foreign_expr(), IsA, lfirst, RelOptInfo::reltarget, and root.

Referenced by add_foreign_ordered_paths(), and appendOrderByClause().

◆ find_modifytable_subplan()

static ForeignScan* find_modifytable_subplan ( PlannerInfo root,
ModifyTable plan,
Index  rtindex,
int  subplan_index 
)
static

Definition at line 2388 of file postgres_fdw.c.

2392 {
2393  Plan *subplan = outerPlan(plan);
2394 
2395  /*
2396  * The cases we support are (1) the desired ForeignScan is the immediate
2397  * child of ModifyTable, or (2) it is the subplan_index'th child of an
2398  * Append node that is the immediate child of ModifyTable. There is no
2399  * point in looking further down, as that would mean that local joins are
2400  * involved, so we can't do the update directly.
2401  *
2402  * There could be a Result atop the Append too, acting to compute the
2403  * UPDATE targetlist values. We ignore that here; the tlist will be
2404  * checked by our caller.
2405  *
2406  * In principle we could examine all the children of the Append, but it's
2407  * currently unlikely that the core planner would generate such a plan
2408  * with the children out-of-order. Moreover, such a search risks costing
2409  * O(N^2) time when there are a lot of children.
2410  */
2411  if (IsA(subplan, Append))
2412  {
2413  Append *appendplan = (Append *) subplan;
2414 
2415  if (subplan_index < list_length(appendplan->appendplans))
2416  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2417  }
2418  else if (IsA(subplan, Result) &&
2419  outerPlan(subplan) != NULL &&
2420  IsA(outerPlan(subplan), Append))
2421  {
2422  Append *appendplan = (Append *) outerPlan(subplan);
2423 
2424  if (subplan_index < list_length(appendplan->appendplans))
2425  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2426  }
2427 
2428  /* Now, have we got a ForeignScan on the desired rel? */
2429  if (IsA(subplan, ForeignScan))
2430  {
2431  ForeignScan *fscan = (ForeignScan *) subplan;
2432 
2433  if (bms_is_member(rtindex, fscan->fs_base_relids))
2434  return fscan;
2435  }
2436 
2437  return NULL;
2438 }
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
#define plan(x)
Definition: pg_regress.c:161
#define outerPlan(node)
Definition: plannodes.h:183
List * appendplans
Definition: plannodes.h:270
Bitmapset * fs_base_relids
Definition: plannodes.h:721

References Append::appendplans, bms_is_member(), ForeignScan::fs_base_relids, IsA, list_length(), list_nth(), outerPlan, and plan.

Referenced by postgresPlanDirectModify().

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4383 of file postgres_fdw.c.

4384 {
4385  Assert(fmstate != NULL);
4386 
4387  /* If we created a prepared statement, destroy it */
4388  deallocate_query(fmstate);
4389 
4390  /* Release remote connection */
4391  ReleaseConnection(fmstate->conn);
4392  fmstate->conn = NULL;
4393 }

References Assert, PgFdwModifyState::conn, deallocate_query(), and ReleaseConnection().

Referenced by postgresEndForeignInsert(), and postgresEndForeignModify().

◆ foreign_grouping_ok()

static bool foreign_grouping_ok ( PlannerInfo root,
RelOptInfo grouped_rel,
Node havingQual 
)
static

Definition at line 6486 of file postgres_fdw.c.

6488 {
6489  Query *query = root->parse;
6490  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
6491  PathTarget *grouping_target = grouped_rel->reltarget;
6492  PgFdwRelationInfo *ofpinfo;
6493  ListCell *lc;
6494  int i;
6495  List *tlist = NIL;
6496 
6497  /* We currently don't support pushing Grouping Sets. */
6498  if (query->groupingSets)
6499  return false;
6500 
6501  /* Get the fpinfo of the underlying scan relation. */
6502  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
6503 
6504  /*
6505  * If underlying scan relation has any local conditions, those conditions
6506  * are required to be applied before performing aggregation. Hence the
6507  * aggregate cannot be pushed down.
6508  */
6509  if (ofpinfo->local_conds)
6510  return false;
6511 
6512  /*
6513  * Examine grouping expressions, as well as other expressions we'd need to
6514  * compute, and check whether they are safe to push down to the foreign
6515  * server. All GROUP BY expressions will be part of the grouping target
6516  * and thus there is no need to search for them separately. Add grouping
6517  * expressions into target list which will be passed to foreign server.
6518  *
6519  * A tricky fine point is that we must not put any expression into the
6520  * target list that is just a foreign param (that is, something that
6521  * deparse.c would conclude has to be sent to the foreign server). If we
6522  * do, the expression will also appear in the fdw_exprs list of the plan
6523  * node, and setrefs.c will get confused and decide that the fdw_exprs
6524  * entry is actually a reference to the fdw_scan_tlist entry, resulting in
6525  * a broken plan. Somewhat oddly, it's OK if the expression contains such
6526  * a node, as long as it's not at top level; then no match is possible.
6527  */
6528  i = 0;
6529  foreach(lc, grouping_target->exprs)
6530  {
6531  Expr *expr = (Expr *) lfirst(lc);
6532  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
6533  ListCell *l;
6534 
6535  /*
6536  * Check whether this expression is part of GROUP BY clause. Note we
6537  * check the whole GROUP BY clause not just processed_groupClause,
6538  * because we will ship all of it, cf. appendGroupByClause.
6539  */
6540  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
6541  {
6542  TargetEntry *tle;
6543 
6544  /*
6545  * If any GROUP BY expression is not shippable, then we cannot
6546  * push down aggregation to the foreign server.
6547  */
6548  if (!is_foreign_expr(root, grouped_rel, expr))
6549  return false;
6550 
6551  /*
6552  * If it would be a foreign param, we can't put it into the tlist,
6553  * so we have to fail.
6554  */
6555  if (is_foreign_param(root, grouped_rel, expr))
6556  return false;
6557 
6558  /*
6559  * Pushable, so add to tlist. We need to create a TLE for this
6560  * expression and apply the sortgroupref to it. We cannot use
6561  * add_to_flat_tlist() here because that avoids making duplicate
6562  * entries in the tlist. If there are duplicate entries with
6563  * distinct sortgrouprefs, we have to duplicate that situation in
6564  * the output tlist.
6565  */
6566  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
6567  tle->ressortgroupref = sgref;
6568  tlist = lappend(tlist, tle);
6569  }
6570  else
6571  {
6572  /*
6573  * Non-grouping expression we need to compute. Can we ship it
6574  * as-is to the foreign server?
6575  */
6576  if (is_foreign_expr(root, grouped_rel, expr) &&
6577  !is_foreign_param(root, grouped_rel, expr))
6578  {
6579  /* Yes, so add to tlist as-is; OK to suppress duplicates */
6580  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6581  }
6582  else
6583  {
6584  /* Not pushable as a whole; extract its Vars and aggregates */
6585  List *aggvars;
6586 
6587  aggvars = pull_var_clause((Node *) expr,
6589 
6590  /*
6591  * If any aggregate expression is not shippable, then we
6592  * cannot push down aggregation to the foreign server. (We
6593  * don't have to check is_foreign_param, since that certainly
6594  * won't return true for any such expression.)
6595  */
6596  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
6597  return false;
6598 
6599  /*
6600  * Add aggregates, if any, into the targetlist. Plain Vars
6601  * outside an aggregate can be ignored, because they should be
6602  * either same as some GROUP BY column or part of some GROUP
6603  * BY expression. In either case, they are already part of
6604  * the targetlist and thus no need to add them again. In fact
6605  * including plain Vars in the tlist when they do not match a
6606  * GROUP BY column would cause the foreign server to complain
6607  * that the shipped query is invalid.
6608  */
6609  foreach(l, aggvars)
6610  {
6611  Expr *aggref = (Expr *) lfirst(l);
6612 
6613  if (IsA(aggref, Aggref))
6614  tlist = add_to_flat_tlist(tlist, list_make1(aggref));
6615  }
6616  }
6617  }
6618 
6619  i++;
6620  }
6621 
6622  /*
6623  * Classify the pushable and non-pushable HAVING clauses and save them in
6624  * remote_conds and local_conds of the grouped rel's fpinfo.
6625  */
6626  if (havingQual)
6627  {
6628  foreach(lc, (List *) havingQual)
6629  {
6630  Expr *expr = (Expr *) lfirst(lc);
6631  RestrictInfo *rinfo;
6632 
6633  /*
6634  * Currently, the core code doesn't wrap havingQuals in
6635  * RestrictInfos, so we must make our own.
6636  */
6637  Assert(!IsA(expr, RestrictInfo));
6638  rinfo = make_restrictinfo(root,
6639  expr,
6640  true,
6641  false,
6642  false,
6643  false,
6644  root->qual_security_level,
6645  grouped_rel->relids,
6646  NULL,
6647  NULL);
6648  if (is_foreign_expr(root, grouped_rel, expr))
6649  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
6650  else
6651  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
6652  }
6653  }
6654 
6655  /*
6656  * If there are any local conditions, pull Vars and aggregates from it and
6657  * check whether they are safe to pushdown or not.
6658  */
6659  if (fpinfo->local_conds)
6660  {
6661  List *aggvars = NIL;
6662 
6663  foreach(lc, fpinfo->local_conds)
6664  {
6665  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6666 
6667  aggvars = list_concat(aggvars,
6668  pull_var_clause((Node *) rinfo->clause,
6670  }
6671 
6672  foreach(lc, aggvars)
6673  {
6674  Expr *expr = (Expr *) lfirst(lc);
6675 
6676  /*
6677  * If aggregates within local conditions are not safe to push
6678  * down, then we cannot push down the query. Vars are already
6679  * part of GROUP BY clause which are checked above, so no need to
6680  * access them again here. Again, we need not check
6681  * is_foreign_param for a foreign aggregate.
6682  */
6683  if (IsA(expr, Aggref))
6684  {
6685  if (!is_foreign_expr(root, grouped_rel, expr))
6686  return false;
6687 
6688  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6689  }
6690  }
6691  }
6692 
6693  /* Store generated targetlist */
6694  fpinfo->grouped_tlist = tlist;
6695 
6696  /* Safe to pushdown */
6697  fpinfo->pushdown_safe = true;
6698 
6699  /*
6700  * Set # of retrieved rows and cached relation costs to some negative
6701  * value, so that we can detect when they are set to some sensible values,
6702  * during one (usually the first) of the calls to estimate_path_cost_size.
6703  */
6704  fpinfo->retrieved_rows = -1;
6705  fpinfo->rel_startup_cost = -1;
6706  fpinfo->rel_total_cost = -1;
6707 
6708  /*
6709  * Set the string describing this grouped relation to be used in EXPLAIN
6710  * output of corresponding ForeignScan. Note that the decoration we add
6711  * to the base relation name mustn't include any digits, or it'll confuse
6712  * postgresExplainForeignScan.
6713  */
6714  fpinfo->relation_name = psprintf("Aggregate on (%s)",
6715  ofpinfo->relation_name);
6716 
6717  return true;
6718 }
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:1080
#define PVC_INCLUDE_AGGREGATES
Definition: optimizer.h:187
#define list_make1(x1)
Definition: pg_list.h:212
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
RestrictInfo * make_restrictinfo(PlannerInfo *root, Expr *clause, bool is_pushed_down, bool has_clone, bool is_clone, bool pseudoconstant, Index security_level, Relids required_relids, Relids incompatible_relids, Relids outer_relids)
Definition: restrictinfo.c:52
List * groupClause
Definition: parsenodes.h:202
List * groupingSets
Definition: parsenodes.h:205
Index ressortgroupref
Definition: primnodes.h:2196
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition: tlist.c:132

References add_to_flat_tlist(), Assert, RestrictInfo::clause, get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), Query::groupClause, PgFdwRelationInfo::grouped_tlist, Query::groupingSets, i, if(), is_foreign_expr(), is_foreign_param(), IsA, lappend(), lfirst, lfirst_node, list_concat(), list_length(), list_make1, PgFdwRelationInfo::local_conds, make_restrictinfo(), makeTargetEntry(), NIL, PgFdwRelationInfo::outerrel, psprintf(), pull_var_clause(), PgFdwRelationInfo::pushdown_safe, PVC_INCLUDE_AGGREGATES, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_name, RelOptInfo::relids, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, TargetEntry::ressortgroupref, PgFdwRelationInfo::retrieved_rows, and root.

Referenced by add_foreign_grouping_paths().

◆ foreign_join_ok()

static bool foreign_join_ok ( PlannerInfo root,
RelOptInfo joinrel,
JoinType  jointype,
RelOptInfo outerrel,
RelOptInfo innerrel,
JoinPathExtraData extra 
)
static

Definition at line 5793 of file postgres_fdw.c.

5796 {
5797  PgFdwRelationInfo *fpinfo;
5798  PgFdwRelationInfo *fpinfo_o;
5799  PgFdwRelationInfo *fpinfo_i;
5800  ListCell *lc;
5801  List *joinclauses;
5802 
5803  /*
5804  * We support pushing down INNER, LEFT, RIGHT, FULL OUTER and SEMI joins.
5805  * Constructing queries representing ANTI joins is hard, hence not
5806  * considered right now.
5807  */
5808  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
5809  jointype != JOIN_RIGHT && jointype != JOIN_FULL &&
5810  jointype != JOIN_SEMI)
5811  return false;
5812 
5813  /*
5814  * We can't push down semi-join if its reltarget is not safe
5815  */
5816  if ((jointype == JOIN_SEMI) && !semijoin_target_ok(root, joinrel, outerrel, innerrel))
5817  return false;
5818 
5819  /*
5820  * If either of the joining relations is marked as unsafe to pushdown, the
5821  * join can not be pushed down.
5822  */
5823  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
5824  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
5825  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
5826  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
5827  !fpinfo_i || !fpinfo_i->pushdown_safe)
5828  return false;
5829 
5830  /*
5831  * If joining relations have local conditions, those conditions are
5832  * required to be applied before joining the relations. Hence the join can
5833  * not be pushed down.
5834  */
5835  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
5836  return false;
5837 
5838  /*
5839  * Merge FDW options. We might be tempted to do this after we have deemed
5840  * the foreign join to be OK. But we must do this beforehand so that we
5841  * know which quals can be evaluated on the foreign server, which might
5842  * depend on shippable_extensions.
5843  */
5844  fpinfo->server = fpinfo_o->server;
5845  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
5846 
5847  /*
5848  * Separate restrict list into join quals and pushed-down (other) quals.
5849  *
5850  * Join quals belonging to an outer join must all be shippable, else we
5851  * cannot execute the join remotely. Add such quals to 'joinclauses'.
5852  *
5853  * Add other quals to fpinfo->remote_conds if they are shippable, else to
5854  * fpinfo->local_conds. In an inner join it's okay to execute conditions
5855  * either locally or remotely; the same is true for pushed-down conditions
5856  * at an outer join.
5857  *
5858  * Note we might return failure after having already scribbled on
5859  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
5860  * won't consult those lists again if we deem the join unshippable.
5861  */
5862  joinclauses = NIL;
5863  foreach(lc, extra->restrictlist)
5864  {
5865  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5866  bool is_remote_clause = is_foreign_expr(root, joinrel,
5867  rinfo->clause);
5868 
5869  if (IS_OUTER_JOIN(jointype) &&
5870  !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
5871  {
5872  if (!is_remote_clause)
5873  return false;
5874  joinclauses = lappend(joinclauses, rinfo);
5875  }
5876  else
5877  {
5878  if (is_remote_clause)
5879  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5880  else
5881  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5882  }
5883  }
5884 
5885  /*
5886  * deparseExplicitTargetList() isn't smart enough to handle anything other
5887  * than a Var. In particular, if there's some PlaceHolderVar that would
5888  * need to be evaluated within this join tree (because there's an upper
5889  * reference to a quantity that may go to NULL as a result of an outer
5890  * join), then we can't try to push the join down because we'll fail when
5891  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
5892  * needs to be evaluated *at the top* of this join tree is OK, because we
5893  * can do that locally after fetching the results from the remote side.
5894  */
5895  foreach(lc, root->placeholder_list)
5896  {
5897  PlaceHolderInfo *phinfo = lfirst(lc);
5898  Relids relids;
5899 
5900  /* PlaceHolderInfo refers to parent relids, not child relids. */
5901  relids = IS_OTHER_REL(joinrel) ?
5902  joinrel->top_parent_relids : joinrel->relids;
5903 
5904  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
5905  bms_nonempty_difference(relids, phinfo->ph_eval_at))
5906  return false;
5907  }
5908 
5909  /* Save the join clauses, for later use. */
5910  fpinfo->joinclauses = joinclauses;
5911 
5912  fpinfo->outerrel = outerrel;
5913  fpinfo->innerrel = innerrel;
5914  fpinfo->jointype = jointype;
5915 
5916  /*
5917  * By default, both the input relations are not required to be deparsed as
5918  * subqueries, but there might be some relations covered by the input
5919  * relations that are required to be deparsed as subqueries, so save the
5920  * relids of those relations for later use by the deparser.
5921  */
5922  fpinfo->make_outerrel_subquery = false;
5923  fpinfo->make_innerrel_subquery = false;
5924  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
5925  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
5927  fpinfo_i->lower_subquery_rels);
5929  fpinfo_i->hidden_subquery_rels);
5930 
5931  /*
5932  * Pull the other remote conditions from the joining relations into join
5933  * clauses or other remote clauses (remote_conds) of this relation
5934  * wherever possible. This avoids building subqueries at every join step.
5935  *
5936  * For an inner join, clauses from both the relations are added to the
5937  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
5938  * the outer side are added to remote_conds since those can be evaluated
5939  * after the join is evaluated. The clauses from inner side are added to
5940  * the joinclauses, since they need to be evaluated while constructing the
5941  * join.
5942  *
5943  * For SEMI-JOIN clauses from inner relation can not be added to
5944  * remote_conds, but should be treated as join clauses (as they are
5945  * deparsed to EXISTS subquery, where inner relation can be referred). A
5946  * list of relation ids, which can't be referred to from higher levels, is
5947  * preserved as a hidden_subquery_rels list.
5948  *
5949  * For a FULL OUTER JOIN, the other clauses from either relation can not
5950  * be added to the joinclauses or remote_conds, since each relation acts
5951  * as an outer relation for the other.
5952  *
5953  * The joining sides can not have local conditions, thus no need to test
5954  * shippability of the clauses being pulled up.
5955  */
5956  switch (jointype)
5957  {
5958  case JOIN_INNER:
5959  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5960  fpinfo_i->remote_conds);
5961  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5962  fpinfo_o->remote_conds);
5963  break;
5964 
5965  case JOIN_LEFT:
5966  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5967  fpinfo_i->remote_conds);
5968  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5969  fpinfo_o->remote_conds);
5970  break;
5971 
5972  case JOIN_RIGHT:
5973  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5974  fpinfo_o->remote_conds);
5975  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5976  fpinfo_i->remote_conds);
5977  break;
5978 
5979  case JOIN_SEMI:
5980  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5981  fpinfo_i->remote_conds);
5982  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5983  fpinfo->remote_conds);
5984  fpinfo->remote_conds = list_copy(fpinfo_o->remote_conds);
5986  innerrel->relids);
5987  break;
5988 
5989  case JOIN_FULL:
5990 
5991  /*
5992  * In this case, if any of the input relations has conditions, we
5993  * need to deparse that relation as a subquery so that the
5994  * conditions can be evaluated before the join. Remember it in
5995  * the fpinfo of this relation so that the deparser can take
5996  * appropriate action. Also, save the relids of base relations
5997  * covered by that relation for later use by the deparser.
5998  */
5999  if (fpinfo_o->remote_conds)
6000  {
6001  fpinfo->make_outerrel_subquery = true;
6002  fpinfo->lower_subquery_rels =
6004  outerrel->relids);
6005  }
6006  if (fpinfo_i->remote_conds)
6007  {
6008  fpinfo->make_innerrel_subquery = true;
6009  fpinfo->lower_subquery_rels =
6011  innerrel->relids);
6012  }
6013  break;
6014 
6015  default:
6016  /* Should not happen, we have just checked this above */
6017  elog(ERROR, "unsupported join type %d", jointype);
6018  }
6019 
6020  /*
6021  * For an inner join, all restrictions can be treated alike. Treating the
6022  * pushed down conditions as join conditions allows a top level full outer
6023  * join to be deparsed without requiring subqueries.
6024  */
6025  if (jointype == JOIN_INNER)
6026  {
6027  Assert(!fpinfo->joinclauses);
6028  fpinfo->joinclauses = fpinfo->remote_conds;
6029  fpinfo->remote_conds = NIL;
6030  }
6031  else if (jointype == JOIN_LEFT || jointype == JOIN_RIGHT || jointype == JOIN_FULL)
6032  {
6033  /*
6034  * Conditions, generated from semi-joins, should be evaluated before
6035  * LEFT/RIGHT/FULL join.
6036  */
6037  if (!bms_is_empty(fpinfo_o->hidden_subquery_rels))
6038  {
6039  fpinfo->make_outerrel_subquery = true;
6040  fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, outerrel->relids);
6041  }
6042 
6043  if (!bms_is_empty(fpinfo_i->hidden_subquery_rels))
6044  {
6045  fpinfo->make_innerrel_subquery = true;
6046  fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, innerrel->relids);
6047  }
6048  }
6049 
6050  /* Mark that this join can be pushed down safely */
6051  fpinfo->pushdown_safe = true;
6052 
6053  /* Get user mapping */
6054  if (fpinfo->use_remote_estimate)
6055  {
6056  if (fpinfo_o->use_remote_estimate)
6057  fpinfo->user = fpinfo_o->user;
6058  else
6059  fpinfo->user = fpinfo_i->user;
6060  }
6061  else
6062  fpinfo->user = NULL;
6063 
6064  /*
6065  * Set # of retrieved rows and cached relation costs to some negative
6066  * value, so that we can detect when they are set to some sensible values,
6067  * during one (usually the first) of the calls to estimate_path_cost_size.
6068  */
6069  fpinfo->retrieved_rows = -1;
6070  fpinfo->rel_startup_cost = -1;
6071  fpinfo->rel_total_cost = -1;
6072 
6073  /*
6074  * Set the string describing this join relation to be used in EXPLAIN
6075  * output of corresponding ForeignScan. Note that the decoration we add
6076  * to the base relation names mustn't include any digits, or it'll confuse
6077  * postgresExplainForeignScan.
6078  */
6079  fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
6080  fpinfo_o->relation_name,
6081  get_jointype_name(fpinfo->jointype),
6082  fpinfo_i->relation_name);
6083 
6084  /*
6085  * Set the relation index. This is defined as the position of this
6086  * joinrel in the join_rel_list list plus the length of the rtable list.
6087  * Note that since this joinrel is at the end of the join_rel_list list
6088  * when we are called, we can get the position by list_length.
6089  */
6090  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
6091  fpinfo->relation_index =
6092  list_length(root->parse->rtable) + list_length(root->join_rel_list);
6093 
6094  return true;
6095 }
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:251
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:641
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1639
List * list_copy(const List *oldlist)
Definition: list.c:1573
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:338
@ JOIN_SEMI
Definition: nodes.h:307
@ JOIN_FULL
Definition: nodes.h:295
@ JOIN_RIGHT
Definition: nodes.h:296
@ JOIN_LEFT
Definition: nodes.h:294
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: pathnodes.h:2731
#define IS_OTHER_REL(rel)
Definition: pathnodes.h:854
static bool semijoin_target_ok(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel)
Relids lower_subquery_rels
Definition: postgres_fdw.h:120
Relids ph_eval_at
Definition: pathnodes.h:3098
Relids top_parent_relids
Definition: pathnodes.h:1009

References Assert, bms_add_members(), bms_is_empty, bms_is_subset(), bms_nonempty_difference(), bms_union(), RestrictInfo::clause, elog, ERROR, get_jointype_name(), PgFdwRelationInfo::hidden_subquery_rels, if(), PgFdwRelationInfo::innerrel, is_foreign_expr(), IS_OTHER_REL, IS_OUTER_JOIN, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, PgFdwRelationInfo::joinclauses, PgFdwRelationInfo::jointype, lappend(), lfirst, lfirst_node, list_concat(), list_copy(), list_length(), PgFdwRelationInfo::local_conds, PgFdwRelationInfo::lower_subquery_rels, PgFdwRelationInfo::make_innerrel_subquery, PgFdwRelationInfo::make_outerrel_subquery, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, PlaceHolderInfo::ph_eval_at, psprintf(), PgFdwRelationInfo::pushdown_safe, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_index, PgFdwRelationInfo::relation_name, RelOptInfo::relids, PgFdwRelationInfo::remote_conds, JoinPathExtraData::restrictlist, PgFdwRelationInfo::retrieved_rows, RINFO_IS_PUSHED_DOWN, root, semijoin_target_ok(), PgFdwRelationInfo::server, RelOptInfo::top_parent_relids, PgFdwRelationInfo::use_remote_estimate, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignJoinPaths().

◆ get_batch_size_option()

static int get_batch_size_option ( Relation  rel)
static

Definition at line 7930 of file postgres_fdw.c.

7931 {
7932  Oid foreigntableid = RelationGetRelid(rel);
7933  ForeignTable *table;
7934  ForeignServer *server;
7935  List *options;
7936  ListCell *lc;
7937 
7938  /* we use 1 by default, which means "no batching" */
7939  int batch_size = 1;
7940 
7941  /*
7942  * Load options for table and server. We append server options after table
7943  * options, because table options take precedence.
7944  */
7945  table = GetForeignTable(foreigntableid);
7946  server = GetForeignServer(table->serverid);
7947 
7948  options = NIL;
7949  options = list_concat(options, table->options);
7950  options = list_concat(options, server->options);
7951 
7952  /* See if either table or server specifies batch_size. */
7953  foreach(lc, options)
7954  {
7955  DefElem *def = (DefElem *) lfirst(lc);
7956 
7957  if (strcmp(def->defname, "batch_size") == 0)
7958  {
7959  (void) parse_int(defGetString(def), &batch_size, 0, NULL);
7960  break;
7961  }
7962  }
7963 
7964  return batch_size;
7965 }
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:111
static char ** options

References defGetString(), DefElem::defname, GetForeignServer(), GetForeignTable(), lfirst, list_concat(), NIL, options, ForeignServer::options, ForeignTable::options, parse_int(), RelationGetRelid, and ForeignTable::serverid.

Referenced by create_foreign_modify(), and postgresGetForeignModifyBatchSize().

◆ get_remote_estimate()

static void get_remote_estimate ( const char *  sql,
PGconn conn,
double *  rows,
int *  width,
Cost startup_cost,
Cost total_cost 
)
static

Definition at line 3597 of file postgres_fdw.c.

3600 {
3601  PGresult *volatile res = NULL;
3602 
3603  /* PGresult must be released before leaving this function. */
3604  PG_TRY();
3605  {
3606  char *line;
3607  char *p;
3608  int n;
3609 
3610  /*
3611  * Execute EXPLAIN remotely.
3612  */
3613  res = pgfdw_exec_query(conn, sql, NULL);
3615  pgfdw_report_error(ERROR, res, conn, false, sql);
3616 
3617  /*
3618  * Extract cost numbers for topmost plan node. Note we search for a
3619  * left paren from the end of the line to avoid being confused by
3620  * other uses of parentheses.
3621  */
3622  line = PQgetvalue(res, 0, 0);
3623  p = strrchr(line, '(');
3624  if (p == NULL)
3625  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3626  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3627  startup_cost, total_cost, rows, width);
3628  if (n != 4)
3629  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3630  }
3631  PG_FINALLY();
3632  {
3633  PQclear(res);
3634  }
3635  PG_END_TRY();
3636 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3876

References conn, elog, ERROR, PG_END_TRY, PG_FINALLY, PG_TRY, pgfdw_exec_query(), pgfdw_report_error(), PGRES_TUPLES_OK, PQclear(), PQgetvalue(), PQresultStatus(), and res.

Referenced by estimate_path_cost_size().

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 4611 of file postgres_fdw.c.

4612 {
4614  EState *estate = node->ss.ps.state;
4615  ResultRelInfo *resultRelInfo = node->resultRelInfo;
4616  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4617  TupleTableSlot *resultSlot;
4618 
4619  Assert(resultRelInfo->ri_projectReturning);
4620 
4621  /* If we didn't get any tuples, must be end of data. */
4622  if (dmstate->next_tuple >= dmstate->num_tuples)
4623  return ExecClearTuple(slot);
4624 
4625  /* Increment the command es_processed count if necessary. */
4626  if (dmstate->set_processed)
4627  estate->es_processed += 1;
4628 
4629  /*
4630  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4631  * tuple. (has_returning is false when the local query is of the form
4632  * "UPDATE/DELETE .. RETURNING 1" for example.)
4633  */
4634  if (!dmstate->has_returning)
4635  {
4636  ExecStoreAllNullTuple(slot);
4637  resultSlot = slot;
4638  }
4639  else
4640  {
4641  /*
4642  * On error, be sure to release the PGresult on the way out. Callers
4643  * do not have PG_TRY blocks to ensure this happens.
4644  */
4645  PG_TRY();
4646  {
4647  HeapTuple newtup;
4648 
4649  newtup = make_tuple_from_result_row(dmstate->result,
4650  dmstate->next_tuple,
4651  dmstate->rel,
4652  dmstate->attinmeta,
4653  dmstate->retrieved_attrs,
4654  node,
4655  dmstate->temp_cxt);
4656  ExecStoreHeapTuple(newtup, slot, false);
4657  }
4658  PG_CATCH();
4659  {
4660  PQclear(dmstate->result);
4661  PG_RE_THROW();
4662  }
4663  PG_END_TRY();
4664 
4665  /* Get the updated/deleted tuple. */
4666  if (dmstate->rel)
4667  resultSlot = slot;
4668  else
4669  resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4670  }
4671  dmstate->next_tuple++;
4672 
4673  /* Make slot available for evaluation of the local query RETURNING list. */
4674  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4675  resultSlot;
4676 
4677  return slot;
4678 }
#define PG_RE_THROW()
Definition: elog.h:412
#define PG_CATCH(...)
Definition: elog.h:381
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1663
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1439
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
ResultRelInfo * resultRelInfo
Definition: execnodes.h:2078
MemoryContext temp_cxt
Definition: postgres_fdw.c:246
AttInMetadata * attinmeta
Definition: postgres_fdw.c:219
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1577

References apply_returning_filter(), Assert, PgFdwDirectModifyState::attinmeta, ExecClearTuple(), ExecStoreAllNullTuple(), ExecStoreHeapTuple(), ForeignScanState::fdw_state, PgFdwDirectModifyState::has_returning, make_tuple_from_result_row(), PgFdwDirectModifyState::next_tuple, PgFdwDirectModifyState::num_tuples, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PQclear(), ScanState::ps, PgFdwDirectModifyState::rel, PgFdwDirectModifyState::result, ForeignScanState::resultRelInfo, PgFdwDirectModifyState::retrieved_attrs, PgFdwDirectModifyState::set_processed, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, and PgFdwDirectModifyState::temp_cxt.

Referenced by postgresIterateDirectModify().

◆ get_tupdesc_for_join_scan_tuples()

static TupleDesc get_tupdesc_for_join_scan_tuples ( ForeignScanState node)
static

Definition at line 1443 of file postgres_fdw.c.

1444 {
1445  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1446  EState *estate = node->ss.ps.state;
1447  TupleDesc tupdesc;
1448 
1449  /*
1450  * The core code has already set up a scan tuple slot based on
1451  * fsplan->fdw_scan_tlist, and this slot's tupdesc is mostly good enough,
1452  * but there's one case where it isn't. If we have any whole-row row
1453  * identifier Vars, they may have vartype RECORD, and we need to replace
1454  * that with the associated table's actual composite type. This ensures
1455  * that when we read those ROW() expression values from the remote server,
1456  * we can convert them to a composite type the local server knows.
1457  */
1459  for (int i = 0; i < tupdesc->natts; i++)
1460  {
1461  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
1462  Var *var;
1463  RangeTblEntry *rte;
1464  Oid reltype;
1465 
1466  /* Nothing to do if it's not a generic RECORD attribute */
1467  if (att->atttypid != RECORDOID || att->atttypmod >= 0)
1468  continue;
1469 
1470  /*
1471  * If we can't identify the referenced table, do nothing. This'll
1472  * likely lead to failure later, but perhaps we can muddle through.
1473  */
1474  var = (Var *) list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
1475  i)->expr;
1476  if (!IsA(var, Var) || var->varattno != 0)
1477  continue;
1478  rte = list_nth(estate->es_range_table, var->varno - 1);
1479  if (rte->rtekind != RTE_RELATION)
1480  continue;
1481  reltype = get_rel_type_id(rte->relid);
1482  if (!OidIsValid(reltype))
1483  continue;
1484  att->atttypid = reltype;
1485  /* shouldn't need to change anything else */
1486  }
1487  return tupdesc;
1488 }
#define OidIsValid(objectId)
Definition: c.h:754
Oid get_rel_type_id(Oid relid)
Definition: lsyscache.c:1979
@ RTE_RELATION
Definition: parsenodes.h:1017
RTEKind rtekind
Definition: parsenodes.h:1047
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:133

References CreateTupleDescCopy(), ForeignScan::fdw_scan_tlist, get_rel_type_id(), i, IsA, list_nth(), list_nth_node, OidIsValid, PlanState::plan, ScanState::ps, RangeTblEntry::relid, RTE_RELATION, RangeTblEntry::rtekind, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, TupleTableSlot::tts_tupleDescriptor, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by postgresBeginDirectModify(), and postgresBeginForeignScan().

◆ get_useful_ecs_for_relation()

static List * get_useful_ecs_for_relation ( PlannerInfo root,
RelOptInfo rel 
)
static

Definition at line 809 of file postgres_fdw.c.

810 {
811  List *useful_eclass_list = NIL;
812  ListCell *lc;
813  Relids relids;
814 
815  /*
816  * First, consider whether any active EC is potentially useful for a merge
817  * join against this relation.
818  */
819  if (rel->has_eclass_joins)
820  {
821  foreach(lc, root->eq_classes)
822  {
823  EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc);
824 
825  if (eclass_useful_for_merging(root, cur_ec, rel))
826  useful_eclass_list = lappend(useful_eclass_list, cur_ec);
827  }
828  }
829 
830  /*
831  * Next, consider whether there are any non-EC derivable join clauses that
832  * are merge-joinable. If the joininfo list is empty, we can exit
833  * quickly.
834  */
835  if (rel->joininfo == NIL)
836  return useful_eclass_list;
837 
838  /* If this is a child rel, we must use the topmost parent rel to search. */
839  if (IS_OTHER_REL(rel))
840  {
842  relids = rel->top_parent_relids;
843  }
844  else
845  relids = rel->relids;
846 
847  /* Check each join clause in turn. */
848  foreach(lc, rel->joininfo)
849  {
850  RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
851 
852  /* Consider only mergejoinable clauses */
853  if (restrictinfo->mergeopfamilies == NIL)
854  continue;
855 
856  /* Make sure we've got canonical ECs. */
857  update_mergeclause_eclasses(root, restrictinfo);
858 
859  /*
860  * restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
861  * that left_ec and right_ec will be initialized, per comments in
862  * distribute_qual_to_rels.
863  *
864  * We want to identify which side of this merge-joinable clause
865  * contains columns from the relation produced by this RelOptInfo. We
866  * test for overlap, not containment, because there could be extra
867  * relations on either side. For example, suppose we've got something
868  * like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
869  * A.y = D.y. The input rel might be the joinrel between A and B, and
870  * we'll consider the join clause A.y = D.y. relids contains a
871  * relation not involved in the join class (B) and the equivalence
872  * class for the left-hand side of the clause contains a relation not
873  * involved in the input rel (C). Despite the fact that we have only
874  * overlap and not containment in either direction, A.y is potentially
875  * useful as a sort column.
876  *
877  * Note that it's even possible that relids overlaps neither side of
878  * the join clause. For example, consider A LEFT JOIN B ON A.x = B.x
879  * AND A.x = 1. The clause A.x = 1 will appear in B's joininfo list,
880  * but overlaps neither side of B. In that case, we just skip this
881  * join clause, since it doesn't suggest a useful sort order for this
882  * relation.
883  */
884  if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
885  useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
886  restrictinfo->right_ec);
887  else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
888  useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
889  restrictinfo->left_ec);
890  }
891 
892  return useful_eclass_list;
893 }
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:582
bool eclass_useful_for_merging(PlannerInfo *root, EquivalenceClass *eclass, RelOptInfo *rel)
Definition: equivclass.c:3266
List * list_append_unique_ptr(List *list, void *datum)
Definition: list.c:1356
void update_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
Definition: pathkeys.c:1509
List * joininfo
Definition: pathnodes.h:991
bool has_eclass_joins
Definition: pathnodes.h:993

References Assert, bms_is_empty, bms_overlap(), eclass_useful_for_merging(), RelOptInfo::has_eclass_joins, IS_OTHER_REL, RelOptInfo::joininfo, lappend(), lfirst, list_append_unique_ptr(), NIL, RelOptInfo::relids, root, RelOptInfo::top_parent_relids, and update_mergeclause_eclasses().

Referenced by get_useful_pathkeys_for_relation().

◆ get_useful_pathkeys_for_relation()

static List * get_useful_pathkeys_for_relation ( PlannerInfo root,
RelOptInfo rel 
)
static

Definition at line 905 of file postgres_fdw.c.

906 {
907  List *useful_pathkeys_list = NIL;
908  List *useful_eclass_list;
909  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
910  EquivalenceClass *query_ec = NULL;
911  ListCell *lc;
912 
913  /*
914  * Pushing the query_pathkeys to the remote server is always worth
915  * considering, because it might let us avoid a local sort.
916  */
917  fpinfo->qp_is_pushdown_safe = false;
918  if (root->query_pathkeys)
919  {
920  bool query_pathkeys_ok = true;
921 
922  foreach(lc, root->query_pathkeys)
923  {
924  PathKey *pathkey = (PathKey *) lfirst(lc);
925 
926  /*
927  * The planner and executor don't have any clever strategy for
928  * taking data sorted by a prefix of the query's pathkeys and
929  * getting it to be sorted by all of those pathkeys. We'll just
930  * end up resorting the entire data set. So, unless we can push
931  * down all of the query pathkeys, forget it.
932  */
933  if (!is_foreign_pathkey(root, rel, pathkey))
934  {
935  query_pathkeys_ok = false;
936  break;
937  }
938  }
939 
940  if (query_pathkeys_ok)
941  {
942  useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
943  fpinfo->qp_is_pushdown_safe = true;
944  }
945  }
946 
947  /*
948  * Even if we're not using remote estimates, having the remote side do the
949  * sort generally won't be any worse than doing it locally, and it might
950  * be much better if the remote side can generate data in the right order
951  * without needing a sort at all. However, what we're going to do next is
952  * try to generate pathkeys that seem promising for possible merge joins,
953  * and that's more speculative. A wrong choice might hurt quite a bit, so
954  * bail out if we can't use remote estimates.
955  */
956  if (!fpinfo->use_remote_estimate)
957  return useful_pathkeys_list;
958 
959  /* Get the list of interesting EquivalenceClasses. */
960  useful_eclass_list = get_useful_ecs_for_relation(root, rel);
961 
962  /* Extract unique EC for query, if any, so we don't consider it again. */
963  if (list_length(root->query_pathkeys) == 1)
964  {
965  PathKey *query_pathkey = linitial(root->query_pathkeys);
966 
967  query_ec = query_pathkey->pk_eclass;
968  }
969 
970  /*
971  * As a heuristic, the only pathkeys we consider here are those of length
972  * one. It's surely possible to consider more, but since each one we
973  * choose to consider will generate a round-trip to the remote side, we
974  * need to be a bit cautious here. It would sure be nice to have a local
975  * cache of information about remote index definitions...
976  */
977  foreach(lc, useful_eclass_list)
978  {
979  EquivalenceClass *cur_ec = lfirst(lc);
980  PathKey *pathkey;
981 
982  /* If redundant with what we did above, skip it. */
983  if (cur_ec == query_ec)
984  continue;
985 
986  /* Can't push down the sort if the EC's opfamily is not shippable. */
988  OperatorFamilyRelationId, fpinfo))
989  continue;
990 
991  /* If no pushable expression for this rel, skip it. */
992  if (find_em_for_rel(root, cur_ec, rel) == NULL)
993  continue;
994 
995  /* Looks like we can generate a pathkey, so let's do it. */
996  pathkey = make_canonical_pathkey(root, cur_ec,
997  linitial_oid(cur_ec->ec_opfamilies),
999  false);
1000  useful_pathkeys_list = lappend(useful_pathkeys_list,
1001  list_make1(pathkey));
1002  }
1003 
1004  return useful_pathkeys_list;
1005 }
bool is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel, PathKey *pathkey)
Definition: deparse.c:1121
PathKey * make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, int strategy, bool nulls_first)
Definition: pathkeys.c:56
#define linitial(l)
Definition: pg_list.h:178
#define linitial_oid(l)
Definition: pg_list.h:180
EquivalenceMember * find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
static List * get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:809
#define BTLessStrategyNumber
Definition: stratnum.h:29
List * ec_opfamilies
Definition: pathnodes.h:1392

References BTLessStrategyNumber, EquivalenceClass::ec_opfamilies, find_em_for_rel(), get_useful_ecs_for_relation(), if(), is_foreign_pathkey(), is_shippable(), lappend(), lfirst, linitial, linitial_oid, list_copy(), list_length(), list_make1, make_canonical_pathkey(), NIL, PgFdwRelationInfo::qp_is_pushdown_safe, root, and PgFdwRelationInfo::use_remote_estimate.

Referenced by add_paths_with_pathkeys_for_rel().

◆ init_returning_filter()

static void init_returning_filter ( PgFdwDirectModifyState dmstate,
List fdw_scan_tlist,
Index  rtindex 
)
static

Definition at line 4684 of file postgres_fdw.c.

4687 {
4688  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4689  ListCell *lc;
4690  int i;
4691 
4692  /*
4693  * Calculate the mapping between the fdw_scan_tlist's entries and the
4694  * result tuple's attributes.
4695  *
4696  * The "map" is an array of indexes of the result tuple's attributes in
4697  * fdw_scan_tlist, i.e., one entry for every attribute of the result
4698  * tuple. We store zero for any attributes that don't have the
4699  * corresponding entries in that list, marking that a NULL is needed in
4700  * the result tuple.
4701  *
4702  * Also get the indexes of the entries for ctid and oid if any.
4703  */
4704  dmstate->attnoMap = (AttrNumber *)
4705  palloc0(resultTupType->natts * sizeof(AttrNumber));
4706 
4707  dmstate->ctidAttno = dmstate->oidAttno = 0;
4708 
4709  i = 1;
4710  dmstate->hasSystemCols = false;
4711  foreach(lc, fdw_scan_tlist)
4712  {
4713  TargetEntry *tle = (TargetEntry *) lfirst(lc);
4714  Var *var = (Var *) tle->expr;
4715 
4716  Assert(IsA(var, Var));
4717 
4718  /*
4719  * If the Var is a column of the target relation to be retrieved from
4720  * the foreign server, get the index of the entry.
4721  */
4722  if (var->varno == rtindex &&
4723  list_member_int(dmstate->retrieved_attrs, i))
4724  {
4725  int attrno = var->varattno;
4726 
4727  if (attrno < 0)
4728  {
4729  /*
4730  * We don't retrieve system columns other than ctid and oid.
4731  */
4732  if (attrno == SelfItemPointerAttributeNumber)
4733  dmstate->ctidAttno = i;
4734  else
4735  Assert(false);
4736  dmstate->hasSystemCols = true;
4737  }
4738  else
4739  {
4740  /*
4741  * We don't retrieve whole-row references to the target
4742  * relation either.
4743  */
4744  Assert(attrno > 0);
4745 
4746  dmstate->attnoMap[attrno - 1] = i;
4747  }
4748  }
4749  i++;
4750  }
4751 }
bool list_member_int(const List *list, int datum)
Definition: list.c:702

References Assert, PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, TargetEntry::expr, PgFdwDirectModifyState::hasSystemCols, i, IsA, lfirst, list_member_int(), TupleDescData::natts, PgFdwDirectModifyState::oidAttno, palloc0(), RelationGetDescr, PgFdwDirectModifyState::resultRel, PgFdwDirectModifyState::retrieved_attrs, SelfItemPointerAttributeNumber, Var::varattno, and Var::varno.

Referenced by postgresBeginDirectModify().

◆ make_tuple_from_result_row()

static HeapTuple make_tuple_from_result_row ( PGresult res,
int  row,
Relation  rel,
AttInMetadata attinmeta,
List retrieved_attrs,
ForeignScanState fsstate,
MemoryContext  temp_context 
)
static

Definition at line 7569 of file postgres_fdw.c.

7576 {
7577  HeapTuple tuple;
7578  TupleDesc tupdesc;
7579  Datum *values;
7580  bool *nulls;
7581  ItemPointer ctid = NULL;
7582  ConversionLocation errpos;
7583  ErrorContextCallback errcallback;
7584  MemoryContext oldcontext;
7585  ListCell *lc;
7586  int j;
7587 
7588  Assert(row < PQntuples(res));
7589 
7590  /*
7591  * Do the following work in a temp context that we reset after each tuple.
7592  * This cleans up not only the data we have direct access to, but any
7593  * cruft the I/O functions might leak.
7594  */
7595  oldcontext = MemoryContextSwitchTo(temp_context);
7596 
7597  /*
7598  * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
7599  * provided, otherwise look to the scan node's ScanTupleSlot.
7600  */
7601  if (rel)
7602  tupdesc = RelationGetDescr(rel);
7603  else
7604  {
7605  Assert(fsstate);
7606  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
7607  }
7608 
7609  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
7610  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
7611  /* Initialize to nulls for any columns not present in result */
7612  memset(nulls, true, tupdesc->natts * sizeof(bool));
7613 
7614  /*
7615  * Set up and install callback to report where conversion error occurs.
7616  */
7617  errpos.cur_attno = 0;
7618  errpos.rel = rel;
7619  errpos.fsstate = fsstate;
7620  errcallback.callback = conversion_error_callback;
7621  errcallback.arg = &errpos;
7622  errcallback.previous = error_context_stack;
7623  error_context_stack = &errcallback;
7624 
7625  /*
7626  * i indexes columns in the relation, j indexes columns in the PGresult.
7627  */
7628  j = 0;
7629  foreach(lc, retrieved_attrs)
7630  {
7631  int i = lfirst_int(lc);
7632  char *valstr;
7633 
7634  /* fetch next column's textual value */
7635  if (PQgetisnull(res, row, j))
7636  valstr = NULL;
7637  else
7638  valstr = PQgetvalue(res, row, j);
7639 
7640  /*
7641  * convert value to internal representation
7642  *
7643  * Note: we ignore system columns other than ctid and oid in result
7644  */
7645  errpos.cur_attno = i;
7646  if (i > 0)
7647  {
7648  /* ordinary column */
7649  Assert(i <= tupdesc->natts);
7650  nulls[i - 1] = (valstr == NULL);
7651  /* Apply the input function even to nulls, to support domains */
7652  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
7653  valstr,
7654  attinmeta->attioparams[i - 1],
7655  attinmeta->atttypmods[i - 1]);
7656  }
7657  else if (i == SelfItemPointerAttributeNumber)
7658  {
7659  /* ctid */
7660  if (valstr != NULL)
7661  {
7662  Datum datum;
7663 
7664  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
7665  ctid = (ItemPointer) DatumGetPointer(datum);
7666  }
7667  }
7668  errpos.cur_attno = 0;
7669 
7670  j++;
7671  }
7672 
7673  /* Uninstall error context callback. */
7674  error_context_stack = errcallback.previous;
7675 
7676  /*
7677  * Check we got the expected number of columns. Note: j == 0 and
7678  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
7679  */
7680  if (j > 0 && j != PQnfields(res))
7681  elog(ERROR, "remote query result does not match the foreign table");
7682 
7683  /*
7684  * Build the result tuple in caller's memory context.
7685  */
7686  MemoryContextSwitchTo(oldcontext);
7687 
7688  tuple = heap_form_tuple(tupdesc, values, nulls);
7689 
7690  /*
7691  * If we have a CTID to return, install it in both t_self and t_ctid.
7692  * t_self is the normal place, but if the tuple is converted to a
7693  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
7694  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
7695  */
7696  if (ctid)
7697  tuple->t_self = tuple->t_data->t_ctid = *ctid;
7698 
7699  /*
7700  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
7701  * heap_form_tuple. heap_form_tuple actually creates the tuple with
7702  * DatumTupleFields, not HeapTupleFields, but the executor expects
7703  * HeapTupleFields and will happily extract system columns on that
7704  * assumption. If we don't do this then, for example, the tuple length
7705  * ends up in the xmin field, which isn't what we want.
7706  */
7710 
7711  /* Clean up */
7712  MemoryContextReset(temp_context);
7713 
7714  return tuple;
7715 }
ErrorContextCallback * error_context_stack
Definition: elog.c:94
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3901
int PQnfields(const PGresult *res)
Definition: fe-exec.c:3489
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1530
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static void conversion_error_callback(void *arg)
FmgrInfo * attinfuncs
Definition: funcapi.h:41
Oid * attioparams
Definition: funcapi.h:44
int32 * atttypmods
Definition: funcapi.h:47
struct ErrorContextCallback * previous
Definition: elog.h:296
void(* callback)(void *arg)
Definition: elog.h:297
ItemPointerData t_ctid
Definition: htup_details.h:161
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:52

References ErrorContextCallback::arg, Assert, AttInMetadata::attinfuncs, AttInMetadata::attioparams, AttInMetadata::atttypmods, ErrorContextCallback::callback, conversion_error_callback(), CStringGetDatum(), ConversionLocation::cur_attno, DatumGetPointer(), DirectFunctionCall1, elog, ERROR, error_context_stack, ConversionLocation::fsstate, heap_form_tuple(), HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, i, InputFunctionCall(), InvalidTransactionId, j, lfirst_int, MemoryContextReset(), MemoryContextSwitchTo(), TupleDescData::natts, palloc(), palloc0(), PQgetisnull(), PQgetvalue(), PQnfields(), PQntuples(), ErrorContextCallback::previous, ConversionLocation::rel, RelationGetDescr, res, SelfItemPointerAttributeNumber, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleData::t_self, tidin(), TupleTableSlot::tts_tupleDescriptor, and values.

Referenced by analyze_row_processor(), fetch_more_data(), get_returning_data(), and store_returning_result().

◆ merge_fdw_options()

static void merge_fdw_options ( PgFdwRelationInfo fpinfo,
const PgFdwRelationInfo fpinfo_o,
const PgFdwRelationInfo fpinfo_i 
)
static

Definition at line 6280 of file postgres_fdw.c.

6283 {
6284  /* We must always have fpinfo_o. */
6285  Assert(fpinfo_o);
6286 
6287  /* fpinfo_i may be NULL, but if present the servers must both match. */
6288  Assert(!fpinfo_i ||
6289  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
6290 
6291  /*
6292  * Copy the server specific FDW options. (For a join, both relations come
6293  * from the same server, so the server options should have the same value
6294  * for both relations.)
6295  */
6296  fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
6297  fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
6298  fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
6299  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
6300  fpinfo->fetch_size = fpinfo_o->fetch_size;
6301  fpinfo->async_capable = fpinfo_o->async_capable;