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 "commands/defrem.h"
#include "commands/explain.h"
#include "commands/vacuum.h"
#include "foreign/fdwapi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.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 "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.01
 
#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, 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 (Query *parsetree, 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 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 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 void estimate_path_cost_size (PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, 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, 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)
 
static PgFdwModifyStatecreate_foreign_modify (EState *estate, RangeTblEntry *rte, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, bool has_returning, List *retrieved_attrs)
 
static TupleTableSlotexecute_foreign_modify (EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static void prepare_foreign_modify (PgFdwModifyState *fmstate)
 
static const char ** convert_prep_stmt_params (PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot *slot)
 
static void store_returning_result (PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
 
static void finish_foreign_modify (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 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)
 
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)
 
Datum postgres_fdw_handler (PG_FUNCTION_ARGS)
 
int set_transmission_modes (void)
 
void reset_transmission_modes (int nestlevel)
 
Exprfind_em_expr_for_input_target (PlannerInfo *root, EquivalenceClass *ec, PathTarget *target)
 

Variables

 PG_MODULE_MAGIC
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 58 of file postgres_fdw.c.

Referenced by adjust_foreign_grouping_path_cost(), and estimate_path_cost_size().

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 52 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.01

Definition at line 55 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 114 of file postgres_fdw.c.

115 {
116  /* SQL statement to execute remotely (as a String node) */
118  /* has-returning flag (as an integer Value node) */
120  /* Integer list of attribute numbers retrieved by RETURNING */
122  /* set-processed flag (as an integer Value node) */
124 };

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 93 of file postgres_fdw.c.

94 {
95  /* SQL statement to execute remotely (as a String node) */
97  /* Integer list of target attribute numbers for INSERT/UPDATE */
99  /* has-returning flag (as an integer Value node) */
101  /* Integer list of attribute numbers retrieved by RETURNING */
103 };

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 262 of file postgres_fdw.c.

263 {
264  /* has-final-sort flag (as an integer Value node) */
266  /* has-limit flag (as an integer Value node) */
268 };

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 67 of file postgres_fdw.c.

68 {
69  /* SQL statement to execute remotely (as a String node) */
71  /* Integer list of attribute numbers retrieved by the SELECT */
73  /* Integer representing the desired fetch_size */
75 
76  /*
77  * String describing join i.e. names of relations being joined and types
78  * of join, added when the scan is join
79  */
81 };

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

References add_path(), Assert, CMD_SELECT, Query::commandType, PgFdwPathExtraData::count_est, FinalPathExtraData::count_est, create_foreign_upper_path(), estimate_path_cost_size(), RelOptInfo::fdw_private, PgFdwPathExtraData::has_final_sort, PgFdwPathExtraData::has_limit, Query::hasTargetSRFs, is_foreign_expr(), IsA, lfirst, FinalPathExtraData::limit_needed, PgFdwPathExtraData::limit_tuples, FinalPathExtraData::limit_tuples, Query::limitCount, Query::limitOffset, list_make2, PgFdwRelationInfo::local_conds, makeInteger(), merge_fdw_options(), NIL, PgFdwPathExtraData::offset_est, FinalPathExtraData::offset_est, PgFdwRelationInfo::outerrel, palloc0(), parse(), PlannerInfo::parse, RelOptInfo::pathlist, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, Query::rowMarks, PgFdwRelationInfo::server, PlannerInfo::sort_pathkeys, PgFdwRelationInfo::stage, subpath(), PgFdwRelationInfo::table, PgFdwPathExtraData::target, PlannerInfo::upper_targets, UPPERREL_FINAL, UPPERREL_GROUP_AGG, UPPERREL_ORDERED, PgFdwRelationInfo::use_remote_estimate, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignUpperPaths().

6073 {
6074  Query *parse = root->parse;
6075  PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6076  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
6077  bool has_final_sort = false;
6078  List *pathkeys = NIL;
6079  PgFdwPathExtraData *fpextra;
6080  bool save_use_remote_estimate = false;
6081  double rows;
6082  int width;
6083  Cost startup_cost;
6084  Cost total_cost;
6085  List *fdw_private;
6086  ForeignPath *final_path;
6087 
6088  /*
6089  * Currently, we only support this for SELECT commands
6090  */
6091  if (parse->commandType != CMD_SELECT)
6092  return;
6093 
6094  /*
6095  * No work if there is no FOR UPDATE/SHARE clause and if there is no need
6096  * to add a LIMIT node
6097  */
6098  if (!parse->rowMarks && !extra->limit_needed)
6099  return;
6100 
6101  /* We don't support cases where there are any SRFs in the targetlist */
6102  if (parse->hasTargetSRFs)
6103  return;
6104 
6105  /* Save the input_rel as outerrel in fpinfo */
6106  fpinfo->outerrel = input_rel;
6107 
6108  /*
6109  * Copy foreign table, foreign server, user mapping, FDW options etc.
6110  * details from the input relation's fpinfo.
6111  */
6112  fpinfo->table = ifpinfo->table;
6113  fpinfo->server = ifpinfo->server;
6114  fpinfo->user = ifpinfo->user;
6115  merge_fdw_options(fpinfo, ifpinfo, NULL);
6116 
6117  /*
6118  * If there is no need to add a LIMIT node, there might be a ForeignPath
6119  * in the input_rel's pathlist that implements all behavior of the query.
6120  * Note: we would already have accounted for the query's FOR UPDATE/SHARE
6121  * (if any) before we get here.
6122  */
6123  if (!extra->limit_needed)
6124  {
6125  ListCell *lc;
6126 
6127  Assert(parse->rowMarks);
6128 
6129  /*
6130  * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
6131  * so the input_rel should be a base, join, or ordered relation; and
6132  * if it's an ordered relation, its input relation should be a base or
6133  * join relation.
6134  */
6135  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6136  input_rel->reloptkind == RELOPT_JOINREL ||
6137  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6138  ifpinfo->stage == UPPERREL_ORDERED &&
6139  (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
6140  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
6141 
6142  foreach(lc, input_rel->pathlist)
6143  {
6144  Path *path = (Path *) lfirst(lc);
6145 
6146  /*
6147  * apply_scanjoin_target_to_paths() uses create_projection_path()
6148  * to adjust each of its input paths if needed, whereas
6149  * create_ordered_paths() uses apply_projection_to_path() to do
6150  * that. So the former might have put a ProjectionPath on top of
6151  * the ForeignPath; look through ProjectionPath and see if the
6152  * path underneath it is ForeignPath.
6153  */
6154  if (IsA(path, ForeignPath) ||
6155  (IsA(path, ProjectionPath) &&
6156  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
6157  {
6158  /*
6159  * Create foreign final path; this gets rid of a
6160  * no-longer-needed outer plan (if any), which makes the
6161  * EXPLAIN output look cleaner
6162  */
6163  final_path = create_foreign_upper_path(root,
6164  path->parent,
6165  path->pathtarget,
6166  path->rows,
6167  path->startup_cost,
6168  path->total_cost,
6169  path->pathkeys,
6170  NULL, /* no extra plan */
6171  NULL); /* no fdw_private */
6172 
6173  /* and add it to the final_rel */
6174  add_path(final_rel, (Path *) final_path);
6175 
6176  /* Safe to push down */
6177  fpinfo->pushdown_safe = true;
6178 
6179  return;
6180  }
6181  }
6182 
6183  /*
6184  * If we get here it means no ForeignPaths; since we would already
6185  * have considered pushing down all operations for the query to the
6186  * remote server, give up on it.
6187  */
6188  return;
6189  }
6190 
6191  Assert(extra->limit_needed);
6192 
6193  /*
6194  * If the input_rel is an ordered relation, replace the input_rel with its
6195  * input relation
6196  */
6197  if (input_rel->reloptkind == RELOPT_UPPER_REL &&
6198  ifpinfo->stage == UPPERREL_ORDERED)
6199  {
6200  input_rel = ifpinfo->outerrel;
6201  ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6202  has_final_sort = true;
6203  pathkeys = root->sort_pathkeys;
6204  }
6205 
6206  /* The input_rel should be a base, join, or grouping relation */
6207  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6208  input_rel->reloptkind == RELOPT_JOINREL ||
6209  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6210  ifpinfo->stage == UPPERREL_GROUP_AGG));
6211 
6212  /*
6213  * We try to create a path below by extending a simple foreign path for
6214  * the underlying base, join, or grouping relation to perform the final
6215  * sort (if has_final_sort) and the LIMIT restriction remotely, which is
6216  * stored into the fdw_private list of the resulting path. (We
6217  * re-estimate the costs of sorting the underlying relation, if
6218  * has_final_sort.)
6219  */
6220 
6221  /*
6222  * Assess if it is safe to push down the LIMIT and OFFSET to the remote
6223  * server
6224  */
6225 
6226  /*
6227  * If the underlying relation has any local conditions, the LIMIT/OFFSET
6228  * cannot be pushed down.
6229  */
6230  if (ifpinfo->local_conds)
6231  return;
6232 
6233  /*
6234  * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
6235  * not safe to remote.
6236  */
6237  if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
6238  !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
6239  return;
6240 
6241  /* Safe to push down */
6242  fpinfo->pushdown_safe = true;
6243 
6244  /* Construct PgFdwPathExtraData */
6245  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6246  fpextra->target = root->upper_targets[UPPERREL_FINAL];
6247  fpextra->has_final_sort = has_final_sort;
6248  fpextra->has_limit = extra->limit_needed;
6249  fpextra->limit_tuples = extra->limit_tuples;
6250  fpextra->count_est = extra->count_est;
6251  fpextra->offset_est = extra->offset_est;
6252 
6253  /*
6254  * Estimate the costs of performing the final sort and the LIMIT
6255  * restriction remotely. If has_final_sort is false, we wouldn't need to
6256  * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
6257  * roughly estimated using the costs we already have for the underlying
6258  * relation, in the same way as when use_remote_estimate is false. Since
6259  * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
6260  * false in that case.
6261  */
6262  if (!fpextra->has_final_sort)
6263  {
6264  save_use_remote_estimate = ifpinfo->use_remote_estimate;
6265  ifpinfo->use_remote_estimate = false;
6266  }
6267  estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
6268  &rows, &width, &startup_cost, &total_cost);
6269  if (!fpextra->has_final_sort)
6270  ifpinfo->use_remote_estimate = save_use_remote_estimate;
6271 
6272  /*
6273  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6274  * Items in the list must match order in enum FdwPathPrivateIndex.
6275  */
6276  fdw_private = list_make2(makeInteger(has_final_sort),
6277  makeInteger(extra->limit_needed));
6278 
6279  /*
6280  * Create foreign final path; this gets rid of a no-longer-needed outer
6281  * plan (if any), which makes the EXPLAIN output look cleaner
6282  */
6283  final_path = create_foreign_upper_path(root,
6284  input_rel,
6286  rows,
6287  startup_cost,
6288  total_cost,
6289  pathkeys,
6290  NULL, /* no extra plan */
6291  fdw_private);
6292 
6293  /* and add it to the final_rel */
6294  add_path(final_rel, (Path *) final_path);
6295 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
Node * limitOffset
Definition: parsenodes.h:160
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
PathTarget * pathtarget
Definition: pathnodes.h:1149
Query * parse
Definition: pathnodes.h:173
ForeignPath * create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Path *fdw_outerpath, List *fdw_private)
Definition: pathnode.c:2227
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
RelOptKind reloptkind
Definition: pathnodes.h:663
PathTarget * target
Definition: postgres_fdw.c:273
ForeignServer * server
Definition: postgres_fdw.h:84
List * rowMarks
Definition: parsenodes.h:164
RelOptInfo * outerrel
Definition: postgres_fdw.h:100
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
Cost startup_cost
Definition: pathnodes.h:1159
RelOptInfo * parent
Definition: pathnodes.h:1148
Node * limitCount
Definition: parsenodes.h:161
List * sort_pathkeys
Definition: pathnodes.h:297
Value * makeInteger(int i)
Definition: value.c:23
UserMapping * user
Definition: postgres_fdw.h:85
void * palloc0(Size size)
Definition: mcxt.c:981
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
void * fdw_private
Definition: pathnodes.h:720
Cost total_cost
Definition: pathnodes.h:1160
CmdType commandType
Definition: parsenodes.h:112
bool hasTargetSRFs
Definition: parsenodes.h:127
List * pathkeys
Definition: pathnodes.h:1162
#define Assert(condition)
Definition: c.h:792
#define lfirst(lc)
Definition: pg_list.h:169
ForeignTable * table
Definition: postgres_fdw.h:83
double rows
Definition: pathnodes.h:1158
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
UpperRelationKind stage
Definition: postgres_fdw.h:107
List * pathlist
Definition: pathnodes.h:680
Definition: pg_list.h:50
double Cost
Definition: nodes.h:662
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:241
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:648
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: pathnodes.h:308

◆ add_foreign_grouping_paths()

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

Definition at line 5852 of file postgres_fdw.c.

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

Referenced by postgresGetForeignUpperPaths().

5855 {
5856  Query *parse = root->parse;
5857  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5858  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
5859  ForeignPath *grouppath;
5860  double rows;
5861  int width;
5862  Cost startup_cost;
5863  Cost total_cost;
5864 
5865  /* Nothing to be done, if there is no grouping or aggregation required. */
5866  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
5867  !root->hasHavingQual)
5868  return;
5869 
5872 
5873  /* save the input_rel as outerrel in fpinfo */
5874  fpinfo->outerrel = input_rel;
5875 
5876  /*
5877  * Copy foreign table, foreign server, user mapping, FDW options etc.
5878  * details from the input relation's fpinfo.
5879  */
5880  fpinfo->table = ifpinfo->table;
5881  fpinfo->server = ifpinfo->server;
5882  fpinfo->user = ifpinfo->user;
5883  merge_fdw_options(fpinfo, ifpinfo, NULL);
5884 
5885  /*
5886  * Assess if it is safe to push down aggregation and grouping.
5887  *
5888  * Use HAVING qual from extra. In case of child partition, it will have
5889  * translated Vars.
5890  */
5891  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
5892  return;
5893 
5894  /*
5895  * Compute the selectivity and cost of the local_conds, so we don't have
5896  * to do it over again for each path. (Currently we create just a single
5897  * path here, but in future it would be possible that we build more paths
5898  * such as pre-sorted paths as in postgresGetForeignPaths and
5899  * postgresGetForeignJoinPaths.) The best we can do for these conditions
5900  * is to estimate selectivity on the basis of local statistics.
5901  */
5902  fpinfo->local_conds_sel = clauselist_selectivity(root,
5903  fpinfo->local_conds,
5904  0,
5905  JOIN_INNER,
5906  NULL);
5907 
5908  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
5909 
5910  /* Estimate the cost of push down */
5911  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
5912  &rows, &width, &startup_cost, &total_cost);
5913 
5914  /* Now update this information in the fpinfo */
5915  fpinfo->rows = rows;
5916  fpinfo->width = width;
5917  fpinfo->startup_cost = startup_cost;
5918  fpinfo->total_cost = total_cost;
5919 
5920  /* Create and add foreign path to the grouping relation. */
5921  grouppath = create_foreign_upper_path(root,
5922  grouped_rel,
5923  grouped_rel->reltarget,
5924  rows,
5925  startup_cost,
5926  total_cost,
5927  NIL, /* no pathkeys */
5928  NULL,
5929  NIL); /* no fdw_private */
5930 
5931  /* Add generated path into grouped_rel by add_path(). */
5932  add_path(grouped_rel, (Path *) grouppath);
5933 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:173
ForeignPath * create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Path *fdw_outerpath, List *fdw_private)
Definition: pathnode.c:2227
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
bool hasAggs
Definition: parsenodes.h:125
ForeignServer * server
Definition: postgres_fdw.h:84
List * groupingSets
Definition: parsenodes.h:150
PartitionwiseAggregateType patype
Definition: pathnodes.h:2503
RelOptInfo * outerrel
Definition: postgres_fdw.h:100
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:4066
Selectivity local_conds_sel
Definition: postgres_fdw.h:56
UserMapping * user
Definition: postgres_fdw.h:85
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
void * fdw_private
Definition: pathnodes.h:720
#define Assert(condition)
Definition: c.h:792
ForeignTable * table
Definition: postgres_fdw.h:83
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
List * groupClause
Definition: parsenodes.h:148
bool hasHavingQual
Definition: pathnodes.h:341
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:102
struct PathTarget * reltarget
Definition: pathnodes.h:677
double Cost
Definition: nodes.h:662
QualCost local_conds_cost
Definition: postgres_fdw.h:55
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:648

◆ add_foreign_ordered_paths()

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

