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 "executor/execAsync.h"
#include "foreign/fdwapi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "postgres_fdw.h"
#include "storage/latch.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
#include "utils/selfuncs.h"
Include dependency graph for postgres_fdw.c:

Go to the source code of this file.

Data Structures

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

Macros

#define DEFAULT_FDW_STARTUP_COST   100.0
 
#define DEFAULT_FDW_TUPLE_COST   0.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, FdwModifyPrivateLen, FdwModifyPrivateHasReturning,
  FdwModifyPrivateRetrievedAttrs
}
 
enum  FdwDirectModifyPrivateIndex { FdwDirectModifyPrivateUpdateSql, FdwDirectModifyPrivateHasReturning, FdwDirectModifyPrivateRetrievedAttrs, FdwDirectModifyPrivateSetProcessed }
 
enum  FdwPathPrivateIndex { FdwPathPrivateHasFinalSort, FdwPathPrivateHasLimit }
 

Functions

 PG_FUNCTION_INFO_V1 (postgres_fdw_handler)
 
static void postgresGetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static void postgresGetForeignPaths (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static ForeignScanpostgresGetForeignPlan (PlannerInfo *root, RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
 
static void postgresBeginForeignScan (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateForeignScan (ForeignScanState *node)
 
static void postgresReScanForeignScan (ForeignScanState *node)
 
static void postgresEndForeignScan (ForeignScanState *node)
 
static void postgresAddForeignUpdateTargets (PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation)
 
static ListpostgresPlanForeignModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginForeignModify (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
 
static TupleTableSlotpostgresExecForeignInsert (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlot ** postgresExecForeignBatchInsert (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
 
static int postgresGetForeignModifyBatchSize (ResultRelInfo *resultRelInfo)
 
static TupleTableSlotpostgresExecForeignUpdate (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlotpostgresExecForeignDelete (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static void postgresEndForeignModify (EState *estate, ResultRelInfo *resultRelInfo)
 
static void postgresBeginForeignInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static void postgresEndForeignInsert (EState *estate, ResultRelInfo *resultRelInfo)
 
static int postgresIsForeignRelUpdatable (Relation rel)
 
static bool postgresPlanDirectModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginDirectModify (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateDirectModify (ForeignScanState *node)
 
static void postgresEndDirectModify (ForeignScanState *node)
 
static void postgresExplainForeignScan (ForeignScanState *node, ExplainState *es)
 
static void postgresExplainForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
 
static void postgresExplainDirectModify (ForeignScanState *node, ExplainState *es)
 
static void postgresExecForeignTruncate (List *rels, List *rels_extra, DropBehavior behavior, bool restart_seqs)
 
static bool postgresAnalyzeForeignTable (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
 
static ListpostgresImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid)
 
static void postgresGetForeignJoinPaths (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static bool postgresRecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot)
 
static void postgresGetForeignUpperPaths (PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra)
 
static bool postgresIsForeignPathAsyncCapable (ForeignPath *path)
 
static void postgresForeignAsyncRequest (AsyncRequest *areq)
 
static void postgresForeignAsyncConfigureWait (AsyncRequest *areq)
 
static void postgresForeignAsyncNotify (AsyncRequest *areq)
 
static void estimate_path_cost_size (PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, 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, PgFdwConnState *conn_state)
 
static PgFdwModifyStatecreate_foreign_modify (EState *estate, RangeTblEntry *rte, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, int len, bool has_returning, List *retrieved_attrs)
 
static TupleTableSlot ** execute_foreign_modify (EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
 
static void prepare_foreign_modify (PgFdwModifyState *fmstate)
 
static const char ** convert_prep_stmt_params (PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot **slots, int numSlots)
 
static void store_returning_result (PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
 
static void finish_foreign_modify (PgFdwModifyState *fmstate)
 
static void deallocate_query (PgFdwModifyState *fmstate)
 
static Listbuild_remote_returning (Index rtindex, Relation rel, List *returningList)
 
static void rebuild_fdw_scan_tlist (ForeignScan *fscan, List *tlist)
 
static void execute_dml_stmt (ForeignScanState *node)
 
static TupleTableSlotget_returning_data (ForeignScanState *node)
 
static void init_returning_filter (PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
 
static TupleTableSlotapply_returning_filter (PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
 
static void prepare_query_params (PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
 
static void process_query_params (ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
 
static int postgresAcquireSampleRowsFunc (Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
 
static void analyze_row_processor (PGresult *res, int row, PgFdwAnalyzeState *astate)
 
static void produce_tuple_asynchronously (AsyncRequest *areq, bool fetch)
 
static void fetch_more_data_begin (AsyncRequest *areq)
 
static 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)
 
static int get_batch_size_option (Relation rel)
 
Datum postgres_fdw_handler (PG_FUNCTION_ARGS)
 
static ForeignScanfind_modifytable_subplan (PlannerInfo *root, ModifyTable *plan, Index rtindex, int subplan_index)
 
int set_transmission_modes (void)
 
void reset_transmission_modes (int nestlevel)
 
void process_pending_request (AsyncRequest *areq)
 
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 61 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 55 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.01

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

122 {
123  /* SQL statement to execute remotely (as a String node) */
125  /* has-returning flag (as an integer Value node) */
127  /* Integer list of attribute numbers retrieved by RETURNING */
129  /* set-processed flag (as an integer Value node) */
131 };

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 98 of file postgres_fdw.c.

99 {
100  /* SQL statement to execute remotely (as a String node) */
102  /* Integer list of target attribute numbers for INSERT/UPDATE */
104  /* Length till the end of VALUES clause (as an integer Value node) */
106  /* has-returning flag (as an integer Value node) */
108  /* Integer list of attribute numbers retrieved by RETURNING */
110 };

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 281 of file postgres_fdw.c.

282 {
283  /* has-final-sort flag (as an integer Value node) */
285  /* has-limit flag (as an integer Value node) */
287 };

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 70 of file postgres_fdw.c.

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

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

6504 {
6505  Query *parse = root->parse;
6506  PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6507  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
6508  bool has_final_sort = false;
6509  List *pathkeys = NIL;
6510  PgFdwPathExtraData *fpextra;
6511  bool save_use_remote_estimate = false;
6512  double rows;
6513  int width;
6514  Cost startup_cost;
6515  Cost total_cost;
6516  List *fdw_private;
6517  ForeignPath *final_path;
6518 
6519  /*
6520  * Currently, we only support this for SELECT commands
6521  */
6522  if (parse->commandType != CMD_SELECT)
6523  return;
6524 
6525  /*
6526  * No work if there is no FOR UPDATE/SHARE clause and if there is no need
6527  * to add a LIMIT node
6528  */
6529  if (!parse->rowMarks && !extra->limit_needed)
6530  return;
6531 
6532  /* We don't support cases where there are any SRFs in the targetlist */
6533  if (parse->hasTargetSRFs)
6534  return;
6535 
6536  /* Save the input_rel as outerrel in fpinfo */
6537  fpinfo->outerrel = input_rel;
6538 
6539  /*
6540  * Copy foreign table, foreign server, user mapping, FDW options etc.
6541  * details from the input relation's fpinfo.
6542  */
6543  fpinfo->table = ifpinfo->table;
6544  fpinfo->server = ifpinfo->server;
6545  fpinfo->user = ifpinfo->user;
6546  merge_fdw_options(fpinfo, ifpinfo, NULL);
6547 
6548  /*
6549  * If there is no need to add a LIMIT node, there might be a ForeignPath
6550  * in the input_rel's pathlist that implements all behavior of the query.
6551  * Note: we would already have accounted for the query's FOR UPDATE/SHARE
6552  * (if any) before we get here.
6553  */
6554  if (!extra->limit_needed)
6555  {
6556  ListCell *lc;
6557 
6558  Assert(parse->rowMarks);
6559 
6560  /*
6561  * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
6562  * so the input_rel should be a base, join, or ordered relation; and
6563  * if it's an ordered relation, its input relation should be a base or
6564  * join relation.
6565  */
6566  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6567  input_rel->reloptkind == RELOPT_JOINREL ||
6568  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6569  ifpinfo->stage == UPPERREL_ORDERED &&
6570  (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
6571  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
6572 
6573  foreach(lc, input_rel->pathlist)
6574  {
6575  Path *path = (Path *) lfirst(lc);
6576 
6577  /*
6578  * apply_scanjoin_target_to_paths() uses create_projection_path()
6579  * to adjust each of its input paths if needed, whereas
6580  * create_ordered_paths() uses apply_projection_to_path() to do
6581  * that. So the former might have put a ProjectionPath on top of
6582  * the ForeignPath; look through ProjectionPath and see if the
6583  * path underneath it is ForeignPath.
6584  */
6585  if (IsA(path, ForeignPath) ||
6586  (IsA(path, ProjectionPath) &&
6587  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
6588  {
6589  /*
6590  * Create foreign final path; this gets rid of a
6591  * no-longer-needed outer plan (if any), which makes the
6592  * EXPLAIN output look cleaner
6593  */
6594  final_path = create_foreign_upper_path(root,
6595  path->parent,
6596  path->pathtarget,
6597  path->rows,
6598  path->startup_cost,
6599  path->total_cost,
6600  path->pathkeys,
6601  NULL, /* no extra plan */
6602  NULL); /* no fdw_private */
6603 
6604  /* and add it to the final_rel */
6605  add_path(final_rel, (Path *) final_path);
6606 
6607  /* Safe to push down */
6608  fpinfo->pushdown_safe = true;
6609 
6610  return;
6611  }
6612  }
6613 
6614  /*
6615  * If we get here it means no ForeignPaths; since we would already
6616  * have considered pushing down all operations for the query to the
6617  * remote server, give up on it.
6618  */
6619  return;
6620  }
6621 
6622  Assert(extra->limit_needed);
6623 
6624  /*
6625  * If the input_rel is an ordered relation, replace the input_rel with its
6626  * input relation
6627  */
6628  if (input_rel->reloptkind == RELOPT_UPPER_REL &&
6629  ifpinfo->stage == UPPERREL_ORDERED)
6630  {
6631  input_rel = ifpinfo->outerrel;
6632  ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6633  has_final_sort = true;
6634  pathkeys = root->sort_pathkeys;
6635  }
6636 
6637  /* The input_rel should be a base, join, or grouping relation */
6638  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6639  input_rel->reloptkind == RELOPT_JOINREL ||
6640  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6641  ifpinfo->stage == UPPERREL_GROUP_AGG));
6642 
6643  /*
6644  * We try to create a path below by extending a simple foreign path for
6645  * the underlying base, join, or grouping relation to perform the final
6646  * sort (if has_final_sort) and the LIMIT restriction remotely, which is
6647  * stored into the fdw_private list of the resulting path. (We
6648  * re-estimate the costs of sorting the underlying relation, if
6649  * has_final_sort.)
6650  */
6651 
6652  /*
6653  * Assess if it is safe to push down the LIMIT and OFFSET to the remote
6654  * server
6655  */
6656 
6657  /*
6658  * If the underlying relation has any local conditions, the LIMIT/OFFSET
6659  * cannot be pushed down.
6660  */
6661  if (ifpinfo->local_conds)
6662  return;
6663 
6664  /*
6665  * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
6666  * not safe to remote.
6667  */
6668  if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
6669  !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
6670  return;
6671 
6672  /* Safe to push down */
6673  fpinfo->pushdown_safe = true;
6674 
6675  /* Construct PgFdwPathExtraData */
6676  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6677  fpextra->target = root->upper_targets[UPPERREL_FINAL];
6678  fpextra->has_final_sort = has_final_sort;
6679  fpextra->has_limit = extra->limit_needed;
6680  fpextra->limit_tuples = extra->limit_tuples;
6681  fpextra->count_est = extra->count_est;
6682  fpextra->offset_est = extra->offset_est;
6683 
6684  /*
6685  * Estimate the costs of performing the final sort and the LIMIT
6686  * restriction remotely. If has_final_sort is false, we wouldn't need to
6687  * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
6688  * roughly estimated using the costs we already have for the underlying
6689  * relation, in the same way as when use_remote_estimate is false. Since
6690  * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
6691  * false in that case.
6692  */
6693  if (!fpextra->has_final_sort)
6694  {
6695  save_use_remote_estimate = ifpinfo->use_remote_estimate;
6696  ifpinfo->use_remote_estimate = false;
6697  }
6698  estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
6699  &rows, &width, &startup_cost, &total_cost);
6700  if (!fpextra->has_final_sort)
6701  ifpinfo->use_remote_estimate = save_use_remote_estimate;
6702 
6703  /*
6704  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6705  * Items in the list must match order in enum FdwPathPrivateIndex.
6706  */
6707  fdw_private = list_make2(makeInteger(has_final_sort),
6708  makeInteger(extra->limit_needed));
6709 
6710  /*
6711  * Create foreign final path; this gets rid of a no-longer-needed outer
6712  * plan (if any), which makes the EXPLAIN output look cleaner
6713  */
6714  final_path = create_foreign_upper_path(root,
6715  input_rel,
6717  rows,
6718  startup_cost,
6719  total_cost,
6720  pathkeys,
6721  NULL, /* no extra plan */
6722  fdw_private);
6723 
6724  /* and add it to the final_rel */
6725  add_path(final_rel, (Path *) final_path);
6726 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
Node * limitOffset
Definition: parsenodes.h:171
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
PathTarget * pathtarget
Definition: pathnodes.h:1175
Query * parse
Definition: pathnodes.h:161
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:2303
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
RelOptKind reloptkind
Definition: pathnodes.h:673
PathTarget * target
Definition: postgres_fdw.c:292
ForeignServer * server
Definition: postgres_fdw.h:86
List * rowMarks
Definition: parsenodes.h:175
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
Cost startup_cost
Definition: pathnodes.h:1185
RelOptInfo * parent
Definition: pathnodes.h:1174
Node * limitCount
Definition: parsenodes.h:172
List * sort_pathkeys
Definition: pathnodes.h:298
Value * makeInteger(int i)
Definition: value.c:23
UserMapping * user
Definition: postgres_fdw.h:87
void * palloc0(Size size)
Definition: mcxt.c:1093
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:732
Cost total_cost
Definition: pathnodes.h:1186
CmdType commandType
Definition: parsenodes.h:120
bool hasTargetSRFs
Definition: parsenodes.h:135
List * pathkeys
Definition: pathnodes.h:1188
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
ForeignTable * table
Definition: postgres_fdw.h:85
double rows
Definition: pathnodes.h:1184
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:231
UpperRelationKind stage
Definition: postgres_fdw.h:109
List * pathlist
Definition: pathnodes.h:690
Definition: pg_list.h:50
double Cost
Definition: nodes.h:673
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:666
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: pathnodes.h:309

◆ add_foreign_grouping_paths()

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

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

6286 {
6287  Query *parse = root->parse;
6288  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6289  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
6290  ForeignPath *grouppath;
6291  double rows;
6292  int width;
6293  Cost startup_cost;
6294  Cost total_cost;
6295 
6296  /* Nothing to be done, if there is no grouping or aggregation required. */
6297  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
6298  !root->hasHavingQual)
6299  return;
6300 
6303 
6304  /* save the input_rel as outerrel in fpinfo */
6305  fpinfo->outerrel = input_rel;
6306 
6307  /*
6308  * Copy foreign table, foreign server, user mapping, FDW options etc.
6309  * details from the input relation's fpinfo.
6310  */
6311  fpinfo->table = ifpinfo->table;
6312  fpinfo->server = ifpinfo->server;
6313  fpinfo->user = ifpinfo->user;
6314  merge_fdw_options(fpinfo, ifpinfo, NULL);
6315 
6316  /*
6317  * Assess if it is safe to push down aggregation and grouping.
6318  *
6319  * Use HAVING qual from extra. In case of child partition, it will have
6320  * translated Vars.
6321  */
6322  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
6323  return;
6324 
6325  /*
6326  * Compute the selectivity and cost of the local_conds, so we don't have
6327  * to do it over again for each path. (Currently we create just a single
6328  * path here, but in future it would be possible that we build more paths
6329  * such as pre-sorted paths as in postgresGetForeignPaths and
6330  * postgresGetForeignJoinPaths.) The best we can do for these conditions
6331  * is to estimate selectivity on the basis of local statistics.
6332  */
6333  fpinfo->local_conds_sel = clauselist_selectivity(root,
6334  fpinfo->local_conds,
6335  0,
6336  JOIN_INNER,
6337  NULL);
6338 
6339  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6340 
6341  /* Estimate the cost of push down */
6342  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
6343  &rows, &width, &startup_cost, &total_cost);
6344 
6345  /* Now update this information in the fpinfo */
6346  fpinfo->rows = rows;
6347  fpinfo->width = width;
6348  fpinfo->startup_cost = startup_cost;
6349  fpinfo->total_cost = total_cost;
6350 
6351  /* Create and add foreign path to the grouping relation. */
6352  grouppath = create_foreign_upper_path(root,
6353  grouped_rel,
6354  grouped_rel->reltarget,
6355  rows,
6356  startup_cost,
6357  total_cost,
6358  NIL, /* no pathkeys */
6359  NULL,
6360  NIL); /* no fdw_private */
6361 
6362  /* Add generated path into grouped_rel by add_path(). */
6363  add_path(grouped_rel, (Path *) grouppath);
6364 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:161
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:2303
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
bool hasAggs
Definition: parsenodes.h:133
ForeignServer * server
Definition: postgres_fdw.h:86
List * groupingSets
Definition: parsenodes.h:161
PartitionwiseAggregateType patype
Definition: pathnodes.h:2588
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
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:4311
Selectivity local_conds_sel
Definition: postgres_fdw.h:57
UserMapping * user
Definition: postgres_fdw.h:87
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:732
#define Assert(condition)
Definition: c.h:804
ForeignTable * table
Definition: postgres_fdw.h:85
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
List * groupClause
Definition: parsenodes.h:158
bool hasHavingQual
Definition: pathnodes.h:347
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:102
struct PathTarget * reltarget
Definition: pathnodes.h:687
double Cost
Definition: nodes.h:673
QualCost local_conds_cost
Definition: postgres_fdw.h:56
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:666

◆ add_foreign_ordered_paths()

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

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

6376 {
6377  Query *parse = root->parse;
6378  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6379  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
6380  PgFdwPathExtraData *fpextra;
6381  double rows;
6382  int width;
6383  Cost startup_cost;
6384  Cost total_cost;
6385  List *fdw_private;
6386  ForeignPath *ordered_path;
6387  ListCell *lc;
6388 
6389  /* Shouldn't get here unless the query has ORDER BY */
6390  Assert(parse->sortClause);
6391 
6392  /* We don't support cases where there are any SRFs in the targetlist */
6393  if (parse->hasTargetSRFs)
6394  return;
6395 
6396  /* Save the input_rel as outerrel in fpinfo */
6397  fpinfo->outerrel = input_rel;
6398 
6399  /*
6400  * Copy foreign table, foreign server, user mapping, FDW options etc.
6401  * details from the input relation's fpinfo.
6402  */
6403  fpinfo->table = ifpinfo->table;
6404  fpinfo->server = ifpinfo->server;
6405  fpinfo->user = ifpinfo->user;
6406  merge_fdw_options(fpinfo, ifpinfo, NULL);
6407 
6408  /*
6409  * If the input_rel is a base or join relation, we would already have
6410  * considered pushing down the final sort to the remote server when
6411  * creating pre-sorted foreign paths for that relation, because the
6412  * query_pathkeys is set to the root->sort_pathkeys in that case (see
6413  * standard_qp_callback()).
6414  */
6415  if (input_rel->reloptkind == RELOPT_BASEREL ||
6416  input_rel->reloptkind == RELOPT_JOINREL)
6417  {
6418  Assert(root->query_pathkeys == root->sort_pathkeys);
6419 
6420  /* Safe to push down if the query_pathkeys is safe to push down */
6421  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
6422 
6423  return;
6424  }
6425 
6426  /* The input_rel should be a grouping relation */
6427  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
6428  ifpinfo->stage == UPPERREL_GROUP_AGG);
6429 
6430  /*
6431  * We try to create a path below by extending a simple foreign path for
6432  * the underlying grouping relation to perform the final sort remotely,
6433  * which is stored into the fdw_private list of the resulting path.
6434  */
6435 
6436  /* Assess if it is safe to push down the final sort */
6437  foreach(lc, root->sort_pathkeys)
6438  {
6439  PathKey *pathkey = (PathKey *) lfirst(lc);
6440  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6441  Expr *sort_expr;
6442 
6443  /*
6444  * is_foreign_expr would detect volatile expressions as well, but
6445  * checking ec_has_volatile here saves some cycles.
6446  */
6447  if (pathkey_ec->ec_has_volatile)
6448  return;
6449 
6450  /* Get the sort expression for the pathkey_ec */
6451  sort_expr = find_em_expr_for_input_target(root,
6452  pathkey_ec,
6453  input_rel->reltarget);
6454 
6455  /* If it's unsafe to remote, we cannot push down the final sort */
6456  if (!is_foreign_expr(root, input_rel, sort_expr))
6457  return;
6458  }
6459 
6460  /* Safe to push down */
6461  fpinfo->pushdown_safe = true;
6462 
6463  /* Construct PgFdwPathExtraData */
6464  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6465  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6466  fpextra->has_final_sort = true;
6467 
6468  /* Estimate the costs of performing the final sort remotely */
6469  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6470  &rows, &width, &startup_cost, &total_cost);
6471 
6472  /*
6473  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6474  * Items in the list must match order in enum FdwPathPrivateIndex.
6475  */
6476  fdw_private = list_make2(makeInteger(true), makeInteger(false));
6477 
6478  /* Create foreign ordering path */
6479  ordered_path = create_foreign_upper_path(root,
6480  input_rel,
6482  rows,
6483  startup_cost,
6484  total_cost,
6485  root->sort_pathkeys,
6486  NULL, /* no extra plan */
6487  fdw_private);
6488 
6489  /* and add it to the ordered_rel */
6490  add_path(ordered_rel, (Path *) ordered_path);
6491 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:161
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:2303
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
List * sortClause
Definition: parsenodes.h:169
RelOptKind reloptkind
Definition: pathnodes.h:673
List * query_pathkeys
Definition: pathnodes.h:293
PathTarget * target
Definition: postgres_fdw.c:292
ForeignServer * server
Definition: postgres_fdw.h:86
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
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:298
Value * makeInteger(int i)
Definition: value.c:23
UserMapping * user
Definition: postgres_fdw.h:87
void * palloc0(Size size)
Definition: mcxt.c:1093
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:732
bool hasTargetSRFs
Definition: parsenodes.h:135
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
ForeignTable * table
Definition: postgres_fdw.h:85
EquivalenceClass * pk_eclass
Definition: pathnodes.h:1058
bool ec_has_volatile
Definition: pathnodes.h:987
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:231
UpperRelationKind stage
Definition: postgres_fdw.h:109
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:687
double Cost
Definition: nodes.h:673
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:666
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: pathnodes.h:309

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

5670 {
5671  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
5672  ListCell *lc;
5673 
5674  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
5675 
5676  /* Create one path for each set of pathkeys we found above. */
5677  foreach(lc, useful_pathkeys_list)
5678  {
5679  double rows;
5680  int width;
5681  Cost startup_cost;
5682  Cost total_cost;
5683  List *useful_pathkeys = lfirst(lc);
5684  Path *sorted_epq_path;
5685 
5686  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
5687  &rows, &width, &startup_cost, &total_cost);
5688 
5689  /*
5690  * The EPQ path must be at least as well sorted as the path itself, in
5691  * case it gets used as input to a mergejoin.
5692  */
5693  sorted_epq_path = epq_path;
5694  if (sorted_epq_path != NULL &&
5695  !pathkeys_contained_in(useful_pathkeys,
5696  sorted_epq_path->pathkeys))
5697  sorted_epq_path = (Path *)
5698  create_sort_path(root,
5699  rel,
5700  sorted_epq_path,
5701  useful_pathkeys,
5702  -1.0);
5703 
5704  if (IS_SIMPLE_REL(rel))
5705  add_path(rel, (Path *)
5706  create_foreignscan_path(root, rel,
5707  NULL,
5708  rows,
5709  startup_cost,
5710  total_cost,
5711  useful_pathkeys,
5712  rel->lateral_relids,
5713  sorted_epq_path,
5714  NIL));
5715  else
5716  add_path(rel, (Path *)
5717  create_foreign_join_path(root, rel,
5718  NULL,
5719  rows,
5720  startup_cost,
5721  total_cost,
5722  useful_pathkeys,
5723  rel->lateral_relids,
5724  sorted_epq_path,
5725  NIL));
5726  }
5727 }
#define NIL
Definition: pg_list.h:65
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:908
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:649
Relids lateral_relids
Definition: pathnodes.h:701
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:2209
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:2253
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:2927
List * pathkeys
Definition: pathnodes.h:1188
#define lfirst(lc)
Definition: pg_list.h:169
Definition: pg_list.h:50
double Cost
Definition: nodes.h:673

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

3548 {
3549  /*
3550  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3551  * side is unlikely to generate properly-sorted output, so it would need
3552  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3553  * if the GROUP BY clause is sort-able but isn't a superset of the given
3554  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3555  * costs by applying the same heuristic as for the scan or join case.
3556  */
3557  if (!grouping_is_sortable(root->parse->groupClause) ||
3558  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3559  {
3560  Path sort_path; /* dummy for result of cost_sort */
3561 
3562  cost_sort(&sort_path,
3563  root,
3564  pathkeys,
3565  *p_startup_cost + *p_run_cost,
3566  retrieved_rows,
3567  width,
3568  0.0,
3569  work_mem,
3570  limit_tuples);
3571 
3572  *p_startup_cost = sort_path.startup_cost;
3573  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3574  }
3575  else
3576  {
3577  /*
3578  * The default extra cost seems too large for foreign-grouping cases;
3579  * add 1/4th of that default.
3580  */
3581  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3582  - 1.0) * 0.25;
3583 
3584  *p_startup_cost *= sort_multiplier;
3585  *p_run_cost *= sort_multiplier;
3586  }
3587 }
List * group_pathkeys
Definition: pathnodes.h:295
Query * parse
Definition: pathnodes.h:161
Cost startup_cost
Definition: pathnodes.h:1185
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:2036
int work_mem
Definition: globals.c:124
Cost total_cost
Definition: pathnodes.h:1186
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:61
List * groupClause
Definition: parsenodes.h:158
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:529

◆ analyze_row_processor()

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

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

5059 {
5060  int targrows = astate->targrows;
5061  int pos; /* array index to store tuple in */
5062  MemoryContext oldcontext;
5063 
5064  /* Always increment sample row counter. */
5065  astate->samplerows += 1;
5066 
5067  /*
5068  * Determine the slot where this sample row should be stored. Set pos to
5069  * negative value to indicate the row should be skipped.
5070  */
5071  if (astate->numrows < targrows)
5072  {
5073  /* First targrows rows are always included into the sample */
5074  pos = astate->numrows++;
5075  }
5076  else
5077  {
5078  /*
5079  * Now we start replacing tuples in the sample until we reach the end
5080  * of the relation. Same algorithm as in acquire_sample_rows in
5081  * analyze.c; see Jeff Vitter's paper.
5082  */
5083  if (astate->rowstoskip < 0)
5084  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5085 
5086  if (astate->rowstoskip <= 0)
5087  {
5088  /* Choose a random reservoir element to replace. */
5089  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
5090  Assert(pos >= 0 && pos < targrows);
5091  heap_freetuple(astate->rows[pos]);
5092  }
5093  else
5094  {
5095  /* Skip this tuple. */
5096  pos = -1;
5097  }
5098 
5099  astate->rowstoskip -= 1;
5100  }
5101 
5102  if (pos >= 0)
5103  {
5104  /*
5105  * Create sample tuple from current result row, and store it in the
5106  * position determined above. The tuple has to be created in anl_cxt.
5107  */
5108  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5109 
5110  astate->rows[pos] = make_tuple_from_result_row(res, row,
5111  astate->rel,
5112  astate->attinmeta,
5113  astate->retrieved_attrs,
5114  NULL,
5115  astate->temp_cxt);
5116 
5117  MemoryContextSwitchTo(oldcontext);
5118  }
5119 }
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:260
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
double sampler_random_fract(SamplerRandomState randstate)
Definition: sampling.c:242
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
ReservoirStateData rstate
Definition: postgres_fdw.c:267
AttInMetadata * attinmeta
Definition: postgres_fdw.c:256
MemoryContext temp_cxt
Definition: postgres_fdw.c:271
#define Assert(condition)
Definition: c.h:804
MemoryContext anl_cxt
Definition: postgres_fdw.c:270
SamplerRandomState randstate
Definition: sampling.h:50
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:146

◆ apply_returning_filter()

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

Definition at line 4638 of file postgres_fdw.c.

References PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, DatumGetPointer, ExecClearTuple(), ExecFetchSlotHeapTuple(), ExecGetReturningSlot(), ExecStoreVirtualTuple(), PgFdwDirectModifyState::hasSystemCols, HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, i, InvalidTransactionId, TupleDescData::natts, RelationGetDescr, PgFdwDirectModifyState::resultRel, slot_getallattrs(), HeapTupleData::t_data, HeapTupleData::t_self, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, and values.

Referenced by get_returning_data().

4642 {
4643  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4644  TupleTableSlot *resultSlot;
4645  Datum *values;
4646  bool *isnull;
4647  Datum *old_values;
4648  bool *old_isnull;
4649  int i;
4650 
4651  /*
4652  * Use the return tuple slot as a place to store the result tuple.
4653  */
4654  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4655 
4656  /*
4657  * Extract all the values of the scan tuple.
4658  */
4659  slot_getallattrs(slot);
4660  old_values = slot->tts_values;
4661  old_isnull = slot->tts_isnull;
4662 
4663  /*
4664  * Prepare to build the result tuple.
4665  */
4666  ExecClearTuple(resultSlot);
4667  values = resultSlot->tts_values;
4668  isnull = resultSlot->tts_isnull;
4669 
4670  /*
4671  * Transpose data into proper fields of the result tuple.
4672  */
4673  for (i = 0; i < resultTupType->natts; i++)
4674  {
4675  int j = dmstate->attnoMap[i];
4676 
4677  if (j == 0)
4678  {
4679  values[i] = (Datum) 0;
4680  isnull[i] = true;
4681  }
4682  else
4683  {
4684  values[i] = old_values[j - 1];
4685  isnull[i] = old_isnull[j - 1];
4686  }
4687  }
4688 
4689  /*
4690  * Build the virtual tuple.
4691  */
4692  ExecStoreVirtualTuple(resultSlot);
4693 
4694  /*
4695  * If we have any system columns to return, materialize a heap tuple in
4696  * the slot from column values set above and install system columns in
4697  * that tuple.
4698  */
4699  if (dmstate->hasSystemCols)
4700  {
4701  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4702 
4703  /* ctid */
4704  if (dmstate->ctidAttno)
4705  {
4706  ItemPointer ctid = NULL;
4707 
4708  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4709  resultTup->t_self = *ctid;
4710  }
4711 
4712  /*
4713  * And remaining columns
4714  *
4715  * Note: since we currently don't allow the target relation to appear
4716  * on the nullable side of an outer join, any system columns wouldn't
4717  * go to NULL.
4718  *
4719  * Note: no need to care about tableoid here because it will be
4720  * initialized in ExecProcessReturning().
4721  */
4725  }
4726 
4727  /*
4728  * And return the result tuple.
4729  */
4730  return resultSlot;
4731 }
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1210
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define RelationGetDescr(relation)
Definition: rel.h:483
Datum * tts_values
Definition: tuptable.h:126
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
ItemPointerData t_self
Definition: htup.h:65
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:354
bool * tts_isnull
Definition: tuptable.h:128
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:380
#define InvalidTransactionId
Definition: transam.h:31
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1644
uintptr_t Datum
Definition: postgres.h:411
#define DatumGetPointer(X)
Definition: postgres.h:593
static Datum values[MAXATTR]
Definition: bootstrap.c:166
int i
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:397
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1552
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:319

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 5735 of file postgres_fdw.c.

References PgFdwRelationInfo::async_capable, 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().

5736 {
5737  ListCell *lc;
5738 
5739  foreach(lc, fpinfo->server->options)
5740  {
5741  DefElem *def = (DefElem *) lfirst(lc);
5742 
5743  if (strcmp(def->defname, "use_remote_estimate") == 0)
5744  fpinfo->use_remote_estimate = defGetBoolean(def);
5745  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
5746  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
5747  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
5748  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
5749  else if (strcmp(def->defname, "extensions") == 0)
5750  fpinfo->shippable_extensions =
5751  ExtractExtensionList(defGetString(def), false);
5752  else if (strcmp(def->defname, "fetch_size") == 0)
5753  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5754  else if (strcmp(def->defname, "async_capable") == 0)
5755  fpinfo->async_capable = defGetBoolean(def);
5756  }
5757 }
ForeignServer * server
Definition: postgres_fdw.h:86
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:388
#define lfirst(lc)
Definition: pg_list.h:169
char * defname
Definition: parsenodes.h:746
List * shippable_extensions
Definition: postgres_fdw.h:81
List * options
Definition: foreign.h:42

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 5765 of file postgres_fdw.c.

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

Referenced by postgresGetForeignRelSize().

5766 {
5767  ListCell *lc;
5768 
5769  foreach(lc, fpinfo->table->options)
5770  {
5771  DefElem *def = (DefElem *) lfirst(lc);
5772 
5773  if (strcmp(def->defname, "use_remote_estimate") == 0)
5774  fpinfo->use_remote_estimate = defGetBoolean(def);
5775  else if (strcmp(def->defname, "fetch_size") == 0)
5776  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5777  else if (strcmp(def->defname, "async_capable") == 0)
5778  fpinfo->async_capable = defGetBoolean(def);
5779  }
5780 }
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * defGetString(DefElem *def)
Definition: define.c:49
#define lfirst(lc)
Definition: pg_list.h:169
ForeignTable * table
Definition: postgres_fdw.h:85
List * options
Definition: foreign.h:57
char * defname
Definition: parsenodes.h:746

◆ build_remote_returning()

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

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

4311 {
4312  bool have_wholerow = false;
4313  List *tlist = NIL;
4314  List *vars;
4315  ListCell *lc;
4316 
4317  Assert(returningList);
4318 
4319  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4320 
4321  /*
4322  * If there's a whole-row reference to the target relation, then we'll
4323  * need all the columns of the relation.
4324  */
4325  foreach(lc, vars)
4326  {
4327  Var *var = (Var *) lfirst(lc);
4328 
4329  if (IsA(var, Var) &&
4330  var->varno == rtindex &&
4331  var->varattno == InvalidAttrNumber)
4332  {
4333  have_wholerow = true;
4334  break;
4335  }
4336  }
4337 
4338  if (have_wholerow)
4339  {
4340  TupleDesc tupdesc = RelationGetDescr(rel);
4341  int i;
4342 
4343  for (i = 1; i <= tupdesc->natts; i++)
4344  {
4345  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4346  Var *var;
4347 
4348  /* Ignore dropped attributes. */
4349  if (attr->attisdropped)
4350  continue;
4351 
4352  var = makeVar(rtindex,
4353  i,
4354  attr->atttypid,
4355  attr->atttypmod,
4356  attr->attcollation,
4357  0);
4358 
4359  tlist = lappend(tlist,
4360  makeTargetEntry((Expr *) var,
4361  list_length(tlist) + 1,
4362  NULL,
4363  false));
4364  }
4365  }
4366 
4367  /* Now add any remaining columns to tlist. */
4368  foreach(lc, vars)
4369  {
4370  Var *var = (Var *) lfirst(lc);
4371 
4372  /*
4373  * No need for whole-row references to the target relation. We don't
4374  * need system columns other than ctid and oid either, since those are
4375  * set locally.
4376  */
4377  if (IsA(var, Var) &&
4378  var->varno == rtindex &&
4379  var->varattno <= InvalidAttrNumber &&
4381  continue; /* don't need it */
4382 
4383  if (tlist_member((Expr *) var, tlist))
4384  continue; /* already got it */
4385 
4386  tlist = lappend(tlist,
4387  makeTargetEntry((Expr *) var,
4388  list_length(tlist) + 1,
4389  NULL,
4390  false));
4391  }
4392 
4393  list_free(vars);
4394 
4395  return tlist;
4396 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
#define RelationGetDescr(relation)
Definition: rel.h:483
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
Definition: nodes.h:539
AttrNumber varattno
Definition: primnodes.h:191
List * pull_var_clause(Node *node, int flags)
Definition: var.c:562
Definition: primnodes.h:186
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:238
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
List * lappend(List *list, void *datum)
Definition: list.c:336
Index varno
Definition: primnodes.h:189
#define PVC_INCLUDE_PLACEHOLDERS
Definition: optimizer.h:189
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:68
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
#define InvalidAttrNumber
Definition: attnum.h:23
void list_free(List *list)
Definition: list.c:1391
int i
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
Definition: regcomp.c:238
Definition: pg_list.h:50

◆ close_cursor()

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

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

3843 {
3844  char sql[64];
3845  PGresult *res;
3846 
3847  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3848 
3849  /*
3850  * We don't use a PG_TRY block here, so be careful not to throw error
3851  * without releasing the PGresult.
3852  */
3853  res = pgfdw_exec_query(conn, sql, conn_state);
3854  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3855  pgfdw_report_error(ERROR, res, conn, true, sql);
3856  PQclear(res);
3857 }
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:685
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3097
#define ERROR
Definition: elog.h:46
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:778
static unsigned int cursor_number
Definition: connection.c:76
void PQclear(PGresult *res)
Definition: fe-exec.c:680
#define snprintf
Definition: port.h:216

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

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

7120 {
7121  const char *attname = NULL;
7122  const char *relname = NULL;
7123  bool is_wholerow = false;
7125 
7126  if (errpos->rel)
7127  {
7128  /* error occurred in a scan against a foreign table */
7129  TupleDesc tupdesc = RelationGetDescr(errpos->rel);
7130  Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
7131 
7132  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7133  attname = NameStr(attr->attname);
7134  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7135  attname = "ctid";
7136 
7137  relname = RelationGetRelationName(errpos->rel);
7138  }
7139  else
7140  {
7141  /* error occurred in a scan against a foreign join */
7142  ForeignScanState *fsstate = errpos->fsstate;
7143  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7144  EState *estate = fsstate->ss.ps.state;
7145  TargetEntry *tle;
7146 
7147  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7148  errpos->cur_attno - 1);
7149 
7150  /*
7151  * Target list can have Vars and expressions. For Vars, we can get
7152  * its relation, however for expressions we can't. Thus for
7153  * expressions, just show generic context message.
7154  */
7155  if (IsA(tle->expr, Var))
7156  {
7157  RangeTblEntry *rte;
7158  Var *var = (Var *) tle->expr;
7159 
7160  rte = exec_rt_fetch(var->varno, estate);
7161 
7162  if (var->varattno == 0)
7163  is_wholerow = true;
7164  else
7165  attname = get_attname(rte->relid, var->varattno, false);
7166 
7167  relname = get_rel_name(rte->relid);
7168  }
7169  else
7170  errcontext("processing expression at position %d in select list",
7171  errpos->cur_attno);
7172  }
7173 
7174  if (relname)
7175  {
7176  if (is_wholerow)
7177  errcontext("whole-row reference to foreign table \"%s\"", relname);
7178  else if (attname)
7179  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7180  }
7181 }
ScanState ss
Definition: execnodes.h:1853
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
#define RelationGetDescr(relation)
Definition: rel.h:483
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
AttrNumber varattno
Definition: primnodes.h:191
List * fdw_scan_tlist
Definition: plannodes.h:638
EState * state
Definition: execnodes.h:966
NameData relname
Definition: pg_class.h:38
Definition: primnodes.h:186
PlanState ps
Definition: execnodes.h:1373
ForeignScanState * fsstate
Definition: postgres_fdw.c:314
NameData attname
Definition: pg_attribute.h:41
#define list_nth_node(type, list, n)
Definition: pg_list.h:306
#define RelationGetRelationName(relation)
Definition: rel.h:491
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:570
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
Index varno
Definition: primnodes.h:189
Plan * plan
Definition: execnodes.h:964
Expr * expr
Definition: primnodes.h:1444
#define errcontext
Definition: elog.h:204
#define NameStr(name)
Definition: c.h:681
void * arg
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:825
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
AttrNumber cur_attno
Definition: postgres_fdw.c:306
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1899

◆ convert_prep_stmt_params()

static const char ** convert_prep_stmt_params ( PgFdwModifyState fmstate,
ItemPointer  tupleid,
TupleTableSlot **  slots,
int  numSlots 
)
static

Definition at line 4155 of file postgres_fdw.c.

References Assert, attnum, i, 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().

4159 {
4160  const char **p_values;
4161  int i;
4162  int j;
4163  int pindex = 0;
4164  MemoryContext oldcontext;
4165 
4166  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4167 
4168  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4169 
4170  /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4171  Assert(!(tupleid != NULL && numSlots > 1));
4172 
4173  /* 1st parameter should be ctid, if it's in use */
4174  if (tupleid != NULL)
4175  {
4176  Assert(numSlots == 1);
4177  /* don't need set_transmission_modes for TID output */
4178  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
4179  PointerGetDatum(tupleid));
4180  pindex++;
4181  }
4182 
4183  /* get following parameters from slots */
4184  if (slots != NULL && fmstate->target_attrs != NIL)
4185  {
4186  int nestlevel;
4187  ListCell *lc;
4188 
4189  nestlevel = set_transmission_modes();
4190 
4191  for (i = 0; i < numSlots; i++)
4192  {
4193  j = (tupleid != NULL) ? 1 : 0;
4194  foreach(lc, fmstate->target_attrs)
4195  {
4196  int attnum = lfirst_int(lc);
4197  Datum value;
4198  bool isnull;
4199 
4200  value = slot_getattr(slots[i], attnum, &isnull);
4201  if (isnull)
4202  p_values[pindex] = NULL;
4203  else
4204  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[j],
4205  value);
4206  pindex++;
4207  j++;
4208  }
4209  }
4210 
4211  reset_transmission_modes(nestlevel);
4212  }
4213 
4214  Assert(pindex == fmstate->p_nums * numSlots);
4215 
4216  MemoryContextSwitchTo(oldcontext);
4217 
4218  return p_values;
4219 }
#define NIL
Definition: pg_list.h:65
static struct @142 value
#define PointerGetDatum(X)
Definition: postgres.h:600
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int set_transmission_modes(void)
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1573
#define lfirst_int(lc)
Definition: pg_list.h:170
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:201
uintptr_t Datum
Definition: postgres.h:411
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:381
int16 attnum
Definition: pg_attribute.h:83
MemoryContext temp_cxt
Definition: postgres_fdw.c:207
#define Assert(condition)
Definition: c.h:804
void reset_transmission_modes(int nestlevel)
void * palloc(Size size)
Definition: mcxt.c:1062
int i

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

Definition at line 3624 of file postgres_fdw.c.

References appendStringInfo(), buf, PgFdwScanState::conn, PgFdwScanState::conn_state, 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, PgFdwConnState::pendingAreq, pfree(), pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), PQsendQueryParams(), process_pending_request(), process_query_params(), ScanState::ps, PlanState::ps_ExprContext, PgFdwScanState::query, ForeignScanState::ss, PgFdwScanState::tuples, and values.

Referenced by fetch_more_data_begin(), and postgresIterateForeignScan().

3625 {
3626  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3627  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3628  int numParams = fsstate->numParams;
3629  const char **values = fsstate->param_values;
3630  PGconn *conn = fsstate->conn;
3632  PGresult *res;
3633 
3634  /* First, process a pending asynchronous request, if any. */
3635  if (fsstate->conn_state->pendingAreq)
3637 
3638  /*
3639  * Construct array of query parameter values in text format. We do the
3640  * conversions in the short-lived per-tuple context, so as not to cause a
3641  * memory leak over repeated scans.
3642  */
3643  if (numParams > 0)
3644  {
3645  MemoryContext oldcontext;
3646 
3647  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3648 
3649  process_query_params(econtext,
3650  fsstate->param_flinfo,
3651  fsstate->param_exprs,
3652  values);
3653 
3654  MemoryContextSwitchTo(oldcontext);
3655  }
3656 
3657  /* Construct the DECLARE CURSOR command */
3658  initStringInfo(&buf);
3659  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3660  fsstate->cursor_number, fsstate->query);
3661 
3662  /*
3663  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3664  * to infer types for all parameters. Since we explicitly cast every
3665  * parameter (see deparse.c), the "inference" is trivial and will produce
3666  * the desired result. This allows us to avoid assuming that the remote
3667  * server has the same OIDs we do for the parameters' types.
3668  */
3669  if (!PQsendQueryParams(conn, buf.data, numParams,
3670  NULL, values, NULL, NULL, 0))
3671  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3672 
3673  /*
3674  * Get the result, and check for success.
3675  *
3676  * We don't use a PG_TRY block here, so be careful not to throw error
3677  * without releasing the PGresult.
3678  */
3679  res = pgfdw_get_result(conn, buf.data);
3680  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3681  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3682  PQclear(res);
3683 
3684  /* Mark the cursor as created, and show no tuples have been retrieved */
3685  fsstate->cursor_exists = true;
3686  fsstate->tuples = NULL;
3687  fsstate->num_tuples = 0;
3688  fsstate->next_tuple = 0;
3689  fsstate->fetch_ct_2 = 0;
3690  fsstate->eof_reached = false;
3691 
3692  /* Clean up */
3693  pfree(buf.data);
3694 }
ScanState ss
Definition: execnodes.h:1853
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:1387
List * param_exprs
Definition: postgres_fdw.c:154
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
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:3097
unsigned int cursor_number
Definition: postgres_fdw.c:150
PlanState ps
Definition: execnodes.h:1373
void pfree(void *pointer)
Definition: mcxt.c:1169
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
#define ERROR
Definition: elog.h:46
const char ** param_values
Definition: postgres_fdw.c:155
PGconn * conn
Definition: streamutil.c:54
void process_pending_request(AsyncRequest *areq)
static char * buf
Definition: pg_test_fsync.c:68
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:778
FmgrInfo * param_flinfo
Definition: postgres_fdw.c:153
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
void PQclear(PGresult *res)
Definition: fe-exec.c:680
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:134
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:713
PgFdwConnState * conn_state
Definition: postgres_fdw.c:149
HeapTuple * tuples
Definition: postgres_fdw.c:158
static Datum values[MAXATTR]
Definition: bootstrap.c:166

◆ create_foreign_modify()

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

Definition at line 3865 of file postgres_fdw.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, Assert, PgFdwModifyState::attinmeta, attnum, AttributeNumberIsValid, PgFdwModifyState::aux_fmstate, PgFdwModifyState::batch_size, RangeTblEntry::checkAsUser, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, PgFdwModifyState::conn_state, PgFdwModifyState::ctidAttno, elog, ERROR, EState::es_query_cxt, ExecFindJunkAttributeInTlist(), fmgr_info(), get_batch_size_option(), GetConnection(), GetForeignTable(), getTypeOutputInfo(), GetUserId(), GetUserMapping(), PgFdwModifyState::has_returning, lfirst_int, list_length(), PgFdwModifyState::num_slots, PgFdwModifyState::orig_query, PgFdwModifyState::p_flinfo, PgFdwModifyState::p_name, PgFdwModifyState::p_nums, palloc0(), pstrdup(), 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(), user, and PgFdwModifyState::values_end.

Referenced by postgresBeginForeignInsert(), and postgresBeginForeignModify().

3875 {
3876  PgFdwModifyState *fmstate;
3877  Relation rel = resultRelInfo->ri_RelationDesc;
3878  TupleDesc tupdesc = RelationGetDescr(rel);
3879  Oid userid;
3880  ForeignTable *table;
3881  UserMapping *user;
3882  AttrNumber n_params;
3883  Oid typefnoid;
3884  bool isvarlena;
3885  ListCell *lc;
3886 
3887  /* Begin constructing PgFdwModifyState. */
3888  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3889  fmstate->rel = rel;
3890 
3891  /*
3892  * Identify which user to do the remote access as. This should match what
3893  * ExecCheckRTEPerms() does.
3894  */
3895  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
3896 
3897  /* Get info about foreign table. */
3898  table = GetForeignTable(RelationGetRelid(rel));
3899  user = GetUserMapping(userid, table->serverid);
3900 
3901  /* Open connection; report that we'll create a prepared statement. */
3902  fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
3903  fmstate->p_name = NULL; /* prepared statement not made yet */
3904 
3905  /* Set up remote query information. */
3906  fmstate->query = query;
3907  if (operation == CMD_INSERT)
3908  fmstate->orig_query = pstrdup(fmstate->query);
3909  fmstate->target_attrs = target_attrs;
3910  fmstate->values_end = values_end;
3911  fmstate->has_returning = has_returning;
3912  fmstate->retrieved_attrs = retrieved_attrs;
3913 
3914  /* Create context for per-tuple temp workspace. */
3915  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
3916  "postgres_fdw temporary data",
3918 
3919  /* Prepare for input conversion of RETURNING results. */
3920  if (fmstate->has_returning)
3921  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
3922 
3923  /* Prepare for output conversion of parameters used in prepared stmt. */
3924  n_params = list_length(fmstate->target_attrs) + 1;
3925  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
3926  fmstate->p_nums = 0;
3927 
3928  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3929  {
3930  Assert(subplan != NULL);
3931 
3932  /* Find the ctid resjunk column in the subplan's result */
3934  "ctid");
3935  if (!AttributeNumberIsValid(fmstate->ctidAttno))
3936  elog(ERROR, "could not find junk ctid column");
3937 
3938  /* First transmittable parameter will be ctid */
3939  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
3940  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3941  fmstate->p_nums++;
3942  }
3943 
3944  if (operation == CMD_INSERT || operation == CMD_UPDATE)
3945  {
3946  /* Set up for remaining transmittable parameters */
3947  foreach(lc, fmstate->target_attrs)
3948  {
3949  int attnum = lfirst_int(lc);
3950  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
3951 
3952  Assert(!attr->attisdropped);
3953 
3954  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
3955  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3956  fmstate->p_nums++;
3957  }
3958  }
3959 
3960  Assert(fmstate->p_nums <= n_params);
3961 
3962  /* Set batch_size from foreign server/table options. */
3963  if (operation == CMD_INSERT)
3964  fmstate->batch_size = get_batch_size_option(rel);
3965 
3966  fmstate->num_slots = 1;
3967 
3968  /* Initialize auxiliary state */
3969  fmstate->aux_fmstate = NULL;
3970 
3971  return fmstate;
3972 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:411
#define AllocSetContextCreate
Definition: memutils.h:173
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2854
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
#define RelationGetDescr(relation)
Definition: rel.h:483
Oid GetUserId(void)
Definition: miscinit.c:478
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
char * pstrdup(const char *in)
Definition: mcxt.c:1299
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:205
static int get_batch_size_option(Relation rel)
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:248
unsigned int Oid
Definition: postgres_ext.h:31
List * retrieved_attrs
Definition: postgres_fdw.c:196
PgFdwConnState * conn_state
Definition: postgres_fdw.c:186
MemoryContext es_query_cxt
Definition: execnodes.h:598
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
Definition: connection.c:125
#define ERROR
Definition: elog.h:46
#define lfirst_int(lc)
Definition: pg_list.h:170
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:126
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
struct PgFdwModifyState * aux_fmstate
Definition: postgres_fdw.c:210
AttrNumber ctidAttno
Definition: postgres_fdw.c:199
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:201
void * palloc0(Size size)
Definition: mcxt.c:1093
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2097
int16 attnum
Definition: pg_attribute.h:83
MemoryContext temp_cxt
Definition: postgres_fdw.c:207
#define Assert(condition)
Definition: c.h:804
Oid serverid
Definition: foreign.h:56
static int list_length(const List *l)
Definition: pg_list.h:149
List * targetlist
Definition: plannodes.h:141
static char * user
Definition: pg_regress.c:95
#define elog(elevel,...)
Definition: elog.h:232
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:198
AttInMetadata * attinmeta
Definition: postgres_fdw.c:182
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:457

◆ deallocate_query()

static void deallocate_query ( PgFdwModifyState fmstate)
static

Definition at line 4281 of file postgres_fdw.c.

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

Referenced by execute_foreign_modify(), and finish_foreign_modify().

4282 {
4283  char sql[64];
4284  PGresult *res;
4285 
4286  /* do nothing if the query is not allocated */
4287  if (!fmstate->p_name)
4288  return;
4289 
4290  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4291 
4292  /*
4293  * We don't use a PG_TRY block here, so be careful not to throw error
4294  * without releasing the PGresult.
4295  */
4296  res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4297  if (PQresultStatus(res) != PGRES_COMMAND_OK)
4298  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
4299  PQclear(res);
4300  pfree(fmstate->p_name);
4301  fmstate->p_name = NULL;
4302 }
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:685
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3097
PgFdwConnState * conn_state
Definition: postgres_fdw.c:186
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:778
void PQclear(PGresult *res)
Definition: fe-exec.c:680
#define snprintf
Definition: port.h:216

◆ ec_member_matches_foreign()

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

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

3598 {
3600  Expr *expr = em->em_expr;
3601 
3602  /*
3603  * If we've identified what we're processing in the current scan, we only
3604  * want to match that expression.
3605  */
3606  if (state->current != NULL)
3607  return equal(expr, state->current);
3608 
3609  /*
3610  * Otherwise, ignore anything we've already processed.
3611  */
3612  if (list_member(state->already_used, expr))
3613  return false;
3614 
3615  /* This is the new target to process. */
3616  state->current = expr;
3617  return true;
3618 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3105
bool list_member(const List *list, const void *datum)
Definition: list.c:628
Definition: regguts.h:317
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 2990 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().

2997 {
2998  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2999  double rows;
3000  double retrieved_rows;
3001  int width;
3002  Cost startup_cost;
3003  Cost total_cost;
3004 
3005  /* Make sure the core code has set up the relation's reltarget */
3006  Assert(foreignrel->reltarget);
3007 
3008  /*
3009  * If the table or the server is configured to use remote estimates,
3010  * connect to the foreign server and execute EXPLAIN to estimate the
3011  * number of rows selected by the restriction+join clauses. Otherwise,
3012  * estimate rows using whatever statistics we have locally, in a way
3013  * similar to ordinary tables.
3014  */
3015  if (fpinfo->use_remote_estimate)
3016  {
3017  List *remote_param_join_conds;
3018  List *local_param_join_conds;
3019  StringInfoData sql;
3020  PGconn *conn;
3021  Selectivity local_sel;
3022  QualCost local_cost;
3023  List *fdw_scan_tlist = NIL;
3024  List *remote_conds;
3025 
3026  /* Required only to be passed to deparseSelectStmtForRel */
3027  List *retrieved_attrs;
3028 
3029  /*
3030  * param_join_conds might contain both clauses that are safe to send
3031  * across, and clauses that aren't.
3032  */
3033  classifyConditions(root, foreignrel, param_join_conds,
3034  &remote_param_join_conds, &local_param_join_conds);
3035 
3036  /* Build the list of columns to be fetched from the foreign server. */
3037  if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
3038  fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
3039  else
3040  fdw_scan_tlist = NIL;
3041 
3042  /*
3043  * The complete list of remote conditions includes everything from
3044  * baserestrictinfo plus any extra join_conds relevant to this
3045  * particular path.
3046  */
3047  remote_conds = list_concat(remote_param_join_conds,
3048  fpinfo->remote_conds);
3049 
3050  /*
3051  * Construct EXPLAIN query including the desired SELECT, FROM, and
3052  * WHERE clauses. Params and other-relation Vars are replaced by dummy
3053  * values, so don't request params_list.
3054  */
3055  initStringInfo(&sql);
3056  appendStringInfoString(&sql, "EXPLAIN ");
3057  deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
3058  remote_conds, pathkeys,
3059  fpextra ? fpextra->has_final_sort : false,
3060  fpextra ? fpextra->has_limit : false,
3061  false, &retrieved_attrs, NULL);
3062 
3063  /* Get the remote estimate */
3064  conn = GetConnection(fpinfo->user, false, NULL);
3065  get_remote_estimate(sql.data, conn, &rows, &width,
3066  &startup_cost, &total_cost);
3067  ReleaseConnection(conn);
3068 
3069  retrieved_rows = rows;
3070 
3071  /* Factor in the selectivity of the locally-checked quals */
3072  local_sel = clauselist_selectivity(root,
3073  local_param_join_conds,
3074  foreignrel->relid,
3075  JOIN_INNER,
3076  NULL);
3077  local_sel *= fpinfo->local_conds_sel;
3078 
3079  rows = clamp_row_est(rows * local_sel);
3080 
3081  /* Add in the eval cost of the locally-checked quals */
3082  startup_cost += fpinfo->local_conds_cost.startup;
3083  total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3084  cost_qual_eval(&local_cost, local_param_join_conds, root);
3085  startup_cost += local_cost.startup;
3086  total_cost += local_cost.per_tuple * retrieved_rows;
3087 
3088  /*
3089  * Add in tlist eval cost for each output row. In case of an
3090  * aggregate, some of the tlist expressions such as grouping
3091  * expressions will be evaluated remotely, so adjust the costs.
3092  */
3093  startup_cost += foreignrel->reltarget->cost.startup;
3094  total_cost += foreignrel->reltarget->cost.startup;
3095  total_cost += foreignrel->reltarget->cost.per_tuple * rows;
3096  if (IS_UPPER_REL(foreignrel))
3097  {
3098  QualCost tlist_cost;
3099 
3100  cost_qual_eval(&tlist_cost, fdw_scan_tlist, root);
3101  startup_cost -= tlist_cost.startup;
3102  total_cost -= tlist_cost.startup;
3103  total_cost -= tlist_cost.per_tuple * rows;
3104  }
3105  }
3106  else
3107  {
3108  Cost run_cost = 0;
3109 
3110  /*
3111  * We don't support join conditions in this mode (hence, no
3112  * parameterized paths can be made).
3113  */
3114  Assert(param_join_conds == NIL);
3115 
3116  /*
3117  * We will come here again and again with different set of pathkeys or
3118  * additional post-scan/join-processing steps that caller wants to
3119  * cost. We don't need to calculate the cost/size estimates for the
3120  * underlying scan, join, or grouping each time. Instead, use those
3121  * estimates if we have cached them already.
3122  */
3123  if (fpinfo->rel_startup_cost >= 0 && fpinfo->rel_total_cost >= 0)
3124  {
3125  Assert(fpinfo->retrieved_rows >= 0);
3126 
3127  rows = fpinfo->rows;
3128  retrieved_rows = fpinfo->retrieved_rows;
3129  width = fpinfo->width;
3130  startup_cost = fpinfo->rel_startup_cost;
3131  run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
3132 
3133  /*
3134  * If we estimate the costs of a foreign scan or a foreign join
3135  * with additional post-scan/join-processing steps, the scan or
3136  * join costs obtained from the cache wouldn't yet contain the
3137  * eval costs for the final scan/join target, which would've been
3138  * updated by apply_scanjoin_target_to_paths(); add the eval costs
3139  * now.
3140  */
3141  if (fpextra && !IS_UPPER_REL(foreignrel))
3142  {
3143  /* Shouldn't get here unless we have LIMIT */
3144  Assert(fpextra->has_limit);
3145  Assert(foreignrel->reloptkind == RELOPT_BASEREL ||
3146  foreignrel->reloptkind == RELOPT_JOINREL);
3147  startup_cost += foreignrel->reltarget->cost.startup;
3148  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3149  }
3150  }
3151  else if (IS_JOIN_REL(foreignrel))
3152  {
3153  PgFdwRelationInfo *fpinfo_i;
3154  PgFdwRelationInfo *fpinfo_o;
3155  QualCost join_cost;
3156  QualCost remote_conds_cost;
3157  double nrows;
3158 
3159  /* Use rows/width estimates made by the core code. */
3160  rows = foreignrel->rows;
3161  width = foreignrel->reltarget->width;
3162 
3163  /* For join we expect inner and outer relations set */
3164  Assert(fpinfo->innerrel && fpinfo->outerrel);
3165 
3166  fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
3167  fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
3168 
3169  /* Estimate of number of rows in cross product */
3170  nrows = fpinfo_i->rows * fpinfo_o->rows;
3171 
3172  /*
3173  * Back into an estimate of the number of retrieved rows. Just in
3174  * case this is nuts, clamp to at most nrows.
3175  */
3176  retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3177  retrieved_rows = Min(retrieved_rows, nrows);
3178 
3179  /*
3180  * The cost of foreign join is estimated as cost of generating
3181  * rows for the joining relations + cost for applying quals on the
3182  * rows.
3183  */
3184 
3185  /*
3186  * Calculate the cost of clauses pushed down to the foreign server
3187  */
3188  cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
3189  /* Calculate the cost of applying join clauses */
3190  cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
3191 
3192  /*
3193  * Startup cost includes startup cost of joining relations and the
3194  * startup cost for join and other clauses. We do not include the
3195  * startup cost specific to join strategy (e.g. setting up hash
3196  * tables) since we do not know what strategy the foreign server
3197  * is going to use.
3198  */
3199  startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
3200  startup_cost += join_cost.startup;
3201  startup_cost += remote_conds_cost.startup;
3202  startup_cost += fpinfo->local_conds_cost.startup;
3203 
3204  /*
3205  * Run time cost includes:
3206  *
3207  * 1. Run time cost (total_cost - startup_cost) of relations being
3208  * joined
3209  *
3210  * 2. Run time cost of applying join clauses on the cross product
3211  * of the joining relations.
3212  *
3213  * 3. Run time cost of applying pushed down other clauses on the
3214  * result of join
3215  *
3216  * 4. Run time cost of applying nonpushable other clauses locally
3217  * on the result fetched from the foreign server.
3218  */
3219  run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
3220  run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
3221  run_cost += nrows * join_cost.per_tuple;
3222  nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
3223  run_cost += nrows * remote_conds_cost.per_tuple;
3224  run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3225 
3226  /* Add in tlist eval cost for each output row */
3227  startup_cost += foreignrel->reltarget->cost.startup;
3228  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3229  }
3230  else if (IS_UPPER_REL(foreignrel))
3231  {
3232  RelOptInfo *outerrel = fpinfo->outerrel;
3233  PgFdwRelationInfo *ofpinfo;
3234  AggClauseCosts aggcosts;
3235  double input_rows;
3236  int numGroupCols;
3237  double numGroups = 1;
3238 
3239  /* The upper relation should have its outer relation set */
3240  Assert(outerrel);
3241  /* and that outer relation should have its reltarget set */
3242  Assert(outerrel->reltarget);
3243 
3244  /*
3245  * This cost model is mixture of costing done for sorted and
3246  * hashed aggregates in cost_agg(). We are not sure which
3247  * strategy will be considered at remote side, thus for
3248  * simplicity, we put all startup related costs in startup_cost
3249  * and all finalization and run cost are added in total_cost.
3250  */
3251 
3252  ofpinfo = (PgFdwRelationInfo *) outerrel->fdw_private;
3253 
3254  /* Get rows from input rel */
3255  input_rows = ofpinfo->rows;
3256 
3257  /* Collect statistics about aggregates for estimating costs. */
3258  MemSet(&aggcosts, 0, sizeof(AggClauseCosts));
3259  if (root->parse->hasAggs)
3260  {
3261  get_agg_clause_costs(root, AGGSPLIT_SIMPLE, &aggcosts);
3262  }
3263 
3264  /* Get number of grouping columns and possible number of groups */
3265  numGroupCols = list_length(root->parse->groupClause);
3266  numGroups = estimate_num_groups(root,
3268  fpinfo->grouped_tlist),
3269  input_rows, NULL, NULL);
3270 
3271  /*
3272  * Get the retrieved_rows and rows estimates. If there are HAVING
3273  * quals, account for their selectivity.
3274  */
3275  if (root->parse->havingQual)
3276  {
3277  /* Factor in the selectivity of the remotely-checked quals */
3278  retrieved_rows =
3279  clamp_row_est(numGroups *
3281  fpinfo->remote_conds,
3282  0,
3283  JOIN_INNER,
3284  NULL));
3285  /* Factor in the selectivity of the locally-checked quals */
3286  rows = clamp_row_est(retrieved_rows * fpinfo->local_conds_sel);
3287  }
3288  else
3289  {
3290  rows = retrieved_rows = numGroups;
3291  }
3292 
3293  /* Use width estimate made by the core code. */
3294  width = foreignrel->reltarget->width;
3295 
3296  /*-----
3297  * Startup cost includes:
3298  * 1. Startup cost for underneath input relation, adjusted for
3299  * tlist replacement by apply_scanjoin_target_to_paths()
3300  * 2. Cost of performing aggregation, per cost_agg()
3301  *-----
3302  */
3303  startup_cost = ofpinfo->rel_startup_cost;
3304  startup_cost += outerrel->reltarget->cost.startup;
3305  startup_cost += aggcosts.transCost.startup;
3306  startup_cost += aggcosts.transCost.per_tuple * input_rows;
3307  startup_cost += aggcosts.finalCost.startup;
3308  startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
3309 
3310  /*-----
3311  * Run time cost includes:
3312  * 1. Run time cost of underneath input relation, adjusted for
3313  * tlist replacement by apply_scanjoin_target_to_paths()
3314  * 2. Run time cost of performing aggregation, per cost_agg()
3315  *-----
3316  */
3317  run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
3318  run_cost += outerrel->reltarget->cost.per_tuple * input_rows;
3319  run_cost += aggcosts.finalCost.per_tuple * numGroups;
3320  run_cost += cpu_tuple_cost * numGroups;
3321 
3322  /* Account for the eval cost of HAVING quals, if any */
3323  if (root->parse->havingQual)
3324  {
3325  QualCost remote_cost;
3326 
3327  /* Add in the eval cost of the remotely-checked quals */
3328  cost_qual_eval(&remote_cost, fpinfo->remote_conds, root);
3329  startup_cost += remote_cost.startup;
3330  run_cost += remote_cost.per_tuple * numGroups;
3331  /* Add in the eval cost of the locally-checked quals */
3332  startup_cost += fpinfo->local_conds_cost.startup;
3333  run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3334  }
3335 
3336  /* Add in tlist eval cost for each output row */
3337  startup_cost += foreignrel->reltarget->cost.startup;
3338  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3339  }
3340  else
3341  {
3342  Cost cpu_per_tuple;
3343 
3344  /* Use rows/width estimates made by set_baserel_size_estimates. */
3345  rows = foreignrel->rows;
3346  width = foreignrel->reltarget->width;
3347 
3348  /*
3349  * Back into an estimate of the number of retrieved rows. Just in
3350  * case this is nuts, clamp to at most foreignrel->tuples.
3351  */
3352  retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3353  retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
3354 
3355  /*
3356  * Cost as though this were a seqscan, which is pessimistic. We
3357  * effectively imagine the local_conds are being evaluated
3358  * remotely, too.
3359  */
3360  startup_cost = 0;
3361  run_cost = 0;
3362  run_cost += seq_page_cost * foreignrel->pages;
3363 
3364  startup_cost += foreignrel->baserestrictcost.startup;
3365  cpu_per_tuple = cpu_tuple_cost + foreignrel->baserestrictcost.per_tuple;
3366  run_cost += cpu_per_tuple * foreignrel->tuples;
3367 
3368  /* Add in tlist eval cost for each output row */
3369  startup_cost += foreignrel->reltarget->cost.startup;
3370  run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3371  }
3372 
3373  /*
3374  * Without remote estimates, we have no real way to estimate the cost
3375  * of generating sorted output. It could be free if the query plan
3376  * the remote side would have chosen generates properly-sorted output
3377  * anyway, but in most cases it will cost something. Estimate a value
3378  * high enough that we won't pick the sorted path when the ordering
3379  * isn't locally useful, but low enough that we'll err on the side of
3380  * pushing down the ORDER BY clause when it's useful to do so.
3381  */
3382  if (pathkeys != NIL)
3383  {
3384  if (IS_UPPER_REL(foreignrel))
3385  {
3386  Assert(foreignrel->reloptkind == RELOPT_UPPER_REL &&
3387  fpinfo->stage == UPPERREL_GROUP_AGG);
3388  adjust_foreign_grouping_path_cost(root, pathkeys,
3389  retrieved_rows, width,
3390  fpextra->limit_tuples,
3391  &startup_cost, &run_cost);
3392  }
3393  else
3394  {
3395  startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3396  run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3397  }
3398  }
3399 
3400  total_cost = startup_cost + run_cost;
3401 
3402  /* Adjust the cost estimates if we have LIMIT */
3403  if (fpextra && fpextra->has_limit)
3404  {
3405  adjust_limit_rows_costs(&rows, &startup_cost, &total_cost,
3406  fpextra->offset_est, fpextra->count_est);
3407  retrieved_rows = rows;
3408  }
3409  }
3410 
3411  /*
3412  * If this includes the final sort step, the given target, which will be
3413  * applied to the resulting path, might have different expressions from
3414  * the foreignrel's reltarget (see make_sort_input_target()); adjust tlist
3415  * eval costs.
3416  */
3417  if (fpextra && fpextra->has_final_sort &&
3418  fpextra->target != foreignrel->reltarget)
3419  {
3420  QualCost oldcost = foreignrel->reltarget->cost;
3421  QualCost newcost = fpextra->target->cost;
3422 
3423  startup_cost += newcost.startup - oldcost.startup;
3424  total_cost += newcost.startup - oldcost.startup;
3425  total_cost += (newcost.per_tuple - oldcost.per_tuple) * rows;
3426  }
3427 
3428  /*
3429  * Cache the retrieved rows and cost estimates for scans, joins, or
3430  * groupings without any parameterization, pathkeys, or additional
3431  * post-scan/join-processing steps, before adding the costs for
3432  * transferring data from the foreign server. These estimates are useful
3433  * for costing remote joins involving this relation or costing other
3434  * remote operations on this relation such as remote sorts and remote
3435  * LIMIT restrictions, when the costs can not be obtained from the foreign
3436  * server. This function will be called at least once for every foreign
3437  * relation without any parameterization, pathkeys, or additional
3438  * post-scan/join-processing steps.
3439  */
3440  if (pathkeys == NIL && param_join_conds == NIL && fpextra == NULL)
3441  {
3442  fpinfo->retrieved_rows = retrieved_rows;
3443  fpinfo->rel_startup_cost = startup_cost;
3444  fpinfo->rel_total_cost = total_cost;
3445  }
3446 
3447  /*
3448  * Add some additional cost factors to account for connection overhead
3449  * (fdw_startup_cost), transferring data across the network
3450  * (fdw_tuple_cost per retrieved row), and local manipulation of the data
3451  * (cpu_tuple_cost per retrieved row).
3452  */
3453  startup_cost += fpinfo->fdw_startup_cost;
3454  total_cost += fpinfo->fdw_startup_cost;
3455  total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
3456  total_cost += cpu_tuple_cost * retrieved_rows;
3457 
3458  /*
3459  * If we have LIMIT, we should prefer performing the restriction remotely
3460  * rather than locally, as the former avoids extra row fetches from the
3461  * remote that the latter might cause. But since the core code doesn't
3462  * account for such fetches when estimating the costs of the local
3463  * restriction (see create_limit_path()), there would be no difference
3464  * between the costs of the local restriction and the costs of the remote
3465  * restriction estimated above if we don't use remote estimates (except
3466  * for the case where the foreignrel is a grouping relation, the given
3467  * pathkeys is not NIL, and the effects of a bounded sort for that rel is
3468  * accounted for in costing the remote restriction). Tweak the costs of
3469  * the remote restriction to ensure we'll prefer it if LIMIT is a useful
3470  * one.
3471  */
3472  if (!fpinfo->use_remote_estimate &&
3473  fpextra && fpextra->has_limit &&
3474  fpextra->limit_tuples > 0 &&
3475  fpextra->limit_tuples < fpinfo->rows)
3476  {
3477  Assert(fpinfo->rows > 0);
3478  total_cost -= (total_cost - startup_cost) * 0.05 *
3479  (fpinfo->rows - fpextra->limit_tuples) / fpinfo->rows;
3480  }
3481 
3482  /* Return results. */
3483  *p_rows = rows;
3484  *p_width = width;
3485  *p_startup_cost = startup_cost;
3486  *p_total_cost = total_cost;
3487 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:161
RelOptKind reloptkind
Definition: pathnodes.h:673
PathTarget * target
Definition: postgres_fdw.c:292
QualCost finalCost
Definition: pathnodes.h:59
double tuples
Definition: pathnodes.h:716
bool hasAggs
Definition: parsenodes.h:133
#define Min(x, y)
Definition: c.h:986
void classifyConditions(PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds, List **local_conds)
Definition: deparse.c:205
#define IS_JOIN_REL(rel)
Definition: pathnodes.h:654
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
#define MemSet(start, val, len)
Definition: c.h:1008
double Selectivity
Definition: nodes.h:672
void get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
Definition: prepagg.c:542
QualCost transCost
Definition: pathnodes.h:58
static void adjust_foreign_grouping_path_cost(PlannerInfo *root, List *pathkeys, double retrieved_rows, double width, double limit_tuples, Cost *p_startup_cost, Cost *p_run_cost)
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
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:3762
void ReleaseConnection(PGconn *conn)
Definition: connection.c:637
Cost per_tuple
Definition: pathnodes.h:46
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
Definition: connection.c:125
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:4311
PGconn * conn
Definition: streamutil.c:54
Selectivity local_conds_sel
Definition: postgres_fdw.h:57
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
Selectivity joinclause_sel
Definition: postgres_fdw.h:60
double cpu_operator_cost
Definition: costsize.c:123
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset, EstimationInfo *estinfo)
Definition: selfuncs.c:3368
Index relid
Definition: pathnodes.h:704
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
UserMapping * user
Definition: postgres_fdw.h:87
double rows
Definition: pathnodes.h:679
void * fdw_private
Definition: pathnodes.h:732
BlockNumber pages
Definition: pathnodes.h:715
#define Assert(condition)
Definition: c.h:804
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:61
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:1001
QualCost cost
Definition: pathnodes.h:1104
static int list_length(const List *l)
Definition: pg_list.h:149
List * get_sortgrouplist_exprs(List *sgClauses, List *targetList)
Definition: tlist.c:381
double cpu_tuple_cost
Definition: costsize.c:121
#define IS_UPPER_REL(rel)
Definition: pathnodes.h:659
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:103
List * build_tlist_to_deparse(RelOptInfo *foreignrel)
Definition: deparse.c:944
List * groupClause
Definition: parsenodes.h:158
UpperRelationKind stage
Definition: postgres_fdw.h:109
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:102
Node * havingQual
Definition: parsenodes.h:163
double clamp_row_est(double nrows)
Definition: costsize.c:199
double seq_page_cost
Definition: costsize.c:119
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:687
QualCost baserestrictcost
Definition: pathnodes.h:741
double Cost
Definition: nodes.h:673
QualCost local_conds_cost
Definition: postgres_fdw.h:56

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

Definition at line 4437 of file postgres_fdw.c.

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

Referenced by postgresIterateDirectModify().

4438 {
4440  ExprContext *econtext = node->ss.ps.ps_ExprContext;
4441  int numParams = dmstate->numParams;
4442  const char **values = dmstate->param_values;
4443 
4444  /* First, process a pending asynchronous request, if any. */
4445  if (dmstate->conn_state->pendingAreq)
4447 
4448  /*
4449  * Construct array of query parameter values in text format.
4450  */
4451  if (numParams > 0)
4452  process_query_params(econtext,
4453  dmstate->param_flinfo,
4454  dmstate->param_exprs,
4455  values);
4456 
4457  /*
4458  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4459  * to infer types for all parameters. Since we explicitly cast every
4460  * parameter (see deparse.c), the "inference" is trivial and will produce
4461  * the desired result. This allows us to avoid assuming that the remote
4462  * server has the same OIDs we do for the parameters' types.
4463  */
4464  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4465  NULL, values, NULL, NULL, 0))
4466  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4467 
4468  /*
4469  * Get the result, and check for success.
4470  *
4471  * We don't use a PG_TRY block here, so be careful not to throw error
4472  * without releasing the PGresult.
4473  */
4474  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
4475  if (PQresultStatus(dmstate->result) !=
4477  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4478  dmstate->query);
4479 
4480  /* Get the number of rows affected. */
4481  if (dmstate->has_returning)
4482  dmstate->num_tuples = PQntuples(dmstate->result);
4483  else
4484  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4485 }
ScanState ss
Definition: execnodes.h:1853
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:1387
const char ** param_values
Definition: postgres_fdw.c:234
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3508
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
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:3167
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3097
PlanState ps
Definition: execnodes.h:1373
#define ERROR
Definition: elog.h:46
void process_pending_request(AsyncRequest *areq)
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:778
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:134
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:713
static Datum values[MAXATTR]
Definition: bootstrap.c:166
PgFdwConnState * conn_state
Definition: postgres_fdw.c:230

◆ execute_foreign_modify()

static TupleTableSlot ** execute_foreign_modify ( EState estate,
ResultRelInfo resultRelInfo,
CmdType  operation,
TupleTableSlot **  slots,
TupleTableSlot **  planSlots,
int *  numSlots 
)
static

Definition at line 3982 of file postgres_fdw.c.

References Assert, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, PgFdwModifyState::conn_state, convert_prep_stmt_params(), PgFdwModifyState::ctidAttno, DatumGetPointer, deallocate_query(), elog, ERROR, ExecGetJunkAttribute(), PgFdwModifyState::has_returning, initStringInfo(), MemoryContextReset(), PgFdwModifyState::num_slots, PgFdwModifyState::orig_query, PgFdwModifyState::p_name, PgFdwModifyState::p_nums, PgFdwConnState::pendingAreq, pfree(), pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQcmdTuples(), PQntuples(), PQresultStatus(), PQsendQueryPrepared(), prepare_foreign_modify(), process_pending_request(), PgFdwModifyState::query, rebuildInsertSql(), ResultRelInfo::ri_FdwState, store_returning_result(), PgFdwModifyState::temp_cxt, and PgFdwModifyState::values_end.

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

3988 {
3989  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
3990  ItemPointer ctid = NULL;
3991  const char **p_values;
3992  PGresult *res;
3993  int n_rows;
3994  StringInfoData sql;
3995 
3996  /* The operation should be INSERT, UPDATE, or DELETE */
3997  Assert(operation == CMD_INSERT ||
3998  operation == CMD_UPDATE ||
3999  operation == CMD_DELETE);
4000 
4001  /* First, process a pending asynchronous request, if any. */
4002  if (fmstate->conn_state->pendingAreq)
4004 
4005  /*
4006  * If the existing query was deparsed and prepared for a different number
4007  * of rows, rebuild it for the proper number.
4008  */
4009  if (operation == CMD_INSERT && fmstate->num_slots != *numSlots)
4010  {
4011  /* Destroy the prepared statement created previously */
4012  if (fmstate->p_name)
4013  deallocate_query(fmstate);
4014 
4015  /* Build INSERT string with numSlots records in its VALUES clause. */
4016  initStringInfo(&sql);
4017  rebuildInsertSql(&sql, fmstate->orig_query, fmstate->values_end,
4018  fmstate->p_nums, *numSlots - 1);
4019  pfree(fmstate->query);
4020  fmstate->query = sql.data;
4021  fmstate->num_slots = *numSlots;
4022  }
4023 
4024  /* Set up the prepared statement on the remote server, if we didn't yet */
4025  if (!fmstate->p_name)
4026  prepare_foreign_modify(fmstate);
4027 
4028  /*
4029  * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
4030  */
4031  if (operation == CMD_UPDATE || operation == CMD_DELETE)
4032  {
4033  Datum datum;
4034  bool isNull;
4035 
4036  datum = ExecGetJunkAttribute(planSlots[0],
4037  fmstate->ctidAttno,
4038  &isNull);
4039  /* shouldn't ever get a null result... */
4040  if (isNull)
4041  elog(ERROR, "ctid is NULL");
4042  ctid = (ItemPointer) DatumGetPointer(datum);
4043  }
4044 
4045  /* Convert parameters needed by prepared statement to text form */
4046  p_values = convert_prep_stmt_params(fmstate, ctid, slots, *numSlots);
4047 
4048  /*
4049  * Execute the prepared statement.
4050  */
4051  if (!PQsendQueryPrepared(fmstate->conn,
4052  fmstate->p_name,
4053  fmstate->p_nums * (*numSlots),
4054  p_values,
4055  NULL,
4056  NULL,
4057  0))
4058  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
4059 
4060  /*
4061  * Get the result, and check for success.
4062  *
4063  * We don't use a PG_TRY block here, so be careful not to throw error
4064  * without releasing the PGresult.
4065  */
4066  res = pgfdw_get_result(fmstate->conn, fmstate->query);
4067  if (PQresultStatus(res) !=
4069  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
4070 
4071  /* Check number of rows affected, and fetch RETURNING tuple if any */
4072  if (fmstate->has_returning)
4073  {
4074  Assert(*numSlots == 1);
4075  n_rows = PQntuples(res);
4076  if (n_rows > 0)
4077  store_returning_result(fmstate, slots[0], res);
4078  }
4079  else
4080  n_rows = atoi(PQcmdTuples(res));
4081 
4082  /* And clean up */
4083  PQclear(res);
4084 
4085  MemoryContextReset(fmstate->temp_cxt);
4086 
4087  *numSlots = n_rows;
4088 
4089  /*
4090  * Return NULL if nothing was inserted/updated/deleted on the remote end
4091  */
4092  return (n_rows > 0) ? slots : NULL;
4093 }
static void deallocate_query(PgFdwModifyState *fmstate)
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3508
static void store_returning_result(PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3167
void rebuildInsertSql(StringInfo buf, char *orig_query, int values_end_len, int num_cols, int num_rows)
Definition: deparse.c:1778
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3097
ItemPointerData * ItemPointer
Definition: itemptr.h:49
PgFdwConnState * conn_state
Definition: postgres_fdw.c:186
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
void process_pending_request(AsyncRequest *areq)
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:778
AttrNumber ctidAttno
Definition: postgres_fdw.c:199
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: executor.h:178
uintptr_t Datum
Definition: postgres.h:411
static const char ** convert_prep_stmt_params(PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot **slots, int numSlots)
void * ri_FdwState
Definition: execnodes.h:458
void PQclear(PGresult *res)
Definition: fe-exec.c:680
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:134
MemoryContext temp_cxt
Definition: postgres_fdw.c:207
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:713
#define Assert(condition)
Definition: c.h:804
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:1533
#define DatumGetPointer(X)
Definition: postgres.h:593
static void prepare_foreign_modify(PgFdwModifyState *fmstate)
#define elog(elevel,...)
Definition: elog.h:232

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3700 of file postgres_fdw.c.

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

Referenced by postgresForeignAsyncNotify(), postgresIterateForeignScan(), and process_pending_request().

3701 {
3702  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3703  PGresult *volatile res = NULL;
3704  MemoryContext oldcontext;
3705 
3706  /*
3707  * We'll store the tuples in the batch_cxt. First, flush the previous
3708  * batch.
3709  */
3710  fsstate->tuples = NULL;
3711  MemoryContextReset(fsstate->batch_cxt);
3712  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3713 
3714  /* PGresult must be released before leaving this function. */
3715  PG_TRY();
3716  {
3717  PGconn *conn = fsstate->conn;
3718  int numrows;
3719  int i;
3720 
3721  if (fsstate->async_capable)
3722  {
3723  Assert(fsstate->conn_state->pendingAreq);
3724 
3725  /*
3726  * The query was already sent by an earlier call to
3727  * fetch_more_data_begin. So now we just fetch the result.
3728  */
3729  res = pgfdw_get_result(conn, fsstate->query);
3730  /* On error, report the original query, not the FETCH. */
3731  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3732  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3733 
3734  /* Reset per-connection state */
3735  fsstate->conn_state->pendingAreq = NULL;
3736  }
3737  else
3738  {
3739  char sql[64];
3740 
3741  /* This is a regular synchronous fetch. */
3742  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3743  fsstate->fetch_size, fsstate->cursor_number);
3744 
3745  res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
3746  /* On error, report the original query, not the FETCH. */
3747  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3748  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3749  }
3750 
3751  /* Convert the data into HeapTuples */
3752  numrows = PQntuples(res);
3753  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3754  fsstate->num_tuples = numrows;
3755  fsstate->next_tuple = 0;
3756 
3757  for (i = 0; i < numrows; i++)
3758  {
3759  Assert(IsA(node->ss.ps.plan, ForeignScan));
3760 
3761  fsstate->tuples[i] =
3763  fsstate->rel,
3764  fsstate->attinmeta,
3765  fsstate->retrieved_attrs,
3766  node,
3767  fsstate->temp_cxt);
3768  }
3769 
3770  /* Update fetch_ct_2 */
3771  if (fsstate->fetch_ct_2 < 2)
3772  fsstate->fetch_ct_2++;
3773 
3774  /* Must be EOF if we didn't get as many tuples as we asked for. */
3775  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3776  }
3777  PG_FINALLY();
3778  {
3779  if (res)
3780  PQclear(res);
3781  }
3782  PG_END_TRY();
3783 
3784  MemoryContextSwitchTo(oldcontext);
3785 }
ScanState ss
Definition: execnodes.h:1853
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
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
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:685
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
List * retrieved_attrs
Definition: postgres_fdw.c:145
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3167
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3097
unsigned int cursor_number
Definition: postgres_fdw.c:150
PlanState ps
Definition: execnodes.h:1373
#define ERROR
Definition: elog.h:46
PGconn * conn
Definition: streamutil.c:54
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:778
AttInMetadata * attinmeta
Definition: postgres_fdw.c:141
#define PG_FINALLY()
Definition: elog.h:330
void * palloc0(Size size)
Definition: mcxt.c:1093
MemoryContext temp_cxt
Definition: postgres_fdw.c:171
Plan * plan
Definition: execnodes.h:964
void PQclear(PGresult *res)
Definition: fe-exec.c:680
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:134
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:713
#define Assert(condition)
Definition: c.h:804
PgFdwConnState * conn_state
Definition: postgres_fdw.c:149
HeapTuple * tuples
Definition: postgres_fdw.c:158
int i
#define PG_TRY()
Definition: elog.h:313
MemoryContext batch_cxt
Definition: postgres_fdw.c:170
#define snprintf
Definition: port.h:216
#define PG_END_TRY()
Definition: elog.h:338

◆ fetch_more_data_begin()

static void fetch_more_data_begin ( AsyncRequest areq)
static

Definition at line 6905 of file postgres_fdw.c.

References Assert, create_cursor(), ERROR, ForeignScanState::fdw_state, pgfdw_report_error(), PQsendQuery(), AsyncRequest::requestee, and snprintf.

Referenced by postgresForeignAsyncConfigureWait(), and produce_tuple_asynchronously().

6906 {
6907  ForeignScanState *node = (ForeignScanState *) areq->requestee;
6908  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
6909  char sql[64];
6910 
6911  Assert(!fsstate->conn_state->pendingAreq);
6912 
6913  /* Create the cursor synchronously. */
6914  if (!fsstate->cursor_exists)
6915  create_cursor(node);
6916 
6917  /* We will send this query, but not wait for the response. */
6918  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
6919  fsstate->fetch_size, fsstate->cursor_number);
6920 
6921  if (PQsendQuery(fsstate->conn, sql) < 0)
6922  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
6923 
6924  /* Remember that the request is in process */
6925  fsstate->conn_state->pendingAreq = areq;
6926 }
static void create_cursor(ForeignScanState *node)
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1279
#define ERROR
Definition: elog.h:46
struct PlanState * requestee
Definition: execnodes.h:537
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:778
#define Assert(condition)
Definition: c.h:804
#define snprintf
Definition: port.h:216

◆ find_em_expr_for_input_target()

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

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

7191 {
7192  ListCell *lc1;
7193  int i;
7194 
7195  i = 0;
7196  foreach(lc1, target->exprs)
7197  {
7198  Expr *expr = (Expr *) lfirst(lc1);
7199  Index sgref = get_pathtarget_sortgroupref(target, i);
7200  ListCell *lc2;
7201 
7202  /* Ignore non-sort expressions */
7203  if (sgref == 0 ||
7205  root->parse->sortClause) == NULL)
7206  {
7207  i++;
7208  continue;
7209  }
7210 
7211  /* We ignore binary-compatible relabeling on both ends */
7212  while (expr && IsA(expr, RelabelType))
7213  expr = ((RelabelType *) expr)->arg;
7214 
7215  /* Locate an EquivalenceClass member matching this expr, if any */
7216  foreach(lc2, ec->ec_members)
7217  {
7219  Expr *em_expr;
7220 
7221  /* Don't match constants */
7222  if (em->em_is_const)
7223  continue;
7224 
7225  /* Ignore child members */
7226  if (em->em_is_child)
7227  continue;
7228 
7229  /* Match if same expression (after stripping relabel) */
7230  em_expr = em->em_expr;
7231  while (em_expr && IsA(em_expr, RelabelType))
7232  em_expr = ((RelabelType *) em_expr)->arg;
7233 
7234  if (equal(em_expr, expr))
7235  return em->em_expr;
7236  }
7237 
7238  i++;
7239  }
7240 
7241  elog(ERROR, "could not find pathkey item to sort");
7242  return NULL; /* keep compiler quiet */
7243 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
Query * parse
Definition: pathnodes.h:161
List * sortClause
Definition: parsenodes.h:169
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3105
#define ERROR
Definition: elog.h:46
List * exprs
Definition: pathnodes.h:1102
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:432
unsigned int Index
Definition: c.h:549
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1111
#define lfirst(lc)
Definition: pg_list.h:169
#define elog(elevel,...)
Definition: elog.h:232
int i
void * arg
List * ec_members
Definition: pathnodes.h:981

◆ find_modifytable_subplan()

static ForeignScan* find_modifytable_subplan ( PlannerInfo root,
ModifyTable plan,
Index  rtindex,
int  subplan_index 
)
static

Definition at line 2294 of file postgres_fdw.c.

References Append::appendplans, bms_is_member(), ForeignScan::fs_relids, IsA, list_length(), list_nth(), and outerPlan.

Referenced by postgresPlanDirectModify().

2298 {
2299  Plan *subplan = outerPlan(plan);
2300 
2301  /*
2302  * The cases we support are (1) the desired ForeignScan is the immediate
2303  * child of ModifyTable, or (2) it is the subplan_index'th child of an
2304  * Append node that is the immediate child of ModifyTable. There is no
2305  * point in looking further down, as that would mean that local joins are
2306  * involved, so we can't do the update directly.
2307  *
2308  * There could be a Result atop the Append too, acting to compute the
2309  * UPDATE targetlist values. We ignore that here; the tlist will be
2310  * checked by our caller.
2311  *
2312  * In principle we could examine all the children of the Append, but it's
2313  * currently unlikely that the core planner would generate such a plan
2314  * with the children out-of-order. Moreover, such a search risks costing
2315  * O(N^2) time when there are a lot of children.
2316  */
2317  if (IsA(subplan, Append))
2318  {
2319  Append *appendplan = (Append *) subplan;
2320 
2321  if (subplan_index < list_length(appendplan->appendplans))
2322  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2323  }
2324  else if (IsA(subplan, Result) && IsA(outerPlan(subplan), Append))
2325  {
2326  Append *appendplan = (Append *) outerPlan(subplan);
2327 
2328  if (subplan_index < list_length(appendplan->appendplans))
2329  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2330  }
2331 
2332  /* Now, have we got a ForeignScan on the desired rel? */
2333  if (IsA(subplan, ForeignScan))
2334  {
2335  ForeignScan *fscan = (ForeignScan *) subplan;
2336 
2337  if (bms_is_member(rtindex, fscan->fs_relids))
2338  return fscan;
2339  }
2340 
2341  return NULL;
2342 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
List * appendplans
Definition: plannodes.h:252
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
#define outerPlan(node)
Definition: plannodes.h:171
static int list_length(const List *l)
Definition: pg_list.h:149
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
Bitmapset * fs_relids
Definition: plannodes.h:640

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4263 of file postgres_fdw.c.

References Assert, PgFdwModifyState::conn, deallocate_query(), and ReleaseConnection().

Referenced by postgresEndForeignInsert(), and postgresEndForeignModify().

4264 {
4265  Assert(fmstate != NULL);
4266 
4267  /* If we created a prepared statement, destroy it */
4268  deallocate_query(fmstate);
4269 
4270  /* Release remote connection */
4271  ReleaseConnection(fmstate->conn);
4272  fmstate->conn = NULL;
4273 }
static void deallocate_query(PgFdwModifyState *fmstate)
void ReleaseConnection(PGconn *conn)
Definition: connection.c:637
#define Assert(condition)
Definition: c.h:804

◆ foreign_grouping_ok()

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

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

5994 {
5995  Query *query = root->parse;
5996  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
5997  PathTarget *grouping_target = grouped_rel->reltarget;
5998  PgFdwRelationInfo *ofpinfo;
5999  ListCell *lc;
6000  int i;
6001  List *tlist = NIL;
6002 
6003  /* We currently don't support pushing Grouping Sets. */
6004  if (query->groupingSets)
6005  return false;
6006 
6007  /* Get the fpinfo of the underlying scan relation. */
6008  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
6009 
6010  /*
6011  * If underlying scan relation has any local conditions, those conditions
6012  * are required to be applied before performing aggregation. Hence the
6013  * aggregate cannot be pushed down.
6014  */
6015  if (ofpinfo->local_conds)
6016  return false;
6017 
6018  /*
6019  * Examine grouping expressions, as well as other expressions we'd need to
6020  * compute, and check whether they are safe to push down to the foreign
6021  * server. All GROUP BY expressions will be part of the grouping target
6022  * and thus there is no need to search for them separately. Add grouping
6023  * expressions into target list which will be passed to foreign server.
6024  *
6025  * A tricky fine point is that we must not put any expression into the
6026  * target list that is just a foreign param (that is, something that
6027  * deparse.c would conclude has to be sent to the foreign server). If we
6028  * do, the expression will also appear in the fdw_exprs list of the plan
6029  * node, and setrefs.c will get confused and decide that the fdw_exprs
6030  * entry is actually a reference to the fdw_scan_tlist entry, resulting in
6031  * a broken plan. Somewhat oddly, it's OK if the expression contains such
6032  * a node, as long as it's not at top level; then no match is possible.
6033  */
6034  i = 0;
6035  foreach(lc, grouping_target->exprs)
6036  {
6037  Expr *expr = (Expr *) lfirst(lc);
6038  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
6039  ListCell *l;
6040 
6041  /* Check whether this expression is part of GROUP BY clause */
6042  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
6043  {
6044  TargetEntry *tle;
6045 
6046  /*
6047  * If any GROUP BY expression is not shippable, then we cannot
6048  * push down aggregation to the foreign server.
6049  */
6050  if (!is_foreign_expr(root, grouped_rel, expr))
6051  return false;
6052 
6053  /*
6054  * If it would be a foreign param, we can't put it into the tlist,
6055  * so we have to fail.
6056  */
6057  if (is_foreign_param(root, grouped_rel, expr))
6058  return false;
6059 
6060  /*
6061  * Pushable, so add to tlist. We need to create a TLE for this
6062  * expression and apply the sortgroupref to it. We cannot use
6063  * add_to_flat_tlist() here because that avoids making duplicate
6064  * entries in the tlist. If there are duplicate entries with
6065  * distinct sortgrouprefs, we have to duplicate that situation in
6066  * the output tlist.
6067  */
6068  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
6069  tle->ressortgroupref = sgref;
6070  tlist = lappend(tlist, tle);
6071  }
6072  else
6073  {
6074  /*
6075  * Non-grouping expression we need to compute. Can we ship it
6076  * as-is to the foreign server?
6077  */
6078  if (is_foreign_expr(root, grouped_rel, expr) &&
6079  !is_foreign_param(root, grouped_rel, expr))
6080  {
6081  /* Yes, so add to tlist as-is; OK to suppress duplicates */
6082  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6083  }
6084  else
6085  {
6086  /* Not pushable as a whole; extract its Vars and aggregates */
6087  List *aggvars;
6088 
6089  aggvars = pull_var_clause((Node *) expr,
6091 
6092  /*
6093  * If any aggregate expression is not shippable, then we
6094  * cannot push down aggregation to the foreign server. (We
6095  * don't have to check is_foreign_param, since that certainly
6096  * won't return true for any such expression.)
6097  */
6098  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
6099  return false;
6100 
6101  /*
6102  * Add aggregates, if any, into the targetlist. Plain Vars
6103  * outside an aggregate can be ignored, because they should be
6104  * either same as some GROUP BY column or part of some GROUP
6105  * BY expression. In either case, they are already part of
6106  * the targetlist and thus no need to add them again. In fact
6107  * including plain Vars in the tlist when they do not match a
6108  * GROUP BY column would cause the foreign server to complain
6109  * that the shipped query is invalid.
6110  */
6111  foreach(l, aggvars)
6112  {
6113  Expr *expr = (Expr *) lfirst(l);
6114 
6115  if (IsA(expr, Aggref))
6116  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6117  }
6118  }
6119  }
6120 
6121  i++;
6122  }
6123 
6124  /*
6125  * Classify the pushable and non-pushable HAVING clauses and save them in
6126  * remote_conds and local_conds of the grouped rel's fpinfo.
6127  */
6128  if (havingQual)
6129  {
6130  ListCell *lc;
6131 
6132  foreach(lc, (List *) havingQual)
6133  {
6134  Expr *expr = (Expr *) lfirst(lc);
6135  RestrictInfo *rinfo;
6136 
6137  /*
6138  * Currently, the core code doesn't wrap havingQuals in
6139  * RestrictInfos, so we must make our own.
6140  */
6141  Assert(!IsA(expr, RestrictInfo));
6142  rinfo = make_restrictinfo(root,
6143  expr,
6144  true,
6145  false,
6146  false,
6147  root->qual_security_level,
6148  grouped_rel->relids,
6149  NULL,
6150  NULL);
6151  if (is_foreign_expr(root, grouped_rel, expr))
6152  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
6153  else
6154  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
6155  }
6156  }
6157 
6158  /*
6159  * If there are any local conditions, pull Vars and aggregates from it and
6160  * check whether they are safe to pushdown or not.
6161  */
6162  if (fpinfo->local_conds)
6163  {
6164  List *aggvars = NIL;
6165  ListCell *lc;
6166 
6167  foreach(lc, fpinfo->local_conds)
6168  {
6169  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6170 
6171  aggvars = list_concat(aggvars,
6172  pull_var_clause((Node *) rinfo->clause,
6174  }
6175 
6176  foreach(lc, aggvars)
6177  {
6178  Expr *expr = (Expr *) lfirst(lc);
6179 
6180  /*
6181  * If aggregates within local conditions are not safe to push
6182  * down, then we cannot push down the query. Vars are already
6183  * part of GROUP BY clause which are checked above, so no need to
6184  * access them again here. Again, we need not check
6185  * is_foreign_param for a foreign aggregate.
6186  */
6187  if (IsA(expr, Aggref))
6188  {
6189  if (!is_foreign_expr(root, grouped_rel, expr))
6190  return false;
6191 
6192  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6193  }
6194  }
6195  }
6196 
6197  /* Store generated targetlist */
6198  fpinfo->grouped_tlist = tlist;
6199 
6200  /* Safe to pushdown */
6201  fpinfo->pushdown_safe = true;
6202 
6203  /*
6204  * Set # of retrieved rows and cached relation costs to some negative
6205  * value, so that we can detect when they are set to some sensible values,
6206  * during one (usually the first) of the calls to estimate_path_cost_size.
6207  */
6208  fpinfo->retrieved_rows = -1;
6209  fpinfo->rel_startup_cost = -1;
6210  fpinfo->rel_total_cost = -1;
6211 
6212  /*
6213  * Set the string describing this grouped relation to be used in EXPLAIN
6214  * output of corresponding ForeignScan. Note that the decoration we add
6215  * to the base relation name mustn't include any digits, or it'll confuse
6216  * postgresExplainForeignScan.
6217  */
6218  fpinfo->relation_name = psprintf("Aggregate on (%s)",
6219  ofpinfo->relation_name);
6220 
6221  return true;
6222 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
Query * parse
Definition: pathnodes.h:161
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
List * groupingSets
Definition: parsenodes.h:161
Definition: nodes.h:539
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
List * pull_var_clause(Node *node, int flags)
Definition: var.c:562
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
#define PVC_INCLUDE_AGGREGATES
Definition: optimizer.h:185
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:877
#define list_make1(x1)
Definition: pg_list.h:206
#define lfirst_node(type, lc)
Definition: pg_list.h:172
Relids relids
Definition: pathnodes.h:676
RestrictInfo * make_restrictinfo(PlannerInfo *root, 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:61
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:238
List * lappend(List *list, void *datum)
Definition: list.c:336
Expr * clause
Definition: pathnodes.h:2045
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:432
unsigned int Index
Definition: c.h:549
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1111
void * fdw_private
Definition: pathnodes.h:732
#define Assert(condition)
Definition: c.h:804
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition: tlist.c:121
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
Index qual_security_level
Definition: pathnodes.h:342
List * groupClause
Definition: parsenodes.h:158
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:231
int i
Index ressortgroupref
Definition: primnodes.h:1447
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:687

◆ foreign_join_ok()

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

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

5409 {
5410  PgFdwRelationInfo *fpinfo;
5411  PgFdwRelationInfo *fpinfo_o;
5412  PgFdwRelationInfo *fpinfo_i;
5413  ListCell *lc;
5414  List *joinclauses;
5415 
5416  /*
5417  * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
5418  * Constructing queries representing SEMI and ANTI joins is hard, hence
5419  * not considered right now.
5420  */
5421  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
5422  jointype != JOIN_RIGHT && jointype != JOIN_FULL)
5423  return false;
5424 
5425  /*
5426  * If either of the joining relations is marked as unsafe to pushdown, the
5427  * join can not be pushed down.
5428  */
5429  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
5430  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
5431  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
5432  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
5433  !fpinfo_i || !fpinfo_i->pushdown_safe)
5434  return false;
5435 
5436  /*
5437  * If joining relations have local conditions, those conditions are
5438  * required to be applied before joining the relations. Hence the join can
5439  * not be pushed down.
5440  */
5441  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
5442  return false;
5443 
5444  /*
5445  * Merge FDW options. We might be tempted to do this after we have deemed
5446  * the foreign join to be OK. But we must do this beforehand so that we
5447  * know which quals can be evaluated on the foreign server, which might
5448  * depend on shippable_extensions.
5449  */
5450  fpinfo->server = fpinfo_o->server;
5451  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
5452 
5453  /*
5454  * Separate restrict list into join quals and pushed-down (other) quals.
5455  *
5456  * Join quals belonging to an outer join must all be shippable, else we
5457  * cannot execute the join remotely. Add such quals to 'joinclauses'.
5458  *
5459  * Add other quals to fpinfo->remote_conds if they are shippable, else to
5460  * fpinfo->local_conds. In an inner join it's okay to execute conditions
5461  * either locally or remotely; the same is true for pushed-down conditions
5462  * at an outer join.
5463  *
5464  * Note we might return failure after having already scribbled on
5465  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
5466  * won't consult those lists again if we deem the join unshippable.
5467  */
5468  joinclauses = NIL;
5469  foreach(lc, extra->restrictlist)
5470  {
5471  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5472  bool is_remote_clause = is_foreign_expr(root, joinrel,
5473  rinfo->clause);
5474 
5475  if (IS_OUTER_JOIN(jointype) &&
5476  !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
5477  {
5478  if (!is_remote_clause)
5479  return false;
5480  joinclauses = lappend(joinclauses, rinfo);
5481  }
5482  else
5483  {
5484  if (is_remote_clause)
5485  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5486  else
5487  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5488  }
5489  }
5490 
5491  /*
5492  * deparseExplicitTargetList() isn't smart enough to handle anything other
5493  * than a Var. In particular, if there's some PlaceHolderVar that would
5494  * need to be evaluated within this join tree (because there's an upper
5495  * reference to a quantity that may go to NULL as a result of an outer
5496  * join), then we can't try to push the join down because we'll fail when
5497  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
5498  * needs to be evaluated *at the top* of this join tree is OK, because we
5499  * can do that locally after fetching the results from the remote side.
5500  */
5501  foreach(lc, root->placeholder_list)
5502  {
5503  PlaceHolderInfo *phinfo = lfirst(lc);
5504  Relids relids;
5505 
5506  /* PlaceHolderInfo refers to parent relids, not child relids. */
5507  relids = IS_OTHER_REL(joinrel) ?
5508  joinrel->top_parent_relids : joinrel->relids;
5509 
5510  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
5511  bms_nonempty_difference(relids, phinfo->ph_eval_at))
5512  return false;
5513  }
5514 
5515  /* Save the join clauses, for later use. */
5516  fpinfo->joinclauses = joinclauses;
5517 
5518  fpinfo->outerrel = outerrel;
5519  fpinfo->innerrel = innerrel;
5520  fpinfo->jointype = jointype;
5521 
5522  /*
5523  * By default, both the input relations are not required to be deparsed as
5524  * subqueries, but there might be some relations covered by the input
5525  * relations that are required to be deparsed as subqueries, so save the
5526  * relids of those relations for later use by the deparser.
5527  */
5528  fpinfo->make_outerrel_subquery = false;
5529  fpinfo->make_innerrel_subquery = false;
5530  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
5531  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
5533  fpinfo_i->lower_subquery_rels);
5534 
5535  /*
5536  * Pull the other remote conditions from the joining relations into join
5537  * clauses or other remote clauses (remote_conds) of this relation
5538  * wherever possible. This avoids building subqueries at every join step.
5539  *
5540  * For an inner join, clauses from both the relations are added to the
5541  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
5542  * the outer side are added to remote_conds since those can be evaluated
5543  * after the join is evaluated. The clauses from inner side are added to
5544  * the joinclauses, since they need to be evaluated while constructing the
5545  * join.
5546  *
5547  * For a FULL OUTER JOIN, the other clauses from either relation can not
5548  * be added to the joinclauses or remote_conds, since each relation acts
5549  * as an outer relation for the other.
5550  *
5551  * The joining sides can not have local conditions, thus no need to test
5552  * shippability of the clauses being pulled up.
5553  */
5554  switch (jointype)
5555  {
5556  case JOIN_INNER:
5557  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5558  fpinfo_i->remote_conds);
5559  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5560  fpinfo_o->remote_conds);
5561  break;
5562 
5563  case JOIN_LEFT:
5564  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5565  fpinfo_i->remote_conds);
5566  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5567  fpinfo_o->remote_conds);
5568  break;
5569 
5570  case JOIN_RIGHT:
5571  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5572  fpinfo_o->remote_conds);
5573  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5574  fpinfo_i->remote_conds);
5575  break;
5576 
5577  case JOIN_FULL:
5578 
5579  /*
5580  * In this case, if any of the input relations has conditions, we
5581  * need to deparse that relation as a subquery so that the
5582  * conditions can be evaluated before the join. Remember it in
5583  * the fpinfo of this relation so that the deparser can take
5584  * appropriate action. Also, save the relids of base relations
5585  * covered by that relation for later use by the deparser.
5586  */
5587  if (fpinfo_o->remote_conds)
5588  {
5589  fpinfo->make_outerrel_subquery = true;
5590  fpinfo->lower_subquery_rels =
5592  outerrel->relids);
5593  }
5594  if (fpinfo_i->remote_conds)
5595  {
5596  fpinfo->make_innerrel_subquery = true;
5597  fpinfo->lower_subquery_rels =
5599  innerrel->relids);
5600  }
5601  break;
5602 
5603  default:
5604  /* Should not happen, we have just checked this above */
5605  elog(ERROR, "unsupported join type %d", jointype);
5606  }
5607 
5608  /*
5609  * For an inner join, all restrictions can be treated alike. Treating the
5610  * pushed down conditions as join conditions allows a top level full outer
5611  * join to be deparsed without requiring subqueries.
5612  */
5613  if (jointype == JOIN_INNER)
5614  {
5615  Assert(!fpinfo->joinclauses);
5616  fpinfo->joinclauses = fpinfo->remote_conds;
5617  fpinfo->remote_conds = NIL;
5618  }
5619 
5620  /* Mark that this join can be pushed down safely */
5621  fpinfo->pushdown_safe = true;
5622 
5623  /* Get user mapping */
5624  if (fpinfo->use_remote_estimate)
5625  {
5626  if (fpinfo_o->use_remote_estimate)
5627  fpinfo->user = fpinfo_o->user;
5628  else
5629  fpinfo->user = fpinfo_i->user;
5630  }
5631  else
5632  fpinfo->user = NULL;
5633 
5634  /*
5635  * Set # of retrieved rows and cached relation costs to some negative
5636  * value, so that we can detect when they are set to some sensible values,
5637  * during one (usually the first) of the calls to estimate_path_cost_size.
5638  */
5639  fpinfo->retrieved_rows = -1;
5640  fpinfo->rel_startup_cost = -1;
5641  fpinfo->rel_total_cost = -1;
5642 
5643  /*
5644  * Set the string describing this join relation to be used in EXPLAIN
5645  * output of corresponding ForeignScan. Note that the decoration we add
5646  * to the base relation names mustn't include any digits, or it'll confuse
5647  * postgresExplainForeignScan.
5648  */
5649  fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
5650  fpinfo_o->relation_name,
5651  get_jointype_name(fpinfo->jointype),
5652  fpinfo_i->relation_name);
5653 
5654  /*
5655  * Set the relation index. This is defined as the position of this
5656  * joinrel in the join_rel_list list plus the length of the rtable list.
5657  * Note that since this joinrel is at the end of the join_rel_list list
5658  * when we are called, we can get the position by list_length.
5659  */
5660  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
5661  fpinfo->relation_index =
5663 
5664  return true;
5665 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:161
Relids ph_eval_at
Definition: pathnodes.h:2402
#define IS_OTHER_REL(rel)
Definition: pathnodes.h:664
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
ForeignServer * server
Definition: postgres_fdw.h:86
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:755
Relids lower_subquery_rels
Definition: postgres_fdw.h:119
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
List * join_rel_list
Definition: pathnodes.h:228
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
List * rtable
Definition: parsenodes.h:147
#define ERROR
Definition: elog.h:46
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:315
#define lfirst_node(type, lc)
Definition: pg_list.h:172
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1375
Relids relids
Definition: pathnodes.h:676
List * lappend(List *list, void *datum)
Definition: list.c:336
Expr * clause
Definition: pathnodes.h:2045
UserMapping * user
Definition: postgres_fdw.h:87
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: pathnodes.h:2128
void * fdw_private
Definition: pathnodes.h:732
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225
static int list_length(const List *l)
Definition: pg_list.h:149
RelOptInfo * innerrel
Definition: postgres_fdw.h:103
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:231
#define elog(elevel,...)
Definition: elog.h:232
List * placeholder_list
Definition: pathnodes.h:289
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:751
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:545

