PostgreSQL Source Code  git master
postgres_fdw.c File Reference
#include "postgres.h"
#include "postgres_fdw.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 "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, 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_rel (EquivalenceClass *ec, RelOptInfo *rel)
 
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 56 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 50 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.01

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

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 91 of file postgres_fdw.c.

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

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 260 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 65 of file postgres_fdw.c.

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

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

6033 {
6034  Query *parse = root->parse;
6035  PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6036  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
6037  bool has_final_sort = false;
6038  List *pathkeys = NIL;
6039  PgFdwPathExtraData *fpextra;
6040  bool save_use_remote_estimate = false;
6041  double rows;
6042  int width;
6043  Cost startup_cost;
6044  Cost total_cost;
6045  List *fdw_private;
6046  ForeignPath *final_path;
6047 
6048  /*
6049  * Currently, we only support this for SELECT commands
6050  */
6051  if (parse->commandType != CMD_SELECT)
6052  return;
6053 
6054  /*
6055  * No work if there is no FOR UPDATE/SHARE clause and if there is no need
6056  * to add a LIMIT node
6057  */
6058  if (!parse->rowMarks && !extra->limit_needed)
6059  return;
6060 
6061  /* We don't support cases where there are any SRFs in the targetlist */
6062  if (parse->hasTargetSRFs)
6063  return;
6064 
6065  /* Save the input_rel as outerrel in fpinfo */
6066  fpinfo->outerrel = input_rel;
6067 
6068  /*
6069  * Copy foreign table, foreign server, user mapping, FDW options etc.
6070  * details from the input relation's fpinfo.
6071  */
6072  fpinfo->table = ifpinfo->table;
6073  fpinfo->server = ifpinfo->server;
6074  fpinfo->user = ifpinfo->user;
6075  merge_fdw_options(fpinfo, ifpinfo, NULL);
6076 
6077  /*
6078  * If there is no need to add a LIMIT node, there might be a ForeignPath
6079  * in the input_rel's pathlist that implements all behavior of the query.
6080  * Note: we would already have accounted for the query's FOR UPDATE/SHARE
6081  * (if any) before we get here.
6082  */
6083  if (!extra->limit_needed)
6084  {
6085  ListCell *lc;
6086 
6087  Assert(parse->rowMarks);
6088 
6089  /*
6090  * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
6091  * so the input_rel should be a base, join, or ordered relation; and
6092  * if it's an ordered relation, its input relation should be a base or
6093  * join relation.
6094  */
6095  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6096  input_rel->reloptkind == RELOPT_JOINREL ||
6097  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6098  ifpinfo->stage == UPPERREL_ORDERED &&
6099  (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
6100  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
6101 
6102  foreach(lc, input_rel->pathlist)
6103  {
6104  Path *path = (Path *) lfirst(lc);
6105 
6106  /*
6107  * apply_scanjoin_target_to_paths() uses create_projection_path()
6108  * to adjust each of its input paths if needed, whereas
6109  * create_ordered_paths() uses apply_projection_to_path() to do
6110  * that. So the former might have put a ProjectionPath on top of
6111  * the ForeignPath; look through ProjectionPath and see if the
6112  * path underneath it is ForeignPath.
6113  */
6114  if (IsA(path, ForeignPath) ||
6115  (IsA(path, ProjectionPath) &&
6116  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
6117  {
6118  /*
6119  * Create foreign final path; this gets rid of a
6120  * no-longer-needed outer plan (if any), which makes the
6121  * EXPLAIN output look cleaner
6122  */
6123  final_path = create_foreign_upper_path(root,
6124  path->parent,
6125  path->pathtarget,
6126  path->rows,
6127  path->startup_cost,
6128  path->total_cost,
6129  path->pathkeys,
6130  NULL, /* no extra plan */
6131  NULL); /* no fdw_private */
6132 
6133  /* and add it to the final_rel */
6134  add_path(final_rel, (Path *) final_path);
6135 
6136  /* Safe to push down */
6137  fpinfo->pushdown_safe = true;
6138 
6139  return;
6140  }
6141  }
6142 
6143  /*
6144  * If we get here it means no ForeignPaths; since we would already
6145  * have considered pushing down all operations for the query to the
6146  * remote server, give up on it.
6147  */
6148  return;
6149  }
6150 
6151  Assert(extra->limit_needed);
6152 
6153  /*
6154  * If the input_rel is an ordered relation, replace the input_rel with its
6155  * input relation
6156  */
6157  if (input_rel->reloptkind == RELOPT_UPPER_REL &&
6158  ifpinfo->stage == UPPERREL_ORDERED)
6159  {
6160  input_rel = ifpinfo->outerrel;
6161  ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6162  has_final_sort = true;
6163  pathkeys = root->sort_pathkeys;
6164  }
6165 
6166  /* The input_rel should be a base, join, or grouping relation */
6167  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6168  input_rel->reloptkind == RELOPT_JOINREL ||
6169  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6170  ifpinfo->stage == UPPERREL_GROUP_AGG));
6171 
6172  /*
6173  * We try to create a path below by extending a simple foreign path for
6174  * the underlying base, join, or grouping relation to perform the final
6175  * sort (if has_final_sort) and the LIMIT restriction remotely, which is
6176  * stored into the fdw_private list of the resulting path. (We
6177  * re-estimate the costs of sorting the underlying relation, if
6178  * has_final_sort.)
6179  */
6180 
6181  /*
6182  * Assess if it is safe to push down the LIMIT and OFFSET to the remote
6183  * server
6184  */
6185 
6186  /*
6187  * If the underlying relation has any local conditions, the LIMIT/OFFSET
6188  * cannot be pushed down.
6189  */
6190  if (ifpinfo->local_conds)
6191  return;
6192 
6193  /*
6194  * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
6195  * not safe to remote.
6196  */
6197  if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
6198  !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
6199  return;
6200 
6201  /* Safe to push down */
6202  fpinfo->pushdown_safe = true;
6203 
6204  /* Construct PgFdwPathExtraData */
6205  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6206  fpextra->target = root->upper_targets[UPPERREL_FINAL];
6207  fpextra->has_final_sort = has_final_sort;
6208  fpextra->has_limit = extra->limit_needed;
6209  fpextra->limit_tuples = extra->limit_tuples;
6210  fpextra->count_est = extra->count_est;
6211  fpextra->offset_est = extra->offset_est;
6212 
6213  /*
6214  * Estimate the costs of performing the final sort and the LIMIT
6215  * restriction remotely. If has_final_sort is false, we wouldn't need to
6216  * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
6217  * roughly estimated using the costs we already have for the underlying
6218  * relation, in the same way as when use_remote_estimate is false. Since
6219  * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
6220  * false in that case.
6221  */
6222  if (!fpextra->has_final_sort)
6223  {
6224  save_use_remote_estimate = ifpinfo->use_remote_estimate;
6225  ifpinfo->use_remote_estimate = false;
6226  }
6227  estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
6228  &rows, &width, &startup_cost, &total_cost);
6229  if (!fpextra->has_final_sort)
6230  ifpinfo->use_remote_estimate = save_use_remote_estimate;
6231 
6232  /*
6233  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6234  * Items in the list must match order in enum FdwPathPrivateIndex.
6235  */
6236  fdw_private = list_make2(makeInteger(has_final_sort),
6237  makeInteger(extra->limit_needed));
6238 
6239  /*
6240  * Create foreign final path; this gets rid of a no-longer-needed outer
6241  * plan (if any), which makes the EXPLAIN output look cleaner
6242  */
6243  final_path = create_foreign_upper_path(root,
6244  input_rel,
6246  rows,
6247  startup_cost,
6248  total_cost,
6249  pathkeys,
6250  NULL, /* no extra plan */
6251  fdw_private);
6252 
6253  /* and add it to the final_rel */
6254  add_path(final_rel, (Path *) final_path);
6255 }
#define list_make2(x1, x2)
Definition: pg_list.h:229
Node * limitOffset
Definition: parsenodes.h:160
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
PathTarget * pathtarget
Definition: pathnodes.h:1115
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:2194
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:423
RelOptKind reloptkind
Definition: pathnodes.h:638
PathTarget * target
Definition: postgres_fdw.c:271
ForeignServer * server
Definition: postgres_fdw.h:85
List * rowMarks
Definition: parsenodes.h:163
RelOptInfo * outerrel
Definition: postgres_fdw.h:98
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
Cost startup_cost
Definition: pathnodes.h:1125
RelOptInfo * parent
Definition: pathnodes.h:1114
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:86
void * palloc0(Size size)
Definition: mcxt.c:980
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:695
Cost total_cost
Definition: pathnodes.h:1126
CmdType commandType
Definition: parsenodes.h:112
bool hasTargetSRFs
Definition: parsenodes.h:127
List * pathkeys
Definition: pathnodes.h:1128
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
ForeignTable * table
Definition: postgres_fdw.h:84
double rows
Definition: pathnodes.h:1124
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:232
UpperRelationKind stage
Definition: postgres_fdw.h:105
List * pathlist
Definition: pathnodes.h:655
Definition: pg_list.h:50
double Cost
Definition: nodes.h:659
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 5812 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().

