PostgreSQL Source Code  git master
postgres_fdw.c File Reference
#include "postgres.h"
#include "postgres_fdw.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "catalog/pg_class.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/vacuum.h"
#include "foreign/fdwapi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/cost.h"
#include "optimizer/clauses.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/var.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "utils/builtins.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  ConversionLocation
 
struct  ec_member_foreign_arg
 

Macros

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

Typedefs

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

Enumerations

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

Functions

 PG_FUNCTION_INFO_V1 (postgres_fdw_handler)
 
static void postgresGetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static void postgresGetForeignPaths (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static ForeignScanpostgresGetForeignPlan (PlannerInfo *root, RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
 
static void postgresBeginForeignScan (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateForeignScan (ForeignScanState *node)
 
static void postgresReScanForeignScan (ForeignScanState *node)
 
static void postgresEndForeignScan (ForeignScanState *node)
 
static void postgresAddForeignUpdateTargets (Query *parsetree, RangeTblEntry *target_rte, Relation target_relation)
 
static ListpostgresPlanForeignModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginForeignModify (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
 
static TupleTableSlotpostgresExecForeignInsert (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlotpostgresExecForeignUpdate (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlotpostgresExecForeignDelete (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static void postgresEndForeignModify (EState *estate, ResultRelInfo *resultRelInfo)
 
static int postgresIsForeignRelUpdatable (Relation rel)
 
static bool postgresPlanDirectModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginDirectModify (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateDirectModify (ForeignScanState *node)
 
static void postgresEndDirectModify (ForeignScanState *node)
 
static void postgresExplainForeignScan (ForeignScanState *node, ExplainState *es)
 
static void postgresExplainForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
 
static void postgresExplainDirectModify (ForeignScanState *node, ExplainState *es)
 
static bool postgresAnalyzeForeignTable (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
 
static ListpostgresImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid)
 
static void postgresGetForeignJoinPaths (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static bool postgresRecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot)
 
static void postgresGetForeignUpperPaths (PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel)
 
static void estimate_path_cost_size (PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, 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 bool ec_member_matches_foreign (PlannerInfo *root, RelOptInfo *rel, EquivalenceClass *ec, EquivalenceMember *em, void *arg)
 
static void create_cursor (ForeignScanState *node)
 
static void fetch_more_data (ForeignScanState *node)
 
static void close_cursor (PGconn *conn, unsigned int cursor_number)
 
static void prepare_foreign_modify (PgFdwModifyState *fmstate)
 
static const char ** convert_prep_stmt_params (PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot *slot)
 
static void store_returning_result (PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
 
static Listbuild_remote_returning (Index rtindex, Relation rel, List *returningList)
 
static void rebuild_fdw_scan_tlist (ForeignScan *fscan, List *tlist)
 
static void execute_dml_stmt (ForeignScanState *node)
 
static TupleTableSlotget_returning_data (ForeignScanState *node)
 
static void init_returning_filter (PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
 
static TupleTableSlotapply_returning_filter (PgFdwDirectModifyState *dmstate, TupleTableSlot *slot, EState *estate)
 
static void prepare_query_params (PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
 
static void process_query_params (ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
 
static int postgresAcquireSampleRowsFunc (Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
 
static void analyze_row_processor (PGresult *res, int row, PgFdwAnalyzeState *astate)
 
static HeapTuple make_tuple_from_result_row (PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
 
static void conversion_error_callback (void *arg)
 
static bool foreign_join_ok (PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra)
 
static bool foreign_grouping_ok (PlannerInfo *root, RelOptInfo *grouped_rel)
 
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)
 
static void apply_server_options (PgFdwRelationInfo *fpinfo)
 
static void apply_table_options (PgFdwRelationInfo *fpinfo)
 
static void merge_fdw_options (PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
 
Datum postgres_fdw_handler (PG_FUNCTION_ARGS)
 
int set_transmission_modes (void)
 
void reset_transmission_modes (int nestlevel)
 
Exprfind_em_expr_for_rel (EquivalenceClass *ec, RelOptInfo *rel)
 

Variables

 PG_MODULE_MAGIC
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 54 of file postgres_fdw.c.

Referenced by estimate_path_cost_size().

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 48 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.01

Definition at line 51 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 110 of file postgres_fdw.c.

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 89 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 63 of file postgres_fdw.c.

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

Function Documentation

◆ add_foreign_grouping_paths()

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

Definition at line 5262 of file postgres_fdw.c.

References add_path(), create_foreignscan_path(), estimate_path_cost_size(), RelOptInfo::fdw_private, foreign_grouping_ok(), Query::groupClause, Query::groupingSets, Query::hasAggs, PlannerInfo::hasHavingQual, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, parse(), PlannerInfo::parse, PgFdwRelationInfo::rows, PgFdwRelationInfo::server, PgFdwRelationInfo::startup_cost, PgFdwRelationInfo::table, PgFdwRelationInfo::total_cost, PlannerInfo::upper_targets, UPPERREL_GROUP_AGG, PgFdwRelationInfo::user, and PgFdwRelationInfo::width.

Referenced by postgresGetForeignUpperPaths().

5264 {
5265  Query *parse = root->parse;
5266  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5267  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
5268  ForeignPath *grouppath;
5269  PathTarget *grouping_target;
5270  double rows;
5271  int width;
5272  Cost startup_cost;
5273  Cost total_cost;
5274 
5275  /* Nothing to be done, if there is no grouping or aggregation required. */
5276  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
5277  !root->hasHavingQual)
5278  return;
5279 
5280  grouping_target = root->upper_targets[UPPERREL_GROUP_AGG];
5281 
5282  /* save the input_rel as outerrel in fpinfo */
5283  fpinfo->outerrel = input_rel;
5284 
5285  /*
5286  * Copy foreign table, foreign server, user mapping, FDW options etc.
5287  * details from the input relation's fpinfo.
5288  */
5289  fpinfo->table = ifpinfo->table;
5290  fpinfo->server = ifpinfo->server;
5291  fpinfo->user = ifpinfo->user;
5292  merge_fdw_options(fpinfo, ifpinfo, NULL);
5293 
5294  /* Assess if it is safe to push down aggregation and grouping. */
5295  if (!foreign_grouping_ok(root, grouped_rel))
5296  return;
5297 
5298  /* Estimate the cost of push down */
5299  estimate_path_cost_size(root, grouped_rel, NIL, NIL, &rows,
5300  &width, &startup_cost, &total_cost);
5301 
5302  /* Now update this information in the fpinfo */
5303  fpinfo->rows = rows;
5304  fpinfo->width = width;
5305  fpinfo->startup_cost = startup_cost;
5306  fpinfo->total_cost = total_cost;
5307 
5308  /* Create and add foreign path to the grouping relation. */
5309  grouppath = create_foreignscan_path(root,
5310  grouped_rel,
5311  grouping_target,
5312  rows,
5313  startup_cost,
5314  total_cost,
5315  NIL, /* no pathkeys */
5316  NULL, /* no required_outer */
5317  NULL,
5318  NIL); /* no fdw_private */
5319 
5320  /* Add generated path into grouped_rel by add_path(). */
5321  add_path(grouped_rel, (Path *) grouppath);
5322 }
#define NIL
Definition: pg_list.h:69
Query * parse
Definition: relation.h:155
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
bool hasAggs
Definition: parsenodes.h:123
ForeignServer * server
Definition: postgres_fdw.h:76
List * groupingSets
Definition: parsenodes.h:148
RelOptInfo * outerrel
Definition: postgres_fdw.h:89
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
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:2038
UserMapping * user
Definition: postgres_fdw.h:77
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
void * fdw_private
Definition: relation.h:637
ForeignTable * table
Definition: postgres_fdw.h:75
List * groupClause
Definition: parsenodes.h:146
bool hasHavingQual
Definition: relation.h:305
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
double Cost
Definition: nodes.h:644
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:649
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: relation.h:278

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

References add_path(), create_foreignscan_path(), create_sort_path(), estimate_path_cost_size(), get_useful_pathkeys_for_relation(), lfirst, NIL, Path::pathkeys, and pathkeys_contained_in().

Referenced by postgresGetForeignJoinPaths(), and postgresGetForeignPaths().

4728 {
4729  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
4730  ListCell *lc;
4731 
4732  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
4733 
4734  /* Create one path for each set of pathkeys we found above. */
4735  foreach(lc, useful_pathkeys_list)
4736  {
4737  double rows;
4738  int width;
4739  Cost startup_cost;
4740  Cost total_cost;
4741  List *useful_pathkeys = lfirst(lc);
4742  Path *sorted_epq_path;
4743 
4744  estimate_path_cost_size(root, rel, NIL, useful_pathkeys,
4745  &rows, &width, &startup_cost, &total_cost);
4746 
4747  /*
4748  * The EPQ path must be at least as well sorted as the path itself,
4749  * in case it gets used as input to a mergejoin.
4750  */
4751  sorted_epq_path = epq_path;
4752  if (sorted_epq_path != NULL &&
4753  !pathkeys_contained_in(useful_pathkeys,
4754  sorted_epq_path->pathkeys))
4755  sorted_epq_path = (Path *)
4756  create_sort_path(root,
4757  rel,
4758  sorted_epq_path,
4759  useful_pathkeys,
4760  -1.0);
4761 
4762  add_path(rel, (Path *)
4763  create_foreignscan_path(root, rel,
4764  NULL,
4765  rows,
4766  startup_cost,
4767  total_cost,
4768  useful_pathkeys,
4769  NULL,
4770  sorted_epq_path,
4771  NIL));
4772  }
4773 }
#define NIL
Definition: pg_list.h:69
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:792
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
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:2038
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:317
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2607
List * pathkeys
Definition: relation.h:1060
#define lfirst(lc)
Definition: pg_list.h:106
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
Definition: pg_list.h:45
double Cost
Definition: nodes.h:644

◆ analyze_row_processor()

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

Definition at line 4121 of file postgres_fdw.c.

References PgFdwAnalyzeState::anl_cxt, Assert, PgFdwAnalyzeState::attinmeta, heap_freetuple(), make_tuple_from_result_row(), MemoryContextSwitchTo(), PgFdwAnalyzeState::numrows, ReservoirStateData::randstate, PgFdwAnalyzeState::rel, reservoir_get_next_S(), PgFdwAnalyzeState::retrieved_attrs, PgFdwAnalyzeState::rows, PgFdwAnalyzeState::rowstoskip, PgFdwAnalyzeState::rstate, sampler_random_fract(), PgFdwAnalyzeState::samplerows, PgFdwAnalyzeState::targrows, and PgFdwAnalyzeState::temp_cxt.

Referenced by postgresAcquireSampleRowsFunc().

4122 {
4123  int targrows = astate->targrows;
4124  int pos; /* array index to store tuple in */
4125  MemoryContext oldcontext;
4126 
4127  /* Always increment sample row counter. */
4128  astate->samplerows += 1;
4129 
4130  /*
4131  * Determine the slot where this sample row should be stored. Set pos to
4132  * negative value to indicate the row should be skipped.
4133  */
4134  if (astate->numrows < targrows)
4135  {
4136  /* First targrows rows are always included into the sample */
4137  pos = astate->numrows++;
4138  }
4139  else
4140  {
4141  /*
4142  * Now we start replacing tuples in the sample until we reach the end
4143  * of the relation. Same algorithm as in acquire_sample_rows in
4144  * analyze.c; see Jeff Vitter's paper.
4145  */
4146  if (astate->rowstoskip < 0)
4147  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
4148 
4149  if (astate->rowstoskip <= 0)
4150  {
4151  /* Choose a random reservoir element to replace. */
4152  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
4153  Assert(pos >= 0 && pos < targrows);
4154  heap_freetuple(astate->rows[pos]);
4155  }
4156  else
4157  {
4158  /* Skip this tuple. */
4159  pos = -1;
4160  }
4161 
4162  astate->rowstoskip -= 1;
4163  }
4164 
4165  if (pos >= 0)
4166  {
4167  /*
4168  * Create sample tuple from current result row, and store it in the
4169  * position determined above. The tuple has to be created in anl_cxt.
4170  */
4171  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
4172 
4173  astate->rows[pos] = make_tuple_from_result_row(res, row,
4174  astate->rel,
4175  astate->attinmeta,
4176  astate->retrieved_attrs,
4177  NULL,
4178  astate->temp_cxt);
4179 
4180  MemoryContextSwitchTo(oldcontext);
4181  }
4182 }
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
HeapTuple * rows
Definition: postgres_fdw.c:233
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
double sampler_random_fract(SamplerRandomState randstate)
Definition: sampling.c:238
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
ReservoirStateData rstate
Definition: postgres_fdw.c:240
AttInMetadata * attinmeta
Definition: postgres_fdw.c:229
MemoryContext temp_cxt
Definition: postgres_fdw.c:244
#define Assert(condition)
Definition: c.h:688
MemoryContext anl_cxt
Definition: postgres_fdw.c:243
SamplerRandomState randstate
Definition: sampling.h:50
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:142

◆ apply_returning_filter()

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

Definition at line 3694 of file postgres_fdw.c.

References PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, DatumGetObjectId, DatumGetPointer, EState::es_trig_tuple_slot, ExecClearTuple(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreVirtualTuple(), PgFdwDirectModifyState::hasSystemCols, HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, HeapTupleSetOid, i, InvalidOid, InvalidTransactionId, tupleDesc::natts, PgFdwDirectModifyState::oidAttno, RelationGetDescr, PgFdwDirectModifyState::resultRel, slot_getallattrs(), HeapTupleData::t_data, HeapTupleData::t_self, TupleTableSlot::tts_isnull, TupleTableSlot::tts_tupleDescriptor, TupleTableSlot::tts_values, and values.

Referenced by get_returning_data().

3697 {
3698  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
3699  TupleTableSlot *resultSlot;
3700  Datum *values;
3701  bool *isnull;
3702  Datum *old_values;
3703  bool *old_isnull;
3704  int i;
3705 
3706  /*
3707  * Use the trigger tuple slot as a place to store the result tuple.
3708  */
3709  resultSlot = estate->es_trig_tuple_slot;
3710  if (resultSlot->tts_tupleDescriptor != resultTupType)
3711  ExecSetSlotDescriptor(resultSlot, resultTupType);
3712 
3713  /*
3714  * Extract all the values of the scan tuple.
3715  */
3716  slot_getallattrs(slot);
3717  old_values = slot->tts_values;
3718  old_isnull = slot->tts_isnull;
3719 
3720  /*
3721  * Prepare to build the result tuple.
3722  */
3723  ExecClearTuple(resultSlot);
3724  values = resultSlot->tts_values;
3725  isnull = resultSlot->tts_isnull;
3726 
3727  /*
3728  * Transpose data into proper fields of the result tuple.
3729  */
3730  for (i = 0; i < resultTupType->natts; i++)
3731  {
3732  int j = dmstate->attnoMap[i];
3733 
3734  if (j == 0)
3735  {
3736  values[i] = (Datum) 0;
3737  isnull[i] = true;
3738  }
3739  else
3740  {
3741  values[i] = old_values[j - 1];
3742  isnull[i] = old_isnull[j - 1];
3743  }
3744  }
3745 
3746  /*
3747  * Build the virtual tuple.
3748  */
3749  ExecStoreVirtualTuple(resultSlot);
3750 
3751  /*
3752  * If we have any system columns to return, install them.
3753  */
3754  if (dmstate->hasSystemCols)
3755  {
3756  HeapTuple resultTup = ExecMaterializeSlot(resultSlot);
3757 
3758  /* ctid */
3759  if (dmstate->ctidAttno)
3760  {
3761  ItemPointer ctid = NULL;
3762 
3763  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
3764  resultTup->t_self = *ctid;
3765  }
3766 
3767  /* oid */
3768  if (dmstate->oidAttno)
3769  {
3770  Oid oid = InvalidOid;
3771 
3772  oid = DatumGetObjectId(old_values[dmstate->oidAttno - 1]);
3773  HeapTupleSetOid(resultTup, oid);
3774  }
3775 
3776  /*
3777  * And remaining columns
3778  *
3779  * Note: since we currently don't allow the target relation to appear
3780  * on the nullable side of an outer join, any system columns wouldn't
3781  * go to NULL.
3782  *
3783  * Note: no need to care about tableoid here because it will be
3784  * initialized in ExecProcessReturning().
3785  */
3789  }
3790 
3791  /*
3792  * And return the result tuple.
3793  */
3794  return resultSlot;
3795 }
#define RelationGetDescr(relation)
Definition: rel.h:437
#define DatumGetObjectId(X)
Definition: postgres.h:483
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
Datum * tts_values
Definition: tuptable.h:125
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:79
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:703
ItemPointerData t_self
Definition: htup.h:65
bool * tts_isnull
Definition: tuptable.h:126
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:379
#define InvalidTransactionId
Definition: transam.h:31
TupleTableSlot * es_trig_tuple_slot
Definition: execnodes.h:477
void slot_getallattrs(TupleTableSlot *slot)
Definition: heaptuple.c:1238
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:121
uintptr_t Datum
Definition: postgres.h:365
void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc)
Definition: execTuples.c:281
#define InvalidOid
Definition: postgres_ext.h:36
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:761
#define DatumGetPointer(X)
Definition: postgres.h:532
static Datum values[MAXATTR]
Definition: bootstrap.c:164
int i
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:396
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:524
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:318

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 4781 of file postgres_fdw.c.

References defGetBoolean(), defGetString(), DefElem::defname, ExtractExtensionList(), PgFdwRelationInfo::fdw_startup_cost, PgFdwRelationInfo::fdw_tuple_cost, PgFdwRelationInfo::fetch_size, lfirst, ForeignServer::options, PgFdwRelationInfo::server, PgFdwRelationInfo::shippable_extensions, and PgFdwRelationInfo::use_remote_estimate.

Referenced by postgresGetForeignRelSize().

4782 {
4783  ListCell *lc;
4784 
4785  foreach(lc, fpinfo->server->options)
4786  {
4787  DefElem *def = (DefElem *) lfirst(lc);
4788 
4789  if (strcmp(def->defname, "use_remote_estimate") == 0)
4790  fpinfo->use_remote_estimate = defGetBoolean(def);
4791  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
4792  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
4793  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
4794  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
4795  else if (strcmp(def->defname, "extensions") == 0)
4796  fpinfo->shippable_extensions =
4797  ExtractExtensionList(defGetString(def), false);
4798  else if (strcmp(def->defname, "fetch_size") == 0)
4799  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
4800  }
4801 }
ForeignServer * server
Definition: postgres_fdw.h:76
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * defGetString(DefElem *def)
Definition: define.c:49
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:328
#define lfirst(lc)
Definition: pg_list.h:106
char * defname
Definition: parsenodes.h:727
List * shippable_extensions
Definition: postgres_fdw.h:72
List * options
Definition: foreign.h:53

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 4809 of file postgres_fdw.c.

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

Referenced by postgresGetForeignRelSize().

4810 {
4811  ListCell *lc;
4812 
4813  foreach(lc, fpinfo->table->options)
4814  {
4815  DefElem *def = (DefElem *) lfirst(lc);
4816 
4817  if (strcmp(def->defname, "use_remote_estimate") == 0)
4818  fpinfo->use_remote_estimate = defGetBoolean(def);
4819  else if (strcmp(def->defname, "fetch_size") == 0)
4820  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
4821  }
4822 }
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * defGetString(DefElem *def)
Definition: define.c:49
#define lfirst(lc)
Definition: pg_list.h:106
ForeignTable * table
Definition: postgres_fdw.h:75
List * options
Definition: foreign.h:68
char * defname
Definition: parsenodes.h:727

◆ build_remote_returning()

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

Definition at line 3367 of file postgres_fdw.c.

References Assert, i, InvalidAttrNumber, IsA, lappend(), lfirst, list_free(), list_length(), makeTargetEntry(), makeVar(), tupleDesc::natts, NIL, ObjectIdAttributeNumber, pull_var_clause(), PVC_INCLUDE_PLACEHOLDERS, RelationGetDescr, SelfItemPointerAttributeNumber, tlist_member(), PgFdwScanState::tupdesc, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by postgresPlanDirectModify().

3368 {
3369  bool have_wholerow = false;
3370  List *tlist = NIL;
3371  List *vars;
3372  ListCell *lc;
3373 
3374  Assert(returningList);
3375 
3376  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
3377 
3378  /*
3379  * If there's a whole-row reference to the target relation, then we'll
3380  * need all the columns of the relation.
3381  */
3382  foreach(lc, vars)
3383  {
3384  Var *var = (Var *) lfirst(lc);
3385 
3386  if (IsA(var, Var) &&
3387  var->varno == rtindex &&
3388  var->varattno == InvalidAttrNumber)
3389  {
3390  have_wholerow = true;
3391  break;
3392  }
3393  }
3394 
3395  if (have_wholerow)
3396  {
3397  TupleDesc tupdesc = RelationGetDescr(rel);
3398  int i;
3399 
3400  for (i = 1; i <= tupdesc->natts; i++)
3401  {
3402  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
3403  Var *var;
3404 
3405  /* Ignore dropped attributes. */
3406  if (attr->attisdropped)
3407  continue;
3408 
3409  var = makeVar(rtindex,
3410  i,
3411  attr->atttypid,
3412  attr->atttypmod,
3413  attr->attcollation,
3414  0);
3415 
3416  tlist = lappend(tlist,
3417  makeTargetEntry((Expr *) var,
3418  list_length(tlist) + 1,
3419  NULL,
3420  false));
3421  }
3422  }
3423 
3424  /* Now add any remaining columns to tlist. */
3425  foreach(lc, vars)
3426  {
3427  Var *var = (Var *) lfirst(lc);
3428 
3429  /*
3430  * No need for whole-row references to the target relation. We don't
3431  * need system columns other than ctid and oid either, since those are
3432  * set locally.
3433  */
3434  if (IsA(var, Var) &&
3435  var->varno == rtindex &&
3436  var->varattno <= InvalidAttrNumber &&
3439  continue; /* don't need it */
3440 
3441  if (tlist_member((Expr *) var, tlist))
3442  continue; /* already got it */
3443 
3444  tlist = lappend(tlist,
3445  makeTargetEntry((Expr *) var,
3446  list_length(tlist) + 1,
3447  NULL,
3448  false));
3449  }
3450 
3451  list_free(vars);
3452 
3453  return tlist;
3454 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:564
#define RelationGetDescr(relation)
Definition: rel.h:437
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
Definition: nodes.h:513
AttrNumber varattno
Definition: primnodes.h:168
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
Definition: primnodes.h:163
int natts
Definition: tupdesc.h:79
#define PVC_INCLUDE_PLACEHOLDERS
Definition: var.h:24
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:237
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
List * lappend(List *list, void *datum)
Definition: list.c:128
Index varno
Definition: primnodes.h:166
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:54
#define Assert(condition)
Definition: c.h:688
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89
#define InvalidAttrNumber
Definition: attnum.h:23
void list_free(List *list)
Definition: list.c:1133
int i
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
Definition: regcomp.c:224
Definition: pg_list.h:45

◆ close_cursor()

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

Definition at line 3202 of file postgres_fdw.c.

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

Referenced by postgresAcquireSampleRowsFunc(), and postgresEndForeignScan().

3203 {
3204  char sql[64];
3205  PGresult *res;
3206 
3207  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3208 
3209  /*
3210  * We don't use a PG_TRY block here, so be careful not to throw error
3211  * without releasing the PGresult.
3212  */
3213  res = pgfdw_exec_query(conn, sql);
3214  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3215  pgfdw_report_error(ERROR, res, conn, true, sql);
3216  PQclear(res);
3217 }
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
static unsigned int cursor_number
Definition: connection.c:69
void PQclear(PGresult *res)
Definition: fe-exec.c:671
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

Definition at line 5500 of file postgres_fdw.c.

References castNode, ConversionLocation::cur_attno, errcontext, EState::es_range_table, TargetEntry::expr, ForeignScan::fdw_scan_tlist, ConversionLocation::fsstate, get_attname(), get_rel_name(), IsA, list_nth_node, NameStr, tupleDesc::natts, ObjectIdAttributeNumber, PlanState::plan, ScanState::ps, ConversionLocation::rel, RelationGetDescr, RelationGetRelationName, RangeTblEntry::relid, rt_fetch, SelfItemPointerAttributeNumber, ForeignScanState::ss, PlanState::state, PgFdwScanState::tupdesc, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by make_tuple_from_result_row().

5501 {
5502  const char *attname = NULL;
5503  const char *relname = NULL;
5504  bool is_wholerow = false;
5506 
5507  if (errpos->rel)
5508  {
5509  /* error occurred in a scan against a foreign table */
5510  TupleDesc tupdesc = RelationGetDescr(errpos->rel);
5511  Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
5512 
5513  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
5514  attname = NameStr(attr->attname);
5515  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
5516  attname = "ctid";
5517  else if (errpos->cur_attno == ObjectIdAttributeNumber)
5518  attname = "oid";
5519 
5520  relname = RelationGetRelationName(errpos->rel);
5521  }
5522  else
5523  {
5524  /* error occurred in a scan against a foreign join */
5525  ForeignScanState *fsstate = errpos->fsstate;
5526  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
5527  EState *estate = fsstate->ss.ps.state;
5528  TargetEntry *tle;
5529 
5530  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
5531  errpos->cur_attno - 1);
5532 
5533  /*
5534  * Target list can have Vars and expressions. For Vars, we can get
5535  * its relation, however for expressions we can't. Thus for
5536  * expressions, just show generic context message.
5537  */
5538  if (IsA(tle->expr, Var))
5539  {
5540  RangeTblEntry *rte;
5541  Var *var = (Var *) tle->expr;
5542 
5543  rte = rt_fetch(var->varno, estate->es_range_table);
5544 
5545  if (var->varattno == 0)
5546  is_wholerow = true;
5547  else
5548  attname = get_attname(rte->relid, var->varattno, false);
5549 
5550  relname = get_rel_name(rte->relid);
5551  }
5552  else
5553  errcontext("processing expression at position %d in select list",
5554  errpos->cur_attno);
5555  }
5556 
5557  if (relname)
5558  {
5559  if (is_wholerow)
5560  errcontext("whole-row reference to foreign table \"%s\"", relname);
5561  else if (attname)
5562  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
5563  }
5564 }
ScanState ss
Definition: execnodes.h:1575
#define IsA(nodeptr, _type_)
Definition: nodes.h:564
#define RelationGetDescr(relation)
Definition: rel.h:437
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define castNode(_type_, nodeptr)
Definition: nodes.h:582
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
AttrNumber varattno
Definition: primnodes.h:168
List * fdw_scan_tlist
Definition: plannodes.h:604
EState * state
Definition: execnodes.h:870
List * es_range_table
Definition: execnodes.h:445
Definition: primnodes.h:163
int natts
Definition: tupdesc.h:79
PlanState ps
Definition: execnodes.h:1124
ForeignScanState * fsstate
Definition: postgres_fdw.c:261
#define list_nth_node(type, list, n)
Definition: pg_list.h:227
#define RelationGetRelationName(relation)
Definition: rel.h:445
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
Index varno
Definition: primnodes.h:166
Plan * plan
Definition: execnodes.h:868
Expr * expr
Definition: primnodes.h:1375
#define errcontext
Definition: elog.h:164
#define NameStr(name)
Definition: c.h:565
void * arg
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:775
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
AttrNumber cur_attno
Definition: postgres_fdw.c:253
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730

◆ convert_prep_stmt_params()

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

Definition at line 3274 of file postgres_fdw.c.

References Assert, lfirst_int, MemoryContextSwitchTo(), NIL, OutputFunctionCall(), PgFdwModifyState::p_flinfo, PgFdwModifyState::p_nums, palloc(), PointerGetDatum, reset_transmission_modes(), set_transmission_modes(), slot_getattr(), PgFdwModifyState::target_attrs, PgFdwModifyState::temp_cxt, and value.

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

3277 {
3278  const char **p_values;
3279  int pindex = 0;
3280  MemoryContext oldcontext;
3281 
3282  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
3283 
3284  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums);
3285 
3286  /* 1st parameter should be ctid, if it's in use */
3287  if (tupleid != NULL)
3288  {
3289  /* don't need set_transmission_modes for TID output */
3290  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3291  PointerGetDatum(tupleid));
3292  pindex++;
3293  }
3294 
3295  /* get following parameters from slot */
3296  if (slot != NULL && fmstate->target_attrs != NIL)
3297  {
3298  int nestlevel;
3299  ListCell *lc;
3300 
3301  nestlevel = set_transmission_modes();
3302 
3303  foreach(lc, fmstate->target_attrs)
3304  {
3305  int attnum = lfirst_int(lc);
3306  Datum value;
3307  bool isnull;
3308 
3309  value = slot_getattr(slot, attnum, &isnull);
3310  if (isnull)
3311  p_values[pindex] = NULL;
3312  else
3313  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3314  value);
3315  pindex++;
3316  }
3317 
3318  reset_transmission_modes(nestlevel);
3319  }
3320 
3321  Assert(pindex == fmstate->p_nums);
3322 
3323  MemoryContextSwitchTo(oldcontext);
3324 
3325  return p_values;
3326 }
#define NIL
Definition: pg_list.h:69
static struct @130 value
#define PointerGetDatum(X)
Definition: postgres.h:539
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int set_transmission_modes(void)
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1662
#define lfirst_int(lc)
Definition: pg_list.h:107
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:182
uintptr_t Datum
Definition: postgres.h:365
MemoryContext temp_cxt
Definition: postgres_fdw.c:185
#define Assert(condition)
Definition: c.h:688
void reset_transmission_modes(int nestlevel)
void * palloc(Size size)
Definition: mcxt.c:835
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1142

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

Definition at line 3006 of file postgres_fdw.c.

References appendStringInfo(), buf, PgFdwScanState::conn, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, ERROR, ForeignScanState::fdw_state, PgFdwScanState::fetch_ct_2, initStringInfo(), MemoryContextSwitchTo(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, PgFdwScanState::numParams, PgFdwScanState::param_exprs, PgFdwScanState::param_flinfo, PgFdwScanState::param_values, pfree(), pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), PQsendQueryParams(), process_query_params(), ScanState::ps, PlanState::ps_ExprContext, PgFdwScanState::query, ForeignScanState::ss, PgFdwScanState::tuples, and values.

Referenced by postgresIterateForeignScan().

3007 {
3008  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3009  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3010  int numParams = fsstate->numParams;
3011  const char **values = fsstate->param_values;
3012  PGconn *conn = fsstate->conn;
3014  PGresult *res;
3015 
3016  /*
3017  * Construct array of query parameter values in text format. We do the
3018  * conversions in the short-lived per-tuple context, so as not to cause a
3019  * memory leak over repeated scans.
3020  */
3021  if (numParams > 0)
3022  {
3023  MemoryContext oldcontext;
3024 
3025  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3026 
3027  process_query_params(econtext,
3028  fsstate->param_flinfo,
3029  fsstate->param_exprs,
3030  values);
3031 
3032  MemoryContextSwitchTo(oldcontext);
3033  }
3034 
3035  /* Construct the DECLARE CURSOR command */
3036  initStringInfo(&buf);
3037  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3038  fsstate->cursor_number, fsstate->query);
3039 
3040  /*
3041  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3042  * to infer types for all parameters. Since we explicitly cast every
3043  * parameter (see deparse.c), the "inference" is trivial and will produce
3044  * the desired result. This allows us to avoid assuming that the remote
3045  * server has the same OIDs we do for the parameters' types.
3046  */
3047  if (!PQsendQueryParams(conn, buf.data, numParams,
3048  NULL, values, NULL, NULL, 0))
3049  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3050 
3051  /*
3052  * Get the result, and check for success.
3053  *
3054  * We don't use a PG_TRY block here, so be careful not to throw error
3055  * without releasing the PGresult.
3056  */
3057  res = pgfdw_get_result(conn, buf.data);
3058  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3059  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3060  PQclear(res);
3061 
3062  /* Mark the cursor as created, and show no tuples have been retrieved */
3063  fsstate->cursor_exists = true;
3064  fsstate->tuples = NULL;
3065  fsstate->num_tuples = 0;
3066  fsstate->next_tuple = 0;
3067  fsstate->fetch_ct_2 = 0;
3068  fsstate->eof_reached = false;
3069 
3070  /* Clean up */
3071  pfree(buf.data);
3072 }
ScanState ss
Definition: execnodes.h:1575
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:1234
List * param_exprs
Definition: postgres_fdw.c:142
ExprContext * ps_ExprContext
Definition: execnodes.h:902
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
static void process_query_params(ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
unsigned int cursor_number
Definition: postgres_fdw.c:138
PlanState ps
Definition: execnodes.h:1124
void pfree(void *pointer)
Definition: mcxt.c:936
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define ERROR
Definition: elog.h:43
const char ** param_values
Definition: postgres_fdw.c:143
PGconn * conn
Definition: streamutil.c:46
static char * buf
Definition: pg_test_fsync.c:67
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
FmgrInfo * param_flinfo
Definition: postgres_fdw.c:141
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
void PQclear(PGresult *res)
Definition: fe-exec.c:671
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:532
HeapTuple * tuples
Definition: postgres_fdw.c:146
static Datum values[MAXATTR]
Definition: bootstrap.c:164

◆ ec_member_matches_foreign()

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

Definition at line 2977 of file postgres_fdw.c.

References ec_member_foreign_arg::already_used, ec_member_foreign_arg::current, EquivalenceMember::em_expr, equal(), and list_member().

Referenced by postgresGetForeignPaths().

2980 {
2982  Expr *expr = em->em_expr;
2983 
2984  /*
2985  * If we've identified what we're processing in the current scan, we only
2986  * want to match that expression.
2987  */
2988  if (state->current != NULL)
2989  return equal(expr, state->current);
2990 
2991  /*
2992  * Otherwise, ignore anything we've already processed.
2993  */
2994  if (list_member(state->already_used, expr))
2995  return false;
2996 
2997  /* This is the new target to process. */
2998  state->current = expr;
2999  return true;
3000 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2991
bool list_member(const List *list, const void *datum)
Definition: list.c:444
Definition: regguts.h:298
void * arg

◆ estimate_path_cost_size()

static void estimate_path_cost_size ( PlannerInfo root,
RelOptInfo foreignrel,
List param_join_conds,
List pathkeys,
double *  p_rows,
int *  p_width,
Cost p_startup_cost,
Cost p_total_cost 
)
static

Definition at line 2584 of file postgres_fdw.c.

References AGGSPLIT_SIMPLE, appendStringInfoString(), Assert, RelOptInfo::baserestrictcost, build_tlist_to_deparse(), clamp_row_est(), classifyConditions(), clauselist_selectivity(), PgFdwScanState::conn, PathTarget::cost, cost_qual_eval(), 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, Query::hasAggs, Query::havingQual, initStringInfo(), PgFdwRelationInfo::innerrel, IS_JOIN_REL, IS_UPPER_REL, JOIN_INNER, PgFdwRelationInfo::joinclause_sel, PgFdwRelationInfo::joinclauses, list_concat(), list_copy(), list_length(), PgFdwRelationInfo::local_conds_cost, PgFdwRelationInfo::local_conds_sel, MemSet, Min, NIL, PgFdwRelationInfo::outerrel, RelOptInfo::pages, PlannerInfo::parse, QualCost::per_tuple, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, ReleaseConnection(), RelOptInfo::relid, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, PgFdwScanState::retrieved_attrs, PgFdwRelationInfo::rows, RelOptInfo::rows, seq_page_cost, QualCost::startup, AggClauseCosts::transCost, RelOptInfo::tuples, PlannerInfo::upper_targets, UPPERREL_GROUP_AGG, PgFdwRelationInfo::use_remote_estimate, PgFdwRelationInfo::user, PgFdwRelationInfo::width, and PathTarget::width.

Referenced by add_foreign_grouping_paths(), add_paths_with_pathkeys_for_rel(), postgresGetForeignJoinPaths(), postgresGetForeignPaths(), and postgresGetForeignRelSize().

2590 {
2591  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2592  double rows;
2593  double retrieved_rows;
2594  int width;
2595  Cost startup_cost;
2596  Cost total_cost;
2597  Cost cpu_per_tuple;
2598 
2599  /*
2600  * If the table or the server is configured to use remote estimates,
2601  * connect to the foreign server and execute EXPLAIN to estimate the
2602  * number of rows selected by the restriction+join clauses. Otherwise,
2603  * estimate rows using whatever statistics we have locally, in a way
2604  * similar to ordinary tables.
2605  */
2606  if (fpinfo->use_remote_estimate)
2607  {
2608  List *remote_param_join_conds;
2609  List *local_param_join_conds;
2610  StringInfoData sql;
2611  PGconn *conn;
2612  Selectivity local_sel;
2613  QualCost local_cost;
2614  List *fdw_scan_tlist = NIL;
2615  List *remote_conds;
2616 
2617  /* Required only to be passed to deparseSelectStmtForRel */
2618  List *retrieved_attrs;
2619 
2620  /*
2621  * param_join_conds might contain both clauses that are safe to send
2622  * across, and clauses that aren't.
2623  */
2624  classifyConditions(root, foreignrel, param_join_conds,
2625  &remote_param_join_conds, &local_param_join_conds);
2626 
2627  /* Build the list of columns to be fetched from the foreign server. */
2628  if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
2629  fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
2630  else
2631  fdw_scan_tlist = NIL;
2632 
2633  /*
2634  * The complete list of remote conditions includes everything from
2635  * baserestrictinfo plus any extra join_conds relevant to this
2636  * particular path.
2637  */
2638  remote_conds = list_concat(list_copy(remote_param_join_conds),
2639  fpinfo->remote_conds);
2640 
2641  /*
2642  * Construct EXPLAIN query including the desired SELECT, FROM, and
2643  * WHERE clauses. Params and other-relation Vars are replaced by dummy
2644  * values, so don't request params_list.
2645  */
2646  initStringInfo(&sql);
2647  appendStringInfoString(&sql, "EXPLAIN ");
2648  deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
2649  remote_conds, pathkeys, false,
2650  &retrieved_attrs, NULL);
2651 
2652  /* Get the remote estimate */
2653  conn = GetConnection(fpinfo->user, false);
2654  get_remote_estimate(sql.data, conn, &rows, &width,
2655  &startup_cost, &total_cost);
2656  ReleaseConnection(conn);
2657 
2658  retrieved_rows = rows;
2659 
2660  /* Factor in the selectivity of the locally-checked quals */
2661  local_sel = clauselist_selectivity(root,
2662  local_param_join_conds,
2663  foreignrel->relid,
2664  JOIN_INNER,
2665  NULL);
2666  local_sel *= fpinfo->local_conds_sel;
2667 
2668  rows = clamp_row_est(rows * local_sel);
2669 
2670  /* Add in the eval cost of the locally-checked quals */
2671  startup_cost += fpinfo->local_conds_cost.startup;
2672  total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
2673  cost_qual_eval(&local_cost, local_param_join_conds, root);
2674  startup_cost += local_cost.startup;
2675  total_cost += local_cost.per_tuple * retrieved_rows;
2676  }
2677  else
2678  {
2679  Cost run_cost = 0;
2680 
2681  /*
2682  * We don't support join conditions in this mode (hence, no
2683  * parameterized paths can be made).
2684  */
2685  Assert(param_join_conds == NIL);
2686 
2687  /*
2688  * Use rows/width estimates made by set_baserel_size_estimates() for
2689  * base foreign relations and set_joinrel_size_estimates() for join
2690  * between foreign relations.
2691  */
2692  rows = foreignrel->rows;
2693  width = foreignrel->reltarget->width;
2694 
2695  /* Back into an estimate of the number of retrieved rows. */
2696  retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
2697 
2698  /*
2699  * We will come here again and again with different set of pathkeys
2700  * that caller wants to cost. We don't need to calculate the cost of
2701  * bare scan each time. Instead, use the costs if we have cached them
2702  * already.
2703  */
2704  if (fpinfo->rel_startup_cost > 0 && fpinfo->rel_total_cost > 0)
2705  {
2706  startup_cost = fpinfo->rel_startup_cost;
2707  run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
2708  }
2709  else if (IS_JOIN_REL(foreignrel))
2710  {
2711  PgFdwRelationInfo *fpinfo_i;
2712  PgFdwRelationInfo *fpinfo_o;
2713  QualCost join_cost;
2714  QualCost remote_conds_cost;
2715  double nrows;
2716 
2717  /* For join we expect inner and outer relations set */
2718  Assert(fpinfo->innerrel && fpinfo->outerrel);
2719 
2720  fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
2721  fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
2722 
2723  /* Estimate of number of rows in cross product */
2724  nrows = fpinfo_i->rows * fpinfo_o->rows;
2725  /* Clamp retrieved rows estimate to at most size of cross product */
2726  retrieved_rows = Min(retrieved_rows, nrows);
2727 
2728  /*
2729  * The cost of foreign join is estimated as cost of generating
2730  * rows for the joining relations + cost for applying quals on the
2731  * rows.
2732  */
2733 
2734  /*
2735  * Calculate the cost of clauses pushed down to the foreign server
2736  */
2737  cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
2738  /* Calculate the cost of applying join clauses */
2739  cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
2740 
2741  /*
2742  * Startup cost includes startup cost of joining relations and the
2743  * startup cost for join and other clauses. We do not include the
2744  * startup cost specific to join strategy (e.g. setting up hash
2745  * tables) since we do not know what strategy the foreign server
2746  * is going to use.
2747  */
2748  startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
2749  startup_cost += join_cost.startup;
2750  startup_cost += remote_conds_cost.startup;
2751  startup_cost += fpinfo->local_conds_cost.startup;
2752 
2753  /*
2754  * Run time cost includes:
2755  *
2756  * 1. Run time cost (total_cost - startup_cost) of relations being
2757  * joined
2758  *
2759  * 2. Run time cost of applying join clauses on the cross product
2760  * of the joining relations.
2761  *
2762  * 3. Run time cost of applying pushed down other clauses on the
2763  * result of join
2764  *
2765  * 4. Run time cost of applying nonpushable other clauses locally
2766  * on the result fetched from the foreign server.
2767  */
2768  run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
2769  run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
2770  run_cost += nrows * join_cost.per_tuple;
2771  nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
2772  run_cost += nrows * remote_conds_cost.per_tuple;
2773  run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
2774  }
2775  else if (IS_UPPER_REL(foreignrel))
2776  {
2777  PgFdwRelationInfo *ofpinfo;
2778  PathTarget *ptarget = root->upper_targets[UPPERREL_GROUP_AGG];
2779  AggClauseCosts aggcosts;
2780  double input_rows;
2781  int numGroupCols;
2782  double numGroups = 1;
2783 
2784  /*
2785  * This cost model is mixture of costing done for sorted and
2786  * hashed aggregates in cost_agg(). We are not sure which
2787  * strategy will be considered at remote side, thus for
2788  * simplicity, we put all startup related costs in startup_cost
2789  * and all finalization and run cost are added in total_cost.
2790  *
2791  * Also, core does not care about costing HAVING expressions and
2792  * adding that to the costs. So similarly, here too we are not
2793  * considering remote and local conditions for costing.
2794  */
2795 
2796  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
2797 
2798  /* Get rows and width from input rel */
2799  input_rows = ofpinfo->rows;
2800  width = ofpinfo->width;
2801 
2802  /* Collect statistics about aggregates for estimating costs. */
2803  MemSet(&aggcosts, 0, sizeof(AggClauseCosts));
2804  if (root->parse->hasAggs)
2805  {
2806  get_agg_clause_costs(root, (Node *) fpinfo->grouped_tlist,
2807  AGGSPLIT_SIMPLE, &aggcosts);
2808  get_agg_clause_costs(root, (Node *) root->parse->havingQual,
2809  AGGSPLIT_SIMPLE, &aggcosts);
2810  }
2811 
2812  /* Get number of grouping columns and possible number of groups */
2813  numGroupCols = list_length(root->parse->groupClause);
2814  numGroups = estimate_num_groups(root,
2816  fpinfo->grouped_tlist),
2817  input_rows, NULL);
2818 
2819  /*
2820  * Number of rows expected from foreign server will be same as
2821  * that of number of groups.
2822  */
2823  rows = retrieved_rows = numGroups;
2824 
2825  /*-----
2826  * Startup cost includes:
2827  * 1. Startup cost for underneath input * relation
2828  * 2. Cost of performing aggregation, per cost_agg()
2829  * 3. Startup cost for PathTarget eval
2830  *-----
2831  */
2832  startup_cost = ofpinfo->rel_startup_cost;
2833  startup_cost += aggcosts.transCost.startup;
2834  startup_cost += aggcosts.transCost.per_tuple * input_rows;
2835  startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
2836  startup_cost += ptarget->cost.startup;
2837 
2838  /*-----
2839  * Run time cost includes:
2840  * 1. Run time cost of underneath input relation
2841  * 2. Run time cost of performing aggregation, per cost_agg()
2842  * 3. PathTarget eval cost for each output row
2843  *-----
2844  */
2845  run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
2846  run_cost += aggcosts.finalCost * numGroups;
2847  run_cost += cpu_tuple_cost * numGroups;
2848  run_cost += ptarget->cost.per_tuple * numGroups;
2849  }
2850  else
2851  {
2852  /* Clamp retrieved rows estimates to at most foreignrel->tuples. */
2853  retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
2854 
2855  /*
2856  * Cost as though this were a seqscan, which is pessimistic. We
2857  * effectively imagine the local_conds are being evaluated
2858  * remotely, too.
2859  */
2860  startup_cost = 0;
2861  run_cost = 0;
2862  run_cost += seq_page_cost * foreignrel->pages;
2863 
2864  startup_cost += foreignrel->baserestrictcost.startup;
2865  cpu_per_tuple = cpu_tuple_cost + foreignrel->baserestrictcost.per_tuple;
2866  run_cost += cpu_per_tuple * foreignrel->tuples;
2867  }
2868 
2869  /*
2870  * Without remote estimates, we have no real way to estimate the cost
2871  * of generating sorted output. It could be free if the query plan
2872  * the remote side would have chosen generates properly-sorted output
2873  * anyway, but in most cases it will cost something. Estimate a value
2874  * high enough that we won't pick the sorted path when the ordering
2875  * isn't locally useful, but low enough that we'll err on the side of
2876  * pushing down the ORDER BY clause when it's useful to do so.
2877  */
2878  if (pathkeys != NIL)
2879  {
2880  startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
2881  run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
2882  }
2883 
2884  total_cost = startup_cost + run_cost;
2885  }
2886 
2887  /*
2888  * Cache the costs for scans without any pathkeys or parameterization
2889  * before adding the costs for transferring data from the foreign server.
2890  * These costs are useful for costing the join between this relation and
2891  * another foreign relation or to calculate the costs of paths with
2892  * pathkeys for this relation, when the costs can not be obtained from the
2893  * foreign server. This function will be called at least once for every
2894  * foreign relation without pathkeys and parameterization.
2895  */
2896  if (pathkeys == NIL && param_join_conds == NIL)
2897  {
2898  fpinfo->rel_startup_cost = startup_cost;
2899  fpinfo->rel_total_cost = total_cost;
2900  }
2901 
2902  /*
2903  * Add some additional cost factors to account for connection overhead
2904  * (fdw_startup_cost), transferring data across the network
2905  * (fdw_tuple_cost per retrieved row), and local manipulation of the data
2906  * (cpu_tuple_cost per retrieved row).
2907  */
2908  startup_cost += fpinfo->fdw_startup_cost;
2909  total_cost += fpinfo->fdw_startup_cost;
2910  total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
2911  total_cost += cpu_tuple_cost * retrieved_rows;
2912 
2913  /* Return results. */
2914  *p_rows = rows;
2915  *p_width = width;
2916  *p_startup_cost = startup_cost;
2917  *p_total_cost = total_cost;
2918 }
#define NIL
Definition: pg_list.h:69
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset)
Definition: selfuncs.c:3398
Query * parse
Definition: relation.h:155
double tuples
Definition: relation.h:625
void get_agg_clause_costs(PlannerInfo *root, Node *clause, AggSplit aggsplit, AggClauseCosts *costs)
Definition: clauses.c:470
bool hasAggs
Definition: parsenodes.h:123
#define Min(x, y)
Definition: c.h:846
#define IS_UPPER_REL(rel)
Definition: relation.h:571
#define IS_JOIN_REL(rel)
Definition: relation.h:566
void classifyConditions(PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds, List **local_conds)
Definition: deparse.c:204
List * list_copy(const List *oldlist)
Definition: list.c:1160
Definition: nodes.h:513
#define MemSet(start, val, len)
Definition: c.h:897
List * list_concat(List *list1, List *list2)
Definition: list.c:321
double Selectivity
Definition: nodes.h:643
QualCost transCost
Definition: relation.h:62
RelOptInfo * outerrel
Definition: postgres_fdw.h:89
Cost startup
Definition: relation.h:45
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
Cost per_tuple
Definition: relation.h:46
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:3703
PGconn * conn
Definition: streamutil.c:46
Selectivity local_conds_sel
Definition: postgres_fdw.h:54
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:157
Selectivity joinclause_sel
Definition: postgres_fdw.h:57
double cpu_operator_cost
Definition: costsize.c:108
Index relid
Definition: relation.h:613
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
UserMapping * user
Definition: postgres_fdw.h:77
void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, bool is_subquery, List **retrieved_attrs, List **params_list)
Definition: deparse.c:931
double rows
Definition: relation.h:588
Cost finalCost
Definition: relation.h:63
void * fdw_private
Definition: relation.h:637
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
BlockNumber pages
Definition: relation.h:624
#define Assert(condition)
Definition: c.h:688
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:54
QualCost cost
Definition: relation.h:978
static int list_length(const List *l)
Definition: pg_list.h:89
List * get_sortgrouplist_exprs(List *sgClauses, List *targetList)
Definition: tlist.c:395
double cpu_tuple_cost
Definition: costsize.c:106
static void get_remote_estimate(const char *sql, PGconn *conn, double *rows, int *width, Cost *startup_cost, Cost *total_cost)
RelOptInfo * innerrel
Definition: postgres_fdw.h:90
int width
Definition: relation.h:979
List * build_tlist_to_deparse(RelOptInfo *foreignrel)
Definition: deparse.c:874
List * groupClause
Definition: parsenodes.h:146
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:99
Node * havingQual
Definition: parsenodes.h:150
double clamp_row_est(double nrows)
Definition: costsize.c:178
double seq_page_cost
Definition: costsize.c:104
Definition: pg_list.h:45
struct PathTarget * reltarget
Definition: relation.h:596
QualCost baserestrictcost
Definition: relation.h:646
double Cost
Definition: nodes.h:644
QualCost local_conds_cost
Definition: postgres_fdw.h:53
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: relation.h:278

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

Definition at line 3495 of file postgres_fdw.c.

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

Referenced by postgresIterateDirectModify().

3496 {
3498  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3499  int numParams = dmstate->numParams;
3500  const char **values = dmstate->param_values;
3501 
3502  /*
3503  * Construct array of query parameter values in text format.
3504  */
3505  if (numParams > 0)
3506  process_query_params(econtext,
3507  dmstate->param_flinfo,
3508  dmstate->param_exprs,
3509  values);
3510 
3511  /*
3512  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3513  * to infer types for all parameters. Since we explicitly cast every
3514  * parameter (see deparse.c), the "inference" is trivial and will produce
3515  * the desired result. This allows us to avoid assuming that the remote
3516  * server has the same OIDs we do for the parameters' types.
3517  */
3518  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
3519  NULL, values, NULL, NULL, 0))
3520  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
3521 
3522  /*
3523  * Get the result, and check for success.
3524  *
3525  * We don't use a PG_TRY block here, so be careful not to throw error
3526  * without releasing the PGresult.
3527  */
3528  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
3529  if (PQresultStatus(dmstate->result) !=
3531  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
3532  dmstate->query);
3533 
3534  /* Get the number of rows affected. */
3535  if (dmstate->has_returning)
3536  dmstate->num_tuples = PQntuples(dmstate->result);
3537  else
3538  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
3539 }
ScanState ss
Definition: execnodes.h:1575
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:1234
const char ** param_values
Definition: postgres_fdw.c:207
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3065
ExprContext * ps_ExprContext
Definition: execnodes.h:902
static void process_query_params(ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
PlanState ps
Definition: execnodes.h:1124
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:532
static Datum values[MAXATTR]
Definition: bootstrap.c:164

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3078 of file postgres_fdw.c.

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

Referenced by postgresIterateForeignScan().

3079 {
3080  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3081  PGresult *volatile res = NULL;
3082  MemoryContext oldcontext;
3083 
3084  /*
3085  * We'll store the tuples in the batch_cxt. First, flush the previous
3086  * batch.
3087  */
3088  fsstate->tuples = NULL;
3089  MemoryContextReset(fsstate->batch_cxt);
3090  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3091 
3092  /* PGresult must be released before leaving this function. */
3093  PG_TRY();
3094  {
3095  PGconn *conn = fsstate->conn;
3096  char sql[64];
3097  int numrows;
3098  int i;
3099 
3100  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3101  fsstate->fetch_size, fsstate->cursor_number);
3102 
3103  res = pgfdw_exec_query(conn, sql);
3104  /* On error, report the original query, not the FETCH. */
3105  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3106  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3107 
3108  /* Convert the data into HeapTuples */
3109  numrows = PQntuples(res);
3110  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3111  fsstate->num_tuples = numrows;
3112  fsstate->next_tuple = 0;
3113 
3114  for (i = 0; i < numrows; i++)
3115  {
3116  Assert(IsA(node->ss.ps.plan, ForeignScan));
3117 
3118  fsstate->tuples[i] =
3120  fsstate->rel,
3121  fsstate->attinmeta,
3122  fsstate->retrieved_attrs,
3123  node,
3124  fsstate->temp_cxt);
3125  }
3126 
3127  /* Update fetch_ct_2 */
3128  if (fsstate->fetch_ct_2 < 2)
3129  fsstate->fetch_ct_2++;
3130 
3131  /* Must be EOF if we didn't get as many tuples as we asked for. */
3132  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3133 
3134  PQclear(res);
3135  res = NULL;
3136  }
3137  PG_CATCH();
3138  {
3139  if (res)
3140  PQclear(res);
3141  PG_RE_THROW();
3142  }
3143  PG_END_TRY();
3144 
3145  MemoryContextSwitchTo(oldcontext);
3146 }
ScanState ss
Definition: execnodes.h:1575
#define IsA(nodeptr, _type_)
Definition: nodes.h:564
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:134
List * retrieved_attrs
Definition: postgres_fdw.c:134
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
unsigned int cursor_number
Definition: postgres_fdw.c:138
PlanState ps
Definition: execnodes.h:1124
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:46
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
AttInMetadata * attinmeta
Definition: postgres_fdw.c:130
void * palloc0(Size size)
Definition: mcxt.c:864
MemoryContext temp_cxt
Definition: postgres_fdw.c:156
Plan * plan
Definition: execnodes.h:868
void PQclear(PGresult *res)
Definition: fe-exec.c:671
#define PG_CATCH()
Definition: elog.h:293
#define Assert(condition)
Definition: c.h:688
HeapTuple * tuples
Definition: postgres_fdw.c:146
#define PG_RE_THROW()
Definition: elog.h:314
int i
#define PG_TRY()
Definition: elog.h:284
MemoryContext batch_cxt
Definition: postgres_fdw.c:155
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
#define PG_END_TRY()
Definition: elog.h:300

◆ find_em_expr_for_rel()

Expr* find_em_expr_for_rel ( EquivalenceClass ec,
RelOptInfo rel 
)

Definition at line 5571 of file postgres_fdw.c.

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

Referenced by appendOrderByClause(), and get_useful_pathkeys_for_relation().

5572 {
5573  ListCell *lc_em;
5574 
5575  foreach(lc_em, ec->ec_members)
5576  {
5577  EquivalenceMember *em = lfirst(lc_em);
5578 
5579  if (bms_is_subset(em->em_relids, rel->relids))
5580  {
5581  /*
5582  * If there is more than one equivalence member whose Vars are
5583  * taken entirely from this relation, we'll be content to choose
5584  * any one of those.
5585  */
5586  return em->em_expr;
5587  }
5588  }
5589 
5590  /* We didn't find any suitable equivalence class expression */
5591  return NULL;
5592 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:352
Relids relids
Definition: relation.h:585
Relids em_relids
Definition: relation.h:915
#define lfirst(lc)
Definition: pg_list.h:106
List * ec_members
Definition: relation.h:866

◆ foreign_grouping_ok()

static bool foreign_grouping_ok ( PlannerInfo root,
RelOptInfo grouped_rel 
)
static

Definition at line 5016 of file postgres_fdw.c.

References add_to_flat_tlist(), appendStringInfo(), Assert, RestrictInfo::clause, StringInfoData::data, PathTarget::exprs, RelOptInfo::fdw_private, get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), Query::groupClause, PgFdwRelationInfo::grouped_tlist, Query::groupingSets, PlannerInfo::hasHavingQual, Query::havingQual, i, is_foreign_expr(), IsA, lappend(), lfirst, lfirst_node, list_concat(), list_length(), list_make1, PgFdwRelationInfo::local_conds, make_restrictinfo(), makeStringInfo(), makeTargetEntry(), NIL, PgFdwRelationInfo::outerrel, PlannerInfo::parse, pull_var_clause(), PgFdwRelationInfo::pushdown_safe, PVC_INCLUDE_AGGREGATES, PlannerInfo::qual_security_level, PgFdwScanState::query, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_name, RelOptInfo::relids, PgFdwRelationInfo::remote_conds, TargetEntry::ressortgroupref, PlannerInfo::upper_targets, and UPPERREL_GROUP_AGG.

Referenced by add_foreign_grouping_paths().

5017 {
5018  Query *query = root->parse;
5019  PathTarget *grouping_target = root->upper_targets[UPPERREL_GROUP_AGG];
5020  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
5021  PgFdwRelationInfo *ofpinfo;
5022  List *aggvars;
5023  ListCell *lc;
5024  int i;
5025  List *tlist = NIL;
5026 
5027  /* We currently don't support pushing Grouping Sets. */
5028  if (query->groupingSets)
5029  return false;
5030 
5031  /* Get the fpinfo of the underlying scan relation. */
5032  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
5033 
5034  /*
5035  * If underlying scan relation has any local conditions, those conditions
5036  * are required to be applied before performing aggregation. Hence the
5037  * aggregate cannot be pushed down.
5038  */
5039  if (ofpinfo->local_conds)
5040  return false;
5041 
5042  /*
5043  * Examine grouping expressions, as well as other expressions we'd need to
5044  * compute, and check whether they are safe to push down to the foreign
5045  * server. All GROUP BY expressions will be part of the grouping target
5046  * and thus there is no need to search for them separately. Add grouping
5047  * expressions into target list which will be passed to foreign server.
5048  */
5049  i = 0;
5050  foreach(lc, grouping_target->exprs)
5051  {
5052  Expr *expr = (Expr *) lfirst(lc);
5053  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
5054  ListCell *l;
5055 
5056  /* Check whether this expression is part of GROUP BY clause */
5057  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
5058  {
5059  TargetEntry *tle;
5060 
5061  /*
5062  * If any GROUP BY expression is not shippable, then we cannot
5063  * push down aggregation to the foreign server.
5064  */
5065  if (!is_foreign_expr(root, grouped_rel, expr))
5066  return false;
5067 
5068  /*
5069  * Pushable, so add to tlist. We need to create a TLE for this
5070  * expression and apply the sortgroupref to it. We cannot use
5071  * add_to_flat_tlist() here because that avoids making duplicate
5072  * entries in the tlist. If there are duplicate entries with
5073  * distinct sortgrouprefs, we have to duplicate that situation in
5074  * the output tlist.
5075  */
5076  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
5077  tle->ressortgroupref = sgref;
5078  tlist = lappend(tlist, tle);
5079  }
5080  else
5081  {
5082  /*
5083  * Non-grouping expression we need to compute. Is it shippable?
5084  */
5085  if (is_foreign_expr(root, grouped_rel, expr))
5086  {
5087  /* Yes, so add to tlist as-is; OK to suppress duplicates */
5088  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5089  }
5090  else
5091  {
5092  /* Not pushable as a whole; extract its Vars and aggregates */
5093  aggvars = pull_var_clause((Node *) expr,
5095 
5096  /*
5097  * If any aggregate expression is not shippable, then we
5098  * cannot push down aggregation to the foreign server.
5099  */
5100  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
5101  return false;
5102 
5103  /*
5104  * Add aggregates, if any, into the targetlist. Plain Vars
5105  * outside an aggregate can be ignored, because they should be
5106  * either same as some GROUP BY column or part of some GROUP
5107  * BY expression. In either case, they are already part of
5108  * the targetlist and thus no need to add them again. In fact
5109  * including plain Vars in the tlist when they do not match a
5110  * GROUP BY column would cause the foreign server to complain
5111  * that the shipped query is invalid.
5112  */
5113  foreach(l, aggvars)
5114  {
5115  Expr *expr = (Expr *) lfirst(l);
5116 
5117  if (IsA(expr, Aggref))
5118  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5119  }
5120  }
5121  }
5122 
5123  i++;
5124  }
5125 
5126  /*
5127  * Classify the pushable and non-pushable HAVING clauses and save them in
5128  * remote_conds and local_conds of the grouped rel's fpinfo.
5129  */
5130  if (root->hasHavingQual && query->havingQual)
5131  {
5132  ListCell *lc;
5133 
5134  foreach(lc, (List *) query->havingQual)
5135  {
5136  Expr *expr = (Expr *) lfirst(lc);
5137  RestrictInfo *rinfo;
5138 
5139  /*
5140  * Currently, the core code doesn't wrap havingQuals in
5141  * RestrictInfos, so we must make our own.
5142  */
5143  Assert(!IsA(expr, RestrictInfo));
5144  rinfo = make_restrictinfo(expr,
5145  true,
5146  false,
5147  false,
5148  root->qual_security_level,
5149  grouped_rel->relids,
5150  NULL,
5151  NULL);
5152  if (is_foreign_expr(root, grouped_rel, expr))
5153  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5154  else
5155  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5156  }
5157  }
5158 
5159  /*
5160  * If there are any local conditions, pull Vars and aggregates from it and
5161  * check whether they are safe to pushdown or not.
5162  */
5163  if (fpinfo->local_conds)
5164  {
5165  List *aggvars = NIL;
5166  ListCell *lc;
5167 
5168  foreach(lc, fpinfo->local_conds)
5169  {
5170  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5171 
5172  aggvars = list_concat(aggvars,
5173  pull_var_clause((Node *) rinfo->clause,
5175  }
5176 
5177  foreach(lc, aggvars)
5178  {
5179  Expr *expr = (Expr *) lfirst(lc);
5180 
5181  /*
5182  * If aggregates within local conditions are not safe to push
5183  * down, then we cannot push down the query. Vars are already
5184  * part of GROUP BY clause which are checked above, so no need to
5185  * access them again here.
5186  */
5187  if (IsA(expr, Aggref))
5188  {
5189  if (!is_foreign_expr(root, grouped_rel, expr))
5190  return false;
5191 
5192  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5193  }
5194  }
5195  }
5196 
5197  /* Store generated targetlist */
5198  fpinfo->grouped_tlist = tlist;
5199 
5200  /* Safe to pushdown */
5201  fpinfo->pushdown_safe = true;
5202 
5203  /*
5204  * Set cached relation costs to some negative value, so that we can detect
5205  * when they are set to some sensible costs, during one (usually the
5206  * first) of the calls to estimate_path_cost_size().
5207  */
5208  fpinfo->rel_startup_cost = -1;
5209  fpinfo->rel_total_cost = -1;
5210 
5211  /*
5212  * Set the string describing this grouped relation to be used in EXPLAIN
5213  * output of corresponding ForeignScan.
5214  */
5215  fpinfo->relation_name = makeStringInfo();
5216  appendStringInfo(fpinfo->relation_name, "Aggregate on (%s)",
5217  ofpinfo->relation_name->data);
5218 
5219  return true;
5220 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:564
Query * parse
Definition: relation.h:155
RestrictInfo * make_restrictinfo(Expr *clause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids)
Definition: restrictinfo.c:57
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
List * groupingSets
Definition: parsenodes.h:148
Definition: nodes.h:513
List * list_concat(List *list1, List *list2)
Definition: list.c:321
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
RelOptInfo * outerrel
Definition: postgres_fdw.h:89
#define PVC_INCLUDE_AGGREGATES
Definition: var.h:20
#define list_make1(x1)
Definition: pg_list.h:139
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define lfirst_node(type, lc)
Definition: pg_list.h:109
Relids relids
Definition: relation.h:585
#define get_pathtarget_sortgroupref(target, colno)
Definition: relation.h:983
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:237
List * lappend(List *list, void *datum)
Definition: list.c:128
Expr * clause
Definition: relation.h:1847
List * exprs
Definition: relation.h:976
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:446
unsigned int Index
Definition: c.h:431
StringInfo relation_name
Definition: postgres_fdw.h:86
void * fdw_private
Definition: relation.h:637
#define Assert(condition)
Definition: c.h:688
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition: tlist.c:135
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89
Index qual_security_level
Definition: relation.h:297
List * groupClause
Definition: parsenodes.h:146
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
int i
Index ressortgroupref
Definition: primnodes.h:1378
bool hasHavingQual
Definition: relation.h:305
Node * havingQual
Definition: parsenodes.h:150
Definition: pg_list.h:45
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: relation.h:278

◆ foreign_join_ok()

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

Definition at line 4471 of file postgres_fdw.c.

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

Referenced by postgresGetForeignJoinPaths().

4474 {
4475  PgFdwRelationInfo *fpinfo;
4476  PgFdwRelationInfo *fpinfo_o;
4477  PgFdwRelationInfo *fpinfo_i;
4478  ListCell *lc;
4479  List *joinclauses;
4480 
4481  /*
4482  * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
4483  * Constructing queries representing SEMI and ANTI joins is hard, hence
4484  * not considered right now.
4485  */
4486  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
4487  jointype != JOIN_RIGHT && jointype != JOIN_FULL)
4488  return false;
4489 
4490  /*
4491  * If either of the joining relations is marked as unsafe to pushdown, the
4492  * join can not be pushed down.
4493  */
4494  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
4495  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
4496  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
4497  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
4498  !fpinfo_i || !fpinfo_i->pushdown_safe)
4499  return false;
4500 
4501  /*
4502  * If joining relations have local conditions, those conditions are
4503  * required to be applied before joining the relations. Hence the join can
4504  * not be pushed down.
4505  */
4506  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
4507  return false;
4508 
4509  /*
4510  * Merge FDW options. We might be tempted to do this after we have deemed
4511  * the foreign join to be OK. But we must do this beforehand so that we
4512  * know which quals can be evaluated on the foreign server, which might
4513  * depend on shippable_extensions.
4514  */
4515  fpinfo->server = fpinfo_o->server;
4516  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
4517 
4518  /*
4519  * Separate restrict list into join quals and pushed-down (other) quals.
4520  *
4521  * Join quals belonging to an outer join must all be shippable, else we
4522  * cannot execute the join remotely. Add such quals to 'joinclauses'.
4523  *
4524  * Add other quals to fpinfo->remote_conds if they are shippable, else to
4525  * fpinfo->local_conds. In an inner join it's okay to execute conditions
4526  * either locally or remotely; the same is true for pushed-down conditions
4527  * at an outer join.
4528  *
4529  * Note we might return failure after having already scribbled on
4530  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
4531  * won't consult those lists again if we deem the join unshippable.
4532  */
4533  joinclauses = NIL;
4534  foreach(lc, extra->restrictlist)
4535  {
4536  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
4537  bool is_remote_clause = is_foreign_expr(root, joinrel,
4538  rinfo->clause);
4539 
4540  if (IS_OUTER_JOIN(jointype) && !rinfo->is_pushed_down)
4541  {
4542  if (!is_remote_clause)
4543  return false;
4544  joinclauses = lappend(joinclauses, rinfo);
4545  }
4546  else
4547  {
4548  if (is_remote_clause)
4549  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
4550  else
4551  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
4552  }
4553  }
4554 
4555  /*
4556  * deparseExplicitTargetList() isn't smart enough to handle anything other
4557  * than a Var. In particular, if there's some PlaceHolderVar that would
4558  * need to be evaluated within this join tree (because there's an upper
4559  * reference to a quantity that may go to NULL as a result of an outer
4560  * join), then we can't try to push the join down because we'll fail when
4561  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
4562  * needs to be evaluated *at the top* of this join tree is OK, because we
4563  * can do that locally after fetching the results from the remote side.
4564  */
4565  foreach(lc, root->placeholder_list)
4566  {
4567  PlaceHolderInfo *phinfo = lfirst(lc);
4568  Relids relids = joinrel->relids;
4569 
4570  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
4571  bms_nonempty_difference(relids, phinfo->ph_eval_at))
4572  return false;
4573  }
4574 
4575  /* Save the join clauses, for later use. */
4576  fpinfo->joinclauses = joinclauses;
4577 
4578  fpinfo->outerrel = outerrel;
4579  fpinfo->innerrel = innerrel;
4580  fpinfo->jointype = jointype;
4581 
4582  /*
4583  * By default, both the input relations are not required to be deparsed as
4584  * subqueries, but there might be some relations covered by the input
4585  * relations that are required to be deparsed as subqueries, so save the
4586  * relids of those relations for later use by the deparser.
4587  */
4588  fpinfo->make_outerrel_subquery = false;
4589  fpinfo->make_innerrel_subquery = false;
4590  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
4591  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
4593  fpinfo_i->lower_subquery_rels);
4594 
4595  /*
4596  * Pull the other remote conditions from the joining relations into join
4597  * clauses or other remote clauses (remote_conds) of this relation
4598  * wherever possible. This avoids building subqueries at every join step.
4599  *
4600  * For an inner join, clauses from both the relations are added to the
4601  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
4602  * the outer side are added to remote_conds since those can be evaluated
4603  * after the join is evaluated. The clauses from inner side are added to
4604  * the joinclauses, since they need to be evaluated while constructing the
4605  * join.
4606  *
4607  * For a FULL OUTER JOIN, the other clauses from either relation can not
4608  * be added to the joinclauses or remote_conds, since each relation acts
4609  * as an outer relation for the other.
4610  *
4611  * The joining sides can not have local conditions, thus no need to test
4612  * shippability of the clauses being pulled up.
4613  */
4614  switch (jointype)
4615  {
4616  case JOIN_INNER:
4617  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4618  list_copy(fpinfo_i->remote_conds));
4619  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4620  list_copy(fpinfo_o->remote_conds));
4621  break;
4622 
4623  case JOIN_LEFT:
4624  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
4625  list_copy(fpinfo_i->remote_conds));
4626  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4627  list_copy(fpinfo_o->remote_conds));
4628  break;
4629 
4630  case JOIN_RIGHT:
4631  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
4632  list_copy(fpinfo_o->remote_conds));
4633  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4634  list_copy(fpinfo_i->remote_conds));
4635  break;
4636 
4637  case JOIN_FULL:
4638 
4639  /*
4640  * In this case, if any of the input relations has conditions, we
4641  * need to deparse that relation as a subquery so that the
4642  * conditions can be evaluated before the join. Remember it in
4643  * the fpinfo of this relation so that the deparser can take
4644  * appropriate action. Also, save the relids of base relations
4645  * covered by that relation for later use by the deparser.
4646  */
4647  if (fpinfo_o->remote_conds)
4648  {
4649  fpinfo->make_outerrel_subquery = true;
4650  fpinfo->lower_subquery_rels =
4652  outerrel->relids);
4653  }
4654  if (fpinfo_i->remote_conds)
4655  {
4656  fpinfo->make_innerrel_subquery = true;
4657  fpinfo->lower_subquery_rels =
4659  innerrel->relids);
4660  }
4661  break;
4662 
4663  default:
4664  /* Should not happen, we have just checked this above */
4665  elog(ERROR, "unsupported join type %d", jointype);
4666  }
4667 
4668  /*
4669  * For an inner join, all restrictions can be treated alike. Treating the
4670  * pushed down conditions as join conditions allows a top level full outer
4671  * join to be deparsed without requiring subqueries.
4672  */
4673  if (jointype == JOIN_INNER)
4674  {
4675  Assert(!fpinfo->joinclauses);
4676  fpinfo->joinclauses = fpinfo->remote_conds;
4677  fpinfo->remote_conds = NIL;
4678  }
4679 
4680  /* Mark that this join can be pushed down safely */
4681  fpinfo->pushdown_safe = true;
4682 
4683  /* Get user mapping */
4684  if (fpinfo->use_remote_estimate)
4685  {
4686  if (fpinfo_o->use_remote_estimate)
4687  fpinfo->user = fpinfo_o->user;
4688  else
4689  fpinfo->user = fpinfo_i->user;
4690  }
4691  else
4692  fpinfo->user = NULL;
4693 
4694  /*
4695  * Set cached relation costs to some negative value, so that we can detect
4696  * when they are set to some sensible costs, during one (usually the
4697  * first) of the calls to estimate_path_cost_size().
4698  */
4699  fpinfo->rel_startup_cost = -1;
4700  fpinfo->rel_total_cost = -1;
4701 
4702  /*
4703  * Set the string describing this join relation to be used in EXPLAIN
4704  * output of corresponding ForeignScan.
4705  */
4706  fpinfo->relation_name = makeStringInfo();
4707  appendStringInfo(fpinfo->relation_name, "(%s) %s JOIN (%s)",
4708  fpinfo_o->relation_name->data,
4709  get_jointype_name(fpinfo->jointype),
4710  fpinfo_i->relation_name->data);
4711 
4712  /*
4713  * Set the relation index. This is defined as the position of this
4714  * joinrel in the join_rel_list list plus the length of the rtable list.
4715  * Note that since this joinrel is at the end of the join_rel_list list
4716  * when we are called, we can get the position by list_length.
4717  */
4718  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
4719  fpinfo->relation_index =
4721 
4722  return true;
4723 }
#define NIL
Definition: pg_list.h:69
Query * parse
Definition: relation.h:155
Relids ph_eval_at
Definition: relation.h:2168
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
ForeignServer * server
Definition: postgres_fdw.h:76
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:726
List * list_copy(const List *oldlist)
Definition: list.c:1160
Relids lower_subquery_rels
Definition: postgres_fdw.h:103
List * join_rel_list
Definition: relation.h:215
List * list_concat(List *list1, List *list2)
Definition: list.c:321
RelOptInfo * outerrel
Definition: postgres_fdw.h:89
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
bool make_outerrel_subquery
Definition: postgres_fdw.h:99
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:352
#define lfirst_node(type, lc)
Definition: pg_list.h:109
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1317
Relids relids
Definition: relation.h:585
List * lappend(List *list, void *datum)
Definition: list.c:128
Expr * clause
Definition: relation.h:1847
UserMapping * user
Definition: postgres_fdw.h:77
List * restrictlist
Definition: relation.h:2284
bool is_pushed_down
Definition: relation.h:1849
StringInfo relation_name
Definition: postgres_fdw.h:86
void * fdw_private
Definition: relation.h:637
#define Assert(condition)
Definition: c.h:688
#define lfirst(lc)
Definition: pg_list.h:106
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:262
static int list_length(const List *l)
Definition: pg_list.h:89
RelOptInfo * innerrel
Definition: postgres_fdw.h:90
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
List * placeholder_list
Definition: relation.h:258
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:799
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:538

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

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

