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 void postgresBeginForeignInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static void postgresEndForeignInsert (EState *estate, ResultRelInfo *resultRelInfo)
 
static int postgresIsForeignRelUpdatable (Relation rel)
 
static bool postgresPlanDirectModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginDirectModify (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateDirectModify (ForeignScanState *node)
 
static void postgresEndDirectModify (ForeignScanState *node)
 
static void postgresExplainForeignScan (ForeignScanState *node, ExplainState *es)
 
static void postgresExplainForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
 
static void postgresExplainDirectModify (ForeignScanState *node, ExplainState *es)
 
static bool postgresAnalyzeForeignTable (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
 
static ListpostgresImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid)
 
static void postgresGetForeignJoinPaths (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static bool postgresRecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot)
 
static void postgresGetForeignUpperPaths (PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra)
 
static void estimate_path_cost_size (PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, 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 PgFdwModifyStatecreate_foreign_modify (EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, bool has_returning, List *retrieved_attrs)
 
static void prepare_foreign_modify (PgFdwModifyState *fmstate)
 
static const char ** convert_prep_stmt_params (PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot *slot)
 
static void store_returning_result (PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
 
static void finish_foreign_modify (PgFdwModifyState *fmstate)
 
static Listbuild_remote_returning (Index rtindex, Relation rel, List *returningList)
 
static void rebuild_fdw_scan_tlist (ForeignScan *fscan, List *tlist)
 
static void execute_dml_stmt (ForeignScanState *node)
 
static TupleTableSlotget_returning_data (ForeignScanState *node)
 
static void init_returning_filter (PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
 
static TupleTableSlotapply_returning_filter (PgFdwDirectModifyState *dmstate, TupleTableSlot *slot, EState *estate)
 
static void prepare_query_params (PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
 
static void process_query_params (ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
 
static int postgresAcquireSampleRowsFunc (Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
 
static void analyze_row_processor (PGresult *res, int row, PgFdwAnalyzeState *astate)
 
static HeapTuple make_tuple_from_result_row (PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
 
static void conversion_error_callback (void *arg)
 
static bool foreign_join_ok (PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra)
 
static bool foreign_grouping_ok (PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
 
static Listget_useful_pathkeys_for_relation (PlannerInfo *root, RelOptInfo *rel)
 
static Listget_useful_ecs_for_relation (PlannerInfo *root, RelOptInfo *rel)
 
static void add_paths_with_pathkeys_for_rel (PlannerInfo *root, RelOptInfo *rel, Path *epq_path)
 
static void add_foreign_grouping_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra)
 
static void 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,
GroupPathExtraData extra 
)
static

Definition at line 5438 of file postgres_fdw.c.

References add_path(), Assert, create_foreignscan_path(), estimate_path_cost_size(), RelOptInfo::fdw_private, foreign_grouping_ok(), Query::groupClause, Query::groupingSets, Query::hasAggs, PlannerInfo::hasHavingQual, GroupPathExtraData::havingQual, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, parse(), PlannerInfo::parse, PARTITIONWISE_AGGREGATE_FULL, PARTITIONWISE_AGGREGATE_NONE, GroupPathExtraData::patype, RelOptInfo::reltarget, PgFdwRelationInfo::rows, PgFdwRelationInfo::server, PgFdwRelationInfo::startup_cost, PgFdwRelationInfo::table, PgFdwRelationInfo::total_cost, PgFdwRelationInfo::user, and PgFdwRelationInfo::width.

Referenced by postgresGetForeignUpperPaths().

5441 {
5442  Query *parse = root->parse;
5443  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5444  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
5445  ForeignPath *grouppath;
5446  double rows;
5447  int width;
5448  Cost startup_cost;
5449  Cost total_cost;
5450 
5451  /* Nothing to be done, if there is no grouping or aggregation required. */
5452  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
5453  !root->hasHavingQual)
5454  return;
5455 
5458 
5459  /* save the input_rel as outerrel in fpinfo */
5460  fpinfo->outerrel = input_rel;
5461 
5462  /*
5463  * Copy foreign table, foreign server, user mapping, FDW options etc.
5464  * details from the input relation's fpinfo.
5465  */
5466  fpinfo->table = ifpinfo->table;
5467  fpinfo->server = ifpinfo->server;
5468  fpinfo->user = ifpinfo->user;
5469  merge_fdw_options(fpinfo, ifpinfo, NULL);
5470 
5471  /*
5472  * Assess if it is safe to push down aggregation and grouping.
5473  *
5474  * Use HAVING qual from extra. In case of child partition, it will have
5475  * translated Vars.
5476  */
5477  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
5478  return;
5479 
5480  /* Estimate the cost of push down */
5481  estimate_path_cost_size(root, grouped_rel, NIL, NIL, &rows,
5482  &width, &startup_cost, &total_cost);
5483 
5484  /* Now update this information in the fpinfo */
5485  fpinfo->rows = rows;
5486  fpinfo->width = width;
5487  fpinfo->startup_cost = startup_cost;
5488  fpinfo->total_cost = total_cost;
5489 
5490  /* Create and add foreign path to the grouping relation. */
5491  grouppath = create_foreignscan_path(root,
5492  grouped_rel,
5493  grouped_rel->reltarget,
5494  rows,
5495  startup_cost,
5496  total_cost,
5497  NIL, /* no pathkeys */
5498  NULL, /* no required_outer */
5499  NULL,
5500  NIL); /* no fdw_private */
5501 
5502  /* Add generated path into grouped_rel by add_path(). */
5503  add_path(grouped_rel, (Path *) grouppath);
5504 }
#define NIL
Definition: pg_list.h:69
Query * parse
Definition: relation.h:169
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
bool hasAggs
Definition: parsenodes.h:125
ForeignServer * server
Definition: postgres_fdw.h:76
List * groupingSets
Definition: parsenodes.h:150
PartitionwiseAggregateType patype
Definition: relation.h:2380
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:2062
UserMapping * user
Definition: postgres_fdw.h:77
void * fdw_private
Definition: relation.h:664
#define Assert(condition)
Definition: c.h:699
ForeignTable * table
Definition: postgres_fdw.h:75
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
List * groupClause
Definition: parsenodes.h:148
bool hasHavingQual
Definition: relation.h:318
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)
struct PathTarget * reltarget
Definition: relation.h:623
double Cost
Definition: nodes.h:648
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:649

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

4901 {
4902  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
4903  ListCell *lc;
4904 
4905  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
4906 
4907  /* Create one path for each set of pathkeys we found above. */
4908  foreach(lc, useful_pathkeys_list)
4909  {
4910  double rows;
4911  int width;
4912  Cost startup_cost;
4913  Cost total_cost;
4914  List *useful_pathkeys = lfirst(lc);
4915  Path *sorted_epq_path;
4916 
4917  estimate_path_cost_size(root, rel, NIL, useful_pathkeys,
4918  &rows, &width, &startup_cost, &total_cost);
4919 
4920  /*
4921  * The EPQ path must be at least as well sorted as the path itself, in
4922  * case it gets used as input to a mergejoin.
4923  */
4924  sorted_epq_path = epq_path;
4925  if (sorted_epq_path != NULL &&
4926  !pathkeys_contained_in(useful_pathkeys,
4927  sorted_epq_path->pathkeys))
4928  sorted_epq_path = (Path *)
4929  create_sort_path(root,
4930  rel,
4931  sorted_epq_path,
4932  useful_pathkeys,
4933  -1.0);
4934 
4935  add_path(rel, (Path *)
4936  create_foreignscan_path(root, rel,
4937  NULL,
4938  rows,
4939  startup_cost,
4940  total_cost,
4941  useful_pathkeys,
4942  NULL,
4943  sorted_epq_path,
4944  NIL));
4945  }
4946 }
#define NIL
Definition: pg_list.h:69
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:810
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:2062
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:2631
List * pathkeys
Definition: relation.h:1092
#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:648

◆ analyze_row_processor()

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

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

4290 {
4291  int targrows = astate->targrows;
4292  int pos; /* array index to store tuple in */
4293  MemoryContext oldcontext;
4294 
4295  /* Always increment sample row counter. */
4296  astate->samplerows += 1;
4297 
4298  /*
4299  * Determine the slot where this sample row should be stored. Set pos to
4300  * negative value to indicate the row should be skipped.
4301  */
4302  if (astate->numrows < targrows)
4303  {
4304  /* First targrows rows are always included into the sample */
4305  pos = astate->numrows++;
4306  }
4307  else
4308  {
4309  /*
4310  * Now we start replacing tuples in the sample until we reach the end
4311  * of the relation. Same algorithm as in acquire_sample_rows in
4312  * analyze.c; see Jeff Vitter's paper.
4313  */
4314  if (astate->rowstoskip < 0)
4315  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
4316 
4317  if (astate->rowstoskip <= 0)
4318  {
4319  /* Choose a random reservoir element to replace. */
4320  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
4321  Assert(pos >= 0 && pos < targrows);
4322  heap_freetuple(astate->rows[pos]);
4323  }
4324  else
4325  {
4326  /* Skip this tuple. */
4327  pos = -1;
4328  }
4329 
4330  astate->rowstoskip -= 1;
4331  }
4332 
4333  if (pos >= 0)
4334  {
4335  /*
4336  * Create sample tuple from current result row, and store it in the
4337  * position determined above. The tuple has to be created in anl_cxt.
4338  */
4339  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
4340 
4341  astate->rows[pos] = make_tuple_from_result_row(res, row,
4342  astate->rel,
4343  astate->attinmeta,
4344  astate->retrieved_attrs,
4345  NULL,
4346  astate->temp_cxt);
4347 
4348  MemoryContextSwitchTo(oldcontext);
4349  }
4350 }
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:1773
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:699
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 3862 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().

3865 {
3866  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
3867  TupleTableSlot *resultSlot;
3868  Datum *values;
3869  bool *isnull;
3870  Datum *old_values;
3871  bool *old_isnull;
3872  int i;
3873 
3874  /*
3875  * Use the trigger tuple slot as a place to store the result tuple.
3876  */
3877  resultSlot = estate->es_trig_tuple_slot;
3878  if (resultSlot->tts_tupleDescriptor != resultTupType)
3879  ExecSetSlotDescriptor(resultSlot, resultTupType);
3880 
3881  /*
3882  * Extract all the values of the scan tuple.
3883  */
3884  slot_getallattrs(slot);
3885  old_values = slot->tts_values;
3886  old_isnull = slot->tts_isnull;
3887 
3888  /*
3889  * Prepare to build the result tuple.
3890  */
3891  ExecClearTuple(resultSlot);
3892  values = resultSlot->tts_values;
3893  isnull = resultSlot->tts_isnull;
3894 
3895  /*
3896  * Transpose data into proper fields of the result tuple.
3897  */
3898  for (i = 0; i < resultTupType->natts; i++)
3899  {
3900  int j = dmstate->attnoMap[i];
3901 
3902  if (j == 0)
3903  {
3904  values[i] = (Datum) 0;
3905  isnull[i] = true;
3906  }
3907  else
3908  {
3909  values[i] = old_values[j - 1];
3910  isnull[i] = old_isnull[j - 1];
3911  }
3912  }
3913 
3914  /*
3915  * Build the virtual tuple.
3916  */
3917  ExecStoreVirtualTuple(resultSlot);
3918 
3919  /*
3920  * If we have any system columns to return, install them.
3921  */
3922  if (dmstate->hasSystemCols)
3923  {
3924  HeapTuple resultTup = ExecMaterializeSlot(resultSlot);
3925 
3926  /* ctid */
3927  if (dmstate->ctidAttno)
3928  {
3929  ItemPointer ctid = NULL;
3930 
3931  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
3932  resultTup->t_self = *ctid;
3933  }
3934 
3935  /* oid */
3936  if (dmstate->oidAttno)
3937  {
3938  Oid oid = InvalidOid;
3939 
3940  oid = DatumGetObjectId(old_values[dmstate->oidAttno - 1]);
3941  HeapTupleSetOid(resultTup, oid);
3942  }
3943 
3944  /*
3945  * And remaining columns
3946  *
3947  * Note: since we currently don't allow the target relation to appear
3948  * on the nullable side of an outer join, any system columns wouldn't
3949  * go to NULL.
3950  *
3951  * Note: no need to care about tableoid here because it will be
3952  * initialized in ExecProcessReturning().
3953  */
3957  }
3958 
3959  /*
3960  * And return the result tuple.
3961  */
3962  return resultSlot;
3963 }
#define RelationGetDescr(relation)
Definition: rel.h:433
#define DatumGetObjectId(X)
Definition: postgres.h:483
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
Datum * tts_values
Definition: tuptable.h:130
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:82
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:715
ItemPointerData t_self
Definition: htup.h:65
bool * tts_isnull
Definition: tuptable.h:132
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:385
#define InvalidTransactionId
Definition: transam.h:31
TupleTableSlot * es_trig_tuple_slot
Definition: execnodes.h:512
void slot_getallattrs(TupleTableSlot *slot)
Definition: heaptuple.c:1612
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
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:781
#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:402
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:524
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:324

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

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

4955 {
4956  ListCell *lc;
4957 
4958  foreach(lc, fpinfo->server->options)
4959  {
4960  DefElem *def = (DefElem *) lfirst(lc);
4961 
4962  if (strcmp(def->defname, "use_remote_estimate") == 0)
4963  fpinfo->use_remote_estimate = defGetBoolean(def);
4964  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
4965  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
4966  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
4967  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
4968  else if (strcmp(def->defname, "extensions") == 0)
4969  fpinfo->shippable_extensions =
4970  ExtractExtensionList(defGetString(def), false);
4971  else if (strcmp(def->defname, "fetch_size") == 0)
4972  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
4973  }
4974 }
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:730
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 4982 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().

4983 {
4984  ListCell *lc;
4985 
4986  foreach(lc, fpinfo->table->options)
4987  {
4988  DefElem *def = (DefElem *) lfirst(lc);
4989 
4990  if (strcmp(def->defname, "use_remote_estimate") == 0)
4991  fpinfo->use_remote_estimate = defGetBoolean(def);
4992  else if (strcmp(def->defname, "fetch_size") == 0)
4993  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
4994  }
4995 }
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:730

◆ build_remote_returning()

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

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

3536 {
3537  bool have_wholerow = false;
3538  List *tlist = NIL;
3539  List *vars;
3540  ListCell *lc;
3541 
3542  Assert(returningList);
3543 
3544  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
3545 
3546  /*
3547  * If there's a whole-row reference to the target relation, then we'll
3548  * need all the columns of the relation.
3549  */
3550  foreach(lc, vars)
3551  {
3552  Var *var = (Var *) lfirst(lc);
3553 
3554  if (IsA(var, Var) &&
3555  var->varno == rtindex &&
3556  var->varattno == InvalidAttrNumber)
3557  {
3558  have_wholerow = true;
3559  break;
3560  }
3561  }
3562 
3563  if (have_wholerow)
3564  {
3565  TupleDesc tupdesc = RelationGetDescr(rel);
3566  int i;
3567 
3568  for (i = 1; i <= tupdesc->natts; i++)
3569  {
3570  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
3571  Var *var;
3572 
3573  /* Ignore dropped attributes. */
3574  if (attr->attisdropped)
3575  continue;
3576 
3577  var = makeVar(rtindex,
3578  i,
3579  attr->atttypid,
3580  attr->atttypmod,
3581  attr->attcollation,
3582  0);
3583 
3584  tlist = lappend(tlist,
3585  makeTargetEntry((Expr *) var,
3586  list_length(tlist) + 1,
3587  NULL,
3588  false));
3589  }
3590  }
3591 
3592  /* Now add any remaining columns to tlist. */
3593  foreach(lc, vars)
3594  {
3595  Var *var = (Var *) lfirst(lc);
3596 
3597  /*
3598  * No need for whole-row references to the target relation. We don't
3599  * need system columns other than ctid and oid either, since those are
3600  * set locally.
3601  */
3602  if (IsA(var, Var) &&
3603  var->varno == rtindex &&
3604  var->varattno <= InvalidAttrNumber &&
3607  continue; /* don't need it */
3608 
3609  if (tlist_member((Expr *) var, tlist))
3610  continue; /* already got it */
3611 
3612  tlist = lappend(tlist,
3613  makeTargetEntry((Expr *) var,
3614  list_length(tlist) + 1,
3615  NULL,
3616  false));
3617  }
3618 
3619  list_free(vars);
3620 
3621  return tlist;
3622 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
#define RelationGetDescr(relation)
Definition: rel.h:433
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
Definition: nodes.h:517
AttrNumber varattno
Definition: primnodes.h:169
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
Definition: primnodes.h:164
int natts
Definition: tupdesc.h:82
#define PVC_INCLUDE_PLACEHOLDERS
Definition: var.h:24
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:197
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:167
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:54
#define Assert(condition)
Definition: c.h:699
#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 3234 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().

3235 {
3236  char sql[64];
3237  PGresult *res;
3238 
3239  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3240 
3241  /*
3242  * We don't use a PG_TRY block here, so be careful not to throw error
3243  * without releasing the PGresult.
3244  */
3245  res = pgfdw_exec_query(conn, sql);
3246  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3247  pgfdw_report_error(ERROR, res, conn, true, sql);
3248  PQclear(res);
3249 }
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 5682 of file postgres_fdw.c.

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

5683 {
5684  const char *attname = NULL;
5685  const char *relname = NULL;
5686  bool is_wholerow = false;
5688 
5689  if (errpos->rel)
5690  {
5691  /* error occurred in a scan against a foreign table */
5692  TupleDesc tupdesc = RelationGetDescr(errpos->rel);
5693  Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
5694 
5695  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
5696  attname = NameStr(attr->attname);
5697  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
5698  attname = "ctid";
5699  else if (errpos->cur_attno == ObjectIdAttributeNumber)
5700  attname = "oid";
5701 
5702  relname = RelationGetRelationName(errpos->rel);
5703  }
5704  else
5705  {
5706  /* error occurred in a scan against a foreign join */
5707  ForeignScanState *fsstate = errpos->fsstate;
5708  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
5709  EState *estate = fsstate->ss.ps.state;
5710  TargetEntry *tle;
5711 
5712  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
5713  errpos->cur_attno - 1);
5714 
5715  /*
5716  * Target list can have Vars and expressions. For Vars, we can get
5717  * its relation, however for expressions we can't. Thus for
5718  * expressions, just show generic context message.
5719  */
5720  if (IsA(tle->expr, Var))
5721  {
5722  RangeTblEntry *rte;
5723  Var *var = (Var *) tle->expr;
5724 
5725  rte = rt_fetch(var->varno, estate->es_range_table);
5726 
5727  if (var->varattno == 0)
5728  is_wholerow = true;
5729  else
5730  attname = get_attname(rte->relid, var->varattno, false);
5731 
5732  relname = get_rel_name(rte->relid);
5733  }
5734  else
5735  errcontext("processing expression at position %d in select list",
5736  errpos->cur_attno);
5737  }
5738 
5739  if (relname)
5740  {
5741  if (is_wholerow)
5742  errcontext("whole-row reference to foreign table \"%s\"", relname);
5743  else if (attname)
5744  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
5745  }
5746 }
ScanState ss
Definition: execnodes.h:1641
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
#define RelationGetDescr(relation)
Definition: rel.h:433
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define castNode(_type_, nodeptr)
Definition: nodes.h:586
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
AttrNumber varattno
Definition: primnodes.h:169
List * fdw_scan_tlist
Definition: plannodes.h:616
EState * state
Definition: execnodes.h:914
List * es_range_table
Definition: execnodes.h:480
Definition: primnodes.h:164
int natts
Definition: tupdesc.h:82
PlanState ps
Definition: execnodes.h:1192
ForeignScanState * fsstate
Definition: postgres_fdw.c:261
NameData attname
Definition: pg_attribute.h:40
#define list_nth_node(type, list, n)
Definition: pg_list.h:227
#define RelationGetRelationName(relation)
Definition: rel.h:441
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:197
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
Index varno
Definition: primnodes.h:167
Plan * plan
Definition: execnodes.h:912
Expr * expr
Definition: primnodes.h:1376
#define errcontext
Definition: elog.h:164
#define NameStr(name)
Definition: c.h:576
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 3409 of file postgres_fdw.c.

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

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

3412 {
3413  const char **p_values;
3414  int pindex = 0;
3415  MemoryContext oldcontext;
3416 
3417  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
3418 
3419  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums);
3420 
3421  /* 1st parameter should be ctid, if it's in use */
3422  if (tupleid != NULL)
3423  {
3424  /* don't need set_transmission_modes for TID output */
3425  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3426  PointerGetDatum(tupleid));
3427  pindex++;
3428  }
3429 
3430  /* get following parameters from slot */
3431  if (slot != NULL && fmstate->target_attrs != NIL)
3432  {
3433  int nestlevel;
3434  ListCell *lc;
3435 
3436  nestlevel = set_transmission_modes();
3437 
3438  foreach(lc, fmstate->target_attrs)
3439  {
3440  int attnum = lfirst_int(lc);
3441  Datum value;
3442  bool isnull;
3443 
3444  value = slot_getattr(slot, attnum, &isnull);
3445  if (isnull)
3446  p_values[pindex] = NULL;
3447  else
3448  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3449  value);
3450  pindex++;
3451  }
3452 
3453  reset_transmission_modes(nestlevel);
3454  }
3455 
3456  Assert(pindex == fmstate->p_nums);
3457 
3458  MemoryContextSwitchTo(oldcontext);
3459 
3460  return p_values;
3461 }
#define NIL
Definition: pg_list.h:69
#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:1753
#define lfirst_int(lc)
Definition: pg_list.h:107
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:182
uintptr_t Datum
Definition: postgres.h:365
static struct @131 value
int16 attnum
Definition: pg_attribute.h:79
MemoryContext temp_cxt
Definition: postgres_fdw.c:185
#define Assert(condition)
Definition: c.h:699
void reset_transmission_modes(int nestlevel)
void * palloc(Size size)
Definition: mcxt.c:924
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1518

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

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

3039 {
3040  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3041  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3042  int numParams = fsstate->numParams;
3043  const char **values = fsstate->param_values;
3044  PGconn *conn = fsstate->conn;
3046  PGresult *res;
3047 
3048  /*
3049  * Construct array of query parameter values in text format. We do the
3050  * conversions in the short-lived per-tuple context, so as not to cause a
3051  * memory leak over repeated scans.
3052  */
3053  if (numParams > 0)
3054  {
3055  MemoryContext oldcontext;
3056 
3057  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3058 
3059  process_query_params(econtext,
3060  fsstate->param_flinfo,
3061  fsstate->param_exprs,
3062  values);
3063 
3064  MemoryContextSwitchTo(oldcontext);
3065  }
3066 
3067  /* Construct the DECLARE CURSOR command */
3068  initStringInfo(&buf);
3069  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3070  fsstate->cursor_number, fsstate->query);
3071 
3072  /*
3073  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3074  * to infer types for all parameters. Since we explicitly cast every
3075  * parameter (see deparse.c), the "inference" is trivial and will produce
3076  * the desired result. This allows us to avoid assuming that the remote
3077  * server has the same OIDs we do for the parameters' types.
3078  */
3079  if (!PQsendQueryParams(conn, buf.data, numParams,
3080  NULL, values, NULL, NULL, 0))
3081  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3082 
3083  /*
3084  * Get the result, and check for success.
3085  *
3086  * We don't use a PG_TRY block here, so be careful not to throw error
3087  * without releasing the PGresult.
3088  */
3089  res = pgfdw_get_result(conn, buf.data);
3090  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3091  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3092  PQclear(res);
3093 
3094  /* Mark the cursor as created, and show no tuples have been retrieved */
3095  fsstate->cursor_exists = true;
3096  fsstate->tuples = NULL;
3097  fsstate->num_tuples = 0;
3098  fsstate->next_tuple = 0;
3099  fsstate->fetch_ct_2 = 0;
3100  fsstate->eof_reached = false;
3101 
3102  /* Clean up */
3103  pfree(buf.data);
3104 }
ScanState ss
Definition: execnodes.h:1641
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:947
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:1192
void pfree(void *pointer)
Definition: mcxt.c:1031
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:55
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

◆ create_foreign_modify()

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

Definition at line 3257 of file postgres_fdw.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, Assert, PgFdwModifyState::attinmeta, attnum, AttributeNumberIsValid, RangeTblEntry::checkAsUser, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, PgFdwModifyState::ctidAttno, elog, ERROR, EState::es_query_cxt, EState::es_range_table, ExecFindJunkAttributeInTlist(), fmgr_info(), GetConnection(), GetForeignTable(), getTypeOutputInfo(), GetUserId(), GetUserMapping(), PgFdwModifyState::has_returning, lfirst_int, list_length(), PgFdwModifyState::p_flinfo, PgFdwModifyState::p_name, PgFdwModifyState::p_nums, palloc0(), PgFdwScanState::query, PgFdwModifyState::query, PgFdwScanState::rel, PgFdwModifyState::rel, RelationGetDescr, RelationGetRelid, PgFdwScanState::retrieved_attrs, PgFdwModifyState::retrieved_attrs, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, rt_fetch, ForeignTable::serverid, PgFdwModifyState::target_attrs, Plan::targetlist, PgFdwModifyState::temp_cxt, PgFdwScanState::tupdesc, TupleDescAttr, TupleDescGetAttInMetadata(), and user.

Referenced by postgresBeginForeignInsert(), and postgresBeginForeignModify().

3265 {
3266  PgFdwModifyState *fmstate;
3267  Relation rel = resultRelInfo->ri_RelationDesc;
3268  TupleDesc tupdesc = RelationGetDescr(rel);
3269  RangeTblEntry *rte;
3270  Oid userid;
3271  ForeignTable *table;
3272  UserMapping *user;
3273  AttrNumber n_params;
3274  Oid typefnoid;
3275  bool isvarlena;
3276  ListCell *lc;
3277 
3278  /* Begin constructing PgFdwModifyState. */
3279  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3280  fmstate->rel = rel;
3281 
3282  /*
3283  * Identify which user to do the remote access as. This should match what
3284  * ExecCheckRTEPerms() does.
3285  */
3286  rte = rt_fetch(resultRelInfo->ri_RangeTableIndex, estate->es_range_table);
3287  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
3288 
3289  /* Get info about foreign table. */
3290  table = GetForeignTable(RelationGetRelid(rel));
3291  user = GetUserMapping(userid, table->serverid);
3292 
3293  /* Open connection; report that we'll create a prepared statement. */
3294  fmstate->conn = GetConnection(user, true);
3295  fmstate->p_name = NULL; /* prepared statement not made yet */
3296 
3297  /* Set up remote query information. */
3298  fmstate->query = query;
3299  fmstate->target_attrs = target_attrs;
3300  fmstate->has_returning = has_returning;
3301  fmstate->retrieved_attrs = retrieved_attrs;
3302 
3303  /* Create context for per-tuple temp workspace. */
3304  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
3305  "postgres_fdw temporary data",
3307 
3308  /* Prepare for input conversion of RETURNING results. */
3309  if (fmstate->has_returning)
3310  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
3311 
3312  /* Prepare for output conversion of parameters used in prepared stmt. */
3313  n_params = list_length(fmstate->target_attrs) + 1;
3314  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
3315  fmstate->p_nums = 0;
3316 
3317  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3318  {
3319  Assert(subplan != NULL);
3320 
3321  /* Find the ctid resjunk column in the subplan's result */
3323  "ctid");
3324  if (!AttributeNumberIsValid(fmstate->ctidAttno))
3325  elog(ERROR, "could not find junk ctid column");
3326 
3327  /* First transmittable parameter will be ctid */
3328  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
3329  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3330  fmstate->p_nums++;
3331  }
3332 
3333  if (operation == CMD_INSERT || operation == CMD_UPDATE)
3334  {
3335  /* Set up for remaining transmittable parameters */
3336  foreach(lc, fmstate->target_attrs)
3337  {
3338  int attnum = lfirst_int(lc);
3339  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
3340 
3341  Assert(!attr->attisdropped);
3342 
3343  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
3344  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3345  fmstate->p_nums++;
3346  }
3347  }
3348 
3349  Assert(fmstate->p_nums <= n_params);
3350 
3351  return fmstate;
3352 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:397
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:433
Oid GetUserId(void)
Definition: miscinit.c:379
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:202
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
List * es_range_table
Definition: execnodes.h:480
unsigned int Oid
Definition: postgres_ext.h:31
List * retrieved_attrs
Definition: postgres_fdw.c:177
Index ri_RangeTableIndex
Definition: execnodes.h:394
MemoryContext es_query_cxt
Definition: execnodes.h:523
#define ERROR
Definition: elog.h:43
#define lfirst_int(lc)
Definition: pg_list.h:107
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:124
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:197
#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:170
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:182
void * palloc0(Size size)
Definition: mcxt.c:955
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1146
int16 attnum
Definition: pg_attribute.h:79
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:699
Oid serverid
Definition: foreign.h:67
static int list_length(const List *l)
Definition: pg_list.h:89
List * targetlist
Definition: plannodes.h:146
static char * user
Definition: pg_regress.c:93
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:166
AttInMetadata * attinmeta
Definition: postgres_fdw.c:167
#define elog
Definition: elog.h:219
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:407

◆ ec_member_matches_foreign()

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

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

3012 {
3014  Expr *expr = em->em_expr;
3015 
3016  /*
3017  * If we've identified what we're processing in the current scan, we only
3018  * want to match that expression.
3019  */
3020  if (state->current != NULL)
3021  return equal(expr, state->current);
3022 
3023  /*
3024  * Otherwise, ignore anything we've already processed.
3025  */
3026  if (list_member(state->already_used, expr))
3027  return false;
3028 
3029  /* This is the new target to process. */
3030  state->current = expr;
3031  return true;
3032 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2986
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 2607 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, 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().

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

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

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

3664 {
3666  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3667  int numParams = dmstate->numParams;
3668  const char **values = dmstate->param_values;
3669 
3670  /*
3671  * Construct array of query parameter values in text format.
3672  */
3673  if (numParams > 0)
3674  process_query_params(econtext,
3675  dmstate->param_flinfo,
3676  dmstate->param_exprs,
3677  values);
3678 
3679  /*
3680  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3681  * to infer types for all parameters. Since we explicitly cast every
3682  * parameter (see deparse.c), the "inference" is trivial and will produce
3683  * the desired result. This allows us to avoid assuming that the remote
3684  * server has the same OIDs we do for the parameters' types.
3685  */
3686  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
3687  NULL, values, NULL, NULL, 0))
3688  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
3689 
3690  /*
3691  * Get the result, and check for success.
3692  *
3693  * We don't use a PG_TRY block here, so be careful not to throw error
3694  * without releasing the PGresult.
3695  */
3696  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
3697  if (PQresultStatus(dmstate->result) !=
3699  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
3700  dmstate->query);
3701 
3702  /* Get the number of rows affected. */
3703  if (dmstate->has_returning)
3704  dmstate->num_tuples = PQntuples(dmstate->result);
3705  else
3706  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
3707 }
ScanState ss
Definition: execnodes.h:1641
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:947
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:1192
#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 3110 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().

3111 {
3112  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3113  PGresult *volatile res = NULL;
3114  MemoryContext oldcontext;
3115 
3116  /*
3117  * We'll store the tuples in the batch_cxt. First, flush the previous
3118  * batch.
3119  */
3120  fsstate->tuples = NULL;
3121  MemoryContextReset(fsstate->batch_cxt);
3122  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3123 
3124  /* PGresult must be released before leaving this function. */
3125  PG_TRY();
3126  {
3127  PGconn *conn = fsstate->conn;
3128  char sql[64];
3129  int numrows;
3130  int i;
3131 
3132  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3133  fsstate->fetch_size, fsstate->cursor_number);
3134 
3135  res = pgfdw_exec_query(conn, sql);
3136  /* On error, report the original query, not the FETCH. */
3137  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3138  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3139 
3140  /* Convert the data into HeapTuples */
3141  numrows = PQntuples(res);
3142  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3143  fsstate->num_tuples = numrows;
3144  fsstate->next_tuple = 0;
3145 
3146  for (i = 0; i < numrows; i++)
3147  {
3148  Assert(IsA(node->ss.ps.plan, ForeignScan));
3149 
3150  fsstate->tuples[i] =
3152  fsstate->rel,
3153  fsstate->attinmeta,
3154  fsstate->retrieved_attrs,
3155  node,
3156  fsstate->temp_cxt);
3157  }
3158 
3159  /* Update fetch_ct_2 */
3160  if (fsstate->fetch_ct_2 < 2)
3161  fsstate->fetch_ct_2++;
3162 
3163  /* Must be EOF if we didn't get as many tuples as we asked for. */
3164  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3165 
3166  PQclear(res);
3167  res = NULL;
3168  }
3169  PG_CATCH();
3170  {
3171  if (res)
3172  PQclear(res);
3173  PG_RE_THROW();
3174  }
3175  PG_END_TRY();
3176 
3177  MemoryContextSwitchTo(oldcontext);
3178 }
ScanState ss
Definition: execnodes.h:1641
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
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:136
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:1192
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:55
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:955
MemoryContext temp_cxt
Definition: postgres_fdw.c:156
Plan * plan
Definition: execnodes.h:912
void PQclear(PGresult *res)
Definition: fe-exec.c:671
#define PG_CATCH()
Definition: elog.h:293
#define Assert(condition)
Definition: c.h:699
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 5753 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().

