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/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 57 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 51 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.01

Definition at line 54 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 113 of file postgres_fdw.c.

114 {
115  /* SQL statement to execute remotely (as a String node) */
117  /* has-returning flag (as an integer Value node) */
119  /* Integer list of attribute numbers retrieved by RETURNING */
121  /* set-processed flag (as an integer Value node) */
123 };

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 92 of file postgres_fdw.c.

93 {
94  /* SQL statement to execute remotely (as a String node) */
96  /* Integer list of target attribute numbers for INSERT/UPDATE */
98  /* has-returning flag (as an integer Value node) */
100  /* Integer list of attribute numbers retrieved by RETURNING */
102 };

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 261 of file postgres_fdw.c.

262 {
263  /* has-final-sort flag (as an integer Value node) */
265  /* has-limit flag (as an integer Value node) */
267 };

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 66 of file postgres_fdw.c.

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

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

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

◆ add_foreign_grouping_paths()

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

Definition at line 5860 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().

5863 {
5864  Query *parse = root->parse;
5865  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5866  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
5867  ForeignPath *grouppath;
5868  double rows;
5869  int width;
5870  Cost startup_cost;
5871  Cost total_cost;
5872 
5873  /* Nothing to be done, if there is no grouping or aggregation required. */
5874  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
5875  !root->hasHavingQual)
5876  return;
5877 
5880 
5881  /* save the input_rel as outerrel in fpinfo */
5882  fpinfo->outerrel = input_rel;
5883 
5884  /*
5885  * Copy foreign table, foreign server, user mapping, FDW options etc.
5886  * details from the input relation's fpinfo.
5887  */
5888  fpinfo->table = ifpinfo->table;
5889  fpinfo->server = ifpinfo->server;
5890  fpinfo->user = ifpinfo->user;
5891  merge_fdw_options(fpinfo, ifpinfo, NULL);
5892 
5893  /*
5894  * Assess if it is safe to push down aggregation and grouping.
5895  *
5896  * Use HAVING qual from extra. In case of child partition, it will have
5897  * translated Vars.
5898  */
5899  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
5900  return;
5901 
5902  /*
5903  * Compute the selectivity and cost of the local_conds, so we don't have
5904  * to do it over again for each path. (Currently we create just a single
5905  * path here, but in future it would be possible that we build more paths
5906  * such as pre-sorted paths as in postgresGetForeignPaths and
5907  * postgresGetForeignJoinPaths.) The best we can do for these conditions
5908  * is to estimate selectivity on the basis of local statistics.
5909  */
5910  fpinfo->local_conds_sel = clauselist_selectivity(root,
5911  fpinfo->local_conds,
5912  0,
5913  JOIN_INNER,
5914  NULL);
5915 
5916  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
5917 
5918  /* Estimate the cost of push down */
5919  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
5920  &rows, &width, &startup_cost, &total_cost);
5921 
5922  /* Now update this information in the fpinfo */
5923  fpinfo->rows = rows;
5924  fpinfo->width = width;
5925  fpinfo->startup_cost = startup_cost;
5926  fpinfo->total_cost = total_cost;
5927 
5928  /* Create and add foreign path to the grouping relation. */
5929  grouppath = create_foreign_upper_path(root,
5930  grouped_rel,
5931  grouped_rel->reltarget,
5932  rows,
5933  startup_cost,
5934  total_cost,
5935  NIL, /* no pathkeys */
5936  NULL,
5937  NIL); /* no fdw_private */
5938 
5939  /* Add generated path into grouped_rel by add_path(). */
5940  add_path(grouped_rel, (Path *) grouppath);
5941 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:177
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:2493
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:4065
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:718
#define Assert(condition)
Definition: c.h:746
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:345
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:69
struct PathTarget * reltarget
Definition: pathnodes.h:675
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 5951 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().

