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_class.h"
#include "catalog/pg_opfamily.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/vacuum.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/clauses.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 63 of file postgres_fdw.c.

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 57 of file postgres_fdw.c.

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.2

Definition at line 60 of file postgres_fdw.c.

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 123 of file postgres_fdw.c.

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 100 of file postgres_fdw.c.

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

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 283 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 72 of file postgres_fdw.c.

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

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 7016 of file postgres_fdw.c.

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

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

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

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 6101 of file postgres_fdw.c.

6103 {
6104  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6105  ListCell *lc;
6106 
6107  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
6108 
6109  /*
6110  * Before creating sorted paths, arrange for the passed-in EPQ path, if
6111  * any, to return columns needed by the parent ForeignScan node so that
6112  * they will propagate up through Sort nodes injected below, if necessary.
6113  */
6114  if (epq_path != NULL && useful_pathkeys_list != NIL)
6115  {
6116  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6117  PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6118 
6119  /* Include columns required for evaluating PHVs in the tlist. */
6121  pull_var_clause((Node *) target->exprs,
6123 
6124  /* Include columns required for evaluating the local conditions. */
6125  foreach(lc, fpinfo->local_conds)
6126  {
6127  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6128 
6130  pull_var_clause((Node *) rinfo->clause,
6132  }
6133 
6134  /*
6135  * If we have added any new columns, adjust the tlist of the EPQ path.
6136  *
6137  * Note: the plan created using this path will only be used to execute
6138  * EPQ checks, where accuracy of the plan cost and width estimates
6139  * would not be important, so we do not do set_pathtarget_cost_width()
6140  * for the new pathtarget here. See also postgresGetForeignPlan().
6141  */
6142  if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6143  {
6144  /* The EPQ path is a join path, so it is projection-capable. */
6146 
6147  /*
6148  * Use create_projection_path() here, so as to avoid modifying it
6149  * in place.
6150  */
6151  epq_path = (Path *) create_projection_path(root,
6152  rel,
6153  epq_path,
6154  target);
6155  }
6156  }
6157 
6158  /* Create one path for each set of pathkeys we found above. */
6159  foreach(lc, useful_pathkeys_list)
6160  {
6161  double rows;
6162  int width;
6163  int disabled_nodes;
6164  Cost startup_cost;
6165  Cost total_cost;
6166  List *useful_pathkeys = lfirst(lc);
6167  Path *sorted_epq_path;
6168 
6169  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
6170  &rows, &width, &disabled_nodes,
6171  &startup_cost, &total_cost);
6172 
6173  /*
6174  * The EPQ path must be at least as well sorted as the path itself, in
6175  * case it gets used as input to a mergejoin.
6176  */
6177  sorted_epq_path = epq_path;
6178  if (sorted_epq_path != NULL &&
6179  !pathkeys_contained_in(useful_pathkeys,
6180  sorted_epq_path->pathkeys))
6181  sorted_epq_path = (Path *)
6183  rel,
6184  sorted_epq_path,
6185  useful_pathkeys,
6186  -1.0);
6187 
6188  if (IS_SIMPLE_REL(rel))
6189  add_path(rel, (Path *)
6191  NULL,
6192  rows,
6193  disabled_nodes,
6194  startup_cost,
6195  total_cost,
6196  useful_pathkeys,
6197  rel->lateral_relids,
6198  sorted_epq_path,
6199  NIL, /* no fdw_restrictinfo
6200  * list */
6201  NIL));
6202  else
6203  add_path(rel, (Path *)
6205  NULL,
6206  rows,
6207  disabled_nodes,
6208  startup_cost,
6209  total_cost,
6210  useful_pathkeys,
6211  rel->lateral_relids,
6212  sorted_epq_path,
6213  restrictlist,
6214  NIL));
6215  }
6216 }
bool is_projection_capable_path(Path *path)
Definition: createplan.c:7221
#define PVC_RECURSE_PLACEHOLDERS
Definition: optimizer.h:191
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:342
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:908
Definition: nodes.h:129
Relids lateral_relids
Definition: pathnodes.h:913
Expr * clause
Definition: pathnodes.h:2571
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:612

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 3646 of file postgres_fdw.c.

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

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