5815 {
5816  Query *parse = root->parse;
5817  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5818  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
5819  ForeignPath *grouppath;
5820  double rows;
5821  int width;
5822  Cost startup_cost;
5823  Cost total_cost;
5824 
5825  /* Nothing to be done, if there is no grouping or aggregation required. */
5826  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
5827  !root->hasHavingQual)
5828  return;
5829 
5832 
5833  /* save the input_rel as outerrel in fpinfo */
5834  fpinfo->outerrel = input_rel;
5835 
5836  /*
5837  * Copy foreign table, foreign server, user mapping, FDW options etc.
5838  * details from the input relation's fpinfo.
5839  */
5840  fpinfo->table = ifpinfo->table;
5841  fpinfo->server = ifpinfo->server;
5842  fpinfo->user = ifpinfo->user;
5843  merge_fdw_options(fpinfo, ifpinfo, NULL);
5844 
5845  /*
5846  * Assess if it is safe to push down aggregation and grouping.
5847  *
5848  * Use HAVING qual from extra. In case of child partition, it will have
5849  * translated Vars.
5850  */
5851  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
5852  return;
5853 
5854  /*
5855  * Compute the selectivity and cost of the local_conds, so we don't have
5856  * to do it over again for each path. (Currently we create just a single
5857  * path here, but in future it would be possible that we build more paths
5858  * such as pre-sorted paths as in postgresGetForeignPaths and
5859  * postgresGetForeignJoinPaths.) The best we can do for these conditions
5860  * is to estimate selectivity on the basis of local statistics.
5861  */
5862  fpinfo->local_conds_sel = clauselist_selectivity(root,
5863  fpinfo->local_conds,
5864  0,
5865  JOIN_INNER,
5866  NULL);
5867 
5868  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
5869 
5870  /* Estimate the cost of push down */
5871  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
5872  &rows, &width, &startup_cost, &total_cost);
5873 
5874  /* Now update this information in the fpinfo */
5875  fpinfo->rows = rows;
5876  fpinfo->width = width;
5877  fpinfo->startup_cost = startup_cost;
5878  fpinfo->total_cost = total_cost;
5879 
5880  /* Create and add foreign path to the grouping relation. */
5881  grouppath = create_foreign_upper_path(root,
5882  grouped_rel,
5883  grouped_rel->reltarget,
5884  rows,
5885  startup_cost,
5886  total_cost,
5887  NIL, /* no pathkeys */
5888  NULL,
5889  NIL); /* no fdw_private */
5890 
5891  /* Add generated path into grouped_rel by add_path(). */
5892  add_path(grouped_rel, (Path *) grouppath);
5893 }
#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:2194
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:423
bool hasAggs
Definition: parsenodes.h:125
ForeignServer * server
Definition: postgres_fdw.h:85
List * groupingSets
Definition: parsenodes.h:150
PartitionwiseAggregateType patype
Definition: pathnodes.h:2447
RelOptInfo * outerrel
Definition: postgres_fdw.h:98
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:3819
Selectivity local_conds_sel
Definition: postgres_fdw.h:57
UserMapping * user
Definition: postgres_fdw.h:86
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:695
#define Assert(condition)
Definition: c.h:732
ForeignTable * table
Definition: postgres_fdw.h:84
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:70
struct PathTarget * reltarget
Definition: pathnodes.h:652
double Cost
Definition: nodes.h:659
QualCost local_conds_cost
Definition: postgres_fdw.h:56
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 5903 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().

5905 {
5906  Query *parse = root->parse;
5907  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5908  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
5909  PgFdwPathExtraData *fpextra;
5910  double rows;
5911  int width;
5912  Cost startup_cost;
5913  Cost total_cost;
5914  List *fdw_private;
5915  ForeignPath *ordered_path;
5916  ListCell *lc;
5917 
5918  /* Shouldn't get here unless the query has ORDER BY */
5919  Assert(parse->sortClause);
5920 
5921  /* We don't support cases where there are any SRFs in the targetlist */
5922  if (parse->hasTargetSRFs)
5923  return;
5924 
5925  /* Save the input_rel as outerrel in fpinfo */
5926  fpinfo->outerrel = input_rel;
5927 
5928  /*
5929  * Copy foreign table, foreign server, user mapping, FDW options etc.
5930  * details from the input relation's fpinfo.
5931  */
5932  fpinfo->table = ifpinfo->table;
5933  fpinfo->server = ifpinfo->server;
5934  fpinfo->user = ifpinfo->user;
5935  merge_fdw_options(fpinfo, ifpinfo, NULL);
5936 
5937  /*
5938  * If the input_rel is a base or join relation, we would already have
5939  * considered pushing down the final sort to the remote server when
5940  * creating pre-sorted foreign paths for that relation, because the
5941  * query_pathkeys is set to the root->sort_pathkeys in that case (see
5942  * standard_qp_callback()).
5943  */
5944  if (input_rel->reloptkind == RELOPT_BASEREL ||
5945  input_rel->reloptkind == RELOPT_JOINREL)
5946  {
5947  Assert(root->query_pathkeys == root->sort_pathkeys);
5948 
5949  /* Safe to push down if the query_pathkeys is safe to push down */
5950  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
5951 
5952  return;
5953  }
5954 
5955  /* The input_rel should be a grouping relation */
5956  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
5957  ifpinfo->stage == UPPERREL_GROUP_AGG);
5958 
5959  /*
5960  * We try to create a path below by extending a simple foreign path for
5961  * the underlying grouping relation to perform the final sort remotely,
5962  * which is stored into the fdw_private list of the resulting path.
5963  */
5964 
5965  /* Assess if it is safe to push down the final sort */
5966  foreach(lc, root->sort_pathkeys)
5967  {
5968  PathKey *pathkey = (PathKey *) lfirst(lc);
5969  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
5970  Expr *sort_expr;
5971 
5972  /*
5973  * is_foreign_expr would detect volatile expressions as well, but
5974  * checking ec_has_volatile here saves some cycles.
5975  */
5976  if (pathkey_ec->ec_has_volatile)
5977  return;
5978 
5979  /* Get the sort expression for the pathkey_ec */
5980  sort_expr = find_em_expr_for_input_target(root,
5981  pathkey_ec,
5982  input_rel->reltarget);
5983 
5984  /* If it's unsafe to remote, we cannot push down the final sort */
5985  if (!is_foreign_expr(root, input_rel, sort_expr))
5986  return;
5987  }
5988 
5989  /* Safe to push down */
5990  fpinfo->pushdown_safe = true;
5991 
5992  /* Construct PgFdwPathExtraData */
5993  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
5994  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
5995  fpextra->has_final_sort = true;
5996 
5997  /* Estimate the costs of performing the final sort remotely */
5998  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
5999  &rows, &width, &startup_cost, &total_cost);
6000 
6001  /*
6002  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6003  * Items in the list must match order in enum FdwPathPrivateIndex.
6004  */
6005  fdw_private = list_make2(makeInteger(true), makeInteger(false));
6006 
6007  /* Create foreign ordering path */
6008  ordered_path = create_foreign_upper_path(root,
6009  input_rel,
6011  rows,
6012  startup_cost,
6013  total_cost,
6014  root->sort_pathkeys,
6015  NULL, /* no extra plan */
6016  fdw_private);
6017 
6018  /* and add it to the ordered_rel */
6019  add_path(ordered_rel, (Path *) ordered_path);
6020 }
#define list_make2(x1, x2)
Definition: pg_list.h:229
#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:2194
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:423
List * sortClause
Definition: parsenodes.h:158
RelOptKind reloptkind
Definition: pathnodes.h:638
List * query_pathkeys
Definition: pathnodes.h:296
PathTarget * target
Definition: postgres_fdw.c:271
ForeignServer * server
Definition: postgres_fdw.h:85
RelOptInfo * outerrel
Definition: postgres_fdw.h:98
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:86
void * palloc0(Size size)
Definition: mcxt.c:980
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:695
bool hasTargetSRFs
Definition: parsenodes.h:127
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
ForeignTable * table
Definition: postgres_fdw.h:84
EquivalenceClass * pk_eclass
Definition: pathnodes.h:1011
bool ec_has_volatile
Definition: pathnodes.h:940
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:232
UpperRelationKind stage
Definition: postgres_fdw.h:105
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:652
double Cost
Definition: nodes.h:659
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 5214 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().

5216 {
5217  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
5218  ListCell *lc;
5219 
5220  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
5221 
5222  /* Create one path for each set of pathkeys we found above. */
5223  foreach(lc, useful_pathkeys_list)
5224  {
5225  double rows;
5226  int width;
5227  Cost startup_cost;
5228  Cost total_cost;
5229  List *useful_pathkeys = lfirst(lc);
5230  Path *sorted_epq_path;
5231 
5232  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
5233  &rows, &width, &startup_cost, &total_cost);
5234 
5235  /*
5236  * The EPQ path must be at least as well sorted as the path itself, in
5237  * case it gets used as input to a mergejoin.
5238  */
5239  sorted_epq_path = epq_path;
5240  if (sorted_epq_path != NULL &&
5241  !pathkeys_contained_in(useful_pathkeys,
5242  sorted_epq_path->pathkeys))
5243  sorted_epq_path = (Path *)
5244  create_sort_path(root,
5245  rel,
5246  sorted_epq_path,
5247  useful_pathkeys,
5248  -1.0);
5249 
5250  if (IS_SIMPLE_REL(rel))
5251  add_path(rel, (Path *)
5252  create_foreignscan_path(root, rel,
5253  NULL,
5254  rows,
5255  startup_cost,
5256  total_cost,
5257  useful_pathkeys,
5258  rel->lateral_relids,
5259  sorted_epq_path,
5260  NIL));
5261  else
5262  add_path(rel, (Path *)
5263  create_foreign_join_path(root, rel,
5264  NULL,
5265  rows,
5266  startup_cost,
5267  total_cost,
5268  useful_pathkeys,
5269  rel->lateral_relids,
5270  sorted_epq_path,
5271  NIL));
5272  }
5273 }
#define NIL
Definition: pg_list.h:65
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:864
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:423
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:614
Relids lateral_relids
Definition: pathnodes.h:666
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:2100
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:2144
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:2767
List * pathkeys
Definition: pathnodes.h:1128
#define lfirst(lc)
Definition: pg_list.h:190
Definition: pg_list.h:50
double Cost
Definition: nodes.h:659

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

3184 {
3185  /*
3186  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3187  * side is unlikely to generate properly-sorted output, so it would need
3188  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3189  * if the GROUP BY clause is sort-able but isn't a superset of the given
3190  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3191  * costs by applying the same heuristic as for the scan or join case.
3192  */
3193  if (!grouping_is_sortable(root->parse->groupClause) ||
3194  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3195  {
3196  Path sort_path; /* dummy for result of cost_sort */
3197 
3198  cost_sort(&sort_path,
3199  root,
3200  pathkeys,
3201  *p_startup_cost + *p_run_cost,
3202  retrieved_rows,
3203  width,
3204  0.0,
3205  work_mem,
3206  limit_tuples);
3207 
3208  *p_startup_cost = sort_path.startup_cost;
3209  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3210  }
3211  else
3212  {
3213  /*
3214  * The default extra cost seems too large for foreign-grouping cases;
3215  * add 1/4th of that default.
3216  */
3217  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3218  - 1.0) * 0.25;
3219 
3220  *p_startup_cost *= sort_multiplier;
3221  *p_run_cost *= sort_multiplier;
3222  }
3223 }
List * group_pathkeys
Definition: pathnodes.h:298
Query * parse
Definition: pathnodes.h:177
Cost startup_cost
Definition: pathnodes.h:1125
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:1693
int work_mem
Definition: globals.c:121
Cost total_cost
Definition: pathnodes.h:1126
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:56
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 4603 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().