Definition at line 5943 of file postgres_fdw.c.

References add_path(), Assert, create_foreign_upper_path(), EquivalenceClass::ec_has_volatile, estimate_path_cost_size(), RelOptInfo::fdw_private, find_em_expr_for_input_target(), PgFdwPathExtraData::has_final_sort, Query::hasTargetSRFs, is_foreign_expr(), lfirst, list_make2, makeInteger(), merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, palloc0(), parse(), PlannerInfo::parse, PathKey::pk_eclass, PgFdwRelationInfo::pushdown_safe, PgFdwRelationInfo::qp_is_pushdown_safe, PlannerInfo::query_pathkeys, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, RelOptInfo::reltarget, PgFdwRelationInfo::server, PlannerInfo::sort_pathkeys, Query::sortClause, PgFdwRelationInfo::stage, PgFdwRelationInfo::table, PgFdwPathExtraData::target, PlannerInfo::upper_targets, UPPERREL_GROUP_AGG, UPPERREL_ORDERED, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignUpperPaths().

5945 {
5946  Query *parse = root->parse;
5947  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5948  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
5949  PgFdwPathExtraData *fpextra;
5950  double rows;
5951  int width;
5952  Cost startup_cost;
5953  Cost total_cost;
5954  List *fdw_private;
5955  ForeignPath *ordered_path;
5956  ListCell *lc;
5957 
5958  /* Shouldn't get here unless the query has ORDER BY */
5959  Assert(parse->sortClause);
5960 
5961  /* We don't support cases where there are any SRFs in the targetlist */
5962  if (parse->hasTargetSRFs)
5963  return;
5964 
5965  /* Save the input_rel as outerrel in fpinfo */
5966  fpinfo->outerrel = input_rel;
5967 
5968  /*
5969  * Copy foreign table, foreign server, user mapping, FDW options etc.
5970  * details from the input relation's fpinfo.
5971  */
5972  fpinfo->table = ifpinfo->table;
5973  fpinfo->server = ifpinfo->server;
5974  fpinfo->user = ifpinfo->user;
5975  merge_fdw_options(fpinfo, ifpinfo, NULL);
5976 
5977  /*
5978  * If the input_rel is a base or join relation, we would already have
5979  * considered pushing down the final sort to the remote server when
5980  * creating pre-sorted foreign paths for that relation, because the
5981  * query_pathkeys is set to the root->sort_pathkeys in that case (see
5982  * standard_qp_callback()).
5983  */
5984  if (input_rel->reloptkind == RELOPT_BASEREL ||
5985  input_rel->reloptkind == RELOPT_JOINREL)
5986  {
5987  Assert(root->query_pathkeys == root->sort_pathkeys);
5988 
5989  /* Safe to push down if the query_pathkeys is safe to push down */
5990  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
5991 
5992  return;
5993  }
5994 
5995  /* The input_rel should be a grouping relation */
5996  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
5997  ifpinfo->stage == UPPERREL_GROUP_AGG);
5998 
5999  /*
6000  * We try to create a path below by extending a simple foreign path for
6001  * the underlying grouping relation to perform the final sort remotely,
6002  * which is stored into the fdw_private list of the resulting path.
6003  */
6004 
6005  /* Assess if it is safe to push down the final sort */
6006  foreach(lc, root->sort_pathkeys)
6007  {
6008  PathKey *pathkey = (PathKey *) lfirst(lc);
6009  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6010  Expr *sort_expr;
6011 
6012  /*
6013  * is_foreign_expr would detect volatile expressions as well, but
6014  * checking ec_has_volatile here saves some cycles.
6015  */
6016  if (pathkey_ec->ec_has_volatile)
6017  return;
6018 
6019  /* Get the sort expression for the pathkey_ec */
6020  sort_expr = find_em_expr_for_input_target(root,
6021  pathkey_ec,
6022  input_rel->reltarget);
6023 
6024  /* If it's unsafe to remote, we cannot push down the final sort */
6025  if (!is_foreign_expr(root, input_rel, sort_expr))
6026  return;
6027  }
6028 
6029  /* Safe to push down */
6030  fpinfo->pushdown_safe = true;
6031 
6032  /* Construct PgFdwPathExtraData */
6033  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6034  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6035  fpextra->has_final_sort = true;
6036 
6037  /* Estimate the costs of performing the final sort remotely */
6038  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6039  &rows, &width, &startup_cost, &total_cost);
6040 
6041  /*
6042  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6043  * Items in the list must match order in enum FdwPathPrivateIndex.
6044  */
6045  fdw_private = list_make2(makeInteger(true), makeInteger(false));
6046 
6047  /* Create foreign ordering path */
6048  ordered_path = create_foreign_upper_path(root,
6049  input_rel,
6051  rows,
6052  startup_cost,
6053  total_cost,
6054  root->sort_pathkeys,
6055  NULL, /* no extra plan */
6056  fdw_private);
6057 
6058  /* and add it to the ordered_rel */
6059  add_path(ordered_rel, (Path *) ordered_path);
6060 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:173
ForeignPath * create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Path *fdw_outerpath, List *fdw_private)
Definition: pathnode.c:2227
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
List * sortClause
Definition: parsenodes.h:158
RelOptKind reloptkind
Definition: pathnodes.h:663
List * query_pathkeys
Definition: pathnodes.h:292
PathTarget * target
Definition: postgres_fdw.c:273
ForeignServer * server
Definition: postgres_fdw.h:84
RelOptInfo * outerrel
Definition: postgres_fdw.h:100
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
Expr * find_em_expr_for_input_target(PlannerInfo *root, EquivalenceClass *ec, PathTarget *target)
List * sort_pathkeys
Definition: pathnodes.h:297
Value * makeInteger(int i)
Definition: value.c:23
UserMapping * user
Definition: postgres_fdw.h:85
void * palloc0(Size size)
Definition: mcxt.c:981
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
void * fdw_private
Definition: pathnodes.h:720
bool hasTargetSRFs
Definition: parsenodes.h:127
#define Assert(condition)
Definition: c.h:792
#define lfirst(lc)
Definition: pg_list.h:169
ForeignTable * table
Definition: postgres_fdw.h:83
EquivalenceClass * pk_eclass
Definition: pathnodes.h:1045
bool ec_has_volatile
Definition: pathnodes.h:974
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
UpperRelationKind stage
Definition: postgres_fdw.h:107
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:677
double Cost
Definition: nodes.h:662
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:648
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: pathnodes.h:308

◆ add_paths_with_pathkeys_for_rel()

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

Definition at line 5253 of file postgres_fdw.c.

References add_path(), create_foreign_join_path(), create_foreignscan_path(), create_sort_path(), estimate_path_cost_size(), get_useful_pathkeys_for_relation(), IS_SIMPLE_REL, RelOptInfo::lateral_relids, lfirst, NIL, Path::pathkeys, and pathkeys_contained_in().

Referenced by postgresGetForeignJoinPaths(), and postgresGetForeignPaths().

5255 {
5256  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
5257  ListCell *lc;
5258 
5259  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
5260 
5261  /* Create one path for each set of pathkeys we found above. */
5262  foreach(lc, useful_pathkeys_list)
5263  {
5264  double rows;
5265  int width;
5266  Cost startup_cost;
5267  Cost total_cost;
5268  List *useful_pathkeys = lfirst(lc);
5269  Path *sorted_epq_path;
5270 
5271  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
5272  &rows, &width, &startup_cost, &total_cost);
5273 
5274  /*
5275  * The EPQ path must be at least as well sorted as the path itself, in
5276  * case it gets used as input to a mergejoin.
5277  */
5278  sorted_epq_path = epq_path;
5279  if (sorted_epq_path != NULL &&
5280  !pathkeys_contained_in(useful_pathkeys,
5281  sorted_epq_path->pathkeys))
5282  sorted_epq_path = (Path *)
5283  create_sort_path(root,
5284  rel,
5285  sorted_epq_path,
5286  useful_pathkeys,
5287  -1.0);
5288 
5289  if (IS_SIMPLE_REL(rel))
5290  add_path(rel, (Path *)
5291  create_foreignscan_path(root, rel,
5292  NULL,
5293  rows,
5294  startup_cost,
5295  total_cost,
5296  useful_pathkeys,
5297  rel->lateral_relids,
5298  sorted_epq_path,
5299  NIL));
5300  else
5301  add_path(rel, (Path *)
5302  create_foreign_join_path(root, rel,
5303  NULL,
5304  rows,
5305  startup_cost,
5306  total_cost,
5307  useful_pathkeys,
5308  rel->lateral_relids,
5309  sorted_epq_path,
5310  NIL));
5311  }
5312 }
#define NIL
Definition: pg_list.h:65
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:853
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:639
Relids lateral_relids
Definition: pathnodes.h:691
ForeignPath * create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_private)
Definition: pathnode.c:2133
ForeignPath * create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_private)
Definition: pathnode.c:2177
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:324
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2851
List * pathkeys
Definition: pathnodes.h:1162
#define lfirst(lc)
Definition: pg_list.h:169
Definition: pg_list.h:50
double Cost
Definition: nodes.h:662

◆ adjust_foreign_grouping_path_cost()

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

Definition at line 3228 of file postgres_fdw.c.

References cost_sort(), DEFAULT_FDW_SORT_MULTIPLIER, PlannerInfo::group_pathkeys, Query::groupClause, grouping_is_sortable(), PlannerInfo::parse, pathkeys_contained_in(), Path::startup_cost, Path::total_cost, and work_mem.

Referenced by estimate_path_cost_size().

3235 {
3236  /*
3237  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3238  * side is unlikely to generate properly-sorted output, so it would need
3239  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3240  * if the GROUP BY clause is sort-able but isn't a superset of the given
3241  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3242  * costs by applying the same heuristic as for the scan or join case.
3243  */
3244  if (!grouping_is_sortable(root->parse->groupClause) ||
3245  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3246  {
3247  Path sort_path; /* dummy for result of cost_sort */
3248 
3249  cost_sort(&sort_path,
3250  root,
3251  pathkeys,
3252  *p_startup_cost + *p_run_cost,
3253  retrieved_rows,
3254  width,
3255  0.0,
3256  work_mem,
3257  limit_tuples);
3258 
3259  *p_startup_cost = sort_path.startup_cost;
3260  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3261  }
3262  else
3263  {
3264  /*
3265  * The default extra cost seems too large for foreign-grouping cases;
3266  * add 1/4th of that default.
3267  */
3268  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3269  - 1.0) * 0.25;
3270 
3271  *p_startup_cost *= sort_multiplier;
3272  *p_run_cost *= sort_multiplier;
3273  }
3274 }
List * group_pathkeys
Definition: pathnodes.h:294
Query * parse
Definition: pathnodes.h:173
Cost startup_cost
Definition: pathnodes.h:1159
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:324
void cost_sort(Path *path, PlannerInfo *root, List *pathkeys, Cost input_cost, double tuples, int width, Cost comparison_cost, int sort_mem, double limit_tuples)
Definition: costsize.c:1937
int work_mem
Definition: globals.c:122
Cost total_cost
Definition: pathnodes.h:1160
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:58
List * groupClause
Definition: parsenodes.h:148
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:557

◆ analyze_row_processor()

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

Definition at line 4646 of file postgres_fdw.c.

References PgFdwAnalyzeState::anl_cxt, Assert, PgFdwAnalyzeState::attinmeta, heap_freetuple(), make_tuple_from_result_row(), MemoryContextSwitchTo(), PgFdwAnalyzeState::numrows, ReservoirStateData::randstate, PgFdwAnalyzeState::rel, 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().

4647 {
4648  int targrows = astate->targrows;
4649  int pos; /* array index to store tuple in */
4650  MemoryContext oldcontext;
4651 
4652  /* Always increment sample row counter. */
4653  astate->samplerows += 1;
4654 
4655  /*
4656  * Determine the slot where this sample row should be stored. Set pos to
4657  * negative value to indicate the row should be skipped.
4658  */
4659  if (astate->numrows < targrows)
4660  {
4661  /* First targrows rows are always included into the sample */
4662  pos = astate->numrows++;
4663  }
4664  else
4665  {
4666  /*
4667  * Now we start replacing tuples in the sample until we reach the end
4668  * of the relation. Same algorithm as in acquire_sample_rows in
4669  * analyze.c; see Jeff Vitter's paper.
4670  */
4671  if (astate->rowstoskip < 0)
4672  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
4673 
4674  if (astate->rowstoskip <= 0)
4675  {
4676  /* Choose a random reservoir element to replace. */
4677  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
4678  Assert(pos >= 0 && pos < targrows);
4679  heap_freetuple(astate->rows[pos]);
4680  }
4681  else
4682  {
4683  /* Skip this tuple. */
4684  pos = -1;
4685  }
4686 
4687  astate->rowstoskip -= 1;
4688  }
4689 
4690  if (pos >= 0)
4691  {
4692  /*
4693  * Create sample tuple from current result row, and store it in the
4694  * position determined above. The tuple has to be created in anl_cxt.
4695  */
4696  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
4697 
4698  astate->rows[pos] = make_tuple_from_result_row(res, row,
4699  astate->rel,
4700  astate->attinmeta,
4701  astate->retrieved_attrs,
4702  NULL,
4703  astate->temp_cxt);
4704 
4705  MemoryContextSwitchTo(oldcontext);
4706  }
4707 }
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
HeapTuple * rows
Definition: postgres_fdw.c:241
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
double sampler_random_fract(SamplerRandomState randstate)
Definition: sampling.c:242
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
ReservoirStateData rstate
Definition: postgres_fdw.c:248
AttInMetadata * attinmeta
Definition: postgres_fdw.c:237
MemoryContext temp_cxt
Definition: postgres_fdw.c:252
#define Assert(condition)
Definition: c.h:792
MemoryContext anl_cxt
Definition: postgres_fdw.c:251
SamplerRandomState randstate
Definition: sampling.h:50
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:146

◆ apply_returning_filter()

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

Definition at line 4226 of file postgres_fdw.c.

References PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, DatumGetPointer, ExecClearTuple(), ExecFetchSlotHeapTuple(), ExecGetReturningSlot(), ExecStoreVirtualTuple(), PgFdwDirectModifyState::hasSystemCols, HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, i, InvalidTransactionId, 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().

4230 {
4231  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4232  TupleTableSlot *resultSlot;
4233  Datum *values;
4234  bool *isnull;
4235  Datum *old_values;
4236  bool *old_isnull;
4237  int i;
4238 
4239  /*
4240  * Use the return tuple slot as a place to store the result tuple.
4241  */
4242  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4243 
4244  /*
4245  * Extract all the values of the scan tuple.
4246  */
4247  slot_getallattrs(slot);
4248  old_values = slot->tts_values;
4249  old_isnull = slot->tts_isnull;
4250 
4251  /*
4252  * Prepare to build the result tuple.
4253  */
4254  ExecClearTuple(resultSlot);
4255  values = resultSlot->tts_values;
4256  isnull = resultSlot->tts_isnull;
4257 
4258  /*
4259  * Transpose data into proper fields of the result tuple.
4260  */
4261  for (i = 0; i < resultTupType->natts; i++)
4262  {
4263  int j = dmstate->attnoMap[i];
4264 
4265  if (j == 0)
4266  {
4267  values[i] = (Datum) 0;
4268  isnull[i] = true;
4269  }
4270  else
4271  {
4272  values[i] = old_values[j - 1];
4273  isnull[i] = old_isnull[j - 1];
4274  }
4275  }
4276 
4277  /*
4278  * Build the virtual tuple.
4279  */
4280  ExecStoreVirtualTuple(resultSlot);
4281 
4282  /*
4283  * If we have any system columns to return, materialize a heap tuple in
4284  * the slot from column values set above and install system columns in
4285  * that tuple.
4286  */
4287  if (dmstate->hasSystemCols)
4288  {
4289  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4290 
4291  /* ctid */
4292  if (dmstate->ctidAttno)
4293  {
4294  ItemPointer ctid = NULL;
4295 
4296  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4297  resultTup->t_self = *ctid;
4298  }
4299 
4300  /*
4301  * And remaining columns
4302  *
4303  * Note: since we currently don't allow the target relation to appear
4304  * on the nullable side of an outer join, any system columns wouldn't
4305  * go to NULL.
4306  *
4307  * Note: no need to care about tableoid here because it will be
4308  * initialized in ExecProcessReturning().
4309  */
4313  }
4314 
4315  /*
4316  * And return the result tuple.
4317  */
4318  return resultSlot;
4319 }
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1209
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define RelationGetDescr(relation)
Definition: rel.h:483
Datum * tts_values
Definition: tuptable.h:126
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
ItemPointerData t_self
Definition: htup.h:65
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:354
bool * tts_isnull
Definition: tuptable.h:128
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:380
#define InvalidTransactionId
Definition: transam.h:31
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1614
uintptr_t Datum
Definition: postgres.h:367
#define DatumGetPointer(X)
Definition: postgres.h:549
static Datum values[MAXATTR]
Definition: bootstrap.c:165
int i
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:397
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1522
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:319

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 5320 of file postgres_fdw.c.

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