Referenced by estimate_path_cost_size().

2928 {
2929  PGresult *volatile res = NULL;
2930 
2931  /* PGresult must be released before leaving this function. */
2932  PG_TRY();
2933  {
2934  char *line;
2935  char *p;
2936  int n;
2937 
2938  /*
2939  * Execute EXPLAIN remotely.
2940  */
2941  res = pgfdw_exec_query(conn, sql);
2942  if (PQresultStatus(res) != PGRES_TUPLES_OK)
2943  pgfdw_report_error(ERROR, res, conn, false, sql);
2944 
2945  /*
2946  * Extract cost numbers for topmost plan node. Note we search for a
2947  * left paren from the end of the line to avoid being confused by
2948  * other uses of parentheses.
2949  */
2950  line = PQgetvalue(res, 0, 0);
2951  p = strrchr(line, '(');
2952  if (p == NULL)
2953  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
2954  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
2955  startup_cost, total_cost, rows, width);
2956  if (n != 4)
2957  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
2958 
2959  PQclear(res);
2960  res = NULL;
2961  }
2962  PG_CATCH();
2963  {
2964  if (res)
2965  PQclear(res);
2966  PG_RE_THROW();
2967  }
2968  PG_END_TRY();
2969 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3118
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
#define ERROR
Definition: elog.h:43
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
void PQclear(PGresult *res)
Definition: fe-exec.c:671
#define PG_CATCH()
Definition: elog.h:293
#define PG_RE_THROW()
Definition: elog.h:314
#define elog
Definition: elog.h:219
#define PG_TRY()
Definition: elog.h:284
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
#define PG_END_TRY()
Definition: elog.h:300

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 3545 of file postgres_fdw.c.

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