5754 {
5755  ListCell *lc_em;
5756 
5757  foreach(lc_em, ec->ec_members)
5758  {
5759  EquivalenceMember *em = lfirst(lc_em);
5760 
5761  if (bms_is_subset(em->em_relids, rel->relids))
5762  {
5763  /*
5764  * If there is more than one equivalence member whose Vars are
5765  * taken entirely from this relation, we'll be content to choose
5766  * any one of those.
5767  */
5768  return em->em_expr;
5769  }
5770  }
5771 
5772  /* We didn't find any suitable equivalence class expression */
5773  return NULL;
5774 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:374
Relids relids
Definition: relation.h:612
Relids em_relids
Definition: relation.h:947
#define lfirst(lc)
Definition: pg_list.h:106
List * ec_members
Definition: relation.h:898

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 3501 of file postgres_fdw.c.

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

Referenced by postgresEndForeignInsert(), and postgresEndForeignModify().

3502 {
3503  Assert(fmstate != NULL);
3504 
3505  /* If we created a prepared statement, destroy it */
3506  if (fmstate->p_name)
3507  {
3508  char sql[64];
3509  PGresult *res;
3510 
3511  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
3512 
3513  /*
3514  * We don't use a PG_TRY block here, so be careful not to throw error
3515  * without releasing the PGresult.
3516  */
3517  res = pgfdw_exec_query(fmstate->conn, sql);
3518  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3519  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
3520  PQclear(res);
3521  fmstate->p_name = NULL;
3522  }
3523 
3524  /* Release remote connection */
3525  ReleaseConnection(fmstate->conn);
3526  fmstate->conn = NULL;
3527 }
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
#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 Assert(condition)
Definition: c.h:699
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508

◆ foreign_grouping_ok()

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

Definition at line 5189 of file postgres_fdw.c.

References add_to_flat_tlist(), appendStringInfo(), Assert, RestrictInfo::clause, StringInfoData::data, RelOptInfo::fdw_private, get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), Query::groupClause, PgFdwRelationInfo::grouped_tlist, Query::groupingSets, 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, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, and TargetEntry::ressortgroupref.