Referenced by postgresGetForeignRelSize().

5321 {
5322  ListCell *lc;
5323 
5324  foreach(lc, fpinfo->server->options)
5325  {
5326  DefElem *def = (DefElem *) lfirst(lc);
5327 
5328  if (strcmp(def->defname, "use_remote_estimate") == 0)
5329  fpinfo->use_remote_estimate = defGetBoolean(def);
5330  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
5331  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
5332  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
5333  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
5334  else if (strcmp(def->defname, "extensions") == 0)
5335  fpinfo->shippable_extensions =
5336  ExtractExtensionList(defGetString(def), false);
5337  else if (strcmp(def->defname, "fetch_size") == 0)
5338  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5339  }
5340 }
ForeignServer * server
Definition: postgres_fdw.h:84
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * defGetString(DefElem *def)
Definition: define.c:49
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:364
#define lfirst(lc)
Definition: pg_list.h:169
char * defname
Definition: parsenodes.h:733
List * shippable_extensions
Definition: postgres_fdw.h:80
List * options
Definition: foreign.h:42

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 5348 of file postgres_fdw.c.

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

Referenced by postgresGetForeignRelSize().

5349 {
5350  ListCell *lc;
5351 
5352  foreach(lc, fpinfo->table->options)
5353  {
5354  DefElem *def = (DefElem *) lfirst(lc);
5355 
5356  if (strcmp(def->defname, "use_remote_estimate") == 0)
5357  fpinfo->use_remote_estimate = defGetBoolean(def);
5358  else if (strcmp(def->defname, "fetch_size") == 0)
5359  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5360  }
5361 }
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * defGetString(DefElem *def)
Definition: define.c:49
#define lfirst(lc)
Definition: pg_list.h:169
ForeignTable * table
Definition: postgres_fdw.h:83
List * options
Definition: foreign.h:57
char * defname
Definition: parsenodes.h:733

◆ build_remote_returning()

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

Definition at line 3902 of file postgres_fdw.c.

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(), PgFdwScanState::tupdesc, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by postgresPlanDirectModify().

3903 {
3904  bool have_wholerow = false;
3905  List *tlist = NIL;
3906  List *vars;
3907  ListCell *lc;
3908 
3909  Assert(returningList);
3910 
3911  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
3912 
3913  /*
3914  * If there's a whole-row reference to the target relation, then we'll
3915  * need all the columns of the relation.
3916  */
3917  foreach(lc, vars)
3918  {
3919  Var *var = (Var *) lfirst(lc);
3920 
3921  if (IsA(var, Var) &&
3922  var->varno == rtindex &&
3923  var->varattno == InvalidAttrNumber)
3924  {
3925  have_wholerow = true;
3926  break;
3927  }
3928  }
3929 
3930  if (have_wholerow)
3931  {
3932  TupleDesc tupdesc = RelationGetDescr(rel);
3933  int i;
3934 
3935  for (i = 1; i <= tupdesc->natts; i++)
3936  {
3937  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
3938  Var *var;
3939 
3940  /* Ignore dropped attributes. */
3941  if (attr->attisdropped)
3942  continue;
3943 
3944  var = makeVar(rtindex,
3945  i,
3946  attr->atttypid,
3947  attr->atttypmod,
3948  attr->attcollation,
3949  0);
3950 
3951  tlist = lappend(tlist,
3952  makeTargetEntry((Expr *) var,
3953  list_length(tlist) + 1,
3954  NULL,
3955  false));
3956  }
3957  }
3958 
3959  /* Now add any remaining columns to tlist. */
3960  foreach(lc, vars)
3961  {
3962  Var *var = (Var *) lfirst(lc);
3963 
3964  /*
3965  * No need for whole-row references to the target relation. We don't
3966  * need system columns other than ctid and oid either, since those are
3967  * set locally.
3968  */
3969  if (IsA(var, Var) &&
3970  var->varno == rtindex &&
3971  var->varattno <= InvalidAttrNumber &&
3973  continue; /* don't need it */
3974 
3975  if (tlist_member((Expr *) var, tlist))
3976  continue; /* already got it */
3977 
3978  tlist = lappend(tlist,
3979  makeTargetEntry((Expr *) var,
3980  list_length(tlist) + 1,
3981  NULL,
3982  false));
3983  }
3984 
3985  list_free(vars);
3986 
3987  return tlist;
3988 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
#define RelationGetDescr(relation)
Definition: rel.h:483
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
Definition: nodes.h:528
AttrNumber varattno
Definition: primnodes.h:186
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
Definition: primnodes.h:181
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:238
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
List * lappend(List *list, void *datum)
Definition: list.c:321
Index varno
Definition: primnodes.h:184
#define PVC_INCLUDE_PLACEHOLDERS
Definition: optimizer.h:186
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:68
#define Assert(condition)
Definition: c.h:792
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
#define InvalidAttrNumber
Definition: attnum.h:23
void list_free(List *list)
Definition: list.c:1376
int i
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
Definition: regcomp.c:224
Definition: pg_list.h:50

◆ close_cursor()

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

Definition at line 3503 of file postgres_fdw.c.

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

Referenced by postgresAcquireSampleRowsFunc(), and postgresEndForeignScan().

3504 {
3505  char sql[64];
3506  PGresult *res;
3507 
3508  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3509 
3510  /*
3511  * We don't use a PG_TRY block here, so be careful not to throw error
3512  * without releasing the PGresult.
3513  */
3514  res = pgfdw_exec_query(conn, sql);
3515  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3516  pgfdw_report_error(ERROR, res, conn, true, sql);
3517  PQclear(res);
3518 }
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2651
#define ERROR
Definition: elog.h:45
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:737
static unsigned int cursor_number
Definition: connection.c:73
void PQclear(PGresult *res)
Definition: fe-exec.c:676
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:648
#define snprintf
Definition: port.h:215

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

Definition at line 6455 of file postgres_fdw.c.

References attname, castNode, ConversionLocation::cur_attno, errcontext, exec_rt_fetch(), TargetEntry::expr, ForeignScan::fdw_scan_tlist, ConversionLocation::fsstate, get_attname(), get_rel_name(), IsA, list_nth_node, NameStr, TupleDescData::natts, PlanState::plan, ScanState::ps, ConversionLocation::rel, RelationGetDescr, RelationGetRelationName, RangeTblEntry::relid, relname, SelfItemPointerAttributeNumber, ForeignScanState::ss, PlanState::state, PgFdwScanState::tupdesc, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by make_tuple_from_result_row().

6456 {
6457  const char *attname = NULL;
6458  const char *relname = NULL;
6459  bool is_wholerow = false;
6461 
6462  if (errpos->rel)
6463  {
6464  /* error occurred in a scan against a foreign table */
6465  TupleDesc tupdesc = RelationGetDescr(errpos->rel);
6466  Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
6467 
6468  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
6469  attname = NameStr(attr->attname);
6470  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
6471  attname = "ctid";
6472 
6473  relname = RelationGetRelationName(errpos->rel);
6474  }
6475  else
6476  {
6477  /* error occurred in a scan against a foreign join */
6478  ForeignScanState *fsstate = errpos->fsstate;
6479  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
6480  EState *estate = fsstate->ss.ps.state;
6481  TargetEntry *tle;
6482 
6483  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
6484  errpos->cur_attno - 1);
6485 
6486  /*
6487  * Target list can have Vars and expressions. For Vars, we can get
6488  * its relation, however for expressions we can't. Thus for
6489  * expressions, just show generic context message.
6490  */
6491  if (IsA(tle->expr, Var))
6492  {
6493  RangeTblEntry *rte;
6494  Var *var = (Var *) tle->expr;
6495 
6496  rte = exec_rt_fetch(var->varno, estate);
6497 
6498  if (var->varattno == 0)
6499  is_wholerow = true;
6500  else
6501  attname = get_attname(rte->relid, var->varattno, false);
6502 
6503  relname = get_rel_name(rte->relid);
6504  }
6505  else
6506  errcontext("processing expression at position %d in select list",
6507  errpos->cur_attno);
6508  }
6509 
6510  if (relname)
6511  {
6512  if (is_wholerow)
6513  errcontext("whole-row reference to foreign table \"%s\"", relname);
6514  else if (attname)
6515  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
6516  }
6517 }
ScanState ss
Definition: execnodes.h:1779
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
#define RelationGetDescr(relation)
Definition: rel.h:483
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
AttrNumber varattno
Definition: primnodes.h:186
List * fdw_scan_tlist
Definition: plannodes.h:619
EState * state
Definition: execnodes.h:930
NameData relname
Definition: pg_class.h:38
Definition: primnodes.h:181
PlanState ps
Definition: execnodes.h:1317
ForeignScanState * fsstate
Definition: postgres_fdw.c:295
NameData attname
Definition: pg_attribute.h:40
#define list_nth_node(type, list, n)
Definition: pg_list.h:294
#define RelationGetRelationName(relation)
Definition: rel.h:491
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:547
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
Index varno
Definition: primnodes.h:184
Plan * plan
Definition: execnodes.h:928
Expr * expr
Definition: primnodes.h:1431
#define errcontext
Definition: elog.h:199
#define NameStr(name)
Definition: c.h:669
void * arg
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:825
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
AttrNumber cur_attno
Definition: postgres_fdw.c:287
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1872

◆ convert_prep_stmt_params()

static const char ** convert_prep_stmt_params ( PgFdwModifyState fmstate,
ItemPointer  tupleid,
TupleTableSlot slot 
)
static

Definition at line 3772 of file postgres_fdw.c.

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

Referenced by execute_foreign_modify().

3775 {
3776  const char **p_values;
3777  int pindex = 0;
3778  MemoryContext oldcontext;
3779 
3780  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
3781 
3782  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums);
3783 
3784  /* 1st parameter should be ctid, if it's in use */
3785  if (tupleid != NULL)
3786  {
3787  /* don't need set_transmission_modes for TID output */
3788  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3789  PointerGetDatum(tupleid));
3790  pindex++;
3791  }
3792 
3793  /* get following parameters from slot */
3794  if (slot != NULL && fmstate->target_attrs != NIL)
3795  {
3796  int nestlevel;
3797  ListCell *lc;
3798 
3799  nestlevel = set_transmission_modes();
3800 
3801  foreach(lc, fmstate->target_attrs)
3802  {
3803  int attnum = lfirst_int(lc);
3804  Datum value;
3805  bool isnull;
3806 
3807  value = slot_getattr(slot, attnum, &isnull);
3808  if (isnull)
3809  p_values[pindex] = NULL;
3810  else
3811  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3812  value);
3813  pindex++;
3814  }
3815 
3816  reset_transmission_modes(nestlevel);
3817  }
3818 
3819  Assert(pindex == fmstate->p_nums);
3820 
3821  MemoryContextSwitchTo(oldcontext);
3822 
3823  return p_values;
3824 }
#define NIL
Definition: pg_list.h:65
#define PointerGetDatum(X)
Definition: postgres.h:556
static struct @144 value
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int set_transmission_modes(void)
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1576
#define lfirst_int(lc)
Definition: pg_list.h:170
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:186
uintptr_t Datum
Definition: postgres.h:367
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:381
int16 attnum
Definition: pg_attribute.h:79
MemoryContext temp_cxt
Definition: postgres_fdw.c:189
#define Assert(condition)
Definition: c.h:792
void reset_transmission_modes(int nestlevel)
void * palloc(Size size)
Definition: mcxt.c:950

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

Definition at line 3311 of file postgres_fdw.c.

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

Referenced by postgresIterateForeignScan().

