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, 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 void complete_pending_request (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 TupleDesc get_tupdesc_for_join_scan_tuples (ForeignScanState *node)
 
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 node) */
127  /* Integer list of attribute numbers retrieved by RETURNING */
129  /* set-processed flag (as an Integer 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 node) */
106  /* has-returning flag (as an Integer 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 node) */
285  /* has-limit flag (as an Integer 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 6594 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().

6597 {
6598  Query *parse = root->parse;
6599  PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6600  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
6601  bool has_final_sort = false;
6602  List *pathkeys = NIL;
6603  PgFdwPathExtraData *fpextra;
6604  bool save_use_remote_estimate = false;
6605  double rows;
6606  int width;
6607  Cost startup_cost;
6608  Cost total_cost;
6609  List *fdw_private;
6610  ForeignPath *final_path;
6611 
6612  /*
6613  * Currently, we only support this for SELECT commands
6614  */
6615  if (parse->commandType != CMD_SELECT)
6616  return;
6617 
6618  /*
6619  * No work if there is no FOR UPDATE/SHARE clause and if there is no need
6620  * to add a LIMIT node
6621  */
6622  if (!parse->rowMarks && !extra->limit_needed)
6623  return;
6624 
6625  /* We don't support cases where there are any SRFs in the targetlist */
6626  if (parse->hasTargetSRFs)
6627  return;
6628 
6629  /* Save the input_rel as outerrel in fpinfo */
6630  fpinfo->outerrel = input_rel;
6631 
6632  /*
6633  * Copy foreign table, foreign server, user mapping, FDW options etc.
6634  * details from the input relation's fpinfo.
6635  */
6636  fpinfo->table = ifpinfo->table;
6637  fpinfo->server = ifpinfo->server;
6638  fpinfo->user = ifpinfo->user;
6639  merge_fdw_options(fpinfo, ifpinfo, NULL);
6640 
6641  /*
6642  * If there is no need to add a LIMIT node, there might be a ForeignPath
6643  * in the input_rel's pathlist that implements all behavior of the query.
6644  * Note: we would already have accounted for the query's FOR UPDATE/SHARE
6645  * (if any) before we get here.
6646  */
6647  if (!extra->limit_needed)
6648  {
6649  ListCell *lc;
6650 
6651  Assert(parse->rowMarks);
6652 
6653  /*
6654  * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
6655  * so the input_rel should be a base, join, or ordered relation; and
6656  * if it's an ordered relation, its input relation should be a base or
6657  * join relation.
6658  */
6659  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6660  input_rel->reloptkind == RELOPT_JOINREL ||
6661  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6662  ifpinfo->stage == UPPERREL_ORDERED &&
6663  (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
6664  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
6665 
6666  foreach(lc, input_rel->pathlist)
6667  {
6668  Path *path = (Path *) lfirst(lc);
6669 
6670  /*
6671  * apply_scanjoin_target_to_paths() uses create_projection_path()
6672  * to adjust each of its input paths if needed, whereas
6673  * create_ordered_paths() uses apply_projection_to_path() to do
6674  * that. So the former might have put a ProjectionPath on top of
6675  * the ForeignPath; look through ProjectionPath and see if the
6676  * path underneath it is ForeignPath.
6677  */
6678  if (IsA(path, ForeignPath) ||
6679  (IsA(path, ProjectionPath) &&
6680  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
6681  {
6682  /*
6683  * Create foreign final path; this gets rid of a
6684  * no-longer-needed outer plan (if any), which makes the
6685  * EXPLAIN output look cleaner
6686  */
6687  final_path = create_foreign_upper_path(root,
6688  path->parent,
6689  path->pathtarget,
6690  path->rows,
6691  path->startup_cost,
6692  path->total_cost,
6693  path->pathkeys,
6694  NULL, /* no extra plan */
6695  NULL); /* no fdw_private */
6696 
6697  /* and add it to the final_rel */
6698  add_path(final_rel, (Path *) final_path);
6699 
6700  /* Safe to push down */
6701  fpinfo->pushdown_safe = true;
6702 
6703  return;
6704  }
6705  }
6706 
6707  /*
6708  * If we get here it means no ForeignPaths; since we would already
6709  * have considered pushing down all operations for the query to the
6710  * remote server, give up on it.
6711  */
6712  return;
6713  }
6714 
6715  Assert(extra->limit_needed);
6716 
6717  /*
6718  * If the input_rel is an ordered relation, replace the input_rel with its
6719  * input relation
6720  */
6721  if (input_rel->reloptkind == RELOPT_UPPER_REL &&
6722  ifpinfo->stage == UPPERREL_ORDERED)
6723  {
6724  input_rel = ifpinfo->outerrel;
6725  ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6726  has_final_sort = true;
6727  pathkeys = root->sort_pathkeys;
6728  }
6729 
6730  /* The input_rel should be a base, join, or grouping relation */
6731  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6732  input_rel->reloptkind == RELOPT_JOINREL ||
6733  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6734  ifpinfo->stage == UPPERREL_GROUP_AGG));
6735 
6736  /*
6737  * We try to create a path below by extending a simple foreign path for
6738  * the underlying base, join, or grouping relation to perform the final
6739  * sort (if has_final_sort) and the LIMIT restriction remotely, which is
6740  * stored into the fdw_private list of the resulting path. (We
6741  * re-estimate the costs of sorting the underlying relation, if
6742  * has_final_sort.)
6743  */
6744 
6745  /*
6746  * Assess if it is safe to push down the LIMIT and OFFSET to the remote
6747  * server
6748  */
6749 
6750  /*
6751  * If the underlying relation has any local conditions, the LIMIT/OFFSET
6752  * cannot be pushed down.
6753  */
6754  if (ifpinfo->local_conds)
6755  return;
6756 
6757  /*
6758  * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
6759  * not safe to remote.
6760  */
6761  if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
6762  !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
6763  return;
6764 
6765  /* Safe to push down */
6766  fpinfo->pushdown_safe = true;
6767 
6768  /* Construct PgFdwPathExtraData */
6769  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6770  fpextra->target = root->upper_targets[UPPERREL_FINAL];
6771  fpextra->has_final_sort = has_final_sort;
6772  fpextra->has_limit = extra->limit_needed;
6773  fpextra->limit_tuples = extra->limit_tuples;
6774  fpextra->count_est = extra->count_est;
6775  fpextra->offset_est = extra->offset_est;
6776 
6777  /*
6778  * Estimate the costs of performing the final sort and the LIMIT
6779  * restriction remotely. If has_final_sort is false, we wouldn't need to
6780  * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
6781  * roughly estimated using the costs we already have for the underlying
6782  * relation, in the same way as when use_remote_estimate is false. Since
6783  * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
6784  * false in that case.
6785  */
6786  if (!fpextra->has_final_sort)
6787  {
6788  save_use_remote_estimate = ifpinfo->use_remote_estimate;
6789  ifpinfo->use_remote_estimate = false;
6790  }
6791  estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
6792  &rows, &width, &startup_cost, &total_cost);
6793  if (!fpextra->has_final_sort)
6794  ifpinfo->use_remote_estimate = save_use_remote_estimate;
6795 
6796  /*
6797  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6798  * Items in the list must match order in enum FdwPathPrivateIndex.
6799  */
6800  fdw_private = list_make2(makeInteger(has_final_sort),
6801  makeInteger(extra->limit_needed));
6802 
6803  /*
6804  * Create foreign final path; this gets rid of a no-longer-needed outer
6805  * plan (if any), which makes the EXPLAIN output look cleaner
6806  */
6807  final_path = create_foreign_upper_path(root,
6808  input_rel,
6810  rows,
6811  startup_cost,
6812  total_cost,
6813  pathkeys,
6814  NULL, /* no extra plan */
6815  fdw_private);
6816 
6817  /* and add it to the final_rel */
6818  add_path(final_rel, (Path *) final_path);
6819 }
#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:587
PathTarget * pathtarget
Definition: pathnodes.h:1183
Query * parse
Definition: pathnodes.h:162
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:2300
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
RelOptKind reloptkind
Definition: pathnodes.h:678
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:1193
RelOptInfo * parent
Definition: pathnodes.h:1182
Node * limitCount
Definition: parsenodes.h:172
List * sort_pathkeys
Definition: pathnodes.h:299
UserMapping * user
Definition: postgres_fdw.h:87
void * palloc0(Size size)
Definition: mcxt.c:1093
Integer * makeInteger(int i)
Definition: value.c:23
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:737
Cost total_cost
Definition: pathnodes.h:1194
CmdType commandType
Definition: parsenodes.h:120
bool hasTargetSRFs
Definition: parsenodes.h:135
List * pathkeys
Definition: pathnodes.h:1196
Cardinality limit_tuples
Definition: pathnodes.h:2615
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
ForeignTable * table
Definition: postgres_fdw.h:85
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:233
UpperRelationKind stage
Definition: postgres_fdw.h:109
List * pathlist
Definition: pathnodes.h:695
Cardinality rows
Definition: pathnodes.h:1192
Definition: pg_list.h:50
double Cost
Definition: nodes.h:670
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:673
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: pathnodes.h:310

◆ add_foreign_grouping_paths()

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

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

6379 {
6380  Query *parse = root->parse;
6381  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6382  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
6383  ForeignPath *grouppath;
6384  double rows;
6385  int width;
6386  Cost startup_cost;
6387  Cost total_cost;
6388 
6389  /* Nothing to be done, if there is no grouping or aggregation required. */
6390  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
6391  !root->hasHavingQual)
6392  return;
6393 
6396 
6397  /* save the input_rel as outerrel in fpinfo */
6398  fpinfo->outerrel = input_rel;
6399 
6400  /*
6401  * Copy foreign table, foreign server, user mapping, FDW options etc.
6402  * details from the input relation's fpinfo.
6403  */
6404  fpinfo->table = ifpinfo->table;
6405  fpinfo->server = ifpinfo->server;
6406  fpinfo->user = ifpinfo->user;
6407  merge_fdw_options(fpinfo, ifpinfo, NULL);
6408 
6409  /*
6410  * Assess if it is safe to push down aggregation and grouping.
6411  *
6412  * Use HAVING qual from extra. In case of child partition, it will have
6413  * translated Vars.
6414  */
6415  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
6416  return;
6417 
6418  /*
6419  * Compute the selectivity and cost of the local_conds, so we don't have
6420  * to do it over again for each path. (Currently we create just a single
6421  * path here, but in future it would be possible that we build more paths
6422  * such as pre-sorted paths as in postgresGetForeignPaths and
6423  * postgresGetForeignJoinPaths.) The best we can do for these conditions
6424  * is to estimate selectivity on the basis of local statistics.
6425  */
6426  fpinfo->local_conds_sel = clauselist_selectivity(root,
6427  fpinfo->local_conds,
6428  0,
6429  JOIN_INNER,
6430  NULL);
6431 
6432  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6433 
6434  /* Estimate the cost of push down */
6435  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
6436  &rows, &width, &startup_cost, &total_cost);
6437 
6438  /* Now update this information in the fpinfo */
6439  fpinfo->rows = rows;
6440  fpinfo->width = width;
6441  fpinfo->startup_cost = startup_cost;
6442  fpinfo->total_cost = total_cost;
6443 
6444  /* Create and add foreign path to the grouping relation. */
6445  grouppath = create_foreign_upper_path(root,
6446  grouped_rel,
6447  grouped_rel->reltarget,
6448  rows,
6449  startup_cost,
6450  total_cost,
6451  NIL, /* no pathkeys */
6452  NULL,
6453  NIL); /* no fdw_private */
6454 
6455  /* Add generated path into grouped_rel by add_path(). */
6456  add_path(grouped_rel, (Path *) grouppath);
6457 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:162
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:2300
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:2599
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:4308
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:737
#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:348
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:102
struct PathTarget * reltarget
Definition: pathnodes.h:692
double Cost
Definition: nodes.h:670
QualCost local_conds_cost
Definition: postgres_fdw.h:56
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:673

