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 "catalog/pg_opfamily.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)
 
EquivalenceMemberfind_em_for_rel (PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 
EquivalenceMemberfind_em_for_rel_target (PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 

Variables

 PG_MODULE_MAGIC
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 62 of file postgres_fdw.c.

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 56 of file postgres_fdw.c.

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.01

Definition at line 59 of file postgres_fdw.c.

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 122 of file postgres_fdw.c.

123 {
124  /* SQL statement to execute remotely (as a String node) */
126  /* has-returning flag (as a Boolean node) */
128  /* Integer list of attribute numbers retrieved by RETURNING */
130  /* set-processed flag (as a Boolean node) */
132 };
@ FdwDirectModifyPrivateSetProcessed
Definition: postgres_fdw.c:131
@ FdwDirectModifyPrivateHasReturning
Definition: postgres_fdw.c:127
@ FdwDirectModifyPrivateRetrievedAttrs
Definition: postgres_fdw.c:129
@ FdwDirectModifyPrivateUpdateSql
Definition: postgres_fdw.c:125

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 99 of file postgres_fdw.c.

100 {
101  /* SQL statement to execute remotely (as a String node) */
103  /* Integer list of target attribute numbers for INSERT/UPDATE */
105  /* Length till the end of VALUES clause (as an Integer node) */
107  /* has-returning flag (as a Boolean node) */
109  /* Integer list of attribute numbers retrieved by RETURNING */
111 };
@ FdwModifyPrivateLen
Definition: postgres_fdw.c:106
@ FdwModifyPrivateUpdateSql
Definition: postgres_fdw.c:102
@ FdwModifyPrivateTargetAttnums
Definition: postgres_fdw.c:104
@ FdwModifyPrivateRetrievedAttrs
Definition: postgres_fdw.c:110
@ FdwModifyPrivateHasReturning
Definition: postgres_fdw.c:108

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 282 of file postgres_fdw.c.

283 {
284  /* has-final-sort flag (as a Boolean node) */
286  /* has-limit flag (as a Boolean node) */
288 };
@ FdwPathPrivateHasLimit
Definition: postgres_fdw.c:287
@ FdwPathPrivateHasFinalSort
Definition: postgres_fdw.c:285

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 71 of file postgres_fdw.c.

72 {
73  /* SQL statement to execute remotely (as a String node) */
75  /* Integer list of attribute numbers retrieved by the SELECT */
77  /* Integer representing the desired fetch_size */
79 
80  /*
81  * String describing join i.e. names of relations being joined and types
82  * of join, added when the scan is join
83  */
85 };
@ FdwScanPrivateRetrievedAttrs
Definition: postgres_fdw.c:76
@ FdwScanPrivateSelectSql
Definition: postgres_fdw.c:74
@ FdwScanPrivateFetchSize
Definition: postgres_fdw.c:78
@ FdwScanPrivateRelations
Definition: postgres_fdw.c: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 6618 of file postgres_fdw.c.

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

References add_path(), Assert(), CMD_SELECT, PgFdwPathExtraData::count_est, FinalPathExtraData::count_est, create_foreign_upper_path(), estimate_path_cost_size(), RelOptInfo::fdw_private, PgFdwPathExtraData::has_final_sort, PgFdwPathExtraData::has_limit, is_foreign_expr(), IsA, lfirst, FinalPathExtraData::limit_needed, PgFdwPathExtraData::limit_tuples, FinalPathExtraData::limit_tuples, list_make2, PgFdwRelationInfo::local_conds, makeBoolean(), merge_fdw_options(), NIL, PgFdwPathExtraData::offset_est, FinalPathExtraData::offset_est, PgFdwRelationInfo::outerrel, palloc0(), Path::parent, parse(), PlannerInfo::parse, Path::pathkeys, RelOptInfo::pathlist, Path::pathtarget, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, Path::rows, PgFdwRelationInfo::server, PlannerInfo::sort_pathkeys, PgFdwRelationInfo::stage, Path::startup_cost, subpath(), PgFdwRelationInfo::table, PgFdwPathExtraData::target, Path::total_cost, PlannerInfo::upper_targets, UPPERREL_FINAL, UPPERREL_GROUP_AGG, UPPERREL_ORDERED, PgFdwRelationInfo::use_remote_estimate, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignUpperPaths().

◆ add_foreign_grouping_paths()

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

Definition at line 6394 of file postgres_fdw.c.

6397 {
6398  Query *parse = root->parse;
6399  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6400  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
6401  ForeignPath *grouppath;
6402  double rows;
6403  int width;
6404  Cost startup_cost;
6405  Cost total_cost;
6406 
6407  /* Nothing to be done, if there is no grouping or aggregation required. */
6408  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
6409  !root->hasHavingQual)
6410  return;
6411 
6414 
6415  /* save the input_rel as outerrel in fpinfo */
6416  fpinfo->outerrel = input_rel;
6417 
6418  /*
6419  * Copy foreign table, foreign server, user mapping, FDW options etc.
6420  * details from the input relation's fpinfo.
6421  */
6422  fpinfo->table = ifpinfo->table;
6423  fpinfo->server = ifpinfo->server;
6424  fpinfo->user = ifpinfo->user;
6425  merge_fdw_options(fpinfo, ifpinfo, NULL);
6426 
6427  /*
6428  * Assess if it is safe to push down aggregation and grouping.
6429  *
6430  * Use HAVING qual from extra. In case of child partition, it will have
6431  * translated Vars.
6432  */
6433  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
6434  return;
6435 
6436  /*
6437  * Compute the selectivity and cost of the local_conds, so we don't have
6438  * to do it over again for each path. (Currently we create just a single
6439  * path here, but in future it would be possible that we build more paths
6440  * such as pre-sorted paths as in postgresGetForeignPaths and
6441  * postgresGetForeignJoinPaths.) The best we can do for these conditions
6442  * is to estimate selectivity on the basis of local statistics.
6443  */
6444  fpinfo->local_conds_sel = clauselist_selectivity(root,
6445  fpinfo->local_conds,
6446  0,
6447  JOIN_INNER,
6448  NULL);
6449 
6450  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6451 
6452  /* Estimate the cost of push down */
6453  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
6454  &rows, &width, &startup_cost, &total_cost);
6455 
6456  /* Now update this information in the fpinfo */
6457  fpinfo->rows = rows;
6458  fpinfo->width = width;
6459  fpinfo->startup_cost = startup_cost;
6460  fpinfo->total_cost = total_cost;
6461 
6462  /* Create and add foreign path to the grouping relation. */
6463  grouppath = create_foreign_upper_path(root,
6464  grouped_rel,
6465  grouped_rel->reltarget,
6466  rows,
6467  startup_cost,
6468  total_cost,
6469  NIL, /* no pathkeys */
6470  NULL,
6471  NIL); /* no fdw_private */
6472 
6473  /* Add generated path into grouped_rel by add_path(). */
6474  add_path(grouped_rel, (Path *) grouppath);
6475 }
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:102
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:4667
@ JOIN_INNER
Definition: nodes.h:749
@ PARTITIONWISE_AGGREGATE_FULL
Definition: pathnodes.h:2590
@ PARTITIONWISE_AGGREGATE_NONE
Definition: pathnodes.h:2589
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
PartitionwiseAggregateType patype
Definition: pathnodes.h:2619
Selectivity local_conds_sel
Definition: postgres_fdw.h:57
QualCost local_conds_cost
Definition: postgres_fdw.h:56
bool hasHavingQual
Definition: pathnodes.h:349
struct PathTarget * reltarget
Definition: pathnodes.h:693

References add_path(), Assert(), clauselist_selectivity(), cost_qual_eval(), create_foreign_upper_path(), estimate_path_cost_size(), RelOptInfo::fdw_private, foreign_grouping_ok(), 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().

◆ add_foreign_ordered_paths()

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

Definition at line 6485 of file postgres_fdw.c.

6487 {
6488  Query *parse = root->parse;
6489  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6490  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
6491  PgFdwPathExtraData *fpextra;
6492  double rows;
6493  int width;
6494  Cost startup_cost;
6495  Cost total_cost;
6496  List *fdw_private;
6497  ForeignPath *ordered_path;
6498  ListCell *lc;
6499 
6500  /* Shouldn't get here unless the query has ORDER BY */
6501  Assert(parse->sortClause);
6502 
6503  /* We don't support cases where there are any SRFs in the targetlist */
6504  if (parse->hasTargetSRFs)
6505  return;
6506 
6507  /* Save the input_rel as outerrel in fpinfo */
6508  fpinfo->outerrel = input_rel;
6509 
6510  /*
6511  * Copy foreign table, foreign server, user mapping, FDW options etc.
6512  * details from the input relation's fpinfo.
6513  */
6514  fpinfo->table = ifpinfo->table;
6515  fpinfo->server = ifpinfo->server;
6516  fpinfo->user = ifpinfo->user;
6517  merge_fdw_options(fpinfo, ifpinfo, NULL);
6518 
6519  /*
6520  * If the input_rel is a base or join relation, we would already have
6521  * considered pushing down the final sort to the remote server when
6522  * creating pre-sorted foreign paths for that relation, because the
6523  * query_pathkeys is set to the root->sort_pathkeys in that case (see
6524  * standard_qp_callback()).
6525  */
6526  if (input_rel->reloptkind == RELOPT_BASEREL ||
6527  input_rel->reloptkind == RELOPT_JOINREL)
6528  {
6529  Assert(root->query_pathkeys == root->sort_pathkeys);
6530 
6531  /* Safe to push down if the query_pathkeys is safe to push down */
6532  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
6533 
6534  return;
6535  }
6536 
6537  /* The input_rel should be a grouping relation */
6538  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
6539  ifpinfo->stage == UPPERREL_GROUP_AGG);
6540 
6541  /*
6542  * We try to create a path below by extending a simple foreign path for
6543  * the underlying grouping relation to perform the final sort remotely,
6544  * which is stored into the fdw_private list of the resulting path.
6545  */
6546 
6547  /* Assess if it is safe to push down the final sort */
6548  foreach(lc, root->sort_pathkeys)
6549  {
6550  PathKey *pathkey = (PathKey *) lfirst(lc);
6551  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6552 
6553  /*
6554  * is_foreign_expr would detect volatile expressions as well, but
6555  * checking ec_has_volatile here saves some cycles.
6556  */
6557  if (pathkey_ec->ec_has_volatile)
6558  return;
6559 
6560  /*
6561  * Can't push down the sort if pathkey's opfamily is not shippable.
6562  */
6563  if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId,
6564  fpinfo))
6565  return;
6566 
6567  /*
6568  * The EC must contain a shippable EM that is computed in input_rel's
6569  * reltarget, else we can't push down the sort.
6570  */
6571  if (find_em_for_rel_target(root,
6572  pathkey_ec,
6573  input_rel) == NULL)
6574  return;
6575  }
6576 
6577  /* Safe to push down */
6578  fpinfo->pushdown_safe = true;
6579 
6580  /* Construct PgFdwPathExtraData */
6581  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6582  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6583  fpextra->has_final_sort = true;
6584 
6585  /* Estimate the costs of performing the final sort remotely */
6586  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6587  &rows, &width, &startup_cost, &total_cost);
6588 
6589  /*
6590  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6591  * Items in the list must match order in enum FdwPathPrivateIndex.
6592  */
6593  fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
6594 
6595  /* Create foreign ordering path */
6596  ordered_path = create_foreign_upper_path(root,
6597  input_rel,
6599  rows,
6600  startup_cost,
6601  total_cost,
6602  root->sort_pathkeys,
6603  NULL, /* no extra plan */
6604  fdw_private);
6605 
6606  /* and add it to the ordered_rel */
6607  add_path(ordered_rel, (Path *) ordered_path);
6608 }
EquivalenceMember * find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
bool is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
Definition: shippable.c:162
bool ec_has_volatile
Definition: pathnodes.h:997
EquivalenceClass * pk_eclass
Definition: pathnodes.h:1068
Oid pk_opfamily
Definition: pathnodes.h:1069
List * query_pathkeys
Definition: pathnodes.h:295