Referenced by postgresIterateDirectModify().

3546 {
3548  EState *estate = node->ss.ps.state;
3549  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
3550  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
3551  TupleTableSlot *resultSlot;
3552 
3553  Assert(resultRelInfo->ri_projectReturning);
3554 
3555  /* If we didn't get any tuples, must be end of data. */
3556  if (dmstate->next_tuple >= dmstate->num_tuples)
3557  return ExecClearTuple(slot);
3558 
3559  /* Increment the command es_processed count if necessary. */
3560  if (dmstate->set_processed)
3561  estate->es_processed += 1;
3562 
3563  /*
3564  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
3565  * tuple. (has_returning is false when the local query is of the form
3566  * "UPDATE/DELETE .. RETURNING 1" for example.)
3567  */
3568  if (!dmstate->has_returning)
3569  {
3570  ExecStoreAllNullTuple(slot);
3571  resultSlot = slot;
3572  }
3573  else
3574  {
3575  /*
3576  * On error, be sure to release the PGresult on the way out. Callers
3577  * do not have PG_TRY blocks to ensure this happens.
3578  */
3579  PG_TRY();
3580  {
3581  HeapTuple newtup;
3582 
3583  newtup = make_tuple_from_result_row(dmstate->result,
3584  dmstate->next_tuple,
3585  dmstate->rel,
3586  dmstate->attinmeta,
3587  dmstate->retrieved_attrs,
3588  node,
3589  dmstate->temp_cxt);
3590  ExecStoreTuple(newtup, slot, InvalidBuffer, false);
3591  }
3592  PG_CATCH();
3593  {
3594  if (dmstate->result)
3595  PQclear(dmstate->result);
3596  PG_RE_THROW();
3597  }
3598  PG_END_TRY();
3599 
3600  /* Get the updated/deleted tuple. */
3601  if (dmstate->rel)
3602  resultSlot = slot;
3603  else
3604  resultSlot = apply_returning_filter(dmstate, slot, estate);
3605  }
3606  dmstate->next_tuple++;
3607 
3608  /* Make slot available for evaluation of the local query RETURNING list. */
3609  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
3610  resultSlot;
3611 
3612  return slot;
3613 }
ScanState ss
Definition: execnodes.h:1575
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:356
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:548
AttInMetadata * attinmeta
Definition: postgres_fdw.c:194
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
#define InvalidBuffer
Definition: buf.h:25
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1127
EState * state
Definition: execnodes.h:870
PlanState ps
Definition: execnodes.h:1124
MemoryContext temp_cxt
Definition: postgres_fdw.c:220
void PQclear(PGresult *res)
Definition: fe-exec.c:671
#define PG_CATCH()
Definition: elog.h:293
#define Assert(condition)
Definition: c.h:688
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, TupleTableSlot *slot, EState *estate)
#define PG_RE_THROW()
Definition: elog.h:314
#define PG_TRY()
Definition: elog.h:284
#define PG_END_TRY()
Definition: elog.h:300