5953 {
5954  Query *parse = root->parse;
5955  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5956  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
5957  PgFdwPathExtraData *fpextra;
5958  double rows;
5959  int width;
5960  Cost startup_cost;
5961  Cost total_cost;
5962  List *fdw_private;
5963  ForeignPath *ordered_path;
5964  ListCell *lc;
5965 
5966  /* Shouldn't get here unless the query has ORDER BY */
5967  Assert(parse->sortClause);
5968 
5969  /* We don't support cases where there are any SRFs in the targetlist */
5970  if (parse->hasTargetSRFs)
5971  return;
5972 
5973  /* Save the input_rel as outerrel in fpinfo */
5974  fpinfo->outerrel = input_rel;
5975 
5976  /*
5977  * Copy foreign table, foreign server, user mapping, FDW options etc.
5978  * details from the input relation's fpinfo.
5979  */
5980  fpinfo->table = ifpinfo->table;
5981  fpinfo->server = ifpinfo->server;
5982  fpinfo->user = ifpinfo->user;
5983  merge_fdw_options(fpinfo, ifpinfo, NULL);
5984 
5985  /*
5986  * If the input_rel is a base or join relation, we would already have
5987  * considered pushing down the final sort to the remote server when
5988  * creating pre-sorted foreign paths for that relation, because the
5989  * query_pathkeys is set to the root->sort_pathkeys in that case (see
5990  * standard_qp_callback()).
5991  */
5992  if (input_rel->reloptkind == RELOPT_BASEREL ||
5993  input_rel->reloptkind == RELOPT_JOINREL)
5994  {
5995  Assert(root->query_pathkeys == root->sort_pathkeys);
5996 
5997  /* Safe to push down if the query_pathkeys is safe to push down */
5998  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
5999 
6000  return;
6001  }
6002 
6003  /* The input_rel should be a grouping relation */
6004  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
6005  ifpinfo->stage == UPPERREL_GROUP_AGG);
6006 
6007  /*
6008  * We try to create a path below by extending a simple foreign path for
6009  * the underlying grouping relation to perform the final sort remotely,
6010  * which is stored into the fdw_private list of the resulting path.
6011  */
6012 
6013  /* Assess if it is safe to push down the final sort */
6014  foreach(lc, root->sort_pathkeys)
6015  {
6016  PathKey *pathkey = (PathKey *) lfirst(lc);
6017  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6018  Expr *sort_expr;
6019 
6020  /*
6021  * is_foreign_expr would detect volatile expressions as well, but
6022  * checking ec_has_volatile here saves some cycles.
6023  */
6024  if (pathkey_ec->ec_has_volatile)
6025  return;
6026 
6027  /* Get the sort expression for the pathkey_ec */
6028  sort_expr = find_em_expr_for_input_target(root,
6029  pathkey_ec,
6030  input_rel->reltarget);
6031 
6032  /* If it's unsafe to remote, we cannot push down the final sort */
6033  if (!is_foreign_expr(root, input_rel, sort_expr))
6034  return;
6035  }
6036 
6037  /* Safe to push down */
6038  fpinfo->pushdown_safe = true;
6039 
6040  /* Construct PgFdwPathExtraData */
6041  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6042  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6043  fpextra->has_final_sort = true;
6044 
6045  /* Estimate the costs of performing the final sort remotely */
6046  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6047  &rows, &width, &startup_cost, &total_cost);
6048 
6049  /*
6050  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6051  * Items in the list must match order in enum FdwPathPrivateIndex.
6052  */
6053  fdw_private = list_make2(makeInteger(true), makeInteger(false));
6054 
6055  /* Create foreign ordering path */
6056  ordered_path = create_foreign_upper_path(root,
6057  input_rel,
6059  rows,
6060  startup_cost,
6061  total_cost,
6062  root->sort_pathkeys,
6063  NULL, /* no extra plan */
6064  fdw_private);
6065 
6066  /* and add it to the ordered_rel */
6067  add_path(ordered_rel, (Path *) ordered_path);
6068 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:177
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:661
List * query_pathkeys
Definition: pathnodes.h:296
PathTarget * target
Definition: postgres_fdw.c:272
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:301
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:718
bool hasTargetSRFs
Definition: parsenodes.h:127
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
ForeignTable * table
Definition: postgres_fdw.h:83
EquivalenceClass * pk_eclass
Definition: pathnodes.h:1040
bool ec_has_volatile
Definition: pathnodes.h:969
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:675
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:312

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

5263 {
5264  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
5265  ListCell *lc;
5266 
5267  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
5268 
5269  /* Create one path for each set of pathkeys we found above. */
5270  foreach(lc, useful_pathkeys_list)
5271  {
5272  double rows;
5273  int width;
5274  Cost startup_cost;
5275  Cost total_cost;
5276  List *useful_pathkeys = lfirst(lc);
5277  Path *sorted_epq_path;
5278 
5279  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
5280  &rows, &width, &startup_cost, &total_cost);
5281 
5282  /*
5283  * The EPQ path must be at least as well sorted as the path itself, in
5284  * case it gets used as input to a mergejoin.
5285  */
5286  sorted_epq_path = epq_path;
5287  if (sorted_epq_path != NULL &&
5288  !pathkeys_contained_in(useful_pathkeys,
5289  sorted_epq_path->pathkeys))
5290  sorted_epq_path = (Path *)
5291  create_sort_path(root,
5292  rel,
5293  sorted_epq_path,
5294  useful_pathkeys,
5295  -1.0);
5296 
5297  if (IS_SIMPLE_REL(rel))
5298  add_path(rel, (Path *)
5299  create_foreignscan_path(root, rel,
5300  NULL,
5301  rows,
5302  startup_cost,
5303  total_cost,
5304  useful_pathkeys,
5305  rel->lateral_relids,
5306  sorted_epq_path,
5307  NIL));
5308  else
5309  add_path(rel, (Path *)
5310  create_foreign_join_path(root, rel,
5311  NULL,
5312  rows,
5313  startup_cost,
5314  total_cost,
5315  useful_pathkeys,
5316  rel->lateral_relids,
5317  sorted_epq_path,
5318  NIL));
5319  }
5320 }
#define NIL
Definition: pg_list.h:65
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:852
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:637
Relids lateral_relids
Definition: pathnodes.h:689
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:1157
#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 3236 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().