References add_path(), Assert(), create_foreign_upper_path(), EquivalenceClass::ec_has_volatile, estimate_path_cost_size(), RelOptInfo::fdw_private, find_em_for_rel_target(), PgFdwPathExtraData::has_final_sort, is_shippable(), lfirst, list_make2, makeBoolean(), merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, palloc0(), parse(), PlannerInfo::parse, PathKey::pk_eclass, PathKey::pk_opfamily, PgFdwRelationInfo::pushdown_safe, PgFdwRelationInfo::qp_is_pushdown_safe, PlannerInfo::query_pathkeys, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, PgFdwRelationInfo::server, PlannerInfo::sort_pathkeys, PgFdwRelationInfo::stage, PgFdwRelationInfo::table, PgFdwPathExtraData::target, PlannerInfo::upper_targets, UPPERREL_GROUP_AGG, UPPERREL_ORDERED, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignUpperPaths().

◆ 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 5777 of file postgres_fdw.c.

5779 {
5780  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
5781  ListCell *lc;
5782 
5783  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
5784 
5785  /* Create one path for each set of pathkeys we found above. */
5786  foreach(lc, useful_pathkeys_list)
5787  {
5788  double rows;
5789  int width;
5790  Cost startup_cost;
5791  Cost total_cost;
5792  List *useful_pathkeys = lfirst(lc);
5793  Path *sorted_epq_path;
5794 
5795  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
5796  &rows, &width, &startup_cost, &total_cost);
5797 
5798  /*
5799  * The EPQ path must be at least as well sorted as the path itself, in
5800  * case it gets used as input to a mergejoin.
5801  */
5802  sorted_epq_path = epq_path;
5803  if (sorted_epq_path != NULL &&
5804  !pathkeys_contained_in(useful_pathkeys,
5805  sorted_epq_path->pathkeys))
5806  sorted_epq_path = (Path *)
5807  create_sort_path(root,
5808  rel,
5809  sorted_epq_path,
5810  useful_pathkeys,
5811  -1.0);
5812 
5813  if (IS_SIMPLE_REL(rel))
5814  add_path(rel, (Path *)
5815  create_foreignscan_path(root, rel,
5816  NULL,
5817  rows,
5818  startup_cost,
5819  total_cost,
5820  useful_pathkeys,
5821  rel->lateral_relids,
5822  sorted_epq_path,
5823  NIL));
5824  else
5825  add_path(rel, (Path *)
5826  create_foreign_join_path(root, rel,
5827  NULL,
5828  rows,
5829  startup_cost,
5830  total_cost,
5831  useful_pathkeys,
5832  rel->lateral_relids,
5833  sorted_epq_path,
5834  NIL));
5835  }
5836 }
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:329
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2942
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:2207
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:2251
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:655
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:902
Relids lateral_relids
Definition: pathnodes.h:707

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

◆ 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 3618 of file postgres_fdw.c.

3625 {
3626  /*
3627  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3628  * side is unlikely to generate properly-sorted output, so it would need
3629  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3630  * if the GROUP BY clause is sort-able but isn't a superset of the given
3631  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3632  * costs by applying the same heuristic as for the scan or join case.
3633  */
3634  if (!grouping_is_sortable(root->parse->groupClause) ||
3635  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3636  {
3637  Path sort_path; /* dummy for result of cost_sort */
3638 
3639  cost_sort(&sort_path,
3640  root,
3641  pathkeys,
3642  *p_startup_cost + *p_run_cost,
3643  retrieved_rows,
3644  width,
3645  0.0,
3646  work_mem,
3647  limit_tuples);
3648 
3649  *p_startup_cost = sort_path.startup_cost;
3650  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3651  }
3652  else
3653  {
3654  /*
3655  * The default extra cost seems too large for foreign-grouping cases;
3656  * add 1/4th of that default.
3657  */
3658  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3659  - 1.0) * 0.25;
3660 
3661  *p_startup_cost *= sort_multiplier;
3662  *p_run_cost *= sort_multiplier;
3663  }
3664 }
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:2395
int work_mem
Definition: globals.c:125
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:62
List * group_pathkeys
Definition: pathnodes.h:297
List * groupClause
Definition: parsenodes.h:163
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:529

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

◆ analyze_row_processor()

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

Definition at line 5148 of file postgres_fdw.c.

5149 {
5150  int targrows = astate->targrows;
5151  int pos; /* array index to store tuple in */
5152  MemoryContext oldcontext;
5153 
5154  /* Always increment sample row counter. */
5155  astate->samplerows += 1;
5156 
5157  /*
5158  * Determine the slot where this sample row should be stored. Set pos to
5159  * negative value to indicate the row should be skipped.
5160  */
5161  if (astate->numrows < targrows)
5162  {
5163  /* First targrows rows are always included into the sample */
5164  pos = astate->numrows++;
5165  }
5166  else
5167  {
5168  /*
5169  * Now we start replacing tuples in the sample until we reach the end
5170  * of the relation. Same algorithm as in acquire_sample_rows in
5171  * analyze.c; see Jeff Vitter's paper.
5172  */
5173  if (astate->rowstoskip < 0)
5174  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5175 
5176  if (astate->rowstoskip <= 0)
5177  {
5178  /* Choose a random reservoir element to replace. */
5179  pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5180  Assert(pos >= 0 && pos < targrows);
5181  heap_freetuple(astate->rows[pos]);
5182  }
5183  else
5184  {
5185  /* Skip this tuple. */
5186  pos = -1;
5187  }
5188 
5189  astate->rowstoskip -= 1;
5190  }
5191 
5192  if (pos >= 0)
5193  {
5194  /*
5195  * Create sample tuple from current result row, and store it in the
5196  * position determined above. The tuple has to be created in anl_cxt.
5197  */
5198  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5199 
5200  astate->rows[pos] = make_tuple_from_result_row(res, row,
5201  astate->rel,
5202  astate->attinmeta,
5203  astate->retrieved_attrs,
5204  NULL,
5205  astate->temp_cxt);
5206 
5207  MemoryContextSwitchTo(oldcontext);
5208  }
5209 }
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
double sampler_random_fract(pg_prng_state *randstate)
Definition: sampling.c:241
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:147
ReservoirStateData rstate
Definition: postgres_fdw.c:268
AttInMetadata * attinmeta
Definition: postgres_fdw.c:257
MemoryContext anl_cxt
Definition: postgres_fdw.c:271
HeapTuple * rows
Definition: postgres_fdw.c:261
MemoryContext temp_cxt
Definition: postgres_fdw.c:272
pg_prng_state randstate
Definition: sampling.h:49

References PgFdwAnalyzeState::anl_cxt, Assert(), PgFdwAnalyzeState::attinmeta, heap_freetuple(), make_tuple_from_result_row(), MemoryContextSwitchTo(), PgFdwAnalyzeState::numrows, ReservoirStateData::randstate, PgFdwAnalyzeState::rel, res, 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().

◆ apply_returning_filter()

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

Definition at line 4728 of file postgres_fdw.c.