◆ get_useful_ecs_for_relation()

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

Definition at line 696 of file postgres_fdw.c.

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

Referenced by get_useful_pathkeys_for_relation().

697 {
698  List *useful_eclass_list = NIL;
699  ListCell *lc;
700  Relids relids;
701 
702  /*
703  * First, consider whether any active EC is potentially useful for a merge
704  * join against this relation.
705  */
706  if (rel->has_eclass_joins)
707  {
708  foreach(lc, root->eq_classes)
709  {
710  EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc);
711 
712  if (eclass_useful_for_merging(root, cur_ec, rel))
713  useful_eclass_list = lappend(useful_eclass_list, cur_ec);
714  }
715  }
716 
717  /*
718  * Next, consider whether there are any non-EC derivable join clauses that
719  * are merge-joinable. If the joininfo list is empty, we can exit
720  * quickly.
721  */
722  if (rel->joininfo == NIL)
723  return useful_eclass_list;
724 
725  /* If this is a child rel, we must use the topmost parent rel to search. */
726  if (IS_OTHER_REL(rel))
727  {
729  relids = rel->top_parent_relids;
730  }
731  else
732  relids = rel->relids;
733 
734  /* Check each join clause in turn. */
735  foreach(lc, rel->joininfo)
736  {
737  RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
738 
739  /* Consider only mergejoinable clauses */
740  if (restrictinfo->mergeopfamilies == NIL)
741  continue;
742 
743  /* Make sure we've got canonical ECs. */
744  update_mergeclause_eclasses(root, restrictinfo);
745 
746  /*
747  * restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
748  * that left_ec and right_ec will be initialized, per comments in
749  * distribute_qual_to_rels.
750  *
751  * We want to identify which side of this merge-joinable clause
752  * contains columns from the relation produced by this RelOptInfo. We
753  * test for overlap, not containment, because there could be extra
754  * relations on either side. For example, suppose we've got something
755  * like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
756  * A.y = D.y. The input rel might be the joinrel between A and B, and
757  * we'll consider the join clause A.y = D.y. relids contains a
758  * relation not involved in the join class (B) and the equivalence
759  * class for the left-hand side of the clause contains a relation not
760  * involved in the input rel (C). Despite the fact that we have only
761  * overlap and not containment in either direction, A.y is potentially
762  * useful as a sort column.
763  *
764  * Note that it's even possible that relids overlaps neither side of
765  * the join clause. For example, consider A LEFT JOIN B ON A.x = B.x
766  * AND A.x = 1. The clause A.x = 1 will appear in B's joininfo list,
767  * but overlaps neither side of B. In that case, we just skip this
768  * join clause, since it doesn't suggest a useful sort order for this
769  * relation.
770  */
771  if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
772  useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
773  restrictinfo->right_ec);
774  else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
775  useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
776  restrictinfo->left_ec);
777  }
778 
779  return useful_eclass_list;
780 }
bool has_eclass_joins
Definition: relation.h:651
#define NIL
Definition: pg_list.h:69
bool eclass_useful_for_merging(PlannerInfo *root, EquivalenceClass *eclass, RelOptInfo *rel)
Definition: equivclass.c:2436
#define IS_OTHER_REL(rel)
Definition: relation.h:574
EquivalenceClass * right_ec
Definition: relation.h:1896
List * mergeopfamilies
Definition: relation.h:1892
List * list_append_unique_ptr(List *list, void *datum)
Definition: list.c:975
List * joininfo
Definition: relation.h:649
Relids ec_relids
Definition: relation.h:869
Relids relids
Definition: relation.h:585
List * lappend(List *list, void *datum)
Definition: list.c:128
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:707
#define Assert(condition)
Definition: c.h:688
#define lfirst(lc)
Definition: pg_list.h:106
List * eq_classes
Definition: relation.h:235
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:487
EquivalenceClass * left_ec
Definition: relation.h:1895
Definition: pg_list.h:45
void update_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
Definition: pathkeys.c:968
Relids top_parent_relids
Definition: relation.h:654