◆ get_batch_size_option()

static int get_batch_size_option ( Relation  rel)
static

Definition at line 7250 of file postgres_fdw.c.

References defGetString(), DefElem::defname, GetForeignServer(), GetForeignTable(), lfirst, list_concat(), NIL, ForeignServer::options, options, ForeignTable::options, RelationGetRelid, and ForeignTable::serverid.

Referenced by create_foreign_modify(), and postgresGetForeignModifyBatchSize().

7251 {
7252  Oid foreigntableid = RelationGetRelid(rel);
7253  ForeignTable *table;
7254  ForeignServer *server;
7255  List *options;
7256  ListCell *lc;
7257 
7258  /* we use 1 by default, which means "no batching" */
7259  int batch_size = 1;
7260 
7261  /*
7262  * Load options for table and server. We append server options after
7263  * table options, because table options take precedence.
7264  */
7265  table = GetForeignTable(foreigntableid);
7266  server = GetForeignServer(table->serverid);
7267 
7268  options = NIL;
7269  options = list_concat(options, table->options);
7270  options = list_concat(options, server->options);
7271 
7272  /* See if either table or server specifies batch_size. */
7273  foreach(lc, options)
7274  {
7275  DefElem *def = (DefElem *) lfirst(lc);
7276 
7277  if (strcmp(def->defname, "batch_size") == 0)
7278  {
7279  batch_size = strtol(defGetString(def), NULL, 10);
7280  break;
7281  }
7282  }
7283 
7284  return batch_size;
7285 }
#define NIL
Definition: pg_list.h:65
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:248
unsigned int Oid
Definition: postgres_ext.h:31
char * defGetString(DefElem *def)
Definition: define.c:49
static char ** options
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:109
#define lfirst(lc)
Definition: pg_list.h:169
Oid serverid
Definition: foreign.h:56
List * options
Definition: foreign.h:57
char * defname
Definition: parsenodes.h:746
Definition: pg_list.h:50
List * options
Definition: foreign.h:42
#define RelationGetRelid(relation)
Definition: rel.h:457

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