3312 {
3313  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3314  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3315  int numParams = fsstate->numParams;
3316  const char **values = fsstate->param_values;
3317  PGconn *conn = fsstate->conn;
3319  PGresult *res;
3320 
3321  /*
3322  * Construct array of query parameter values in text format. We do the
3323  * conversions in the short-lived per-tuple context, so as not to cause a
3324  * memory leak over repeated scans.
3325  */
3326  if (numParams > 0)
3327  {
3328  MemoryContext oldcontext;
3329 
3330  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3331 
3332  process_query_params(econtext,
3333  fsstate->param_flinfo,
3334  fsstate->param_exprs,
3335  values);
3336 
3337  MemoryContextSwitchTo(oldcontext);
3338  }
3339 
3340  /* Construct the DECLARE CURSOR command */
3341  initStringInfo(&buf);
3342  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3343  fsstate->cursor_number, fsstate->query);
3344 
3345  /*
3346  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3347  * to infer types for all parameters. Since we explicitly cast every
3348  * parameter (see deparse.c), the "inference" is trivial and will produce
3349  * the desired result. This allows us to avoid assuming that the remote
3350  * server has the same OIDs we do for the parameters' types.
3351  */
3352  if (!PQsendQueryParams(conn, buf.data, numParams,
3353  NULL, values, NULL, NULL, 0))
3354  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3355 
3356  /*
3357  * Get the result, and check for success.
3358  *
3359  * We don't use a PG_TRY block here, so be careful not to throw error
3360  * without releasing the PGresult.
3361  */
3362  res = pgfdw_get_result(conn, buf.data);
3363  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3364  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3365  PQclear(res);
3366 
3367  /* Mark the cursor as created, and show no tuples have been retrieved */
3368  fsstate->cursor_exists = true;
3369  fsstate->tuples = NULL;
3370  fsstate->num_tuples = 0;
3371  fsstate->next_tuple = 0;
3372  fsstate->fetch_ct_2 = 0;
3373  fsstate->eof_reached = false;
3374 
3375  /* Clean up */
3376  pfree(buf.data);
3377 }
ScanState ss
Definition: execnodes.h:1779
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:1250
List * param_exprs
Definition: postgres_fdw.c:146
ExprContext * ps_ExprContext
Definition: execnodes.h:967
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
static void process_query_params(ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2651
unsigned int cursor_number
Definition: postgres_fdw.c:142
PlanState ps
Definition: execnodes.h:1317
void pfree(void *pointer)
Definition: mcxt.c:1057
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
#define ERROR
Definition: elog.h:45
const char ** param_values
Definition: postgres_fdw.c:147
PGconn * conn
Definition: streamutil.c:54
static char * buf
Definition: pg_test_fsync.c:68
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:737
FmgrInfo * param_flinfo
Definition: postgres_fdw.c:145
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
void PQclear(PGresult *res)
Definition: fe-exec.c:676
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:672
HeapTuple * tuples
Definition: postgres_fdw.c:150
static Datum values[MAXATTR]
Definition: bootstrap.c:165

◆ create_foreign_modify()

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

Definition at line 3526 of file postgres_fdw.c.

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

Referenced by postgresBeginForeignInsert(), and postgresBeginForeignModify().

3535 {
3536  PgFdwModifyState *fmstate;
3537  Relation rel = resultRelInfo->ri_RelationDesc;
3538  TupleDesc tupdesc = RelationGetDescr(rel);
3539  Oid userid;
3540  ForeignTable *table;
3541  UserMapping *user;
3542  AttrNumber n_params;
3543  Oid typefnoid;
3544  bool isvarlena;
3545  ListCell *lc;
3546 
3547  /* Begin constructing PgFdwModifyState. */
3548  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3549  fmstate->rel = rel;
3550 
3551  /*
3552  * Identify which user to do the remote access as. This should match what
3553  * ExecCheckRTEPerms() does.
3554  */
3555  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
3556 
3557  /* Get info about foreign table. */
3558  table = GetForeignTable(RelationGetRelid(rel));
3559  user = GetUserMapping(userid, table->serverid);
3560 
3561  /* Open connection; report that we'll create a prepared statement. */
3562  fmstate->conn = GetConnection(user, true);
3563  fmstate->p_name = NULL; /* prepared statement not made yet */
3564 
3565  /* Set up remote query information. */
3566  fmstate->query = query;
3567  fmstate->target_attrs = target_attrs;
3568  fmstate->has_returning = has_returning;
3569  fmstate->retrieved_attrs = retrieved_attrs;
3570 
3571  /* Create context for per-tuple temp workspace. */
3572  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
3573  "postgres_fdw temporary data",
3575 
3576  /* Prepare for input conversion of RETURNING results. */
3577  if (fmstate->has_returning)
3578  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
3579 
3580  /* Prepare for output conversion of parameters used in prepared stmt. */
3581  n_params = list_length(fmstate->target_attrs) + 1;
3582  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
3583  fmstate->p_nums = 0;
3584 
3585  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3586  {
3587  Assert(subplan != NULL);
3588 
3589  /* Find the ctid resjunk column in the subplan's result */
3591  "ctid");
3592  if (!AttributeNumberIsValid(fmstate->ctidAttno))
3593  elog(ERROR, "could not find junk ctid column");
3594 
3595  /* First transmittable parameter will be ctid */
3596  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
3597  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3598  fmstate->p_nums++;
3599  }
3600 
3601  if (operation == CMD_INSERT || operation == CMD_UPDATE)
3602  {
3603  /* Set up for remaining transmittable parameters */
3604  foreach(lc, fmstate->target_attrs)
3605  {
3606  int attnum = lfirst_int(lc);
3607  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
3608 
3609  Assert(!attr->attisdropped);
3610 
3611  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
3612  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3613  fmstate->p_nums++;
3614  }
3615  }
3616 
3617  Assert(fmstate->p_nums <= n_params);
3618 
3619  /* Initialize auxiliary state */
3620  fmstate->aux_fmstate = NULL;
3621 
3622  return fmstate;
3623 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:412
#define AllocSetContextCreate
Definition: memutils.h:170
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2827
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:246
#define RelationGetDescr(relation)
Definition: rel.h:483
Oid GetUserId(void)
Definition: miscinit.c:476
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:202
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:248
unsigned int Oid
Definition: postgres_ext.h:31
List * retrieved_attrs
Definition: postgres_fdw.c:181
MemoryContext es_query_cxt
Definition: execnodes.h:559
#define ERROR
Definition: elog.h:45
#define lfirst_int(lc)
Definition: pg_list.h:170
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:126
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
struct PgFdwModifyState * aux_fmstate
Definition: postgres_fdw.c:192
AttrNumber ctidAttno
Definition: postgres_fdw.c:184
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:186
void * palloc0(Size size)
Definition: mcxt.c:981
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2067
int16 attnum
Definition: pg_attribute.h:79
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:117
MemoryContext temp_cxt
Definition: postgres_fdw.c:189
#define Assert(condition)
Definition: c.h:792
Oid serverid
Definition: foreign.h:56
static int list_length(const List *l)
Definition: pg_list.h:149
List * targetlist
Definition: plannodes.h:136
static char * user
Definition: pg_regress.c:95
#define elog(elevel,...)
Definition: elog.h:228
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:198
AttInMetadata * attinmeta
Definition: postgres_fdw.c:171
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:457

◆ ec_member_matches_foreign()

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

Definition at line 3282 of file postgres_fdw.c.

References ec_member_foreign_arg::already_used, ec_member_foreign_arg::current, EquivalenceMember::em_expr, equal(), and list_member().

Referenced by postgresGetForeignPaths().

3285 {
3287  Expr *expr = em->em_expr;
3288 
3289  /*
3290  * If we've identified what we're processing in the current scan, we only
3291  * want to match that expression.
3292  */
3293  if (state->current != NULL)
3294  return equal(expr, state->current);
3295 
3296  /*
3297  * Otherwise, ignore anything we've already processed.
3298  */
3299  if (list_member(state->already_used, expr))
3300  return false;
3301 
3302  /* This is the new target to process. */
3303  state->current = expr;
3304  return true;
3305 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3042
bool list_member(const List *list, const void *datum)
Definition: list.c:613
Definition: regguts.h:298
void * arg

◆ 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,
Cost p_startup_cost,
Cost p_total_cost 
)
static

Definition at line 2677 of file postgres_fdw.c.

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(), PgFdwScanState::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(), RelOptInfo::fdw_private, PgFdwRelationInfo::fdw_startup_cost, PgFdwRelationInfo::fdw_tuple_cost, AggClauseCosts::finalCost, get_agg_clause_costs(), get_remote_estimate(), get_sortgrouplist_exprs(), GetConnection(), Query::groupClause, PgFdwRelationInfo::grouped_tlist, PgFdwPathExtraData::has_final_sort, PgFdwPathExtraData::has_limit, Query::hasAggs, Query::havingQual, 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, PlannerInfo::parse, 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, PgFdwScanState::retrieved_attrs, PgFdwRelationInfo::retrieved_rows, 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().

2684 {
2685  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2686  double rows;
2687  double retrieved_rows;
2688  int width;
2689  Cost startup_cost;
2690  Cost total_cost;
2691 
2692  /* Make sure the core code has set up the relation's reltarget */
2693  Assert(foreignrel->reltarget);
2694 
2695  /*
2696  * If the table or the server is configured to use remote estimates,
2697  * connect to the foreign server and execute EXPLAIN to estimate the
2698  * number of rows selected by the restriction+join clauses. Otherwise,
2699  * estimate rows using whatever statistics we have locally, in a way
2700  * similar to ordinary tables.
2701  */
2702  if (fpinfo->use_remote_estimate)
2703  {
2704  List *remote_param_join_conds;
2705  List *local_param_join_conds;
2706  StringInfoData sql;
2707  PGconn *conn;
2708  Selectivity local_sel;
2709  QualCost local_cost;
2710  List *fdw_scan_tlist = NIL;
2711  List *remote_conds;
2712 
2713  /* Required only to be passed to deparseSelectStmtForRel */
2714  List *retrieved_attrs;
2715 
2716  /*
2717  * param_join_conds might contain both clauses that are safe to send
2718  * across, and clauses that aren't.
2719  */
2720  classifyConditions(root, foreignrel, param_join_conds,
2721  &remote_param_join_conds, &local_param_join_conds);
2722 
2723  /* Build the list of columns to be fetched from the foreign server. */
2724  if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
2725  fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
2726  else
2727  fdw_scan_tlist = NIL;
2728 
2729  /*
2730  * The complete list of remote conditions includes everything from
2731  * baserestrictinfo plus any extra join_conds relevant to this
2732  * particular path.
2733  */
2734  remote_conds = list_concat(remote_param_join_conds,
2735  fpinfo->remote_conds);
2736 
2737  /*
2738  * Construct EXPLAIN query including the desired SELECT, FROM, and
2739  * WHERE clauses. Params and other-relation Vars are replaced by dummy
2740  * values, so don't request params_list.
2741  */
2742  initStringInfo(&sql);
2743  appendStringInfoString(&sql, "EXPLAIN ");
2744  deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
2745  remote_conds, pathkeys,
2746  fpextra ? fpextra->has_final_sort : false,
2747  fpextra ? fpextra->has_limit : false,
2748  false, &retrieved_attrs, NULL);
2749 
2750  /* Get the remote estimate */
2751  conn = GetConnection(fpinfo->user, false);
2752  get_remote_estimate(sql.data, conn, &rows, &width,
2753  &startup_cost, &total_cost);
2754  ReleaseConnection(conn);
2755 
2756  retrieved_rows = rows;
2757 
2758  /* Factor in the selectivity of the locally-checked quals */
2759  local_sel = clauselist_selectivity(root,
2760  local_param_join_conds,
2761  foreignrel->relid,
2762  JOIN_INNER,
2763  NULL);
2764  local_sel *= fpinfo->local_conds_sel;
2765 
2766  rows = clamp_row_est(rows * local_sel);
2767 
2768  /* Add in the eval cost of the locally-checked quals */
2769  startup_cost += fpinfo->local_conds_cost.startup;
2770  total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
2771  cost_qual_eval(&local_cost, local_param_join_conds, root);
2772  startup_cost += local_cost.startup;
2773  total_cost += local_cost.per_tuple * retrieved_rows;
2774 
2775  /*
2776  * Add in tlist eval cost for each output row. In case of an
2777  * aggregate, some of the tlist expressions such as grouping
2778  * expressions will be evaluated remotely, so adjust the costs.
2779  */
2780  startup_cost += foreignrel->reltarget->cost.startup;
2781  total_cost += foreignrel->reltarget->cost.startup;
2782  total_cost += foreignrel->reltarget->cost.per_tuple * rows;
2783  if (IS_UPPER_REL(foreignrel))
2784  {
2785  QualCost tlist_cost;
2786 
2787  cost_qual_eval(&tlist_cost, fdw_scan_tlist, root);
2788  startup_cost -= tlist_cost.startup;
2789  total_cost -= tlist_cost.startup;
2790  total_cost -= tlist_cost.per_tuple * rows;
2791  }
2792  }
2793  else
2794  {
2795  Cost run_cost = 0;
2796 
2797  /*
2798  * We don't support join conditions in this mode (hence, no
2799  * parameterized paths can be made).
2800  */
2801  Assert(param_join_conds == NIL);
2802 
2803  /*
2804  * We will come here again and again with different set of pathkeys or
2805  * additional post-scan/join-processing steps that caller wants to
2806  * cost. We don't need to calculate the cost/size estimates for the
2807  * underlying scan, join, or grouping each time. Instead, use those
2808  * estimates if we have cached them already.
2809  */
2810  if (fpinfo->rel_startup_cost >= 0 && fpinfo->rel_total_cost >= 0)
2811  {
2812  Assert(fpinfo->retrieved_rows >= 1);
2813 
2814  rows = fpinfo->rows;
2815  retrieved_rows = fpinfo->retrieved_rows;
2816  width = fpinfo->width;
2817  startup_cost = fpinfo->rel_startup_cost;
2818  run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
2819 
2820  /*
2821  * If we estimate the costs of a foreign scan or a foreign join
2822  * with additional post-scan/join-processing steps, the scan or
2823  * join costs obtained from the cache wouldn't yet contain the
2824  * eval costs for the final scan/join target, which would've been
2825  * updated by apply_scanjoin_target_to_paths(); add the eval costs
2826  * now.
2827  */
2828  if (fpextra && !IS_UPPER_REL(foreignrel))
2829  {
2830  /* Shouldn't get here unless we have LIMIT */
2831  Assert(fpextra->has_limit);
2832  Assert(foreignrel->reloptkind == RELOPT_BASEREL ||
2833  foreignrel->reloptkind == RELOPT_JOINREL);
2834  startup_cost += foreignrel->reltarget->cost.startup;
2835  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
2836  }
2837  }
2838  else if (IS_JOIN_REL(foreignrel))
2839  {
2840  PgFdwRelationInfo *fpinfo_i;
2841  PgFdwRelationInfo *fpinfo_o;
2842  QualCost join_cost;
2843  QualCost remote_conds_cost;
2844  double nrows;
2845 
2846  /* Use rows/width estimates made by the core code. */
2847  rows = foreignrel->rows;
2848  width = foreignrel->reltarget->width;
2849 
2850  /* For join we expect inner and outer relations set */
2851  Assert(fpinfo->innerrel && fpinfo->outerrel);
2852 
2853  fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
2854  fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
2855 
2856  /* Estimate of number of rows in cross product */
2857  nrows = fpinfo_i->rows * fpinfo_o->rows;
2858 
2859  /*
2860  * Back into an estimate of the number of retrieved rows. Just in
2861  * case this is nuts, clamp to at most nrows.
2862  */
2863  retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
2864  retrieved_rows = Min(retrieved_rows, nrows);
2865 
2866  /*
2867  * The cost of foreign join is estimated as cost of generating
2868  * rows for the joining relations + cost for applying quals on the
2869  * rows.
2870  */
2871 
2872  /*
2873  * Calculate the cost of clauses pushed down to the foreign server
2874  */
2875  cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
2876  /* Calculate the cost of applying join clauses */
2877  cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
2878 
2879  /*
2880  * Startup cost includes startup cost of joining relations and the
2881  * startup cost for join and other clauses. We do not include the
2882  * startup cost specific to join strategy (e.g. setting up hash
2883  * tables) since we do not know what strategy the foreign server
2884  * is going to use.
2885  */
2886  startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
2887  startup_cost += join_cost.startup;
2888  startup_cost += remote_conds_cost.startup;
2889  startup_cost += fpinfo->local_conds_cost.startup;
2890 
2891  /*
2892  * Run time cost includes:
2893  *
2894  * 1. Run time cost (total_cost - startup_cost) of relations being
2895  * joined
2896  *
2897  * 2. Run time cost of applying join clauses on the cross product
2898  * of the joining relations.
2899  *
2900  * 3. Run time cost of applying pushed down other clauses on the
2901  * result of join
2902  *
2903  * 4. Run time cost of applying nonpushable other clauses locally
2904  * on the result fetched from the foreign server.
2905  */
2906  run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
2907  run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
2908  run_cost += nrows * join_cost.per_tuple;
2909  nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
2910  run_cost += nrows * remote_conds_cost.per_tuple;
2911  run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
2912 
2913  /* Add in tlist eval cost for each output row */
2914  startup_cost += foreignrel->reltarget->cost.startup;
2915  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
2916  }
2917  else if (IS_UPPER_REL(foreignrel))
2918  {
2919  RelOptInfo *outerrel = fpinfo->outerrel;
2920  PgFdwRelationInfo *ofpinfo;
2921  AggClauseCosts aggcosts;
2922  double input_rows;
2923  int numGroupCols;
2924  double numGroups = 1;
2925 
2926  /* The upper relation should have its outer relation set */
2927  Assert(outerrel);
2928  /* and that outer relation should have its reltarget set */
2929  Assert(outerrel->reltarget);
2930 
2931  /*
2932  * This cost model is mixture of costing done for sorted and
2933  * hashed aggregates in cost_agg(). We are not sure which
2934  * strategy will be considered at remote side, thus for
2935  * simplicity, we put all startup related costs in startup_cost
2936  * and all finalization and run cost are added in total_cost.
2937  */
2938 
2939  ofpinfo = (PgFdwRelationInfo *) outerrel->fdw_private;
2940 
2941  /* Get rows from input rel */
2942  input_rows = ofpinfo->rows;
2943 
2944  /* Collect statistics about aggregates for estimating costs. */
2945  MemSet(&aggcosts, 0, sizeof(AggClauseCosts));
2946  if (root->parse->hasAggs)
2947  {
2948  get_agg_clause_costs(root, AGGSPLIT_SIMPLE, &aggcosts);
2949  }
2950 
2951  /* Get number of grouping columns and possible number of groups */
2952  numGroupCols = list_length(root->parse->groupClause);
2953  numGroups = estimate_num_groups(root,
2955  fpinfo->grouped_tlist),
2956  input_rows, NULL);
2957 
2958  /*
2959  * Get the retrieved_rows and rows estimates. If there are HAVING
2960  * quals, account for their selectivity.
2961  */
2962  if (root->parse->havingQual)
2963  {
2964  /* Factor in the selectivity of the remotely-checked quals */
2965  retrieved_rows =
2966  clamp_row_est(numGroups *
2968  fpinfo->remote_conds,
2969  0,
2970  JOIN_INNER,
2971  NULL));
2972  /* Factor in the selectivity of the locally-checked quals */
2973  rows = clamp_row_est(retrieved_rows * fpinfo->local_conds_sel);
2974  }
2975  else
2976  {
2977  rows = retrieved_rows = numGroups;
2978  }
2979 
2980  /* Use width estimate made by the core code. */
2981  width = foreignrel->reltarget->width;
2982 
2983  /*-----
2984  * Startup cost includes:
2985  * 1. Startup cost for underneath input relation, adjusted for
2986  * tlist replacement by apply_scanjoin_target_to_paths()
2987  * 2. Cost of performing aggregation, per cost_agg()
2988  *-----
2989  */
2990  startup_cost = ofpinfo->rel_startup_cost;
2991  startup_cost += outerrel->reltarget->cost.startup;
2992  startup_cost += aggcosts.transCost.startup;
2993  startup_cost += aggcosts.transCost.per_tuple * input_rows;
2994  startup_cost += aggcosts.finalCost.startup;
2995  startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
2996 
2997  /*-----
2998  * Run time cost includes:
2999  * 1. Run time cost of underneath input relation, adjusted for
3000  * tlist replacement by apply_scanjoin_target_to_paths()
3001  * 2. Run time cost of performing aggregation, per cost_agg()
3002  *-----
3003  */
3004  run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
3005  run_cost += outerrel->reltarget->cost.per_tuple * input_rows;
3006  run_cost += aggcosts.finalCost.per_tuple * numGroups;
3007  run_cost += cpu_tuple_cost * numGroups;
3008 
3009  /* Account for the eval cost of HAVING quals, if any */
3010  if (root->parse->havingQual)
3011  {
3012  QualCost remote_cost;
3013 
3014  /* Add in the eval cost of the remotely-checked quals */
3015  cost_qual_eval(&remote_cost, fpinfo->remote_conds, root);
3016  startup_cost += remote_cost.startup;
3017  run_cost += remote_cost.per_tuple * numGroups;
3018  /* Add in the eval cost of the locally-checked quals */
3019  startup_cost += fpinfo->local_conds_cost.startup;
3020  run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3021  }
3022 
3023  /* Add in tlist eval cost for each output row */
3024  startup_cost += foreignrel->reltarget->cost.startup;
3025  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3026  }
3027  else
3028  {
3029  Cost cpu_per_tuple;
3030 
3031  /* Use rows/width estimates made by set_baserel_size_estimates. */
3032  rows = foreignrel->rows;
3033  width = foreignrel->reltarget->width;
3034 
3035  /*
3036  * Back into an estimate of the number of retrieved rows. Just in
3037  * case this is nuts, clamp to at most foreignrel->tuples.
3038  */
3039  retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3040  retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
3041 
3042  /*
3043  * Cost as though this were a seqscan, which is pessimistic. We
3044  * effectively imagine the local_conds are being evaluated
3045  * remotely, too.
3046  */
3047  startup_cost = 0;
3048  run_cost = 0;
3049  run_cost += seq_page_cost * foreignrel->pages;
3050 
3051  startup_cost += foreignrel->baserestrictcost.startup;
3052  cpu_per_tuple = cpu_tuple_cost + foreignrel->baserestrictcost.per_tuple;
3053  run_cost += cpu_per_tuple * foreignrel->tuples;
3054 
3055  /* Add in tlist eval cost for each output row */
3056  startup_cost += foreignrel->reltarget->cost.startup;
3057  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3058  }
3059 
3060  /*
3061  * Without remote estimates, we have no real way to estimate the cost
3062  * of generating sorted output. It could be free if the query plan
3063  * the remote side would have chosen generates properly-sorted output
3064  * anyway, but in most cases it will cost something. Estimate a value
3065  * high enough that we won't pick the sorted path when the ordering
3066  * isn't locally useful, but low enough that we'll err on the side of
3067  * pushing down the ORDER BY clause when it's useful to do so.
3068  */
3069  if (pathkeys != NIL)
3070  {
3071  if (IS_UPPER_REL(foreignrel))
3072  {
3073  Assert(foreignrel->reloptkind == RELOPT_UPPER_REL &&
3074  fpinfo->stage == UPPERREL_GROUP_AGG);
3075  adjust_foreign_grouping_path_cost(root, pathkeys,
3076  retrieved_rows, width,
3077  fpextra->limit_tuples,
3078  &startup_cost, &run_cost);
3079  }
3080  else
3081  {
3082  startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3083  run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3084  }
3085  }
3086 
3087  total_cost = startup_cost + run_cost;
3088 
3089  /* Adjust the cost estimates if we have LIMIT */
3090  if (fpextra && fpextra->has_limit)
3091  {
3092  adjust_limit_rows_costs(&rows, &startup_cost, &total_cost,
3093  fpextra->offset_est, fpextra->count_est);
3094  retrieved_rows = rows;
3095  }
3096  }
3097 
3098  /*
3099  * If this includes the final sort step, the given target, which will be
3100  * applied to the resulting path, might have different expressions from
3101  * the foreignrel's reltarget (see make_sort_input_target()); adjust tlist
3102  * eval costs.
3103  */
3104  if (fpextra && fpextra->has_final_sort &&
3105  fpextra->target != foreignrel->reltarget)
3106  {
3107  QualCost oldcost = foreignrel->reltarget->cost;
3108  QualCost newcost = fpextra->target->cost;
3109 
3110  startup_cost += newcost.startup - oldcost.startup;
3111  total_cost += newcost.startup - oldcost.startup;
3112  total_cost += (newcost.per_tuple - oldcost.per_tuple) * rows;
3113  }
3114 
3115  /*
3116  * Cache the retrieved rows and cost estimates for scans, joins, or
3117  * groupings without any parameterization, pathkeys, or additional
3118  * post-scan/join-processing steps, before adding the costs for
3119  * transferring data from the foreign server. These estimates are useful
3120  * for costing remote joins involving this relation or costing other
3121  * remote operations on this relation such as remote sorts and remote
3122  * LIMIT restrictions, when the costs can not be obtained from the foreign
3123  * server. This function will be called at least once for every foreign
3124  * relation without any parameterization, pathkeys, or additional
3125  * post-scan/join-processing steps.
3126  */
3127  if (pathkeys == NIL && param_join_conds == NIL && fpextra == NULL)
3128  {
3129  fpinfo->retrieved_rows = retrieved_rows;
3130  fpinfo->rel_startup_cost = startup_cost;
3131  fpinfo->rel_total_cost = total_cost;
3132  }
3133 
3134  /*
3135  * Add some additional cost factors to account for connection overhead
3136  * (fdw_startup_cost), transferring data across the network
3137  * (fdw_tuple_cost per retrieved row), and local manipulation of the data
3138  * (cpu_tuple_cost per retrieved row).
3139  */
3140  startup_cost += fpinfo->fdw_startup_cost;
3141  total_cost += fpinfo->fdw_startup_cost;
3142  total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
3143  total_cost += cpu_tuple_cost * retrieved_rows;
3144 
3145  /*
3146  * If we have LIMIT, we should prefer performing the restriction remotely
3147  * rather than locally, as the former avoids extra row fetches from the
3148  * remote that the latter might cause. But since the core code doesn't
3149  * account for such fetches when estimating the costs of the local
3150  * restriction (see create_limit_path()), there would be no difference
3151  * between the costs of the local restriction and the costs of the remote
3152  * restriction estimated above if we don't use remote estimates (except
3153  * for the case where the foreignrel is a grouping relation, the given
3154  * pathkeys is not NIL, and the effects of a bounded sort for that rel is
3155  * accounted for in costing the remote restriction). Tweak the costs of
3156  * the remote restriction to ensure we'll prefer it if LIMIT is a useful
3157  * one.
3158  */
3159  if (!fpinfo->use_remote_estimate &&
3160  fpextra && fpextra->has_limit &&
3161  fpextra->limit_tuples > 0 &&
3162  fpextra->limit_tuples < fpinfo->rows)
3163  {
3164  Assert(fpinfo->rows > 0);
3165  total_cost -= (total_cost - startup_cost) * 0.05 *
3166  (fpinfo->rows - fpextra->limit_tuples) / fpinfo->rows;
3167  }
3168 
3169  /* Return results. */
3170  *p_rows = rows;
3171  *p_width = width;
3172  *p_startup_cost = startup_cost;
3173  *p_total_cost = total_cost;
3174 }
#define NIL
Definition: pg_list.h:65
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset)
Definition: selfuncs.c:3360
Query * parse
Definition: pathnodes.h:173
RelOptKind reloptkind
Definition: pathnodes.h:663
PathTarget * target
Definition: postgres_fdw.c:273
QualCost finalCost
Definition: pathnodes.h:59
double tuples
Definition: pathnodes.h:706
bool hasAggs
Definition: parsenodes.h:125
#define Min(x, y)
Definition: c.h:974
void classifyConditions(PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds, List **local_conds)
Definition: deparse.c:204
#define IS_JOIN_REL(rel)
Definition: pathnodes.h:644
List * list_concat(List *list1, const List *list2)
Definition: list.c:515
#define MemSet(start, val, len)
Definition: c.h:996
double Selectivity
Definition: nodes.h:661
void get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
Definition: prepagg.c:542
QualCost transCost
Definition: pathnodes.h:58
static void adjust_foreign_grouping_path_cost(PlannerInfo *root, List *pathkeys, double retrieved_rows, double width, double limit_tuples, Cost *p_startup_cost, Cost *p_run_cost)
RelOptInfo * outerrel
Definition: postgres_fdw.h:100
Cost startup
Definition: pathnodes.h:45
void adjust_limit_rows_costs(double *rows, Cost *startup_cost, Cost *total_cost, int64 offset_est, int64 count_est)
Definition: pathnode.c:3693
void ReleaseConnection(PGconn *conn)
Definition: connection.c:600
Cost per_tuple
Definition: pathnodes.h:46
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:4066
PGconn * conn
Definition: streamutil.c:54
Selectivity local_conds_sel
Definition: postgres_fdw.h:56
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
Selectivity joinclause_sel
Definition: postgres_fdw.h:59
double cpu_operator_cost
Definition: costsize.c:122
Index relid
Definition: pathnodes.h:694
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
UserMapping * user
Definition: postgres_fdw.h:85
double rows
Definition: pathnodes.h:669
void * fdw_private
Definition: pathnodes.h:720
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:117
BlockNumber pages
Definition: pathnodes.h:705
#define Assert(condition)
Definition: c.h:792
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:58
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:1000
QualCost cost
Definition: pathnodes.h:1080
static int list_length(const List *l)
Definition: pg_list.h:149
List * get_sortgrouplist_exprs(List *sgClauses, List *targetList)
Definition: tlist.c:409
double cpu_tuple_cost
Definition: costsize.c:120
#define IS_UPPER_REL(rel)
Definition: pathnodes.h:649
static void get_remote_estimate(const char *sql, PGconn *conn, double *rows, int *width, Cost *startup_cost, Cost *total_cost)
RelOptInfo * innerrel
Definition: postgres_fdw.h:101
List * build_tlist_to_deparse(RelOptInfo *foreignrel)
Definition: deparse.c:943
List * groupClause
Definition: parsenodes.h:148
UpperRelationKind stage
Definition: postgres_fdw.h:107
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:102
Node * havingQual
Definition: parsenodes.h:152
double clamp_row_est(double nrows)
Definition: costsize.c:196
double seq_page_cost
Definition: costsize.c:118
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:677
QualCost baserestrictcost
Definition: pathnodes.h:729
double Cost
Definition: nodes.h:662
QualCost local_conds_cost
Definition: postgres_fdw.h:55

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