4764 {
4765  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4766  TupleTableSlot *resultSlot;
4767  Datum *values;
4768  bool *isnull;
4769  Datum *old_values;
4770  bool *old_isnull;
4771  int i;
4772 
4773  /*
4774  * Use the return tuple slot as a place to store the result tuple.
4775  */
4776  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4777 
4778  /*
4779  * Extract all the values of the scan tuple.
4780  */
4781  slot_getallattrs(slot);
4782  old_values = slot->tts_values;
4783  old_isnull = slot->tts_isnull;
4784 
4785  /*
4786  * Prepare to build the result tuple.
4787  */
4788  ExecClearTuple(resultSlot);
4789  values = resultSlot->tts_values;
4790  isnull = resultSlot->tts_isnull;
4791 
4792  /*
4793  * Transpose data into proper fields of the result tuple.
4794  */
4795  for (i = 0; i < resultTupType->natts; i++)
4796  {
4797  int j = dmstate->attnoMap[i];
4798 
4799  if (j == 0)
4800  {
4801  values[i] = (Datum) 0;
4802  isnull[i] = true;
4803  }
4804  else
4805  {
4806  values[i] = old_values[j - 1];
4807  isnull[i] = old_isnull[j - 1];
4808  }
4809  }
4810 
4811  /*
4812  * Build the virtual tuple.
4813  */
4814  ExecStoreVirtualTuple(resultSlot);
4815 
4816  /*
4817  * If we have any system columns to return, materialize a heap tuple in
4818  * the slot from column values set above and install system columns in
4819  * that tuple.
4820  */
4821  if (dmstate->hasSystemCols)
4822  {
4823  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4824 
4825  /* ctid */
4826  if (dmstate->ctidAttno)
4827  {
4828  ItemPointer ctid = NULL;
4829 
4830  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4831  resultTup->t_self = *ctid;
4832  }
4833 
4834  /*
4835  * And remaining columns
4836  *
4837  * Note: since we currently don't allow the target relation to appear
4838  * on the nullable side of an outer join, any system columns wouldn't
4839  * go to NULL.
4840  *
4841  * Note: no need to care about tableoid here because it will be
4842  * initialized in ExecProcessReturning().
4843  */
4847  }
4848 
4849  /*
4850  * And return the result tuple.
4851  */
4852  return resultSlot;
4853 }
static Datum values[MAXATTR]
Definition: bootstrap.c:150
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:1182
#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:74
int i
Definition: isn.c:73
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 6224 of file postgres_fdw.c.

6225 {
6226  ListCell *lc;
6227 
6228  foreach(lc, fpinfo->server->options)
6229  {
6230  DefElem *def = (DefElem *) lfirst(lc);
6231 
6232  if (strcmp(def->defname, "use_remote_estimate") == 0)
6233  fpinfo->use_remote_estimate = defGetBoolean(def);
6234  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
6235  (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
6236  NULL);
6237  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
6238  (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
6239  NULL);
6240  else if (strcmp(def->defname, "extensions") == 0)
6241  fpinfo->shippable_extensions =
6242  ExtractExtensionList(defGetString(def), false);
6243  else if (strcmp(def->defname, "fetch_size") == 0)
6244  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6245  else if (strcmp(def->defname, "async_capable") == 0)
6246  fpinfo->async_capable = defGetBoolean(def);
6247  }
6248 }
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:445
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:2870
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:2960
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 6256 of file postgres_fdw.c.

6257 {
6258  ListCell *lc;
6259 
6260  foreach(lc, fpinfo->table->options)
6261  {
6262  DefElem *def = (DefElem *) lfirst(lc);
6263 
6264  if (strcmp(def->defname, "use_remote_estimate") == 0)
6265  fpinfo->use_remote_estimate = defGetBoolean(def);
6266  else if (strcmp(def->defname, "fetch_size") == 0)
6267  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6268  else if (strcmp(def->defname, "async_capable") == 0)
6269  fpinfo->async_capable = defGetBoolean(def);
6270  }
6271 }
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 4433 of file postgres_fdw.c.