4732 {
4733  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4734  TupleTableSlot *resultSlot;
4735  Datum *values;
4736  bool *isnull;
4737  Datum *old_values;
4738  bool *old_isnull;
4739  int i;
4740 
4741  /*
4742  * Use the return tuple slot as a place to store the result tuple.
4743  */
4744  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4745 
4746  /*
4747  * Extract all the values of the scan tuple.
4748  */
4749  slot_getallattrs(slot);
4750  old_values = slot->tts_values;
4751  old_isnull = slot->tts_isnull;
4752 
4753  /*
4754  * Prepare to build the result tuple.
4755  */
4756  ExecClearTuple(resultSlot);
4757  values = resultSlot->tts_values;
4758  isnull = resultSlot->tts_isnull;
4759 
4760  /*
4761  * Transpose data into proper fields of the result tuple.
4762  */
4763  for (i = 0; i < resultTupType->natts; i++)
4764  {
4765  int j = dmstate->attnoMap[i];
4766 
4767  if (j == 0)
4768  {
4769  values[i] = (Datum) 0;
4770  isnull[i] = true;
4771  }
4772  else
4773  {
4774  values[i] = old_values[j - 1];
4775  isnull[i] = old_isnull[j - 1];
4776  }
4777  }
4778 
4779  /*
4780  * Build the virtual tuple.
4781  */
4782  ExecStoreVirtualTuple(resultSlot);
4783 
4784  /*
4785  * If we have any system columns to return, materialize a heap tuple in
4786  * the slot from column values set above and install system columns in
4787  * that tuple.
4788  */
4789  if (dmstate->hasSystemCols)
4790  {
4791  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4792 
4793  /* ctid */
4794  if (dmstate->ctidAttno)
4795  {
4796  ItemPointer ctid = NULL;
4797 
4798  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4799  resultTup->t_self = *ctid;
4800  }
4801 
4802  /*
4803  * And remaining columns
4804  *
4805  * Note: since we currently don't allow the target relation to appear
4806  * on the nullable side of an outer join, any system columns wouldn't
4807  * go to NULL.
4808  *
4809  * Note: no need to care about tableoid here because it will be
4810  * initialized in ExecProcessReturning().
4811  */
4815  }
4816 
4817  /*
4818  * And return the result tuple.
4819  */
4820  return resultSlot;
4821 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1552
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1644
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1210
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:314
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:375
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:392
int j
Definition: isn.c:74
int i
Definition: isn.c:73
ItemPointerData * ItemPointer
Definition: itemptr.h:49
uintptr_t Datum
Definition: postgres.h:411
#define DatumGetPointer(X)
Definition: postgres.h:593
#define RelationGetDescr(relation)
Definition: rel.h:515
ItemPointerData t_self
Definition: htup.h:65
HeapTupleHeader t_data
Definition: htup.h:68
bool * tts_isnull
Definition: tuptable.h:128
Datum * tts_values
Definition: tuptable.h:126
#define InvalidTransactionId
Definition: transam.h:31
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:354

References PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, DatumGetPointer, ExecClearTuple(), ExecFetchSlotHeapTuple(), ExecGetReturningSlot(), ExecStoreVirtualTuple(), PgFdwDirectModifyState::hasSystemCols, HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, i, InvalidTransactionId, j, 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().

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 5844 of file postgres_fdw.c.

5845 {
5846  ListCell *lc;
5847 
5848  foreach(lc, fpinfo->server->options)
5849  {
5850  DefElem *def = (DefElem *) lfirst(lc);
5851 
5852  if (strcmp(def->defname, "use_remote_estimate") == 0)
5853  fpinfo->use_remote_estimate = defGetBoolean(def);
5854  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
5855  (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
5856  NULL);
5857  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
5858  (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
5859  NULL);
5860  else if (strcmp(def->defname, "extensions") == 0)
5861  fpinfo->shippable_extensions =
5862  ExtractExtensionList(defGetString(def), false);
5863  else if (strcmp(def->defname, "fetch_size") == 0)
5864  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
5865  else if (strcmp(def->defname, "async_capable") == 0)
5866  fpinfo->async_capable = defGetBoolean(def);
5867  }
5868 }
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:413
bool defGetBoolean(DefElem *def)
Definition: define.c:108
char * defGetString(DefElem *def)
Definition: define.c:49
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:7098
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:7188
char * defname
Definition: parsenodes.h:765
List * options
Definition: foreign.h:42
List * shippable_extensions
Definition: postgres_fdw.h:81

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

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 5876 of file postgres_fdw.c.

5877 {
5878  ListCell *lc;
5879 
5880  foreach(lc, fpinfo->table->options)
5881  {
5882  DefElem *def = (DefElem *) lfirst(lc);
5883 
5884  if (strcmp(def->defname, "use_remote_estimate") == 0)
5885  fpinfo->use_remote_estimate = defGetBoolean(def);
5886  else if (strcmp(def->defname, "fetch_size") == 0)
5887  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
5888  else if (strcmp(def->defname, "async_capable") == 0)
5889  fpinfo->async_capable = defGetBoolean(def);
5890  }
5891 }
List * options
Definition: foreign.h:57

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

◆ build_remote_returning()

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

Definition at line 4400 of file postgres_fdw.c.

4401 {
4402  bool have_wholerow = false;
4403  List *tlist = NIL;
4404  List *vars;
4405  ListCell *lc;
4406 
4407  Assert(returningList);
4408 
4409  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4410 
4411  /*
4412  * If there's a whole-row reference to the target relation, then we'll
4413  * need all the columns of the relation.
4414  */
4415  foreach(lc, vars)
4416  {
4417  Var *var = (Var *) lfirst(lc);
4418 
4419  if (IsA(var, Var) &&
4420  var->varno == rtindex &&
4421  var->varattno == InvalidAttrNumber)
4422  {
4423  have_wholerow = true;
4424  break;
4425  }
4426  }
4427 
4428  if (have_wholerow)
4429  {
4430  TupleDesc tupdesc = RelationGetDescr(rel);
4431  int i;
4432 
4433  for (i = 1; i <= tupdesc->natts; i++)
4434  {
4435  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4436  Var *var;
4437 
4438  /* Ignore dropped attributes. */
4439  if (attr->attisdropped)
4440  continue;
4441 
4442  var = makeVar(rtindex,
4443  i,
4444  attr->atttypid,
4445  attr->atttypmod,
4446  attr->attcollation,
4447  0);
4448 
4449  tlist = lappend(tlist,
4450  makeTargetEntry((Expr *) var,
4451  list_length(tlist) + 1,
4452  NULL,
4453  false));
4454  }
4455  }
4456 
4457  /* Now add any remaining columns to tlist. */
4458  foreach(lc, vars)
4459  {
4460  Var *var = (Var *) lfirst(lc);
4461 
4462  /*
4463  * No need for whole-row references to the target relation. We don't
4464  * need system columns other than ctid and oid either, since those are
4465  * set locally.
4466  */
4467  if (IsA(var, Var) &&
4468  var->varno == rtindex &&
4469  var->varattno <= InvalidAttrNumber &&
4471  continue; /* don't need it */
4472 
4473  if (tlist_member((Expr *) var, tlist))
4474  continue; /* already got it */
4475 
4476  tlist = lappend(tlist,
4477  makeTargetEntry((Expr *) var,
4478  list_length(tlist) + 1,
4479  NULL,
4480  false));
4481  }
4482 
4483  list_free(vars);
4484 
4485  return tlist;
4486 }
#define InvalidAttrNumber
Definition: attnum.h:23
List * lappend(List *list, void *datum)
Definition: list.c:336
void list_free(List *list)
Definition: list.c:1505
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:239
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
#define PVC_INCLUDE_PLACEHOLDERS
Definition: optimizer.h:192
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
static int list_length(const List *l)
Definition: pg_list.h:149
Definition: nodes.h:574
Definition: primnodes.h:196
AttrNumber varattno
Definition: primnodes.h:200
int varno
Definition: primnodes.h:198
Definition: regcomp.c:238
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:68
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
List * pull_var_clause(Node *node, int flags)
Definition: var.c:604

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(), TupleDescAttr, Var::varattno, and Var::varno.

Referenced by postgresPlanDirectModify().

◆ close_cursor()

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

Definition at line 3918 of file postgres_fdw.c.

3920 {
3921  char sql[64];
3922  PGresult *res;
3923 
3924  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3925 
3926  /*
3927  * We don't use a PG_TRY block here, so be careful not to throw error
3928  * without releasing the PGresult.
3929  */
3930  res = pgfdw_exec_query(conn, sql, conn_state);
3932  pgfdw_report_error(ERROR, res, conn, true, sql);
3933  PQclear(res);
3934 }
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:865
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:772
static unsigned int cursor_number
Definition: connection.c:77
#define ERROR
Definition: elog.h:33
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3270
void PQclear(PGresult *res)
Definition: fe-exec.c:718
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:97
#define snprintf
Definition: port.h:225
PGconn * conn
Definition: streamutil.c:54

References conn, cursor_number, ERROR, pgfdw_exec_query(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), res, and snprintf.

Referenced by postgresAcquireSampleRowsFunc(), and postgresEndForeignScan().

◆ complete_pending_request()

static void complete_pending_request ( AsyncRequest areq)
static

Definition at line 7117 of file postgres_fdw.c.

7118 {
7119  /* The request would have been pending for a callback */
7120  Assert(areq->callback_pending);
7121 
7122  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7123  areq->callback_pending = false;
7124 
7125  /* We begin a fetch afterwards if necessary; don't fetch */
7126  produce_tuple_asynchronously(areq, false);
7127 
7128  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7129  ExecAsyncResponse(areq);
7130 
7131  /* Also, we do instrumentation ourselves, if required */
7132  if (areq->requestee->instrument)
7134  TupIsNull(areq->result) ? 0.0 : 1.0);
7135 }
void ExecAsyncResponse(AsyncRequest *areq)
Definition: execAsync.c:117
void InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
Definition: instrument.c:132
static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch)
TupleTableSlot * result
Definition: execnodes.h:574
bool callback_pending
Definition: execnodes.h:572
struct PlanState * requestee
Definition: execnodes.h:570
Instrumentation * instrument
Definition: execnodes.h:1008
#define TupIsNull(slot)
Definition: tuptable.h:292

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

Referenced by postgresForeignAsyncConfigureWait().

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

Definition at line 7308 of file postgres_fdw.c.

7309 {
7311  Relation rel = errpos->rel;
7312  ForeignScanState *fsstate = errpos->fsstate;
7313  const char *attname = NULL;
7314  const char *relname = NULL;
7315  bool is_wholerow = false;
7316 
7317  /*
7318  * If we're in a scan node, always use aliases from the rangetable, for
7319  * consistency between the simple-relation and remote-join cases. Look at
7320  * the relation's tupdesc only if we're not in a scan node.
7321  */
7322  if (fsstate)
7323  {
7324  /* ForeignScan case */
7325  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7326  int varno = 0;
7327  AttrNumber colno = 0;
7328 
7329  if (fsplan->scan.scanrelid > 0)
7330  {
7331  /* error occurred in a scan against a foreign table */
7332  varno = fsplan->scan.scanrelid;
7333  colno = errpos->cur_attno;
7334  }
7335  else
7336  {
7337  /* error occurred in a scan against a foreign join */
7338  TargetEntry *tle;
7339 
7340  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7341  errpos->cur_attno - 1);
7342 
7343  /*
7344  * Target list can have Vars and expressions. For Vars, we can
7345  * get some information, however for expressions we can't. Thus
7346  * for expressions, just show generic context message.
7347  */
7348  if (IsA(tle->expr, Var))
7349  {
7350  Var *var = (Var *) tle->expr;
7351 
7352  varno = var->varno;
7353  colno = var->varattno;
7354  }
7355  }
7356 
7357  if (varno > 0)
7358  {
7359  EState *estate = fsstate->ss.ps.state;
7360  RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7361 
7362  relname = rte->eref->aliasname;
7363 
7364  if (colno == 0)
7365  is_wholerow = true;
7366  else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7367  attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7368  else if (colno == SelfItemPointerAttributeNumber)
7369  attname = "ctid";
7370  }
7371  }
7372  else if (rel)
7373  {
7374  /* Non-ForeignScan case (we should always have a rel here) */
7375  TupleDesc tupdesc = RelationGetDescr(rel);
7376 
7378  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7379  {
7380  Form_pg_attribute attr = TupleDescAttr(tupdesc,
7381  errpos->cur_attno - 1);
7382 
7383  attname = NameStr(attr->attname);
7384  }
7385  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7386  attname = "ctid";
7387  }
7388 
7389  if (relname && is_wholerow)
7390  errcontext("whole-row reference to foreign table \"%s\"", relname);
7391  else if (relname && attname)
7392  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7393  else
7394  errcontext("processing expression at position %d in select list",
7395  errpos->cur_attno);
7396 }
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:681
#define errcontext
Definition: elog.h:190
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:575
#define castNode(_type_, nodeptr)
Definition: nodes.h:642
NameData attname
Definition: pg_attribute.h:41
void * arg
NameData relname
Definition: pg_class.h:38
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:523
char * aliasname
Definition: primnodes.h:42
List * colnames
Definition: primnodes.h:43
AttrNumber cur_attno
Definition: postgres_fdw.c:306
ForeignScanState * fsstate
Definition: postgres_fdw.c:308
ScanState ss
Definition: execnodes.h:1903
List * fdw_scan_tlist
Definition: plannodes.h:672
Plan * plan
Definition: execnodes.h:998
EState * state
Definition: execnodes.h:1000
Alias * eref
Definition: parsenodes.h:1161
PlanState ps
Definition: execnodes.h:1423
Index scanrelid
Definition: plannodes.h:347
Expr * expr
Definition: primnodes.h:1716
#define strVal(v)
Definition: value.h:72