Definition at line 4029 of file postgres_fdw.c.

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

Referenced by postgresIterateDirectModify().

4030 {
4032  ExprContext *econtext = node->ss.ps.ps_ExprContext;
4033  int numParams = dmstate->numParams;
4034  const char **values = dmstate->param_values;
4035 
4036  /*
4037  * Construct array of query parameter values in text format.
4038  */
4039  if (numParams > 0)
4040  process_query_params(econtext,
4041  dmstate->param_flinfo,
4042  dmstate->param_exprs,
4043  values);
4044 
4045  /*
4046  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4047  * to infer types for all parameters. Since we explicitly cast every
4048  * parameter (see deparse.c), the "inference" is trivial and will produce
4049  * the desired result. This allows us to avoid assuming that the remote
4050  * server has the same OIDs we do for the parameters' types.
4051  */
4052  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4053  NULL, values, NULL, NULL, 0))
4054  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4055 
4056  /*
4057  * Get the result, and check for success.
4058  *
4059  * We don't use a PG_TRY block here, so be careful not to throw error
4060  * without releasing the PGresult.
4061  */
4062  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
4063  if (PQresultStatus(dmstate->result) !=
4065  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4066  dmstate->query);
4067 
4068  /* Get the number of rows affected. */
4069  if (dmstate->has_returning)
4070  dmstate->num_tuples = PQntuples(dmstate->result);
4071  else
4072  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4073 }
ScanState ss
Definition: execnodes.h:1779
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:1250
const char ** param_values
Definition: postgres_fdw.c:215
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3069
ExprContext * ps_ExprContext
Definition: execnodes.h:967
static void process_query_params(ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2728
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2651
PlanState ps
Definition: execnodes.h:1317
#define ERROR
Definition: elog.h:45
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:737
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:672
static Datum values[MAXATTR]
Definition: bootstrap.c:165

◆ execute_foreign_modify()

static TupleTableSlot * execute_foreign_modify ( EState estate,
ResultRelInfo resultRelInfo,
CmdType  operation,
TupleTableSlot slot,
TupleTableSlot planSlot 
)
static

Definition at line 3632 of file postgres_fdw.c.

References Assert, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, convert_prep_stmt_params(), PgFdwModifyState::ctidAttno, DatumGetPointer, elog, ERROR, ExecGetJunkAttribute(), PgFdwModifyState::has_returning, MemoryContextReset(), PgFdwModifyState::p_name, PgFdwModifyState::p_nums, pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQcmdTuples(), PQntuples(), PQresultStatus(), PQsendQueryPrepared(), prepare_foreign_modify(), PgFdwModifyState::query, ResultRelInfo::ri_FdwState, store_returning_result(), and PgFdwModifyState::temp_cxt.

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

3637 {
3638  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
3639  ItemPointer ctid = NULL;
3640  const char **p_values;
3641  PGresult *res;
3642  int n_rows;
3643 
3644  /* The operation should be INSERT, UPDATE, or DELETE */
3645  Assert(operation == CMD_INSERT ||
3646  operation == CMD_UPDATE ||
3647  operation == CMD_DELETE);
3648 
3649  /* Set up the prepared statement on the remote server, if we didn't yet */
3650  if (!fmstate->p_name)
3651  prepare_foreign_modify(fmstate);
3652 
3653  /*
3654  * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
3655  */
3656  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3657  {
3658  Datum datum;
3659  bool isNull;
3660 
3661  datum = ExecGetJunkAttribute(planSlot,
3662  fmstate->ctidAttno,
3663  &isNull);
3664  /* shouldn't ever get a null result... */
3665  if (isNull)
3666  elog(ERROR, "ctid is NULL");
3667  ctid = (ItemPointer) DatumGetPointer(datum);
3668  }
3669 
3670  /* Convert parameters needed by prepared statement to text form */
3671  p_values = convert_prep_stmt_params(fmstate, ctid, slot);
3672 
3673  /*
3674  * Execute the prepared statement.
3675  */
3676  if (!PQsendQueryPrepared(fmstate->conn,
3677  fmstate->p_name,
3678  fmstate->p_nums,
3679  p_values,
3680  NULL,
3681  NULL,
3682  0))
3683  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
3684 
3685  /*
3686  * Get the result, and check for success.
3687  *
3688  * We don't use a PG_TRY block here, so be careful not to throw error
3689  * without releasing the PGresult.
3690  */
3691  res = pgfdw_get_result(fmstate->conn, fmstate->query);
3692  if (PQresultStatus(res) !=
3694  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
3695 
3696  /* Check number of rows affected, and fetch RETURNING tuple if any */
3697  if (fmstate->has_returning)
3698  {
3699  n_rows = PQntuples(res);
3700  if (n_rows > 0)
3701  store_returning_result(fmstate, slot, res);
3702  }
3703  else
3704  n_rows = atoi(PQcmdTuples(res));
3705 
3706  /* And clean up */
3707  PQclear(res);
3708 
3709  MemoryContextReset(fmstate->temp_cxt);
3710 
3711  /*
3712  * Return NULL if nothing was inserted/updated/deleted on the remote end
3713  */
3714  return (n_rows > 0) ? slot : NULL;
3715 }
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3069
static void store_returning_result(PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:137
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2728
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2651
ItemPointerData * ItemPointer
Definition: itemptr.h:49
#define ERROR
Definition: elog.h:45
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:737
AttrNumber ctidAttno
Definition: postgres_fdw.c:184
uintptr_t Datum
Definition: postgres.h:367
void * ri_FdwState
Definition: execnodes.h:444
void PQclear(PGresult *res)
Definition: fe-exec.c:676
MemoryContext temp_cxt
Definition: postgres_fdw.c:189
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:672
#define Assert(condition)
Definition: c.h:792
static const char ** convert_prep_stmt_params(PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot *slot)
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:1392
#define DatumGetPointer(X)
Definition: postgres.h:549
static void prepare_foreign_modify(PgFdwModifyState *fmstate)
Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: execJunk.c:273
#define elog(elevel,...)
Definition: elog.h:228

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3383 of file postgres_fdw.c.

References Assert, PgFdwScanState::attinmeta, PgFdwScanState::batch_cxt, PgFdwScanState::conn, 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(), PG_END_TRY, PG_FINALLY, PG_TRY, pgfdw_exec_query(), pgfdw_report_error(), PGRES_TUPLES_OK, PlanState::plan, PQclear(), PQntuples(), PQresultStatus(), ScanState::ps, PgFdwScanState::query, PgFdwScanState::rel, PgFdwScanState::retrieved_attrs, snprintf, ForeignScanState::ss, PgFdwScanState::temp_cxt, and PgFdwScanState::tuples.

Referenced by postgresIterateForeignScan().

3384 {
3385  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3386  PGresult *volatile res = NULL;
3387  MemoryContext oldcontext;
3388 
3389  /*
3390  * We'll store the tuples in the batch_cxt. First, flush the previous
3391  * batch.
3392  */
3393  fsstate->tuples = NULL;
3394  MemoryContextReset(fsstate->batch_cxt);
3395  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3396 
3397  /* PGresult must be released before leaving this function. */
3398  PG_TRY();
3399  {
3400  PGconn *conn = fsstate->conn;
3401  char sql[64];
3402  int numrows;
3403  int i;
3404 
3405  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3406  fsstate->fetch_size, fsstate->cursor_number);
3407 
3408  res = pgfdw_exec_query(conn, sql);
3409  /* On error, report the original query, not the FETCH. */
3410  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3411  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3412 
3413  /* Convert the data into HeapTuples */
3414  numrows = PQntuples(res);
3415  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3416  fsstate->num_tuples = numrows;
3417  fsstate->next_tuple = 0;
3418 
3419  for (i = 0; i < numrows; i++)
3420  {
3421  Assert(IsA(node->ss.ps.plan, ForeignScan));
3422 
3423  fsstate->tuples[i] =
3425  fsstate->rel,
3426  fsstate->attinmeta,
3427  fsstate->retrieved_attrs,
3428  node,
3429  fsstate->temp_cxt);
3430  }
3431 
3432  /* Update fetch_ct_2 */
3433  if (fsstate->fetch_ct_2 < 2)
3434  fsstate->fetch_ct_2++;
3435 
3436  /* Must be EOF if we didn't get as many tuples as we asked for. */
3437  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3438  }
3439  PG_FINALLY();
3440  {
3441  if (res)
3442  PQclear(res);
3443  }
3444  PG_END_TRY();
3445 
3446  MemoryContextSwitchTo(oldcontext);
3447 }
ScanState ss
Definition: execnodes.h:1779
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:137
List * retrieved_attrs
Definition: postgres_fdw.c:138
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2728
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2651
unsigned int cursor_number
Definition: postgres_fdw.c:142
PlanState ps
Definition: execnodes.h:1317
#define ERROR
Definition: elog.h:45
PGconn * conn
Definition: streamutil.c:54
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:737
AttInMetadata * attinmeta
Definition: postgres_fdw.c:134
#define PG_FINALLY()
Definition: elog.h:326
void * palloc0(Size size)
Definition: mcxt.c:981
MemoryContext temp_cxt
Definition: postgres_fdw.c:160
Plan * plan
Definition: execnodes.h:928
void PQclear(PGresult *res)
Definition: fe-exec.c:676
#define Assert(condition)
Definition: c.h:792
HeapTuple * tuples
Definition: postgres_fdw.c:150
int i
#define PG_TRY()
Definition: elog.h:309
MemoryContext batch_cxt
Definition: postgres_fdw.c:159
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:648
#define snprintf
Definition: port.h:215
#define PG_END_TRY()
Definition: elog.h:334

◆ find_em_expr_for_input_target()

Expr* find_em_expr_for_input_target ( PlannerInfo root,
EquivalenceClass ec,
PathTarget target 
)

Definition at line 6524 of file postgres_fdw.c.

References arg, EquivalenceClass::ec_members, elog, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, EquivalenceMember::em_is_const, equal(), ERROR, PathTarget::exprs, get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), i, IsA, lfirst, PlannerInfo::parse, and Query::sortClause.