4604 {
4605  int targrows = astate->targrows;
4606  int pos; /* array index to store tuple in */
4607  MemoryContext oldcontext;
4608 
4609  /* Always increment sample row counter. */
4610  astate->samplerows += 1;
4611 
4612  /*
4613  * Determine the slot where this sample row should be stored. Set pos to
4614  * negative value to indicate the row should be skipped.
4615  */
4616  if (astate->numrows < targrows)
4617  {
4618  /* First targrows rows are always included into the sample */
4619  pos = astate->numrows++;
4620  }
4621  else
4622  {
4623  /*
4624  * Now we start replacing tuples in the sample until we reach the end
4625  * of the relation. Same algorithm as in acquire_sample_rows in
4626  * analyze.c; see Jeff Vitter's paper.
4627  */
4628  if (astate->rowstoskip < 0)
4629  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
4630 
4631  if (astate->rowstoskip <= 0)
4632  {
4633  /* Choose a random reservoir element to replace. */
4634  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
4635  Assert(pos >= 0 && pos < targrows);
4636  heap_freetuple(astate->rows[pos]);
4637  }
4638  else
4639  {
4640  /* Skip this tuple. */
4641  pos = -1;
4642  }
4643 
4644  astate->rowstoskip -= 1;
4645  }
4646 
4647  if (pos >= 0)
4648  {
4649  /*
4650  * Create sample tuple from current result row, and store it in the
4651  * position determined above. The tuple has to be created in anl_cxt.
4652  */
4653  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
4654 
4655  astate->rows[pos] = make_tuple_from_result_row(res, row,
4656  astate->rel,
4657  astate->attinmeta,
4658  astate->retrieved_attrs,
4659  NULL,
4660  astate->temp_cxt);
4661 
4662  MemoryContextSwitchTo(oldcontext);
4663  }
4664 }
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:239
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
double sampler_random_fract(SamplerRandomState randstate)
Definition: sampling.c:238
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
ReservoirStateData rstate
Definition: postgres_fdw.c:246
AttInMetadata * attinmeta
Definition: postgres_fdw.c:235
MemoryContext temp_cxt
Definition: postgres_fdw.c:250
#define Assert(condition)
Definition: c.h:732
MemoryContext anl_cxt
Definition: postgres_fdw.c:249
SamplerRandomState randstate
Definition: sampling.h:50
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:142

◆ apply_returning_filter()

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

Definition at line 4179 of file postgres_fdw.c.

References PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, DatumGetPointer, EState::es_result_relation_info, 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().

4182 {
4183  ResultRelInfo *relInfo = estate->es_result_relation_info;
4184  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4185  TupleTableSlot *resultSlot;
4186  Datum *values;
4187  bool *isnull;
4188  Datum *old_values;
4189  bool *old_isnull;
4190  int i;
4191 
4192  /*
4193  * Use the return tuple slot as a place to store the result tuple.
4194  */
4195  resultSlot = ExecGetReturningSlot(estate, relInfo);
4196 
4197  /*
4198  * Extract all the values of the scan tuple.
4199  */
4200  slot_getallattrs(slot);
4201  old_values = slot->tts_values;
4202  old_isnull = slot->tts_isnull;
4203 
4204  /*
4205  * Prepare to build the result tuple.
4206  */
4207  ExecClearTuple(resultSlot);
4208  values = resultSlot->tts_values;
4209  isnull = resultSlot->tts_isnull;
4210 
4211  /*
4212  * Transpose data into proper fields of the result tuple.
4213  */
4214  for (i = 0; i < resultTupType->natts; i++)
4215  {
4216  int j = dmstate->attnoMap[i];
4217 
4218  if (j == 0)
4219  {
4220  values[i] = (Datum) 0;
4221  isnull[i] = true;
4222  }
4223  else
4224  {
4225  values[i] = old_values[j - 1];
4226  isnull[i] = old_isnull[j - 1];
4227  }
4228  }
4229 
4230  /*
4231  * Build the virtual tuple.
4232  */
4233  ExecStoreVirtualTuple(resultSlot);
4234 
4235  /*
4236  * If we have any system columns to return, materialize a heap tuple in
4237  * the slot from column values set above and install system columns in
4238  * that tuple.
4239  */
4240  if (dmstate->hasSystemCols)
4241  {
4242  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4243 
4244  /* ctid */
4245  if (dmstate->ctidAttno)
4246  {
4247  ItemPointer ctid = NULL;
4248 
4249  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4250  resultTup->t_self = *ctid;
4251  }
4252 
4253  /*
4254  * And remaining columns
4255  *
4256  * Note: since we currently don't allow the target relation to appear
4257  * on the nullable side of an outer join, any system columns wouldn't
4258  * go to NULL.
4259  *
4260  * Note: no need to care about tableoid here because it will be
4261  * initialized in ExecProcessReturning().
4262  */
4266  }
4267 
4268  /*
4269  * And return the result tuple.
4270  */
4271  return resultSlot;
4272 }
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1148
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:426
#define RelationGetDescr(relation)
Definition: rel.h:445
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:355
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:1609
uintptr_t Datum
Definition: postgres.h:367
#define DatumGetPointer(X)
Definition: postgres.h:549
static Datum values[MAXATTR]
Definition: bootstrap.c:167
int i
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:397
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1517
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:319
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:522

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

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

5282 {
5283  ListCell *lc;
5284 
5285  foreach(lc, fpinfo->server->options)
5286  {
5287  DefElem *def = (DefElem *) lfirst(lc);
5288 
5289  if (strcmp(def->defname, "use_remote_estimate") == 0)
5290  fpinfo->use_remote_estimate = defGetBoolean(def);
5291  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
5292  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
5293  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
5294  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
5295  else if (strcmp(def->defname, "extensions") == 0)
5296  fpinfo->shippable_extensions =
5297  ExtractExtensionList(defGetString(def), false);
5298  else if (strcmp(def->defname, "fetch_size") == 0)
5299  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5300  }
5301 }
ForeignServer * server
Definition: postgres_fdw.h:85
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:328
#define lfirst(lc)
Definition: pg_list.h:190
char * defname
Definition: parsenodes.h:730
List * shippable_extensions
Definition: postgres_fdw.h:81
List * options
Definition: foreign.h:42

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

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

5310 {
5311  ListCell *lc;
5312 
5313  foreach(lc, fpinfo->table->options)
5314  {
5315  DefElem *def = (DefElem *) lfirst(lc);
5316 
5317  if (strcmp(def->defname, "use_remote_estimate") == 0)
5318  fpinfo->use_remote_estimate = defGetBoolean(def);
5319  else if (strcmp(def->defname, "fetch_size") == 0)
5320  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5321  }
5322 }
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * defGetString(DefElem *def)
Definition: define.c:49
#define lfirst(lc)
Definition: pg_list.h:190
ForeignTable * table
Definition: postgres_fdw.h:84
List * options
Definition: foreign.h:57
char * defname
Definition: parsenodes.h:730

◆ build_remote_returning()

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

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

3856 {
3857  bool have_wholerow = false;
3858  List *tlist = NIL;
3859  List *vars;
3860  ListCell *lc;
3861 
3862  Assert(returningList);
3863 
3864  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
3865 
3866  /*
3867  * If there's a whole-row reference to the target relation, then we'll
3868  * need all the columns of the relation.
3869  */
3870  foreach(lc, vars)
3871  {
3872  Var *var = (Var *) lfirst(lc);
3873 
3874  if (IsA(var, Var) &&
3875  var->varno == rtindex &&
3876  var->varattno == InvalidAttrNumber)
3877  {
3878  have_wholerow = true;
3879  break;
3880  }
3881  }
3882 
3883  if (have_wholerow)
3884  {
3885  TupleDesc tupdesc = RelationGetDescr(rel);
3886  int i;
3887 
3888  for (i = 1; i <= tupdesc->natts; i++)
3889  {
3890  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
3891  Var *var;
3892 
3893  /* Ignore dropped attributes. */
3894  if (attr->attisdropped)
3895  continue;
3896 
3897  var = makeVar(rtindex,
3898  i,
3899  attr->atttypid,
3900  attr->atttypmod,
3901  attr->attcollation,
3902  0);
3903 
3904  tlist = lappend(tlist,
3905  makeTargetEntry((Expr *) var,
3906  list_length(tlist) + 1,
3907  NULL,
3908  false));
3909  }
3910  }
3911 
3912  /* Now add any remaining columns to tlist. */
3913  foreach(lc, vars)
3914  {
3915  Var *var = (Var *) lfirst(lc);
3916 
3917  /*
3918  * No need for whole-row references to the target relation. We don't
3919  * need system columns other than ctid and oid either, since those are
3920  * set locally.
3921  */
3922  if (IsA(var, Var) &&
3923  var->varno == rtindex &&
3924  var->varattno <= InvalidAttrNumber &&
3926  continue; /* don't need it */
3927 
3928  if (tlist_member((Expr *) var, tlist))
3929  continue; /* already got it */
3930 
3931  tlist = lappend(tlist,
3932  makeTargetEntry((Expr *) var,
3933  list_length(tlist) + 1,
3934  NULL,
3935  false));
3936  }
3937 
3938  list_free(vars);
3939 
3940  return tlist;
3941 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
#define RelationGetDescr(relation)
Definition: rel.h:445
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
Definition: nodes.h:525
AttrNumber varattno
Definition: primnodes.h:172
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
Definition: primnodes.h:167
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:236
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:322
Index varno
Definition: primnodes.h:170
#define PVC_INCLUDE_PLACEHOLDERS
Definition: optimizer.h:174
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:73
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
static int list_length(const List *l)
Definition: pg_list.h:169
#define InvalidAttrNumber
Definition: attnum.h:23
void list_free(List *list)
Definition: list.c:1377
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 3456 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().

3457 {
3458  char sql[64];
3459  PGresult *res;
3460 
3461  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3462 
3463  /*
3464  * We don't use a PG_TRY block here, so be careful not to throw error
3465  * without releasing the PGresult.
3466  */
3467  res = pgfdw_exec_query(conn, sql);
3468  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3469  pgfdw_report_error(ERROR, res, conn, true, sql);
3470  PQclear(res);
3471 }
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:597
static unsigned int cursor_number
Definition: connection.c:69
void PQclear(PGresult *res)
Definition: fe-exec.c:695
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
#define snprintf
Definition: port.h:192

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

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

6416 {
6417  const char *attname = NULL;
6418  const char *relname = NULL;
6419  bool is_wholerow = false;
6421 
6422  if (errpos->rel)
6423  {
6424  /* error occurred in a scan against a foreign table */
6425  TupleDesc tupdesc = RelationGetDescr(errpos->rel);
6426  Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
6427 
6428  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
6429  attname = NameStr(attr->attname);
6430  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
6431  attname = "ctid";
6432 
6433  relname = RelationGetRelationName(errpos->rel);
6434  }
6435  else
6436  {
6437  /* error occurred in a scan against a foreign join */
6438  ForeignScanState *fsstate = errpos->fsstate;
6439  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
6440  EState *estate = fsstate->ss.ps.state;
6441  TargetEntry *tle;
6442 
6443  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
6444  errpos->cur_attno - 1);
6445 
6446  /*
6447  * Target list can have Vars and expressions. For Vars, we can get
6448  * its relation, however for expressions we can't. Thus for
6449  * expressions, just show generic context message.
6450  */
6451  if (IsA(tle->expr, Var))
6452  {
6453  RangeTblEntry *rte;
6454  Var *var = (Var *) tle->expr;
6455 
6456  rte = exec_rt_fetch(var->varno, estate);
6457 
6458  if (var->varattno == 0)
6459  is_wholerow = true;
6460  else
6461  attname = get_attname(rte->relid, var->varattno, false);
6462 
6463  relname = get_rel_name(rte->relid);
6464  }
6465  else
6466  errcontext("processing expression at position %d in select list",
6467  errpos->cur_attno);
6468  }
6469 
6470  if (relname)
6471  {
6472  if (is_wholerow)
6473  errcontext("whole-row reference to foreign table \"%s\"", relname);
6474  else if (attname)
6475  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
6476  }
6477 }
ScanState ss
Definition: execnodes.h:1785
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
#define RelationGetDescr(relation)
Definition: rel.h:445
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
AttrNumber varattno
Definition: primnodes.h:172
List * fdw_scan_tlist
Definition: plannodes.h:615
EState * state
Definition: execnodes.h:942
NameData relname
Definition: pg_class.h:35
Definition: primnodes.h:167
PlanState ps
Definition: execnodes.h:1331
ForeignScanState * fsstate
Definition: postgres_fdw.c:293
NameData attname
Definition: pg_attribute.h:40
#define list_nth_node(type, list, n)
Definition: pg_list.h:305
#define RelationGetRelationName(relation)
Definition: rel.h:453
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:537
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
Index varno
Definition: primnodes.h:170
Plan * plan
Definition: execnodes.h:940
Expr * expr
Definition: primnodes.h:1393
#define errcontext
Definition: elog.h:183
#define NameStr(name)
Definition: c.h:609
void * arg
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:775
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
AttrNumber cur_attno
Definition: postgres_fdw.c:285
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730

◆ convert_prep_stmt_params()

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

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

3728 {
3729  const char **p_values;
3730  int pindex = 0;
3731  MemoryContext oldcontext;
3732 
3733  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
3734 
3735  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums);
3736 
3737  /* 1st parameter should be ctid, if it's in use */
3738  if (tupleid != NULL)
3739  {
3740  /* don't need set_transmission_modes for TID output */
3741  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3742  PointerGetDatum(tupleid));
3743  pindex++;
3744  }
3745 
3746  /* get following parameters from slot */
3747  if (slot != NULL && fmstate->target_attrs != NIL)
3748  {
3749  int nestlevel;
3750  ListCell *lc;
3751 
3752  nestlevel = set_transmission_modes();
3753 
3754  foreach(lc, fmstate->target_attrs)
3755  {
3756  int attnum = lfirst_int(lc);
3757  Datum value;
3758  bool isnull;
3759 
3760  value = slot_getattr(slot, attnum, &isnull);
3761  if (isnull)
3762  p_values[pindex] = NULL;
3763  else
3764  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3765  value);
3766  pindex++;
3767  }
3768 
3769  reset_transmission_modes(nestlevel);
3770  }
3771 
3772  Assert(pindex == fmstate->p_nums);
3773 
3774  MemoryContextSwitchTo(oldcontext);
3775 
3776  return p_values;
3777 }
#define NIL
Definition: pg_list.h:65
#define PointerGetDatum(X)
Definition: postgres.h:556
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
static struct @145 value
int set_transmission_modes(void)
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1575
#define lfirst_int(lc)
Definition: pg_list.h:191
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:184
uintptr_t Datum
Definition: postgres.h:367
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:382
int16 attnum
Definition: pg_attribute.h:79
MemoryContext temp_cxt
Definition: postgres_fdw.c:187
#define Assert(condition)
Definition: c.h:732
void reset_transmission_modes(int nestlevel)
void * palloc(Size size)
Definition: mcxt.c:949

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

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