3243 {
3244  /*
3245  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3246  * side is unlikely to generate properly-sorted output, so it would need
3247  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3248  * if the GROUP BY clause is sort-able but isn't a superset of the given
3249  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3250  * costs by applying the same heuristic as for the scan or join case.
3251  */
3252  if (!grouping_is_sortable(root->parse->groupClause) ||
3253  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3254  {
3255  Path sort_path; /* dummy for result of cost_sort */
3256 
3257  cost_sort(&sort_path,
3258  root,
3259  pathkeys,
3260  *p_startup_cost + *p_run_cost,
3261  retrieved_rows,
3262  width,
3263  0.0,
3264  work_mem,
3265  limit_tuples);
3266 
3267  *p_startup_cost = sort_path.startup_cost;
3268  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3269  }
3270  else
3271  {
3272  /*
3273  * The default extra cost seems too large for foreign-grouping cases;
3274  * add 1/4th of that default.
3275  */
3276  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3277  - 1.0) * 0.25;
3278 
3279  *p_startup_cost *= sort_multiplier;
3280  *p_run_cost *= sort_multiplier;
3281  }
3282 }
List * group_pathkeys
Definition: pathnodes.h:298
Query * parse
Definition: pathnodes.h:177
Cost startup_cost
Definition: pathnodes.h:1154
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:121
Cost total_cost
Definition: pathnodes.h:1155
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:57
List * groupClause
Definition: parsenodes.h:148
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:562

◆ analyze_row_processor()

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

Definition at line 4654 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().

4655 {
4656  int targrows = astate->targrows;
4657  int pos; /* array index to store tuple in */
4658  MemoryContext oldcontext;
4659 
4660  /* Always increment sample row counter. */
4661  astate->samplerows += 1;
4662 
4663  /*
4664  * Determine the slot where this sample row should be stored. Set pos to
4665  * negative value to indicate the row should be skipped.
4666  */
4667  if (astate->numrows < targrows)
4668  {
4669  /* First targrows rows are always included into the sample */
4670  pos = astate->numrows++;
4671  }
4672  else
4673  {
4674  /*
4675  * Now we start replacing tuples in the sample until we reach the end
4676  * of the relation. Same algorithm as in acquire_sample_rows in
4677  * analyze.c; see Jeff Vitter's paper.
4678  */
4679  if (astate->rowstoskip < 0)
4680  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
4681 
4682  if (astate->rowstoskip <= 0)
4683  {
4684  /* Choose a random reservoir element to replace. */
4685  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
4686  Assert(pos >= 0 && pos < targrows);
4687  heap_freetuple(astate->rows[pos]);
4688  }
4689  else
4690  {
4691  /* Skip this tuple. */
4692  pos = -1;
4693  }
4694 
4695  astate->rowstoskip -= 1;
4696  }
4697 
4698  if (pos >= 0)
4699  {
4700  /*
4701  * Create sample tuple from current result row, and store it in the
4702  * position determined above. The tuple has to be created in anl_cxt.
4703  */
4704  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
4705 
4706  astate->rows[pos] = make_tuple_from_result_row(res, row,
4707  astate->rel,
4708  astate->attinmeta,
4709  astate->retrieved_attrs,
4710  NULL,
4711  astate->temp_cxt);
4712 
4713  MemoryContextSwitchTo(oldcontext);
4714  }
4715 }
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:240
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:247
AttInMetadata * attinmeta
Definition: postgres_fdw.c:236
MemoryContext temp_cxt
Definition: postgres_fdw.c:251
#define Assert(condition)
Definition: c.h:746
MemoryContext anl_cxt
Definition: postgres_fdw.c:250
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 4234 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().

4238 {
4239  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4240  TupleTableSlot *resultSlot;
4241  Datum *values;
4242  bool *isnull;
4243  Datum *old_values;
4244  bool *old_isnull;
4245  int i;
4246 
4247  /*
4248  * Use the return tuple slot as a place to store the result tuple.
4249  */
4250  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4251 
4252  /*
4253  * Extract all the values of the scan tuple.
4254  */
4255  slot_getallattrs(slot);
4256  old_values = slot->tts_values;
4257  old_isnull = slot->tts_isnull;
4258 
4259  /*
4260  * Prepare to build the result tuple.
4261  */
4262  ExecClearTuple(resultSlot);
4263  values = resultSlot->tts_values;
4264  isnull = resultSlot->tts_isnull;
4265 
4266  /*
4267  * Transpose data into proper fields of the result tuple.
4268  */
4269  for (i = 0; i < resultTupType->natts; i++)
4270  {
4271  int j = dmstate->attnoMap[i];
4272 
4273  if (j == 0)
4274  {
4275  values[i] = (Datum) 0;
4276  isnull[i] = true;
4277  }
4278  else
4279  {
4280  values[i] = old_values[j - 1];
4281  isnull[i] = old_isnull[j - 1];
4282  }
4283  }
4284 
4285  /*
4286  * Build the virtual tuple.
4287  */
4288  ExecStoreVirtualTuple(resultSlot);
4289 
4290  /*
4291  * If we have any system columns to return, materialize a heap tuple in
4292  * the slot from column values set above and install system columns in
4293  * that tuple.
4294  */
4295  if (dmstate->hasSystemCols)
4296  {
4297  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4298 
4299  /* ctid */
4300  if (dmstate->ctidAttno)
4301  {
4302  ItemPointer ctid = NULL;
4303 
4304  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4305  resultTup->t_self = *ctid;
4306  }
4307 
4308  /*
4309  * And remaining columns
4310  *
4311  * Note: since we currently don't allow the target relation to appear
4312  * on the nullable side of an outer join, any system columns wouldn't
4313  * go to NULL.
4314  *
4315  * Note: no need to care about tableoid here because it will be
4316  * initialized in ExecProcessReturning().
4317  */
4321  }
4322 
4323  /*
4324  * And return the result tuple.
4325  */
4326  return resultSlot;
4327 }
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:482
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 5328 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().