References Alias::aliasname, arg, 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, ConversionLocation::rel, RelationGetDescr, RelationGetRelationName, relname, ForeignScan::scan, Scan::scanrelid, SelfItemPointerAttributeNumber, ForeignScanState::ss, PlanState::state, strVal, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by make_tuple_from_result_row().

◆ convert_prep_stmt_params()

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

Definition at line 4240 of file postgres_fdw.c.

4244 {
4245  const char **p_values;
4246  int i;
4247  int j;
4248  int pindex = 0;
4249  MemoryContext oldcontext;
4250 
4251  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4252 
4253  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4254 
4255  /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4256  Assert(!(tupleid != NULL && numSlots > 1));
4257 
4258  /* 1st parameter should be ctid, if it's in use */
4259  if (tupleid != NULL)
4260  {
4261  Assert(numSlots == 1);
4262  /* don't need set_transmission_modes for TID output */
4263  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
4264  PointerGetDatum(tupleid));
4265  pindex++;
4266  }
4267 
4268  /* get following parameters from slots */
4269  if (slots != NULL && fmstate->target_attrs != NIL)
4270  {
4271  TupleDesc tupdesc = RelationGetDescr(fmstate->rel);
4272  int nestlevel;
4273  ListCell *lc;
4274 
4275  nestlevel = set_transmission_modes();
4276 
4277  for (i = 0; i < numSlots; i++)
4278  {
4279  j = (tupleid != NULL) ? 1 : 0;
4280  foreach(lc, fmstate->target_attrs)
4281  {
4282  int attnum = lfirst_int(lc);
4283  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4284  Datum value;
4285  bool isnull;
4286 
4287  /* Ignore generated columns; they are set to DEFAULT */
4288  if (attr->attgenerated)
4289  continue;
4290  value = slot_getattr(slots[i], attnum, &isnull);
4291  if (isnull)
4292  p_values[pindex] = NULL;
4293  else
4294  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[j],
4295  value);
4296  pindex++;
4297  j++;
4298  }
4299  }
4300 
4301  reset_transmission_modes(nestlevel);
4302  }
4303 
4304  Assert(pindex == fmstate->p_nums * numSlots);
4305 
4306  MemoryContextSwitchTo(oldcontext);
4307 
4308  return p_values;
4309 }
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1559
static struct @151 value
void * palloc(Size size)
Definition: mcxt.c:1068
int16 attnum
Definition: pg_attribute.h:83
#define lfirst_int(lc)
Definition: pg_list.h:170
#define PointerGetDatum(X)
Definition: postgres.h:600
void reset_transmission_modes(int nestlevel)
int set_transmission_modes(void)
MemoryContext temp_cxt
Definition: postgres_fdw.c:208
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:202
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:381

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

Referenced by execute_foreign_modify().

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

Definition at line 3701 of file postgres_fdw.c.

3702 {
3703  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3704  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3705  int numParams = fsstate->numParams;
3706  const char **values = fsstate->param_values;
3707  PGconn *conn = fsstate->conn;
3709  PGresult *res;
3710 
3711  /* First, process a pending asynchronous request, if any. */
3712  if (fsstate->conn_state->pendingAreq)
3714 
3715  /*
3716  * Construct array of query parameter values in text format. We do the
3717  * conversions in the short-lived per-tuple context, so as not to cause a
3718  * memory leak over repeated scans.
3719  */
3720  if (numParams > 0)
3721  {
3722  MemoryContext oldcontext;
3723 
3724  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3725 
3726  process_query_params(econtext,
3727  fsstate->param_flinfo,
3728  fsstate->param_exprs,
3729  values);
3730 
3731  MemoryContextSwitchTo(oldcontext);
3732  }
3733 
3734  /* Construct the DECLARE CURSOR command */
3735  initStringInfo(&buf);
3736  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3737  fsstate->cursor_number, fsstate->query);
3738 
3739  /*
3740  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3741  * to infer types for all parameters. Since we explicitly cast every
3742  * parameter (see deparse.c), the "inference" is trivial and will produce
3743  * the desired result. This allows us to avoid assuming that the remote
3744  * server has the same OIDs we do for the parameters' types.
3745  */
3746  if (!PQsendQueryParams(conn, buf.data, numParams,
3747  NULL, values, NULL, NULL, 0))
3748  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3749 
3750  /*
3751  * Get the result, and check for success.
3752  *
3753  * We don't use a PG_TRY block here, so be careful not to throw error
3754  * without releasing the PGresult.
3755  */
3756  res = pgfdw_get_result(conn, buf.data);
3758  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3759  PQclear(res);
3760 
3761  /* Mark the cursor as created, and show no tuples have been retrieved */
3762  fsstate->cursor_exists = true;
3763  fsstate->tuples = NULL;
3764  fsstate->num_tuples = 0;
3765  fsstate->next_tuple = 0;
3766  fsstate->fetch_ct_2 = 0;
3767  fsstate->eof_reached = false;
3768 
3769  /* Clean up */
3770  pfree(buf.data);
3771 }
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:800
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:1536
void pfree(void *pointer)
Definition: mcxt.c:1175
static char * buf
Definition: pg_test_fsync.c:67
void process_pending_request(AsyncRequest *areq)
static void process_query_params(ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
AsyncRequest * pendingAreq
Definition: postgres_fdw.h:134
FmgrInfo * param_flinfo
Definition: postgres_fdw.c:154
const char ** param_values
Definition: postgres_fdw.c:156
unsigned int cursor_number
Definition: postgres_fdw.c:151
List * param_exprs
Definition: postgres_fdw.c:155
PgFdwConnState * conn_state
Definition: postgres_fdw.c:150
HeapTuple * tuples
Definition: postgres_fdw.c:159
ExprContext * ps_ExprContext
Definition: execnodes.h:1037

References appendStringInfo(), buf, PgFdwScanState::conn, conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, ERROR, ForeignScanState::fdw_state, PgFdwScanState::fetch_ct_2, if(), 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, res, ForeignScanState::ss, PgFdwScanState::tuples, and values.

Referenced by fetch_more_data_begin(), and postgresIterateForeignScan().

◆ 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 3942 of file postgres_fdw.c.

3952 {
3953  PgFdwModifyState *fmstate;
3954  Relation rel = resultRelInfo->ri_RelationDesc;
3955  TupleDesc tupdesc = RelationGetDescr(rel);
3956  Oid userid;
3957  ForeignTable *table;
3958  UserMapping *user;
3959  AttrNumber n_params;
3960  Oid typefnoid;
3961  bool isvarlena;
3962  ListCell *lc;
3963 
3964  /* Begin constructing PgFdwModifyState. */
3965  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3966  fmstate->rel = rel;
3967 
3968  /*
3969  * Identify which user to do the remote access as. This should match what
3970  * ExecCheckRTEPerms() does.
3971  */
3972  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
3973 
3974  /* Get info about foreign table. */
3975  table = GetForeignTable(RelationGetRelid(rel));
3976  user = GetUserMapping(userid, table->serverid);
3977 
3978  /* Open connection; report that we'll create a prepared statement. */
3979  fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
3980  fmstate->p_name = NULL; /* prepared statement not made yet */
3981 
3982  /* Set up remote query information. */
3983  fmstate->query = query;
3984  if (operation == CMD_INSERT)
3985  {
3986  fmstate->query = pstrdup(fmstate->query);
3987  fmstate->orig_query = pstrdup(fmstate->query);
3988  }
3989  fmstate->target_attrs = target_attrs;
3990  fmstate->values_end = values_end;
3991  fmstate->has_returning = has_returning;
3992  fmstate->retrieved_attrs = retrieved_attrs;
3993 
3994  /* Create context for per-tuple temp workspace. */
3995  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
3996  "postgres_fdw temporary data",
3998 
3999  /* Prepare for input conversion of RETURNING results. */
4000  if (fmstate->has_returning)
4001  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
4002 
4003  /* Prepare for output conversion of parameters used in prepared stmt. */
4004  n_params = list_length(fmstate->target_attrs) + 1;
4005  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
4006  fmstate->p_nums = 0;
4007 
4008  if (operation == CMD_UPDATE || operation == CMD_DELETE)
4009  {
4010  Assert(subplan != NULL);
4011 
4012  /* Find the ctid resjunk column in the subplan's result */
4014  "ctid");
4015  if (!AttributeNumberIsValid(fmstate->ctidAttno))
4016  elog(ERROR, "could not find junk ctid column");
4017 
4018  /* First transmittable parameter will be ctid */
4019  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
4020  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4021  fmstate->p_nums++;
4022  }
4023 
4024  if (operation == CMD_INSERT || operation == CMD_UPDATE)
4025  {
4026  /* Set up for remaining transmittable parameters */
4027  foreach(lc, fmstate->target_attrs)
4028  {
4029  int attnum = lfirst_int(lc);
4030  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4031 
4032  Assert(!attr->attisdropped);
4033 
4034  /* Ignore generated columns; they are set to DEFAULT */
4035  if (attr->attgenerated)
4036  continue;
4037  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
4038  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4039  fmstate->p_nums++;
4040  }
4041  }
4042 
4043  Assert(fmstate->p_nums <= n_params);
4044 
4045  /* Set batch_size from foreign server/table options. */
4046  if (operation == CMD_INSERT)
4047  fmstate->batch_size = get_batch_size_option(rel);
4048 
4049  fmstate->num_slots = 1;
4050 
4051  /* Initialize auxiliary state */
4052  fmstate->aux_fmstate = NULL;
4053 
4054  return fmstate;
4055 }
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
Definition: connection.c:134
#define elog(elevel,...)
Definition: elog.h:218
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2086
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:126
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:249
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:199
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2864
char * pstrdup(const char *in)
Definition: mcxt.c:1305
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:207
Oid GetUserId(void)
Definition: miscinit.c:492
@ CMD_INSERT
Definition: nodes.h:723
@ CMD_DELETE
Definition: nodes.h:724
@ CMD_UPDATE
Definition: nodes.h:722
static char * user
Definition: pg_regress.c:95
unsigned int Oid
Definition: postgres_ext.h:31
static int get_batch_size_option(Relation rel)
#define RelationGetRelid(relation)
Definition: rel.h:489
MemoryContext es_query_cxt
Definition: execnodes.h:632
Definition: fmgr.h:57
Oid serverid
Definition: foreign.h:56
AttInMetadata * attinmeta
Definition: postgres_fdw.c:183
PgFdwConnState * conn_state
Definition: postgres_fdw.c:187
AttrNumber ctidAttno
Definition: postgres_fdw.c:200
struct PgFdwModifyState * aux_fmstate
Definition: postgres_fdw.c:211
List * retrieved_attrs
Definition: postgres_fdw.c:197
List * targetlist
Definition: plannodes.h:142
Relation ri_RelationDesc
Definition: execnodes.h:433

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(), PgFdwModifyState::query, PgFdwModifyState::rel, RelationGetDescr, RelationGetRelid, PgFdwModifyState::retrieved_attrs, ResultRelInfo::ri_RelationDesc, ForeignTable::serverid, PgFdwModifyState::target_attrs, Plan::targetlist, PgFdwModifyState::temp_cxt, TupleDescAttr, TupleDescGetAttInMetadata(), user, and PgFdwModifyState::values_end.