◆ get_useful_pathkeys_for_relation()

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

Definition at line 792 of file postgres_fdw.c.

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

Referenced by add_paths_with_pathkeys_for_rel().

793 {
794  List *useful_pathkeys_list = NIL;
795  List *useful_eclass_list;
797  EquivalenceClass *query_ec = NULL;
798  ListCell *lc;
799 
800  /*
801  * Pushing the query_pathkeys to the remote server is always worth
802  * considering, because it might let us avoid a local sort.
803  */
804  if (root->query_pathkeys)
805  {
806  bool query_pathkeys_ok = true;
807 
808  foreach(lc, root->query_pathkeys)
809  {
810  PathKey *pathkey = (PathKey *) lfirst(lc);
811  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
812  Expr *em_expr;
813 
814  /*
815  * The planner and executor don't have any clever strategy for
816  * taking data sorted by a prefix of the query's pathkeys and
817  * getting it to be sorted by all of those pathkeys. We'll just
818  * end up resorting the entire data set. So, unless we can push
819  * down all of the query pathkeys, forget it.
820  *
821  * is_foreign_expr would detect volatile expressions as well, but
822  * checking ec_has_volatile here saves some cycles.
823  */
824  if (pathkey_ec->ec_has_volatile ||
825  !(em_expr = find_em_expr_for_rel(pathkey_ec, rel)) ||
826  !is_foreign_expr(root, rel, em_expr))
827  {
828  query_pathkeys_ok = false;
829  break;
830  }
831  }
832 
833  if (query_pathkeys_ok)
834  useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
835  }
836 
837  /*
838  * Even if we're not using remote estimates, having the remote side do the
839  * sort generally won't be any worse than doing it locally, and it might
840  * be much better if the remote side can generate data in the right order
841  * without needing a sort at all. However, what we're going to do next is
842  * try to generate pathkeys that seem promising for possible merge joins,
843  * and that's more speculative. A wrong choice might hurt quite a bit, so
844  * bail out if we can't use remote estimates.
845  */
846  if (!fpinfo->use_remote_estimate)
847  return useful_pathkeys_list;
848 
849  /* Get the list of interesting EquivalenceClasses. */
850  useful_eclass_list = get_useful_ecs_for_relation(root, rel);
851 
852  /* Extract unique EC for query, if any, so we don't consider it again. */
853  if (list_length(root->query_pathkeys) == 1)
854  {
855  PathKey *query_pathkey = linitial(root->query_pathkeys);
856 
857  query_ec = query_pathkey->pk_eclass;
858  }
859 
860  /*
861  * As a heuristic, the only pathkeys we consider here are those of length
862  * one. It's surely possible to consider more, but since each one we
863  * choose to consider will generate a round-trip to the remote side, we
864  * need to be a bit cautious here. It would sure be nice to have a local
865  * cache of information about remote index definitions...
866  */
867  foreach(lc, useful_eclass_list)
868  {
869  EquivalenceClass *cur_ec = lfirst(lc);
870  Expr *em_expr;
871  PathKey *pathkey;
872 
873  /* If redundant with what we did above, skip it. */
874  if (cur_ec == query_ec)
875  continue;
876 
877  /* If no pushable expression for this rel, skip it. */
878  em_expr = find_em_expr_for_rel(cur_ec, rel);
879  if (em_expr == NULL || !is_foreign_expr(root, rel, em_expr))
880  continue;
881 
882  /* Looks like we can generate a pathkey, so let's do it. */
883  pathkey = make_canonical_pathkey(root, cur_ec,
884  linitial_oid(cur_ec->ec_opfamilies),
886  false);
887  useful_pathkeys_list = lappend(useful_pathkeys_list,
888  list_make1(pathkey));
889  }
890 
891  return useful_pathkeys_list;
892 }
#define NIL
Definition: pg_list.h:69
List * query_pathkeys
Definition: relation.h:262
static List * get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:696
List * list_copy(const List *oldlist)
Definition: list.c:1160
PathKey * make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, int strategy, bool nulls_first)
Definition: pathkeys.c:51
#define list_make1(x1)
Definition: pg_list.h:139
#define linitial(l)
Definition: pg_list.h:111
List * lappend(List *list, void *datum)
Definition: list.c:128
List * ec_opfamilies
Definition: relation.h:864
void * fdw_private
Definition: relation.h:637
Expr * find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel)
#define lfirst(lc)
Definition: pg_list.h:106
EquivalenceClass * pk_eclass
Definition: relation.h:943
#define linitial_oid(l)
Definition: pg_list.h:113
static int list_length(const List *l)
Definition: pg_list.h:89
bool ec_has_volatile
Definition: relation.h:872
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
#define BTLessStrategyNumber
Definition: stratnum.h:29
Definition: pg_list.h:45