5329 {
5330  ListCell *lc;
5331 
5332  foreach(lc, fpinfo->server->options)
5333  {
5334  DefElem *def = (DefElem *) lfirst(lc);
5335 
5336  if (strcmp(def->defname, "use_remote_estimate") == 0)
5337  fpinfo->use_remote_estimate = defGetBoolean(def);
5338  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
5339  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
5340  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
5341  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
5342  else if (strcmp(def->defname, "extensions") == 0)
5343  fpinfo->shippable_extensions =
5344  ExtractExtensionList(defGetString(def), false);
5345  else if (strcmp(def->defname, "fetch_size") == 0)
5346  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5347  }
5348 }
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 5356 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().

5357 {
5358  ListCell *lc;
5359 
5360  foreach(lc, fpinfo->table->options)
5361  {
5362  DefElem *def = (DefElem *) lfirst(lc);
5363 
5364  if (strcmp(def->defname, "use_remote_estimate") == 0)
5365  fpinfo->use_remote_estimate = defGetBoolean(def);
5366  else if (strcmp(def->defname, "fetch_size") == 0)
5367  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5368  }
5369 }
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 3910 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().

3911 {
3912  bool have_wholerow = false;
3913  List *tlist = NIL;
3914  List *vars;
3915  ListCell *lc;
3916 
3917  Assert(returningList);
3918 
3919  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
3920 
3921  /*
3922  * If there's a whole-row reference to the target relation, then we'll
3923  * need all the columns of the relation.
3924  */
3925  foreach(lc, vars)
3926  {
3927  Var *var = (Var *) lfirst(lc);
3928 
3929  if (IsA(var, Var) &&
3930  var->varno == rtindex &&
3931  var->varattno == InvalidAttrNumber)
3932  {
3933  have_wholerow = true;
3934  break;
3935  }
3936  }
3937 
3938  if (have_wholerow)
3939  {
3940  TupleDesc tupdesc = RelationGetDescr(rel);
3941  int i;
3942 
3943  for (i = 1; i <= tupdesc->natts; i++)
3944  {
3945  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
3946  Var *var;
3947 
3948  /* Ignore dropped attributes. */
3949  if (attr->attisdropped)
3950  continue;
3951 
3952  var = makeVar(rtindex,
3953  i,
3954  attr->atttypid,
3955  attr->atttypmod,
3956  attr->attcollation,
3957  0);
3958 
3959  tlist = lappend(tlist,
3960  makeTargetEntry((Expr *) var,
3961  list_length(tlist) + 1,
3962  NULL,
3963  false));
3964  }
3965  }
3966 
3967  /* Now add any remaining columns to tlist. */
3968  foreach(lc, vars)
3969  {
3970  Var *var = (Var *) lfirst(lc);
3971 
3972  /*
3973  * No need for whole-row references to the target relation. We don't
3974  * need system columns other than ctid and oid either, since those are
3975  * set locally.
3976  */
3977  if (IsA(var, Var) &&
3978  var->varno == rtindex &&
3979  var->varattno <= InvalidAttrNumber &&
3981  continue; /* don't need it */
3982 
3983  if (tlist_member((Expr *) var, tlist))
3984  continue; /* already got it */
3985 
3986  tlist = lappend(tlist,
3987  makeTargetEntry((Expr *) var,
3988  list_length(tlist) + 1,
3989  NULL,
3990  false));
3991  }
3992 
3993  list_free(vars);
3994 
3995  return tlist;
3996 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
#define RelationGetDescr(relation)
Definition: rel.h:482
#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:175
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:73
#define Assert(condition)
Definition: c.h:746
#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 3511 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().

3512 {
3513  char sql[64];
3514  PGresult *res;
3515 
3516  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3517 
3518  /*
3519  * We don't use a PG_TRY block here, so be careful not to throw error
3520  * without releasing the PGresult.
3521  */
3522  res = pgfdw_exec_query(conn, sql);
3523  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3524  pgfdw_report_error(ERROR, res, conn, true, sql);
3525  PQclear(res);
3526 }
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2692
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:731
static unsigned int cursor_number
Definition: connection.c:70
void PQclear(PGresult *res)
Definition: fe-exec.c:694
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:642
#define snprintf
Definition: port.h:215

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

Definition at line 6463 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().

6464 {
6465  const char *attname = NULL;
6466  const char *relname = NULL;
6467  bool is_wholerow = false;
6469 
6470  if (errpos->rel)
6471  {
6472  /* error occurred in a scan against a foreign table */
6473  TupleDesc tupdesc = RelationGetDescr(errpos->rel);
6474  Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
6475 
6476  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
6477  attname = NameStr(attr->attname);
6478  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
6479  attname = "ctid";
6480 
6481  relname = RelationGetRelationName(errpos->rel);
6482  }
6483  else
6484  {
6485  /* error occurred in a scan against a foreign join */
6486  ForeignScanState *fsstate = errpos->fsstate;
6487  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
6488  EState *estate = fsstate->ss.ps.state;
6489  TargetEntry *tle;
6490 
6491  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
6492  errpos->cur_attno - 1);
6493 
6494  /*
6495  * Target list can have Vars and expressions. For Vars, we can get
6496  * its relation, however for expressions we can't. Thus for
6497  * expressions, just show generic context message.
6498  */
6499  if (IsA(tle->expr, Var))
6500  {
6501  RangeTblEntry *rte;
6502  Var *var = (Var *) tle->expr;
6503 
6504  rte = exec_rt_fetch(var->varno, estate);
6505 
6506  if (var->varattno == 0)
6507  is_wholerow = true;
6508  else
6509  attname = get_attname(rte->relid, var->varattno, false);
6510 
6511  relname = get_rel_name(rte->relid);
6512  }
6513  else
6514  errcontext("processing expression at position %d in select list",
6515  errpos->cur_attno);
6516  }
6517 
6518  if (relname)
6519  {
6520  if (is_wholerow)
6521  errcontext("whole-row reference to foreign table \"%s\"", relname);
6522  else if (attname)
6523  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
6524  }
6525 }
ScanState ss
Definition: execnodes.h:1790
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
#define RelationGetDescr(relation)
Definition: rel.h:482
#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:941
NameData relname
Definition: pg_class.h:38
Definition: primnodes.h:181
PlanState ps
Definition: execnodes.h:1328
ForeignScanState * fsstate
Definition: postgres_fdw.c:294
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:490
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:544
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
Index varno
Definition: primnodes.h:184
Plan * plan
Definition: execnodes.h:939
Expr * expr
Definition: primnodes.h:1410
#define errcontext
Definition: elog.h:185
#define NameStr(name)
Definition: c.h:623
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:286
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1840

◆ convert_prep_stmt_params()

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

Definition at line 3780 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().

3783 {
3784  const char **p_values;
3785  int pindex = 0;
3786  MemoryContext oldcontext;
3787 
3788  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
3789 
3790  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums);
3791 
3792  /* 1st parameter should be ctid, if it's in use */
3793  if (tupleid != NULL)
3794  {
3795  /* don't need set_transmission_modes for TID output */
3796  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3797  PointerGetDatum(tupleid));
3798  pindex++;
3799  }
3800 
3801  /* get following parameters from slot */
3802  if (slot != NULL && fmstate->target_attrs != NIL)
3803  {
3804  int nestlevel;
3805  ListCell *lc;
3806 
3807  nestlevel = set_transmission_modes();
3808 
3809  foreach(lc, fmstate->target_attrs)
3810  {
3811  int attnum = lfirst_int(lc);
3812  Datum value;
3813  bool isnull;
3814 
3815  value = slot_getattr(slot, attnum, &isnull);
3816  if (isnull)
3817  p_values[pindex] = NULL;
3818  else
3819  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3820  value);
3821  pindex++;
3822  }
3823 
3824  reset_transmission_modes(nestlevel);
3825  }
3826 
3827  Assert(pindex == fmstate->p_nums);
3828 
3829  MemoryContextSwitchTo(oldcontext);
3830 
3831  return p_values;
3832 }
#define NIL
Definition: pg_list.h:65
#define PointerGetDatum(X)
Definition: postgres.h:556
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int set_transmission_modes(void)
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1577
#define lfirst_int(lc)
Definition: pg_list.h:170
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:185
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
static struct @143 value
MemoryContext temp_cxt
Definition: postgres_fdw.c:188
#define Assert(condition)
Definition: c.h:746
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 3319 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().

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