Referenced by postgresBeginForeignInsert(), and postgresBeginForeignModify().

◆ deallocate_query()

static void deallocate_query ( PgFdwModifyState fmstate)
static

Definition at line 4371 of file postgres_fdw.c.

4372 {
4373  char sql[64];
4374  PGresult *res;
4375 
4376  /* do nothing if the query is not allocated */
4377  if (!fmstate->p_name)
4378  return;
4379 
4380  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4381 
4382  /*
4383  * We don't use a PG_TRY block here, so be careful not to throw error
4384  * without releasing the PGresult.
4385  */
4386  res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4388  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
4389  PQclear(res);
4390  pfree(fmstate->p_name);
4391  fmstate->p_name = NULL;
4392 }

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

Referenced by execute_foreign_modify(), and finish_foreign_modify().

◆ ec_member_matches_foreign()

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

Definition at line 3672 of file postgres_fdw.c.

3675 {
3677  Expr *expr = em->em_expr;
3678 
3679  /*
3680  * If we've identified what we're processing in the current scan, we only
3681  * want to match that expression.
3682  */
3683  if (state->current != NULL)
3684  return equal(expr, state->current);
3685 
3686  /*
3687  * Otherwise, ignore anything we've already processed.
3688  */
3689  if (list_member(state->already_used, expr))
3690  return false;
3691 
3692  /* This is the new target to process. */
3693  state->current = expr;
3694  return true;
3695 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3564
bool list_member(const List *list, const void *datum)
Definition: list.c:640
Definition: regguts.h:318

References arg, EquivalenceMember::em_expr, equal(), and list_member().

Referenced by postgresGetForeignPaths().

◆ 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 3067 of file postgres_fdw.c.

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

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

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

Definition at line 4527 of file postgres_fdw.c.

4528 {
4530  ExprContext *econtext = node->ss.ps.ps_ExprContext;
4531  int numParams = dmstate->numParams;
4532  const char **values = dmstate->param_values;
4533 
4534  /* First, process a pending asynchronous request, if any. */
4535  if (dmstate->conn_state->pendingAreq)
4537 
4538  /*
4539  * Construct array of query parameter values in text format.
4540  */
4541  if (numParams > 0)
4542  process_query_params(econtext,
4543  dmstate->param_flinfo,
4544  dmstate->param_exprs,
4545  values);
4546 
4547  /*
4548  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4549  * to infer types for all parameters. Since we explicitly cast every
4550  * parameter (see deparse.c), the "inference" is trivial and will produce
4551  * the desired result. This allows us to avoid assuming that the remote
4552  * server has the same OIDs we do for the parameters' types.
4553  */
4554  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4555  NULL, values, NULL, NULL, 0))
4556  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4557 
4558  /*
4559  * Get the result, and check for success.
4560  *
4561  * We don't use a PG_TRY block here, so be careful not to throw error
4562  * without releasing the PGresult.
4563  */
4564  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
4565  if (PQresultStatus(dmstate->result) !=
4567  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4568  dmstate->query);
4569 
4570  /* Get the number of rows affected. */
4571  if (dmstate->has_returning)
4572  dmstate->num_tuples = PQntuples(dmstate->result);
4573  else
4574  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4575 }
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3681
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3340
@ PGRES_TUPLES_OK
Definition: libpq-fe.h:100
PgFdwConnState * conn_state
Definition: postgres_fdw.c:231
const char ** param_values
Definition: postgres_fdw.c:235

References PgFdwDirectModifyState::conn, PgFdwDirectModifyState::conn_state, ERROR, ForeignScanState::fdw_state, PgFdwDirectModifyState::has_returning, if(), PgFdwDirectModifyState::num_tuples, 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().

◆ execute_foreign_modify()

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

Definition at line 4065 of file postgres_fdw.c.

4071 {
4072  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
4073  ItemPointer ctid = NULL;
4074  const char **p_values;
4075  PGresult *res;
4076  int n_rows;
4077  StringInfoData sql;
4078 
4079  /* The operation should be INSERT, UPDATE, or DELETE */
4080  Assert(operation == CMD_INSERT ||
4081  operation == CMD_UPDATE ||
4082  operation == CMD_DELETE);
4083 
4084  /* First, process a pending asynchronous request, if any. */
4085  if (fmstate->conn_state->pendingAreq)
4087 
4088  /*
4089  * If the existing query was deparsed and prepared for a different number
4090  * of rows, rebuild it for the proper number.
4091  */
4092  if (operation == CMD_INSERT && fmstate->num_slots != *numSlots)
4093  {
4094  /* Destroy the prepared statement created previously */
4095  if (fmstate->p_name)
4096  deallocate_query(fmstate);
4097 
4098  /* Build INSERT string with numSlots records in its VALUES clause. */
4099  initStringInfo(&sql);
4100  rebuildInsertSql(&sql, fmstate->rel,
4101  fmstate->orig_query, fmstate->target_attrs,
4102  fmstate->values_end, fmstate->p_nums,
4103  *numSlots - 1);
4104  pfree(fmstate->query);
4105  fmstate->query = sql.data;
4106  fmstate->num_slots = *numSlots;
4107  }
4108 
4109  /* Set up the prepared statement on the remote server, if we didn't yet */
4110  if (!fmstate->p_name)
4111  prepare_foreign_modify(fmstate);
4112 
4113  /*
4114  * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
4115  */
4116  if (operation == CMD_UPDATE || operation == CMD_DELETE)
4117  {
4118  Datum datum;
4119  bool isNull;
4120 
4121  datum = ExecGetJunkAttribute(planSlots[0],
4122  fmstate->ctidAttno,
4123  &isNull);
4124  /* shouldn't ever get a null result... */
4125  if (isNull)
4126  elog(ERROR, "ctid is NULL");
4127  ctid = (ItemPointer) DatumGetPointer(datum);
4128  }
4129 
4130  /* Convert parameters needed by prepared statement to text form */
4131  p_values = convert_prep_stmt_params(fmstate, ctid, slots, *numSlots);
4132 
4133  /*
4134  * Execute the prepared statement.
4135  */
4136  if (!PQsendQueryPrepared(fmstate->conn,
4137  fmstate->p_name,
4138  fmstate->p_nums * (*numSlots),
4139  p_values,
4140  NULL,
4141  NULL,
4142  0))
4143  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
4144 
4145  /*
4146  * Get the result, and check for success.
4147  *
4148  * We don't use a PG_TRY block here, so be careful not to throw error
4149  * without releasing the PGresult.
4150  */
4151  res = pgfdw_get_result(fmstate->conn, fmstate->query);
4152  if (PQresultStatus(res) !=
4154  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
4155 
4156  /* Check number of rows affected, and fetch RETURNING tuple if any */
4157  if (fmstate->has_returning)
4158  {
4159  Assert(*numSlots == 1);
4160  n_rows = PQntuples(res);
4161  if (n_rows > 0)
4162  store_returning_result(fmstate, slots[0], res);
4163  }
4164  else
4165  n_rows = atoi(PQcmdTuples(res));
4166 
4167  /* And clean up */
4168  PQclear(res);
4169 
4170  MemoryContextReset(fmstate->temp_cxt);
4171 
4172  *numSlots = n_rows;
4173 
4174  /*
4175  * Return NULL if nothing was inserted/updated/deleted on the remote end
4176  */
4177  return (n_rows > 0) ? slots : NULL;
4178 }
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:1945
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: executor.h:178
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:1682
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
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 deallocate_query(PgFdwModifyState *fmstate)
static void prepare_foreign_modify(PgFdwModifyState *fmstate)
void * ri_FdwState
Definition: execnodes.h:480

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, res, ResultRelInfo::ri_FdwState, store_returning_result(), PgFdwModifyState::target_attrs, PgFdwModifyState::temp_cxt, and PgFdwModifyState::values_end.

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

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3777 of file postgres_fdw.c.

3778 {
3779  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3780  PGresult *volatile res = NULL;
3781  MemoryContext oldcontext;
3782 
3783  /*
3784  * We'll store the tuples in the batch_cxt. First, flush the previous
3785  * batch.
3786  */
3787  fsstate->tuples = NULL;
3788  MemoryContextReset(fsstate->batch_cxt);
3789  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3790 
3791  /* PGresult must be released before leaving this function. */
3792  PG_TRY();
3793  {
3794  PGconn *conn = fsstate->conn;
3795  int numrows;
3796  int i;
3797 
3798  if (fsstate->async_capable)
3799  {
3800  Assert(fsstate->conn_state->pendingAreq);
3801 
3802  /*
3803  * The query was already sent by an earlier call to
3804  * fetch_more_data_begin. So now we just fetch the result.
3805  */
3806  res = pgfdw_get_result(conn, fsstate->query);
3807  /* On error, report the original query, not the FETCH. */
3809  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3810 
3811  /* Reset per-connection state */
3812  fsstate->conn_state->pendingAreq = NULL;
3813  }
3814  else
3815  {
3816  char sql[64];
3817 
3818  /* This is a regular synchronous fetch. */
3819  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3820  fsstate->fetch_size, fsstate->cursor_number);
3821 
3822  res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
3823  /* On error, report the original query, not the FETCH. */
3825  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3826  }
3827 
3828  /* Convert the data into HeapTuples */
3829  numrows = PQntuples(res);
3830  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3831  fsstate->num_tuples = numrows;
3832  fsstate->next_tuple = 0;
3833 
3834  for (i = 0; i < numrows; i++)
3835  {
3836  Assert(IsA(node->ss.ps.plan, ForeignScan));
3837 
3838  fsstate->tuples[i] =
3840  fsstate->rel,
3841  fsstate->attinmeta,
3842  fsstate->retrieved_attrs,
3843  node,
3844  fsstate->temp_cxt);
3845  }
3846 
3847  /* Update fetch_ct_2 */
3848  if (fsstate->fetch_ct_2 < 2)
3849  fsstate->fetch_ct_2++;
3850 
3851  /* Must be EOF if we didn't get as many tuples as we asked for. */
3852  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3853  }
3854  PG_FINALLY();
3855  {
3856  if (res)
3857  PQclear(res);
3858  }
3859  PG_END_TRY();
3860 
3861  MemoryContextSwitchTo(oldcontext);
3862 }
#define PG_END_TRY()
Definition: elog.h:324
#define PG_TRY()
Definition: elog.h:299
#define PG_FINALLY()
Definition: elog.h:316
List * retrieved_attrs
Definition: postgres_fdw.c:146
AttInMetadata * attinmeta
Definition: postgres_fdw.c:142
MemoryContext batch_cxt
Definition: postgres_fdw.c:171
MemoryContext temp_cxt
Definition: postgres_fdw.c:172