◆ add_foreign_ordered_paths()

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

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

6469 {
6470  Query *parse = root->parse;
6471  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6472  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
6473  PgFdwPathExtraData *fpextra;
6474  double rows;
6475  int width;
6476  Cost startup_cost;
6477  Cost total_cost;
6478  List *fdw_private;
6479  ForeignPath *ordered_path;
6480  ListCell *lc;
6481 
6482  /* Shouldn't get here unless the query has ORDER BY */
6483  Assert(parse->sortClause);
6484 
6485  /* We don't support cases where there are any SRFs in the targetlist */
6486  if (parse->hasTargetSRFs)
6487  return;
6488 
6489  /* Save the input_rel as outerrel in fpinfo */
6490  fpinfo->outerrel = input_rel;
6491 
6492  /*
6493  * Copy foreign table, foreign server, user mapping, FDW options etc.
6494  * details from the input relation's fpinfo.
6495  */
6496  fpinfo->table = ifpinfo->table;
6497  fpinfo->server = ifpinfo->server;
6498  fpinfo->user = ifpinfo->user;
6499  merge_fdw_options(fpinfo, ifpinfo, NULL);
6500 
6501  /*
6502  * If the input_rel is a base or join relation, we would already have
6503  * considered pushing down the final sort to the remote server when
6504  * creating pre-sorted foreign paths for that relation, because the
6505  * query_pathkeys is set to the root->sort_pathkeys in that case (see
6506  * standard_qp_callback()).
6507  */
6508  if (input_rel->reloptkind == RELOPT_BASEREL ||
6509  input_rel->reloptkind == RELOPT_JOINREL)
6510  {
6511  Assert(root->query_pathkeys == root->sort_pathkeys);
6512 
6513  /* Safe to push down if the query_pathkeys is safe to push down */
6514  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
6515 
6516  return;
6517  }
6518 
6519  /* The input_rel should be a grouping relation */
6520  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
6521  ifpinfo->stage == UPPERREL_GROUP_AGG);
6522 
6523  /*
6524  * We try to create a path below by extending a simple foreign path for
6525  * the underlying grouping relation to perform the final sort remotely,
6526  * which is stored into the fdw_private list of the resulting path.
6527  */
6528 
6529  /* Assess if it is safe to push down the final sort */
6530  foreach(lc, root->sort_pathkeys)
6531  {
6532  PathKey *pathkey = (PathKey *) lfirst(lc);
6533  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6534  Expr *sort_expr;
6535 
6536  /*
6537  * is_foreign_expr would detect volatile expressions as well, but
6538  * checking ec_has_volatile here saves some cycles.
6539  */
6540  if (pathkey_ec->ec_has_volatile)
6541  return;
6542 
6543  /* Get the sort expression for the pathkey_ec */
6544  sort_expr = find_em_expr_for_input_target(root,
6545  pathkey_ec,
6546  input_rel->reltarget);
6547 
6548  /* If it's unsafe to remote, we cannot push down the final sort */
6549  if (!is_foreign_expr(root, input_rel, sort_expr))
6550  return;
6551  }
6552 
6553  /* Safe to push down */
6554  fpinfo->pushdown_safe = true;
6555 
6556  /* Construct PgFdwPathExtraData */
6557  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6558  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6559  fpextra->has_final_sort = true;
6560 
6561  /* Estimate the costs of performing the final sort remotely */
6562  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6563  &rows, &width, &startup_cost, &total_cost);
6564 
6565  /*
6566  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6567  * Items in the list must match order in enum FdwPathPrivateIndex.
6568  */
6569  fdw_private = list_make2(makeInteger(true), makeInteger(false));
6570 
6571  /* Create foreign ordering path */
6572  ordered_path = create_foreign_upper_path(root,
6573  input_rel,
6575  rows,
6576  startup_cost,
6577  total_cost,
6578  root->sort_pathkeys,
6579  NULL, /* no extra plan */
6580  fdw_private);
6581 
6582  /* and add it to the ordered_rel */
6583  add_path(ordered_rel, (Path *) ordered_path);
6584 }
#define list_make2(x1, x2)
Definition: pg_list.h:208
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:162
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:2300
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
List * sortClause
Definition: parsenodes.h:169
RelOptKind reloptkind
Definition: pathnodes.h:678
List * query_pathkeys
Definition: pathnodes.h:294
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:299
UserMapping * user
Definition: postgres_fdw.h:87
void * palloc0(Size size)
Definition: mcxt.c:1093
Integer * makeInteger(int i)
Definition: value.c:23
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:737
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:1066
bool ec_has_volatile
Definition: pathnodes.h:995
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:233
UpperRelationKind stage
Definition: postgres_fdw.h:109
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:692
double Cost
Definition: nodes.h:670
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:673
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: pathnodes.h:310

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

5761 {
5762  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
5763  ListCell *lc;
5764 
5765  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
5766 
5767  /* Create one path for each set of pathkeys we found above. */
5768  foreach(lc, useful_pathkeys_list)
5769  {
5770  double rows;
5771  int width;
5772  Cost startup_cost;
5773  Cost total_cost;
5774  List *useful_pathkeys = lfirst(lc);
5775  Path *sorted_epq_path;
5776 
5777  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
5778  &rows, &width, &startup_cost, &total_cost);
5779 
5780  /*
5781  * The EPQ path must be at least as well sorted as the path itself, in
5782  * case it gets used as input to a mergejoin.
5783  */
5784  sorted_epq_path = epq_path;
5785  if (sorted_epq_path != NULL &&
5786  !pathkeys_contained_in(useful_pathkeys,
5787  sorted_epq_path->pathkeys))
5788  sorted_epq_path = (Path *)
5789  create_sort_path(root,
5790  rel,
5791  sorted_epq_path,
5792  useful_pathkeys,
5793  -1.0);
5794 
5795  if (IS_SIMPLE_REL(rel))
5796  add_path(rel, (Path *)
5797  create_foreignscan_path(root, rel,
5798  NULL,
5799  rows,
5800  startup_cost,
5801  total_cost,
5802  useful_pathkeys,
5803  rel->lateral_relids,
5804  sorted_epq_path,
5805  NIL));
5806  else
5807  add_path(rel, (Path *)
5808  create_foreign_join_path(root, rel,
5809  NULL,
5810  rows,
5811  startup_cost,
5812  total_cost,
5813  useful_pathkeys,
5814  rel->lateral_relids,
5815  sorted_epq_path,
5816  NIL));
5817  }
5818 }
#define NIL
Definition: pg_list.h:65
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:901
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:654
Relids lateral_relids
Definition: pathnodes.h:706
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:2206
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:2250
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:2941
List * pathkeys
Definition: pathnodes.h:1196
#define lfirst(lc)
Definition: pg_list.h:169
Definition: pg_list.h:50
double Cost
Definition: nodes.h:670

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

3607 {
3608  /*
3609  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3610  * side is unlikely to generate properly-sorted output, so it would need
3611  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3612  * if the GROUP BY clause is sort-able but isn't a superset of the given
3613  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3614  * costs by applying the same heuristic as for the scan or join case.
3615  */
3616  if (!grouping_is_sortable(root->parse->groupClause) ||
3617  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3618  {
3619  Path sort_path; /* dummy for result of cost_sort */
3620 
3621  cost_sort(&sort_path,
3622  root,
3623  pathkeys,
3624  *p_startup_cost + *p_run_cost,
3625  retrieved_rows,
3626  width,
3627  0.0,
3628  work_mem,
3629  limit_tuples);
3630 
3631  *p_startup_cost = sort_path.startup_cost;
3632  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3633  }
3634  else
3635  {
3636  /*
3637  * The default extra cost seems too large for foreign-grouping cases;
3638  * add 1/4th of that default.
3639  */
3640  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3641  - 1.0) * 0.25;
3642 
3643  *p_startup_cost *= sort_multiplier;
3644  *p_run_cost *= sort_multiplier;
3645  }
3646 }
List * group_pathkeys
Definition: pathnodes.h:296
Query * parse
Definition: pathnodes.h:162
Cost startup_cost
Definition: pathnodes.h:1193
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:1194
#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 5130 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().

5131 {
5132  int targrows = astate->targrows;
5133  int pos; /* array index to store tuple in */
5134  MemoryContext oldcontext;
5135 
5136  /* Always increment sample row counter. */
5137  astate->samplerows += 1;
5138 
5139  /*
5140  * Determine the slot where this sample row should be stored. Set pos to
5141  * negative value to indicate the row should be skipped.
5142  */
5143  if (astate->numrows < targrows)
5144  {
5145  /* First targrows rows are always included into the sample */
5146  pos = astate->numrows++;
5147  }
5148  else
5149  {
5150  /*
5151  * Now we start replacing tuples in the sample until we reach the end
5152  * of the relation. Same algorithm as in acquire_sample_rows in
5153  * analyze.c; see Jeff Vitter's paper.
5154  */
5155  if (astate->rowstoskip < 0)
5156  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5157 
5158  if (astate->rowstoskip <= 0)
5159  {
5160  /* Choose a random reservoir element to replace. */
5161  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
5162  Assert(pos >= 0 && pos < targrows);
5163  heap_freetuple(astate->rows[pos]);
5164  }
5165  else
5166  {
5167  /* Skip this tuple. */
5168  pos = -1;
5169  }
5170 
5171  astate->rowstoskip -= 1;
5172  }
5173 
5174  if (pos >= 0)
5175  {
5176  /*
5177  * Create sample tuple from current result row, and store it in the
5178  * position determined above. The tuple has to be created in anl_cxt.
5179  */
5180  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5181 
5182  astate->rows[pos] = make_tuple_from_result_row(res, row,
5183  astate->rel,
5184  astate->attinmeta,
5185  astate->retrieved_attrs,
5186  NULL,
5187  astate->temp_cxt);
5188 
5189  MemoryContextSwitchTo(oldcontext);
5190  }
5191 }
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 4710 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().