3543 {
3544  PgFdwModifyState *fmstate;
3545  Relation rel = resultRelInfo->ri_RelationDesc;
3546  TupleDesc tupdesc = RelationGetDescr(rel);
3547  Oid userid;
3548  ForeignTable *table;
3549  UserMapping *user;
3550  AttrNumber n_params;
3551  Oid typefnoid;
3552  bool isvarlena;
3553  ListCell *lc;
3554 
3555  /* Begin constructing PgFdwModifyState. */
3556  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3557  fmstate->rel = rel;
3558 
3559  /*
3560  * Identify which user to do the remote access as. This should match what
3561  * ExecCheckRTEPerms() does.
3562  */
3563  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
3564 
3565  /* Get info about foreign table. */
3566  table = GetForeignTable(RelationGetRelid(rel));
3567  user = GetUserMapping(userid, table->serverid);
3568 
3569  /* Open connection; report that we'll create a prepared statement. */
3570  fmstate->conn = GetConnection(user, true);
3571  fmstate->p_name = NULL; /* prepared statement not made yet */
3572 
3573  /* Set up remote query information. */
3574  fmstate->query = query;
3575  fmstate->target_attrs = target_attrs;
3576  fmstate->has_returning = has_returning;
3577  fmstate->retrieved_attrs = retrieved_attrs;
3578 
3579  /* Create context for per-tuple temp workspace. */
3580  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
3581  "postgres_fdw temporary data",
3583 
3584  /* Prepare for input conversion of RETURNING results. */
3585  if (fmstate->has_returning)
3586  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
3587 
3588  /* Prepare for output conversion of parameters used in prepared stmt. */
3589  n_params = list_length(fmstate->target_attrs) + 1;
3590  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
3591  fmstate->p_nums = 0;
3592 
3593  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3594  {
3595  Assert(subplan != NULL);
3596 
3597  /* Find the ctid resjunk column in the subplan's result */
3599  "ctid");
3600  if (!AttributeNumberIsValid(fmstate->ctidAttno))
3601  elog(ERROR, "could not find junk ctid column");
3602 
3603  /* First transmittable parameter will be ctid */
3604  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
3605  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3606  fmstate->p_nums++;
3607  }
3608 
3609  if (operation == CMD_INSERT || operation == CMD_UPDATE)
3610  {
3611  /* Set up for remaining transmittable parameters */
3612  foreach(lc, fmstate->target_attrs)
3613  {
3614  int attnum = lfirst_int(lc);
3615  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
3616 
3617  Assert(!attr->attisdropped);
3618 
3619  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
3620  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3621  fmstate->p_nums++;
3622  }
3623  }
3624 
3625  Assert(fmstate->p_nums <= n_params);
3626 
3627  /* Initialize auxiliary state */
3628  fmstate->aux_fmstate = NULL;
3629 
3630  return fmstate;
3631 }
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:2784
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:220
#define RelationGetDescr(relation)
Definition: rel.h:482
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:180
MemoryContext es_query_cxt
Definition: execnodes.h:559
#define ERROR
Definition: elog.h:43
#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:191
AttrNumber ctidAttno
Definition: postgres_fdw.c:183
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:185
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:109
MemoryContext temp_cxt
Definition: postgres_fdw.c:188
#define Assert(condition)
Definition: c.h:746
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:214
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:198
AttInMetadata * attinmeta
Definition: postgres_fdw.c:170
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:456