3261 {
3262  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3263  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3264  int numParams = fsstate->numParams;
3265  const char **values = fsstate->param_values;
3266  PGconn *conn = fsstate->conn;
3268  PGresult *res;
3269 
3270  /*
3271  * Construct array of query parameter values in text format. We do the
3272  * conversions in the short-lived per-tuple context, so as not to cause a
3273  * memory leak over repeated scans.
3274  */
3275  if (numParams > 0)
3276  {
3277  MemoryContext oldcontext;
3278 
3279  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3280 
3281  process_query_params(econtext,
3282  fsstate->param_flinfo,
3283  fsstate->param_exprs,
3284  values);
3285 
3286  MemoryContextSwitchTo(oldcontext);
3287  }
3288 
3289  /* Construct the DECLARE CURSOR command */
3290  initStringInfo(&buf);
3291  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3292  fsstate->cursor_number, fsstate->query);
3293 
3294  /*
3295  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3296  * to infer types for all parameters. Since we explicitly cast every
3297  * parameter (see deparse.c), the "inference" is trivial and will produce
3298  * the desired result. This allows us to avoid assuming that the remote
3299  * server has the same OIDs we do for the parameters' types.
3300  */
3301  if (!PQsendQueryParams(conn, buf.data, numParams,
3302  NULL, values, NULL, NULL, 0))
3303  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3304 
3305  /*
3306  * Get the result, and check for success.
3307  *
3308  * We don't use a PG_TRY block here, so be careful not to throw error
3309  * without releasing the PGresult.
3310  */
3311  res = pgfdw_get_result(conn, buf.data);
3312  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3313  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3314  PQclear(res);
3315 
3316  /* Mark the cursor as created, and show no tuples have been retrieved */
3317  fsstate->cursor_exists = true;
3318  fsstate->tuples = NULL;
3319  fsstate->num_tuples = 0;
3320  fsstate->next_tuple = 0;
3321  fsstate->fetch_ct_2 = 0;
3322  fsstate->eof_reached = false;
3323 
3324  /* Clean up */
3325  pfree(buf.data);
3326 }
ScanState ss
Definition: execnodes.h:1785
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:1286
List * param_exprs
Definition: postgres_fdw.c:144
ExprContext * ps_ExprContext
Definition: execnodes.h:979
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:2693
unsigned int cursor_number
Definition: postgres_fdw.c:140
PlanState ps
Definition: execnodes.h:1331
void pfree(void *pointer)
Definition: mcxt.c:1056
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define ERROR
Definition: elog.h:43
const char ** param_values
Definition: postgres_fdw.c:145
PGconn * conn
Definition: streamutil.c:56
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:597
FmgrInfo * param_flinfo
Definition: postgres_fdw.c:143
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
void PQclear(PGresult *res)
Definition: fe-exec.c:695
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:532
HeapTuple * tuples
Definition: postgres_fdw.c:148
static Datum values[MAXATTR]
Definition: bootstrap.c:167

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

3488 {
3489  PgFdwModifyState *fmstate;
3490  Relation rel = resultRelInfo->ri_RelationDesc;
3491  TupleDesc tupdesc = RelationGetDescr(rel);
3492  Oid userid;
3493  ForeignTable *table;
3494  UserMapping *user;
3495  AttrNumber n_params;
3496  Oid typefnoid;
3497  bool isvarlena;
3498  ListCell *lc;
3499 
3500  /* Begin constructing PgFdwModifyState. */
3501  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3502  fmstate->rel = rel;
3503 
3504  /*
3505  * Identify which user to do the remote access as. This should match what
3506  * ExecCheckRTEPerms() does.
3507  */
3508  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
3509 
3510  /* Get info about foreign table. */
3511  table = GetForeignTable(RelationGetRelid(rel));
3512  user = GetUserMapping(userid, table->serverid);
3513 
3514  /* Open connection; report that we'll create a prepared statement. */
3515  fmstate->conn = GetConnection(user, true);
3516  fmstate->p_name = NULL; /* prepared statement not made yet */
3517 
3518  /* Set up remote query information. */
3519  fmstate->query = query;
3520  fmstate->target_attrs = target_attrs;
3521  fmstate->has_returning = has_returning;
3522  fmstate->retrieved_attrs = retrieved_attrs;
3523 
3524  /* Create context for per-tuple temp workspace. */
3525  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
3526  "postgres_fdw temporary data",
3528 
3529  /* Prepare for input conversion of RETURNING results. */
3530  if (fmstate->has_returning)
3531  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
3532 
3533  /* Prepare for output conversion of parameters used in prepared stmt. */
3534  n_params = list_length(fmstate->target_attrs) + 1;
3535  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
3536  fmstate->p_nums = 0;
3537 
3538  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3539  {
3540  Assert(subplan != NULL);
3541 
3542  /* Find the ctid resjunk column in the subplan's result */
3544  "ctid");
3545  if (!AttributeNumberIsValid(fmstate->ctidAttno))
3546  elog(ERROR, "could not find junk ctid column");
3547 
3548  /* First transmittable parameter will be ctid */
3549  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
3550  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3551  fmstate->p_nums++;
3552  }
3553 
3554  if (operation == CMD_INSERT || operation == CMD_UPDATE)
3555  {
3556  /* Set up for remaining transmittable parameters */
3557  foreach(lc, fmstate->target_attrs)
3558  {
3559  int attnum = lfirst_int(lc);
3560  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
3561 
3562  Assert(!attr->attisdropped);
3563 
3564  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
3565  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3566  fmstate->p_nums++;
3567  }
3568  }
3569 
3570  Assert(fmstate->p_nums <= n_params);
3571 
3572  /* Initialize auxiliary state */
3573  fmstate->aux_fmstate = NULL;
3574 
3575  return fmstate;
3576 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:411
#define AllocSetContextCreate
Definition: memutils.h:170
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2674
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:220
#define RelationGetDescr(relation)
Definition: rel.h:445
Oid GetUserId(void)
Definition: miscinit.c:380
#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:179
MemoryContext es_query_cxt
Definition: execnodes.h:550
#define ERROR
Definition: elog.h:43
#define lfirst_int(lc)
Definition: pg_list.h:191
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:124
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
struct PgFdwModifyState * aux_fmstate
Definition: postgres_fdw.c:190
AttrNumber ctidAttno
Definition: postgres_fdw.c:182
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:184
void * palloc0(Size size)
Definition: mcxt.c:980
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2063
int16 attnum
Definition: pg_attribute.h:79
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
MemoryContext temp_cxt
Definition: postgres_fdw.c:187
#define Assert(condition)
Definition: c.h:732
Oid serverid
Definition: foreign.h:56
static int list_length(const List *l)
Definition: pg_list.h:169
List * targetlist
Definition: plannodes.h:140
static char * user
Definition: pg_regress.c:95
#define elog(elevel,...)
Definition: elog.h:226
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:198
AttInMetadata * attinmeta
Definition: postgres_fdw.c:169
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:419

◆ ec_member_matches_foreign()

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

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