3497 {
3498  PGresult *volatile res = NULL;
3499 
3500  /* PGresult must be released before leaving this function. */
3501  PG_TRY();
3502  {
3503  char *line;
3504  char *p;
3505  int n;
3506 
3507  /*
3508  * Execute EXPLAIN remotely.
3509  */
3510  res = pgfdw_exec_query(conn, sql, NULL);
3511  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3512  pgfdw_report_error(ERROR, res, conn, false, sql);
3513 
3514  /*
3515  * Extract cost numbers for topmost plan node. Note we search for a
3516  * left paren from the end of the line to avoid being confused by
3517  * other uses of parentheses.
3518  */
3519  line = PQgetvalue(res, 0, 0);
3520  p = strrchr(line, '(');
3521  if (p == NULL)
3522  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3523  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3524  startup_cost, total_cost, rows, width);
3525  if (n != 4)
3526  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3527  }
3528  PG_FINALLY();
3529  {
3530  if (res)
3531  PQclear(res);
3532  }
3533  PG_END_TRY();
3534 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3561
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:685
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3097
#define ERROR
Definition: elog.h:46
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:778
#define PG_FINALLY()
Definition: elog.h:330
void PQclear(PGresult *res)
Definition: fe-exec.c:680
#define elog(elevel,...)
Definition: elog.h:232
#define PG_TRY()
Definition: elog.h:313
#define PG_END_TRY()
Definition: elog.h:338

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 4491 of file postgres_fdw.c.