Referenced by add_foreign_grouping_paths().

5191 {
5192  Query *query = root->parse;
5193  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
5194  PathTarget *grouping_target = grouped_rel->reltarget;
5195  PgFdwRelationInfo *ofpinfo;
5196  List *aggvars;
5197  ListCell *lc;
5198  int i;
5199  List *tlist = NIL;
5200 
5201  /* We currently don't support pushing Grouping Sets. */
5202  if (query->groupingSets)
5203  return false;
5204 
5205  /* Get the fpinfo of the underlying scan relation. */
5206  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
5207 
5208  /*
5209  * If underlying scan relation has any local conditions, those conditions
5210  * are required to be applied before performing aggregation. Hence the
5211  * aggregate cannot be pushed down.
5212  */
5213  if (ofpinfo->local_conds)
5214  return false;
5215 
5216  /*
5217  * Examine grouping expressions, as well as other expressions we'd need to
5218  * compute, and check whether they are safe to push down to the foreign
5219  * server. All GROUP BY expressions will be part of the grouping target
5220  * and thus there is no need to search for them separately. Add grouping
5221  * expressions into target list which will be passed to foreign server.
5222  */
5223  i = 0;
5224  foreach(lc, grouping_target->exprs)
5225  {
5226  Expr *expr = (Expr *) lfirst(lc);
5227  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
5228  ListCell *l;
5229 
5230  /* Check whether this expression is part of GROUP BY clause */
5231  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
5232  {
5233  TargetEntry *tle;
5234 
5235  /*
5236  * If any GROUP BY expression is not shippable, then we cannot
5237  * push down aggregation to the foreign server.
5238  */
5239  if (!is_foreign_expr(root, grouped_rel, expr))
5240  return false;
5241 
5242  /*
5243  * Pushable, so add to tlist. We need to create a TLE for this
5244  * expression and apply the sortgroupref to it. We cannot use
5245  * add_to_flat_tlist() here because that avoids making duplicate
5246  * entries in the tlist. If there are duplicate entries with
5247  * distinct sortgrouprefs, we have to duplicate that situation in
5248  * the output tlist.
5249  */
5250  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
5251  tle->ressortgroupref = sgref;
5252  tlist = lappend(tlist, tle);
5253  }
5254  else
5255  {
5256  /*
5257  * Non-grouping expression we need to compute. Is it shippable?
5258  */
5259  if (is_foreign_expr(root, grouped_rel, expr))
5260  {
5261  /* Yes, so add to tlist as-is; OK to suppress duplicates */
5262  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5263  }
5264  else
5265  {
5266  /* Not pushable as a whole; extract its Vars and aggregates */
5267  aggvars = pull_var_clause((Node *) expr,
5269 
5270  /*
5271  * If any aggregate expression is not shippable, then we
5272  * cannot push down aggregation to the foreign server.
5273  */
5274  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
5275  return false;
5276 
5277  /*
5278  * Add aggregates, if any, into the targetlist. Plain Vars
5279  * outside an aggregate can be ignored, because they should be
5280  * either same as some GROUP BY column or part of some GROUP
5281  * BY expression. In either case, they are already part of
5282  * the targetlist and thus no need to add them again. In fact
5283  * including plain Vars in the tlist when they do not match a
5284  * GROUP BY column would cause the foreign server to complain
5285  * that the shipped query is invalid.
5286  */
5287  foreach(l, aggvars)
5288  {
5289  Expr *expr = (Expr *) lfirst(l);
5290 
5291  if (IsA(expr, Aggref))
5292  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5293  }
5294  }
5295  }
5296 
5297  i++;
5298  }
5299 
5300  /*
5301  * Classify the pushable and non-pushable HAVING clauses and save them in
5302  * remote_conds and local_conds of the grouped rel's fpinfo.
5303  */
5304  if (havingQual)
5305  {
5306  ListCell *lc;
5307 
5308  foreach(lc, (List *) havingQual)
5309  {
5310  Expr *expr = (Expr *) lfirst(lc);
5311  RestrictInfo *rinfo;
5312 
5313  /*
5314  * Currently, the core code doesn't wrap havingQuals in
5315  * RestrictInfos, so we must make our own.
5316  */
5317  Assert(!IsA(expr, RestrictInfo));
5318  rinfo = make_restrictinfo(expr,
5319  true,
5320  false,
5321  false,
5322  root->qual_security_level,
5323  grouped_rel->relids,
5324  NULL,
5325  NULL);
5326  if (is_foreign_expr(root, grouped_rel, expr))
5327  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5328  else
5329  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5330  }
5331  }
5332 
5333  /*
5334  * If there are any local conditions, pull Vars and aggregates from it and
5335  * check whether they are safe to pushdown or not.
5336  */
5337  if (fpinfo->local_conds)
5338  {
5339  List *aggvars = NIL;
5340  ListCell *lc;
5341 
5342  foreach(lc, fpinfo->local_conds)
5343  {
5344  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5345 
5346  aggvars = list_concat(aggvars,
5347  pull_var_clause((Node *) rinfo->clause,
5349  }
5350 
5351  foreach(lc, aggvars)
5352  {
5353  Expr *expr = (Expr *) lfirst(lc);
5354 
5355  /*
5356  * If aggregates within local conditions are not safe to push
5357  * down, then we cannot push down the query. Vars are already
5358  * part of GROUP BY clause which are checked above, so no need to
5359  * access them again here.
5360  */
5361  if (IsA(expr, Aggref))
5362  {
5363  if (!is_foreign_expr(root, grouped_rel, expr))
5364  return false;
5365 
5366  tlist = add_to_flat_tlist(tlist, list_make1(expr));
5367  }
5368  }
5369  }
5370 
5371  /* Store generated targetlist */
5372  fpinfo->grouped_tlist = tlist;
5373 
5374  /* Safe to pushdown */
5375  fpinfo->pushdown_safe = true;
5376 
5377  /*
5378  * Set cached relation costs to some negative value, so that we can detect
5379  * when they are set to some sensible costs, during one (usually the
5380  * first) of the calls to estimate_path_cost_size().
5381  */
5382  fpinfo->rel_startup_cost = -1;
5383  fpinfo->rel_total_cost = -1;
5384 
5385  /*
5386  * Set the string describing this grouped relation to be used in EXPLAIN
5387  * output of corresponding ForeignScan.
5388  */
5389  fpinfo->relation_name = makeStringInfo();
5390  appendStringInfo(fpinfo->relation_name, "Aggregate on (%s)",
5391  ofpinfo->relation_name->data);
5392 
5393  return true;
5394 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
Query * parse
Definition: relation.h:169
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:150
Definition: nodes.h:517
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:612
#define get_pathtarget_sortgroupref(target, colno)
Definition: relation.h:1015
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:1880
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:446
unsigned int Index
Definition: c.h:442
StringInfo relation_name
Definition: postgres_fdw.h:86
void * fdw_private
Definition: relation.h:664
#define Assert(condition)
Definition: c.h:699
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:309
List * groupClause
Definition: parsenodes.h:148
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:230
int i
Index ressortgroupref
Definition: primnodes.h:1379
Definition: pg_list.h:45
struct PathTarget * reltarget
Definition: relation.h:623

◆ foreign_join_ok()

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

Definition at line 4639 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_OTHER_REL, IS_OUTER_JOIN, JOIN_FULL, JOIN_INNER, JOIN_LEFT, PlannerInfo::join_rel_list, JOIN_RIGHT, PgFdwRelationInfo::joinclauses, PgFdwRelationInfo::jointype, lappend(), lfirst, lfirst_node, list_concat(), list_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, RINFO_IS_PUSHED_DOWN, Query::rtable, PgFdwRelationInfo::server, RelOptInfo::top_parent_relids, PgFdwRelationInfo::use_remote_estimate, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignJoinPaths().

4642 {
4643  PgFdwRelationInfo *fpinfo;
4644  PgFdwRelationInfo *fpinfo_o;
4645  PgFdwRelationInfo *fpinfo_i;
4646  ListCell *lc;
4647  List *joinclauses;
4648 
4649  /*
4650  * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
4651  * Constructing queries representing SEMI and ANTI joins is hard, hence
4652  * not considered right now.
4653  */
4654  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
4655  jointype != JOIN_RIGHT && jointype != JOIN_FULL)
4656  return false;
4657 
4658  /*
4659  * If either of the joining relations is marked as unsafe to pushdown, the
4660  * join can not be pushed down.
4661  */
4662  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
4663  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
4664  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
4665  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
4666  !fpinfo_i || !fpinfo_i->pushdown_safe)
4667  return false;
4668 
4669  /*
4670  * If joining relations have local conditions, those conditions are
4671  * required to be applied before joining the relations. Hence the join can
4672  * not be pushed down.
4673  */
4674  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
4675  return false;
4676 
4677  /*
4678  * Merge FDW options. We might be tempted to do this after we have deemed
4679  * the foreign join to be OK. But we must do this beforehand so that we
4680  * know which quals can be evaluated on the foreign server, which might
4681  * depend on shippable_extensions.
4682  */
4683  fpinfo->server = fpinfo_o->server;
4684  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
4685 
4686  /*
4687  * Separate restrict list into join quals and pushed-down (other) quals.
4688  *
4689  * Join quals belonging to an outer join must all be shippable, else we
4690  * cannot execute the join remotely. Add such quals to 'joinclauses'.
4691  *
4692  * Add other quals to fpinfo->remote_conds if they are shippable, else to
4693  * fpinfo->local_conds. In an inner join it's okay to execute conditions
4694  * either locally or remotely; the same is true for pushed-down conditions
4695  * at an outer join.
4696  *
4697  * Note we might return failure after having already scribbled on
4698  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
4699  * won't consult those lists again if we deem the join unshippable.
4700  */
4701  joinclauses = NIL;
4702  foreach(lc, extra->restrictlist)
4703  {
4704  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
4705  bool is_remote_clause = is_foreign_expr(root, joinrel,
4706  rinfo->clause);
4707 
4708  if (IS_OUTER_JOIN(jointype) &&
4709  !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
4710  {
4711  if (!is_remote_clause)
4712  return false;
4713  joinclauses = lappend(joinclauses, rinfo);
4714  }
4715  else
4716  {
4717  if (is_remote_clause)
4718  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
4719  else
4720  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
4721  }
4722  }
4723 
4724  /*
4725  * deparseExplicitTargetList() isn't smart enough to handle anything other
4726  * than a Var. In particular, if there's some PlaceHolderVar that would
4727  * need to be evaluated within this join tree (because there's an upper
4728  * reference to a quantity that may go to NULL as a result of an outer
4729  * join), then we can't try to push the join down because we'll fail when
4730  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
4731  * needs to be evaluated *at the top* of this join tree is OK, because we
4732  * can do that locally after fetching the results from the remote side.
4733  */
4734  foreach(lc, root->placeholder_list)
4735  {
4736  PlaceHolderInfo *phinfo = lfirst(lc);
4737  Relids relids;
4738 
4739  /* PlaceHolderInfo refers to parent relids, not child relids. */
4740  relids = IS_OTHER_REL(joinrel) ?
4741  joinrel->top_parent_relids : joinrel->relids;
4742 
4743  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
4744  bms_nonempty_difference(relids, phinfo->ph_eval_at))
4745  return false;
4746  }
4747 
4748  /* Save the join clauses, for later use. */
4749  fpinfo->joinclauses = joinclauses;
4750 
4751  fpinfo->outerrel = outerrel;
4752  fpinfo->innerrel = innerrel;
4753  fpinfo->jointype = jointype;
4754 
4755  /*
4756  * By default, both the input relations are not required to be deparsed as
4757  * subqueries, but there might be some relations covered by the input
4758  * relations that are required to be deparsed as subqueries, so save the
4759  * relids of those relations for later use by the deparser.
4760  */
4761  fpinfo->make_outerrel_subquery = false;
4762  fpinfo->make_innerrel_subquery = false;
4763  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
4764  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
4766  fpinfo_i->lower_subquery_rels);
4767 
4768  /*
4769  * Pull the other remote conditions from the joining relations into join
4770  * clauses or other remote clauses (remote_conds) of this relation
4771  * wherever possible. This avoids building subqueries at every join step.
4772  *
4773  * For an inner join, clauses from both the relations are added to the
4774  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
4775  * the outer side are added to remote_conds since those can be evaluated
4776  * after the join is evaluated. The clauses from inner side are added to
4777  * the joinclauses, since they need to be evaluated while constructing the
4778  * join.
4779  *
4780  * For a FULL OUTER JOIN, the other clauses from either relation can not
4781  * be added to the joinclauses or remote_conds, since each relation acts
4782  * as an outer relation for the other.
4783  *
4784  * The joining sides can not have local conditions, thus no need to test
4785  * shippability of the clauses being pulled up.
4786  */
4787  switch (jointype)
4788  {
4789  case JOIN_INNER:
4790  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4791  list_copy(fpinfo_i->remote_conds));
4792  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4793  list_copy(fpinfo_o->remote_conds));
4794  break;
4795 
4796  case JOIN_LEFT:
4797  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
4798  list_copy(fpinfo_i->remote_conds));
4799  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4800  list_copy(fpinfo_o->remote_conds));
4801  break;
4802 
4803  case JOIN_RIGHT:
4804  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
4805  list_copy(fpinfo_o->remote_conds));
4806  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4807  list_copy(fpinfo_i->remote_conds));
4808  break;
4809 
4810  case JOIN_FULL:
4811 
4812  /*
4813  * In this case, if any of the input relations has conditions, we
4814  * need to deparse that relation as a subquery so that the
4815  * conditions can be evaluated before the join. Remember it in
4816  * the fpinfo of this relation so that the deparser can take
4817  * appropriate action. Also, save the relids of base relations
4818  * covered by that relation for later use by the deparser.
4819  */
4820  if (fpinfo_o->remote_conds)
4821  {
4822  fpinfo->make_outerrel_subquery = true;
4823  fpinfo->lower_subquery_rels =
4825  outerrel->relids);
4826  }
4827  if (fpinfo_i->remote_conds)
4828  {
4829  fpinfo->make_innerrel_subquery = true;
4830  fpinfo->lower_subquery_rels =
4832  innerrel->relids);
4833  }
4834  break;
4835 
4836  default:
4837  /* Should not happen, we have just checked this above */
4838  elog(ERROR, "unsupported join type %d", jointype);
4839  }
4840 
4841  /*
4842  * For an inner join, all restrictions can be treated alike. Treating the
4843  * pushed down conditions as join conditions allows a top level full outer
4844  * join to be deparsed without requiring subqueries.
4845  */
4846  if (jointype == JOIN_INNER)
4847  {
4848  Assert(!fpinfo->joinclauses);
4849  fpinfo->joinclauses = fpinfo->remote_conds;
4850  fpinfo->remote_conds = NIL;
4851  }
4852 
4853  /* Mark that this join can be pushed down safely */
4854  fpinfo->pushdown_safe = true;
4855 
4856  /* Get user mapping */
4857  if (fpinfo->use_remote_estimate)
4858  {
4859  if (fpinfo_o->use_remote_estimate)
4860  fpinfo->user = fpinfo_o->user;
4861  else
4862  fpinfo->user = fpinfo_i->user;
4863  }
4864  else
4865  fpinfo->user = NULL;
4866 
4867  /*
4868  * Set cached relation costs to some negative value, so that we can detect
4869  * when they are set to some sensible costs, during one (usually the
4870  * first) of the calls to estimate_path_cost_size().
4871  */
4872  fpinfo->rel_startup_cost = -1;
4873  fpinfo->rel_total_cost = -1;
4874 
4875  /*
4876  * Set the string describing this join relation to be used in EXPLAIN
4877  * output of corresponding ForeignScan.
4878  */
4879  fpinfo->relation_name = makeStringInfo();
4880  appendStringInfo(fpinfo->relation_name, "(%s) %s JOIN (%s)",
4881  fpinfo_o->relation_name->data,
4882  get_jointype_name(fpinfo->jointype),
4883  fpinfo_i->relation_name->data);
4884 
4885  /*
4886  * Set the relation index. This is defined as the position of this
4887  * joinrel in the join_rel_list list plus the length of the rtable list.
4888  * Note that since this joinrel is at the end of the join_rel_list list
4889  * when we are called, we can get the position by list_length.
4890  */
4891  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
4892  fpinfo->relation_index =
4894 
4895  return true;
4896 }
#define NIL
Definition: pg_list.h:69
Query * parse
Definition: relation.h:169
Relids ph_eval_at
Definition: relation.h:2194
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
ForeignServer * server
Definition: postgres_fdw.h:76
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:730
List * list_copy(const List *oldlist)
Definition: list.c:1160
Relids lower_subquery_rels
Definition: postgres_fdw.h:103
#define IS_OTHER_REL(rel)
Definition: relation.h:600
List * join_rel_list
Definition: relation.h:229
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
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: relation.h:1957
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
List * rtable
Definition: parsenodes.h:137
#define ERROR
Definition: elog.h:43
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:374
#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:612
List * lappend(List *list, void *datum)
Definition: list.c:128
Expr * clause
Definition: relation.h:1880
UserMapping * user
Definition: postgres_fdw.h:77
List * restrictlist
Definition: relation.h:2310
StringInfo relation_name
Definition: postgres_fdw.h:86
void * fdw_private
Definition: relation.h:664
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:284
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:270
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:821
Relids top_parent_relids
Definition: relation.h:681
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:560

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