4714 {
4715  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4716  TupleTableSlot *resultSlot;
4717  Datum *values;
4718  bool *isnull;
4719  Datum *old_values;
4720  bool *old_isnull;
4721  int i;
4722 
4723  /*
4724  * Use the return tuple slot as a place to store the result tuple.
4725  */
4726  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4727 
4728  /*
4729  * Extract all the values of the scan tuple.
4730  */
4731  slot_getallattrs(slot);
4732  old_values = slot->tts_values;
4733  old_isnull = slot->tts_isnull;
4734 
4735  /*
4736  * Prepare to build the result tuple.
4737  */
4738  ExecClearTuple(resultSlot);
4739  values = resultSlot->tts_values;
4740  isnull = resultSlot->tts_isnull;
4741 
4742  /*
4743  * Transpose data into proper fields of the result tuple.
4744  */
4745  for (i = 0; i < resultTupType->natts; i++)
4746  {
4747  int j = dmstate->attnoMap[i];
4748 
4749  if (j == 0)
4750  {
4751  values[i] = (Datum) 0;
4752  isnull[i] = true;
4753  }
4754  else
4755  {
4756  values[i] = old_values[j - 1];
4757  isnull[i] = old_isnull[j - 1];
4758  }
4759  }
4760 
4761  /*
4762  * Build the virtual tuple.
4763  */
4764  ExecStoreVirtualTuple(resultSlot);
4765 
4766  /*
4767  * If we have any system columns to return, materialize a heap tuple in
4768  * the slot from column values set above and install system columns in
4769  * that tuple.
4770  */
4771  if (dmstate->hasSystemCols)
4772  {
4773  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4774 
4775  /* ctid */
4776  if (dmstate->ctidAttno)
4777  {
4778  ItemPointer ctid = NULL;
4779 
4780  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4781  resultTup->t_self = *ctid;
4782  }
4783 
4784  /*
4785  * And remaining columns
4786  *
4787  * Note: since we currently don't allow the target relation to appear
4788  * on the nullable side of an outer join, any system columns wouldn't
4789  * go to NULL.
4790  *
4791  * Note: no need to care about tableoid here because it will be
4792  * initialized in ExecProcessReturning().
4793  */
4797  }
4798 
4799  /*
4800  * And return the result tuple.
4801  */
4802  return resultSlot;
4803 }
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:503
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:156
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 5826 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, parse_int(), parse_real(), PgFdwRelationInfo::server, PgFdwRelationInfo::shippable_extensions, and PgFdwRelationInfo::use_remote_estimate.

Referenced by postgresGetForeignRelSize().

5827 {
5828  ListCell *lc;
5829 
5830  foreach(lc, fpinfo->server->options)
5831  {
5832  DefElem *def = (DefElem *) lfirst(lc);
5833 
5834  if (strcmp(def->defname, "use_remote_estimate") == 0)
5835  fpinfo->use_remote_estimate = defGetBoolean(def);
5836  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
5837  (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
5838  NULL);
5839  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
5840  (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
5841  NULL);
5842  else if (strcmp(def->defname, "extensions") == 0)
5843  fpinfo->shippable_extensions =
5844  ExtractExtensionList(defGetString(def), false);
5845  else if (strcmp(def->defname, "fetch_size") == 0)
5846  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
5847  else if (strcmp(def->defname, "async_capable") == 0)
5848  fpinfo->async_capable = defGetBoolean(def);
5849  }
5850 }
ForeignServer * server
Definition: postgres_fdw.h:86
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:6949
bool defGetBoolean(DefElem *def)
Definition: define.c:106
char * defGetString(DefElem *def)
Definition: define.c:49
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:408
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:6859
#define lfirst(lc)
Definition: pg_list.h:169
char * defname
Definition: parsenodes.h:758
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 5858 of file postgres_fdw.c.

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

Referenced by postgresGetForeignRelSize().

5859 {
5860  ListCell *lc;
5861 
5862  foreach(lc, fpinfo->table->options)
5863  {
5864  DefElem *def = (DefElem *) lfirst(lc);
5865 
5866  if (strcmp(def->defname, "use_remote_estimate") == 0)
5867  fpinfo->use_remote_estimate = defGetBoolean(def);
5868  else if (strcmp(def->defname, "fetch_size") == 0)
5869  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
5870  else if (strcmp(def->defname, "async_capable") == 0)
5871  fpinfo->async_capable = defGetBoolean(def);
5872  }
5873 }
bool defGetBoolean(DefElem *def)
Definition: define.c:106
char * defGetString(DefElem *def)
Definition: define.c:49
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:6859
#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:758

◆ build_remote_returning()

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

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

4383 {
4384  bool have_wholerow = false;
4385  List *tlist = NIL;
4386  List *vars;
4387  ListCell *lc;
4388 
4389  Assert(returningList);
4390 
4391  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4392 
4393  /*
4394  * If there's a whole-row reference to the target relation, then we'll
4395  * need all the columns of the relation.
4396  */
4397  foreach(lc, vars)
4398  {
4399  Var *var = (Var *) lfirst(lc);
4400 
4401  if (IsA(var, Var) &&
4402  var->varno == rtindex &&
4403  var->varattno == InvalidAttrNumber)
4404  {
4405  have_wholerow = true;
4406  break;
4407  }
4408  }
4409 
4410  if (have_wholerow)
4411  {
4412  TupleDesc tupdesc = RelationGetDescr(rel);
4413  int i;
4414 
4415  for (i = 1; i <= tupdesc->natts; i++)
4416  {
4417  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4418  Var *var;
4419 
4420  /* Ignore dropped attributes. */
4421  if (attr->attisdropped)
4422  continue;
4423 
4424  var = makeVar(rtindex,
4425  i,
4426  attr->atttypid,
4427  attr->atttypmod,
4428  attr->attcollation,
4429  0);
4430 
4431  tlist = lappend(tlist,
4432  makeTargetEntry((Expr *) var,
4433  list_length(tlist) + 1,
4434  NULL,
4435  false));
4436  }
4437  }
4438 
4439  /* Now add any remaining columns to tlist. */
4440  foreach(lc, vars)
4441  {
4442  Var *var = (Var *) lfirst(lc);
4443 
4444  /*
4445  * No need for whole-row references to the target relation. We don't
4446  * need system columns other than ctid and oid either, since those are
4447  * set locally.
4448  */
4449  if (IsA(var, Var) &&
4450  var->varno == rtindex &&
4451  var->varattno <= InvalidAttrNumber &&
4453  continue; /* don't need it */
4454 
4455  if (tlist_member((Expr *) var, tlist))
4456  continue; /* already got it */
4457 
4458  tlist = lappend(tlist,
4459  makeTargetEntry((Expr *) var,
4460  list_length(tlist) + 1,
4461  NULL,
4462  false));
4463  }
4464 
4465  list_free(vars);
4466 
4467  return tlist;
4468 }
int varno
Definition: primnodes.h:189
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:587
#define RelationGetDescr(relation)
Definition: rel.h:503
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
Definition: nodes.h:536
AttrNumber varattno
Definition: primnodes.h:191
List * pull_var_clause(Node *node, int flags)
Definition: var.c:597
Definition: primnodes.h:186
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:238
List * lappend(List *list, void *datum)
Definition: list.c:336
#define PVC_INCLUDE_PLACEHOLDERS
Definition: optimizer.h:190
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:237
Definition: pg_list.h:50

◆ close_cursor()

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

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

3902 {
3903  char sql[64];
3904  PGresult *res;
3905 
3906  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3907 
3908  /*
3909  * We don't use a PG_TRY block here, so be careful not to throw error
3910  * without releasing the PGresult.
3911  */
3912  res = pgfdw_exec_query(conn, sql, conn_state);
3913  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3914  pgfdw_report_error(ERROR, res, conn, true, sql);
3915  PQclear(res);
3916 }
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:702
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
#define ERROR
Definition: elog.h:46
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:795
static unsigned int cursor_number
Definition: connection.c:76
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define snprintf
Definition: port.h:217

◆ complete_pending_request()

static void complete_pending_request ( AsyncRequest areq)
static

Definition at line 7091 of file postgres_fdw.c.

References Assert, AsyncRequest::callback_pending, ExecAsyncResponse(), PlanState::instrument, InstrUpdateTupleCount(), produce_tuple_asynchronously(), AsyncRequest::requestee, AsyncRequest::result, and TupIsNull.

Referenced by postgresForeignAsyncConfigureWait().

7092 {
7093  /* The request would have been pending for a callback */
7094  Assert(areq->callback_pending);
7095 
7096  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7097  areq->callback_pending = false;
7098 
7099  /* We begin a fetch afterwards if necessary; don't fetch */
7100  produce_tuple_asynchronously(areq, false);
7101 
7102  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7103  ExecAsyncResponse(areq);
7104 
7105  /* Also, we do instrumentation ourselves, if required */
7106  if (areq->requestee->instrument)
7108  TupIsNull(areq->result) ? 0.0 : 1.0);
7109 }
static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch)
Instrumentation * instrument
Definition: execnodes.h:977
struct PlanState * requestee
Definition: execnodes.h:539
#define TupIsNull(slot)
Definition: tuptable.h:292
TupleTableSlot * result
Definition: execnodes.h:543
bool callback_pending
Definition: execnodes.h:541
#define Assert(condition)
Definition: c.h:804
void ExecAsyncResponse(AsyncRequest *areq)
Definition: execAsync.c:117
void InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
Definition: instrument.c:132

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

Definition at line 7282 of file postgres_fdw.c.

References Alias::aliasname, attname, castNode, Alias::colnames, ConversionLocation::cur_attno, RangeTblEntry::eref, errcontext, exec_rt_fetch(), TargetEntry::expr, ForeignScan::fdw_scan_tlist, ConversionLocation::fsstate, IsA, list_length(), list_nth(), list_nth_node, NameStr, TupleDescData::natts, PlanState::plan, ScanState::ps, PgFdwScanState::rel, ConversionLocation::rel, RelationGetDescr, RelationGetRelationName, relname, ForeignScan::scan, Scan::scanrelid, SelfItemPointerAttributeNumber, ForeignScanState::ss, PlanState::state, strVal, PgFdwScanState::tupdesc, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by make_tuple_from_result_row().