References apply_returning_filter(), Assert, PgFdwDirectModifyState::attinmeta, ExecClearTuple(), ExecStoreAllNullTuple(), ExecStoreHeapTuple(), ForeignScanState::fdw_state, PgFdwDirectModifyState::has_returning, make_tuple_from_result_row(), PgFdwDirectModifyState::next_tuple, PgFdwDirectModifyState::num_tuples, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PQclear(), ScanState::ps, PgFdwDirectModifyState::rel, PgFdwDirectModifyState::result, ForeignScanState::resultRelInfo, PgFdwDirectModifyState::retrieved_attrs, PgFdwDirectModifyState::set_processed, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, and PgFdwDirectModifyState::temp_cxt.

Referenced by postgresIterateDirectModify().

4492 {
4494  EState *estate = node->ss.ps.state;
4495  ResultRelInfo *resultRelInfo = node->resultRelInfo;
4496  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4497  TupleTableSlot *resultSlot;
4498 
4499  Assert(resultRelInfo->ri_projectReturning);
4500 
4501  /* If we didn't get any tuples, must be end of data. */
4502  if (dmstate->next_tuple >= dmstate->num_tuples)
4503  return ExecClearTuple(slot);
4504 
4505  /* Increment the command es_processed count if necessary. */
4506  if (dmstate->set_processed)
4507  estate->es_processed += 1;
4508 
4509  /*
4510  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4511  * tuple. (has_returning is false when the local query is of the form
4512  * "UPDATE/DELETE .. RETURNING 1" for example.)
4513  */
4514  if (!dmstate->has_returning)
4515  {
4516  ExecStoreAllNullTuple(slot);
4517  resultSlot = slot;
4518  }
4519  else
4520  {
4521  /*
4522  * On error, be sure to release the PGresult on the way out. Callers
4523  * do not have PG_TRY blocks to ensure this happens.
4524  */
4525  PG_TRY();
4526  {
4527  HeapTuple newtup;
4528 
4529  newtup = make_tuple_from_result_row(dmstate->result,
4530  dmstate->next_tuple,
4531  dmstate->rel,
4532  dmstate->attinmeta,
4533  dmstate->retrieved_attrs,
4534  node,
4535  dmstate->temp_cxt);
4536  ExecStoreHeapTuple(newtup, slot, false);
4537  }
4538  PG_CATCH();
4539  {
4540  if (dmstate->result)
4541  PQclear(dmstate->result);
4542  PG_RE_THROW();
4543  }
4544  PG_END_TRY();
4545 
4546  /* Get the updated/deleted tuple. */
4547  if (dmstate->rel)
4548  resultSlot = slot;
4549  else
4550  resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4551  }
4552  dmstate->next_tuple++;
4553 
4554  /* Make slot available for evaluation of the local query RETURNING list. */
4555  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4556  resultSlot;
4557 
4558  return slot;
4559 }
ScanState ss
Definition: execnodes.h:1853
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:1576
AttInMetadata * attinmeta
Definition: postgres_fdw.c:220
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1376
EState * state
Definition: execnodes.h:966
PlanState ps
Definition: execnodes.h:1373
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1856
MemoryContext temp_cxt
Definition: postgres_fdw.c:247
void PQclear(PGresult *res)
Definition: fe-exec.c:680
#define PG_CATCH()
Definition: elog.h:323
#define Assert(condition)
Definition: c.h:804
#define PG_RE_THROW()
Definition: elog.h:354
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
#define PG_TRY()
Definition: elog.h:313
#define PG_END_TRY()
Definition: elog.h:338
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1352