3234 {
3236  Expr *expr = em->em_expr;
3237 
3238  /*
3239  * If we've identified what we're processing in the current scan, we only
3240  * want to match that expression.
3241  */
3242  if (state->current != NULL)
3243  return equal(expr, state->current);
3244 
3245  /*
3246  * Otherwise, ignore anything we've already processed.
3247  */
3248  if (list_member(state->already_used, expr))
3249  return false;
3250 
3251  /* This is the new target to process. */
3252  state->current = expr;
3253  return true;
3254 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3008
bool list_member(const List *list, const void *datum)
Definition: list.c:614
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 2613 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().

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

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

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

3983 {
3985  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3986  int numParams = dmstate->numParams;
3987  const char **values = dmstate->param_values;
3988 
3989  /*
3990  * Construct array of query parameter values in text format.
3991  */
3992  if (numParams > 0)
3993  process_query_params(econtext,
3994  dmstate->param_flinfo,
3995  dmstate->param_exprs,
3996  values);
3997 
3998  /*
3999  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4000  * to infer types for all parameters. Since we explicitly cast every
4001  * parameter (see deparse.c), the "inference" is trivial and will produce
4002  * the desired result. This allows us to avoid assuming that the remote
4003  * server has the same OIDs we do for the parameters' types.
4004  */
4005  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4006  NULL, values, NULL, NULL, 0))
4007  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4008 
4009  /*
4010  * Get the result, and check for success.
4011  *
4012  * We don't use a PG_TRY block here, so be careful not to throw error
4013  * without releasing the PGresult.
4014  */
4015  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
4016  if (PQresultStatus(dmstate->result) !=
4018  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4019  dmstate->query);
4020 
4021  /* Get the number of rows affected. */
4022  if (dmstate->has_returning)
4023  dmstate->num_tuples = PQntuples(dmstate->result);
4024  else
4025  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4026 }
ScanState ss
Definition: execnodes.h:1785
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:1286
const char ** param_values
Definition: postgres_fdw.c:213
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3111
ExprContext * ps_ExprContext
Definition: execnodes.h:979
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:2770
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
PlanState ps
Definition: execnodes.h:1331
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:597
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:532
static Datum values[MAXATTR]
Definition: bootstrap.c:167

◆ execute_foreign_modify()

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

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

3590 {
3591  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
3592  ItemPointer ctid = NULL;
3593  const char **p_values;
3594  PGresult *res;
3595  int n_rows;
3596 
3597  /* The operation should be INSERT, UPDATE, or DELETE */
3598  Assert(operation == CMD_INSERT ||
3599  operation == CMD_UPDATE ||
3600  operation == CMD_DELETE);
3601 
3602  /* Set up the prepared statement on the remote server, if we didn't yet */
3603  if (!fmstate->p_name)
3604  prepare_foreign_modify(fmstate);
3605 
3606  /*
3607  * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
3608  */
3609  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3610  {
3611  Datum datum;
3612  bool isNull;
3613 
3614  datum = ExecGetJunkAttribute(planSlot,
3615  fmstate->ctidAttno,
3616  &isNull);
3617  /* shouldn't ever get a null result... */
3618  if (isNull)
3619  elog(ERROR, "ctid is NULL");
3620  ctid = (ItemPointer) DatumGetPointer(datum);
3621  }
3622 
3623  /* Convert parameters needed by prepared statement to text form */
3624  p_values = convert_prep_stmt_params(fmstate, ctid, slot);
3625 
3626  /*
3627  * Execute the prepared statement.
3628  */
3629  if (!PQsendQueryPrepared(fmstate->conn,
3630  fmstate->p_name,
3631  fmstate->p_nums,
3632  p_values,
3633  NULL,
3634  NULL,
3635  0))
3636  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
3637 
3638  /*
3639  * Get the result, and check for success.
3640  *
3641  * We don't use a PG_TRY block here, so be careful not to throw error
3642  * without releasing the PGresult.
3643  */
3644  res = pgfdw_get_result(fmstate->conn, fmstate->query);
3645  if (PQresultStatus(res) !=
3647  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
3648 
3649  /* Check number of rows affected, and fetch RETURNING tuple if any */
3650  if (fmstate->has_returning)
3651  {
3652  n_rows = PQntuples(res);
3653  if (n_rows > 0)
3654  store_returning_result(fmstate, slot, res);
3655  }
3656  else
3657  n_rows = atoi(PQcmdTuples(res));
3658 
3659  /* And clean up */
3660  PQclear(res);
3661 
3662  MemoryContextReset(fmstate->temp_cxt);
3663 
3664  /*
3665  * Return NULL if nothing was inserted/updated/deleted on the remote end
3666  */
3667  return (n_rows > 0) ? slot : NULL;
3668 }
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3111
static void store_returning_result(PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2770
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
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:597
AttrNumber ctidAttno
Definition: postgres_fdw.c:182
uintptr_t Datum
Definition: postgres.h:367
void * ri_FdwState
Definition: execnodes.h:443
void PQclear(PGresult *res)
Definition: fe-exec.c:695
MemoryContext temp_cxt
Definition: postgres_fdw.c:187
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:532
#define Assert(condition)
Definition: c.h:732
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:1428
#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:226

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3332 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_CATCH, PG_END_TRY, PG_RE_THROW, 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().

3333 {
3334  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3335  PGresult *volatile res = NULL;
3336  MemoryContext oldcontext;
3337 
3338  /*
3339  * We'll store the tuples in the batch_cxt. First, flush the previous
3340  * batch.
3341  */
3342  fsstate->tuples = NULL;
3343  MemoryContextReset(fsstate->batch_cxt);
3344  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3345 
3346  /* PGresult must be released before leaving this function. */
3347  PG_TRY();
3348  {
3349  PGconn *conn = fsstate->conn;
3350  char sql[64];
3351  int numrows;
3352  int i;
3353 
3354  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3355  fsstate->fetch_size, fsstate->cursor_number);
3356 
3357  res = pgfdw_exec_query(conn, sql);
3358  /* On error, report the original query, not the FETCH. */
3359  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3360  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3361 
3362  /* Convert the data into HeapTuples */
3363  numrows = PQntuples(res);
3364  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3365  fsstate->num_tuples = numrows;
3366  fsstate->next_tuple = 0;
3367 
3368  for (i = 0; i < numrows; i++)
3369  {
3370  Assert(IsA(node->ss.ps.plan, ForeignScan));
3371 
3372  fsstate->tuples[i] =
3374  fsstate->rel,
3375  fsstate->attinmeta,
3376  fsstate->retrieved_attrs,
3377  node,
3378  fsstate->temp_cxt);
3379  }
3380 
3381  /* Update fetch_ct_2 */
3382  if (fsstate->fetch_ct_2 < 2)
3383  fsstate->fetch_ct_2++;
3384 
3385  /* Must be EOF if we didn't get as many tuples as we asked for. */
3386  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3387 
3388  PQclear(res);
3389  res = NULL;
3390  }
3391  PG_CATCH();
3392  {
3393  if (res)
3394  PQclear(res);
3395  PG_RE_THROW();
3396  }
3397  PG_END_TRY();
3398 
3399  MemoryContextSwitchTo(oldcontext);
3400 }
ScanState ss
Definition: execnodes.h:1785
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
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:136
List * retrieved_attrs
Definition: postgres_fdw.c:136
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2770
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
unsigned int cursor_number
Definition: postgres_fdw.c:140
PlanState ps
Definition: execnodes.h:1331
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:56
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:597
AttInMetadata * attinmeta
Definition: postgres_fdw.c:132
void * palloc0(Size size)
Definition: mcxt.c:980
MemoryContext temp_cxt
Definition: postgres_fdw.c:158
Plan * plan
Definition: execnodes.h:940
void PQclear(PGresult *res)
Definition: fe-exec.c:695
#define PG_CATCH()
Definition: elog.h:310
#define Assert(condition)
Definition: c.h:732
HeapTuple * tuples
Definition: postgres_fdw.c:148
#define PG_RE_THROW()
Definition: elog.h:331
int i
#define PG_TRY()
Definition: elog.h:301
MemoryContext batch_cxt
Definition: postgres_fdw.c:157
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
#define snprintf
Definition: port.h:192
#define PG_END_TRY()
Definition: elog.h:317

◆ find_em_expr_for_input_target()

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

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

6516 {
6517  ListCell *lc1;
6518  int i;
6519 
6520  i = 0;
6521  foreach(lc1, target->exprs)
6522  {
6523  Expr *expr = (Expr *) lfirst(lc1);
6524  Index sgref = get_pathtarget_sortgroupref(target, i);
6525  ListCell *lc2;
6526 
6527  /* Ignore non-sort expressions */
6528  if (sgref == 0 ||
6530  root->parse->sortClause) == NULL)
6531  {
6532  i++;
6533  continue;
6534  }
6535 
6536  /* We ignore binary-compatible relabeling on both ends */
6537  while (expr && IsA(expr, RelabelType))
6538  expr = ((RelabelType *) expr)->arg;
6539 
6540  /* Locate an EquivalenceClass member matching this expr, if any */
6541  foreach(lc2, ec->ec_members)
6542  {
6544  Expr *em_expr;
6545 
6546  /* Don't match constants */
6547  if (em->em_is_const)
6548  continue;
6549 
6550  /* Ignore child members */
6551  if (em->em_is_child)
6552  continue;
6553 
6554  /* Match if same expression (after stripping relabel) */
6555  em_expr = em->em_expr;
6556  while (em_expr && IsA(em_expr, RelabelType))
6557  em_expr = ((RelabelType *) em_expr)->arg;
6558 
6559  if (equal(em_expr, expr))
6560  return em->em_expr;
6561  }
6562 
6563  i++;
6564  }
6565 
6566  elog(ERROR, "could not find pathkey item to sort");
6567  return NULL; /* keep compiler quiet */
6568 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
Query * parse
Definition: pathnodes.h:177
List * sortClause
Definition: parsenodes.h:158
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3008
#define ERROR
Definition: elog.h:43
List * exprs
Definition: pathnodes.h:1044
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:465
unsigned int Index
Definition: c.h:475
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1051
#define lfirst(lc)
Definition: pg_list.h:190
#define elog(elevel,...)
Definition: elog.h:226
int i
void * arg
List * ec_members
Definition: pathnodes.h:934

◆ find_em_expr_for_rel()

Expr* find_em_expr_for_rel ( EquivalenceClass ec,
RelOptInfo rel 
)

Definition at line 6484 of file postgres_fdw.c.

References bms_is_empty(), bms_is_subset(), EquivalenceClass::ec_members, EquivalenceMember::em_expr, EquivalenceMember::em_relids, lfirst, and RelOptInfo::relids.

Referenced by appendOrderByClause(), and get_useful_pathkeys_for_relation().

6485 {
6486  ListCell *lc_em;
6487 
6488  foreach(lc_em, ec->ec_members)
6489  {
6490  EquivalenceMember *em = lfirst(lc_em);
6491 
6492  if (bms_is_subset(em->em_relids, rel->relids) &&
6493  !bms_is_empty(em->em_relids))
6494  {
6495  /*
6496  * If there is more than one equivalence member whose Vars are
6497  * taken entirely from this relation, we'll be content to choose
6498  * any one of those.
6499  */
6500  return em->em_expr;
6501  }
6502  }
6503 
6504  /* We didn't find any suitable equivalence class expression */
6505  return NULL;
6506 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:315
Relids relids
Definition: pathnodes.h:641
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
#define lfirst(lc)
Definition: pg_list.h:190
List * ec_members
Definition: pathnodes.h:934

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

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

3822 {
3823  Assert(fmstate != NULL);
3824 
3825  /* If we created a prepared statement, destroy it */
3826  if (fmstate->p_name)
3827  {
3828  char sql[64];
3829  PGresult *res;
3830 
3831  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
3832 
3833  /*
3834  * We don't use a PG_TRY block here, so be careful not to throw error
3835  * without releasing the PGresult.
3836  */
3837  res = pgfdw_exec_query(fmstate->conn, sql);
3838  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3839  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
3840  PQclear(res);
3841  fmstate->p_name = NULL;
3842  }
3843 
3844  /* Release remote connection */
3845  ReleaseConnection(fmstate->conn);
3846  fmstate->conn = NULL;
3847 }
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:597
void PQclear(PGresult *res)
Definition: fe-exec.c:695
#define Assert(condition)
Definition: c.h:732
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
#define snprintf
Definition: port.h:192

◆ foreign_grouping_ok()

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

Definition at line 5523 of file postgres_fdw.c.

References add_to_flat_tlist(), appendStringInfo(), Assert, RestrictInfo::clause, StringInfoData::data, 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(), makeStringInfo(), makeTargetEntry(), NIL, PgFdwRelationInfo::outerrel, PlannerInfo::parse, 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().

5525 {
5526  Query *query = root->parse;
5527  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
5528  PathTarget *grouping_target = grouped_rel->reltarget;
5529  PgFdwRelationInfo *ofpinfo;
5530  ListCell *lc;
5531  int i;
5532  List *tlist = NIL;
5533 
5534  /* We currently don't support pushing Grouping Sets. */
5535  if (query->groupingSets)
5536  return false;
5537 
5538  /* Get the fpinfo of the underlying scan relation. */
5539  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
5540 
5541  /*
5542  * If underlying scan relation has any local conditions, those conditions
5543  * are required to be applied before performing aggregation. Hence the
5544  * aggregate cannot be pushed down.
5545  */
5546  if (ofpinfo->local_conds)
5547  return false;
5548 
5549  /*
5550  * Examine grouping expressions, as well as other expressions we'd need to
5551  * compute, and check whether they are safe to push down to the foreign
5552  * server. All GROUP BY expressions will be part of the grouping target
5553  * and thus there is no need to search for them separately. Add grouping
5554  * expressions into target list which will be passed to foreign server.
5555  *
5556  * A tricky fine point is that we must not put any expression into the
5557  * target list that is just a foreign param (that is, something that
5558  * deparse.c would conclude has to be sent to the foreign server). If we
5559  * do, the expression will also appear in the fdw_exprs list of the plan
5560  * node, and setrefs.c will get confused and decide that the fdw_exprs
5561  * entry is actually a reference to the fdw_scan_tlist entry, resulting in
5562  * a broken plan. Somewhat oddly, it's OK if the expression contains such
5563  * a node, as long as it's not at top level; then no match is possible.
5564  */
5565  i = 0;
5566  foreach(lc, grouping_target->exprs)
5567  {
5568  Expr *expr = (Expr *) lfirst(lc);
5569  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
5570  ListCell *l;
5571 
5572  /* Check whether this expression is part of GROUP BY clause */
5573  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
5574  {
5575  TargetEntry *tle;
5576 
5577  /*
5578  * If any GROUP BY expression is not shippable, then we cannot
5579  * push down aggregation to the foreign server.
5580  */
5581  if (!is_foreign_expr(root, grouped_rel, expr))
5582  return false;
5583 
5584  /*
5585  * If it would be a foreign param, we can't put it into the tlist,
5586  * so we have to fail.
5587  */
5588  if (is_foreign_param(root, grouped_rel, expr))
5589  return false;
5590 
5591  /*
5592  * Pushable, so add to tlist. We need to create a TLE for this
5593  * expression and apply the sortgroupref to it. We cannot use
5594  * add_to_flat_tlist() here because that avoids making duplicate
5595  * entries in the tlist. If there are duplicate entries with
5596  * distinct sortgrouprefs, we have to duplicate that situation in
5597  * the output tlist.
5598  */
5599  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
5600  tle->ressortgroupref = sgref;
5601  tlist = lappend(tlist, tle);
5602  }
5603  else
5604  {
5605  /*
5606  * Non-grouping expression we need to compute. Can we ship it
5607  * as-is to the foreign server?
5608  */
5609  if (is_foreign_expr(root, grouped_rel, expr) &&
5610  !is_foreign_param(root, grouped_rel, expr))
5611  {
5612  /* Yes, so add to tlist as-is; OK to suppress duplicates */
5613  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5614  }
5615  else
5616  {
5617  /* Not pushable as a whole; extract its Vars and aggregates */
5618  List *aggvars;
5619 
5620  aggvars = pull_var_clause((Node *) expr,
5622 
5623  /*
5624  * If any aggregate expression is not shippable, then we
5625  * cannot push down aggregation to the foreign server. (We
5626  * don't have to check is_foreign_param, since that certainly
5627  * won't return true for any such expression.)
5628  */
5629  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
5630  return false;
5631 
5632  /*
5633  * Add aggregates, if any, into the targetlist. Plain Vars
5634  * outside an aggregate can be ignored, because they should be
5635  * either same as some GROUP BY column or part of some GROUP
5636  * BY expression. In either case, they are already part of
5637  * the targetlist and thus no need to add them again. In fact
5638  * including plain Vars in the tlist when they do not match a
5639  * GROUP BY column would cause the foreign server to complain
5640  * that the shipped query is invalid.
5641  */
5642  foreach(l, aggvars)
5643  {
5644  Expr *expr = (Expr *) lfirst(l);
5645 
5646  if (IsA(expr, Aggref))
5647  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5648  }
5649  }
5650  }
5651 
5652  i++;
5653  }
5654 
5655  /*
5656  * Classify the pushable and non-pushable HAVING clauses and save them in
5657  * remote_conds and local_conds of the grouped rel's fpinfo.
5658  */
5659  if (havingQual)
5660  {
5661  ListCell *lc;
5662 
5663  foreach(lc, (List *) havingQual)
5664  {
5665  Expr *expr = (Expr *) lfirst(lc);
5666  RestrictInfo *rinfo;
5667 
5668  /*
5669  * Currently, the core code doesn't wrap havingQuals in
5670  * RestrictInfos, so we must make our own.
5671  */
5672  Assert(!IsA(expr, RestrictInfo));
5673  rinfo = make_restrictinfo(expr,
5674  true,
5675  false,
5676  false,
5677  root->qual_security_level,
5678  grouped_rel->relids,
5679  NULL,
5680  NULL);
5681  if (is_foreign_expr(root, grouped_rel, expr))
5682  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5683  else
5684  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5685  }
5686  }
5687 
5688  /*
5689  * If there are any local conditions, pull Vars and aggregates from it and
5690  * check whether they are safe to pushdown or not.
5691  */
5692  if (fpinfo->local_conds)
5693  {
5694  List *aggvars = NIL;
5695  ListCell *lc;
5696 
5697  foreach(lc, fpinfo->local_conds)
5698  {
5699  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5700 
5701  aggvars = list_concat(aggvars,
5702  pull_var_clause((Node *) rinfo->clause,
5704  }
5705 
5706  foreach(lc, aggvars)
5707  {
5708  Expr *expr = (Expr *) lfirst(lc);
5709 
5710  /*
5711  * If aggregates within local conditions are not safe to push
5712  * down, then we cannot push down the query. Vars are already
5713  * part of GROUP BY clause which are checked above, so no need to
5714  * access them again here. Again, we need not check
5715  * is_foreign_param for a foreign aggregate.
5716  */
5717  if (IsA(expr, Aggref))
5718  {
5719  if (!is_foreign_expr(root, grouped_rel, expr))
5720  return false;
5721 
5722  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5723  }
5724  }
5725  }
5726 
5727  /* Store generated targetlist */
5728  fpinfo->grouped_tlist = tlist;
5729 
5730  /* Safe to pushdown */
5731  fpinfo->pushdown_safe = true;
5732 
5733  /*
5734  * Set # of retrieved rows and cached relation costs to some negative
5735  * value, so that we can detect when they are set to some sensible values,
5736  * during one (usually the first) of the calls to estimate_path_cost_size.
5737  */
5738  fpinfo->retrieved_rows = -1;
5739  fpinfo->rel_startup_cost = -1;
5740  fpinfo->rel_total_cost = -1;
5741 
5742  /*
5743  * Set the string describing this grouped relation to be used in EXPLAIN
5744  * output of corresponding ForeignScan.
5745  */
5746  fpinfo->relation_name = makeStringInfo();
5747  appendStringInfo(fpinfo->relation_name, "Aggregate on (%s)",
5748  ofpinfo->relation_name->data);
5749 
5750  return true;
5751 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
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
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
List * groupingSets
Definition: parsenodes.h:150
Definition: nodes.h:525
List * list_concat(List *list1, const List *list2)
Definition: list.c:516
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
RelOptInfo * outerrel
Definition: postgres_fdw.h:98
#define PVC_INCLUDE_AGGREGATES
Definition: optimizer.h:170
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:857
#define list_make1(x1)
Definition: pg_list.h:227
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define lfirst_node(type, lc)
Definition: pg_list.h:193
Relids relids
Definition: pathnodes.h:641
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:236
List * lappend(List *list, void *datum)
Definition: list.c:322
Expr * clause
Definition: pathnodes.h:1943
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:465
unsigned int Index
Definition: c.h:475
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1051
StringInfo relation_name
Definition: postgres_fdw.h:95
void * fdw_private
Definition: pathnodes.h:695
#define Assert(condition)
Definition: c.h:732
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition: tlist.c:154
#define lfirst(lc)
Definition: pg_list.h:190
static int list_length(const List *l)
Definition: pg_list.h:169
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:232
int i
Index ressortgroupref
Definition: primnodes.h:1396
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:652

◆ foreign_join_ok()

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

Definition at line 4953 of file postgres_fdw.c.

References appendStringInfo(), Assert, bms_add_members(), bms_is_subset(), bms_nonempty_difference(), bms_union(), RestrictInfo::clause, StringInfoData::data, 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, makeStringInfo(), merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, PlannerInfo::parse, PlaceHolderInfo::ph_eval_at, PlannerInfo::placeholder_list, 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().

4956 {
4957  PgFdwRelationInfo *fpinfo;
4958  PgFdwRelationInfo *fpinfo_o;
4959  PgFdwRelationInfo *fpinfo_i;
4960  ListCell *lc;
4961  List *joinclauses;
4962 
4963  /*
4964  * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
4965  * Constructing queries representing SEMI and ANTI joins is hard, hence
4966  * not considered right now.
4967  */
4968  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
4969  jointype != JOIN_RIGHT && jointype != JOIN_FULL)
4970  return false;
4971 
4972  /*
4973  * If either of the joining relations is marked as unsafe to pushdown, the
4974  * join can not be pushed down.
4975  */
4976  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
4977  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
4978  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
4979  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
4980  !fpinfo_i || !fpinfo_i->pushdown_safe)
4981  return false;
4982 
4983  /*
4984  * If joining relations have local conditions, those conditions are
4985  * required to be applied before joining the relations. Hence the join can
4986  * not be pushed down.
4987  */
4988  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
4989  return false;
4990 
4991  /*
4992  * Merge FDW options. We might be tempted to do this after we have deemed
4993  * the foreign join to be OK. But we must do this beforehand so that we
4994  * know which quals can be evaluated on the foreign server, which might
4995  * depend on shippable_extensions.
4996  */
4997  fpinfo->server = fpinfo_o->server;
4998  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
4999 
5000  /*
5001  * Separate restrict list into join quals and pushed-down (other) quals.
5002  *
5003  * Join quals belonging to an outer join must all be shippable, else we
5004  * cannot execute the join remotely. Add such quals to 'joinclauses'.
5005  *
5006  * Add other quals to fpinfo->remote_conds if they are shippable, else to
5007  * fpinfo->local_conds. In an inner join it's okay to execute conditions
5008  * either locally or remotely; the same is true for pushed-down conditions
5009  * at an outer join.
5010  *
5011  * Note we might return failure after having already scribbled on
5012  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
5013  * won't consult those lists again if we deem the join unshippable.
5014  */
5015  joinclauses = NIL;
5016  foreach(lc, extra->restrictlist)
5017  {
5018  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5019  bool is_remote_clause = is_foreign_expr(root, joinrel,
5020  rinfo->clause);
5021 
5022  if (IS_OUTER_JOIN(jointype) &&
5023  !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
5024  {
5025  if (!is_remote_clause)
5026  return false;
5027  joinclauses = lappend(joinclauses, rinfo);
5028  }
5029  else
5030  {
5031  if (is_remote_clause)
5032  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5033  else
5034  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5035  }
5036  }
5037 
5038  /*
5039  * deparseExplicitTargetList() isn't smart enough to handle anything other
5040  * than a Var. In particular, if there's some PlaceHolderVar that would
5041  * need to be evaluated within this join tree (because there's an upper
5042  * reference to a quantity that may go to NULL as a result of an outer
5043  * join), then we can't try to push the join down because we'll fail when
5044  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
5045  * needs to be evaluated *at the top* of this join tree is OK, because we
5046  * can do that locally after fetching the results from the remote side.
5047  */
5048  foreach(lc, root->placeholder_list)
5049  {
5050  PlaceHolderInfo *phinfo = lfirst(lc);
5051  Relids relids;
5052 
5053  /* PlaceHolderInfo refers to parent relids, not child relids. */
5054  relids = IS_OTHER_REL(joinrel) ?
5055  joinrel->top_parent_relids : joinrel->relids;
5056 
5057  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
5058  bms_nonempty_difference(relids, phinfo->ph_eval_at))
5059  return false;
5060  }
5061 
5062  /* Save the join clauses, for later use. */
5063  fpinfo->joinclauses = joinclauses;
5064 
5065  fpinfo->outerrel = outerrel;
5066  fpinfo->innerrel = innerrel;
5067  fpinfo->jointype = jointype;
5068 
5069  /*
5070  * By default, both the input relations are not required to be deparsed as
5071  * subqueries, but there might be some relations covered by the input
5072  * relations that are required to be deparsed as subqueries, so save the
5073  * relids of those relations for later use by the deparser.
5074  */
5075  fpinfo->make_outerrel_subquery = false;
5076  fpinfo->make_innerrel_subquery = false;
5077  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
5078  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
5080  fpinfo_i->lower_subquery_rels);
5081 
5082  /*
5083  * Pull the other remote conditions from the joining relations into join
5084  * clauses or other remote clauses (remote_conds) of this relation
5085  * wherever possible. This avoids building subqueries at every join step.
5086  *
5087  * For an inner join, clauses from both the relations are added to the
5088  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
5089  * the outer side are added to remote_conds since those can be evaluated
5090  * after the join is evaluated. The clauses from inner side are added to
5091  * the joinclauses, since they need to be evaluated while constructing the
5092  * join.
5093  *
5094  * For a FULL OUTER JOIN, the other clauses from either relation can not
5095  * be added to the joinclauses or remote_conds, since each relation acts
5096  * as an outer relation for the other.
5097  *
5098  * The joining sides can not have local conditions, thus no need to test
5099  * shippability of the clauses being pulled up.
5100  */
5101  switch (jointype)
5102  {
5103  case JOIN_INNER:
5104  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5105  fpinfo_i->remote_conds);
5106  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5107  fpinfo_o->remote_conds);
5108  break;
5109 
5110  case JOIN_LEFT:
5111  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5112  fpinfo_i->remote_conds);
5113  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5114  fpinfo_o->remote_conds);
5115  break;
5116 
5117  case JOIN_RIGHT:
5118  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5119  fpinfo_o->remote_conds);
5120  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5121  fpinfo_i->remote_conds);
5122  break;
5123 
5124  case JOIN_FULL:
5125 
5126  /*
5127  * In this case, if any of the input relations has conditions, we
5128  * need to deparse that relation as a subquery so that the
5129  * conditions can be evaluated before the join. Remember it in
5130  * the fpinfo of this relation so that the deparser can take
5131  * appropriate action. Also, save the relids of base relations
5132  * covered by that relation for later use by the deparser.
5133  */
5134  if (fpinfo_o->remote_conds)
5135  {
5136  fpinfo->make_outerrel_subquery = true;
5137  fpinfo->lower_subquery_rels =
5139  outerrel->relids);
5140  }
5141  if (fpinfo_i->remote_conds)
5142  {
5143  fpinfo->make_innerrel_subquery = true;
5144  fpinfo->lower_subquery_rels =
5146  innerrel->relids);
5147  }
5148  break;
5149 
5150  default:
5151  /* Should not happen, we have just checked this above */
5152  elog(ERROR, "unsupported join type %d", jointype);
5153  }
5154 
5155  /*
5156  * For an inner join, all restrictions can be treated alike. Treating the
5157  * pushed down conditions as join conditions allows a top level full outer
5158  * join to be deparsed without requiring subqueries.
5159  */
5160  if (jointype == JOIN_INNER)
5161  {
5162  Assert(!fpinfo->joinclauses);
5163  fpinfo->joinclauses = fpinfo->remote_conds;
5164  fpinfo->remote_conds = NIL;
5165  }
5166 
5167  /* Mark that this join can be pushed down safely */
5168  fpinfo->pushdown_safe = true;
5169 
5170  /* Get user mapping */
5171  if (fpinfo->use_remote_estimate)
5172  {
5173  if (fpinfo_o->use_remote_estimate)
5174  fpinfo->user = fpinfo_o->user;
5175  else
5176  fpinfo->user = fpinfo_i->user;
5177  }
5178  else
5179  fpinfo->user = NULL;
5180 
5181  /*
5182  * Set # of retrieved rows and cached relation costs to some negative
5183  * value, so that we can detect when they are set to some sensible values,
5184  * during one (usually the first) of the calls to estimate_path_cost_size.
5185  */
5186  fpinfo->retrieved_rows = -1;
5187  fpinfo->rel_startup_cost = -1;
5188  fpinfo->rel_total_cost = -1;
5189 
5190  /*
5191  * Set the string describing this join relation to be used in EXPLAIN
5192  * output of corresponding ForeignScan.
5193  */
5194  fpinfo->relation_name = makeStringInfo();
5195  appendStringInfo(fpinfo->relation_name, "(%s) %s JOIN (%s)",
5196  fpinfo_o->relation_name->data,
5197  get_jointype_name(fpinfo->jointype),
5198  fpinfo_i->relation_name->data);
5199 
5200  /*
5201  * Set the relation index. This is defined as the position of this
5202  * joinrel in the join_rel_list list plus the length of the rtable list.
5203  * Note that since this joinrel is at the end of the join_rel_list list
5204  * when we are called, we can get the position by list_length.
5205  */
5206  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
5207  fpinfo->relation_index =
5209 
5210  return true;
5211 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:177
Relids ph_eval_at
Definition: pathnodes.h:2261
#define IS_OTHER_REL(rel)
Definition: pathnodes.h:629
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
ForeignServer * server
Definition: postgres_fdw.h:85
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:741
Relids lower_subquery_rels
Definition: postgres_fdw.h:115
List * list_concat(List *list1, const List *list2)
Definition: list.c:516
List * join_rel_list
Definition: pathnodes.h:244
RelOptInfo * outerrel
Definition: postgres_fdw.h:98
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
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:193
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1355
Relids relids
Definition: pathnodes.h:641
List * lappend(List *list, void *datum)
Definition: list.c:322
Expr * clause
Definition: pathnodes.h:1943
UserMapping * user
Definition: postgres_fdw.h:86
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: pathnodes.h:2020
StringInfo relation_name
Definition: postgres_fdw.h:95
void * fdw_private
Definition: pathnodes.h:695
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225
static int list_length(const List *l)
Definition: pg_list.h:169
RelOptInfo * innerrel
Definition: postgres_fdw.h:99
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:232
#define elog(elevel,...)
Definition: elog.h:226
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:714
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 3126 of file postgres_fdw.c.

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