7283 {
7285  Relation rel = errpos->rel;
7286  ForeignScanState *fsstate = errpos->fsstate;
7287  const char *attname = NULL;
7288  const char *relname = NULL;
7289  bool is_wholerow = false;
7290 
7291  /*
7292  * If we're in a scan node, always use aliases from the rangetable, for
7293  * consistency between the simple-relation and remote-join cases. Look at
7294  * the relation's tupdesc only if we're not in a scan node.
7295  */
7296  if (fsstate)
7297  {
7298  /* ForeignScan case */
7299  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7300  int varno = 0;
7301  AttrNumber colno = 0;
7302 
7303  if (fsplan->scan.scanrelid > 0)
7304  {
7305  /* error occurred in a scan against a foreign table */
7306  varno = fsplan->scan.scanrelid;
7307  colno = errpos->cur_attno;
7308  }
7309  else
7310  {
7311  /* error occurred in a scan against a foreign join */
7312  TargetEntry *tle;
7313 
7314  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7315  errpos->cur_attno - 1);
7316 
7317  /*
7318  * Target list can have Vars and expressions. For Vars, we can
7319  * get some information, however for expressions we can't. Thus
7320  * for expressions, just show generic context message.
7321  */
7322  if (IsA(tle->expr, Var))
7323  {
7324  Var *var = (Var *) tle->expr;
7325 
7326  varno = var->varno;
7327  colno = var->varattno;
7328  }
7329  }
7330 
7331  if (varno > 0)
7332  {
7333  EState *estate = fsstate->ss.ps.state;
7334  RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7335 
7336  relname = rte->eref->aliasname;
7337 
7338  if (colno == 0)
7339  is_wholerow = true;
7340  else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7341  attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7342  else if (colno == SelfItemPointerAttributeNumber)
7343  attname = "ctid";
7344  }
7345  }
7346  else if (rel)
7347  {
7348  /* Non-ForeignScan case (we should always have a rel here) */
7349  TupleDesc tupdesc = RelationGetDescr(rel);
7350 
7351  relname = RelationGetRelationName(rel);
7352  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7353  {
7354  Form_pg_attribute attr = TupleDescAttr(tupdesc,
7355  errpos->cur_attno - 1);
7356 
7357  attname = NameStr(attr->attname);
7358  }
7359  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7360  attname = "ctid";
7361  }
7362 
7363  if (relname && is_wholerow)
7364  errcontext("whole-row reference to foreign table \"%s\"", relname);
7365  else if (relname && attname)
7366  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7367  else
7368  errcontext("processing expression at position %d in select list",
7369  errpos->cur_attno);
7370 }
int varno
Definition: primnodes.h:189
ScanState ss
Definition: execnodes.h:1858
#define IsA(nodeptr, _type_)
Definition: nodes.h:587
Index scanrelid
Definition: plannodes.h:344
List * colnames
Definition: primnodes.h:43
#define RelationGetDescr(relation)
Definition: rel.h:503
#define castNode(_type_, nodeptr)
Definition: nodes.h:605
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define strVal(v)
Definition: value.h:65
AttrNumber varattno
Definition: primnodes.h:191
List * fdw_scan_tlist
Definition: plannodes.h:642
EState * state
Definition: execnodes.h:969
NameData relname
Definition: pg_class.h:38
Definition: primnodes.h:186
PlanState ps
Definition: execnodes.h:1378
ForeignScanState * fsstate
Definition: postgres_fdw.c:307
NameData attname
Definition: pg_attribute.h:41
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
#define list_nth_node(type, list, n)
Definition: pg_list.h:306
#define RelationGetRelationName(relation)
Definition: rel.h:511
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:571
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
Plan * plan
Definition: execnodes.h:967
char * aliasname
Definition: primnodes.h:42
Expr * expr
Definition: primnodes.h:1455
static int list_length(const List *l)
Definition: pg_list.h:149
#define errcontext
Definition: elog.h:204
#define NameStr(name)
Definition: c.h:681
void * arg
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
Alias * eref
Definition: parsenodes.h:1153
AttrNumber cur_attno
Definition: postgres_fdw.c:305
int16 AttrNumber
Definition: attnum.h:21

◆ convert_prep_stmt_params()

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

Definition at line 4222 of file postgres_fdw.c.

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

Referenced by execute_foreign_modify().

4226 {
4227  const char **p_values;
4228  int i;
4229  int j;
4230  int pindex = 0;
4231  MemoryContext oldcontext;
4232 
4233  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4234 
4235  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4236 
4237  /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4238  Assert(!(tupleid != NULL && numSlots > 1));
4239 
4240  /* 1st parameter should be ctid, if it's in use */
4241  if (tupleid != NULL)
4242  {
4243  Assert(numSlots == 1);
4244  /* don't need set_transmission_modes for TID output */
4245  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
4246  PointerGetDatum(tupleid));
4247  pindex++;
4248  }
4249 
4250  /* get following parameters from slots */
4251  if (slots != NULL && fmstate->target_attrs != NIL)
4252  {
4253  TupleDesc tupdesc = RelationGetDescr(fmstate->rel);
4254  int nestlevel;
4255  ListCell *lc;
4256 
4257  nestlevel = set_transmission_modes();
4258 
4259  for (i = 0; i < numSlots; i++)
4260  {
4261  j = (tupleid != NULL) ? 1 : 0;
4262  foreach(lc, fmstate->target_attrs)
4263  {
4264  int attnum = lfirst_int(lc);
4265  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4266  Datum value;
4267  bool isnull;
4268 
4269  /* Ignore generated columns; they are set to DEFAULT */
4270  if (attr->attgenerated)
4271  continue;
4272  value = slot_getattr(slots[i], attnum, &isnull);
4273  if (isnull)
4274  p_values[pindex] = NULL;
4275  else
4276  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[j],
4277  value);
4278  pindex++;
4279  j++;
4280  }
4281  }
4282 
4283  reset_transmission_modes(nestlevel);
4284  }
4285 
4286  Assert(pindex == fmstate->p_nums * numSlots);
4287 
4288  MemoryContextSwitchTo(oldcontext);
4289 
4290  return p_values;
4291 }
#define NIL
Definition: pg_list.h:65
#define RelationGetDescr(relation)
Definition: rel.h:503
#define PointerGetDatum(X)
Definition: postgres.h:600
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
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
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
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
static struct @143 value
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 3683 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().

3684 {
3685  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3686  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3687  int numParams = fsstate->numParams;
3688  const char **values = fsstate->param_values;
3689  PGconn *conn = fsstate->conn;
3691  PGresult *res;
3692 
3693  /* First, process a pending asynchronous request, if any. */
3694  if (fsstate->conn_state->pendingAreq)
3696 
3697  /*
3698  * Construct array of query parameter values in text format. We do the
3699  * conversions in the short-lived per-tuple context, so as not to cause a
3700  * memory leak over repeated scans.
3701  */
3702  if (numParams > 0)
3703  {
3704  MemoryContext oldcontext;
3705 
3706  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3707 
3708  process_query_params(econtext,
3709  fsstate->param_flinfo,
3710  fsstate->param_exprs,
3711  values);
3712 
3713  MemoryContextSwitchTo(oldcontext);
3714  }
3715 
3716  /* Construct the DECLARE CURSOR command */
3717  initStringInfo(&buf);
3718  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3719  fsstate->cursor_number, fsstate->query);
3720 
3721  /*
3722  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3723  * to infer types for all parameters. Since we explicitly cast every
3724  * parameter (see deparse.c), the "inference" is trivial and will produce
3725  * the desired result. This allows us to avoid assuming that the remote
3726  * server has the same OIDs we do for the parameters' types.
3727  */
3728  if (!PQsendQueryParams(conn, buf.data, numParams,
3729  NULL, values, NULL, NULL, 0))
3730  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3731 
3732  /*
3733  * Get the result, and check for success.
3734  *
3735  * We don't use a PG_TRY block here, so be careful not to throw error
3736  * without releasing the PGresult.
3737  */
3738  res = pgfdw_get_result(conn, buf.data);
3739  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3740  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3741  PQclear(res);
3742 
3743  /* Mark the cursor as created, and show no tuples have been retrieved */
3744  fsstate->cursor_exists = true;
3745  fsstate->tuples = NULL;
3746  fsstate->num_tuples = 0;
3747  fsstate->next_tuple = 0;
3748  fsstate->fetch_ct_2 = 0;
3749  fsstate->eof_reached = false;
3750 
3751  /* Clean up */
3752  pfree(buf.data);
3753 }
ScanState ss
Definition: execnodes.h:1858
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:1438
List * param_exprs
Definition: postgres_fdw.c:154
ExprContext * ps_ExprContext
Definition: execnodes.h:1006
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:3178
unsigned int cursor_number
Definition: postgres_fdw.c:150
PlanState ps
Definition: execnodes.h:1378
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:795
FmgrInfo * param_flinfo
Definition: postgres_fdw.c:153
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
void PQclear(PGresult *res)
Definition: fe-exec.c:694
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:134
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:730
PgFdwConnState * conn_state
Definition: postgres_fdw.c:149
HeapTuple * tuples
Definition: postgres_fdw.c:158
static Datum values[MAXATTR]
Definition: bootstrap.c:156

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