◆ get_useful_ecs_for_relation()

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

Definition at line 812 of file postgres_fdw.c.

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

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

◆ get_useful_pathkeys_for_relation()

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

Definition at line 908 of file postgres_fdw.c.

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

909 {
910  List *useful_pathkeys_list = NIL;
911  List *useful_eclass_list;
913  EquivalenceClass *query_ec = NULL;
914  ListCell *lc;
915 
916  /*
917  * Pushing the query_pathkeys to the remote server is always worth
918  * considering, because it might let us avoid a local sort.
919  */
920  fpinfo->qp_is_pushdown_safe = false;
921  if (root->query_pathkeys)
922  {
923  bool query_pathkeys_ok = true;
924 
925  foreach(lc, root->query_pathkeys)
926  {
927  PathKey *pathkey = (PathKey *) lfirst(lc);
928  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
929  Expr *em_expr;
930 
931  /*
932  * The planner and executor don't have any clever strategy for
933  * taking data sorted by a prefix of the query's pathkeys and
934  * getting it to be sorted by all of those pathkeys. We'll just
935  * end up resorting the entire data set. So, unless we can push
936  * down all of the query pathkeys, forget it.
937  *
938  * is_foreign_expr would detect volatile expressions as well, but
939  * checking ec_has_volatile here saves some cycles.
940  */
941  if (pathkey_ec->ec_has_volatile ||
942  !(em_expr = find_em_expr_for_rel(pathkey_ec, rel)) ||
943  !is_foreign_expr(root, rel, em_expr))
944  {
945  query_pathkeys_ok = false;
946  break;
947  }
948  }
949 
950  if (query_pathkeys_ok)
951  {
952  useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
953  fpinfo->qp_is_pushdown_safe = true;
954  }
955  }
956 
957  /*
958  * Even if we're not using remote estimates, having the remote side do the
959  * sort generally won't be any worse than doing it locally, and it might
960  * be much better if the remote side can generate data in the right order
961  * without needing a sort at all. However, what we're going to do next is
962  * try to generate pathkeys that seem promising for possible merge joins,
963  * and that's more speculative. A wrong choice might hurt quite a bit, so
964  * bail out if we can't use remote estimates.
965  */
966  if (!fpinfo->use_remote_estimate)
967  return useful_pathkeys_list;
968 
969  /* Get the list of interesting EquivalenceClasses. */
970  useful_eclass_list = get_useful_ecs_for_relation(root, rel);
971 
972  /* Extract unique EC for query, if any, so we don't consider it again. */
973  if (list_length(root->query_pathkeys) == 1)
974  {
975  PathKey *query_pathkey = linitial(root->query_pathkeys);
976 
977  query_ec = query_pathkey->pk_eclass;
978  }
979 
980  /*
981  * As a heuristic, the only pathkeys we consider here are those of length
982  * one. It's surely possible to consider more, but since each one we
983  * choose to consider will generate a round-trip to the r