◆ init_returning_filter()

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

Definition at line 3619 of file postgres_fdw.c.

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

Referenced by postgresBeginDirectModify().

3622 {
3623  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
3624  ListCell *lc;
3625  int i;
3626 
3627  /*
3628  * Calculate the mapping between the fdw_scan_tlist's entries and the
3629  * result tuple's attributes.
3630  *
3631  * The "map" is an array of indexes of the result tuple's attributes in
3632  * fdw_scan_tlist, i.e., one entry for every attribute of the result
3633  * tuple. We store zero for any attributes that don't have the
3634  * corresponding entries in that list, marking that a NULL is needed in
3635  * the result tuple.
3636  *
3637  * Also get the indexes of the entries for ctid and oid if any.
3638  */
3639  dmstate->attnoMap = (AttrNumber *)
3640  palloc0(resultTupType->natts * sizeof(AttrNumber));
3641 
3642  dmstate->ctidAttno = dmstate->oidAttno = 0;
3643 
3644  i = 1;
3645  dmstate->hasSystemCols = false;
3646  foreach(lc, fdw_scan_tlist)
3647  {
3648  TargetEntry *tle = (TargetEntry *) lfirst(lc);
3649  Var *var = (Var *) tle->expr;
3650 
3651  Assert(IsA(var, Var));
3652 
3653  /*
3654  * If the Var is a column of the target relation to be retrieved from
3655  * the foreign server, get the index of the entry.
3656  */
3657  if (var->varno == rtindex &&
3658  list_member_int(dmstate->retrieved_attrs, i))
3659  {
3660  int attrno = var->varattno;
3661 
3662  if (attrno < 0)
3663  {
3664  /*
3665  * We don't retrieve system columns other than ctid and oid.
3666  */
3667  if (attrno == SelfItemPointerAttributeNumber)
3668  dmstate->ctidAttno = i;
3669  else if (attrno == ObjectIdAttributeNumber)
3670  dmstate->oidAttno = i;
3671  else
3672  Assert(false);
3673  dmstate->hasSystemCols = true;
3674  }
3675  else
3676  {
3677  /*
3678  * We don't retrieve whole-row references to the target
3679  * relation either.
3680  */
3681  Assert(attrno > 0);
3682 
3683  dmstate->attnoMap[attrno - 1] = i;
3684  }
3685  }
3686  i++;
3687  }
3688 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:564
#define RelationGetDescr(relation)
Definition: rel.h:437
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
AttrNumber varattno
Definition: primnodes.h:168
Definition: primnodes.h:163
int natts
Definition: tupdesc.h:79
bool list_member_int(const List *list, int datum)
Definition: list.c:485
Index varno
Definition: primnodes.h:166
void * palloc0(Size size)
Definition: mcxt.c:864
#define Assert(condition)
Definition: c.h:688
#define lfirst(lc)
Definition: pg_list.h:106
Expr * expr
Definition: primnodes.h:1375
int i
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
int16 AttrNumber
Definition: attnum.h:21

◆ make_tuple_from_result_row()

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

Definition at line 5333 of file postgres_fdw.c.

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

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

5340 {
5341  HeapTuple tuple;
5342  TupleDesc tupdesc;
5343  Datum *values;
5344  bool *nulls;
5345  ItemPointer ctid = NULL;
5346  Oid oid = InvalidOid;
5347  ConversionLocation errpos;
5348  ErrorContextCallback errcallback;
5349  MemoryContext oldcontext;
5350  ListCell *lc;
5351  int j;
5352 
5353  Assert(row < PQntuples(res));
5354 
5355  /*
5356  * Do the following work in a temp context that we reset after each tuple.
5357  * This cleans up not only the data we have direct access to, but any
5358  * cruft the I/O functions might leak.
5359  */
5360  oldcontext = MemoryContextSwitchTo(temp_context);
5361 
5362  if (rel)
5363  tupdesc = RelationGetDescr(rel);
5364  else
5365  {
5366  Assert(fsstate);
5367  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
5368  }
5369 
5370  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
5371  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
5372  /* Initialize to nulls for any columns not present in result */
5373  memset(nulls, true, tupdesc->natts * sizeof(bool));
5374 
5375  /*
5376  * Set up and install callback to report where conversion error occurs.
5377  */
5378  errpos.rel = rel;
5379  errpos.cur_attno = 0;
5380  errpos.fsstate = fsstate;
5381  errcallback.callback = conversion_error_callback;
5382  errcallback.arg = (void *) &errpos;
5383  errcallback.previous = error_context_stack;
5384  error_context_stack = &errcallback;
5385 
5386  /*
5387  * i indexes columns in the relation, j indexes columns in the PGresult.
5388  */
5389  j = 0;
5390  foreach(lc, retrieved_attrs)
5391  {
5392  int i = lfirst_int(lc);
5393  char *valstr;
5394 
5395  /* fetch next column's textual value */
5396  if (PQgetisnull(res, row, j))
5397  valstr = NULL;
5398  else
5399  valstr = PQgetvalue(res, row, j);
5400 
5401  /*
5402  * convert value to internal representation
5403  *
5404  * Note: we ignore system columns other than ctid and oid in result
5405  */
5406  errpos.cur_attno = i;
5407  if (i > 0)
5408  {
5409  /* ordinary column */
5410  Assert(i <= tupdesc->natts);
5411  nulls[i - 1] = (valstr == NULL);
5412  /* Apply the input function even to nulls, to support domains */
5413  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
5414  valstr,
5415  attinmeta->attioparams[i - 1],
5416  attinmeta->atttypmods[i - 1]);
5417  }
5418  else if (i == SelfItemPointerAttributeNumber)
5419  {
5420  /* ctid */
5421  if (valstr != NULL)
5422  {
5423  Datum datum;
5424 
5425  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
5426  ctid = (ItemPointer) DatumGetPointer(datum);
5427  }
5428  }
5429  else if (i == ObjectIdAttributeNumber)
5430  {
5431  /* oid */
5432  if (valstr != NULL)
5433  {
5434  Datum datum;
5435 
5436  datum = DirectFunctionCall1(oidin, CStringGetDatum(valstr));
5437  oid = DatumGetObjectId(datum);
5438  }
5439  }
5440  errpos.cur_attno = 0;
5441 
5442  j++;
5443  }
5444 
5445  /* Uninstall error context callback. */
5446  error_context_stack = errcallback.previous;
5447 
5448  /*
5449  * Check we got the expected number of columns. Note: j == 0 and
5450  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
5451  */
5452  if (j > 0 && j != PQnfields(res))
5453  elog(ERROR, "remote query result does not match the foreign table");
5454 
5455  /*
5456  * Build the result tuple in caller's memory context.
5457  */
5458  MemoryContextSwitchTo(oldcontext);
5459 
5460  tuple = heap_form_tuple(tupdesc, values, nulls);
5461 
5462  /*
5463  * If we have a CTID to return, install it in both t_self and t_ctid.
5464  * t_self is the normal place, but if the tuple is converted to a
5465  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
5466  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
5467  */
5468  if (ctid)
5469  tuple->t_self = tuple->t_data->t_ctid = *ctid;
5470 
5471  /*
5472  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
5473  * heap_form_tuple. heap_form_tuple actually creates the tuple with
5474  * DatumTupleFields, not HeapTupleFields, but the executor expects
5475  * HeapTupleFields and will happily extract system columns on that
5476  * assumption. If we don't do this then, for example, the tuple length
5477  * ends up in the xmin field, which isn't what we want.
5478  */
5482 
5483  /*
5484  * If we have an OID to return, install it.
5485  */
5486  if (OidIsValid(oid))
5487  HeapTupleSetOid(tuple, oid);
5488 
5489  /* Clean up */
5490  MemoryContextReset(temp_context);
5491 
5492  return tuple;
5493 }
ScanState ss
Definition: execnodes.h:1575
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2732
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3118
#define RelationGetDescr(relation)
Definition: rel.h:437
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define DatumGetObjectId(X)
Definition: postgres.h:483
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int32 * atttypmods
Definition: funcapi.h:48
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1127
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:134
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:695
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:585
unsigned int Oid
Definition: postgres_ext.h:31
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
void(* callback)(void *arg)
Definition: elog.h:239
struct ErrorContextCallback * previous
Definition: elog.h:238
#define OidIsValid(objectId)
Definition: c.h:594
Oid * attioparams
Definition: funcapi.h:45
int natts
Definition: tupdesc.h:79
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:703
ErrorContextCallback * error_context_stack
Definition: elog.c:88
ForeignScanState * fsstate
Definition: postgres_fdw.c:261
#define ERROR
Definition: elog.h:43
#define lfirst_int(lc)
Definition: pg_list.h:107
ItemPointerData t_ctid
Definition: htup_details.h:155
ItemPointerData t_self
Definition: htup.h:65
#define CStringGetDatum(X)
Definition: postgres.h:561
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:379
#define InvalidTransactionId
Definition: transam.h:31
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:121
void * palloc0(Size size)
Definition: mcxt.c:864
static void conversion_error_callback(void *arg)
uintptr_t Datum
Definition: postgres.h:365
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1618
#define InvalidOid
Definition: postgres_ext.h:36
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:53
#define Assert(condition)
Definition: c.h:688
#define DatumGetPointer(X)
Definition: postgres.h:532
static Datum values[MAXATTR]
Definition: bootstrap.c:164
void * palloc(Size size)
Definition: mcxt.c:835
int i
Datum oidin(PG_FUNCTION_ARGS)
Definition: oid.c:117
FmgrInfo * attinfuncs
Definition: funcapi.h:42
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
#define elog
Definition: elog.h:219
AttrNumber cur_attno
Definition: postgres_fdw.c:253
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3143
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:396
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:318

◆ merge_fdw_options()

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

Definition at line 4834 of file postgres_fdw.c.

References Assert, 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_grouping_paths(), and foreign_join_ok().