4434 {
4435  bool have_wholerow = false;
4436  List *tlist = NIL;
4437  List *vars;
4438  ListCell *lc;
4439 
4440  Assert(returningList);
4441 
4442  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4443 
4444  /*
4445  * If there's a whole-row reference to the target relation, then we'll
4446  * need all the columns of the relation.
4447  */
4448  foreach(lc, vars)
4449  {
4450  Var *var = (Var *) lfirst(lc);
4451 
4452  if (IsA(var, Var) &&
4453  var->varno == rtindex &&
4454  var->varattno == InvalidAttrNumber)
4455  {
4456  have_wholerow = true;
4457  break;
4458  }
4459  }
4460 
4461  if (have_wholerow)
4462  {
4463  TupleDesc tupdesc = RelationGetDescr(rel);
4464  int i;
4465 
4466  for (i = 1; i <= tupdesc->natts; i++)
4467  {
4468  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4469  Var *var;
4470 
4471  /* Ignore dropped attributes. */
4472  if (attr->attisdropped)
4473  continue;
4474 
4475  var = makeVar(rtindex,
4476  i,
4477  attr->atttypid,
4478  attr->atttypmod,
4479  attr->attcollation,
4480  0);
4481 
4482  tlist = lappend(tlist,
4483  makeTargetEntry((Expr *) var,
4484  list_length(tlist) + 1,
4485  NULL,
4486  false));
4487  }
4488  }
4489 
4490  /* Now add any remaining columns to tlist. */
4491  foreach(lc, vars)
4492  {
4493  Var *var = (Var *) lfirst(lc);
4494 
4495  /*
4496  * No need for whole-row references to the target relation. We don't
4497  * need system columns other than ctid and oid either, since those are
4498  * set locally.
4499  */
4500  if (IsA(var, Var) &&
4501  var->varno == rtindex &&
4502  var->varattno <= InvalidAttrNumber &&
4504  continue; /* don't need it */
4505 
4506  if (tlist_member((Expr *) var, tlist))
4507  continue; /* already got it */
4508 
4509  tlist = lappend(tlist,
4510  makeTargetEntry((Expr *) var,
4511  list_length(tlist) + 1,
4512  NULL,
4513  false));
4514  }
4515 
4516  list_free(vars);
4517 
4518  return tlist;
4519 }
#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:190
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:281
#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 3955 of file postgres_fdw.c.

3957 {
3958  char sql[64];
3959  PGresult *res;
3960 
3961  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3962 
3963  /*
3964  * We don't use a PG_TRY block here, so be careful not to throw error
3965  * without releasing the PGresult.
3966  */
3967  res = pgfdw_exec_query(conn, sql, conn_state);
3969  pgfdw_report_error(ERROR, res, conn, true, sql);
3970  PQclear(res);
3971 }
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:889
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:853
static unsigned int cursor_number
Definition: connection.c:84
#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:55

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 7538 of file postgres_fdw.c.

7539 {
7540  /* The request would have been pending for a callback */
7541  Assert(areq->callback_pending);
7542 
7543  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7544  areq->callback_pending = false;
7545 
7546  /* We begin a fetch afterwards if necessary; don't fetch */
7547  produce_tuple_asynchronously(areq, false);
7548 
7549  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7550  ExecAsyncResponse(areq);
7551 
7552  /* Also, we do instrumentation ourselves, if required */
7553  if (areq->requestee->instrument)
7555  TupIsNull(areq->result) ? 0.0 : 1.0);
7556 }
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:612
bool callback_pending
Definition: execnodes.h:610
struct PlanState * requestee
Definition: execnodes.h:608
Instrumentation * instrument
Definition: execnodes.h:1129
#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 7729 of file postgres_fdw.c.

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

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

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

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 3979 of file postgres_fdw.c.

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

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 4404 of file postgres_fdw.c.

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

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 3702 of file postgres_fdw.c.

3705 {
3707  Expr *expr = em->em_expr;
3708 
3709  /*
3710  * If we've identified what we're processing in the current scan, we only
3711  * want to match that expression.
3712  */
3713  if (state->current != NULL)
3714  return equal(expr, state->current);
3715 
3716  /*
3717  * Otherwise, ignore anything we've already processed.
3718  */
3719  if (list_member(state->already_used, expr))
3720  return false;
3721 
3722  /* This is the new target to process. */
3723  state->current = expr;
3724  return true;
3725 }
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 3092 of file postgres_fdw.c.

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

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

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 4099 of file postgres_fdw.c.

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

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 3807 of file postgres_fdw.c.

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

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 7478 of file postgres_fdw.c.

7479 {
7480  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7481  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7482  char sql[64];
7483 
7484  Assert(!fsstate->conn_state->pendingAreq);
7485 
7486  /* Create the cursor synchronously. */
7487  if (!fsstate->cursor_exists)
7488  create_cursor(node);
7489 
7490  /* We will send this query, but not wait for the response. */
7491  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7492  fsstate->fetch_size, fsstate->cursor_number);
7493 
7494  if (!PQsendQuery(fsstate->conn, sql))
7495  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7496 
7497  /* Remember that the request is in process */
7498  fsstate->conn_state->pendingAreq = areq;
7499 }
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 7832 of file postgres_fdw.c.

7833 {
7834  ListCell *lc;
7835 
7836  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7837 
7838  foreach(lc, ec->ec_members)
7839  {
7841 
7842  /*
7843  * Note we require !bms_is_empty, else we'd accept constant
7844  * expressions which are not suitable for the purpose.
7845  */
7846  if (bms_is_subset(em->em_relids, rel->relids) &&
7847  !bms_is_empty(em->em_relids) &&
7849  is_foreign_expr(root, rel, em->em_expr))
7850  return em;
7851  }
7852 
7853  return NULL;
7854 }
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 7868 of file postgres_fdw.c.

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

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

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

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 6489 of file postgres_fdw.c.

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

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

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

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

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

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 1446 of file postgres_fdw.c.

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

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

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

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 4687 of file postgres_fdw.c.

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

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

6286 {
6287  /* We must always have fpinfo_o. */
6288  Assert(fpinfo_o);
6289 
6290  /* fpinfo_i may be NULL, but if present the servers must both match. */
6291  Assert(!fpinfo_i ||
6292  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
6293 
6294  /*
6295  * Copy the server specific FDW options. (For a join, both relations come
6296  * from the same server, so the server options should have the same value