Referenced by add_foreign_ordered_paths(), and appendOrderByClause().

6527 {
6528  ListCell *lc1;
6529  int i;
6530 
6531  i = 0;
6532  foreach(lc1, target->exprs)
6533  {
6534  Expr *expr = (Expr *) lfirst(lc1);
6535  Index sgref = get_pathtarget_sortgroupref(target, i);
6536  ListCell *lc2;
6537 
6538  /* Ignore non-sort expressions */
6539  if (sgref == 0 ||
6541  root->parse->sortClause) == NULL)
6542  {
6543  i++;
6544  continue;
6545  }
6546 
6547  /* We ignore binary-compatible relabeling on both ends */
6548  while (expr && IsA(expr, RelabelType))
6549  expr = ((RelabelType *) expr)->arg;
6550 
6551  /* Locate an EquivalenceClass member matching this expr, if any */
6552  foreach(lc2, ec->ec_members)
6553  {
6555  Expr *em_expr;
6556 
6557  /* Don't match constants */
6558  if (em->em_is_const)
6559  continue;
6560 
6561  /* Ignore child members */
6562  if (em->em_is_child)
6563  continue;
6564 
6565  /* Match if same expression (after stripping relabel) */
6566  em_expr = em->em_expr;
6567  while (em_expr && IsA(em_expr, RelabelType))
6568  em_expr = ((RelabelType *) em_expr)->arg;
6569 
6570  if (equal(em_expr, expr))
6571  return em->em_expr;
6572  }
6573 
6574  i++;
6575  }
6576 
6577  elog(ERROR, "could not find pathkey item to sort");
6578  return NULL; /* keep compiler quiet */
6579 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
Query * parse
Definition: pathnodes.h:173
List * sortClause
Definition: parsenodes.h:158
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3042
#define ERROR
Definition: elog.h:45
List * exprs
Definition: pathnodes.h:1078
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:460
unsigned int Index
Definition: c.h:537
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1085
#define lfirst(lc)
Definition: pg_list.h:169
#define elog(elevel,...)
Definition: elog.h:228
int i
void * arg
List * ec_members
Definition: pathnodes.h:968

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 3868 of file postgres_fdw.c.

References Assert, PgFdwModifyState::conn, ERROR, PgFdwModifyState::p_name, pgfdw_exec_query(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), ReleaseConnection(), and snprintf.

Referenced by postgresEndForeignInsert(), and postgresEndForeignModify().

3869 {
3870  Assert(fmstate != NULL);
3871 
3872  /* If we created a prepared statement, destroy it */
3873  if (fmstate->p_name)
3874  {
3875  char sql[64];
3876  PGresult *res;
3877 
3878  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
3879 
3880  /*
3881  * We don't use a PG_TRY block here, so be careful not to throw error
3882  * without releasing the PGresult.
3883  */
3884  res = pgfdw_exec_query(fmstate->conn, sql);
3885  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3886  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
3887  PQclear(res);
3888  fmstate->p_name = NULL;
3889  }
3890 
3891  /* Release remote connection */
3892  ReleaseConnection(fmstate->conn);
3893  fmstate->conn = NULL;
3894 }
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2651
void ReleaseConnection(PGconn *conn)
Definition: connection.c:600
#define ERROR
Definition: elog.h:45
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:737
void PQclear(PGresult *res)
Definition: fe-exec.c:676
#define Assert(condition)
Definition: c.h:792
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:648
#define snprintf
Definition: port.h:215

◆ foreign_grouping_ok()

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

Definition at line 5562 of file postgres_fdw.c.

References add_to_flat_tlist(), Assert, RestrictInfo::clause, RelOptInfo::fdw_private, get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), Query::groupClause, PgFdwRelationInfo::grouped_tlist, Query::groupingSets, i, 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, PlannerInfo::parse, psprintf(), pull_var_clause(), PgFdwRelationInfo::pushdown_safe, PVC_INCLUDE_AGGREGATES, PlannerInfo::qual_security_level, PgFdwScanState::query, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_name, RelOptInfo::relids, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, TargetEntry::ressortgroupref, and PgFdwRelationInfo::retrieved_rows.

Referenced by add_foreign_grouping_paths().