◆ ec_member_matches_foreign()

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

Definition at line 3290 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().

3293 {
3295  Expr *expr = em->em_expr;
3296 
3297  /*
3298  * If we've identified what we're processing in the current scan, we only
3299  * want to match that expression.
3300  */
3301  if (state->current != NULL)
3302  return equal(expr, state->current);
3303 
3304  /*
3305  * Otherwise, ignore anything we've already processed.
3306  */
3307  if (list_member(state->already_used, expr))
3308  return false;
3309 
3310  /* This is the new target to process. */
3311  state->current = expr;
3312  return true;
3313 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3032
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 2676 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().

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

4038 {
4040  ExprContext *econtext = node->ss.ps.ps_ExprContext;
4041  int numParams = dmstate->numParams;
4042  const char **values = dmstate->param_values;
4043 
4044  /*
4045  * Construct array of query parameter values in text format.
4046  */
4047  if (numParams > 0)
4048  process_query_params(econtext,
4049  dmstate->param_flinfo,
4050  dmstate->param_exprs,
4051  values);
4052 
4053  /*
4054  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4055  * to infer types for all parameters. Since we explicitly cast every
4056  * parameter (see deparse.c), the "inference" is trivial and will produce
4057  * the desired result. This allows us to avoid assuming that the remote
4058  * server has the same OIDs we do for the parameters' types.
4059  */
4060  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4061  NULL, values, NULL, NULL, 0))
4062  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4063 
4064  /*
4065  * Get the result, and check for success.
4066  *
4067  * We don't use a PG_TRY block here, so be careful not to throw error
4068  * without releasing the PGresult.
4069  */
4070  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
4071  if (PQresultStatus(dmstate->result) !=
4073  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4074  dmstate->query);
4075 
4076  /* Get the number of rows affected. */
4077  if (dmstate->has_returning)
4078  dmstate->num_tuples = PQntuples(dmstate->result);
4079  else
4080  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4081 }
ScanState ss
Definition: execnodes.h:1790
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:1285
const char ** param_values
Definition: postgres_fdw.c:214
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3110
ExprContext * ps_ExprContext
Definition: execnodes.h:978
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:2769
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2692
PlanState ps
Definition: execnodes.h:1328
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:731
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:666
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 3640 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().

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

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3391 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().

3392 {
3393  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3394  PGresult *volatile res = NULL;
3395  MemoryContext oldcontext;
3396 
3397  /*
3398  * We'll store the tuples in the batch_cxt. First, flush the previous
3399  * batch.
3400  */
3401  fsstate->tuples = NULL;
3402  MemoryContextReset(fsstate->batch_cxt);
3403  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3404 
3405  /* PGresult must be released before leaving this function. */
3406  PG_TRY();
3407  {
3408  PGconn *conn = fsstate->conn;
3409  char sql[64];
3410  int numrows;
3411  int i;
3412 
3413  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3414  fsstate->fetch_size, fsstate->cursor_number);
3415 
3416  res = pgfdw_exec_query(conn, sql);
3417  /* On error, report the original query, not the FETCH. */
3418  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3419  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3420 
3421  /* Convert the data into HeapTuples */
3422  numrows = PQntuples(res);
3423  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3424  fsstate->num_tuples = numrows;
3425  fsstate->next_tuple = 0;
3426 
3427  for (i = 0; i < numrows; i++)
3428  {
3429  Assert(IsA(node->ss.ps.plan, ForeignScan));
3430 
3431  fsstate->tuples[i] =
3433  fsstate->rel,
3434  fsstate->attinmeta,
3435  fsstate->retrieved_attrs,
3436  node,
3437  fsstate->temp_cxt);
3438  }
3439 
3440  /* Update fetch_ct_2 */
3441  if (fsstate->fetch_ct_2 < 2)
3442  fsstate->fetch_ct_2++;
3443 
3444  /* Must be EOF if we didn't get as many tuples as we asked for. */
3445  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3446  }
3447  PG_FINALLY();
3448  {
3449  if (res)
3450  PQclear(res);
3451  }
3452  PG_END_TRY();
3453 
3454  MemoryContextSwitchTo(oldcontext);
3455 }
ScanState ss
Definition: execnodes.h:1790
#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:137
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2769
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2692
unsigned int cursor_number
Definition: postgres_fdw.c:141
PlanState ps
Definition: execnodes.h:1328
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:54
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:731
AttInMetadata * attinmeta
Definition: postgres_fdw.c:133
#define PG_FINALLY()
Definition: elog.h:312
void * palloc0(Size size)
Definition: mcxt.c:981
MemoryContext temp_cxt
Definition: postgres_fdw.c:159
Plan * plan
Definition: execnodes.h:939
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define Assert(condition)
Definition: c.h:746
HeapTuple * tuples
Definition: postgres_fdw.c:149
int i
#define PG_TRY()
Definition: elog.h:295
MemoryContext batch_cxt
Definition: postgres_fdw.c:158
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:642
#define snprintf
Definition: port.h:215
#define PG_END_TRY()
Definition: elog.h:320