3934 {
3935  PgFdwModifyState *fmstate;
3936  Relation rel = resultRelInfo->ri_RelationDesc;
3937  TupleDesc tupdesc = RelationGetDescr(rel);
3938  Oid userid;
3939  ForeignTable *table;
3940  UserMapping *user;
3941  AttrNumber n_params;
3942  Oid typefnoid;
3943  bool isvarlena;
3944  ListCell *lc;
3945 
3946  /* Begin constructing PgFdwModifyState. */
3947  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3948  fmstate->rel = rel;
3949 
3950  /*
3951  * Identify which user to do the remote access as. This should match what
3952  * ExecCheckRTEPerms() does.
3953  */
3954  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
3955 
3956  /* Get info about foreign table. */
3957  table = GetForeignTable(RelationGetRelid(rel));
3958  user = GetUserMapping(userid, table->serverid);
3959 
3960  /* Open connection; report that we'll create a prepared statement. */
3961  fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
3962  fmstate->p_name = NULL; /* prepared statement not made yet */
3963 
3964  /* Set up remote query information. */
3965  fmstate->query = query;
3966  if (operation == CMD_INSERT)
3967  {
3968  fmstate->query = pstrdup(fmstate->query);
3969  fmstate->orig_query = pstrdup(fmstate->query);
3970  }
3971  fmstate->target_attrs = target_attrs;
3972  fmstate->values_end = values_end;
3973  fmstate->has_returning = has_returning;
3974  fmstate->retrieved_attrs = retrieved_attrs;
3975 
3976  /* Create context for per-tuple temp workspace. */
3977  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
3978  "postgres_fdw temporary data",
3980 
3981  /* Prepare for input conversion of RETURNING results. */
3982  if (fmstate->has_returning)
3983  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
3984 
3985  /* Prepare for output conversion of parameters used in prepared stmt. */
3986  n_params = list_length(fmstate->target_attrs) + 1;
3987  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
3988  fmstate->p_nums = 0;
3989 
3990  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3991  {
3992  Assert(subplan != NULL);
3993 
3994  /* Find the ctid resjunk column in the subplan's result */
3996  "ctid");
3997  if (!AttributeNumberIsValid(fmstate->ctidAttno))
3998  elog(ERROR, "could not find junk ctid column");
3999 
4000  /* First transmittable parameter will be ctid */
4001  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
4002  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4003  fmstate->p_nums++;
4004  }
4005 
4006  if (operation == CMD_INSERT || operation == CMD_UPDATE)
4007  {
4008  /* Set up for remaining transmittable parameters */
4009  foreach(lc, fmstate->target_attrs)
4010  {
4011  int attnum = lfirst_int(lc);
4012  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4013 
4014  Assert(!attr->attisdropped);
4015 
4016  /* Ignore generated columns; they are set to DEFAULT */
4017  if (attr->attgenerated)
4018  continue;
4019  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
4020  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4021  fmstate->p_nums++;
4022  }
4023  }
4024 
4025  Assert(fmstate->p_nums <= n_params);
4026 
4027  /* Set batch_size from foreign server/table options. */
4028  if (operation == CMD_INSERT)
4029  fmstate->batch_size = get_batch_size_option(rel);
4030 
4031  fmstate->num_slots = 1;
4032 
4033  /* Initialize auxiliary state */
4034  fmstate->aux_fmstate = NULL;
4035 
4036  return fmstate;
4037 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:412
#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:503
Oid GetUserId(void)
Definition: miscinit.c:495
#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:601
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
Definition: connection.c:127
#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:207
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:477

◆ deallocate_query()

static void deallocate_query ( PgFdwModifyState fmstate)
static

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

4354 {
4355  char sql[64];
4356  PGresult *res;
4357 
4358  /* do nothing if the query is not allocated */
4359  if (!fmstate->p_name)
4360  return;
4361 
4362  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4363 
4364  /*
4365  * We don't use a PG_TRY block here, so be careful not to throw error
4366  * without releasing the PGresult.
4367  */
4368  res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4369  if (PQresultStatus(res) != PGRES_COMMAND_OK)
4370  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
4371  PQclear(res);
4372  pfree(fmstate->p_name);
4373  fmstate->p_name = NULL;
4374 }
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:702
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
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:795
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define snprintf
Definition: port.h:217

◆ ec_member_matches_foreign()

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

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

3657 {
3659  Expr *expr = em->em_expr;
3660 
3661  /*
3662  * If we've identified what we're processing in the current scan, we only
3663  * want to match that expression.
3664  */
3665  if (state->current != NULL)
3666  return equal(expr, state->current);
3667 
3668  /*
3669  * Otherwise, ignore anything we've already processed.
3670  */
3671  if (list_member(state->already_used, expr))
3672  return false;
3673 
3674  /* This is the new target to process. */
3675  state->current = expr;
3676  return true;
3677 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3149
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 3049 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().

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

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

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

4510 {
4512  ExprContext *econtext = node->ss.ps.ps_ExprContext;
4513  int numParams = dmstate->numParams;
4514  const char **values = dmstate->param_values;
4515 
4516  /* First, process a pending asynchronous request, if any. */
4517  if (dmstate->conn_state->pendingAreq)
4519 
4520  /*
4521  * Construct array of query parameter values in text format.
4522  */
4523  if (numParams > 0)
4524  process_query_params(econtext,
4525  dmstate->param_flinfo,
4526  dmstate->param_exprs,
4527  values);
4528 
4529  /*
4530  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4531  * to infer types for all parameters. Since we explicitly cast every
4532  * parameter (see deparse.c), the "inference" is trivial and will produce
4533  * the desired result. This allows us to avoid assuming that the remote
4534  * server has the same OIDs we do for the parameters' types.
4535  */
4536  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4537  NULL, values, NULL, NULL, 0))
4538  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4539 
4540  /*
4541  * Get the result, and check for success.
4542  *
4543  * We don't use a PG_TRY block here, so be careful not to throw error
4544  * without releasing the PGresult.
4545  */
4546  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
4547  if (PQresultStatus(dmstate->result) !=
4549  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4550  dmstate->query);
4551 
4552  /* Get the number of rows affected. */
4553  if (dmstate->has_returning)
4554  dmstate->num_tuples = PQntuples(dmstate->result);
4555  else
4556  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4557 }
ScanState ss
Definition: execnodes.h:1858
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:1438
const char ** param_values
Definition: postgres_fdw.c:234
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3589
ExprContext * ps_ExprContext
Definition: execnodes.h:1006
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:3248
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
PlanState ps
Definition: execnodes.h:1378
#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:795
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:134
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:730
static Datum values[MAXATTR]
Definition: bootstrap.c:156
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 4047 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(), PgFdwModifyState::rel, ResultRelInfo::ri_FdwState, store_returning_result(), PgFdwModifyState::target_attrs, PgFdwModifyState::temp_cxt, and PgFdwModifyState::values_end.

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

4053 {
4054  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
4055  ItemPointer ctid = NULL;
4056  const char **p_values;
4057  PGresult *res;
4058  int n_rows;
4059  StringInfoData sql;
4060 
4061  /* The operation should be INSERT, UPDATE, or DELETE */
4062  Assert(operation == CMD_INSERT ||
4063  operation == CMD_UPDATE ||
4064  operation == CMD_DELETE);
4065 
4066  /* First, process a pending asynchronous request, if any. */
4067  if (fmstate->conn_state->pendingAreq)
4069 
4070  /*
4071  * If the existing query was deparsed and prepared for a different number
4072  * of rows, rebuild it for the proper number.
4073  */
4074  if (operation == CMD_INSERT && fmstate->num_slots != *numSlots)
4075  {
4076  /* Destroy the prepared statement created previously */
4077  if (fmstate->p_name)
4078  deallocate_query(fmstate);
4079 
4080  /* Build INSERT string with numSlots records in its VALUES clause. */
4081  initStringInfo(&sql);
4082  rebuildInsertSql(&sql, fmstate->rel,
4083  fmstate->orig_query, fmstate->target_attrs,
4084  fmstate->values_end, fmstate->p_nums,
4085  *numSlots - 1);
4086  pfree(fmstate->query);
4087  fmstate->query = sql.data;
4088  fmstate->num_slots = *numSlots;
4089  }
4090 
4091  /* Set up the prepared statement on the remote server, if we didn't yet */
4092  if (!fmstate->p_name)
4093  prepare_foreign_modify(fmstate);
4094 
4095  /*
4096  * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
4097  */
4098  if (operation == CMD_UPDATE || operation == CMD_DELETE)
4099  {
4100  Datum datum;
4101  bool isNull;
4102 
4103  datum = ExecGetJunkAttribute(planSlots[0],
4104  fmstate->ctidAttno,
4105  &isNull);
4106  /* shouldn't ever get a null result... */
4107  if (isNull)
4108  elog(ERROR, "ctid is NULL");
4109  ctid = (ItemPointer) DatumGetPointer(datum);
4110  }
4111 
4112  /* Convert parameters needed by prepared statement to text form */
4113  p_values = convert_prep_stmt_params(fmstate, ctid, slots, *numSlots);
4114 
4115  /*
4116  * Execute the prepared statement.
4117  */
4118  if (!PQsendQueryPrepared(fmstate->conn,
4119  fmstate->p_name,
4120  fmstate->p_nums * (*numSlots),
4121  p_values,
4122  NULL,
4123  NULL,
4124  0))
4125  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
4126 
4127  /*
4128  * Get the result, and check for success.
4129  *
4130  * We don't use a PG_TRY block here, so be careful not to throw error
4131  * without releasing the PGresult.
4132  */
4133  res = pgfdw_get_result(fmstate->conn, fmstate->query);
4134  if (PQresultStatus(res) !=
4136  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
4137 
4138  /* Check number of rows affected, and fetch RETURNING tuple if any */
4139  if (fmstate->has_returning)
4140  {
4141  Assert(*numSlots == 1);
4142  n_rows = PQntuples(res);
4143  if (n_rows > 0)
4144  store_returning_result(fmstate, slots[0], res);
4145  }
4146  else
4147  n_rows = atoi(PQcmdTuples(res));
4148 
4149  /* And clean up */
4150  PQclear(res);
4151 
4152  MemoryContextReset(fmstate->temp_cxt);
4153 
4154  *numSlots = n_rows;
4155 
4156  /*
4157  * Return NULL if nothing was inserted/updated/deleted on the remote end
4158  */
4159  return (n_rows > 0) ? slots : NULL;
4160 }
static void deallocate_query(PgFdwModifyState *fmstate)
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3589
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:3248
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
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:795
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:459
void PQclear(PGresult *res)
Definition: fe-exec.c:694
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:730
#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:1584
#define DatumGetPointer(X)
Definition: postgres.h:593
static void prepare_foreign_modify(PgFdwModifyState *fmstate)
#define elog(elevel,...)
Definition: elog.h:232
void rebuildInsertSql(StringInfo buf, Relation rel, char *orig_query, List *target_attrs, int values_end_len, int num_params, int num_rows)
Definition: deparse.c:1914

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

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

3760 {
3761  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3762  PGresult *volatile res = NULL;
3763  MemoryContext oldcontext;
3764 
3765  /*
3766  * We'll store the tuples in the batch_cxt. First, flush the previous
3767  * batch.
3768  */
3769  fsstate->tuples = NULL;
3770  MemoryContextReset(fsstate->batch_cxt);
3771  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3772 
3773  /* PGresult must be released before leaving this function. */
3774  PG_TRY();
3775  {
3776  PGconn *conn = fsstate->conn;
3777  int numrows;
3778  int i;
3779 
3780  if (fsstate->async_capable)
3781  {
3782  Assert(fsstate->conn_state->pendingAreq);
3783 
3784  /*
3785  * The query was already sent by an earlier call to
3786  * fetch_more_data_begin. So now we just fetch the result.
3787  */
3788  res = pgfdw_get_result(conn, fsstate->query);
3789  /* On error, report the original query, not the FETCH. */
3790  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3791  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3792 
3793  /* Reset per-connection state */
3794  fsstate->conn_state->pendingAreq = NULL;
3795  }
3796  else
3797  {
3798  char sql[64];
3799 
3800  /* This is a regular synchronous fetch. */
3801  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3802  fsstate->fetch_size, fsstate->cursor_number);
3803 
3804  res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
3805  /* On error, report the original query, not the FETCH. */
3806  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3807  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3808  }
3809 
3810  /* Convert the data into HeapTuples */
3811  numrows = PQntuples(res);
3812  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3813  fsstate->num_tuples = numrows;
3814  fsstate->next_tuple = 0;
3815 
3816  for (i = 0; i < numrows; i++)
3817  {
3818  Assert(IsA(node->ss.ps.plan, ForeignScan));
3819 
3820  fsstate->tuples[i] =
3822  fsstate->rel,
3823  fsstate->attinmeta,
3824  fsstate->retrieved_attrs,
3825  node,
3826  fsstate->temp_cxt);
3827  }
3828 
3829  /* Update fetch_ct_2 */
3830  if (fsstate->fetch_ct_2 < 2)
3831  fsstate->fetch_ct_2++;
3832 
3833  /* Must be EOF if we didn't get as many tuples as we asked for. */
3834  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3835  }
3836  PG_FINALLY();
3837  {
3838  if (res)
3839  PQclear(res);
3840  }
3841  PG_END_TRY();
3842 
3843  MemoryContextSwitchTo(oldcontext);
3844 }
ScanState ss
Definition: execnodes.h:1858
#define IsA(nodeptr, _type_)
Definition: nodes.h:587
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:702
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:3248
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
unsigned int cursor_number
Definition: postgres_fdw.c:150
PlanState ps
Definition: execnodes.h:1378
#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:795
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:967
void PQclear(PGresult *res)
Definition: fe-exec.c:694
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:134
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:730
#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:217
#define PG_END_TRY()
Definition: elog.h:338