References Assert(), PgFdwScanState::async_capable, PgFdwScanState::attinmeta, PgFdwScanState::batch_cxt, PgFdwScanState::conn, 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, res, PgFdwScanState::retrieved_attrs, snprintf, ForeignScanState::ss, PgFdwScanState::temp_cxt, and PgFdwScanState::tuples.

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

◆ fetch_more_data_begin()

static void fetch_more_data_begin ( AsyncRequest areq)
static

Definition at line 7057 of file postgres_fdw.c.

7058 {
7059  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7060  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7061  char sql[64];
7062 
7063  Assert(!fsstate->conn_state->pendingAreq);
7064 
7065  /* Create the cursor synchronously. */
7066  if (!fsstate->cursor_exists)
7067  create_cursor(node);
7068 
7069  /* We will send this query, but not wait for the response. */
7070  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7071  fsstate->fetch_size, fsstate->cursor_number);
7072 
7073  if (PQsendQuery(fsstate->conn, sql) < 0)
7074  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7075 
7076  /* Remember that the request is in process */
7077  fsstate->conn_state->pendingAreq = areq;
7078 }
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1424
static void create_cursor(ForeignScanState *node)

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

Referenced by postgresForeignAsyncConfigureWait(), and produce_tuple_asynchronously().

◆ find_em_for_rel()

EquivalenceMember* find_em_for_rel ( PlannerInfo root,
EquivalenceClass ec,
RelOptInfo rel 
)

Definition at line 7411 of file postgres_fdw.c.

7412 {
7413  ListCell *lc;
7414 
7415  foreach(lc, ec->ec_members)
7416  {
7418 
7419  /*
7420  * Note we require !bms_is_empty, else we'd accept constant
7421  * expressions which are not suitable for the purpose.
7422  */
7423  if (bms_is_subset(em->em_relids, rel->relids) &&
7424  !bms_is_empty(em->em_relids) &&
7425  is_foreign_expr(root, rel, em->em_expr))
7426  return em;
7427  }
7428 
7429  return NULL;
7430 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:315
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:703
List * ec_members
Definition: pathnodes.h:991
Relids relids
Definition: pathnodes.h:682

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

Referenced by appendOrderByClause(), get_useful_pathkeys_for_relation(), and is_foreign_pathkey().

◆ find_em_for_rel_target()

EquivalenceMember* find_em_for_rel_target ( PlannerInfo root,
EquivalenceClass ec,
RelOptInfo rel 
)

Definition at line 7444 of file postgres_fdw.c.

7446 {
7447  PathTarget *target = rel->reltarget;
7448  ListCell *lc1;
7449  int i;
7450 
7451  i = 0;
7452  foreach(lc1, target->exprs)
7453  {
7454  Expr *expr = (Expr *) lfirst(lc1);
7455  Index sgref = get_pathtarget_sortgroupref(target, i);
7456  ListCell *lc2;
7457 
7458  /* Ignore non-sort expressions */
7459  if (sgref == 0 ||
7461  root->parse->sortClause) == NULL)
7462  {
7463  i++;
7464  continue;
7465  }
7466 
7467  /* We ignore binary-compatible relabeling on both ends */
7468  while (expr && IsA(expr, RelabelType))
7469  expr = ((RelabelType *) expr)->arg;
7470 
7471  /* Locate an EquivalenceClass member matching this expr, if any */
7472  foreach(lc2, ec->ec_members)
7473  {
7475  Expr *em_expr;
7476 
7477  /* Don't match constants */
7478  if (em->em_is_const)
7479  continue;
7480 
7481  /* Ignore child members */
7482  if (em->em_is_child)
7483  continue;
7484 
7485  /* Match if same expression (after stripping relabel) */
7486  em_expr = em->em_expr;
7487  while (em_expr && IsA(em_expr, RelabelType))
7488  em_expr = ((RelabelType *) em_expr)->arg;
7489 
7490  if (!equal(em_expr, expr))
7491  continue;
7492 
7493  /* Check that expression (including relabels!) is shippable */
7494  if (is_foreign_expr(root, rel, em->em_expr))
7495  return em;
7496  }
7497 
7498  i++;
7499  }
7500 
7501  return NULL;
7502 }
unsigned int Index
Definition: c.h:549
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1131
List * exprs
Definition: pathnodes.h:1122
List * sortClause
Definition: parsenodes.h:174
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:432

References arg, EquivalenceClass::ec_members, EquivalenceMember::em_expr, EquivalenceMember::em_is_child, EquivalenceMember::em_is_const, equal(), PathTarget::exprs, get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), i, is_foreign_expr(), IsA, lfirst, PlannerInfo::parse, RelOptInfo::reltarget, and Query::sortClause.

Referenced by add_foreign_ordered_paths(), and appendOrderByClause().

◆ find_modifytable_subplan()

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

Definition at line 2363 of file postgres_fdw.c.

2367 {
2368  Plan *subplan = outerPlan(plan);
2369 
2370  /*
2371  * The cases we support are (1) the desired ForeignScan is the immediate
2372  * child of ModifyTable, or (2) it is the subplan_index'th child of an
2373  * Append node that is the immediate child of ModifyTable. There is no
2374  * point in looking further down, as that would mean that local joins are
2375  * involved, so we can't do the update directly.
2376  *
2377  * There could be a Result atop the Append too, acting to compute the
2378  * UPDATE targetlist values. We ignore that here; the tlist will be
2379  * checked by our caller.
2380  *
2381  * In principle we could examine all the children of the Append, but it's
2382  * currently unlikely that the core planner would generate such a plan
2383  * with the children out-of-order. Moreover, such a search risks costing
2384  * O(N^2) time when there are a lot of children.
2385  */
2386  if (IsA(subplan, Append))
2387  {
2388  Append *appendplan = (Append *) subplan;
2389 
2390  if (subplan_index < list_length(appendplan->appendplans))
2391  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2392  }
2393  else if (IsA(subplan, Result) &&
2394  outerPlan(subplan) != NULL &&
2395  IsA(outerPlan(subplan), Append))
2396  {
2397  Append *appendplan = (Append *) outerPlan(subplan);
2398 
2399  if (subplan_index < list_length(appendplan->appendplans))
2400  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2401  }
2402 
2403  /* Now, have we got a ForeignScan on the desired rel? */
2404  if (IsA(subplan, ForeignScan))
2405  {
2406  ForeignScan *fscan = (ForeignScan *) subplan;
2407 
2408  if (bms_is_member(rtindex, fscan->fs_relids))
2409  return fscan;
2410  }
2411 
2412  return NULL;
2413 }
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
#define outerPlan(node)
Definition: plannodes.h:172
List * appendplans
Definition: plannodes.h:256
Bitmapset * fs_relids
Definition: plannodes.h:674

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

Referenced by postgresPlanDirectModify().

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4353 of file postgres_fdw.c.

4354 {
4355  Assert(fmstate != NULL);
4356 
4357  /* If we created a prepared statement, destroy it */
4358  deallocate_query(fmstate);
4359 
4360  /* Release remote connection */
4361  ReleaseConnection(fmstate->conn);
4362  fmstate->conn = NULL;
4363 }

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

Referenced by postgresEndForeignInsert(), and postgresEndForeignModify().

◆ foreign_grouping_ok()

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

Definition at line 6103 of file postgres_fdw.c.

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

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

◆ foreign_join_ok()

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

Definition at line 5515 of file postgres_fdw.c.

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

References Assert(), bms_add_members(), bms_is_subset(), bms_nonempty_difference(), bms_union(), RestrictInfo::clause, elog, ERROR, RelOptInfo::fdw_private, get_jointype_name(), if(), 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().

◆ get_batch_size_option()

static int get_batch_size_option ( Relation  rel)
static

Definition at line 7509 of file postgres_fdw.c.

7510 {
7511  Oid foreigntableid = RelationGetRelid(rel);
7512  ForeignTable *table;
7513  ForeignServer *server;
7514  List *options;
7515  ListCell *lc;
7516 
7517  /* we use 1 by default, which means "no batching" */
7518  int batch_size = 1;
7519 
7520  /*
7521  * Load options for table and server. We append server options after table
7522  * options, because table options take precedence.
7523  */
7524  table = GetForeignTable(foreigntableid);
7525  server = GetForeignServer(table->serverid);
7526 
7527  options = NIL;
7528  options = list_concat(options, table->options);
7529  options = list_concat(options, server->options);
7530 
7531  /* See if either table or server specifies batch_size. */
7532  foreach(lc, options)
7533  {
7534  DefElem *def = (DefElem *) lfirst(lc);
7535 
7536  if (strcmp(def->defname, "batch_size") == 0)
7537  {
7538  (void) parse_int(defGetString(def), &batch_size, 0, NULL);
7539  break;
7540  }
7541  }
7542 
7543  return batch_size;
7544 }
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:110
static char ** options

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