4837 {
4838  /* We must always have fpinfo_o. */
4839  Assert(fpinfo_o);
4840 
4841  /* fpinfo_i may be NULL, but if present the servers must both match. */
4842  Assert(!fpinfo_i ||
4843  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
4844 
4845  /*
4846  * Copy the server specific FDW options. (For a join, both relations come
4847  * from the same server, so the server options should have the same value
4848  * for both relations.)
4849  */
4850  fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
4851  fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
4852  fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
4853  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
4854  fpinfo->fetch_size = fpinfo_o->fetch_size;
4855 
4856  /* Merge the table level options from either side of the join. */
4857  if (fpinfo_i)
4858  {
4859  /*
4860  * We'll prefer to use remote estimates for this join if any table
4861  * from either side of the join is using remote estimates. This is
4862  * most likely going to be preferred since they're already willing to
4863  * pay the price of a round trip to get the remote EXPLAIN. In any
4864  * case it's not entirely clear how we might otherwise handle this
4865  * best.
4866  */
4867  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
4868  fpinfo_i->use_remote_estimate;
4869 
4870  /*
4871  * Set fetch size to maximum of the joining sides, since we are
4872  * expecting the rows returned by the join to be proportional to the
4873  * relation sizes.
4874  */
4875  fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
4876  }
4877 }
ForeignServer * server
Definition: postgres_fdw.h:76
#define Max(x, y)
Definition: c.h:840
#define Assert(condition)
Definition: c.h:688
List * shippable_extensions
Definition: postgres_fdw.h:72
Oid serverid
Definition: foreign.h:47

◆ PG_FUNCTION_INFO_V1()

PG_FUNCTION_INFO_V1 ( postgres_fdw_handler  )

◆ postgres_fdw_handler()

Datum postgres_fdw_handler ( PG_FUNCTION_ARGS  )

Definition at line 443 of file postgres_fdw.c.

References FdwRoutine::AddForeignUpdateTargets, FdwRoutine::AnalyzeForeignTable, FdwRoutine::BeginDirectModify, FdwRoutine::BeginForeignModify, FdwRoutine::BeginForeignScan, FdwRoutine::EndDirectModify, FdwRoutine::EndForeignModify, FdwRoutine::EndForeignScan, FdwRoutine::ExecForeignDelete, FdwRoutine::ExecForeignInsert, FdwRoutine::ExecForeignUpdate, FdwRoutine::ExplainDirectModify, FdwRoutine::ExplainForeignModify, FdwRoutine::ExplainForeignScan, FdwRoutine::GetForeignJoinPaths, FdwRoutine::GetForeignPaths, FdwRoutine::GetForeignPlan, FdwRoutine::GetForeignRelSize, FdwRoutine::GetForeignUpperPaths, FdwRoutine::ImportForeignSchema, FdwRoutine::IsForeignRelUpdatable, FdwRoutine::IterateDirectModify, FdwRoutine::IterateForeignScan, makeNode, PG_RETURN_POINTER, FdwRoutine::PlanDirectModify, FdwRoutine::PlanForeignModify, postgresAddForeignUpdateTargets(), postgresAnalyzeForeignTable(), postgresBeginDirectModify(), postgresBeginForeignModify(), postgresBeginForeignScan(), postgresEndDirectModify(), postgresEndForeignModify(), postgresEndForeignScan(), postgresExecForeignDelete(), postgresExecForeignInsert(), postgresExecForeignUpdate(), postgresExplainDirectModify(), postgresExplainForeignModify(), postgresExplainForeignScan(), postgresGetForeignJoinPaths(), postgresGetForeignPaths(), postgresGetForeignPlan(), postgresGetForeignRelSize(), postgresGetForeignUpperPaths(), postgresImportForeignSchema(), postgresIsForeignRelUpdatable(), postgresIterateDirectModify(), postgresIterateForeignScan(), postgresPlanDirectModify(), postgresPlanForeignModify(), postgresRecheckForeignScan(), postgresReScanForeignScan(), FdwRoutine::RecheckForeignScan, and FdwRoutine::ReScanForeignScan.

444 {
445  FdwRoutine *routine = makeNode(FdwRoutine);
446 
447  /* Functions for scanning foreign tables */
455 
456  /* Functions for updating foreign tables */
469 
470  /* Function for EvalPlanQual rechecks */
472  /* Support functions for EXPLAIN */
476 
477  /* Support functions for ANALYZE */
479 
480  /* Support functions for IMPORT FOREIGN SCHEMA */
482 
483  /* Support functions for join push-down */
485 
486  /* Support functions for upper relation push-down */
488 
489  PG_RETURN_POINTER(routine);
490 }
GetForeignPlan_function GetForeignPlan
Definition: fdwapi.h:182
BeginForeignScan_function BeginForeignScan
Definition: fdwapi.h:183
GetForeignUpperPaths_function GetForeignUpperPaths
Definition: fdwapi.h:197
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:205
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:321
EndDirectModify_function EndDirectModify
Definition: fdwapi.h:211
static void postgresExplainForeignModify(ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
static List * postgresPlanForeignModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
ExplainForeignScan_function ExplainForeignScan
Definition: fdwapi.h:219
static ForeignScan * postgresGetForeignPlan(PlannerInfo *root, RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
static TupleTableSlot * postgresExecForeignUpdate(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
AnalyzeForeignTable_function AnalyzeForeignTable
Definition: fdwapi.h:224
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:203
static void postgresBeginForeignScan(ForeignScanState *node, int eflags)
static void postgresGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
Definition: postgres_fdw.c:500
static int postgresIsForeignRelUpdatable(Relation rel)
static bool postgresAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
static void postgresExplainDirectModify(ForeignScanState *node, ExplainState *es)
static TupleTableSlot * postgresExecForeignInsert(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
AddForeignUpdateTargets_function AddForeignUpdateTargets
Definition: fdwapi.h:200
static void postgresEndDirectModify(ForeignScanState *node)
RecheckForeignScan_function RecheckForeignScan
Definition: fdwapi.h:216
IterateDirectModify_function IterateDirectModify
Definition: fdwapi.h:210
static void postgresEndForeignScan(ForeignScanState *node)
GetForeignJoinPaths_function GetForeignJoinPaths
Definition: fdwapi.h:194
static List * postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
static void postgresBeginDirectModify(ForeignScanState *node, int eflags)
GetForeignRelSize_function GetForeignRelSize
Definition: fdwapi.h:180
EndForeignScan_function EndForeignScan
Definition: fdwapi.h:186
ExplainDirectModify_function ExplainDirectModify
Definition: fdwapi.h:221
ImportForeignSchema_function ImportForeignSchema
Definition: fdwapi.h:227
PlanForeignModify_function PlanForeignModify
Definition: fdwapi.h:201
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:206
GetForeignPaths_function GetForeignPaths
Definition: fdwapi.h:181
static bool postgresPlanDirectModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
static TupleTableSlot * postgresIterateForeignScan(ForeignScanState *node)
PlanDirectModify_function PlanDirectModify
Definition: fdwapi.h:208
static void postgresGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
Definition: postgres_fdw.c:899
static void postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel)
BeginDirectModify_function BeginDirectModify
Definition: fdwapi.h:209
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:204
static TupleTableSlot * postgresExecForeignDelete(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
#define makeNode(_type_)
Definition: nodes.h:561
ReScanForeignScan_function ReScanForeignScan
Definition: fdwapi.h:185
IterateForeignScan_function IterateForeignScan
Definition: fdwapi.h:184
static bool postgresRecheckForeignScan(ForeignScanState *node, TupleTableSlot *slot)
static void postgresExplainForeignScan(ForeignScanState *node, ExplainState *es)
static void postgresBeginForeignModify(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
ExplainForeignModify_function ExplainForeignModify
Definition: fdwapi.h:220
static void postgresAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation)
static void postgresEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo)
IsForeignRelUpdatable_function IsForeignRelUpdatable
Definition: fdwapi.h:207
static TupleTableSlot * postgresIterateDirectModify(ForeignScanState *node)
static void postgresGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:202
static void postgresReScanForeignScan(ForeignScanState *node)

◆ postgresAcquireSampleRowsFunc()

static int postgresAcquireSampleRowsFunc ( Relation  relation,
int  elevel,
HeapTuple rows,
int  targrows,
double *  totalrows,
double *  totaldeadrows 
)
static

Definition at line 3965 of file postgres_fdw.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, analyze_row_processor(), PgFdwAnalyzeState::anl_cxt, appendStringInfo(), PgFdwAnalyzeState::attinmeta, CHECK_FOR_INTERRUPTS, close_cursor(), PgFdwScanState::conn, CurrentMemoryContext, PgFdwScanState::cursor_number, StringInfoData::data, defGetString(), DefElem::defname, deparseAnalyzeSql(), ereport, errmsg(), ERROR, PgFdwScanState::fetch_size, GetConnection(), GetCursorNumber(), GetForeignServer(), GetForeignTable(), GetUserMapping(), i, initStringInfo(), lfirst, PgFdwAnalyzeState::numrows, ForeignServer::options, ForeignTable::options, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pgfdw_exec_query(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQntuples(), PQresultStatus(), RelationData::rd_rel, PgFdwAnalyzeState::rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseConnection(), reservoir_init_selection_state(), PgFdwAnalyzeState::retrieved_attrs, PgFdwAnalyzeState::rows, PgFdwAnalyzeState::rowstoskip, PgFdwAnalyzeState::rstate, PgFdwAnalyzeState::samplerows, ForeignTable::serverid, snprintf(), PgFdwAnalyzeState::targrows, PgFdwAnalyzeState::temp_cxt, TupleDescGetAttInMetadata(), and user.

Referenced by postgresAnalyzeForeignTable().

3969 {
3970  PgFdwAnalyzeState astate;
3971  ForeignTable *table;
3972  ForeignServer *server;
3973  UserMapping *user;
3974  PGconn *conn;
3975  unsigned int cursor_number;
3976  StringInfoData sql;
3977  PGresult *volatile res = NULL;
3978 
3979  /* Initialize workspace state */
3980  astate.rel = relation;
3982 
3983  astate.rows = rows;
3984  astate.targrows = targrows;
3985  astate.numrows = 0;
3986  astate.samplerows = 0;
3987  astate.rowstoskip = -1; /* -1 means not set yet */
3988  reservoir_init_selection_state(&astate.rstate, targrows);
3989 
3990  /* Remember ANALYZE context, and create a per-tuple temp context */
3991  astate.anl_cxt = CurrentMemoryContext;
3993  "postgres_fdw temporary data",
3995 
3996  /*
3997  * Get the connection to use. We do the remote access as the table's
3998  * owner, even if the ANALYZE was started by some other user.
3999  */
4000  table = GetForeignTable(RelationGetRelid(relation));
4001  server = GetForeignServer(table->serverid);
4002  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4003  conn = GetConnection(user, false);
4004 
4005  /*
4006  * Construct cursor that retrieves whole rows from remote.
4007  */
4008  cursor_number = GetCursorNumber(conn);
4009  initStringInfo(&sql);
4010  appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
4011  deparseAnalyzeSql(&sql, relation, &astate.retrieved_attrs);
4012 
4013  /* In what follows, do not risk leaking any PGresults. */
4014  PG_TRY();
4015  {
4016  res = pgfdw_exec_query(conn, sql.data);
4017  if (PQresultStatus(res) != PGRES_COMMAND_OK)
4018  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4019  PQclear(res);
4020  res = NULL;
4021 
4022  /* Retrieve and process rows a batch at a time. */
4023  for (;;)
4024  {
4025  char fetch_sql[64];
4026  int fetch_size;
4027  int numrows;
4028  int i;
4029  ListCell *lc;
4030 
4031  /* Allow users to cancel long query */
4033 
4034  /*
4035  * XXX possible future improvement: if rowstoskip is large, we
4036  * could issue a MOVE rather than physically fetching the rows,
4037  * then just adjust rowstoskip and samplerows appropriately.
4038  */
4039 
4040  /* The fetch size is arbitrary, but shouldn't be enormous. */
4041  fetch_size = 100;
4042  foreach(lc, server->options)
4043  {
4044  DefElem *def = (DefElem *) lfirst(lc);
4045 
4046  if (strcmp(def->defname, "fetch_size") == 0)
4047  {
4048  fetch_size = strtol(defGetString(def), NULL, 10);
4049  break;
4050  }
4051  }
4052  foreach(lc, table->options)
4053  {
4054  DefElem *def = (DefElem *) lfirst(lc);
4055 
4056  if (strcmp(def->defname, "fetch_size") == 0)
4057  {
4058  fetch_size = strtol(defGetString(def), NULL, 10);
4059  break;
4060  }
4061  }
4062 
4063  /* Fetch some rows */
4064  snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
4065  fetch_size, cursor_number);
4066 
4067  res = pgfdw_exec_query(conn, fetch_sql);
4068  /* On error, report the original query, not the FETCH. */
4069  if (PQresultStatus(res) != PGRES_TUPLES_OK)
4070  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4071 
4072  /* Process whatever we got. */
4073  numrows = PQntuples(res);
4074  for (i = 0; i < numrows; i++)
4075  analyze_row_processor(res, i, &astate);
4076 
4077  PQclear(res);
4078  res = NULL;
4079 
4080  /* Must be EOF if we didn't get all the rows requested. */
4081  if (numrows < fetch_size)
4082  break;
4083  }
4084 
4085  /* Close the cursor, just to be tidy. */
4086  close_cursor(conn, cursor_number);
4087  }
4088  PG_CATCH();
4089  {
4090  if (res)
4091  PQclear(res);
4092  PG_RE_THROW();
4093  }
4094  PG_END_TRY();
4095 
4096  ReleaseConnection(conn);
4097 
4098  /* We assume that we have no dead tuple. */
4099  *totaldeadrows = 0.0;
4100 
4101  /* We've retrieved all living tuples from foreign server. */
4102  *totalrows = astate.samplerows;
4103 
4104  /*
4105  * Emit some interesting relation info
4106  */
4107  ereport(elevel,
4108  (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
4109  RelationGetRelationName(relation),
4110  astate.samplerows, astate.numrows)));
4111 
4112  return astate.numrows;
4113 }
HeapTuple * rows
Definition: postgres_fdw.c:233
#define RelationGetDescr(relation)
Definition: rel.h:437
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:207
uint64 fetch_size
Definition: logging.c:21
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
void reservoir_init_selection_state(ReservoirState rs, int n)
Definition: sampling.c:129
Form_pg_class rd_rel
Definition: rel.h:114
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
static void close_cursor(PGconn *conn, unsigned int cursor_number)
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:46
char * defGetString(DefElem *def)
Definition: define.c:49
#define RelationGetRelationName(relation)
Definition: rel.h:445
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
static unsigned int cursor_number
Definition: connection.c:69
#define ereport(elevel, rest)
Definition: elog.h:122
ReservoirStateData rstate
Definition: postgres_fdw.c:240
void deparseAnalyzeSql(StringInfo buf, Relation rel, List **retrieved_attrs)
Definition: deparse.c:1990
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:165
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
AttInMetadata * attinmeta
Definition: postgres_fdw.c:229
static int elevel
Definition: vacuumlazy.c:136
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:93
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1125
void PQclear(PGresult *res)
Definition: fe-exec.c:671
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
#define PG_CATCH()
Definition: elog.h:293
MemoryContext temp_cxt
Definition: postgres_fdw.c:244
#define lfirst(lc)
Definition: pg_list.h:106
Oid serverid
Definition: foreign.h:67
unsigned int GetCursorNumber(PGconn *conn)
Definition: connection.c:481
MemoryContext anl_cxt
Definition: postgres_fdw.c:243
#define PG_RE_THROW()
Definition: elog.h:314
List * options
Definition: foreign.h:68
static char * user
Definition: pg_regress.c:93
int errmsg(const char *fmt,...)
Definition: elog.c:797
static void analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
int i
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:166
char * defname
Definition: parsenodes.h:727
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define PG_TRY()
Definition: elog.h:284
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
List * options
Definition: foreign.h:53
#define RelationGetRelid(relation)
Definition: rel.h:425
#define PG_END_TRY()
Definition: elog.h:300

◆ postgresAddForeignUpdateTargets()

static void postgresAddForeignUpdateTargets ( Query parsetree,
RangeTblEntry target_rte,
Relation  target_relation 
)
static

Definition at line 1519 of file postgres_fdw.c.

References InvalidOid, lappend(), list_length(), makeTargetEntry(), makeVar(), pstrdup(), Query::resultRelation, SelfItemPointerAttributeNumber, Query::targetList, and TIDOID.

Referenced by postgres_fdw_handler().

1522 {
1523  Var *var;
1524  const char *attrname;
1525  TargetEntry *tle;
1526 
1527  /*
1528  * In postgres_fdw, what we need is the ctid, same as for a regular table.
1529  */
1530 
1531  /* Make a Var representing the desired value */
1532  var = makeVar(parsetree->resultRelation,
1534  TIDOID,
1535  -1,
1536  InvalidOid,
1537  0);
1538 
1539  /* Wrap it in a resjunk TLE with the right name ... */
1540  attrname = "ctid";
1541 
1542  tle = makeTargetEntry((Expr *) var,
1543  list_length(parsetree->targetList) + 1,
1544  pstrdup(attrname),
1545  true);
1546 
1547  /* ... and add it to the query's targetlist */
1548  parsetree->targetList = lappend(parsetree->targetList, tle);
1549 }
char * pstrdup(const char *in)
Definition: mcxt.c:1063
int resultRelation
Definition: parsenodes.h:120
Definition: primnodes.h:163
List * targetList
Definition: parsenodes.h:138
#define TIDOID
Definition: pg_type.h:332
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:237
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
List * lappend(List *list, void *datum)
Definition: list.c:128
#define InvalidOid
Definition: postgres_ext.h:36
static int list_length(const List *l)
Definition: pg_list.h:89
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21

◆ postgresAnalyzeForeignTable()

static bool postgresAnalyzeForeignTable ( Relation  relation,
AcquireSampleRowsFunc func,
BlockNumber totalpages 
)
static

Definition at line 3887 of file postgres_fdw.c.

References PgFdwScanState::conn, StringInfoData::data, deparseAnalyzeSizeSql(), elog, ERROR, GetConnection(), GetForeignTable(), GetUserMapping(), initStringInfo(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pgfdw_exec_query(), pgfdw_report_error(), PGRES_TUPLES_OK, postgresAcquireSampleRowsFunc(), PQclear(), PQgetvalue(), PQnfields(), PQntuples(), PQresultStatus(), RelationData::rd_rel, RelationGetRelid, ReleaseConnection(), ForeignTable::serverid, and user.

Referenced by postgres_fdw_handler().

3890 {
3891  ForeignTable *table;
3892  UserMapping *user;
3893  PGconn *conn;
3894  StringInfoData sql;
3895  PGresult *volatile res = NULL;
3896 
3897  /* Return the row-analysis function pointer */
3899 
3900  /*
3901  * Now we have to get the number of pages. It's annoying that the ANALYZE
3902  * API requires us to return that now, because it forces some duplication
3903  * of effort between this routine and postgresAcquireSampleRowsFunc. But
3904  * it's probably not worth redefining that API at this point.
3905  */
3906 
3907  /*
3908  * Get the connection to use. We do the remote access as the table's
3909  * owner, even if the ANALYZE was started by some other user.
3910  */
3911  table = GetForeignTable(RelationGetRelid(relation));
3912  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
3913  conn = GetConnection(user, false);
3914 
3915  /*
3916  * Construct command to get page count for relation.
3917  */
3918  initStringInfo(&sql);
3919  deparseAnalyzeSizeSql(&sql, relation);
3920 
3921  /* In what follows, do not risk leaking any PGresults. */
3922  PG_TRY();
3923  {
3924  res = pgfdw_exec_query(conn, sql.data);
3925  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3926  pgfdw_report_error(ERROR, res, conn, false, sql.data);
3927 
3928  if (PQntuples(res) != 1 || PQnfields(res) != 1)
3929  elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
3930  *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
3931 
3932  PQclear(res);
3933  res = NULL;
3934  }
3935  PG_CATCH();
3936  {
3937  if (res)
3938  PQclear(res);
3939  PG_RE_THROW();
3940  }
3941  PG_END_TRY();
3942 
3943  ReleaseConnection(conn);
3944 
3945  return true;
3946 }
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2732
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3118
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
Form_pg_class rd_rel
Definition: rel.h:114
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:46
static int postgresAcquireSampleRowsFunc(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
void PQclear(PGresult *res)
Definition: fe-exec.c:671
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
#define PG_CATCH()
Definition: elog.h:293
void deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
Definition: deparse.c:1970
Oid serverid
Definition: foreign.h:67
#define PG_RE_THROW()
Definition: elog.h:314
static char * user
Definition: pg_regress.c:93
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:166
#define elog
Definition: elog.h:219
#define PG_TRY()
Definition: elog.h:284
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
#define RelationGetRelid(relation)
Definition: rel.h:425
#define PG_END_TRY()
Definition: elog.h:300

◆ postgresBeginDirectModify()

static void postgresBeginDirectModify ( ForeignScanState node,
int  eflags 
)
static

Definition at line 2312 of file postgres_fdw.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, EXEC_FLAG_EXPLAIN_ONLY, ExecOpenScanRelation(), ForeignScan::fdw_exprs, ForeignScan::fdw_private, ForeignScan::fdw_scan_tlist, ForeignScanState::fdw_state, FdwDirectModifyPrivateHasReturning, FdwDirectModifyPrivateRetrievedAttrs, FdwDirectModifyPrivateSetProcessed, FdwDirectModifyPrivateUpdateSql, GetConnection(), GetForeignTable(), GetUserId(), GetUserMapping(), init_returning_filter(), intVal, list_length(), list_nth(), PgFdwScanState::numParams, palloc0(), PlanState::plan, prepare_query_params(), ScanState::ps, RelationGetDescr, RelationGetRelid, rt_fetch, ForeignScan::scan, Scan::scanrelid, ForeignScanState::ss, ScanState::ss_currentRelation, ScanState::ss_ScanTupleSlot, PlanState::state, strVal, TupleTableSlot::tts_tupleDescriptor, PgFdwScanState::tupdesc, TupleDescGetAttInMetadata(), and user.

Referenced by postgres_fdw_handler().

2313 {
2314  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
2315  EState *estate = node->ss.ps.state;
2316  PgFdwDirectModifyState *dmstate;
2317  Index rtindex;
2318  RangeTblEntry *rte;
2319  Oid userid;
2320  ForeignTable *table;
2321  UserMapping *user;
2322  int numParams;
2323 
2324  /*
2325  * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
2326  */
2327  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2328  return;
2329 
2330  /*
2331  * We'll save private state in node->fdw_state.
2332  */
2333  dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
2334  node->fdw_state = (void *) dmstate;
2335 
2336  /*
2337  * Identify which user to do the remote access as. This should match what
2338  * ExecCheckRTEPerms() does.
2339  */
2340  rtindex = estate->es_result_relation_info->ri_RangeTableIndex;
2341  rte = rt_fetch(rtindex, estate->es_range_table);
2342  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
2343 
2344  /* Get info about foreign table. */
2345  if (fsplan->scan.scanrelid == 0)
2346  dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags);
2347  else
2348  dmstate->rel = node->ss.ss_currentRelation;
2349  table = GetForeignTable(RelationGetRelid(dmstate->rel));
2350  user = GetUserMapping(userid, table->serverid);
2351 
2352  /*
2353  * Get connection to the foreign server. Connection manager will
2354  * establish new connection if necessary.
2355  */
2356  dmstate->conn = GetConnection(user, false);
2357 
2358  /* Update the foreign-join-related fields. */
2359  if (fsplan->scan.scanrelid == 0)
2360  {
2361  /* Save info about foreign table. */
2362  dmstate->resultRel = dmstate->rel;
2363 
2364  /*
2365  * Set dmstate->rel to NULL to teach get_returning_data() and
2366  * make_tuple_from_result_row() that columns fetched from the remote
2367  * server are described by fdw_scan_tlist of the foreign-scan plan
2368  * node, not the tuple descriptor for the target relation.
2369  */
2370  dmstate->rel = NULL;
2371  }
2372 
2373  /* Initialize state variable */
2374  dmstate->num_tuples = -1; /* -1 means not set yet */
2375 
2376  /* Get private info created by planner functions. */
2377  dmstate->query = strVal(list_nth(fsplan->fdw_private,
2379  dmstate->has_returning = intVal(list_nth(fsplan->fdw_private,
2381  dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
2383  dmstate->set_processed = intVal(list_nth(fsplan->fdw_private,
2385 
2386  /* Create context for per-tuple temp workspace. */
2387  dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
2388  "postgres_fdw temporary data",
2390 
2391  /* Prepare for input conversion of RETURNING results. */
2392  if (dmstate->has_returning)
2393  {
2394  TupleDesc tupdesc;
2395 
2396  if (fsplan->scan.scanrelid == 0)
2397  tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2398  else
2399  tupdesc = RelationGetDescr(dmstate->rel);
2400 
2401  dmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
2402 
2403  /*
2404  * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2405  * initialize a filter to extract an updated/deleted tuple from a scan
2406  * tuple.
2407  */
2408  if (fsplan->scan.scanrelid == 0)
2409  init_returning_filter(dmstate, fsplan->fdw_scan_tlist, rtindex);
2410  }
2411 
2412  /*
2413  * Prepare for processing of parameters used in remote query, if any.
2414  */
2415  numParams = list_length(fsplan->fdw_exprs);
2416  dmstate->numParams = numParams;
2417  if (numParams > 0)
2419  fsplan->fdw_exprs,
2420  numParams,
2421  &dmstate->param_flinfo,
2422  &dmstate->param_exprs,
2423  &dmstate->param_values);
2424 }
ScanState ss
Definition: execnodes.h:1575
Index scanrelid
Definition: plannodes.h:331
#define RelationGetDescr(relation)
Definition: rel.h:437
Oid GetUserId(void)
Definition: miscinit.c:284
static void prepare_query_params(PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:207
List * fdw_exprs
Definition: plannodes.h:602
List * fdw_private
Definition: plannodes.h:603
#define strVal(v)
Definition: value.h:54
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1127
List * fdw_scan_tlist
Definition: plannodes.h:604
Relation ss_currentRelation
Definition: execnodes.h:1125
EState * state
Definition: execnodes.h:870
unsigned int Oid
Definition: postgres_ext.h:31
PlanState ps
Definition: execnodes.h:1124
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:638
void * list_nth(const List *list, int n)
Definition: list.c:410
static void init_returning_filter(PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:165
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:121
void * palloc0(Size size)
Definition: mcxt.c:864
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1125
unsigned int Index
Definition: c.h:431
Plan * plan
Definition: execnodes.h:868
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
static int list_length(const List *l)
Definition: pg_list.h:89
static char * user
Definition: pg_regress.c:93
#define intVal(v)
Definition: value.h:52
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:166
Definition: pg_list.h:45
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:59
#define RelationGetRelid(relation)
Definition: rel.h:425

◆ postgresBeginForeignModify()

static void postgresBeginForeignModify ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo,
List fdw_private,
int  subplan_index,
int  eflags 
)
static

Definition at line 1674 of file postgres_fdw.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, Assert, PgFdwModifyState::attinmeta, AttributeNumberIsValid, RangeTblEntry::checkAsUser, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, PgFdwModifyState::ctidAttno, elog, ERROR, EState::es_query_cxt, EState::es_range_table, EXEC_FLAG_EXPLAIN_ONLY, ExecFindJunkAttributeInTlist(), FdwModifyPrivateHasReturning, FdwModifyPrivateRetrievedAttrs, FdwModifyPrivateTargetAttnums, FdwModifyPrivateUpdateSql, fmgr_info(), GetConnection(), GetForeignTable(), getTypeOutputInfo(), GetUserId(), GetUserMapping(), PgFdwModifyState::has_returning, intVal, lfirst_int, list_length(), list_nth(), ModifyTableState::mt_plans, ModifyTableState::operation, PgFdwModifyState::p_flinfo, PgFdwModifyState::p_name, PgFdwModifyState::p_nums, palloc0(), PlanState::plan, ModifyTableState::ps, PgFdwModifyState::query, PgFdwScanState::rel, PgFdwModifyState::rel, RelationGetDescr, RelationGetRelid, PgFdwModifyState::retrieved_attrs, ResultRelInfo::ri_FdwState, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, rt_fetch, ForeignTable::serverid, PlanState::state, strVal, PgFdwModifyState::target_attrs, Plan::targetlist, PgFdwModifyState::temp_cxt, TIDOID, PgFdwScanState::tupdesc, TupleDescAttr, TupleDescGetAttInMetadata(), and user.

Referenced by postgres_fdw_handler().

1679 {
1680  PgFdwModifyState *fmstate;
1681  EState *estate = mtstate->ps.state;
1682  CmdType operation = mtstate->operation;
1683  Relation rel = resultRelInfo->ri_RelationDesc;
1684  RangeTblEntry *rte;
1685  Oid userid;
1686  ForeignTable *table;
1687  UserMapping *user;
1688  AttrNumber n_params;
1689  Oid typefnoid;
1690  bool isvarlena;
1691  ListCell *lc;
1692  TupleDesc tupdesc = RelationGetDescr(rel);
1693 
1694  /*
1695  * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
1696  * stays NULL.
1697  */
1698  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1699  return;
1700 
1701  /* Begin constructing PgFdwModifyState. */
1702  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
1703  fmstate->rel = rel;
1704 
1705  /*
1706  * Identify which user to do the remote access as. This should match what
1707  * ExecCheckRTEPerms() does.
1708  */
1709  rte = rt_fetch(resultRelInfo->ri_RangeTableIndex, estate->es_range_table);
1710  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
1711 
1712  /* Get info about foreign table. */
1713  table = GetForeignTable(RelationGetRelid(rel));
1714  user = GetUserMapping(userid, table->serverid);
1715 
1716  /* Open connection; report that we'll create a prepared statement. */
1717  fmstate->conn = GetConnection(user, true);
1718  fmstate->p_name = NULL; /* prepared statement not made yet */
1719 
1720  /* Deconstruct fdw_private data. */
1721  fmstate->query = strVal(list_nth(fdw_private,
1723  fmstate->target_attrs = (List *) list_nth(fdw_private,
1725  fmstate->has_returning = intVal(list_nth(fdw_private,
1727  fmstate->retrieved_attrs = (List *) list_nth(fdw_private,
1729 
1730  /* Create context for per-tuple temp workspace. */
1731  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
1732  "postgres_fdw temporary data",
1734 
1735  /* Prepare for input conversion of RETURNING results. */
1736  if (fmstate->has_returning)
1737  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1738 
1739  /* Prepare for output conversion of parameters used in prepared stmt. */
1740  n_params = list_length(fmstate->target_attrs) + 1;
1741  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
1742  fmstate->p_nums = 0;
1743 
1744  if (operation == CMD_UPDATE || operation == CMD_DELETE)
1745  {
1746  /* Find the ctid resjunk column in the subplan's result */
1747  Plan *subplan = mtstate->mt_plans[subplan_index]->plan;
1748 
1750  "ctid");
1751  if (!AttributeNumberIsValid(fmstate->ctidAttno))
1752  elog(ERROR, "could not find junk ctid column");
1753 
1754  /* First transmittable parameter will be ctid */
1755  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
1756  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
1757  fmstate->p_nums++;
1758  }
1759 
1760  if (operation == CMD_INSERT || operation == CMD_UPDATE)
1761  {
1762  /* Set up for remaining transmittable parameters */
1763  foreach(lc, fmstate->target_attrs)
1764  {
1765  int attnum = lfirst_int(lc);
1766  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
1767 
1768  Assert(!attr->attisdropped);
1769 
1770  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
1771  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
1772  fmstate->p_nums++;
1773  }
1774  }
1775 
1776  Assert(fmstate->p_nums <= n_params);
1777 
1778  resultRelInfo->ri_FdwState = fmstate;
1779 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:368
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2650
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:221
#define RelationGetDescr(relation)
Definition: rel.h:437
Oid GetUserId(void)
Definition: miscinit.c:284
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:207
#define strVal(v)
Definition: value.h:54
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
CmdType operation
Definition: execnodes.h:980
EState * state
Definition: execnodes.h:870
List * es_range_table
Definition: execnodes.h:445
unsigned int Oid
Definition: postgres_ext.h:31
List * retrieved_attrs
Definition: postgres_fdw.c:177
Index ri_RangeTableIndex
Definition: execnodes.h:365
#define TIDOID
Definition: pg_type.h:332
MemoryContext es_query_cxt
Definition: execnodes.h:488
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:979
#define lfirst_int(lc)
Definition: pg_list.h:107
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:122
void * list_nth(const List *list, int n)
Definition: list.c:410
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
AttrNumber ctidAttno
Definition: postgres_fdw.c:180
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:165
PlanState ** mt_plans
Definition: execnodes.h:983
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:182
void * palloc0(Size size)
Definition: mcxt.c:864
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1125
Plan * plan
Definition: execnodes.h:868
void * ri_FdwState
Definition: execnodes.h:395
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
MemoryContext temp_cxt
Definition: postgres_fdw.c:185
#define Assert(condition)
Definition: c.h:688
Oid serverid
Definition: foreign.h:67
static int list_length(const List *l)
Definition: pg_list.h:89
List * targetlist
Definition: plannodes.h:144
static char * user
Definition: pg_regress.c:93
#define intVal(v)
Definition: value.h:52
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:166
AttInMetadata * attinmeta
Definition: postgres_fdw.c:167
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:59
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:425
CmdType
Definition: nodes.h:653

◆ postgresBeginForeignScan()

static void postgresBeginForeignScan ( ForeignScanState node,
int  eflags 
)
static

Definition at line 1294 of file postgres_fdw.c.

References ALLOCSET_DEFAULT_SIZES, ALLOCSET_SMALL_SIZES, AllocSetContextCreate, bms_next_member(), EXEC_FLAG_EXPLAIN_ONLY, ForeignScan::fdw_exprs, ForeignScan::fdw_private, ForeignScanState::fdw_state, FdwScanPrivateFetchSize, FdwScanPrivateRetrievedAttrs, FdwScanPrivateSelectSql, ForeignScan::fs_relids, GetConnection(), GetCursorNumber(), GetForeignTable(), GetUserId(), GetUserMapping(), intVal, list_length(), list_nth(), PgFdwScanState::numParams, palloc0(), PlanState::plan, prepare_query_params(), ScanState::ps, RelationGetDescr, rt_fetch, ForeignScan::scan, Scan::scanrelid, ForeignScanState::ss, ScanState::ss_currentRelation, ScanState::ss_ScanTupleSlot, PlanState::state, strVal, TupleTableSlot::tts_tupleDescriptor, TupleDescGetAttInMetadata(), and user.

Referenced by postgres_fdw_handler().

1295 {
1296  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1297  EState *estate = node->ss.ps.state;
1298  PgFdwScanState *fsstate;
1299  RangeTblEntry *rte;
1300  Oid userid;
1301  ForeignTable *table;
1302  UserMapping *user;
1303  int rtindex;
1304  int numParams;
1305 
1306  /*
1307  * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
1308  */
1309  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1310  return;
1311 
1312  /*
1313  * We'll save private state in node->fdw_state.
1314  */
1315  fsstate = (PgFdwScanState *) palloc0(sizeof(PgFdwScanState));
1316  node->fdw_state = (void *) fsstate;
1317 
1318  /*
1319  * Identify which user to do the remote access as. This should match what
1320  * ExecCheckRTEPerms() does. In case of a join or aggregate, use the
1321  * lowest-numbered member RTE as a representative; we would get the same
1322  * result from any.
1323  */
1324  if (fsplan->scan.scanrelid > 0)
1325  rtindex = fsplan->scan.scanrelid;
1326  else
1327  rtindex = bms_next_member(fsplan->fs_relids, -1);
1328  rte = rt_fetch(rtindex, estate->es_range_table);
1329  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
1330 
1331  /* Get info about foreign table. */
1332  table = GetForeignTable(rte->relid);
1333  user = GetUserMapping(userid, table->serverid);
1334 
1335  /*
1336  * Get connection to the foreign server. Connection manager will
1337  * establish new connection if necessary.
1338  */
1339  fsstate->conn = GetConnection(user, false);
1340 
1341  /* Assign a unique ID for my cursor */
1342  fsstate->cursor_number = GetCursorNumber(fsstate->conn);
1343  fsstate->cursor_exists = false;
1344 
1345  /* Get private info created by planner functions. */
1346  fsstate->query = strVal(list_nth(fsplan->fdw_private,
1348  fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
1350  fsstate->fetch_size = intVal(list_nth(fsplan->fdw_private,
1352 
1353  /* Create contexts for batches of tuples and per-tuple temp workspace. */
1354  fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
1355  "postgres_fdw tuple data",
1357  fsstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
1358  "postgres_fdw temporary data",
1360 
1361  /*
1362  * Get info we'll need for converting data fetched from the foreign server
1363  * into local representation and error reporting during that process.
1364  */
1365  if (fsplan->scan.scanrelid > 0)
1366  {
1367  fsstate->rel = node->ss.ss_currentRelation;
1368  fsstate->tupdesc = RelationGetDescr(fsstate->rel);
1369  }
1370  else
1371  {
1372  fsstate->rel = NULL;
1373  fsstate->tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
1374  }
1375 
1376  fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
1377 
1378  /*
1379  * Prepare for processing of parameters used in remote query, if any.
1380  */
1381  numParams = list_length(fsplan->fdw_exprs);
1382  fsstate->numParams = numParams;
1383  if (numParams > 0)
1385  fsplan->fdw_exprs,
1386  numParams,
1387  &fsstate->param_flinfo,
1388  &fsstate->param_exprs,
1389  &fsstate->param_values);
1390 }
ScanState ss
Definition: execnodes.h:1575
Index scanrelid
Definition: plannodes.h:331
#define RelationGetDescr(relation)
Definition: rel.h:437
Oid GetUserId(void)
Definition: miscinit.c:284
static void prepare_query_params(PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:207
List * fdw_exprs
Definition: plannodes.h:602
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1053
List * fdw_private
Definition: plannodes.h:603
#define strVal(v)
Definition: value.h:54
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1127
Relation ss_currentRelation
Definition: execnodes.h:1125
EState * state
Definition: execnodes.h:870
unsigned int Oid
Definition: postgres_ext.h:31
PlanState ps
Definition: execnodes.h:1124
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197
void * list_nth(const List *list, int n)
Definition: list.c:410
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
#define AllocSetContextCreate(parent, name, allocparams)
Defi