◆ find_em_expr_for_input_target()

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

Definition at line 6532 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().

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

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 3876 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().

3877 {
3878  Assert(fmstate != NULL);
3879 
3880  /* If we created a prepared statement, destroy it */
3881  if (fmstate->p_name)
3882  {
3883  char sql[64];
3884  PGresult *res;
3885 
3886  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
3887 
3888  /*
3889  * We don't use a PG_TRY block here, so be careful not to throw error
3890  * without releasing the PGresult.
3891  */
3892  res = pgfdw_exec_query(fmstate->conn, sql);
3893  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3894  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
3895  PQclear(res);
3896  fmstate->p_name = NULL;
3897  }
3898 
3899  /* Release remote connection */
3900  ReleaseConnection(fmstate->conn);
3901  fmstate->conn = NULL;
3902 }
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2692
void ReleaseConnection(PGconn *conn)
Definition: connection.c:594
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:731
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define Assert(condition)
Definition: c.h:746
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:642
#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 5570 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().

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

◆ foreign_join_ok()

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

Definition at line 4999 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().

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

3192 {
3193  PGresult *volatile res = NULL;
3194 
3195  /* PGresult must be released before leaving this function. */
3196  PG_TRY();
3197  {
3198  char *line;
3199  char *p;
3200  int n;
3201 
3202  /*
3203  * Execute EXPLAIN remotely.
3204  */
3205  res = pgfdw_exec_query(conn, sql);
3206  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3207  pgfdw_report_error(ERROR, res, conn, false, sql);
3208 
3209  /*
3210  * Extract cost numbers for topmost plan node. Note we search for a
3211  * left paren from the end of the line to avoid being confused by
3212  * other uses of parentheses.
3213  */
3214  line = PQgetvalue(res, 0, 0);
3215  p = strrchr(line, '(');
3216  if (p == NULL)
3217  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3218  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3219  startup_cost, total_cost, rows, width);
3220  if (n != 4)
3221  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3222  }
3223  PG_FINALLY();
3224  {
3225  if (res)
3226  PQclear(res);
3227  }
3228  PG_END_TRY();
3229 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3163
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2692
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:731
#define PG_FINALLY()
Definition: elog.h:312
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define elog(elevel,...)
Definition: elog.h:214
#define PG_TRY()
Definition: elog.h:295
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:642
#define PG_END_TRY()
Definition: elog.h:320

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 4087 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().

4088 {
4090  EState *estate = node->ss.ps.state;
4091  ResultRelInfo *resultRelInfo = node->resultRelInfo;
4092  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4093  TupleTableSlot *resultSlot;
4094 
4095  Assert(resultRelInfo->ri_projectReturning);
4096 
4097  /* If we didn't get any tuples, must be end of data. */
4098  if (dmstate->next_tuple >= dmstate->num_tuples)
4099  return ExecClearTuple(slot);
4100 
4101  /* Increment the command es_processed count if necessary. */
4102  if (dmstate->set_processed)
4103  estate->es_processed += 1;
4104 
4105  /*
4106  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4107  * tuple. (has_returning is false when the local query is of the form
4108  * "UPDATE/DELETE .. RETURNING 1" for example.)
4109  */
4110  if (!dmstate->has_returning)
4111  {
4112  ExecStoreAllNullTuple(slot);
4113  resultSlot = slot;
4114  }
4115  else
4116  {
4117  /*
4118  * On error, be sure to release the PGresult on the way out. Callers
4119  * do not have PG_TRY blocks to ensure this happens.
4120  */
4121  PG_TRY();
4122  {
4123  HeapTuple newtup;
4124 
4125  newtup = make_tuple_from_result_row(dmstate->result,
4126  dmstate->next_tuple,
4127  dmstate->rel,
4128  dmstate->attinmeta,
4129  dmstate->retrieved_attrs,
4130  node,
4131  dmstate->temp_cxt);
4132  ExecStoreHeapTuple(newtup, slot, false);
4133  }
4134  PG_CATCH();
4135  {
4136  if (dmstate->result)
4137  PQclear(dmstate->result);
4138  PG_RE_THROW();
4139  }
4140  PG_END_TRY();
4141 
4142  /* Get the updated/deleted tuple. */
4143  if (dmstate->rel)
4144  resultSlot = slot;
4145  else
4146  resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4147  }
4148  dmstate->next_tuple++;
4149 
4150  /* Make slot available for evaluation of the local query RETURNING list. */
4151  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4152  resultSlot;
4153 
4154  return slot;
4155 }
ScanState ss
Definition: execnodes.h:1790
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:201
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1331
EState * state
Definition: execnodes.h:941
PlanState ps
Definition: execnodes.h:1328
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1793
MemoryContext temp_cxt
Definition: postgres_fdw.c:227
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define PG_CATCH()
Definition: elog.h:305
#define Assert(condition)
Definition: c.h:746
#define PG_RE_THROW()
Definition: elog.h:336
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
#define PG_TRY()
Definition: elog.h:295
#define PG_END_TRY()
Definition: elog.h:320
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 756 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().

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