Referenced by create_foreign_modify(), and postgresGetForeignModifyBatchSize().

◆ 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 3571 of file postgres_fdw.c.

3574 {
3575  PGresult *volatile res = NULL;
3576 
3577  /* PGresult must be released before leaving this function. */
3578  PG_TRY();
3579  {
3580  char *line;
3581  char *p;
3582  int n;
3583 
3584  /*
3585  * Execute EXPLAIN remotely.
3586  */
3587  res = pgfdw_exec_query(conn, sql, NULL);
3589  pgfdw_report_error(ERROR, res, conn, false, sql);
3590 
3591  /*
3592  * Extract cost numbers for topmost plan node. Note we search for a
3593  * left paren from the end of the line to avoid being confused by
3594  * other uses of parentheses.
3595  */
3596  line = PQgetvalue(res, 0, 0);
3597  p = strrchr(line, '(');
3598  if (p == NULL)
3599  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3600  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3601  startup_cost, total_cost, rows, width);
3602  if (n != 4)
3603  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3604  }
3605  PG_FINALLY();
3606  {
3607  if (res)
3608  PQclear(res);
3609  }
3610  PG_END_TRY();
3611 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3735

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

Referenced by estimate_path_cost_size().

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 4581 of file postgres_fdw.c.

4582 {
4584  EState *estate = node->ss.ps.state;
4585  ResultRelInfo *resultRelInfo = node->resultRelInfo;
4586  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4587  TupleTableSlot *resultSlot;
4588 
4589  Assert(resultRelInfo->ri_projectReturning);
4590 
4591  /* If we didn't get any tuples, must be end of data. */
4592  if (dmstate->next_tuple >= dmstate->num_tuples)
4593  return ExecClearTuple(slot);
4594 
4595  /* Increment the command es_processed count if necessary. */
4596  if (dmstate->set_processed)
4597  estate->es_processed += 1;
4598 
4599  /*
4600  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4601  * tuple. (has_returning is false when the local query is of the form
4602  * "UPDATE/DELETE .. RETURNING 1" for example.)
4603  */
4604  if (!dmstate->has_returning)
4605  {
4606  ExecStoreAllNullTuple(slot);
4607  resultSlot = slot;
4608  }
4609  else
4610  {
4611  /*
4612  * On error, be sure to release the PGresult on the way out. Callers
4613  * do not have PG_TRY blocks to ensure this happens.
4614  */
4615  PG_TRY();
4616  {
4617  HeapTuple newtup;
4618 
4619  newtup = make_tuple_from_result_row(dmstate->result,
4620  dmstate->next_tuple,
4621  dmstate->rel,
4622  dmstate->attinmeta,
4623  dmstate->retrieved_attrs,
4624  node,
4625  dmstate->temp_cxt);
4626  ExecStoreHeapTuple(newtup, slot, false);
4627  }
4628  PG_CATCH();
4629  {
4630  if (dmstate->result)
4631  PQclear(dmstate->result);
4632  PG_RE_THROW();
4633  }
4634  PG_END_TRY();
4635 
4636  /* Get the updated/deleted tuple. */
4637  if (dmstate->rel)
4638  resultSlot = slot;
4639  else
4640  resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4641  }
4642  dmstate->next_tuple++;
4643 
4644  /* Make slot available for evaluation of the local query RETURNING list. */
4645  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4646  resultSlot;
4647 
4648  return slot;
4649 }
#define PG_RE_THROW()
Definition: elog.h:340
#define PG_CATCH()
Definition: elog.h:309
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1576
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1352
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1906
MemoryContext temp_cxt
Definition: postgres_fdw.c:248
AttInMetadata * attinmeta
Definition: postgres_fdw.c:221
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1426

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

◆ get_tupdesc_for_join_scan_tuples()

static TupleDesc get_tupdesc_for_join_scan_tuples ( ForeignScanState node)
static

Definition at line 1437 of file postgres_fdw.c.

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

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

Referenced by postgresBeginDirectModify(), and postgresBeginForeignScan().

◆ get_useful_ecs_for_relation()

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

Definition at line 806 of file postgres_fdw.c.

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

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

Referenced by get_useful_pathkeys_for_relation().

◆ get_useful_pathkeys_for_relation()

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

Definition at line 902 of file postgres_fdw.c.

903 {
904  List *useful_pathkeys_list = NIL;
905  List *useful_eclass_list;
907  EquivalenceClass *query_ec = NULL;
908  ListCell *lc;
909 
910  /*
911  * Pushing the query_pathkeys to the remote server is always worth
912  * considering, because it might let us avoid a local sort.
913  */
914  fpinfo->qp_is_pushdown_safe = false;
915  if (root->query_pathkeys)
916  {
917  bool query_pathkeys_ok = true;
918 
919  foreach(lc, root->query_pathkeys)
920  {
921  PathKey *pathkey = (PathKey *) lfirst(lc);
922 
923  /*
924  * The planner and executor don't have any clever strategy for
925  * taking data sorted by a prefix of the query's pathkeys and
926  * getting it to be sorted by all of those pathkeys. We'll just
927  * end up resorting the entire data set. So, unless we can push
928  * down all of the query pathkeys, forget it.
929  */
930  if (!is_foreign_pathkey(root, rel, pathkey))
931  {
932  query_pathkeys_ok = false;
933  break;
934  }
935  }
936 
937  if (query_pathkeys_ok)
938  {
939  useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
940  fpinfo->qp_is_pushdown_safe = true;
941  }
942  }
943 
944  /*
945  * Even if we're not using remote estimates, having the remote side do the
946  * sort generally won't be any worse than doing it locally, and it might
947  * be much better if the remote side can generate data in the right order
948  * without needing a sort at all. However, what we're going to do next is
949  * try to generate pathkeys that seem promising for possible merge joins,
950  * and that's more speculative. A wrong choice might hurt quite a bit, so
951  * bail out if we can't use remote estimates.
952  */
953  if (!fpinfo->use_remote_estimate)
954  return useful_pathkeys_list;
955 
956  /* Get the list of interesting EquivalenceClasses. */
957  useful_eclass_list = get_useful_ecs_for_relation(root, rel);
958 
959  /* Extract unique EC for query, if any, so we don't consider it again. */
960  if (list_length(root->query_pathkeys) == 1)
961  {
962  PathKey *query_pathkey = linitial(root->query_pathkeys);
963 
964  query_ec = query_pathkey->pk_eclass;
965  }
966 
967  /*
968  * As a heuristic, the only pathkeys we consider here are those of length
969  * one. It's surely possible to consider more, but since each one we
970  * choose to consider will generate a round-trip to the remote side, we
971  * need to be a bit cautious here. It would sure be nice to have a local
972  * cache of information about remote index definitions...
973  */
974  foreach(lc, useful_eclass_list)
975  {
976  EquivalenceClass *cur_ec = lfirst(lc);
977  PathKey *pathkey;
978 
979  /* If redundant with what we did above, skip it. */
980  if (cur_ec == query_ec)
981  continue;
982 
983  /* Can't push down the sort if the EC's opfamily is not shippable. */
985  OperatorFamilyRelationId, fpinfo))
986  continue;
987 
988  /* If no pushable expression for this rel, skip it. */
989  if (find_em_for_rel(root, cur_ec, rel) == NULL)
990  continue;
991 
992  /* Looks like we can generate a pathkey, so let's do it. */
993  pathkey = make_canonical_pathkey(root, cur_ec,
994  linitial_oid(cur_ec->ec_opfamilies),
996  false);
997  useful_pathkeys_list = lappend(useful_pathkeys_list,
998  list_make1(pathkey));
999  }
1000 
1001  return useful_pathkeys_list;
1002 }
bool is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel, PathKey *pathkey)
Definition: deparse.c:1049
List * list_copy(const List *oldlist)
Definition: list.c:1532
PathKey * make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, int strategy, bool nulls_first)
Definition: pathkeys.c:59
#define linitial(l)
Definition: pg_list.h:174
#define linitial_oid(l)
Definition: pg_list.h:176
EquivalenceMember * find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
static List * get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:806
#define BTLessStrategyNumber
Definition: stratnum.h:29
List * ec_opfamilies
Definition: pathnodes.h:989

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

Referenced by add_paths_with_pathkeys_for_rel().

◆ init_returning_filter()

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

Definition at line 4655 of file postgres_fdw.c.

4658 {
4659  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4660  ListCell *lc;
4661  int i;
4662 
4663  /*
4664  * Calculate the mapping between the fdw_scan_tlist's entries and the
4665  * result tuple's attributes.
4666  *
4667  * The "map" is an array of indexes of the result tuple's attributes in
4668  * fdw_scan_tlist, i.e., one entry for every attribute of the result
4669  * tuple. We store zero for any attributes that don't have the
4670  * corresponding entries in that list, marking that a NULL is needed in
4671  * the result tuple.
4672  *
4673  * Also get the indexes of the entries for ctid and oid if any.
4674  */
4675  dmstate->attnoMap = (AttrNumber *)
4676  palloc0(resultTupType->natts * sizeof(AttrNumber));
4677 
4678  dmstate->ctidAttno = dmstate->oidAttno = 0;
4679 
4680  i = 1;
4681  dmstate->hasSystemCols = false;
4682  foreach(lc, fdw_scan_tlist)
4683  {
4684  TargetEntry *tle = (TargetEntry *) lfirst(lc);
4685  Var *var = (Var *) tle->expr;
4686 
4687  Assert(IsA(var, Var));
4688 
4689  /*
4690  * If the Var is a column of the target relation to be retrieved from
4691  * the foreign server, get the index of the entry.
4692  */
4693  if (var->varno == rtindex &&
4694  list_member_int(dmstate->retrieved_attrs, i))
4695  {
4696  int attrno = var->varattno;
4697 
4698  if (attrno < 0)
4699  {
4700  /*
4701  * We don't retrieve system columns other than ctid and oid.
4702  */
4703  if (attrno == SelfItemPointerAttributeNumber)
4704  dmstate->ctidAttno = i;
4705  else
4706  Assert(false);
4707  dmstate->hasSystemCols = true;
4708  }
4709  else
4710  {
4711  /*
4712  * We don't retrieve whole-row references to the target
4713  * relation either.
4714  */
4715  Assert(attrno > 0);
4716 
4717  dmstate->attnoMap[attrno - 1] = i;
4718  }
4719  }
4720  i++;
4721  }
4722 }
bool list_member_int(const List *list, int datum)
Definition: list.c:681