2960 {
2961  PGresult *volatile res = NULL;
2962 
2963  /* PGresult must be released before leaving this function. */
2964  PG_TRY();
2965  {
2966  char *line;
2967  char *p;
2968  int n;
2969 
2970  /*
2971  * Execute EXPLAIN remotely.
2972  */
2973  res = pgfdw_exec_query(conn, sql);
2974  if (PQresultStatus(res) != PGRES_TUPLES_OK)
2975  pgfdw_report_error(ERROR, res, conn, false, sql);
2976 
2977  /*
2978  * Extract cost numbers for topmost plan node. Note we search for a
2979  * left paren from the end of the line to avoid being confused by
2980  * other uses of parentheses.
2981  */
2982  line = PQgetvalue(res, 0, 0);
2983  p = strrchr(line, '(');
2984  if (p == NULL)
2985  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
2986  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
2987  startup_cost, total_cost, rows, width);
2988  if (n != 4)
2989  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
2990 
2991  PQclear(res);
2992  res = NULL;
2993  }
2994  PG_CATCH();
2995  {
2996  if (res)
2997  PQclear(res);
2998  PG_RE_THROW();
2999  }
3000  PG_END_TRY();
3001 }
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 3713 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().

3714 {
3716  EState *estate = node->ss.ps.state;
3717  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
3718  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
3719  TupleTableSlot *resultSlot;
3720 
3721  Assert(resultRelInfo->ri_projectReturning);
3722 
3723  /* If we didn't get any tuples, must be end of data. */
3724  if (dmstate->next_tuple >= dmstate->num_tuples)
3725  return ExecClearTuple(slot);
3726 
3727  /* Increment the command es_processed count if necessary. */
3728  if (dmstate->set_processed)
3729  estate->es_processed += 1;
3730 
3731  /*
3732  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
3733  * tuple. (has_returning is false when the local query is of the form
3734  * "UPDATE/DELETE .. RETURNING 1" for example.)
3735  */
3736  if (!dmstate->has_returning)
3737  {
3738  ExecStoreAllNullTuple(slot);
3739  resultSlot = slot;
3740  }
3741  else
3742  {
3743  /*
3744  * On error, be sure to release the PGresult on the way out. Callers
3745  * do not have PG_TRY blocks to ensure this happens.
3746  */
3747  PG_TRY();
3748  {
3749  HeapTuple newtup;
3750 
3751  newtup = make_tuple_from_result_row(dmstate->result,
3752  dmstate->next_tuple,
3753  dmstate->rel,
3754  dmstate->attinmeta,
3755  dmstate->retrieved_attrs,
3756  node,
3757  dmstate->temp_cxt);
3758  ExecStoreTuple(newtup, slot, InvalidBuffer, false);
3759  }
3760  PG_CATCH();
3761  {
3762  if (dmstate->result)
3763  PQclear(dmstate->result);
3764  PG_RE_THROW();
3765  }
3766  PG_END_TRY();
3767 
3768  /* Get the updated/deleted tuple. */
3769  if (dmstate->rel)
3770  resultSlot = slot;
3771  else
3772  resultSlot = apply_returning_filter(dmstate, slot, estate);
3773  }
3774  dmstate->next_tuple++;
3775 
3776  /* Make slot available for evaluation of the local query RETURNING list. */
3777  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
3778  resultSlot;
3779 
3780  return slot;
3781 }
ScanState ss
Definition: execnodes.h:1641
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:1195
EState * state
Definition: execnodes.h:914
PlanState ps
Definition: execnodes.h:1192
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:699
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 714 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().