5564 {
5565  Query *query = root->parse;
5566  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
5567  PathTarget *grouping_target = grouped_rel->reltarget;
5568  PgFdwRelationInfo *ofpinfo;
5569  ListCell *lc;
5570  int i;
5571  List *tlist = NIL;
5572 
5573  /* We currently don't support pushing Grouping Sets. */
5574  if (query->groupingSets)
5575  return false;
5576 
5577  /* Get the fpinfo of the underlying scan relation. */
5578  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
5579 
5580  /*
5581  * If underlying scan relation has any local conditions, those conditions
5582  * are required to be applied before performing aggregation. Hence the
5583  * aggregate cannot be pushed down.
5584  */
5585  if (ofpinfo->local_conds)
5586  return false;
5587 
5588  /*
5589  * Examine grouping expressions, as well as other expressions we'd need to
5590  * compute, and check whether they are safe to push down to the foreign
5591  * server. All GROUP BY expressions will be part of the grouping target
5592  * and thus there is no need to search for them separately. Add grouping
5593  * expressions into target list which will be passed to foreign server.
5594  *
5595  * A tricky fine point is that we must not put any expression into the
5596  * target list that is just a foreign param (that is, something that
5597  * deparse.c would conclude has to be sent to the foreign server). If we
5598  * do, the expression will also appear in the fdw_exprs list of the plan
5599  * node, and setrefs.c will get confused and decide that the fdw_exprs
5600  * entry is actually a reference to the fdw_scan_tlist entry, resulting in
5601  * a broken plan. Somewhat oddly, it's OK if the expression contains such
5602  * a node, as long as it's not at top level; then no match is possible.
5603  */
5604  i = 0;
5605  foreach(lc, grouping_target->exprs)
5606  {
5607  Expr *expr = (Expr *) lfirst(lc);
5608  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
5609  ListCell *l;
5610 
5611  /* Check whether this expression is part of GROUP BY clause */
5612  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
5613  {
5614  TargetEntry *tle;
5615 
5616  /*
5617  * If any GROUP BY expression is not shippable, then we cannot
5618  * push down aggregation to the foreign server.
5619  */
5620  if (!is_foreign_expr(root, grouped_rel, expr))
5621  return false;
5622 
5623  /*
5624  * If it would be a foreign param, we can't put it into the tlist,
5625  * so we have to fail.
5626  */
5627  if (is_foreign_param(root, grouped_rel, expr))
5628  return false;
5629 
5630  /*
5631  * Pushable, so add to tlist. We need to create a TLE for this
5632  * expression and apply the sortgroupref to it. We cannot use
5633  * add_to_flat_tlist() here because that avoids making duplicate
5634  * entries in the tlist. If there are duplicate entries with
5635  * distinct sortgrouprefs, we have to duplicate that situation in
5636  * the output tlist.
5637  */
5638  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
5639  tle->ressortgroupref = sgref;
5640  tlist = lappend(tlist, tle);
5641  }
5642  else
5643  {
5644  /*
5645  * Non-grouping expression we need to compute. Can we ship it
5646  * as-is to the foreign server?
5647  */
5648  if (is_foreign_expr(root, grouped_rel, expr) &&
5649  !is_foreign_param(root, grouped_rel, expr))
5650  {
5651  /* Yes, so add to tlist as-is; OK to suppress duplicates */
5652  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5653  }
5654  else
5655  {
5656  /* Not pushable as a whole; extract its Vars and aggregates */
5657  List *aggvars;
5658 
5659  aggvars = pull_var_clause((Node *) expr,
5661 
5662  /*
5663  * If any aggregate expression is not shippable, then we
5664  * cannot push down aggregation to the foreign server. (We
5665  * don't have to check is_foreign_param, since that certainly
5666  * won't return true for any such expression.)
5667  */
5668  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
5669  return false;
5670 
5671  /*
5672  * Add aggregates, if any, into the targetlist. Plain Vars
5673  * outside an aggregate can be ignored, because they should be
5674  * either same as some GROUP BY column or part of some GROUP
5675  * BY expression. In either case, they are already part of
5676  * the targetlist and thus no need to add them again. In fact
5677  * including plain Vars in the tlist when they do not match a
5678  * GROUP BY column would cause the foreign server to complain
5679  * that the shipped query is invalid.
5680  */
5681  foreach(l, aggvars)
5682  {
5683  Expr *expr = (Expr *) lfirst(l);
5684 
5685  if (IsA(expr, Aggref))
5686  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5687  }
5688  }
5689  }
5690 
5691  i++;
5692  }
5693 
5694  /*
5695  * Classify the pushable and non-pushable HAVING clauses and save them in
5696  * remote_conds and local_conds of the grouped rel's fpinfo.
5697  */
5698  if (havingQual)
5699  {
5700  ListCell *lc;
5701 
5702  foreach(lc, (List *) havingQual)
5703  {
5704  Expr *expr = (Expr *) lfirst(lc);
5705  RestrictInfo *rinfo;
5706 
5707  /*
5708  * Currently, the core code doesn't wrap havingQuals in
5709  * RestrictInfos, so we must make our own.
5710  */
5711  Assert(!IsA(expr, RestrictInfo));
5712  rinfo = make_restrictinfo(expr,
5713  true,
5714  false,
5715  false,
5716  root->qual_security_level,
5717  grouped_rel->relids,
5718  NULL,
5719  NULL);
5720  if (is_foreign_expr(root, grouped_rel, expr))
5721  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5722  else
5723  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5724  }
5725  }
5726 
5727  /*
5728  * If there are any local conditions, pull Vars and aggregates from it and
5729  * check whether they are safe to pushdown or not.
5730  */
5731  if (fpinfo->local_conds)
5732  {
5733  List *aggvars = NIL;
5734  ListCell *lc;
5735 
5736  foreach(lc, fpinfo->local_conds)
5737  {
5738  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5739 
5740  aggvars = list_concat(aggvars,
5741  pull_var_clause((Node *) rinfo->clause,
5743  }
5744 
5745  foreach(lc, aggvars)
5746  {
5747  Expr *expr = (Expr *) lfirst(lc);
5748 
5749  /*
5750  * If aggregates within local conditions are not safe to push
5751  * down, then we cannot push down the query. Vars are already
5752  * part of GROUP BY clause which are checked above, so no need to
5753  * access them again here. Again, we need not check
5754  * is_foreign_param for a foreign aggregate.
5755  */
5756  if (IsA(expr, Aggref))
5757  {
5758  if (!is_foreign_expr(root, grouped_rel, expr))
5759  return false;
5760 
5761  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5762  }
5763  }
5764  }
5765 
5766  /* Store generated targetlist */
5767  fpinfo->grouped_tlist = tlist;
5768 
5769  /* Safe to pushdown */
5770  fpinfo->pushdown_safe = true;
5771 
5772  /*
5773  * Set # of retrieved rows and cached relation costs to some negative
5774  * value, so that we can detect when they are set to some sensible values,
5775  * during one (usually the first) of the calls to estimate_path_cost_size.
5776  */
5777  fpinfo->retrieved_rows = -1;
5778  fpinfo->rel_startup_cost = -1;
5779  fpinfo->rel_total_cost = -1;
5780 
5781  /*
5782  * Set the string describing this grouped relation to be used in EXPLAIN
5783  * output of corresponding ForeignScan. Note that the decoration we add
5784  * to the base relation name mustn't include any digits, or it'll confuse
5785  * postgresExplainForeignScan.
5786  */
5787  fpinfo->relation_name = psprintf("Aggregate on (%s)",
5788  ofpinfo->relation_name);
5789 
5790  return true;
5791 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
Query * parse
Definition: pathnodes.h:173
RestrictInfo * make_restrictinfo(Expr *clause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids)
Definition: restrictinfo.c:59
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
List * groupingSets
Definition: parsenodes.h:150
Definition: nodes.h:528
List * list_concat(List *list1, const List *list2)
Definition: list.c:515
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
RelOptInfo * outerrel
Definition: postgres_fdw.h:100
#define PVC_INCLUDE_AGGREGATES
Definition: optimizer.h:182
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:876
#define list_make1(x1)
Definition: pg_list.h:206
#define lfirst_node(type, lc)
Definition: pg_list.h:172
Relids relids
Definition: pathnodes.h:666
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:238
List * lappend(List *list, void *datum)
Definition: list.c:321
Expr * clause
Definition: pathnodes.h:1994
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:460
unsigned int Index
Definition: c.h:537
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1085
void * fdw_private
Definition: pathnodes.h:720
#define Assert(condition)
Definition: c.h:792
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition: tlist.c:149
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
Index qual_security_level
Definition: pathnodes.h:333
List * groupClause
Definition: parsenodes.h:148
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
int i
Index ressortgroupref
Definition: primnodes.h:1434
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:677

◆ foreign_join_ok()

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

Definition at line 4991 of file postgres_fdw.c.

References Assert, bms_add_members(), bms_is_subset(), bms_nonempty_difference(), bms_union(), RestrictInfo::clause, elog, ERROR, RelOptInfo::fdw_private, get_jointype_name(), PgFdwRelationInfo::innerrel, is_foreign_expr(), IS_OTHER_REL, IS_OUTER_JOIN, JOIN_FULL, JOIN_INNER, JOIN_LEFT, PlannerInfo::join_rel_list, JOIN_RIGHT, PgFdwRelationInfo::joinclauses, PgFdwRelationInfo::jointype, lappend(), lfirst, lfirst_node, list_concat(), list_length(), PgFdwRelationInfo::local_conds, PgFdwRelationInfo::lower_subquery_rels, PgFdwRelationInfo::make_innerrel_subquery, PgFdwRelationInfo::make_outerrel_subquery, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, PlannerInfo::parse, PlaceHolderInfo::ph_eval_at, PlannerInfo::placeholder_list, 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, Query::rtable, PgFdwRelationInfo::server, RelOptInfo::top_parent_relids, PgFdwRelationInfo::use_remote_estimate, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignJoinPaths().

4994 {
4995  PgFdwRelationInfo *fpinfo;
4996  PgFdwRelationInfo *fpinfo_o;
4997  PgFdwRelationInfo *fpinfo_i;
4998  ListCell *lc;
4999  List *joinclauses;
5000 
5001  /*
5002  * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
5003  * Constructing queries representing SEMI and ANTI joins is hard, hence
5004  * not considered right now.
5005  */
5006  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
5007  jointype != JOIN_RIGHT && jointype != JOIN_FULL)
5008  return false;
5009 
5010  /*
5011  * If either of the joining relations is marked as unsafe to pushdown, the
5012  * join can not be pushed down.
5013  */
5014  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
5015  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
5016  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
5017  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
5018  !fpinfo_i || !fpinfo_i->pushdown_safe)
5019  return false;
5020 
5021  /*
5022  * If joining relations have local conditions, those conditions are
5023  * required to be applied before joining the relations. Hence the join can
5024  * not be pushed down.
5025  */
5026  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
5027  return false;
5028 
5029  /*
5030  * Merge FDW options. We might be tempted to do this after we have deemed
5031  * the foreign join to be OK. But we must do this beforehand so that we
5032  * know which quals can be evaluated on the foreign server, which might
5033  * depend on shippable_extensions.
5034  */
5035  fpinfo->server = fpinfo_o->server;
5036  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
5037 
5038  /*
5039  * Separate restrict list into join quals and pushed-down (other) quals.
5040  *
5041  * Join quals belonging to an outer join must all be shippable, else we
5042  * cannot execute the join remotely. Add such quals to 'joinclauses'.
5043  *
5044  * Add other quals to fpinfo->remote_conds if they are shippable, else to
5045  * fpinfo->local_conds. In an inner join it's okay to execute conditions
5046  * either locally or remotely; the same is true for pushed-down conditions
5047  * at an outer join.
5048  *
5049  * Note we might return failure after having already scribbled on
5050  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
5051  * won't consult those lists again if we deem the join unshippable.
5052  */
5053  joinclauses = NIL;
5054  foreach(lc, extra->restrictlist)
5055  {
5056  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5057  bool is_remote_clause = is_foreign_expr(root, joinrel,
5058  rinfo->clause);
5059 
5060  if (IS_OUTER_JOIN(jointype) &&
5061  !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
5062  {
5063  if (!is_remote_clause)
5064  return false;
5065  joinclauses = lappend(joinclauses, rinfo);
5066  }
5067  else
5068  {
5069  if (is_remote_clause)
5070  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5071  else
5072  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5073  }
5074  }
5075 
5076  /*
5077  * deparseExplicitTargetList() isn't smart enough to handle anything other
5078  * than a Var. In particular, if there's some PlaceHolderVar that would
5079  * need to be evaluated within this join tree (because there's an upper
5080  * reference to a quantity that may go to NULL as a result of an outer
5081  * join), then we can't try to push the join down because we'll fail when
5082  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
5083  * needs to be evaluated *at the top* of this join tree is OK, because we
5084  * can do that locally after fetching the results from the remote side.
5085  */
5086  foreach(lc, root->placeholder_list)
5087  {
5088  PlaceHolderInfo *phinfo = lfirst(lc);
5089  Relids relids;
5090 
5091  /* PlaceHolderInfo refers to parent relids, not child relids. */
5092  relids = IS_OTHER_REL(joinrel) ?
5093  joinrel->top_parent_relids : joinrel->relids;
5094 
5095  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
5096  bms_nonempty_difference(relids, phinfo->ph_eval_at))
5097  return false;
5098  }
5099 
5100  /* Save the join clauses, for later use. */
5101  fpinfo->joinclauses = joinclauses;
5102 
5103  fpinfo->outerrel = outerrel;
5104  fpinfo->innerrel = innerrel;
5105  fpinfo->jointype = jointype;
5106 
5107  /*
5108  * By default, both the input relations are not required to be deparsed as
5109  * subqueries, but there might be some relations covered by the input
5110  * relations that are required to be deparsed as subqueries, so save the
5111  * relids of those relations for later use by the deparser.
5112  */
5113  fpinfo->make_outerrel_subquery = false;
5114  fpinfo->make_innerrel_subquery = false;
5115  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
5116  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
5118  fpinfo_i->lower_subquery_rels);
5119 
5120  /*
5121  * Pull the other remote conditions from the joining relations into join
5122  * clauses or other remote clauses (remote_conds) of this relation
5123  * wherever possible. This avoids building subqueries at every join step.
5124  *
5125  * For an inner join, clauses from both the relations are added to the
5126  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
5127  * the outer side are added to remote_conds since those can be evaluated
5128  * after the join is evaluated. The clauses from inner side are added to
5129  * the joinclauses, since they need to be evaluated while constructing the
5130  * join.
5131  *
5132  * For a FULL OUTER JOIN, the other clauses from either relation can not
5133  * be added to the joinclauses or remote_conds, since each relation acts
5134  * as an outer relation for the other.
5135  *
5136  * The joining sides can not have local conditions, thus no need to test
5137  * shippability of the clauses being pulled up.
5138  */
5139  switch (jointype)
5140  {
5141  case JOIN_INNER:
5142  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5143  fpinfo_i->remote_conds);
5144  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5145  fpinfo_o->remote_conds);
5146  break;
5147 
5148  case JOIN_LEFT:
5149  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5150  fpinfo_i->remote_conds);
5151  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5152  fpinfo_o->remote_conds);
5153  break;
5154 
5155  case JOIN_RIGHT:
5156  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5157  fpinfo_o->remote_conds);
5158  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5159  fpinfo_i->remote_conds);
5160  break;
5161 
5162  case JOIN_FULL:
5163 
5164  /*
5165  * In this case, if any of the input relations has conditions, we
5166  * need to deparse that relation as a subquery so that the
5167  * conditions can be evaluated before the join. Remember it in
5168  * the fpinfo of this relation so that the deparser can take
5169  * appropriate action. Also, save the relids of base relations
5170  * covered by that relation for later use by the deparser.
5171  */
5172  if (fpinfo_o->remote_conds)
5173  {
5174  fpinfo->make_outerrel_subquery = true;
5175  fpinfo->lower_subquery_rels =
5177  outerrel->relids);
5178  }
5179  if (fpinfo_i->remote_conds)
5180  {
5181  fpinfo->make_innerrel_subquery = true;
5182  fpinfo->lower_subquery_rels =
5184  innerrel->relids);
5185  }
5186  break;
5187 
5188  default:
5189  /* Should not happen, we have just checked this above */
5190  elog(ERROR, "unsupported join type %d", jointype);
5191  }
5192 
5193  /*
5194  * For an inner join, all restrictions can be treated alike. Treating the
5195  * pushed down conditions as join conditions allows a top level full outer
5196  * join to be deparsed without requiring subqueries.
5197  */
5198  if (jointype == JOIN_INNER)
5199  {
5200  Assert(!fpinfo->joinclauses);
5201  fpinfo->joinclauses = fpinfo->remote_conds;
5202  fpinfo->remote_conds = NIL;
5203  }
5204 
5205  /* Mark that this join can be pushed down safely */
5206  fpinfo->pushdown_safe = true;
5207 
5208  /* Get user mapping */
5209  if (fpinfo->use_remote_estimate)
5210  {
5211  if (fpinfo_o->use_remote_estimate)
5212  fpinfo->user = fpinfo_o->user;
5213  else
5214  fpinfo->user = fpinfo_i->user;
5215  }
5216  else
5217  fpinfo->user = NULL;
5218 
5219  /*
5220  * Set # of retrieved rows and cached relation costs to some negative
5221  * value, so that we can detect when they are set to some sensible values,
5222  * during one (usually the first) of the calls to estimate_path_cost_size.
5223  */
5224  fpinfo->retrieved_rows = -1;
5225  fpinfo->rel_startup_cost = -1;
5226  fpinfo->rel_total_cost = -1;
5227 
5228  /*
5229  * Set the string describing this join relation to be used in EXPLAIN
5230  * output of corresponding ForeignScan. Note that the decoration we add
5231  * to the base relation names mustn't include any digits, or it'll confuse
5232  * postgresExplainForeignScan.
5233  */
5234  fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
5235  fpinfo_o->relation_name,
5236  get_jointype_name(fpinfo->jointype),
5237  fpinfo_i->relation_name);
5238 
5239  /*
5240  * Set the relation index. This is defined as the position of this
5241  * joinrel in the join_rel_list list plus the length of the rtable list.
5242  * Note that since this joinrel is at the end of the join_rel_list list
5243  * when we are called, we can get the position by list_length.
5244  */
5245  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
5246  fpinfo->relation_index =
5248 
5249  return true;
5250 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:173
Relids ph_eval_at
Definition: pathnodes.h:2317
#define IS_OTHER_REL(rel)
Definition: pathnodes.h:654
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
ForeignServer * server
Definition: postgres_fdw.h:84
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:744
Relids lower_subquery_rels
Definition: postgres_fdw.h:117
List * list_concat(List *list1, const List *list2)
Definition: list.c:515
List * join_rel_list
Definition: pathnodes.h:240
RelOptInfo * outerrel
Definition: postgres_fdw.h:100
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
List * rtable
Definition: parsenodes.h:137
#define ERROR
Definition: elog.h:45
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:315
#define lfirst_node(type, lc)
Definition: pg_list.h:172
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1374
Relids relids
Definition: pathnodes.h:666
List * lappend(List *list, void *datum)
Definition: list.c:321
Expr * clause
Definition: pathnodes.h:1994
UserMapping * user
Definition: postgres_fdw.h:85
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: pathnodes.h:2071
void * fdw_private
Definition: pathnodes.h:720
#define Assert(condition)
Definition: c.h:792
#define lfirst(lc)
Definition: pg_list.h:169
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225
static int list_length(const List *l)
Definition: pg_list.h:149
RelOptInfo * innerrel
Definition: postgres_fdw.h:101
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
#define elog(elevel,...)
Definition: elog.h:228
List * placeholder_list
Definition: pathnodes.h:288
Definition: pg_list.h:50
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:793
Relids top_parent_relids
Definition: pathnodes.h:739
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:545

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

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

Referenced by estimate_path_cost_size().

3184 {
3185  PGresult *volatile res = NULL;
3186 
3187  /* PGresult must be released before leaving this function. */
3188  PG_TRY();
3189  {
3190  char *line;
3191  char *p;
3192  int n;
3193 
3194  /*
3195  * Execute EXPLAIN remotely.
3196  */
3197  res = pgfdw_exec_query(conn, sql);
3198  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3199  pgfdw_report_error(ERROR, res, conn, false, sql);
3200 
3201  /*
3202  * Extract cost numbers for topmost plan node. Note we search for a
3203  * left paren from the end of the line to avoid being confused by
3204  * other uses of parentheses.
3205  */
3206  line = PQgetvalue(res, 0, 0);
3207  p = strrchr(line, '(');
3208  if (p == NULL)
3209  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3210  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3211  startup_cost, total_cost, rows, width);
3212  if (n != 4)
3213  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3214  }
3215  PG_FINALLY();
3216  {
3217  if (res)
3218  PQclear(res);
3219  }
3220  PG_END_TRY();
3221 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3122
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2651
#define ERROR
Definition: elog.h:45
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:737
#define PG_FINALLY()
Definition: elog.h:326
void PQclear(PGresult *res)
Definition: fe-exec.c:676
#define elog(elevel,...)
Definition: elog.h:228
#define PG_TRY()
Definition: elog.h:309
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:648
#define PG_END_TRY()
Definition: elog.h:334

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 4079 of file postgres_fdw.c.

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().

4080 {
4082  EState *estate = node->ss.ps.state;
4083  ResultRelInfo *resultRelInfo = node->resultRelInfo;
4084  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4085  TupleTableSlot *resultSlot;
4086 
4087  Assert(resultRelInfo->ri_projectReturning);
4088 
4089  /* If we didn't get any tuples, must be end of data. */
4090  if (dmstate->next_tuple >= dmstate->num_tuples)
4091  return ExecClearTuple(slot);
4092 
4093  /* Increment the command es_processed count if necessary. */
4094  if (dmstate->set_processed)
4095  estate->es_processed += 1;
4096 
4097  /*
4098  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4099  * tuple. (has_returning is false when the local query is of the form
4100  * "UPDATE/DELETE .. RETURNING 1" for example.)
4101  */
4102  if (!dmstate->has_returning)
4103  {
4104  ExecStoreAllNullTuple(slot);
4105  resultSlot = slot;
4106  }
4107  else
4108  {
4109  /*
4110  * On error, be sure to release the PGresult on the way out. Callers
4111  * do not have PG_TRY blocks to ensure this happens.
4112  */
4113  PG_TRY();
4114  {
4115  HeapTuple newtup;
4116 
4117  newtup = make_tuple_from_result_row(dmstate->result,
4118  dmstate->next_tuple,
4119  dmstate->rel,
4120  dmstate->attinmeta,
4121  dmstate->retrieved_attrs,
4122  node,
4123  dmstate->temp_cxt);
4124  ExecStoreHeapTuple(newtup, slot, false);
4125  }
4126  PG_CATCH();
4127  {
4128  if (dmstate->result)
4129  PQclear(dmstate->result);
4130  PG_RE_THROW();
4131  }
4132  PG_END_TRY();
4133 
4134  /* Get the updated/deleted tuple. */
4135  if (dmstate->rel)
4136  resultSlot = slot;
4137  else
4138  resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4139  }
4140  dmstate->next_tuple++;
4141 
4142  /* Make slot available for evaluation of the local query RETURNING list. */
4143  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4144  resultSlot;
4145 
4146  return slot;
4147 }
ScanState ss
Definition: execnodes.h:1779
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1546
AttInMetadata * attinmeta
Definition: postgres_fdw.c:202
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1320
EState * state
Definition: execnodes.h:930
PlanState ps
Definition: execnodes.h:1317
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1782
MemoryContext temp_cxt
Definition: postgres_fdw.c:228
void PQclear(PGresult *res)
Definition: fe-exec.c:676
#define PG_CATCH()
Definition: elog.h:319
#define Assert(condition)
Definition: c.h:792
#define PG_RE_THROW()
Definition: elog.h:350
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
#define PG_TRY()
Definition: elog.h:309
#define PG_END_TRY()
Definition: elog.h:334
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1322

◆ get_useful_ecs_for_relation()

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

Definition at line 757 of file postgres_fdw.c.

References Assert, bms_is_empty(), bms_overlap(), EquivalenceClass::ec_relids, eclass_useful_for_merging(), PlannerInfo::eq_classes, RelOptInfo::has_eclass_joins, IS_OTHER_REL, RelOptInfo::joininfo, lappend(), RestrictInfo::left_ec, lfirst, list_append_unique_ptr(), RestrictInfo::mergeopfamilies, NIL, RelOptInfo::relids, RestrictInfo::right_ec, RelOptInfo::top_parent_relids, and update_mergeclause_eclasses().

Referenced by get_useful_pathkeys_for_relation().