◆ fetch_more_data_begin()

static void fetch_more_data_begin ( AsyncRequest areq)
static

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

7032 {
7033  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7034  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7035  char sql[64];
7036 
7037  Assert(!fsstate->conn_state->pendingAreq);
7038 
7039  /* Create the cursor synchronously. */
7040  if (!fsstate->cursor_exists)
7041  create_cursor(node);
7042 
7043  /* We will send this query, but not wait for the response. */
7044  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7045  fsstate->fetch_size, fsstate->cursor_number);
7046 
7047  if (PQsendQuery(fsstate->conn, sql) < 0)
7048  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7049 
7050  /* Remember that the request is in process */
7051  fsstate->conn_state->pendingAreq = areq;
7052 }
static void create_cursor(ForeignScanState *node)
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1326
#define ERROR
Definition: elog.h:46
struct PlanState * requestee
Definition: execnodes.h:539
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:795
#define Assert(condition)
Definition: c.h:804
#define snprintf
Definition: port.h:217

◆ find_em_expr_for_input_target()

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

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

7380 {
7381  ListCell *lc1;
7382  int i;
7383 
7384  i = 0;
7385  foreach(lc1, target->exprs)
7386  {
7387  Expr *expr = (Expr *) lfirst(lc1);
7388  Index sgref = get_pathtarget_sortgroupref(target, i);
7389  ListCell *lc2;
7390 
7391  /* Ignore non-sort expressions */
7392  if (sgref == 0 ||
7394  root->parse->sortClause) == NULL)
7395  {
7396  i++;
7397  continue;
7398  }
7399 
7400  /* We ignore binary-compatible relabeling on both ends */
7401  while (expr && IsA(expr, RelabelType))
7402  expr = ((RelabelType *) expr)->arg;
7403 
7404  /* Locate an EquivalenceClass member matching this expr, if any */
7405  foreach(lc2, ec->ec_members)
7406  {
7408  Expr *em_expr;
7409 
7410  /* Don't match constants */
7411  if (em->em_is_const)
7412  continue;
7413 
7414  /* Ignore child members */
7415  if (em->em_is_child)
7416  continue;
7417 
7418  /* Match if same expression (after stripping relabel) */
7419  em_expr = em->em_expr;
7420  while (em_expr && IsA(em_expr, RelabelType))
7421  em_expr = ((RelabelType *) em_expr)->arg;
7422 
7423  if (equal(em_expr, expr))
7424  return em->em_expr;
7425  }
7426 
7427  i++;
7428  }
7429 
7430  elog(ERROR, "could not find pathkey item to sort");
7431  return NULL; /* keep compiler quiet */
7432 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:587
Query * parse
Definition: pathnodes.h:162
List * sortClause
Definition: parsenodes.h:169
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3149
#define ERROR
Definition: elog.h:46
List * exprs
Definition: pathnodes.h:1110
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:1119
#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:989

◆ find_modifytable_subplan()

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

Definition at line 2345 of file postgres_fdw.c.

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

Referenced by postgresPlanDirectModify().