715 {
716  List *useful_eclass_list = NIL;
717  ListCell *lc;
718  Relids relids;
719 
720  /*
721  * First, consider whether any active EC is potentially useful for a merge
722  * join against this relation.
723  */
724  if (rel->has_eclass_joins)
725  {
726  foreach(lc, root->eq_classes)
727  {
728  EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc);
729 
730  if (eclass_useful_for_merging(root, cur_ec, rel))
731  useful_eclass_list = lappend(useful_eclass_list, cur_ec);
732  }
733  }
734 
735  /*
736  * Next, consider whether there are any non-EC derivable join clauses that
737  * are merge-joinable. If the joininfo list is empty, we can exit
738  * quickly.
739  */
740  if (rel->joininfo == NIL)
741  return useful_eclass_list;
742 
743  /* If this is a child rel, we must use the topmost parent rel to search. */
744  if (IS_OTHER_REL(rel))
745  {
747  relids = rel->top_parent_relids;
748  }
749  else
750  relids = rel->relids;
751 
752  /* Check each join clause in turn. */
753  foreach(lc, rel->joininfo)
754  {
755  RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
756 
757  /* Consider only mergejoinable clauses */
758  if (restrictinfo->mergeopfamilies == NIL)
759  continue;
760 
761  /* Make sure we've got canonical ECs. */
762  update_mergeclause_eclasses(root, restrictinfo);
763 
764  /*
765  * restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
766  * that left_ec and right_ec will be initialized, per comments in
767  * distribute_qual_to_rels.
768  *
769  * We want to identify which side of this merge-joinable clause
770  * contains columns from the relation produced by this RelOptInfo. We
771  * test for overlap, not containment, because there could be extra
772  * relations on either side. For example, suppose we've got something
773  * like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
774  * A.y = D.y. The input rel might be the joinrel between A and B, and
775  * we'll consider the join clause A.y = D.y. relids contains a
776  * relation not involved in the join class (B) and the equivalence
777  * class for the left-hand side of the clause contains a relation not
778  * involved in the input rel (C). Despite the fact that we have only
779  * overlap and not containment in either direction, A.y is potentially
780  * useful as a sort column.
781  *
782  * Note that it's even possible that relids overlaps neither side of
783  * the join clause. For example, consider A LEFT JOIN B ON A.x = B.x
784  * AND A.x = 1. The clause A.x = 1 will appear in B's joininfo list,
785  * but overlaps neither side of B. In that case, we just skip this
786  * join clause, since it doesn't suggest a useful sort order for this
787  * relation.
788  */
789  if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
790  useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
791  restrictinfo->right_ec);
792  else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
793  useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
794  restrictinfo->left_ec);
795  }
796 
797  return useful_eclass_list;
798 }
bool has_eclass_joins
Definition: relation.h:678
#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:600
EquivalenceClass * right_ec
Definition: relation.h:1929
List * mergeopfamilies
Definition: relation.h:1925
List * list_append_unique_ptr(List *list, void *datum)
Definition: list.c:975
List * joininfo
Definition: relation.h:676
Relids ec_relids
Definition: relation.h:901
Relids relids
Definition: relation.h:612
List * lappend(List *list, void *datum)
Definition: list.c:128
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:729
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
List * eq_classes
Definition: relation.h:249
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:509
EquivalenceClass * left_ec
Definition: relation.h:1928
Definition: pg_list.h:45
void update_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
Definition: pathkeys.c:977
Relids top_parent_relids
Definition: relation.h:681