Referenced by estimate_path_cost_size().

3129 {
3130  PGresult *volatile res = NULL;
3131 
3132  /* PGresult must be released before leaving this function. */
3133  PG_TRY();
3134  {
3135  char *line;
3136  char *p;
3137  int n;
3138 
3139  /*
3140  * Execute EXPLAIN remotely.
3141  */
3142  res = pgfdw_exec_query(conn, sql);
3143  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3144  pgfdw_report_error(ERROR, res, conn, false, sql);
3145 
3146  /*
3147  * Extract cost numbers for topmost plan node. Note we search for a
3148  * left paren from the end of the line to avoid being confused by
3149  * other uses of parentheses.
3150  */
3151  line = PQgetvalue(res, 0, 0);
3152  p = strrchr(line, '(');
3153  if (p == NULL)
3154  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3155  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3156  startup_cost, total_cost, rows, width);
3157  if (n != 4)
3158  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3159 
3160  PQclear(res);
3161  res = NULL;
3162  }
3163  PG_CATCH();
3164  {
3165  if (res)
3166  PQclear(res);
3167  PG_RE_THROW();
3168  }
3169  PG_END_TRY();
3170 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3164
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:597
void PQclear(PGresult *res)
Definition: fe-exec.c:695
#define PG_CATCH()
Definition: elog.h:310
#define PG_RE_THROW()
Definition: elog.h:331
#define elog(elevel,...)
Definition: elog.h:226
#define PG_TRY()
Definition: elog.h:301
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
#define PG_END_TRY()
Definition: elog.h:317

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 4032 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, PgFdwDirectModifyState::retrieved_attrs, PgFdwDirectModifyState::set_processed, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, and PgFdwDirectModifyState::temp_cxt.