758 {
759  List *useful_eclass_list = NIL;
760  ListCell *lc;
761  Relids relids;
762 
763  /*
764  * First, consider whether any active EC is potentially useful for a merge
765  * join against this relation.
766  */
767  if (rel->has_eclass_joins)
768  {
769  foreach(lc, root->eq_classes)
770  {
771  EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc);
772 
773  if (eclass_useful_for_merging(root, cur_ec, rel))
774  useful_eclass_list = lappend(useful_eclass_list, cur_ec);
775  }
776  }
777 
778  /*
779  * Next, consider whether there are any non-EC derivable join clauses that
780  * are merge-joinable. If the joininfo list is empty, we can exit
781  * quickly.
782  */
783  if (rel->joininfo == NIL)
784  return useful_eclass_list;
785 
786  /* If this is a child rel, we must use the topmost parent rel to search. */
787  if (IS_OTHER_REL(rel))
788  {
790  relids = rel->top_parent_relids;
791  }
792  else
793  relids = rel->relids;
794 
795  /* Check each join clause in turn. */
796  foreach(lc, rel->joininfo)
797  {
798  RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
799 
800  /* Consider only mergejoinable clauses */
801  if (restrictinfo->mergeopfamilies == NIL)
802  continue;
803 
804  /* Make sure we've got canonical ECs. */
805  update_mergeclause_eclasses(root, restrictinfo);
806 
807  /*
808  * restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
809  * that left_ec and right_ec will be initialized, per comments in
810  * distribute_qual_to_rels.
811  *
812  * We want to identify which side of this merge-joinable clause
813  * contains columns from the relation produced by this RelOptInfo. We
814  * test for overlap, not containment, because there could be extra
815  * relations on either side. For example, suppose we've got something
816  * like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
817  * A.y = D.y. The input rel might be the joinrel between A and B, and
818  * we'll consider the join clause A.y = D.y. relids contains a
819  * relation not involved in the join class (B) and the equivalence
820  * class for the left-hand side of the clause contains a relation not
821  * involved in the input rel (C). Despite the fact that we have only
822  * overlap and not containment in either direction, A.y is potentially
823  * useful as a sort column.
824  *
825  * Note that it's even possible that relids overlaps neither side of
826  * the join clause. For example, consider A LEFT JOIN B ON A.x = B.x
827  * AND A.x = 1. The clause A.x = 1 will appear in B's joininfo list,
828  * but overlaps neither side of B. In that case, we just skip this
829  * join clause, since it doesn't suggest a useful sort order for this
830  * relation.
831  */
832  if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
833  useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
834  restrictinfo->right_ec);
835  else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
836  useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
837  restrictinfo->left_ec);
838  }
839 
840  return useful_eclass_list;
841 }
bool has_eclass_joins
Definition: pathnodes.h:734
#define NIL
Definition: pg_list.h:65
#define IS_OTHER_REL(rel)
Definition: pathnodes.h:654
bool eclass_useful_for_merging(PlannerInfo *root, EquivalenceClass *eclass, RelOptInfo *rel)
Definition: equivclass.c:2914
EquivalenceClass * right_ec
Definition: pathnodes.h:2043
List * mergeopfamilies
Definition: pathnodes.h:2039
List * list_append_unique_ptr(List *list, void *datum)
Definition: list.c:1193
List * joininfo
Definition: pathnodes.h:732
Relids ec_relids
Definition: pathnodes.h:971
Relids relids
Definition: pathnodes.h:666
List * lappend(List *list, void *datum)
Definition: list.c:321
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
#define Assert(condition)
Definition: c.h:792
#define lfirst(lc)
Definition: pg_list.h:169
List * eq_classes
Definition: pathnodes.h:260
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:494
EquivalenceClass * left_ec
Definition: pathnodes.h:2042
Definition: pg_list.h:50
void update_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
Definition: pathkeys.c:1228
Relids top_parent_relids
Definition: pathnodes.h:739

◆ get_useful_pathkeys_for_relation()

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

Definition at line 853 of file postgres_fdw.c.

References BTLessStrategyNumber, EquivalenceClass::ec_has_volatile, EquivalenceClass::ec_opfamilies, RelOptInfo::fdw_private, find_em_expr_for_rel(), get_useful_ecs_for_relation(), is_foreign_expr(), lappend(), lfirst, linitial, linitial_oid, list_copy(), list_length(), list_make1, make_canonical_pathkey(), NIL, PathKey::pk_eclass, PgFdwRelationInfo::qp_is_pushdown_safe, PlannerInfo::query_pathkeys, and PgFdwRelationInfo::use_remote_estimate.

Referenced by add_paths_with_pathkeys_for_rel().

854 {
855  List *useful_pathkeys_list = NIL;
856  List *useful_eclass_list;
858  EquivalenceClass *query_ec = NULL;
859  ListCell *lc;
860 
861  /*
862  * Pushing the query_pathkeys to the remote server is always worth
863  * considering, because it might let us avoid a local sort.
864  */
865  fpinfo->qp_is_pushdown_safe = false;
866  if (root->query_pathkeys)
867  {
868  bool query_pathkeys_ok = true;
869 
870  foreach(lc, root->query_pathkeys)
871  {
872  PathKey *pathkey = (PathKey *) lfirst(lc);
873  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
874  Expr *em_expr;
875 
876  /*
877  * The planner and executor don't have any clever strategy for
878  * taking data sorted by a prefix of the query's pathkeys and
879  * getting it to be sorted by all of those pathkeys. We'll just
880  * end up resorting the entire data set. So, unless we can push
881  * down all of the query pathkeys, forget it.
882  *
883  * is_foreign_expr would detect volatile expressions as well, but
884  * checking ec_has_volatile here saves some cycles.
885  */
886  if (pathkey_ec->ec_has_volatile ||
887  !(em_expr = find_em_expr_for_rel(pathkey_ec, rel)) ||
888  !is_foreign_expr(root, rel, em_expr))
889  {
890  query_pathkeys_ok = false;
891  break;
892  }
893  }
894 
895  if (query_pathkeys_ok)
896  {
897  useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
898  fpinfo->qp_is_pushdown_safe = true;
899  }
900  }
901 
902  /*
903  * Even if we're not using remote estimates, having the remote side do the
904  * sort generally won't be any worse than doing it locally, and it might
905  * be much better if the remote side can generate data in the right order
906  * without needing a sort at all. However, what we're going to do next is
907  * try to generate pathkeys that seem promising for possible merge joins,
908  * and that's more speculative. A wrong choice might hurt quite a bit, so
909  * bail out if we can't use remote estimates.
910  */
911  if (!fpinfo->use_remote_estimate)
912  return useful_pathkeys_list;
913 
914  /* Get the list of interesting EquivalenceClasses. */
915  useful_eclass_list = get_useful_ecs_for_relation(root, rel);
916 
917  /* Extract unique EC for query, if any, so we don't consider it again. */
918  if (list_length(root->query_pathkeys) == 1)
919  {
920  PathKey *query_pathkey = linitial(root->query_pathkeys);
921 
922  query_ec = query_pathkey->pk_eclass;
923  }
924 
925  /*
926  * As a heuristic, the only pathkeys we consider here are those of length
927  * one. It's surely possible to consider more, but since each one we
928  * choose to consider will generate a round-trip to the remote side, we
929  * need to be a bit cautious here. It would sure be nice to have a local
930  * cache of information about remote index definitions...
931  */
932  foreach(lc, useful_eclass_list)
933  {
934  EquivalenceClass *cur_ec = lfirst(lc);
935  Expr *em_expr;
936  PathKey *pathkey;
937 
938  /* If redundant with what we did above, skip it. */
939  if (cur_ec == query_ec)
940  continue;
941 
942  /* If no pushable expression for this rel, skip it. */
943  em_expr = find_em_expr_for_rel(cur_ec, rel);
944  if (em_expr == NULL || !is_foreign_expr(root, rel, em_expr))
945  continue;
946 
947  /* Looks like we can generate a pathkey, so let's do it. */
948  pathkey = make_canonical_pathkey(root, cur_ec,
949  linitial_oid(cur_ec->ec_opfamilies),
951  false);
952  useful_pathkeys_list = lappend(useful_pathkeys_list,
953  list_make1(pathkey));
954  }
955 
956  return useful_pathkeys_list;
957 }
#define NIL
Definition: pg_list.h:65
List * query_pathkeys
Definition: pathnodes.h:292
static List * get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:757
List * list_copy(const List *oldlist)
Definition: list.c:1403
PathKey * make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, int strategy, bool nulls_first)
Definition: pathkeys.c:54
#define list_make1(x1)
Definition: pg_list.h:206
#define linitial(l)
Definition: pg_list.h:174
List * lappend(List *list, void *datum)
Definition: list.c:321
List * ec_opfamilies
Definition: pathnodes.h:966
Expr * find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel)
Definition: equivclass.c:776
void * fdw_private
Definition: pathnodes.h:720
#define lfirst(lc)
Definition: pg_list.h:169
EquivalenceClass * pk_eclass
Definition: pathnodes.h:1045
#define linitial_oid(l)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:149
bool ec_has_volatile
Definition: pathnodes.h:974
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
#define BTLessStrategyNumber
Definition: stratnum.h:29
Definition: pg_list.h:50

◆ init_returning_filter()

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

Definition at line 4153 of file postgres_fdw.c.

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().

4156 {
4157  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4158  ListCell *lc;
4159  int i;
4160 
4161  /*
4162  * Calculate the mapping between the fdw_scan_tlist's entries and the
4163  * result tuple's attributes.
4164  *
4165  * The "map" is an array of indexes of the result tuple's attributes in
4166  * fdw_scan_tlist, i.e., one entry for every attribute of the result
4167  * tuple. We store zero for any attributes that don't have the
4168  * corresponding entries in that list, marking that a NULL is needed in
4169  * the result tuple.
4170  *
4171  * Also get the indexes of the entries for ctid and oid if any.
4172  */
4173  dmstate->attnoMap = (AttrNumber *)
4174  palloc0(resultTupType->natts * sizeof(AttrNumber));
4175 
4176  dmstate->ctidAttno = dmstate->oidAttno = 0;
4177 
4178  i = 1;
4179  dmstate->hasSystemCols = false;
4180  foreach(lc, fdw_scan_tlist)
4181  {
4182  TargetEntry *tle = (TargetEntry *) lfirst(lc);
4183  Var *var = (Var *) tle->expr;
4184 
4185  Assert(IsA(var, Var));
4186 
4187  /*
4188  * If the Var is a column of the target relation to be retrieved from
4189  * the foreign server, get the index of the entry.
4190  */
4191  if (var->varno == rtindex &&
4192  list_member_int(dmstate->retrieved_attrs, i))
4193  {
4194  int attrno = var->varattno;
4195 
4196  if (attrno < 0)
4197  {
4198  /*
4199  * We don't retrieve system columns other than ctid and oid.
4200  */
4201  if (attrno == SelfItemPointerAttributeNumber)
4202  dmstate->ctidAttno = i;
4203  else
4204  Assert(false);
4205  dmstate->hasSystemCols = true;
4206  }
4207  else
4208  {
4209  /*
4210  * We don't retrieve whole-row references to the target
4211  * relation either.
4212  */
4213  Assert(attrno > 0);
4214 
4215  dmstate->attnoMap[attrno - 1] = i;
4216  }
4217  }
4218  i++;
4219  }
4220 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
#define RelationGetDescr(relation)
Definition: rel.h:483
AttrNumber varattno
Definition: primnodes.h:186
Definition: primnodes.h:181
bool list_member_int(const List *list, int datum)
Definition: list.c:654
Index varno
Definition: primnodes.h:184
void * palloc0(Size size)
Definition: mcxt.c:981
#define Assert(condition)
Definition: c.h:792
#define lfirst(lc)
Definition: pg_list.h:169
Expr * expr
Definition: primnodes.h:1431
int i
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
int16 AttrNumber
Definition: attnum.h:21

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

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, lfirst_int, MemoryContextReset(), MemoryContextSwitchTo(), TupleDescData::natts, palloc(), palloc0(), PQgetisnull(), PQgetvalue(), PQnfields(), PQntuples(), ErrorContextCallback::previous, PgFdwScanState::rel, ConversionLocation::rel, RelationGetDescr, SelfItemPointerAttributeNumber, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleData::t_self, tidin(), TupleTableSlot::tts_tupleDescriptor, PgFdwScanState::tupdesc, and values.

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

6313 {
6314  HeapTuple tuple;
6315  TupleDesc tupdesc;
6316  Datum *values;
6317  bool *nulls;
6318  ItemPointer ctid = NULL;
6319  ConversionLocation errpos;
6320  ErrorContextCallback errcallback;
6321  MemoryContext oldcontext;
6322  ListCell *lc;
6323  int j;
6324 
6325  Assert(row < PQntuples(res));
6326 
6327  /*
6328  * Do the following work in a temp context that we reset after each tuple.
6329  * This cleans up not only the data we have direct access to, but any
6330  * cruft the I/O functions might leak.
6331  */
6332  oldcontext = MemoryContextSwitchTo(temp_context);
6333 
6334  if (rel)
6335  tupdesc = RelationGetDescr(rel);
6336  else
6337  {
6338  Assert(fsstate);
6339  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
6340  }
6341 
6342  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
6343  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
6344  /* Initialize to nulls for any columns not present in result */
6345  memset(nulls, true, tupdesc->natts * sizeof(bool));
6346 
6347  /*
6348  * Set up and install callback to report where conversion error occurs.
6349  */
6350  errpos.rel = rel;
6351  errpos.cur_attno = 0;
6352  errpos.fsstate = fsstate;
6353  errcallback.callback = conversion_error_callback;
6354  errcallback.arg = (void *) &errpos;
6355  errcallback.previous = error_context_stack;
6356  error_context_stack = &errcallback;
6357 
6358  /*
6359  * i indexes columns in the relation, j indexes columns in the PGresult.
6360  */
6361  j = 0;
6362  foreach(lc, retrieved_attrs)
6363  {
6364  int i = lfirst_int(lc);
6365  char *valstr;
6366 
6367  /* fetch next column's textual value */
6368  if (PQgetisnull(res, row, j))
6369  valstr = NULL;
6370  else
6371  valstr = PQgetvalue(res, row, j);
6372 
6373  /*
6374  * convert value to internal representation
6375  *
6376  * Note: we ignore system columns other than ctid and oid in result
6377  */
6378  errpos.cur_attno = i;
6379  if (i > 0)
6380  {
6381  /* ordinary column */
6382  Assert(i <= tupdesc->natts);
6383  nulls[i - 1] = (valstr == NULL);
6384  /* Apply the input function even to nulls, to support domains */
6385  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
6386  valstr,
6387  attinmeta->attioparams[i - 1],
6388  attinmeta->atttypmods[i - 1]);
6389  }
6390  else if (i == SelfItemPointerAttributeNumber)
6391  {
6392  /* ctid */
6393  if (valstr != NULL)
6394  {
6395  Datum datum;
6396 
6397  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
6398  ctid = (ItemPointer) DatumGetPointer(datum);
6399  }
6400  }
6401  errpos.cur_attno = 0;
6402 
6403  j++;
6404  }
6405 
6406  /* Uninstall error context callback. */
6407  error_context_stack = errcallback.previous;
6408 
6409  /*
6410  * Check we got the expected number of columns. Note: j == 0 and
6411  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
6412  */
6413  if (j > 0 && j != PQnfields(res))
6414  elog(ERROR, "remote query result does not match the foreign table");
6415 
6416  /*
6417  * Build the result tuple in caller's memory context.
6418  */
6419  MemoryContextSwitchTo(oldcontext);
6420 
6421  tuple = heap_form_tuple(tupdesc, values, nulls);
6422 
6423  /*
6424  * If we have a CTID to return, install it in both t_self and t_ctid.
6425  * t_self is the normal place, but if the tuple is converted to a
6426  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
6427  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
6428  */
6429  if (ctid)
6430  tuple->t_self = tuple->t_data->t_ctid = *ctid;
6431 
6432  /*
6433  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
6434  * heap_form_tuple. heap_form_tuple actually creates the tuple with
6435  * DatumTupleFields, not HeapTupleFields, but the executor expects
6436  * HeapTupleFields and will happily extract system columns on that
6437  * assumption. If we don't do this then, for example, the tuple length
6438  * ends up in the xmin field, which isn't what we want.
6439  */
6443 
6444  /* Clean up */
6445  MemoryContextReset(temp_context);
6446 
6447  return tuple;
6448 }
ScanState ss
Definition: execnodes.h:1779
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2736
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3122
#define RelationGetDescr(relation)
Definition: rel.h:483
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int32 * atttypmods
Definition: funcapi.h:47
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1320
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:137
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:626
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2728
void(* callback)(void *arg)
Definition: elog.h:243
struct ErrorContextCallback * previous
Definition: elog.h:242
Oid * attioparams
Definition: funcapi.h:44
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
ErrorContextCallback * error_context_stack
Definition: elog.c:93
ForeignScanState * fsstate
Definition: postgres_fdw.c:295
#define ERROR
Definition: elog.h:45
#define lfirst_int(lc)
Definition: pg_list.h:170
ItemPointerData t_ctid
Definition: htup_details.h:160
ItemPointerData t_self
Definition: htup.h:65
#define CStringGetDatum(X)
Definition: postgres.h:578
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:380
#define InvalidTransactionId
Definition: transam.h:31
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
void * palloc0(Size size)
Definition: mcxt.c:981
static void conversion_error_callback(void *arg)
uintptr_t Datum
Definition: postgres.h:367
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1532
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:57
#define Assert(condition)
Definition: c.h:792
#define DatumGetPointer(X)
Definition: postgres.h:549
static Datum values[MAXATTR]
Definition: bootstrap.c:165
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:228
int i
FmgrInfo * attinfuncs
Definition: funcapi.h:41