◆ get_useful_pathkeys_for_relation()

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

Definition at line 852 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().

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

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

6321 {
6322  HeapTuple tuple;
6323  TupleDesc tupdesc;
6324  Datum *values;
6325  bool *nulls;
6326  ItemPointer ctid = NULL;
6327  ConversionLocation errpos;
6328  ErrorContextCallback errcallback;
6329  MemoryContext oldcontext;
6330  ListCell *lc;
6331  int j;
6332 
6333  Assert(row < PQntuples(res));
6334 
6335  /*
6336  * Do the following work in a temp context that we reset after each tuple.
6337  * This cleans up not only the data we have direct access to, but any
6338  * cruft the I/O functions might leak.
6339  */
6340  oldcontext = MemoryContextSwitchTo(temp_context);
6341 
6342  if (rel)
6343  tupdesc = RelationGetDescr(rel);
6344  else
6345  {
6346  Assert(fsstate);
6347  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
6348  }
6349 
6350  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
6351  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
6352  /* Initialize to nulls for any columns not present in result */
6353  memset(nulls, true, tupdesc->natts * sizeof(bool));
6354 
6355  /*
6356  * Set up and install callback to report where conversion error occurs.
6357  */
6358  errpos.rel = rel;
6359  errpos.cur_attno = 0;
6360  errpos.fsstate = fsstate;
6361  errcallback.callback = conversion_error_callback;
6362  errcallback.arg = (void *) &errpos;
6363  errcallback.previous = error_context_stack;
6364  error_context_stack = &errcallback;
6365 
6366  /*
6367  * i indexes columns in the relation, j indexes columns in the PGresult.
6368  */
6369  j = 0;
6370  foreach(lc, retrieved_attrs)
6371  {
6372  int i = lfirst_int(lc);
6373  char *valstr;
6374 
6375  /* fetch next column's textual value */
6376  if (PQgetisnull(res, row, j))
6377  valstr = NULL;
6378  else
6379  valstr = PQgetvalue(res, row, j);
6380 
6381  /*
6382  * convert value to internal representation
6383  *
6384  * Note: we ignore system columns other than ctid and oid in result
6385  */
6386  errpos.cur_attno = i;
6387  if (i > 0)
6388  {
6389  /* ordinary column */
6390  Assert(i <= tupdesc->natts);
6391  nulls[i - 1] = (valstr == NULL);
6392  /* Apply the input function even to nulls, to support domains */
6393  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
6394  valstr,
6395  attinmeta->attioparams[i - 1],
6396  attinmeta->atttypmods[i - 1]);
6397  }
6398  else if (i == SelfItemPointerAttributeNumber)
6399  {
6400  /* ctid */
6401  if (valstr != NULL)
6402  {
6403  Datum datum;
6404 
6405  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
6406  ctid = (ItemPointer) DatumGetPointer(datum);
6407  }
6408  }
6409  errpos.cur_attno = 0;
6410 
6411  j++;
6412  }
6413 
6414  /* Uninstall error context callback. */
6415  error_context_stack = errcallback.previous;
6416 
6417  /*
6418  * Check we got the expected number of columns. Note: j == 0 and
6419  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
6420  */
6421  if (j > 0 && j != PQnfields(res))
6422  elog(ERROR, "remote query result does not match the foreign table");
6423 
6424  /*
6425  * Build the result tuple in caller's memory context.
6426  */
6427  MemoryContextSwitchTo(oldcontext);
6428 
6429  tuple = heap_form_tuple(tupdesc, values, nulls);
6430 
6431  /*
6432  * If we have a CTID to return, install it in both t_self and t_ctid.
6433  * t_self is the normal place, but if the tuple is converted to a
6434  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
6435  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
6436  */
6437  if (ctid)
6438  tuple->t_self = tuple->t_data->t_ctid = *ctid;
6439 
6440  /*
6441  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
6442  * heap_form_tuple. heap_form_tuple actually creates the tuple with
6443  * DatumTupleFields, not HeapTupleFields, but the executor expects
6444  * HeapTupleFields and will happily extract system columns on that
6445  * assumption. If we don't do this then, for example, the tuple length
6446  * ends up in the xmin field, which isn't what we want.
6447  */
6451 
6452  /* Clean up */
6453  MemoryContextReset(temp_context);
6454 
6455  return tuple;
6456 }
ScanState ss
Definition: execnodes.h:1790
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2777
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3163
#define RelationGetDescr(relation)
Definition: rel.h:482
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int32 * atttypmods
Definition: funcapi.h:47
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1331
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:624
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2769
void(* callback)(void *arg)
Definition: elog.h:229
struct ErrorContextCallback * previous
Definition: elog.h:228
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:92
ForeignScanState * fsstate
Definition: postgres_fdw.c:294
#define ERROR
Definition: elog.h:43
#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:1533
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:55
#define Assert(condition)
Definition: c.h:746
#define DatumGetPointer(X)
Definition: postgres.h:549