◆ get_useful_pathkeys_for_relation()

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

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

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

3790 {
3791  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
3792  ListCell *lc;
3793  int i;
3794 
3795  /*
3796  * Calculate the mapping between the fdw_scan_tlist's entries and the
3797  * result tuple's attributes.
3798  *
3799  * The "map" is an array of indexes of the result tuple's attributes in
3800  * fdw_scan_tlist, i.e., one entry for every attribute of the result
3801  * tuple. We store zero for any attributes that don't have the
3802  * corresponding entries in that list, marking that a NULL is needed in
3803  * the result tuple.
3804  *
3805  * Also get the indexes of the entries for ctid and oid if any.
3806  */
3807  dmstate->attnoMap = (AttrNumber *)
3808  palloc0(resultTupType->natts * sizeof(AttrNumber));
3809 
3810  dmstate->ctidAttno = dmstate->oidAttno = 0;
3811 
3812  i = 1;
3813  dmstate->hasSystemCols = false;
3814  foreach(lc, fdw_scan_tlist)
3815  {
3816  TargetEntry *tle = (TargetEntry *) lfirst(lc);
3817  Var *var = (Var *) tle->expr;
3818 
3819  Assert(IsA(var, Var));
3820 
3821  /*
3822  * If the Var is a column of the target relation to be retrieved from
3823  * the foreign server, get the index of the entry.
3824  */
3825  if (var->varno == rtindex &&
3826  list_member_int(dmstate->retrieved_attrs, i))
3827  {
3828  int attrno = var->varattno;
3829 
3830  if (attrno < 0)
3831  {
3832  /*
3833  * We don't retrieve system columns other than ctid and oid.
3834  */
3835  if (attrno == SelfItemPointerAttributeNumber)
3836  dmstate->ctidAttno = i;
3837  else if (attrno == ObjectIdAttributeNumber)
3838  dmstate->oidAttno = i;
3839  else
3840  Assert(false);
3841  dmstate->hasSystemCols = true;
3842  }
3843  else
3844  {
3845  /*
3846  * We don't retrieve whole-row references to the target
3847  * relation either.
3848  */
3849  Assert(attrno > 0);
3850 
3851  dmstate->attnoMap[attrno - 1] = i;
3852  }
3853  }
3854  i++;
3855  }
3856 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
#define RelationGetDescr(relation)
Definition: rel.h:433
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
AttrNumber varattno
Definition: primnodes.h:169
Definition: primnodes.h:164
int natts
Definition: tupdesc.h:82
bool list_member_int(const List *list, int datum)
Definition: list.c:485
Index varno
Definition: primnodes.h:167
void * palloc0(Size size)
Definition: mcxt.c:955
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Expr * expr
Definition: primnodes.h:1376
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 5515 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().

5522 {
5523  HeapTuple tuple;
5524  TupleDesc tupdesc;
5525  Datum *values;
5526  bool *nulls;
5527  ItemPointer ctid = NULL;
5528  Oid oid = InvalidOid;
5529  ConversionLocation errpos;
5530  ErrorContextCallback errcallback;
5531  MemoryContext oldcontext;
5532  ListCell *lc;
5533  int j;
5534 
5535  Assert(row < PQntuples(res));
5536 
5537  /*
5538  * Do the following work in a temp context that we reset after each tuple.
5539  * This cleans up not only the data we have direct access to, but any
5540  * cruft the I/O functions might leak.
5541  */
5542  oldcontext = MemoryContextSwitchTo(temp_context);
5543 
5544  if (rel)
5545  tupdesc = RelationGetDescr(rel);
5546  else
5547  {
5548  Assert(fsstate);
5549  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
5550  }
5551 
5552  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
5553  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
5554  /* Initialize to nulls for any columns not present in result */
5555  memset(nulls, true, tupdesc->natts * sizeof(bool));
5556 
5557  /*
5558  * Set up and install callback to report where conversion error occurs.
5559  */
5560  errpos.rel = rel;
5561  errpos.cur_attno = 0;
5562  errpos.fsstate = fsstate;
5563  errcallback.callback = conversion_error_callback;
5564  errcallback.arg = (void *) &errpos;
5565  errcallback.previous = error_context_stack;
5566  error_context_stack = &errcallback;
5567 
5568  /*
5569  * i indexes columns in the relation, j indexes columns in the PGresult.
5570  */
5571  j = 0;
5572  foreach(lc, retrieved_attrs)
5573  {
5574  int i = lfirst_int(lc);
5575  char *valstr;
5576 
5577  /* fetch next column's textual value */
5578  if (PQgetisnull(res, row, j))
5579  valstr = NULL;
5580  else
5581  valstr = PQgetvalue(res, row, j);
5582 
5583  /*
5584  * convert value to internal representation
5585  *
5586  * Note: we ignore system columns other than ctid and oid in result
5587  */
5588  errpos.cur_attno = i;
5589  if (i > 0)
5590  {
5591  /* ordinary column */
5592  Assert(i <= tupdesc->natts);
5593  nulls[i - 1] = (valstr == NULL);
5594  /* Apply the input function even to nulls, to support domains */
5595  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
5596  valstr,
5597  attinmeta->attioparams[i - 1],
5598  attinmeta->atttypmods[i - 1]);
5599  }
5600  else if (i == SelfItemPointerAttributeNumber)
5601  {
5602  /* ctid */
5603  if (valstr != NULL)
5604  {
5605  Datum datum;
5606 
5607  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
5608  ctid = (ItemPointer) DatumGetPointer(datum);
5609  }
5610  }
5611  else if (i == ObjectIdAttributeNumber)
5612  {
5613  /* oid */
5614  if (valstr != NULL)
5615  {
5616  Datum datum;
5617 
5618  datum = DirectFunctionCall1(oidin, CStringGetDatum(valstr));
5619  oid = DatumGetObjectId(datum);
5620  }
5621  }
5622  errpos.cur_attno = 0;
5623 
5624  j++;
5625  }
5626 
5627  /* Uninstall error context callback. */
5628  error_context_stack = errcallback.previous;
5629 
5630  /*
5631  * Check we got the expected number of columns. Note: j == 0 and
5632  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
5633  */
5634  if (j > 0 && j != PQnfields(res))
5635  elog(ERROR, "remote query result does not match the foreign table");
5636 
5637  /*
5638  * Build the result tuple in caller's memory context.
5639  */
5640  MemoryContextSwitchTo(oldcontext);
5641 
5642  tuple = heap_form_tuple(tupdesc, values, nulls);
5643 
5644  /*
5645  * If we have a CTID to return, install it in both t_self and t_ctid.
5646  * t_self is the normal place, but if the tuple is converted to a
5647  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
5648  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
5649  */
5650  if (ctid)
5651  tuple->t_self = tuple->t_data->t_ctid = *ctid;
5652 
5653  /*
5654  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
5655  * heap_form_tuple. heap_form_tuple actually creates the tuple with
5656  * DatumTupleFields, not HeapTupleFields, but the executor expects
5657  * HeapTupleFields and will happily extract system columns on that
5658  * assumption. If we don't do this then, for example, the tuple length
5659  * ends up in the xmin field, which isn't what we want.
5660  */
5664 
5665  /*
5666  * If we have an OID to return, install it.
5667  */
5668  if (OidIsValid(oid))
5669  HeapTupleSetOid(tuple, oid);
5670 
5671  /* Clean up */
5672  MemoryContextReset(temp_context);
5673 
5674  return tuple;
5675 }
ScanState ss
Definition: execnodes.h:1641
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:433
#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:1195
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1074
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:590
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:605
Oid * attioparams
Definition: funcapi.h:45
int natts
Definition: tupdesc.h:82
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:715
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:157
ItemPointerData t_self
Definition: htup.h:65
#define CStringGetDatum(X)
Definition: postgres.h:561
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:385
#define InvalidTransactionId
Definition: transam.h:31
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
void * palloc0(Size size)
Definition: mcxt.c:955
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:1709
#define InvalidOid
Definition: postgres_ext.h:36
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:53
#define Assert(condition)
Definition: c.h:699
#define DatumGetPointer(X)
Definition: postgres.h:532
static Datum values[MAXATTR]
Definition: bootstrap.c:164
void * palloc(Size size)
Definition: mcxt.c:924
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:402
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:324