References Assert(), PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, TargetEntry::expr, PgFdwDirectModifyState::hasSystemCols, i, IsA, lfirst, list_member_int(), TupleDescData::natts, PgFdwDirectModifyState::oidAttno, palloc0(), RelationGetDescr, PgFdwDirectModifyState::resultRel, PgFdwDirectModifyState::retrieved_attrs, SelfItemPointerAttributeNumber, Var::varattno, and Var::varno.

Referenced by postgresBeginDirectModify().

◆ make_tuple_from_result_row()

static HeapTuple make_tuple_from_result_row ( PGresult res,
int  row,
Relation  rel,
AttInMetadata attinmeta,
List retrieved_attrs,
ForeignScanState fsstate,
MemoryContext  temp_context 
)
static

Definition at line 7151 of file postgres_fdw.c.

7158 {
7159  HeapTuple tuple;
7160  TupleDesc tupdesc;
7161  Datum *values;
7162  bool *nulls;
7163  ItemPointer ctid = NULL;
7164  ConversionLocation errpos;
7165  ErrorContextCallback errcallback;
7166  MemoryContext oldcontext;
7167  ListCell *lc;
7168  int j;
7169 
7170  Assert(row < PQntuples(res));
7171 
7172  /*
7173  * Do the following work in a temp context that we reset after each tuple.
7174  * This cleans up not only the data we have direct access to, but any
7175  * cruft the I/O functions might leak.
7176  */
7177  oldcontext = MemoryContextSwitchTo(temp_context);
7178 
7179  /*
7180  * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
7181  * provided, otherwise look to the scan node's ScanTupleSlot.
7182  */
7183  if (rel)
7184  tupdesc = RelationGetDescr(rel);
7185  else
7186  {
7187  Assert(fsstate);
7188  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
7189  }
7190 
7191  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
7192  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
7193  /* Initialize to nulls for any columns not present in result */
7194  memset(nulls, true, tupdesc->natts * sizeof(bool));
7195 
7196  /*
7197  * Set up and install callback to report where conversion error occurs.
7198  */
7199  errpos.cur_attno = 0;
7200  errpos.rel = rel;
7201  errpos.fsstate = fsstate;
7202  errcallback.callback = conversion_error_callback;
7203  errcallback.arg = (void *) &errpos;
7204  errcallback.previous = error_context_stack;
7205  error_context_stack = &errcallback;
7206 
7207  /*
7208  * i indexes columns in the relation, j indexes columns in the PGresult.
7209  */
7210  j = 0;
7211  foreach(lc, retrieved_attrs)
7212  {
7213  int i = lfirst_int(lc);
7214  char *valstr;
7215 
7216  /* fetch next column's textual value */
7217  if (PQgetisnull(res, row, j))
7218  valstr = NULL;
7219  else
7220  valstr = PQgetvalue(res, row, j);
7221 
7222  /*
7223  * convert value to internal representation
7224  *
7225  * Note: we ignore system columns other than ctid and oid in result
7226  */
7227  errpos.cur_attno = i;
7228  if (i > 0)
7229  {
7230  /* ordinary column */
7231  Assert(i <= tupdesc->natts);
7232  nulls[i - 1] = (valstr == NULL);
7233  /* Apply the input function even to nulls, to support domains */
7234  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
7235  valstr,
7236  attinmeta->attioparams[i - 1],
7237  attinmeta->atttypmods[i - 1]);
7238  }
7239  else if (i == SelfItemPointerAttributeNumber)
7240  {
7241  /* ctid */
7242  if (valstr != NULL)
7243  {
7244  Datum datum;
7245 
7246  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
7247  ctid = (ItemPointer) DatumGetPointer(datum);
7248  }
7249  }
7250  errpos.cur_attno = 0;
7251 
7252  j++;
7253  }
7254 
7255  /* Uninstall error context callback. */
7256  error_context_stack = errcallback.previous;
7257 
7258  /*
7259  * Check we got the expected number of columns. Note: j == 0 and
7260  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
7261  */
7262  if (j > 0 && j != PQnfields(res))
7263  elog(ERROR, "remote query result does not match the foreign table");
7264 
7265  /*
7266  * Build the result tuple in caller's memory context.
7267  */
7268  MemoryContextSwitchTo(oldcontext);
7269 
7270  tuple = heap_form_tuple(tupdesc, values, nulls);
7271 
7272  /*
7273  * If we have a CTID to return, install it in both t_self and t_ctid.
7274  * t_self is the normal place, but if the tuple is converted to a
7275  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
7276  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
7277  */
7278  if (ctid)
7279  tuple->t_self = tuple->t_data->t_ctid = *ctid;
7280 
7281  /*
7282  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
7283  * heap_form_tuple. heap_form_tuple actually creates the tuple with
7284  * DatumTupleFields, not HeapTupleFields, but the executor expects
7285  * HeapTupleFields and will happily extract system columns on that
7286  * assumption. If we don't do this then, for example, the tuple length
7287  * ends up in the xmin field, which isn't what we want.
7288  */
7292 
7293  /* Clean up */
7294  MemoryContextReset(temp_context);
7295 
7296  return tuple;
7297 }
ErrorContextCallback * error_context_stack
Definition: elog.c:93
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3760
int PQnfields(const PGresult *res)
Definition: fe-exec.c:3348
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1515
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:631
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
#define CStringGetDatum(X)
Definition: postgres.h:622
static void conversion_error_callback(void *arg)
FmgrInfo * attinfuncs
Definition: funcapi.h:41
Oid * attioparams
Definition: funcapi.h:44
int32 * atttypmods
Definition: funcapi.h:47
struct ErrorContextCallback * previous
Definition: elog.h:232
void(* callback)(void *arg)
Definition: elog.h:233
ItemPointerData t_ctid
Definition: htup_details.h:160
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:57

References ErrorContextCallback::arg, Assert(), AttInMetadata::attinfuncs, AttInMetadata::attioparams, AttInMetadata::atttypmods, ErrorContextCallback::callback, conversion_error_callback(), CStringGetDatum, ConversionLocation::cur_attno, DatumGetPointer, DirectFunctionCall1, elog, ERROR, error_context_stack, ConversionLocation::fsstate, heap_form_tuple(), HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, i, InputFunctionCall(), InvalidTransactionId, j, lfirst_int, MemoryContextReset(), MemoryContextSwitchTo(), TupleDescData::natts, palloc(), palloc0(), PQgetisnull(), PQgetvalue(), PQnfields(), PQntuples(), ErrorContextCallback::previous, ConversionLocation::rel, RelationGetDescr, res, SelfItemPointerAttributeNumber, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleData::t_self, tidin(), TupleTableSlot::tts_tupleDescriptor, and values.

Referenced by analyze_row_processor(), fetch_more_data(), get_returning_data(), and store_returning_result().

◆ merge_fdw_options()

static void merge_fdw_options ( PgFdwRelationInfo fpinfo,
const PgFdwRelationInfo fpinfo_o,
const PgFdwRelationInfo fpinfo_i 
)
static

Definition at line 5903 of file postgres_fdw.c.

5906 {
5907  /* We must always have fpinfo_o. */
5908  Assert(fpinfo_o);
5909 
5910  /* fpinfo_i may be NULL, but if present the servers must both match. */
5911  Assert(!fpinfo_i ||
5912  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
5913 
5914  /*
5915  * Copy the server specific FDW options. (For a join, both relations come
5916  * from the same server, so the server options should have the same value
5917  * for both relations.)
5918  */
5919  fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
5920  fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
5921  fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
5922  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
5923  fpinfo->fetch_size = fpinfo_o->fetch_size;
5924  fpinfo->async_capable = fpinfo_o->async_capable;
5925 
5926  /* Merge the table level options from either side of the join. */
5927  if (fpinfo_i)
5928  {
5929  /*
5930  * We'll prefer to use remote estimates for this join if any table
5931  * from either side of the join is using remote estimates. This is
5932  * most likely going to be preferred since they're already willing to
5933  * pay the price of a round trip to get the remote EXPLAIN. In any
5934  * case it's not entirely clear how we might otherwise handle this
5935  * best.
5936  */
5937  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
5938  fpinfo_i->use_remote_estimate;
5939 
5940  /*
5941  * Set fetch size to maximum of the joining sides, since we are
5942  * expecting the rows returned by the join to be proportional to the
5943  * relation sizes.
5944  */
5945  fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
5946 
5947  /*
5948  * We'll prefer to consider this join async-capable if any table from
5949  * either side of the join is considered async-capable. This would be
5950  * reasonable because in that case the foreign server would have its
5951  * own resources to scan that table asynchronously, and the join could
5952  * also be computed asynchronously using the resources.
5953  */
5954  fpinfo->async_capable = fpinfo_o->async_capable ||
5955  fpinfo_i->async_capable;
5956  }
5957 }
#define Max(x, y)
Definition: c.h:980
Oid serverid
Definition: foreign.h:36

References Assert(), PgFdwRelationInfo::async_capable, PgFdwRelationInfo::fdw_startup_cost, PgFdwRelationInfo::fdw_tuple_cost, PgFdwRelationInfo::fetch_size, Max, PgFdwRelationInfo::server, ForeignServer::serverid, PgFdwRelationInfo::shippable_extensions, and PgFdwRelationInfo::use_remote_estimate.

Referenced by add_foreign_final_paths(), add_foreign_grouping_paths(), add_foreign_ordered_paths(), and foreign_join_ok().

◆ PG_FUNCTION_INFO_V1()

PG_FUNCTION_INFO_V1 ( postgres_fdw_handler  )

◆ postgres_fdw_handler()

Datum postgres_fdw_handler ( PG_FUNCTION_ARGS  )

Definition at line 551 of file postgres_fdw.c.

552 {
553  FdwRoutine *routine = makeNode(FdwRoutine);
554 
555  /* Functions for scanning foreign tables */
563 
564  /* Functions for updating foreign tables */
581 
582  /* Function for EvalPlanQual rechecks */
584  /* Support functions for EXPLAIN */
588 
589  /* Support function for TRUNCATE */
591 
592  /* Support functions for ANALYZE */
594 
595  /* Support functions for IMPORT FOREIGN SCHEMA */
597 
598  /* Support functions for join push-down */
600 
601  /* Support functions for upper relation push-down */
603 
604  /* Support functions for asynchronous execution */
605  routine->IsForeignPathAsyncCapable =