Referenced by postgresIterateDirectModify().

4033 {
4035  EState *estate = node->ss.ps.state;
4036  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
4037  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4038  TupleTableSlot *resultSlot;
4039 
4040  Assert(resultRelInfo->ri_projectReturning);
4041 
4042  /* If we didn't get any tuples, must be end of data. */
4043  if (dmstate->next_tuple >= dmstate->num_tuples)
4044  return ExecClearTuple(slot);
4045 
4046  /* Increment the command es_processed count if necessary. */
4047  if (dmstate->set_processed)
4048  estate->es_processed += 1;
4049 
4050  /*
4051  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4052  * tuple. (has_returning is false when the local query is of the form
4053  * "UPDATE/DELETE .. RETURNING 1" for example.)
4054  */
4055  if (!dmstate->has_returning)
4056  {
4057  ExecStoreAllNullTuple(slot);
4058  resultSlot = slot;
4059  }
4060  else
4061  {
4062  /*
4063  * On error, be sure to release the PGresult on the way out. Callers
4064  * do not have PG_TRY blocks to ensure this happens.
4065  */
4066  PG_TRY();
4067  {
4068  HeapTuple newtup;
4069 
4070  newtup = make_tuple_from_result_row(dmstate->result,
4071  dmstate->next_tuple,
4072  dmstate->rel,
4073  dmstate->attinmeta,
4074  dmstate->retrieved_attrs,
4075  node,
4076  dmstate->temp_cxt);
4077  ExecStoreHeapTuple(newtup, slot, false);
4078  }
4079  PG_CATCH();
4080  {
4081  if (dmstate->result)
4082  PQclear(dmstate->result);
4083  PG_RE_THROW();
4084  }
4085  PG_END_TRY();
4086 
4087  /* Get the updated/deleted tuple. */
4088  if (dmstate->rel)
4089  resultSlot = slot;
4090  else
4091  resultSlot = apply_returning_filter(dmstate, slot, estate);
4092  }
4093  dmstate->next_tuple++;
4094 
4095  /* Make slot available for evaluation of the local query RETURNING list. */
4096  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4097  resultSlot;
4098 
4099  return slot;
4100 }
ScanState ss
Definition: execnodes.h:1785
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:426
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:1541
AttInMetadata * attinmeta
Definition: postgres_fdw.c:200
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1334
EState * state
Definition: execnodes.h:942
PlanState ps
Definition: execnodes.h:1331
MemoryContext temp_cxt
Definition: postgres_fdw.c:226
void PQclear(PGresult *res)
Definition: fe-exec.c:695
#define PG_CATCH()
Definition: elog.h:310
#define Assert(condition)
Definition: c.h:732
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, TupleTableSlot *slot, EState *estate)
#define PG_RE_THROW()
Definition: elog.h:331
#define PG_TRY()
Definition: elog.h:301
#define PG_END_TRY()
Definition: elog.h:317
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1317

◆ get_useful_ecs_for_relation()

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

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

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

◆ get_useful_pathkeys_for_relation()

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

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

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

4109 {
4110  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4111  ListCell *lc;
4112  int i;
4113 
4114  /*
4115  * Calculate the mapping between the fdw_scan_tlist's entries and the
4116  * result tuple's attributes.
4117  *
4118  * The "map" is an array of indexes of the result tuple's attributes in
4119  * fdw_scan_tlist, i.e., one entry for every attribute of the result
4120  * tuple. We store zero for any attributes that don't have the
4121  * corresponding entries in that list, marking that a NULL is needed in
4122  * the result tuple.
4123  *
4124  * Also get the indexes of the entries for ctid and oid if any.
4125  */
4126  dmstate->attnoMap = (AttrNumber *)
4127  palloc0(resultTupType->natts * sizeof(AttrNumber));
4128 
4129  dmstate->ctidAttno = dmstate->oidAttno = 0;
4130 
4131  i = 1;
4132  dmstate->hasSystemCols = false;
4133  foreach(lc, fdw_scan_tlist)
4134  {
4135  TargetEntry *tle = (TargetEntry *) lfirst(lc);
4136  Var *var = (Var *) tle->expr;
4137 
4138  Assert(IsA(var, Var));
4139 
4140  /*
4141  * If the Var is a column of the target relation to be retrieved from
4142  * the foreign server, get the index of the entry.
4143  */
4144  if (var->varno == rtindex &&
4145  list_member_int(dmstate->retrieved_attrs, i))
4146  {
4147  int attrno = var->varattno;
4148 
4149  if (attrno < 0)
4150  {
4151  /*
4152  * We don't retrieve system columns other than ctid and oid.
4153  */
4154  if (attrno == SelfItemPointerAttributeNumber)
4155  dmstate->ctidAttno = i;
4156  else
4157  Assert(false);
4158  dmstate->hasSystemCols = true;
4159  }
4160  else
4161  {
4162  /*
4163  * We don't retrieve whole-row references to the target
4164  * relation either.
4165  */
4166  Assert(attrno > 0);
4167 
4168  dmstate->attnoMap[attrno - 1] = i;
4169  }
4170  }
4171  i++;
4172  }
4173 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
#define RelationGetDescr(relation)
Definition: rel.h:445
AttrNumber varattno
Definition: primnodes.h:172
Definition: primnodes.h:167
bool list_member_int(const List *list, int datum)
Definition: list.c:655
Index varno
Definition: primnodes.h:170
void * palloc0(Size size)
Definition: mcxt.c:980
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
Expr * expr
Definition: primnodes.h:1393
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 6266 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().

6273 {
6274  HeapTuple tuple;
6275  TupleDesc tupdesc;
6276  Datum *values;
6277  bool *nulls;
6278  ItemPointer ctid = NULL;
6279  ConversionLocation errpos;
6280  ErrorContextCallback errcallback;
6281  MemoryContext oldcontext;
6282  ListCell *lc;
6283  int j;
6284 
6285  Assert(row < PQntuples(res));
6286 
6287  /*
6288  * Do the following work in a temp context that we reset after each tuple.
6289  * This cleans up not only the data we have direct access to, but any
6290  * cruft the I/O functions might leak.
6291  */
6292  oldcontext = MemoryContextSwitchTo(temp_context);
6293 
6294  if (rel)
6295  tupdesc = RelationGetDescr(rel);
6296  else
6297  {
6298  Assert(fsstate);
6299  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
6300  }
6301 
6302  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
6303  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
6304  /* Initialize to nulls for any columns not present in result */
6305  memset(nulls, true, tupdesc->natts * sizeof(bool));
6306 
6307  /*
6308  * Set up and install callback to report where conversion error occurs.
6309  */
6310  errpos.rel = rel;
6311  errpos.cur_attno = 0;
6312  errpos.fsstate = fsstate;
6313  errcallback.callback = conversion_error_callback;
6314  errcallback.arg = (void *) &errpos;
6315  errcallback.previous = error_context_stack;
6316  error_context_stack = &errcallback;
6317 
6318  /*
6319  * i indexes columns in the relation, j indexes columns in the PGresult.
6320  */
6321  j = 0;
6322  foreach(lc, retrieved_attrs)
6323  {
6324  int i = lfirst_int(lc);
6325  char *valstr;
6326 
6327  /* fetch next column's textual value */
6328  if (PQgetisnull(res, row, j))
6329  valstr = NULL;
6330  else
6331  valstr = PQgetvalue(res, row, j);
6332 
6333  /*
6334  * convert value to internal representation
6335  *
6336  * Note: we ignore system columns other than ctid and oid in result
6337  */
6338  errpos.cur_attno = i;
6339  if (i > 0)
6340  {
6341  /* ordinary column */
6342  Assert(i <= tupdesc->natts);
6343  nulls[i - 1] = (valstr == NULL);
6344  /* Apply the input function even to nulls, to support domains */
6345  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
6346  valstr,
6347  attinmeta->attioparams[i - 1],
6348  attinmeta->atttypmods[i - 1]);
6349  }
6350  else if (i == SelfItemPointerAttributeNumber)
6351  {
6352  /* ctid */
6353  if (valstr != NULL)
6354  {
6355  Datum datum;
6356 
6357  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
6358  ctid = (ItemPointer) DatumGetPointer(datum);
6359  }
6360  }
6361  errpos.cur_attno = 0;
6362 
6363  j++;
6364  }
6365 
6366  /* Uninstall error context callback. */
6367  error_context_stack = errcallback.previous;
6368 
6369  /*
6370  * Check we got the expected number of columns. Note: j == 0 and
6371  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
6372  */
6373  if (j > 0 && j != PQnfields(res))
6374  elog(ERROR, "remote query result does not match the foreign table");
6375 
6376  /*
6377  * Build the result tuple in caller's memory context.
6378  */
6379  MemoryContextSwitchTo(oldcontext);
6380 
6381  tuple = heap_form_tuple(tupdesc, values, nulls);
6382 
6383  /*
6384  * If we have a CTID to return, install it in both t_self and t_ctid.
6385  * t_self is the normal place, but if the tuple is converted to a
6386  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
6387  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
6388  */
6389  if (ctid)
6390  tuple->t_self = tuple->t_data->t_ctid = *ctid;
6391 
6392  /*
6393  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
6394  * heap_form_tuple. heap_form_tuple actually creates the tuple with
6395  * DatumTupleFields, not HeapTupleFields, but the executor expects
6396  * HeapTupleFields and will happily extract system columns on that
6397  * assumption. If we don't do this then, for example, the tuple length
6398  * ends up in the xmin field, which isn't what we want.
6399  */
6403 
6404  /* Clean up */
6405  MemoryContextReset(temp_context);
6406 
6407  return tuple;
6408 }
ScanState ss
Definition: execnodes.h:1785
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2778
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3164
#define RelationGetDescr(relation)
Definition: rel.h:445
static MemoryContext MemoryContextSwitchTo(MemoryContext context)