◆ merge_fdw_options()

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

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

5010 {
5011  /* We must always have fpinfo_o. */
5012  Assert(fpinfo_o);
5013 
5014  /* fpinfo_i may be NULL, but if present the servers must both match. */
5015  Assert(!fpinfo_i ||
5016  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
5017 
5018  /*
5019  * Copy the server specific FDW options. (For a join, both relations come
5020  * from the same server, so the server options should have the same value
5021  * for both relations.)
5022  */
5023  fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
5024  fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
5025  fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
5026  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
5027  fpinfo->fetch_size = fpinfo_o->fetch_size;
5028 
5029  /* Merge the table level options from either side of the join. */
5030  if (fpinfo_i)
5031  {
5032  /*
5033  * We'll prefer to use remote estimates for this join if any table
5034  * from either side of the join is using remote estimates. This is
5035  * most likely going to be preferred since they're already willing to
5036  * pay the price of a round trip to get the remote EXPLAIN. In any
5037  * case it's not entirely clear how we might otherwise handle this
5038  * best.
5039  */
5040  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
5041  fpinfo_i->use_remote_estimate;
5042 
5043  /*
5044  * Set fetch size to maximum of the joining sides, since we are
5045  * expecting the rows returned by the join to be proportional to the
5046  * relation sizes.
5047  */
5048  fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
5049  }
5050 }
ForeignServer * server
Definition: postgres_fdw.h:76
#define Max(x, y)
Definition: c.h:851
#define Assert(condition)
Definition: c.h:699
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 459 of file postgres_fdw.c.

References FdwRoutine::AddForeignUpdateTargets, FdwRoutine::AnalyzeForeignTable, FdwRoutine::BeginDirectModify, FdwRoutine::BeginForeignInsert, FdwRoutine::BeginForeignModify, FdwRoutine::BeginForeignScan, FdwRoutine::EndDirectModify, FdwRoutine::EndForeignInsert, 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(), postgresBeginForeignInsert(), postgresBeginForeignModify(), postgresBeginForeignScan(), postgresEndDirectModify(), postgresEndForeignInsert(), 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.

460 {
461  FdwRoutine *routine = makeNode(FdwRoutine);
462 
463  /* Functions for scanning foreign tables */
471 
472  /* Functions for updating foreign tables */
487 
488  /* Function for EvalPlanQual rechecks */
490  /* Support functions for EXPLAIN */
494 
495  /* Support functions for ANALYZE */
497 
498  /* Support functions for IMPORT FOREIGN SCHEMA */
500 
501  /* Support functions for join push-down */
503 
504  /* Support functions for upper relation push-down */
506 
507  PG_RETURN_POINTER(routine);
508 }
GetForeignPlan_function GetForeignPlan
Definition: fdwapi.h:189
BeginForeignScan_function BeginForeignScan
Definition: fdwapi.h:190
GetForeignUpperPaths_function GetForeignUpperPaths
Definition: fdwapi.h:204
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:212
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:326
EndDirectModify_function EndDirectModify
Definition: fdwapi.h:220
static void postgresExplainForeignModify(ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
static void postgresBeginForeignInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static List * postgresPlanForeignModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
ExplainForeignScan_function ExplainForeignScan
Definition: fdwapi.h:228
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:233
BeginForeignInsert_function BeginForeignInsert
Definition: fdwapi.h:214
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:210
static void postgresBeginForeignScan(ForeignScanState *node, int eflags)
static void postgresGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
Definition: postgres_fdw.c:518
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:207
static void postgresEndDirectModify(ForeignScanState *node)
static void postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra)
RecheckForeignScan_function RecheckForeignScan
Definition: fdwapi.h:225
IterateDirectModify_function IterateDirectModify
Definition: fdwapi.h:219
static void postgresEndForeignScan(ForeignScanState *node)
GetForeignJoinPaths_function GetForeignJoinPaths
Definition: fdwapi.h:201
static List * postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
static void postgresBeginDirectModify(ForeignScanState *node, int eflags)
GetForeignRelSize_function GetForeignRelSize
Definition: fdwapi.h:187
EndForeignScan_function EndForeignScan
Definition: fdwapi.h:193
ExplainDirectModify_function ExplainDirectModify
Definition: fdwapi.h:230
ImportForeignSchema_function ImportForeignSchema
Definition: fdwapi.h:236
PlanForeignModify_function PlanForeignModify
Definition: fdwapi.h:208
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:213
GetForeignPaths_function GetForeignPaths
Definition: fdwapi.h:188
static bool postgresPlanDirectModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
static TupleTableSlot * postgresIterateForeignScan(ForeignScanState *node)
PlanDirectModify_function PlanDirectModify
Definition: fdwapi.h:217
static void postgresGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
Definition: postgres_fdw.c:917
BeginDirectModify_function BeginDirectModify
Definition: fdwapi.h:218
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:211
static TupleTableSlot * postgresExecForeignDelete(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
#define makeNode(_type_)
Definition: nodes.h:565
ReScanForeignScan_function ReScanForeignScan
Definition: fdwapi.h:192
IterateForeignScan_function IterateForeignScan
Definition: fdwapi.h:191
static bool postgresRecheckForeignScan(ForeignScanState *node, TupleTableSlot *slot)
static void postgresExplainForeignScan(ForeignScanState *node, ExplainState *es)
static void postgresEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo)
static void postgresBeginForeignModify(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
ExplainForeignModify_function ExplainForeignModify
Definition: fdwapi.h:229
static void postgresAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation)
static void postgresEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo)
IsForeignRelUpdatable_function IsForeignRelUpdatable
Definition: fdwapi.h:216
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:209
static void postgresReScanForeignScan(ForeignScanState *node)
EndForeignInsert_function EndForeignInsert
Definition: fdwapi.h:215

◆ postgresAcquireSampleRowsFunc()

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

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

4137 {
4138  PgFdwAnalyzeState astate;
4139  ForeignTable *table;
4140  ForeignServer *server;
4141  UserMapping *user;
4142  PGconn *conn;
4143  unsigned int cursor_number;
4144  StringInfoData sql;
4145  PGresult *volatile res = NULL;
4146 
4147  /* Initialize workspace state */
4148  astate.rel = relation;
4150 
4151  astate.rows = rows;
4152  astate.targrows = targrows;
4153  astate.numrows = 0;
4154  astate.samplerows = 0;
4155  astate.rowstoskip = -1; /* -1 means not set yet */
4156  reservoir_init_selection_state(&astate.rstate, targrows);
4157 
4158  /* Remember ANALYZE context, and create a per-tuple temp context */
4159  astate.anl_cxt = CurrentMemoryContext;
4161  "postgres_fdw temporary data",
4163 
4164  /*
4165  * Get the connection to use. We do the remote access as the table's
4166  * owner, even if the ANALYZE was started by some other user.
4167  */
4168  table = GetForeignTable(RelationGetRelid(relation));
4169  server = GetForeignServer(table->serverid);
4170  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4171  conn = GetConnection(user, false);
4172 
4173  /*
4174  * Construct cursor that retrieves whole rows from remote.
4175  */
4176  cursor_number = GetCursorNumber(conn);
4177  initStringInfo(&sql);
4178  appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
4179  deparseAnalyzeSql(&sql, relation, &astate.retrieved_attrs);
4180 
4181  /* In what follows, do not risk leaking any PGresults. */
4182  PG_TRY();
4183  {
4184  res = pgfdw_exec_query(conn, sql.data);
4185  if (PQresultStatus(res) != PGRES_COMMAND_OK)
4186  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4187  PQclear(res);
4188  res = NULL;
4189 
4190  /* Retrieve and process rows a batch at a time. */
4191  for (;;)
4192  {
4193  char fetch_sql[64];
4194  int fetch_size;
4195  int numrows;
4196  int i;
4197  ListCell *lc;
4198 
4199  /* Allow users to cancel long query */
4201 
4202  /*
4203  * XXX possible future improvement: if rowstoskip is large, we
4204  * could issue a MOVE rather than physically fetching the rows,
4205  * then just adjust rowstoskip and samplerows appropriately.
4206  */
4207 
4208  /* The fetch size is arbitrary, but shouldn't be enormous. */
4209  fetch_size = 100;
4210  foreach(lc, server->options)
4211  {
4212  DefElem *def = (DefElem *) lfirst(lc);
4213 
4214  if (strcmp(def->defname, "fetch_size") == 0)
4215  {
4216  fetch_size = strtol(defGetString(def), NULL, 10);
4217  break;
4218  }
4219  }
4220  foreach(lc, table->options)
4221  {
4222  DefElem *def = (DefElem *) lfirst(lc);
4223 
4224  if (strcmp(def->defname, "fetch_size") == 0)
4225  {
4226  fetch_size = strtol(defGetString(def), NULL, 10);
4227  break;
4228  }
4229  }
4230 
4231  /* Fetch some rows */
4232  snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
4233  fetch_size, cursor_number);
4234 
4235  res = pgfdw_exec_query(conn, fetch_sql);
4236  /* On error, report the original query, not the FETCH. */
4237  if (PQresultStatus(res) != PGRES_TUPLES_OK)
4238  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4239 
4240  /* Process whatever we got. */
4241  numrows = PQntuples(res);
4242  for (i = 0; i < numrows; i++)
4243  analyze_row_processor(res, i, &astate);
4244 
4245  PQclear(res);
4246  res = NULL;
4247 
4248  /* Must be EOF if we didn't get all the rows requested. */
4249  if (numrows < fetch_size)
4250  break;
4251  }
4252 
4253  /* Close the cursor, just to be tidy. */
4254  close_cursor(conn, cursor_number);
4255  }
4256  PG_CATCH();
4257  {
4258  if (res)
4259  PQclear(res);
4260  PG_RE_THROW();
4261  }
4262  PG_END_TRY();
4263 
4264  ReleaseConnection(conn);
4265 
4266  /* We assume that we have no dead tuple. */
4267  *totaldeadrows = 0.0;
4268 
4269  /* We've retrieved all living tuples from foreign server. */
4270  *totalrows = astate.samplerows;
4271 
4272  /*
4273  * Emit some interesting relation info
4274  */
4275  ereport(elevel,
4276  (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
4277  RelationGetRelationName(relation),
4278  astate.samplerows, astate.numrows)));
4279 
4280  return astate.numrows;
4281 }
HeapTuple * rows
Definition: postgres_fdw.c:233
#define RelationGetDescr(relation)
Definition: rel.h:433
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:202
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:84
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:55
char * defGetString(DefElem *def)
Definition: define.c:49
#define RelationGetRelationName(relation)
Definition: rel.h:441
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
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:170
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
AttInMetadata * attinmeta
Definition: postgres_fdw.c:229
static int elevel
Definition: vacuumlazy.c:144
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:93
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1146
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:730
#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:407
#define PG_END_TRY()
Definition: elog.h:300

◆ postgresAddForeignUpdateTargets()

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

Definition at line 1537 of file postgres_fdw.c.

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

