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

Go to the source code of this file.

Data Structures

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

Macros

#define DEFAULT_FDW_STARTUP_COST   100.0
 
#define DEFAULT_FDW_TUPLE_COST   0.01
 
#define DEFAULT_FDW_SORT_MULTIPLIER   1.2
 

Typedefs

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

Enumerations

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

Functions

 PG_FUNCTION_INFO_V1 (postgres_fdw_handler)
 
static void postgresGetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static void postgresGetForeignPaths (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static ForeignScanpostgresGetForeignPlan (PlannerInfo *root, RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
 
static void postgresBeginForeignScan (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateForeignScan (ForeignScanState *node)
 
static void postgresReScanForeignScan (ForeignScanState *node)
 
static void postgresEndForeignScan (ForeignScanState *node)
 
static void postgresAddForeignUpdateTargets (Query *parsetree, RangeTblEntry *target_rte, Relation target_relation)
 
static ListpostgresPlanForeignModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginForeignModify (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
 
static TupleTableSlotpostgresExecForeignInsert (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlotpostgresExecForeignUpdate (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlotpostgresExecForeignDelete (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static void postgresEndForeignModify (EState *estate, ResultRelInfo *resultRelInfo)
 
static void postgresBeginForeignInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static void postgresEndForeignInsert (EState *estate, ResultRelInfo *resultRelInfo)
 
static int postgresIsForeignRelUpdatable (Relation rel)
 
static bool postgresPlanDirectModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginDirectModify (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateDirectModify (ForeignScanState *node)
 
static void postgresEndDirectModify (ForeignScanState *node)
 
static void postgresExplainForeignScan (ForeignScanState *node, ExplainState *es)
 
static void postgresExplainForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
 
static void postgresExplainDirectModify (ForeignScanState *node, ExplainState *es)
 
static bool postgresAnalyzeForeignTable (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
 
static ListpostgresImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid)
 
static void postgresGetForeignJoinPaths (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static bool postgresRecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot)
 
static void postgresGetForeignUpperPaths (PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra)
 
static void estimate_path_cost_size (PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
 
static void get_remote_estimate (const char *sql, PGconn *conn, double *rows, int *width, Cost *startup_cost, Cost *total_cost)
 
static void adjust_foreign_grouping_path_cost (PlannerInfo *root, List *pathkeys, double retrieved_rows, double width, double limit_tuples, Cost *p_startup_cost, Cost *p_run_cost)
 
static bool ec_member_matches_foreign (PlannerInfo *root, RelOptInfo *rel, EquivalenceClass *ec, EquivalenceMember *em, void *arg)
 
static void create_cursor (ForeignScanState *node)
 
static void fetch_more_data (ForeignScanState *node)
 
static void close_cursor (PGconn *conn, unsigned int cursor_number)
 
static PgFdwModifyStatecreate_foreign_modify (EState *estate, RangeTblEntry *rte, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, bool has_returning, List *retrieved_attrs)
 
static TupleTableSlotexecute_foreign_modify (EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static void prepare_foreign_modify (PgFdwModifyState *fmstate)
 
static const char ** convert_prep_stmt_params (PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot *slot)
 
static void store_returning_result (PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
 
static void finish_foreign_modify (PgFdwModifyState *fmstate)
 
static Listbuild_remote_returning (Index rtindex, Relation rel, List *returningList)
 
static void rebuild_fdw_scan_tlist (ForeignScan *fscan, List *tlist)
 
static void execute_dml_stmt (ForeignScanState *node)
 
static TupleTableSlotget_returning_data (ForeignScanState *node)
 
static void init_returning_filter (PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
 
static TupleTableSlotapply_returning_filter (PgFdwDirectModifyState *dmstate, 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 57 of file postgres_fdw.c.

Referenced by adjust_foreign_grouping_path_cost(), and estimate_path_cost_size().

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 51 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.01

Definition at line 54 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 113 of file postgres_fdw.c.

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 92 of file postgres_fdw.c.

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

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 261 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 66 of file postgres_fdw.c.

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

Function Documentation

◆ add_foreign_final_paths()

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

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

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

◆ add_foreign_grouping_paths()

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

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

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

◆ add_foreign_ordered_paths()

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

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

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

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

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

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

◆ analyze_row_processor()

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

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

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

◆ apply_returning_filter()

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

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

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

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

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

5328 {
5329  ListCell *lc;
5330 
5331  foreach(lc, fpinfo->server->options)
5332  {
5333  DefElem *def = (DefElem *) lfirst(lc);
5334 
5335  if (strcmp(def->defname, "use_remote_estimate") == 0)
5336  fpinfo->use_remote_estimate = defGetBoolean(def);
5337  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
5338  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
5339  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
5340  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
5341  else if (strcmp(def->defname, "extensions") == 0)
5342  fpinfo->shippable_extensions =
5343  ExtractExtensionList(defGetString(def), false);
5344  else if (strcmp(def->defname, "fetch_size") == 0)
5345  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5346  }
5347 }
ForeignServer * server
Definition: postgres_fdw.h:84
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * defGetString(DefElem *def)
Definition: define.c:49
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:326
#define lfirst(lc)
Definition: pg_list.h:190
char * defname
Definition: parsenodes.h:730
List * shippable_extensions
Definition: postgres_fdw.h:80
List * options
Definition: foreign.h:42

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

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

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

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

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

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

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

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

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

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

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

◆ ec_member_matches_foreign()

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

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

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

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

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

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

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

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

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3390 of file postgres_fdw.c.

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

Referenced by postgresIterateForeignScan().

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

◆ find_em_expr_for_input_target()

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

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

6563 {
6564  ListCell *lc1;
6565  int i;
6566 
6567  i = 0;
6568  foreach(lc1, target->exprs)
6569  {
6570  Expr *expr = (Expr *) lfirst(lc1);
6571  Index sgref = get_pathtarget_sortgroupref(target, i);
6572  ListCell *lc2;
6573 
6574  /* Ignore non-sort expressions */
6575  if (sgref == 0 ||
6577  root->parse->sortClause) == NULL)
6578  {
6579  i++;
6580  continue;
6581  }
6582 
6583  /* We ignore binary-compatible relabeling on both ends */
6584  while (expr && IsA(expr, RelabelType))
6585  expr = ((RelabelType *) expr)->arg;
6586 
6587  /* Locate an EquivalenceClass member matching this expr, if any */
6588  foreach(lc2, ec->ec_members)
6589  {
6591  Expr *em_expr;
6592 
6593  /* Don't match constants */
6594  if (em->em_is_const)
6595  continue;
6596 
6597  /* Ignore child members */
6598  if (em->em_is_child)
6599  continue;
6600 
6601  /* Match if same expression (after stripping relabel) */
6602  em_expr = em->em_expr;
6603  while (em_expr && IsA(em_expr, RelabelType))
6604  em_expr = ((RelabelType *) em_expr)->arg;
6605 
6606  if (equal(em_expr, expr))
6607  return em->em_expr;
6608  }
6609 
6610  i++;
6611  }
6612 
6613  elog(ERROR, "could not find pathkey item to sort");
6614  return NULL; /* keep compiler quiet */
6615 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
Query * parse
Definition: pathnodes.h:179
List * sortClause
Definition: parsenodes.h:158
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3011
#define ERROR
Definition: elog.h:43
List * exprs
Definition: pathnodes.h:1046
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:465
unsigned int Index
Definition: c.h:476
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1053
#define lfirst(lc)
Definition: pg_list.h:190
#define elog(elevel,...)
Definition: elog.h:228
int i
void * arg
List * ec_members
Definition: pathnodes.h:936

◆ find_em_expr_for_rel()

Expr* find_em_expr_for_rel ( EquivalenceClass ec,
RelOptInfo rel 
)

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

6532 {
6533  ListCell *lc_em;
6534 
6535  foreach(lc_em, ec->ec_members)
6536  {
6537  EquivalenceMember *em = lfirst(lc_em);
6538 
6539  if (bms_is_subset(em->em_relids, rel->relids) &&
6540  !bms_is_empty(em->em_relids))
6541  {
6542  /*
6543  * If there is more than one equivalence member whose Vars are
6544  * taken entirely from this relation, we'll be content to choose
6545  * any one of those.
6546  */
6547  return em->em_expr;
6548  }
6549  }
6550 
6551  /* We didn't find any suitable equivalence class expression */
6552  return NULL;
6553 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:315
Relids relids
Definition: pathnodes.h:643
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:936

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

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

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

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

Referenced by add_foreign_grouping_paths().

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

◆ foreign_join_ok()

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

Definition at line 4998 of file postgres_fdw.c.

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

Referenced by postgresGetForeignJoinPaths().

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

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

Referenced by estimate_path_cost_size().

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

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

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

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

◆ get_useful_ecs_for_relation()

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

Definition at line 756 of file postgres_fdw.c.

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

Referenced by get_useful_pathkeys_for_relation().

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

◆ get_useful_pathkeys_for_relation()

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

Definition at line 852 of file postgres_fdw.c.

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

Referenced by add_paths_with_pathkeys_for_rel().

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

◆ init_returning_filter()

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

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

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

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