2349 {
2350  Plan *subplan = outerPlan(plan);
2351 
2352  /*
2353  * The cases we support are (1) the desired ForeignScan is the immediate
2354  * child of ModifyTable, or (2) it is the subplan_index'th child of an
2355  * Append node that is the immediate child of ModifyTable. There is no
2356  * point in looking further down, as that would mean that local joins are
2357  * involved, so we can't do the update directly.
2358  *
2359  * There could be a Result atop the Append too, acting to compute the
2360  * UPDATE targetlist values. We ignore that here; the tlist will be
2361  * checked by our caller.
2362  *
2363  * In principle we could examine all the children of the Append, but it's
2364  * currently unlikely that the core planner would generate such a plan
2365  * with the children out-of-order. Moreover, such a search risks costing
2366  * O(N^2) time when there are a lot of children.
2367  */
2368  if (IsA(subplan, Append))
2369  {
2370  Append *appendplan = (Append *) subplan;
2371 
2372  if (subplan_index < list_length(appendplan->appendplans))
2373  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2374  }
2375  else if (IsA(subplan, Result) &&
2376  outerPlan(subplan) != NULL &&
2377  IsA(outerPlan(subplan), Append))
2378  {
2379  Append *appendplan = (Append *) outerPlan(subplan);
2380 
2381  if (subplan_index < list_length(appendplan->appendplans))
2382  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2383  }
2384 
2385  /* Now, have we got a ForeignScan on the desired rel? */
2386  if (IsA(subplan, ForeignScan))
2387  {
2388  ForeignScan *fscan = (ForeignScan *) subplan;
2389 
2390  if (bms_is_member(rtindex, fscan->fs_relids))
2391  return fscan;
2392  }
2393 
2394  return NULL;
2395 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:587
List * appendplans
Definition: plannodes.h:253
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:644

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4335 of file postgres_fdw.c.

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

Referenced by postgresEndForeignInsert(), and postgresEndForeignModify().

4336 {
4337  Assert(fmstate != NULL);
4338 
4339  /* If we created a prepared statement, destroy it */
4340  deallocate_query(fmstate);
4341 
4342  /* Release remote connection */
4343  ReleaseConnection(fmstate->conn);
4344  fmstate->conn = NULL;
4345 }
static void deallocate_query(PgFdwModifyState *fmstate)
void ReleaseConnection(PGconn *conn)
Definition: connection.c:654
#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 6085 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().

6087 {
6088  Query *query = root->parse;
6089  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
6090  PathTarget *grouping_target = grouped_rel->reltarget;
6091  PgFdwRelationInfo *ofpinfo;
6092  ListCell *lc;
6093  int i;
6094  List *tlist = NIL;
6095 
6096  /* We currently don't support pushing Grouping Sets. */
6097  if (query->groupingSets)
6098  return false;
6099 
6100  /* Get the fpinfo of the underlying scan relation. */
6101  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
6102 
6103  /*
6104  * If underlying scan relation has any local conditions, those conditions
6105  * are required to be applied before performing aggregation. Hence the
6106  * aggregate cannot be pushed down.
6107  */
6108  if (ofpinfo->local_conds)
6109  return false;
6110 
6111  /*
6112  * Examine grouping expressions, as well as other expressions we'd need to
6113  * compute, and check whether they are safe to push down to the foreign
6114  * server. All GROUP BY expressions will be part of the grouping target
6115  * and thus there is no need to search for them separately. Add grouping
6116  * expressions into target list which will be passed to foreign server.
6117  *
6118  * A tricky fine point is that we must not put any expression into the
6119  * target list that is just a foreign param (that is, something that
6120  * deparse.c would conclude has to be sent to the foreign server). If we
6121  * do, the expression will also appear in the fdw_exprs list of the plan
6122  * node, and setrefs.c will get confused and decide that the fdw_exprs
6123  * entry is actually a reference to the fdw_scan_tlist entry, resulting in
6124  * a broken plan. Somewhat oddly, it's OK if the expression contains such
6125  * a node, as long as it's not at top level; then no match is possible.
6126  */
6127  i = 0;
6128  foreach(lc, grouping_target->exprs)
6129  {
6130  Expr *expr = (Expr *) lfirst(lc);
6131  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
6132  ListCell *l;
6133 
6134  /* Check whether this expression is part of GROUP BY clause */
6135  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
6136  {
6137  TargetEntry *tle;
6138 
6139  /*
6140  * If any GROUP BY expression is not shippable, then we cannot
6141  * push down aggregation to the foreign server.
6142  */
6143  if (!is_foreign_expr(root, grouped_rel, expr))
6144  return false;
6145 
6146  /*
6147  * If it would be a foreign param, we can't put it into the tlist,
6148  * so we have to fail.
6149  */
6150  if (is_foreign_param(root, grouped_rel, expr))
6151  return false;
6152 
6153  /*
6154  * Pushable, so add to tlist. We need to create a TLE for this
6155  * expression and apply the sortgroupref to it. We cannot use
6156  * add_to_flat_tlist() here because that avoids making duplicate
6157  * entries in the tlist. If there are duplicate entries with
6158  * distinct sortgrouprefs, we have to duplicate that situation in
6159  * the output tlist.
6160  */
6161  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
6162  tle->ressortgroupref = sgref;
6163  tlist = lappend(tlist, tle);
6164  }
6165  else
6166  {
6167  /*
6168  * Non-grouping expression we need to compute. Can we ship it
6169  * as-is to the foreign server?
6170  */
6171  if (is_foreign_expr(root, grouped_rel, expr) &&
6172  !is_foreign_param(root, grouped_rel, expr))
6173  {
6174  /* Yes, so add to tlist as-is; OK to suppress duplicates */
6175  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6176  }
6177  else
6178  {
6179  /* Not pushable as a whole; extract its Vars and aggregates */
6180  List *aggvars;
6181 
6182  aggvars = pull_var_clause((Node *) expr,
6184 
6185  /*
6186  * If any aggregate expression is not shippable, then we
6187  * cannot push down aggregation to the foreign server. (We
6188  * don't have to check is_foreign_param, since that certainly
6189  * won't return true for any such expression.)
6190  */
6191  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
6192  return false;
6193 
6194  /*
6195  * Add aggregates, if any, into the targetlist. Plain Vars
6196  * outside an aggregate can be ignored, because they should be
6197  * either same as some GROUP BY column or part of some GROUP
6198  * BY expression. In either case, they are already part of
6199  * the targetlist and thus no need to add them again. In fact
6200  * including plain Vars in the tlist when they do not match a
6201  * GROUP BY column would cause the foreign server to complain
6202  * that the shipped query is invalid.
6203  */
6204  foreach(l, aggvars)
6205  {
6206  Expr *expr = (Expr *) lfirst(l);
6207 
6208  if (IsA(expr, Aggref))
6209  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6210  }
6211  }
6212  }
6213 
6214  i++;
6215  }
6216 
6217  /*
6218  * Classify the pushable and non-pushable HAVING clauses and save them in
6219  * remote_conds and local_conds of the grouped rel's fpinfo.
6220  */
6221  if (havingQual)
6222  {
6223  ListCell *lc;
6224 
6225  foreach(lc, (List *) havingQual)
6226  {
6227  Expr *expr = (Expr *) lfirst(lc);
6228  RestrictInfo *rinfo;
6229 
6230  /*
6231  * Currently, the core code doesn't wrap havingQuals in
6232  * RestrictInfos, so we must make our own.
6233  */
6234  Assert(!IsA(expr, RestrictInfo));
6235  rinfo = make_restrictinfo(root,
6236  expr,
6237  true,
6238  false,
6239  false,
6240  root->qual_security_level,
6241  grouped_rel->relids,
6242  NULL,
6243  NULL);
6244  if (is_foreign_expr(root, grouped_rel, expr))
6245  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
6246  else
6247  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
6248  }
6249  }
6250 
6251  /*
6252  * If there are any local conditions, pull Vars and aggregates from it and
6253  * check whether they are safe to pushdown or not.
6254  */
6255  if (fpinfo->local_conds)
6256  {
6257  List *aggvars = NIL;
6258  ListCell *lc;
6259 
6260  foreach(lc, fpinfo->local_conds)
6261  {
6262  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6263 
6264  aggvars = list_concat(aggvars,
6265  pull_var_clause((Node *) rinfo->clause,
6267  }
6268 
6269  foreach(lc, aggvars)
6270  {
6271  Expr *expr = (Expr *) lfirst(lc);
6272 
6273  /*
6274  * If aggregates within local conditions are not safe to push
6275  * down, then we cannot push down the query. Vars are already
6276  * part of GROUP BY clause which are checked above, so no need to
6277  * access them again here. Again, we need not check
6278  * is_foreign_param for a foreign aggregate.
6279  */
6280  if (IsA(expr, Aggref))
6281  {
6282  if (!is_foreign_expr(root, grouped_rel, expr))
6283  return false;
6284 
6285  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6286  }
6287  }
6288  }
6289 
6290  /* Store generated targetlist */
6291  fpinfo->grouped_tlist = tlist;
6292 
6293  /* Safe to pushdown */
6294  fpinfo->pushdown_safe = true;
6295 
6296  /*
6297  * Set # of retrieved rows and cached relation costs to some negative
6298  * value, so that we can detect when they are set to some sensible values,
6299  * during one (usually the first) of the calls to estimate_path_cost_size.
6300  */
6301  fpinfo->retrieved_rows = -1;
6302  fpinfo->rel_startup_cost = -1;
6303  fpinfo->rel_total_cost = -1;
6304 
6305  /*
6306  * Set the string describing this grouped relation to be used in EXPLAIN
6307  * output of corresponding ForeignScan. Note that the decoration we add
6308  * to the base relation name mustn't include any digits, or it'll confuse
6309  * postgresExplainForeignScan.
6310  */
6311  fpinfo->relation_name = psprintf("Aggregate on (%s)",
6312  ofpinfo->relation_name);
6313 
6314  return true;
6315 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:587
Query * parse
Definition: pathnodes.h:162
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
List * groupingSets
Definition: parsenodes.h:161
Definition: nodes.h:536
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
List * pull_var_clause(Node *node, int flags)
Definition: var.c:597
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
#define PVC_INCLUDE_AGGREGATES
Definition: optimizer.h:186
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:1004
#define list_make1(x1)
Definition: pg_list.h:206
#define lfirst_node(type, lc)
Definition: pg_list.h:172
Relids relids
Definition: pathnodes.h:681
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:2056
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:1119
void * fdw_private
Definition: pathnodes.h:737
#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:343
List * groupClause
Definition: parsenodes.h:158
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:233
int i
Index ressortgroupref
Definition: primnodes.h:1458
Definition: pg_list.h:50
struct PathTarget * reltarget
Definition: pathnodes.h:692

◆ foreign_join_ok()

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

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

5500 {
5501  PgFdwRelationInfo *fpinfo;
5502  PgFdwRelationInfo *fpinfo_o;
5503  PgFdwRelationInfo *fpinfo_i;
5504  ListCell *lc;
5505  List *joinclauses;
5506 
5507  /*
5508  * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
5509  * Constructing queries representing SEMI and ANTI joins is hard, hence
5510  * not considered right now.
5511  */
5512  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
5513  jointype != JOIN_RIGHT && jointype != JOIN_FULL)
5514  return false;
5515 
5516  /*
5517  * If either of the joining relations is marked as unsafe to pushdown, the
5518  * join can not be pushed down.
5519  */
5520  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
5521  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
5522  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
5523  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
5524  !fpinfo_i || !fpinfo_i->pushdown_safe)
5525  return false;
5526 
5527  /*
5528  * If joining relations have local conditions, those conditions are
5529  * required to be applied before joining the relations. Hence the join can
5530  * not be pushed down.
5531  */
5532  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
5533  return false;
5534 
5535  /*
5536  * Merge FDW options. We might be tempted to do this after we have deemed
5537  * the foreign join to be OK. But we must do this beforehand so that we
5538  * know which quals can be evaluated on the foreign server, which might
5539  * depend on shippable_extensions.
5540  */
5541  fpinfo->server = fpinfo_o->server;
5542  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
5543 
5544  /*
5545  * Separate restrict list into join quals and pushed-down (other) quals.
5546  *
5547  * Join quals belonging to an outer join must all be shippable, else we
5548  * cannot execute the join remotely. Add such quals to 'joinclauses'.
5549  *
5550  * Add other quals to fpinfo->remote_conds if they are shippable, else to
5551  * fpinfo->local_conds. In an inner join it's okay to execute conditions
5552  * either locally or remotely; the same is true for pushed-down conditions
5553  * at an outer join.
5554  *
5555  * Note we might return failure after having already scribbled on
5556  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
5557  * won't consult those lists again if we deem the join unshippable.
5558  */
5559  joinclauses = NIL;
5560  foreach(lc, extra->restrictlist)
5561  {
5562  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5563  bool is_remote_clause = is_foreign_expr(root, joinrel,
5564  rinfo->clause);
5565 
5566  if (IS_OUTER_JOIN(jointype) &&
5567  !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
5568  {
5569  if (!is_remote_clause)
5570  return false;
5571  joinclauses = lappend(joinclauses, rinfo);
5572  }
5573  else
5574  {
5575  if (is_remote_clause)
5576  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5577  else
5578  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5579  }
5580  }
5581 
5582  /*
5583  * deparseExplicitTargetList() isn't smart enough to handle anything other
5584  * than a Var. In particular, if there's some PlaceHolderVar that would
5585  * need to be evaluated within this join tree (because there's an upper
5586  * reference to a quantity that may go to NULL as a result of an outer
5587  * join), then we can't try to push the join down because we'll fail when
5588  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
5589  * needs to be evaluated *at the top* of this join tree is OK, because we
5590  * can do that locally after fetching the results from the remote side.
5591  */
5592  foreach(lc, root->placeholder_list)
5593  {
5594  PlaceHolderInfo *phinfo = lfirst(lc);
5595  Relids relids;
5596 
5597  /* PlaceHolderInfo refers to parent relids, not child relids. */
5598  relids = IS_OTHER_REL(joinrel) ?
5599  joinrel->top_parent_relids : joinrel->relids;
5600 
5601  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
5602  bms_nonempty_difference(relids, phinfo->ph_eval_at))
5603  return false;
5604  }
5605 
5606  /* Save the join clauses, for later use. */
5607  fpinfo->joinclauses = joinclauses;
5608 
5609  fpinfo->outerrel = outerrel;
5610  fpinfo->innerrel = innerrel;
5611  fpinfo->jointype = jointype;
5612 
5613  /*
5614  * By default, both the input relations are not required to be deparsed as
5615  * subqueries, but there might be some relations covered by the input
5616  * relations that are required to be deparsed as subqueries, so save the
5617  * relids of those relations for later use by the deparser.
5618  */
5619  fpinfo->make_outerrel_subquery = false;
5620  fpinfo->make_innerrel_subquery = false;
5621  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
5622  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
5624  fpinfo_i->lower_subquery_rels);
5625 
5626  /*
5627  * Pull the other remote conditions from the joining relations into join
5628  * clauses or other remote clauses (remote_conds) of this relation
5629  * wherever possible. This avoids building subqueries at every join step.
5630  *
5631  * For an inner join, clauses from both the relations are added to the
5632  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
5633  * the outer side are added to remote_conds since those can be evaluated
5634  * after the join is evaluated. The clauses from inner side are added to
5635  * the joinclauses, since they need to be evaluated while constructing the
5636  * join.
5637  *
5638  * For a FULL OUTER JOIN, the other clauses from either relation can not
5639  * be added to the joinclauses or remote_conds, since each relation acts
5640  * as an outer relation for the other.
5641  *
5642  * The joining sides can not have local conditions, thus no need to test
5643  * shippability of the clauses being pulled up.
5644  */
5645  switch (jointype)
5646  {
5647  case JOIN_INNER:
5648  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5649  fpinfo_i->remote_conds);
5650  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5651  fpinfo_o->remote_conds);
5652  break;
5653 
5654  case JOIN_LEFT:
5655  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5656  fpinfo_i->remote_conds);
5657  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5658  fpinfo_o->remote_conds);
5659  break;
5660 
5661  case JOIN_RIGHT:
5662  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5663  fpinfo_o->remote_conds);
5664  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5665  fpinfo_i->remote_conds);
5666  break;
5667 
5668  case JOIN_FULL:
5669 
5670  /*
5671  * In this case, if any of the input relations has conditions, we
5672  * need to deparse that relation as a subquery so that the
5673  * conditions can be evaluated before the join. Remember it in
5674  * the fpinfo of this relation so that the deparser can take
5675  * appropriate action. Also, save the relids of base relations
5676  * covered by that relation for later use by the deparser.
5677  */
5678  if (fpinfo_o->remote_conds)
5679  {
5680  fpinfo->make_outerrel_subquery = true;
5681  fpinfo->lower_subquery_rels =
5683  outerrel->relids);
5684  }
5685  if (fpinfo_i->remote_conds)
5686  {
5687  fpinfo->make_innerrel_subquery = true;
5688  fpinfo->lower_subquery_rels =
5690  innerrel->relids);
5691  }
5692  break;
5693 
5694  default:
5695  /* Should not happen, we have just checked this above */
5696  elog(ERROR, "unsupported join type %d", jointype);
5697  }
5698 
5699  /*
5700  * For an inner join, all restrictions can be treated alike. Treating the
5701  * pushed down conditions as join conditions allows a top level full outer
5702  * join to be deparsed without requiring subqueries.
5703  */
5704  if (jointype == JOIN_INNER)
5705  {
5706  Assert(!fpinfo->joinclauses);
5707  fpinfo->joinclauses = fpinfo->remote_conds;
5708  fpinfo->remote_conds = NIL;
5709  }
5710 
5711  /* Mark that this join can be pushed down safely */
5712  fpinfo->pushdown_safe = true;
5713 
5714  /* Get user mapping */
5715  if (fpinfo->use_remote_estimate)
5716  {
5717  if (fpinfo_o->use_remote_estimate)
5718  fpinfo->user = fpinfo_o->user;
5719  else
5720  fpinfo->user = fpinfo_i->user;
5721  }
5722  else
5723  fpinfo->user = NULL;
5724 
5725  /*
5726  * Set # of retrieved rows and cached relation costs to some negative
5727  * value, so that we can detect when they are set to some sensible values,
5728  * during one (usually the first) of the calls to estimate_path_cost_size.
5729  */
5730  fpinfo->retrieved_rows = -1;
5731  fpinfo->rel_startup_cost = -1;
5732  fpinfo->rel_total_cost = -1;
5733 
5734  /*
5735  * Set the string describing this join relation to be used in EXPLAIN
5736  * output of corresponding ForeignScan. Note that the decoration we add
5737  * to the base relation names mustn't include any digits, or it'll confuse
5738  * postgresExplainForeignScan.
5739  */
5740  fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
5741  fpinfo_o->relation_name,
5742  get_jointype_name(fpinfo->jointype),
5743  fpinfo_i->relation_name);
5744 
5745  /*
5746  * Set the relation index. This is defined as the position of this
5747  * joinrel in the join_rel_list list plus the length of the rtable list.
5748  * Note that since this joinrel is at the end of the join_rel_list list
5749  * when we are called, we can get the position by list_length.
5750  */
5751  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
5752  fpinfo->relation_index =
5754 
5755  return true;
5756 }
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:162
Relids ph_eval_at
Definition: pathnodes.h:2413
#define IS_OTHER_REL(rel)
Definition: pathnodes.h:669
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
ForeignServer * server
Definition: postgres_fdw.h:86
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:753
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:229
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:1502
Relids relids
Definition: pathnodes.h:681
List * lappend(List *list, void *datum)
Definition: list.c:336
Expr * clause
Definition: pathnodes.h:2056
UserMapping * user
Definition: postgres_fdw.h:87
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: pathnodes.h:2139
void * fdw_private
Definition: pathnodes.h:737
#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:233
#define elog(elevel,...)
Definition: elog.h:232
List * placeholder_list
Definition: pathnodes.h:290
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:756
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 7439 of file postgres_fdw.c.

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