Referenced by postgres_fdw_handler().

1540 {
1541  Var *var;
1542  const char *attrname;
1543  TargetEntry *tle;
1544 
1545  /*
1546  * In postgres_fdw, what we need is the ctid, same as for a regular table.
1547  */
1548 
1549  /* Make a Var representing the desired value */
1550  var = makeVar(parsetree->resultRelation,
1552  TIDOID,
1553  -1,
1554  InvalidOid,
1555  0);
1556 
1557  /* Wrap it in a resjunk TLE with the right name ... */
1558  attrname = "ctid";
1559 
1560  tle = makeTargetEntry((Expr *) var,
1561  list_length(parsetree->targetList) + 1,
1562  pstrdup(attrname),
1563  true);
1564 
1565  /* ... and add it to the query's targetlist */
1566  parsetree->targetList = lappend(parsetree->targetList, tle);
1567 }
char * pstrdup(const char *in)
Definition: mcxt.c:1161
int resultRelation
Definition: parsenodes.h:122
Definition: primnodes.h:164
List * targetList
Definition: parsenodes.h:140
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 4055 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().

4058 {
4059  ForeignTable *table;
4060  UserMapping *user;
4061  PGconn *conn;
4062  StringInfoData sql;
4063  PGresult *volatile res = NULL;
4064 
4065  /* Return the row-analysis function pointer */
4067 
4068  /*
4069  * Now we have to get the number of pages. It's annoying that the ANALYZE
4070  * API requires us to return that now, because it forces some duplication
4071  * of effort between this routine and postgresAcquireSampleRowsFunc. But
4072  * it's probably not worth redefining that API at this point.
4073  */
4074 
4075  /*
4076  * Get the connection to use. We do the remote access as the table's
4077  * owner, even if the ANALYZE was started by some other user.
4078  */
4079  table = GetForeignTable(RelationGetRelid(relation));
4080  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4081  conn = GetConnection(user, false);
4082 
4083  /*
4084  * Construct command to get page count for relation.
4085  */
4086  initStringInfo(&sql);
4087  deparseAnalyzeSizeSql(&sql, relation);
4088 
4089  /* In what follows, do not risk leaking any PGresults. */
4090  PG_TRY();
4091  {
4092  res = pgfdw_exec_query(conn, sql.data);
4093  if (PQresultStatus(res) != PGRES_TUPLES_OK)
4094  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4095 
4096  if (PQntuples(res) != 1 || PQnfields(res) != 1)
4097  elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
4098  *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
4099 
4100  PQclear(res);
4101  res = NULL;
4102  }
4103  PG_CATCH();
4104  {
4105  if (res)
4106  PQclear(res);
4107  PG_RE_THROW();
4108  }
4109  PG_END_TRY();
4110 
4111  ReleaseConnection(conn);
4112 
4113  return true;
4114 }
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:84
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:55
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:407
#define PG_END_TRY()
Definition: elog.h:300

◆ postgresBeginDirectModify()

static void postgresBeginDirectModify ( ForeignScanState node,
int  eflags 
)
static

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

2336 {
2337  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
2338  EState *estate = node->ss.ps.state;
2339  PgFdwDirectModifyState *dmstate;
2340  Index rtindex;
2341  RangeTblEntry *rte;
2342  Oid userid;
2343  ForeignTable *table;
2344  UserMapping *user;
2345  int numParams;
2346 
2347  /*
2348  * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
2349  */
2350  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2351  return;
2352 
2353  /*
2354  * We'll save private state in node->fdw_state.
2355  */
2356  dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
2357  node->fdw_state = (void *) dmstate;
2358 
2359  /*
2360  * Identify which user to do the remote access as. This should match what
2361  * ExecCheckRTEPerms() does.
2362  */
2363  rtindex = estate->es_result_relation_info->ri_RangeTableIndex;
2364  rte = rt_fetch(rtindex, estate->es_range_table);
2365  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
2366 
2367  /* Get info about foreign table. */
2368  if (fsplan->scan.scanrelid == 0)
2369  dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags);
2370  else
2371  dmstate->rel = node->ss.ss_currentRelation;
2372  table = GetForeignTable(RelationGetRelid(dmstate->rel));
2373  user = GetUserMapping(userid, table->serverid);
2374 
2375  /*
2376  * Get connection to the foreign server. Connection manager will
2377  * establish new connection if necessary.
2378  */
2379  dmstate->conn = GetConnection(user, false);
2380 
2381  /* Update the foreign-join-related fields. */
2382  if (fsplan->scan.scanrelid == 0)
2383  {
2384  /* Save info about foreign table. */
2385  dmstate->resultRel = dmstate->rel;
2386 
2387  /*
2388  * Set dmstate->rel to NULL to teach get_returning_data() and
2389  * make_tuple_from_result_row() that columns fetched from the remote
2390  * server are described by fdw_scan_tlist of the foreign-scan plan
2391  * node, not the tuple descriptor for the target relation.
2392  */
2393  dmstate->rel = NULL;
2394  }
2395 
2396  /* Initialize state variable */
2397  dmstate->num_tuples = -1; /* -1 means not set yet */
2398 
2399  /* Get private info created by planner functions. */
2400  dmstate->query = strVal(list_nth(fsplan->fdw_private,
2402  dmstate->has_returning = intVal(list_nth(fsplan->fdw_private,
2404  dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
2406  dmstate->set_processed = intVal(list_nth(fsplan->fdw_private,
2408 
2409  /* Create context for per-tuple temp workspace. */
2410  dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
2411  "postgres_fdw temporary data",
2413 
2414  /* Prepare for input conversion of RETURNING results. */
2415  if (dmstate->has_returning)
2416  {
2417  TupleDesc tupdesc;
2418 
2419  if (fsplan->scan.scanrelid == 0)
2420  tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2421  else
2422  tupdesc = RelationGetDescr(dmstate->rel);
2423 
2424  dmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
2425 
2426  /*
2427  * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2428  * initialize a filter to extract an updated/deleted tuple from a scan
2429  * tuple.
2430  */
2431  if (fsplan->scan.scanrelid == 0)
2432  init_returning_filter(dmstate, fsplan->fdw_scan_tlist, rtindex);
2433  }
2434 
2435  /*
2436  * Prepare for processing of parameters used in remote query, if any.
2437  */
2438  numParams = list_length(fsplan->fdw_exprs);
2439  dmstate->numParams = numParams;
2440  if (numParams > 0)
2442  fsplan->fdw_exprs,
2443  numParams,
2444  &dmstate->param_flinfo,
2445  &dmstate->param_exprs,
2446  &dmstate->param_values);
2447 }
ScanState ss
Definition: execnodes.h:1641
Index scanrelid
Definition: plannodes.h:343
#define RelationGetDescr(relation)
Definition: rel.h:433
Oid GetUserId(void)
Definition: miscinit.c:379
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:202
List * fdw_exprs
Definition: plannodes.h:614
List * fdw_private
Definition: plannodes.h:615
#define strVal(v)
Definition: value.h:54
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
List * fdw_scan_tlist
Definition: plannodes.h:616
Relation ss_currentRelation
Definition: execnodes.h:1193
EState * state
Definition: execnodes.h:914
unsigned int Oid
Definition: postgres_ext.h:31
PlanState ps
Definition: execnodes.h:1192
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:643
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:170
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
void * palloc0(Size size)
Definition: mcxt.c:955
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1146
unsigned int Index
Definition: c.h:442
Plan * plan
Definition: execnodes.h:912
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:58
#define RelationGetRelid(relation)
Definition: rel.h:407

◆ postgresBeginForeignInsert()

static void postgresBeginForeignInsert ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 1973 of file postgres_fdw.c.

References attnum, CMD_INSERT, Query::commandType, create_foreign_modify(), StringInfoData::data, deparseInsertSql(), elog, ERROR, initStringInfo(), lappend_int(), list_make1, makeNode, tupleDesc::natts, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, PlannerInfo::parse, PlanState::plan, ModifyTableState::ps, PgFdwScanState::query, PgFdwScanState::rel, RelationGetDescr, RelationGetRelid, RangeTblEntry::relid, RangeTblEntry::relkind, Query::resultRelation, PgFdwScanState::retrieved_attrs, ResultRelInfo::ri_FdwState, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_returningList, Query::rtable, RTE_RELATION, RangeTblEntry::rtekind, PlanState::state, PgFdwScanState::tupdesc, and TupleDescAttr.

Referenced by postgres_fdw_handler().

1975 {
1976  PgFdwModifyState *fmstate;
1977  Plan *plan = mtstate->ps.plan;
1978  Relation rel = resultRelInfo->ri_RelationDesc;
1979  RangeTblEntry *rte;
1980  Query *query;
1981  PlannerInfo *root;
1982  TupleDesc tupdesc = RelationGetDescr(rel);
1983  int attnum;
1984  StringInfoData sql;
1985  List *targetAttrs = NIL;
1986  List *retrieved_attrs = NIL;
1987  bool doNothing = false;
1988 
1989  initStringInfo(&sql);
1990 
1991  /* Set up largely-dummy planner state. */
1992  rte = makeNode(RangeTblEntry);
1993  rte->rtekind = RTE_RELATION;
1994  rte->relid = RelationGetRelid(rel);
1995  rte->relkind = RELKIND_FOREIGN_TABLE;
1996  query = makeNode(Query);
1997  query->commandType = CMD_INSERT;
1998  query->resultRelation = 1;
1999  query->rtable = list_make1(rte);
2000  root = makeNode(PlannerInfo);
2001  root->parse = query;
2002 
2003  /* We transmit all columns that are defined in the foreign table. */
2004  for (attnum = 1; attnum <= tupdesc->natts; attnum++)
2005  {
2006  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2007 
2008  if (!attr->attisdropped)
2009  targetAttrs = lappend_int(targetAttrs, attnum);
2010  }
2011 
2012  /* Check if we add the ON CONFLICT clause to the remote query. */
2013  if (plan)
2014  {
2015  OnConflictAction onConflictAction = ((ModifyTable *) plan)->onConflictAction;
2016 
2017  /* We only support DO NOTHING without an inference specification. */
2018  if (onConflictAction == ONCONFLICT_NOTHING)
2019  doNothing = true;
2020  else if (onConflictAction != ONCONFLICT_NONE)
2021  elog(ERROR, "unexpected ON CONFLICT specification: %d",
2022  (int) onConflictAction);
2023  }
2024 
2025  /* Construct the SQL command string. */
2026  deparseInsertSql(&sql, root, 1, rel, targetAttrs, doNothing,
2027  resultRelInfo->ri_returningList, &retrieved_attrs);
2028 
2029  /* Construct an execution state. */
2030  fmstate = create_foreign_modify(mtstate->ps.state,
2031  resultRelInfo,
2032  CMD_INSERT,
2033  NULL,
2034  sql.data,
2035  targetAttrs,
2036  retrieved_attrs != NIL,
2037  retrieved_attrs);
2038 
2039  resultRelInfo->ri_FdwState = fmstate;
2040 }
#define NIL
Definition: pg_list.h:69
Relation ri_RelationDesc
Definition: execnodes.h:397
Query * parse
Definition: relation.h:169
#define RelationGetDescr(relation)
Definition: rel.h:433
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
int resultRelation
Definition: parsenodes.h:122
EState * state
Definition: execnodes.h:914
int natts
Definition: tupdesc.h:82