Referenced by create_foreign_modify(), and postgresGetForeignModifyBatchSize().

7440 {
7441  Oid foreigntableid = RelationGetRelid(rel);
7442  ForeignTable *table;
7443  ForeignServer *server;
7444  List *options;
7445  ListCell *lc;
7446 
7447  /* we use 1 by default, which means "no batching" */
7448  int batch_size = 1;
7449 
7450  /*
7451  * Load options for table and server. We append server options after table
7452  * options, because table options take precedence.
7453  */
7454  table = GetForeignTable(foreigntableid);
7455  server = GetForeignServer(table->serverid);
7456 
7457  options = NIL;
7458  options = list_concat(options, table->options);
7459  options = list_concat(options, server->options);
7460 
7461  /* See if either table or server specifies batch_size. */
7462  foreach(lc, options)
7463  {
7464  DefElem *def = (DefElem *) lfirst(lc);
7465 
7466  if (strcmp(def->defname, "batch_size") == 0)
7467  {
7468  (void) parse_int(defGetString(def), &batch_size, 0, NULL);
7469  break;
7470  }
7471  }
7472 
7473  return batch_size;
7474 }
#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
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:6859
#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:758
Definition: pg_list.h:50
List * options
Definition: foreign.h:42
#define RelationGetRelid(relation)
Definition: rel.h:477

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

3556 {
3557  PGresult *volatile res = NULL;
3558 
3559  /* PGresult must be released before leaving this function. */
3560  PG_TRY();
3561  {
3562  char *line;
3563  char *p;
3564  int n;
3565 
3566  /*
3567  * Execute EXPLAIN remotely.
3568  */
3569  res = pgfdw_exec_query(conn, sql, NULL);
3570  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3571  pgfdw_report_error(ERROR, res, conn, false, sql);
3572 
3573  /*
3574  * Extract cost numbers for topmost plan node. Note we search for a
3575  * left paren from the end of the line to avoid being confused by
3576  * other uses of parentheses.
3577  */
3578  line = PQgetvalue(res, 0, 0);
3579  p = strrchr(line, '(');
3580  if (p == NULL)
3581  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3582  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3583  startup_cost, total_cost, rows, width);
3584  if (n != 4)
3585  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3586  }
3587  PG_FINALLY();
3588  {
3589  if (res)
3590  PQclear(res);
3591  }
3592  PG_END_TRY();
3593 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3642
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:702
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
#define ERROR
Definition: elog.h:46
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:795
#define PG_FINALLY()
Definition: elog.h:330
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#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 4563 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().

4564 {
4566  EState *estate = node->ss.ps.state;
4567  ResultRelInfo *resultRelInfo = node->resultRelInfo;
4568  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4569  TupleTableSlot *resultSlot;
4570 
4571  Assert(resultRelInfo->ri_projectReturning);
4572 
4573  /* If we didn't get any tuples, must be end of data. */
4574  if (dmstate->next_tuple >= dmstate->num_tuples)
4575  return ExecClearTuple(slot);
4576 
4577  /* Increment the command es_processed count if necessary. */
4578  if (dmstate->set_processed)
4579  estate->es_processed += 1;
4580 
4581  /*
4582  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4583  * tuple. (has_returning is false when the local query is of the form
4584  * "UPDATE/DELETE .. RETURNING 1" for example.)
4585  */
4586  if (!dmstate->has_returning)
4587  {
4588  ExecStoreAllNullTuple(slot);
4589  resultSlot = slot;
4590  }
4591  else
4592  {
4593  /*
4594  * On error, be sure to release the PGresult on the way out. Callers
4595  * do not have PG_TRY blocks to ensure this happens.
4596  */
4597  PG_TRY();
4598  {
4599  HeapTuple newtup;
4600 
4601  newtup = make_tuple_from_result_row(dmstate->result,
4602  dmstate->next_tuple,
4603  dmstate->rel,
4604  dmstate->attinmeta,
4605  dmstate->retrieved_attrs,
4606  node,
4607  dmstate->temp_cxt);
4608  ExecStoreHeapTuple(newtup, slot, false);
4609  }
4610  PG_CATCH();
4611  {
4612  if (dmstate->result)
4613  PQclear(dmstate->result);
4614  PG_RE_THROW();
4615  }
4616  PG_END_TRY();
4617 
4618  /* Get the updated/deleted tuple. */
4619  if (dmstate->rel)
4620  resultSlot = slot;
4621  else
4622  resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4623  }
4624  dmstate->next_tuple++;
4625 
4626  /* Make slot available for evaluation of the local query RETURNING list. */
4627  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4628  resultSlot;
4629 
4630  return slot;
4631 }
ScanState ss
Definition: execnodes.h:1858
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:1381
EState * state
Definition: execnodes.h:969
PlanState ps
Definition: execnodes.h:1378
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1861
MemoryContext temp_cxt
Definition: postgres_fdw.c:247
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#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_tupdesc_for_join_scan_tuples()

static TupleDesc get_tupdesc_for_join_scan_tuples ( ForeignScanState node)
static

Definition at line 1440 of file postgres_fdw.c.

References CreateTupleDescCopy(), ForeignScan::fdw_scan_tlist, get_rel_type_id(), i, IsA, list_nth(), list_nth_node, TupleDescData::natts, OidIsValid, PlanState::plan, ScanState::ps, RangeTblEntry::relid, RTE_RELATION, RangeTblEntry::rtekind, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, TupleTableSlot::tts_tupleDescriptor, PgFdwScanState::tupdesc, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by postgresBeginDirectModify(), and postgresBeginForeignScan().

1441 {
1442  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1443  EState *estate = node->ss.ps.state;
1444  TupleDesc tupdesc;
1445 
1446  /*
1447  * The core code has already set up a scan tuple slot based on
1448  * fsplan->fdw_scan_tlist, and this slot's tupdesc is mostly good enough,
1449  * but there's one case where it isn't. If we have any whole-row row
1450  * identifier Vars, they may have vartype RECORD, and we need to replace
1451  * that with the associated table's actual composite type. This ensures
1452  * that when we read those ROW() expression values from the remote server,
1453  * we can convert them to a composite type the local server knows.
1454  */
1456  for (int i = 0; i < tupdesc->natts; i++)
1457  {
1458  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
1459  Var *var;
1460  RangeTblEntry *rte;
1461  Oid reltype;
1462 
1463  /* Nothing to do if it's not a generic RECORD attribute */
1464  if (att->atttypid != RECORDOID || att->atttypmod >= 0)
1465  continue;
1466 
1467  /*
1468  * If we can't identify the referenced table, do nothing. This'll
1469  * likely lead to failure later, but perhaps we can muddle through.
1470  */
1471  var = (Var *) list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
1472  i)->expr;
1473  if (!IsA(var, Var) || var->varattno != 0)
1474  continue;
1475  rte = list_nth(estate->es_range_table, var->varno - 1);
1476  if (rte->rtekind != RTE_RELATION)
1477  continue;
1478  reltype = get_rel_type_id(rte->relid);
1479  if (!OidIsValid(reltype))
1480  continue;
1481  att->atttypid = reltype;
1482  /* shouldn't need to change anything else */
1483  }
1484  return tupdesc;
1485 }
int varno
Definition: primnodes.h:189
ScanState ss
Definition: execnodes.h:1858
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:111
#define IsA(nodeptr, _type_)
Definition: nodes.h:587
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
AttrNumber varattno
Definition: primnodes.h:191
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1381
List * fdw_scan_tlist
Definition: plannodes.h:642
EState * state
Definition: execnodes.h:969
unsigned int Oid
Definition: postgres_ext.h:31
Definition: primnodes.h:186
#define OidIsValid(objectId)
Definition: