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, RangeTblEntry *rte, 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 5461 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().

5464 {
5465  Query *parse = root->parse;
5466  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
5467  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
5468  ForeignPath *grouppath;
5469  double rows;
5470  int width;
5471  Cost startup_cost;
5472  Cost total_cost;
5473 
5474  /* Nothing to be done, if there is no grouping or aggregation required. */
5475  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
5476  !root->hasHavingQual)
5477  return;
5478 
5481 
5482  /* save the input_rel as outerrel in fpinfo */
5483  fpinfo->outerrel = input_rel;
5484 
5485  /*
5486  * Copy foreign table, foreign server, user mapping, FDW options etc.
5487  * details from the input relation's fpinfo.
5488  */
5489  fpinfo->table = ifpinfo->table;
5490  fpinfo->server = ifpinfo->server;
5491  fpinfo->user = ifpinfo->user;
5492  merge_fdw_options(fpinfo, ifpinfo, NULL);
5493 
5494  /*
5495  * Assess if it is safe to push down aggregation and grouping.
5496  *
5497  * Use HAVING qual from extra. In case of child partition, it will have
5498  * translated Vars.
5499  */
5500  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
5501  return;
5502 
5503  /* Estimate the cost of push down */
5504  estimate_path_cost_size(root, grouped_rel, NIL, NIL, &rows,
5505  &width, &startup_cost, &total_cost);
5506 
5507  /* Now update this information in the fpinfo */
5508  fpinfo->rows = rows;
5509  fpinfo->width = width;
5510  fpinfo->startup_cost = startup_cost;
5511  fpinfo->total_cost = total_cost;
5512 
5513  /* Create and add foreign path to the grouping relation. */
5514  grouppath = create_foreignscan_path(root,
5515  grouped_rel,
5516  grouped_rel->reltarget,
5517  rows,
5518  startup_cost,
5519  total_cost,
5520  NIL, /* no pathkeys */
5521  NULL, /* no required_outer */
5522  NULL,
5523  NIL); /* no fdw_private */
5524 
5525  /* Add generated path into grouped_rel by add_path(). */
5526  add_path(grouped_rel, (Path *) grouppath);
5527 }
#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:647
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 4922 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().

4924 {
4925  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
4926  ListCell *lc;
4927 
4928  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
4929 
4930  /* Create one path for each set of pathkeys we found above. */
4931  foreach(lc, useful_pathkeys_list)
4932  {
4933  double rows;
4934  int width;
4935  Cost startup_cost;
4936  Cost total_cost;
4937  List *useful_pathkeys = lfirst(lc);
4938  Path *sorted_epq_path;
4939 
4940  estimate_path_cost_size(root, rel, NIL, useful_pathkeys,
4941  &rows, &width, &startup_cost, &total_cost);
4942 
4943  /*
4944  * The EPQ path must be at least as well sorted as the path itself, in
4945  * case it gets used as input to a mergejoin.
4946  */
4947  sorted_epq_path = epq_path;
4948  if (sorted_epq_path != NULL &&
4949  !pathkeys_contained_in(useful_pathkeys,
4950  sorted_epq_path->pathkeys))
4951  sorted_epq_path = (Path *)
4952  create_sort_path(root,
4953  rel,
4954  sorted_epq_path,
4955  useful_pathkeys,
4956  -1.0);
4957 
4958  add_path(rel, (Path *)
4959  create_foreignscan_path(root, rel,
4960  NULL,
4961  rows,
4962  startup_cost,
4963  total_cost,
4964  useful_pathkeys,
4965  NULL,
4966  sorted_epq_path,
4967  NIL));
4968  }
4969 }
#define NIL
Definition: pg_list.h:69
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:811
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:647

◆ analyze_row_processor()

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

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

4313 {
4314  int targrows = astate->targrows;
4315  int pos; /* array index to store tuple in */
4316  MemoryContext oldcontext;
4317 
4318  /* Always increment sample row counter. */
4319  astate->samplerows += 1;
4320 
4321  /*
4322  * Determine the slot where this sample row should be stored. Set pos to
4323  * negative value to indicate the row should be skipped.
4324  */
4325  if (astate->numrows < targrows)
4326  {
4327  /* First targrows rows are always included into the sample */
4328  pos = astate->numrows++;
4329  }
4330  else
4331  {
4332  /*
4333  * Now we start replacing tuples in the sample until we reach the end
4334  * of the relation. Same algorithm as in acquire_sample_rows in
4335  * analyze.c; see Jeff Vitter's paper.
4336  */
4337  if (astate->rowstoskip < 0)
4338  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
4339 
4340  if (astate->rowstoskip <= 0)
4341  {
4342  /* Choose a random reservoir element to replace. */
4343  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
4344  Assert(pos >= 0 && pos < targrows);
4345  heap_freetuple(astate->rows[pos]);
4346  }
4347  else
4348  {
4349  /* Skip this tuple. */
4350  pos = -1;
4351  }
4352 
4353  astate->rowstoskip -= 1;
4354  }
4355 
4356  if (pos >= 0)
4357  {
4358  /*
4359  * Create sample tuple from current result row, and store it in the
4360  * position determined above. The tuple has to be created in anl_cxt.
4361  */
4362  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
4363 
4364  astate->rows[pos] = make_tuple_from_result_row(res, row,
4365  astate->rel,
4366  astate->attinmeta,
4367  astate->retrieved_attrs,
4368  NULL,
4369  astate->temp_cxt);
4370 
4371  MemoryContextSwitchTo(oldcontext);
4372  }
4373 }
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 3885 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().

3888 {
3889  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
3890  TupleTableSlot *resultSlot;
3891  Datum *values;
3892  bool *isnull;
3893  Datum *old_values;
3894  bool *old_isnull;
3895  int i;
3896 
3897  /*
3898  * Use the trigger tuple slot as a place to store the result tuple.
3899  */
3900  resultSlot = estate->es_trig_tuple_slot;
3901  if (resultSlot->tts_tupleDescriptor != resultTupType)
3902  ExecSetSlotDescriptor(resultSlot, resultTupType);
3903 
3904  /*
3905  * Extract all the values of the scan tuple.
3906  */
3907  slot_getallattrs(slot);
3908  old_values = slot->tts_values;
3909  old_isnull = slot->tts_isnull;
3910 
3911  /*
3912  * Prepare to build the result tuple.
3913  */
3914  ExecClearTuple(resultSlot);
3915  values = resultSlot->tts_values;
3916  isnull = resultSlot->tts_isnull;
3917 
3918  /*
3919  * Transpose data into proper fields of the result tuple.
3920  */
3921  for (i = 0; i < resultTupType->natts; i++)
3922  {
3923  int j = dmstate->attnoMap[i];
3924 
3925  if (j == 0)
3926  {
3927  values[i] = (Datum) 0;
3928  isnull[i] = true;
3929  }
3930  else
3931  {
3932  values[i] = old_values[j - 1];
3933  isnull[i] = old_isnull[j - 1];
3934  }
3935  }
3936 
3937  /*
3938  * Build the virtual tuple.
3939  */
3940  ExecStoreVirtualTuple(resultSlot);
3941 
3942  /*
3943  * If we have any system columns to return, install them.
3944  */
3945  if (dmstate->hasSystemCols)
3946  {
3947  HeapTuple resultTup = ExecMaterializeSlot(resultSlot);
3948 
3949  /* ctid */
3950  if (dmstate->ctidAttno)
3951  {
3952  ItemPointer ctid = NULL;
3953 
3954  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
3955  resultTup->t_self = *ctid;
3956  }
3957 
3958  /* oid */
3959  if (dmstate->oidAttno)
3960  {
3961  Oid oid = InvalidOid;
3962 
3963  oid = DatumGetObjectId(old_values[dmstate->oidAttno - 1]);
3964  HeapTupleSetOid(resultTup, oid);
3965  }
3966 
3967  /*
3968  * And remaining columns
3969  *
3970  * Note: since we currently don't allow the target relation to appear
3971  * on the nullable side of an outer join, any system columns wouldn't
3972  * go to NULL.
3973  *
3974  * Note: no need to care about tableoid here because it will be
3975  * initialized in ExecProcessReturning().
3976  */
3980  }
3981 
3982  /*
3983  * And return the result tuple.
3984  */
3985  return resultSlot;
3986 }
#define RelationGetDescr(relation)
Definition: rel.h:433
#define DatumGetObjectId(X)
Definition: postgres.h:485
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:710
ItemPointerData t_self
Definition: htup.h:65
bool * tts_isnull
Definition: tuptable.h:132
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:379
#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:367
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:534
static Datum values[MAXATTR]
Definition: bootstrap.c:164
int i
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:396
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:524
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:318

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

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

4978 {
4979  ListCell *lc;
4980 
4981  foreach(lc, fpinfo->server->options)
4982  {
4983  DefElem *def = (DefElem *) lfirst(lc);
4984 
4985  if (strcmp(def->defname, "use_remote_estimate") == 0)
4986  fpinfo->use_remote_estimate = defGetBoolean(def);
4987  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
4988  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
4989  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
4990  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
4991  else if (strcmp(def->defname, "extensions") == 0)
4992  fpinfo->shippable_extensions =
4993  ExtractExtensionList(defGetString(def), false);
4994  else if (strcmp(def->defname, "fetch_size") == 0)
4995  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
4996  }
4997 }
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 5005 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().

5006 {
5007  ListCell *lc;
5008 
5009  foreach(lc, fpinfo->table->options)
5010  {
5011  DefElem *def = (DefElem *) lfirst(lc);
5012 
5013  if (strcmp(def->defname, "use_remote_estimate") == 0)
5014  fpinfo->use_remote_estimate = defGetBoolean(def);
5015  else if (strcmp(def->defname, "fetch_size") == 0)
5016  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
5017  }
5018 }
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 3558 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().

3559 {
3560  bool have_wholerow = false;
3561  List *tlist = NIL;
3562  List *vars;
3563  ListCell *lc;
3564 
3565  Assert(returningList);
3566 
3567  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
3568 
3569  /*
3570  * If there's a whole-row reference to the target relation, then we'll
3571  * need all the columns of the relation.
3572  */
3573  foreach(lc, vars)
3574  {
3575  Var *var = (Var *) lfirst(lc);
3576 
3577  if (IsA(var, Var) &&
3578  var->varno == rtindex &&
3579  var->varattno == InvalidAttrNumber)
3580  {
3581  have_wholerow = true;
3582  break;
3583  }
3584  }
3585 
3586  if (have_wholerow)
3587  {
3588  TupleDesc tupdesc = RelationGetDescr(rel);
3589  int i;
3590 
3591  for (i = 1; i <= tupdesc->natts; i++)
3592  {
3593  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
3594  Var *var;
3595 
3596  /* Ignore dropped attributes. */
3597  if (attr->attisdropped)
3598  continue;
3599 
3600  var = makeVar(rtindex,
3601  i,
3602  attr->atttypid,
3603  attr->atttypmod,
3604  attr->attcollation,
3605  0);
3606 
3607  tlist = lappend(tlist,
3608  makeTargetEntry((Expr *) var,
3609  list_length(tlist) + 1,
3610  NULL,
3611  false));
3612  }
3613  }
3614 
3615  /* Now add any remaining columns to tlist. */
3616  foreach(lc, vars)
3617  {
3618  Var *var = (Var *) lfirst(lc);
3619 
3620  /*
3621  * No need for whole-row references to the target relation. We don't
3622  * need system columns other than ctid and oid either, since those are
3623  * set locally.
3624  */
3625  if (IsA(var, Var) &&
3626  var->varno == rtindex &&
3627  var->varattno <= InvalidAttrNumber &&
3630  continue; /* don't need it */
3631 
3632  if (tlist_member((Expr *) var, tlist))
3633  continue; /* already got it */
3634 
3635  tlist = lappend(tlist,
3636  makeTargetEntry((Expr *) var,
3637  list_length(tlist) + 1,
3638  NULL,
3639  false));
3640  }
3641 
3642  list_free(vars);
3643 
3644  return tlist;
3645 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:567
#define RelationGetDescr(relation)
Definition: rel.h:433
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
Definition: nodes.h:516
AttrNumber varattno
Definition: primnodes.h:168
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
Definition: primnodes.h:163
int natts
Definition: tupdesc.h: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:166
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:72
#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 3258 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().

3259 {
3260  char sql[64];
3261  PGresult *res;
3262 
3263  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3264 
3265  /*
3266  * We don't use a PG_TRY block here, so be careful not to throw error
3267  * without releasing the PGresult.
3268  */
3269  res = pgfdw_exec_query(conn, sql);
3270  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3271  pgfdw_report_error(ERROR, res, conn, true, sql);
3272  PQclear(res);
3273 }
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 5705 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().

5706 {
5707  const char *attname = NULL;
5708  const char *relname = NULL;
5709  bool is_wholerow = false;
5711 
5712  if (errpos->rel)
5713  {
5714  /* error occurred in a scan against a foreign table */
5715  TupleDesc tupdesc = RelationGetDescr(errpos->rel);
5716  Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
5717 
5718  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
5719  attname = NameStr(attr->attname);
5720  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
5721  attname = "ctid";
5722  else if (errpos->cur_attno == ObjectIdAttributeNumber)
5723  attname = "oid";
5724 
5725  relname = RelationGetRelationName(errpos->rel);
5726  }
5727  else
5728  {
5729  /* error occurred in a scan against a foreign join */
5730  ForeignScanState *fsstate = errpos->fsstate;
5731  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
5732  EState *estate = fsstate->ss.ps.state;
5733  TargetEntry *tle;
5734 
5735  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
5736  errpos->cur_attno - 1);
5737 
5738  /*
5739  * Target list can have Vars and expressions. For Vars, we can get
5740  * its relation, however for expressions we can't. Thus for
5741  * expressions, just show generic context message.
5742  */
5743  if (IsA(tle->expr, Var))
5744  {
5745  RangeTblEntry *rte;
5746  Var *var = (Var *) tle->expr;
5747 
5748  rte = rt_fetch(var->varno, estate->es_range_table);
5749 
5750  if (var->varattno == 0)
5751  is_wholerow = true;
5752  else
5753  attname = get_attname(rte->relid, var->varattno, false);
5754 
5755  relname = get_rel_name(rte->relid);
5756  }
5757  else
5758  errcontext("processing expression at position %d in select list",
5759  errpos->cur_attno);
5760  }
5761 
5762  if (relname)
5763  {
5764  if (is_wholerow)
5765  errcontext("whole-row reference to foreign table \"%s\"", relname);
5766  else if (attname)
5767  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
5768  }
5769 }
ScanState ss
Definition: execnodes.h:1641
#define IsA(nodeptr, _type_)
Definition: nodes.h:567
#define RelationGetDescr(relation)
Definition: rel.h:433
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define castNode(_type_, nodeptr)
Definition: nodes.h:585
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
AttrNumber varattno
Definition: primnodes.h:168
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:163
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:166
Plan * plan
Definition: execnodes.h:912
Expr * expr
Definition: primnodes.h:1375
#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 3432 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().

3435 {
3436  const char **p_values;
3437  int pindex = 0;
3438  MemoryContext oldcontext;
3439 
3440  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
3441 
3442  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums);
3443 
3444  /* 1st parameter should be ctid, if it's in use */
3445  if (tupleid != NULL)
3446  {
3447  /* don't need set_transmission_modes for TID output */
3448  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3449  PointerGetDatum(tupleid));
3450  pindex++;
3451  }
3452 
3453  /* get following parameters from slot */
3454  if (slot != NULL && fmstate->target_attrs != NIL)
3455  {
3456  int nestlevel;
3457  ListCell *lc;
3458 
3459  nestlevel = set_transmission_modes();
3460 
3461  foreach(lc, fmstate->target_attrs)
3462  {
3463  int attnum = lfirst_int(lc);
3464  Datum value;
3465  bool isnull;
3466 
3467  value = slot_getattr(slot, attnum, &isnull);
3468  if (isnull)
3469  p_values[pindex] = NULL;
3470  else
3471  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3472  value);
3473  pindex++;
3474  }
3475 
3476  reset_transmission_modes(nestlevel);
3477  }
3478 
3479  Assert(pindex == fmstate->p_nums);
3480 
3481  MemoryContextSwitchTo(oldcontext);
3482 
3483  return p_values;
3484 }
#define NIL
Definition: pg_list.h:69
#define PointerGetDatum(X)
Definition: postgres.h:541
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:367
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 3062 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().

3063 {
3064  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3065  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3066  int numParams = fsstate->numParams;
3067  const char **values = fsstate->param_values;
3068  PGconn *conn = fsstate->conn;
3070  PGresult *res;
3071 
3072  /*
3073  * Construct array of query parameter values in text format. We do the
3074  * conversions in the short-lived per-tuple context, so as not to cause a
3075  * memory leak over repeated scans.
3076  */
3077  if (numParams > 0)
3078  {
3079  MemoryContext oldcontext;
3080 
3081  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3082 
3083  process_query_params(econtext,
3084  fsstate->param_flinfo,
3085  fsstate->param_exprs,
3086  values);
3087 
3088  MemoryContextSwitchTo(oldcontext);
3089  }
3090 
3091  /* Construct the DECLARE CURSOR command */
3092  initStringInfo(&buf);
3093  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3094  fsstate->cursor_number, fsstate->query);
3095 
3096  /*
3097  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3098  * to infer types for all parameters. Since we explicitly cast every
3099  * parameter (see deparse.c), the "inference" is trivial and will produce
3100  * the desired result. This allows us to avoid assuming that the remote
3101  * server has the same OIDs we do for the parameters' types.
3102  */
3103  if (!PQsendQueryParams(conn, buf.data, numParams,
3104  NULL, values, NULL, NULL, 0))
3105  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3106 
3107  /*
3108  * Get the result, and check for success.
3109  *
3110  * We don't use a PG_TRY block here, so be careful not to throw error
3111  * without releasing the PGresult.
3112  */
3113  res = pgfdw_get_result(conn, buf.data);
3114  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3115  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3116  PQclear(res);
3117 
3118  /* Mark the cursor as created, and show no tuples have been retrieved */
3119  fsstate->cursor_exists = true;
3120  fsstate->tuples = NULL;
3121  fsstate->num_tuples = 0;
3122  fsstate->next_tuple = 0;
3123  fsstate->fetch_ct_2 = 0;
3124  fsstate->eof_reached = false;
3125 
3126  /* Clean up */
3127  pfree(buf.data);
3128 }
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,
RangeTblEntry rte,
ResultRelInfo resultRelInfo,
CmdType  operation,
Plan subplan,
char *  query,
List target_attrs,
bool  has_returning,
List retrieved_attrs 
)
static

Definition at line 3281 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, 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_RelationDesc, ForeignTable::serverid, PgFdwModifyState::target_attrs, Plan::targetlist, PgFdwModifyState::temp_cxt, PgFdwScanState::tupdesc, TupleDescAttr, TupleDescGetAttInMetadata(), and user.

Referenced by postgresBeginForeignInsert(), and postgresBeginForeignModify().

3290 {
3291  PgFdwModifyState *fmstate;
3292  Relation rel = resultRelInfo->ri_RelationDesc;
3293  TupleDesc tupdesc = RelationGetDescr(rel);
3294  Oid userid;
3295  ForeignTable *table;
3296  UserMapping *user;
3297  AttrNumber n_params;
3298  Oid typefnoid;
3299  bool isvarlena;
3300  ListCell *lc;
3301 
3302  /* Begin constructing PgFdwModifyState. */
3303  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3304  fmstate->rel = rel;
3305 
3306  /*
3307  * Identify which user to do the remote access as. This should match what
3308  * ExecCheckRTEPerms() does.
3309  */
3310  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
3311 
3312  /* Get info about foreign table. */
3313  table = GetForeignTable(RelationGetRelid(rel));
3314  user = GetUserMapping(userid, table->serverid);
3315 
3316  /* Open connection; report that we'll create a prepared statement. */
3317  fmstate->conn = GetConnection(user, true);
3318  fmstate->p_name = NULL; /* prepared statement not made yet */
3319 
3320  /* Set up remote query information. */
3321  fmstate->query = query;
3322  fmstate->target_attrs = target_attrs;
3323  fmstate->has_returning = has_returning;
3324  fmstate->retrieved_attrs = retrieved_attrs;
3325 
3326  /* Create context for per-tuple temp workspace. */
3327  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
3328  "postgres_fdw temporary data",
3330 
3331  /* Prepare for input conversion of RETURNING results. */
3332  if (fmstate->has_returning)
3333  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
3334 
3335  /* Prepare for output conversion of parameters used in prepared stmt. */
3336  n_params = list_length(fmstate->target_attrs) + 1;
3337  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
3338  fmstate->p_nums = 0;
3339 
3340  if (operation == CMD_UPDATE || operation == CMD_DELETE)
3341  {
3342  Assert(subplan != NULL);
3343 
3344  /* Find the ctid resjunk column in the subplan's result */
3346  "ctid");
3347  if (!AttributeNumberIsValid(fmstate->ctidAttno))
3348  elog(ERROR, "could not find junk ctid column");
3349 
3350  /* First transmittable parameter will be ctid */
3351  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
3352  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3353  fmstate->p_nums++;
3354  }
3355 
3356  if (operation == CMD_INSERT || operation == CMD_UPDATE)
3357  {
3358  /* Set up for remaining transmittable parameters */
3359  foreach(lc, fmstate->target_attrs)
3360  {
3361  int attnum = lfirst_int(lc);
3362  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
3363 
3364  Assert(!attr->attisdropped);
3365 
3366  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
3367  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
3368  fmstate->p_nums++;
3369  }
3370  }
3371 
3372  Assert(fmstate->p_nums <= n_params);
3373 
3374  return fmstate;
3375 }
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
unsigned int Oid
Definition: postgres_ext.h:31
List * retrieved_attrs
Definition: postgres_fdw.c:177
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
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:147
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 3033 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().

3036 {
3038  Expr *expr = em->em_expr;
3039 
3040  /*
3041  * If we've identified what we're processing in the current scan, we only
3042  * want to match that expression.
3043  */
3044  if (state->current != NULL)
3045  return equal(expr, state->current);
3046 
3047  /*
3048  * Otherwise, ignore anything we've already processed.
3049  */
3050  if (list_member(state->already_used, expr))
3051  return false;
3052 
3053  /* This is the new target to process. */
3054  state->current = expr;
3055  return true;
3056 }
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 2631 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().

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

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

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

3687 {
3689  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3690  int numParams = dmstate->numParams;
3691  const char **values = dmstate->param_values;
3692 
3693  /*
3694  * Construct array of query parameter values in text format.
3695  */
3696  if (numParams > 0)
3697  process_query_params(econtext,
3698  dmstate->param_flinfo,
3699  dmstate->param_exprs,
3700  values);
3701 
3702  /*
3703  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3704  * to infer types for all parameters. Since we explicitly cast every
3705  * parameter (see deparse.c), the "inference" is trivial and will produce
3706  * the desired result. This allows us to avoid assuming that the remote
3707  * server has the same OIDs we do for the parameters' types.
3708  */
3709  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
3710  NULL, values, NULL, NULL, 0))
3711  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
3712 
3713  /*
3714  * Get the result, and check for success.
3715  *
3716  * We don't use a PG_TRY block here, so be careful not to throw error
3717  * without releasing the PGresult.
3718  */
3719  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
3720  if (PQresultStatus(dmstate->result) !=
3722  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
3723  dmstate->query);
3724 
3725  /* Get the number of rows affected. */
3726  if (dmstate->has_returning)
3727  dmstate->num_tuples = PQntuples(dmstate->result);
3728  else
3729  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
3730 }
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 3134 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().

3135 {
3136  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3137  PGresult *volatile res = NULL;
3138  MemoryContext oldcontext;
3139 
3140  /*
3141  * We'll store the tuples in the batch_cxt. First, flush the previous
3142  * batch.
3143  */
3144  fsstate->tuples = NULL;
3145  MemoryContextReset(fsstate->batch_cxt);
3146  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3147 
3148  /* PGresult must be released before leaving this function. */
3149  PG_TRY();
3150  {
3151  PGconn *conn = fsstate->conn;
3152  char sql[64];
3153  int numrows;
3154  int i;
3155 
3156  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3157  fsstate->fetch_size, fsstate->cursor_number);
3158 
3159  res = pgfdw_exec_query(conn, sql);
3160  /* On error, report the original query, not the FETCH. */
3161  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3162  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3163 
3164  /* Convert the data into HeapTuples */
3165  numrows = PQntuples(res);
3166  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3167  fsstate->num_tuples = numrows;
3168  fsstate->next_tuple = 0;
3169 
3170  for (i = 0; i < numrows; i++)
3171  {
3172  Assert(IsA(node->ss.ps.plan, ForeignScan));
3173 
3174  fsstate->tuples[i] =
3176  fsstate->rel,
3177  fsstate->attinmeta,
3178  fsstate->retrieved_attrs,
3179  node,
3180  fsstate->temp_cxt);
3181  }
3182 
3183  /* Update fetch_ct_2 */
3184  if (fsstate->fetch_ct_2 < 2)
3185  fsstate->fetch_ct_2++;
3186 
3187  /* Must be EOF if we didn't get as many tuples as we asked for. */
3188  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3189 
3190  PQclear(res);
3191  res = NULL;
3192  }
3193  PG_CATCH();
3194  {
3195  if (res)
3196  PQclear(res);
3197  PG_RE_THROW();
3198  }
3199  PG_END_TRY();
3200 
3201  MemoryContextSwitchTo(oldcontext);
3202 }
ScanState ss
Definition: execnodes.h:1641
#define IsA(nodeptr, _type_)
Definition: nodes.h:567
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 5776 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().

5777 {
5778  ListCell *lc_em;
5779 
5780  foreach(lc_em, ec->ec_members)
5781  {
5782  EquivalenceMember *em = lfirst(lc_em);
5783 
5784  if (bms_is_subset(em->em_relids, rel->relids))
5785  {
5786  /*
5787  * If there is more than one equivalence member whose Vars are
5788  * taken entirely from this relation, we'll be content to choose
5789  * any one of those.
5790  */
5791  return em->em_expr;
5792  }
5793  }
5794 
5795  /* We didn't find any suitable equivalence class expression */
5796  return NULL;
5797 }
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 3524 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().

3525 {
3526  Assert(fmstate != NULL);
3527 
3528  /* If we created a prepared statement, destroy it */
3529  if (fmstate->p_name)
3530  {
3531  char sql[64];
3532  PGresult *res;
3533 
3534  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
3535 
3536  /*
3537  * We don't use a PG_TRY block here, so be careful not to throw error
3538  * without releasing the PGresult.
3539  */
3540  res = pgfdw_exec_query(fmstate->conn, sql);
3541  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3542  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
3543  PQclear(res);
3544  fmstate->p_name = NULL;
3545  }
3546 
3547  /* Release remote connection */
3548  ReleaseConnection(fmstate->conn);
3549  fmstate->conn = NULL;
3550 }
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 5212 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().

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

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

2984 {
2985  PGresult *volatile res = NULL;
2986 
2987  /* PGresult must be released before leaving this function. */
2988  PG_TRY();
2989  {
2990  char *line;
2991  char *p;
2992  int n;
2993 
2994  /*
2995  * Execute EXPLAIN remotely.
2996  */
2997  res = pgfdw_exec_query(conn, sql);
2998  if (PQresultStatus(res) != PGRES_TUPLES_OK)
2999  pgfdw_report_error(ERROR, res, conn, false, sql);
3000 
3001  /*
3002  * Extract cost numbers for topmost plan node. Note we search for a
3003  * left paren from the end of the line to avoid being confused by
3004  * other uses of parentheses.
3005  */
3006  line = PQgetvalue(res, 0, 0);
3007  p = strrchr(line, '(');
3008  if (p == NULL)
3009  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3010  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3011  startup_cost, total_cost, rows, width);
3012  if (n != 4)
3013  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3014 
3015  PQclear(res);
3016  res = NULL;
3017  }
3018  PG_CATCH();
3019  {
3020  if (res)
3021  PQclear(res);
3022  PG_RE_THROW();
3023  }
3024  PG_END_TRY();
3025 }
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 3736 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().

3737 {
3739  EState *estate = node->ss.ps.state;
3740  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
3741  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
3742  TupleTableSlot *resultSlot;
3743 
3744  Assert(resultRelInfo->ri_projectReturning);
3745 
3746  /* If we didn't get any tuples, must be end of data. */
3747  if (dmstate->next_tuple >= dmstate->num_tuples)
3748  return ExecClearTuple(slot);
3749 
3750  /* Increment the command es_processed count if necessary. */
3751  if (dmstate->set_processed)
3752  estate->es_processed += 1;
3753 
3754  /*
3755  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
3756  * tuple. (has_returning is false when the local query is of the form
3757  * "UPDATE/DELETE .. RETURNING 1" for example.)
3758  */
3759  if (!dmstate->has_returning)
3760  {
3761  ExecStoreAllNullTuple(slot);
3762  resultSlot = slot;
3763  }
3764  else
3765  {
3766  /*
3767  * On error, be sure to release the PGresult on the way out. Callers
3768  * do not have PG_TRY blocks to ensure this happens.
3769  */
3770  PG_TRY();
3771  {
3772  HeapTuple newtup;
3773 
3774  newtup = make_tuple_from_result_row(dmstate->result,
3775  dmstate->next_tuple,
3776  dmstate->rel,
3777  dmstate->attinmeta,
3778  dmstate->retrieved_attrs,
3779  node,
3780  dmstate->temp_cxt);
3781  ExecStoreTuple(newtup, slot, InvalidBuffer, false);
3782  }
3783  PG_CATCH();
3784  {
3785  if (dmstate->result)
3786  PQclear(dmstate->result);
3787  PG_RE_THROW();
3788  }
3789  PG_END_TRY();
3790 
3791  /* Get the updated/deleted tuple. */
3792  if (dmstate->rel)
3793  resultSlot = slot;
3794  else
3795  resultSlot = apply_returning_filter(dmstate, slot, estate);
3796  }
3797  dmstate->next_tuple++;
3798 
3799  /* Make slot available for evaluation of the local query RETURNING list. */
3800  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
3801  resultSlot;
3802 
3803  return slot;
3804 }
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 715 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().

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

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

3813 {
3814  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
3815  ListCell *lc;
3816  int i;
3817 
3818  /*
3819  * Calculate the mapping between the fdw_scan_tlist's entries and the
3820  * result tuple's attributes.
3821  *
3822  * The "map" is an array of indexes of the result tuple's attributes in
3823  * fdw_scan_tlist, i.e., one entry for every attribute of the result
3824  * tuple. We store zero for any attributes that don't have the
3825  * corresponding entries in that list, marking that a NULL is needed in
3826  * the result tuple.
3827  *
3828  * Also get the indexes of the entries for ctid and oid if any.
3829  */
3830  dmstate->attnoMap = (AttrNumber *)
3831  palloc0(resultTupType->natts * sizeof(AttrNumber));
3832 
3833  dmstate->ctidAttno = dmstate->oidAttno = 0;
3834 
3835  i = 1;
3836  dmstate->hasSystemCols = false;
3837  foreach(lc, fdw_scan_tlist)
3838  {
3839  TargetEntry *tle = (TargetEntry *) lfirst(lc);
3840  Var *var = (Var *) tle->expr;
3841 
3842  Assert(IsA(var, Var));
3843 
3844  /*
3845  * If the Var is a column of the target relation to be retrieved from
3846  * the foreign server, get the index of the entry.
3847  */
3848  if (var->varno == rtindex &&
3849  list_member_int(dmstate->retrieved_attrs, i))
3850  {
3851  int attrno = var->varattno;
3852 
3853  if (attrno < 0)
3854  {
3855  /*
3856  * We don't retrieve system columns other than ctid and oid.
3857  */
3858  if (attrno == SelfItemPointerAttributeNumber)
3859  dmstate->ctidAttno = i;
3860  else if (attrno == ObjectIdAttributeNumber)
3861  dmstate->oidAttno = i;
3862  else
3863  Assert(false);
3864  dmstate->hasSystemCols = true;
3865  }
3866  else
3867  {
3868  /*
3869  * We don't retrieve whole-row references to the target
3870  * relation either.
3871  */
3872  Assert(attrno > 0);
3873 
3874  dmstate->attnoMap[attrno - 1] = i;
3875  }
3876  }
3877  i++;
3878  }
3879 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:567
#define RelationGetDescr(relation)
Definition: rel.h:433
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
AttrNumber varattno
Definition: primnodes.h:168
Definition: primnodes.h:163
int natts
Definition: tupdesc.h:82
bool list_member_int(const List *list, int datum)
Definition: list.c:485
Index varno
Definition: primnodes.h:166
void * palloc0(Size size)
Definition: mcxt.c:955
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Expr * expr
Definition: primnodes.h:1375
int i
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
int16 AttrNumber
Definition: attnum.h:21

◆ make_tuple_from_result_row()

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

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

5545 {
5546  HeapTuple tuple;
5547  TupleDesc tupdesc;
5548  Datum *values;
5549  bool *nulls;
5550  ItemPointer ctid = NULL;
5551  Oid oid = InvalidOid;
5552  ConversionLocation errpos;
5553  ErrorContextCallback errcallback;
5554  MemoryContext oldcontext;
5555  ListCell *lc;
5556  int j;
5557 
5558  Assert(row < PQntuples(res));
5559 
5560  /*
5561  * Do the following work in a temp context that we reset after each tuple.
5562  * This cleans up not only the data we have direct access to, but any
5563  * cruft the I/O functions might leak.
5564  */
5565  oldcontext = MemoryContextSwitchTo(temp_context);
5566 
5567  if (rel)
5568  tupdesc = RelationGetDescr(rel);
5569  else
5570  {
5571  Assert(fsstate);
5572  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
5573  }
5574 
5575  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
5576  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
5577  /* Initialize to nulls for any columns not present in result */
5578  memset(nulls, true, tupdesc->natts * sizeof(bool));
5579 
5580  /*
5581  * Set up and install callback to report where conversion error occurs.
5582  */
5583  errpos.rel = rel;
5584  errpos.cur_attno = 0;
5585  errpos.fsstate = fsstate;
5586  errcallback.callback = conversion_error_callback;
5587  errcallback.arg = (void *) &errpos;
5588  errcallback.previous = error_context_stack;
5589  error_context_stack = &errcallback;
5590 
5591  /*
5592  * i indexes columns in the relation, j indexes columns in the PGresult.
5593  */
5594  j = 0;
5595  foreach(lc, retrieved_attrs)
5596  {
5597  int i = lfirst_int(lc);
5598  char *valstr;
5599 
5600  /* fetch next column's textual value */
5601  if (PQgetisnull(res, row, j))
5602  valstr = NULL;
5603  else
5604  valstr = PQgetvalue(res, row, j);
5605 
5606  /*
5607  * convert value to internal representation
5608  *
5609  * Note: we ignore system columns other than ctid and oid in result
5610  */
5611  errpos.cur_attno = i;
5612  if (i > 0)
5613  {
5614  /* ordinary column */
5615  Assert(i <= tupdesc->natts);
5616  nulls[i - 1] = (valstr == NULL);
5617  /* Apply the input function even to nulls, to support domains */
5618  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
5619  valstr,
5620  attinmeta->attioparams[i - 1],
5621  attinmeta->atttypmods[i - 1]);
5622  }
5623  else if (i == SelfItemPointerAttributeNumber)
5624  {
5625  /* ctid */
5626  if (valstr != NULL)
5627  {
5628  Datum datum;
5629 
5630  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
5631  ctid = (ItemPointer) DatumGetPointer(datum);
5632  }
5633  }
5634  else if (i == ObjectIdAttributeNumber)
5635  {
5636  /* oid */
5637  if (valstr != NULL)
5638  {
5639  Datum datum;
5640 
5641  datum = DirectFunctionCall1(oidin, CStringGetDatum(valstr));
5642  oid = DatumGetObjectId(datum);
5643  }
5644  }
5645  errpos.cur_attno = 0;
5646 
5647  j++;
5648  }
5649 
5650  /* Uninstall error context callback. */
5651  error_context_stack = errcallback.previous;
5652 
5653  /*
5654  * Check we got the expected number of columns. Note: j == 0 and
5655  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
5656  */
5657  if (j > 0 && j != PQnfields(res))
5658  elog(ERROR, "remote query result does not match the foreign table");
5659 
5660  /*
5661  * Build the result tuple in caller's memory context.
5662  */
5663  MemoryContextSwitchTo(oldcontext);
5664 
5665  tuple = heap_form_tuple(tupdesc, values, nulls);
5666 
5667  /*
5668  * If we have a CTID to return, install it in both t_self and t_ctid.
5669  * t_self is the normal place, but if the tuple is converted to a
5670  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
5671  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
5672  */
5673  if (ctid)
5674  tuple->t_self = tuple->t_data->t_ctid = *ctid;
5675 
5676  /*
5677  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
5678  * heap_form_tuple. heap_form_tuple actually creates the tuple with
5679  * DatumTupleFields, not HeapTupleFields, but the executor expects
5680  * HeapTupleFields and will happily extract system columns on that
5681  * assumption. If we don't do this then, for example, the tuple length
5682  * ends up in the xmin field, which isn't what we want.
5683  */
5687 
5688  /*
5689  * If we have an OID to return, install it.
5690  */
5691  if (OidIsValid(oid))
5692  HeapTupleSetOid(tuple, oid);
5693 
5694  /* Clean up */
5695  MemoryContextReset(temp_context);
5696 
5697  return tuple;
5698 }
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:485
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:710
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:159
ItemPointerData t_self
Definition: htup.h:65
#define CStringGetDatum(X)
Definition: postgres.h:563
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:379
#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:367
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:534
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:396
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:318

◆ merge_fdw_options()

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

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

5033 {
5034  /* We must always have fpinfo_o. */
5035  Assert(fpinfo_o);
5036 
5037  /* fpinfo_i may be NULL, but if present the servers must both match. */
5038  Assert(!fpinfo_i ||
5039  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
5040 
5041  /*
5042  * Copy the server specific FDW options. (For a join, both relations come
5043  * from the same server, so the server options should have the same value
5044  * for both relations.)
5045  */
5046  fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
5047  fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
5048  fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
5049  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
5050  fpinfo->fetch_size = fpinfo_o->fetch_size;
5051 
5052  /* Merge the table level options from either side of the join. */
5053  if (fpinfo_i)
5054  {
5055  /*
5056  * We'll prefer to use remote estimates for this join if any table
5057  * from either side of the join is using remote estimates. This is
5058  * most likely going to be preferred since they're already willing to
5059  * pay the price of a round trip to get the remote EXPLAIN. In any
5060  * case it's not entirely clear how we might otherwise handle this
5061  * best.
5062  */
5063  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
5064  fpinfo_i->use_remote_estimate;
5065 
5066  /*
5067  * Set fetch size to maximum of the joining sides, since we are
5068  * expecting the rows returned by the join to be proportional to the
5069  * relation sizes.
5070  */
5071  fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
5072  }
5073 }
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 460 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.

461 {
462  FdwRoutine *routine = makeNode(FdwRoutine);
463 
464  /* Functions for scanning foreign tables */
472 
473  /* Functions for updating foreign tables */
488 
489  /* Function for EvalPlanQual rechecks */
491  /* Support functions for EXPLAIN */
495 
496  /* Support functions for ANALYZE */
498 
499  /* Support functions for IMPORT FOREIGN SCHEMA */
501 
502  /* Support functions for join push-down */
504 
505  /* Support functions for upper relation push-down */
507 
508  PG_RETURN_POINTER(routine);
509 }
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:519
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:918
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:564
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 4156 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().

4160 {
4161  PgFdwAnalyzeState astate;
4162  ForeignTable *table;
4163  ForeignServer *server;
4164  UserMapping *user;
4165  PGconn *conn;
4166  unsigned int cursor_number;
4167  StringInfoData sql;
4168  PGresult *volatile res = NULL;
4169 
4170  /* Initialize workspace state */
4171  astate.rel = relation;
4173 
4174  astate.rows = rows;
4175  astate.targrows = targrows;
4176  astate.numrows = 0;
4177  astate.samplerows = 0;
4178  astate.rowstoskip = -1; /* -1 means not set yet */
4179  reservoir_init_selection_state(&astate.rstate, targrows);
4180 
4181  /* Remember ANALYZE context, and create a per-tuple temp context */
4182  astate.anl_cxt = CurrentMemoryContext;
4184  "postgres_fdw temporary data",
4186 
4187  /*
4188  * Get the connection to use. We do the remote access as the table's
4189  * owner, even if the ANALYZE was started by some other user.
4190  */
4191  table = GetForeignTable(RelationGetRelid(relation));
4192  server = GetForeignServer(table->serverid);
4193  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4194  conn = GetConnection(user, false);
4195 
4196  /*
4197  * Construct cursor that retrieves whole rows from remote.
4198  */
4199  cursor_number = GetCursorNumber(conn);
4200  initStringInfo(&sql);
4201  appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
4202  deparseAnalyzeSql(&sql, relation, &astate.retrieved_attrs);
4203 
4204  /* In what follows, do not risk leaking any PGresults. */
4205  PG_TRY();
4206  {
4207  res = pgfdw_exec_query(conn, sql.data);
4208  if (PQresultStatus(res) != PGRES_COMMAND_OK)
4209  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4210  PQclear(res);
4211  res = NULL;
4212 
4213  /* Retrieve and process rows a batch at a time. */
4214  for (;;)
4215  {
4216  char fetch_sql[64];
4217  int fetch_size;
4218  int numrows;
4219  int i;
4220  ListCell *lc;
4221 
4222  /* Allow users to cancel long query */
4224 
4225  /*
4226  * XXX possible future improvement: if rowstoskip is large, we
4227  * could issue a MOVE rather than physically fetching the rows,
4228  * then just adjust rowstoskip and samplerows appropriately.
4229  */
4230 
4231  /* The fetch size is arbitrary, but shouldn't be enormous. */
4232  fetch_size = 100;
4233  foreach(lc, server->options)
4234  {
4235  DefElem *def = (DefElem *) lfirst(lc);
4236 
4237  if (strcmp(def->defname, "fetch_size") == 0)
4238  {
4239  fetch_size = strtol(defGetString(def), NULL, 10);
4240  break;
4241  }
4242  }
4243  foreach(lc, table->options)
4244  {
4245  DefElem *def = (DefElem *) lfirst(lc);
4246 
4247  if (strcmp(def->defname, "fetch_size") == 0)
4248  {
4249  fetch_size = strtol(defGetString(def), NULL, 10);
4250  break;
4251  }
4252  }
4253 
4254  /* Fetch some rows */
4255  snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
4256  fetch_size, cursor_number);
4257 
4258  res = pgfdw_exec_query(conn, fetch_sql);
4259  /* On error, report the original query, not the FETCH. */
4260  if (PQresultStatus(res) != PGRES_TUPLES_OK)
4261  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4262 
4263  /* Process whatever we got. */
4264  numrows = PQntuples(res);
4265  for (i = 0; i < numrows; i++)
4266  analyze_row_processor(res, i, &astate);
4267 
4268  PQclear(res);
4269  res = NULL;
4270 
4271  /* Must be EOF if we didn't get all the rows requested. */
4272  if (numrows < fetch_size)
4273  break;
4274  }
4275 
4276  /* Close the cursor, just to be tidy. */
4277  close_cursor(conn, cursor_number);
4278  }
4279  PG_CATCH();
4280  {
4281  if (res)
4282  PQclear(res);
4283  PG_RE_THROW();
4284  }
4285  PG_END_TRY();
4286 
4287  ReleaseConnection(conn);
4288 
4289  /* We assume that we have no dead tuple. */
4290  *totaldeadrows = 0.0;
4291 
4292  /* We've retrieved all living tuples from foreign server. */
4293  *totalrows = astate.samplerows;
4294 
4295  /*
4296  * Emit some interesting relation info
4297  */
4298  ereport(elevel,
4299  (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
4300  RelationGetRelationName(relation),
4301  astate.samplerows, astate.numrows)));
4302 
4303  return astate.numrows;
4304 }
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:1992
#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 1538 of file postgres_fdw.c.

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

Referenced by postgres_fdw_handler().

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

4081 {
4082  ForeignTable *table;
4083  UserMapping *user;
4084  PGconn *conn;
4085  StringInfoData sql;
4086  PGresult *volatile res = NULL;
4087 
4088  /* Return the row-analysis function pointer */
4090 
4091  /*
4092  * Now we have to get the number of pages. It's annoying that the ANALYZE
4093  * API requires us to return that now, because it forces some duplication
4094  * of effort between this routine and postgresAcquireSampleRowsFunc. But
4095  * it's probably not worth redefining that API at this point.
4096  */
4097 
4098  /*
4099  * Get the connection to use. We do the remote access as the table's
4100  * owner, even if the ANALYZE was started by some other user.
4101  */
4102  table = GetForeignTable(RelationGetRelid(relation));
4103  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4104  conn = GetConnection(user, false);
4105 
4106  /*
4107  * Construct command to get page count for relation.
4108  */
4109  initStringInfo(&sql);
4110  deparseAnalyzeSizeSql(&sql, relation);
4111 
4112  /* In what follows, do not risk leaking any PGresults. */
4113  PG_TRY();
4114  {
4115  res = pgfdw_exec_query(conn, sql.data);
4116  if (PQresultStatus(res) != PGRES_TUPLES_OK)
4117  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4118 
4119  if (PQntuples(res) != 1 || PQnfields(res) != 1)
4120  elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
4121  *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
4122 
4123  PQclear(res);
4124  res = NULL;
4125  }
4126  PG_CATCH();
4127  {
4128  if (res)
4129  PQclear(res);
4130  PG_RE_THROW();
4131  }
4132  PG_END_TRY();
4133 
4134  ReleaseConnection(conn);
4135 
4136  return true;
4137 }
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:1972
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 2359 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().

2360 {
2361  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
2362  EState *estate = node->ss.ps.state;
2363  PgFdwDirectModifyState *dmstate;
2364  Index rtindex;
2365  RangeTblEntry *rte;
2366  Oid userid;
2367  ForeignTable *table;
2368  UserMapping *user;
2369  int numParams;
2370 
2371  /*
2372  * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
2373  */
2374  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2375  return;
2376 
2377  /*
2378  * We'll save private state in node->fdw_state.
2379  */
2380  dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
2381  node->fdw_state = (void *) dmstate;
2382 
2383  /*
2384  * Identify which user to do the remote access as. This should match what
2385  * ExecCheckRTEPerms() does.
2386  */
2387  rtindex = estate->es_result_relation_info->ri_RangeTableIndex;
2388  rte = rt_fetch(rtindex, estate->es_range_table);
2389  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
2390 
2391  /* Get info about foreign table. */
2392  if (fsplan->scan.scanrelid == 0)
2393  dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags);
2394  else
2395  dmstate->rel = node->ss.ss_currentRelation;
2396  table = GetForeignTable(RelationGetRelid(dmstate->rel));
2397  user = GetUserMapping(userid, table->serverid);
2398 
2399  /*
2400  * Get connection to the foreign server. Connection manager will
2401  * establish new connection if necessary.
2402  */
2403  dmstate->conn = GetConnection(user, false);
2404 
2405  /* Update the foreign-join-related fields. */
2406  if (fsplan->scan.scanrelid == 0)
2407  {
2408  /* Save info about foreign table. */
2409  dmstate->resultRel = dmstate->rel;
2410 
2411  /*
2412  * Set dmstate->rel to NULL to teach get_returning_data() and
2413  * make_tuple_from_result_row() that columns fetched from the remote
2414  * server are described by fdw_scan_tlist of the foreign-scan plan
2415  * node, not the tuple descriptor for the target relation.
2416  */
2417  dmstate->rel = NULL;
2418  }
2419 
2420  /* Initialize state variable */
2421  dmstate->num_tuples = -1; /* -1 means not set yet */
2422 
2423  /* Get private info created by planner functions. */
2424  dmstate->query = strVal(list_nth(fsplan->fdw_private,
2426  dmstate->has_returning = intVal(list_nth(fsplan->fdw_private,
2428  dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
2430  dmstate->set_processed = intVal(list_nth(fsplan->fdw_private,
2432 
2433  /* Create context for per-tuple temp workspace. */
2434  dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
2435  "postgres_fdw temporary data",
2437 
2438  /* Prepare for input conversion of RETURNING results. */
2439  if (dmstate->has_returning)
2440  {
2441  TupleDesc tupdesc;
2442 
2443  if (fsplan->scan.scanrelid == 0)
2444  tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2445  else
2446  tupdesc = RelationGetDescr(dmstate->rel);
2447 
2448  dmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
2449 
2450  /*
2451  * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2452  * initialize a filter to extract an updated/deleted tuple from a scan
2453  * tuple.
2454  */
2455  if (fsplan->scan.scanrelid == 0)
2456  init_returning_filter(dmstate, fsplan->fdw_scan_tlist, rtindex);
2457  }
2458 
2459  /*
2460  * Prepare for processing of parameters used in remote query, if any.
2461  */
2462  numParams = list_length(fsplan->fdw_exprs);
2463  dmstate->numParams = numParams;
2464  if (numParams > 0)
2466  fsplan->fdw_exprs,
2467  numParams,
2468  &dmstate->param_flinfo,
2469  &dmstate->param_exprs,
2470  &dmstate->param_values);
2471 }
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 1980 of file postgres_fdw.c.

References attnum, castNode, CMD_INSERT, CMD_UPDATE, copyObject, create_foreign_modify(), StringInfoData::data, deparseInsertSql(), elog, ERROR, EState::es_range_table, initStringInfo(), lappend_int(), list_nth(), tupleDesc::natts, NIL, ModifyTable::nominalRelation, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ModifyTable::onConflictAction, ModifyTable::operation, PlanState::plan, ModifyTableState::ps, PgFdwScanState::rel, RelationGetDescr, RelationGetRelid, RangeTblEntry::relid, RangeTblEntry::relkind, ModifyTableState::resultRelInfo, PgFdwScanState::retrieved_attrs, ResultRelInfo::ri_FdwState, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_returningList, PlanState::state, PgFdwScanState::tupdesc, and TupleDescAttr.

Referenced by postgres_fdw_handler().

1982 {
1983  PgFdwModifyState *fmstate;
1984  ModifyTable *plan = castNode(ModifyTable, mtstate->ps.plan);
1985  EState *estate = mtstate->ps.state;
1986  Index resultRelation = resultRelInfo->ri_RangeTableIndex;
1987  Relation rel = resultRelInfo->ri_RelationDesc;
1988  RangeTblEntry *rte;
1989  TupleDesc tupdesc = RelationGetDescr(rel);
1990  int attnum;
1991  StringInfoData sql;
1992  List *targetAttrs = NIL;
1993  List *retrieved_attrs = NIL;
1994  bool doNothing = false;
1995 
1996  initStringInfo(&sql);
1997 
1998  /* We transmit all columns that are defined in the foreign table. */
1999  for (attnum = 1; attnum <= tupdesc->natts; attnum++)
2000  {
2001  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2002 
2003  if (!attr->attisdropped)
2004  targetAttrs = lappend_int(targetAttrs, attnum);
2005  }
2006 
2007  /* Check if we add the ON CONFLICT clause to the remote query. */
2008  if (plan)
2009  {
2010  OnConflictAction onConflictAction = plan->onConflictAction;
2011 
2012  /* We only support DO NOTHING without an inference specification. */
2013  if (onConflictAction == ONCONFLICT_NOTHING)
2014  doNothing = true;
2015  else if (onConflictAction != ONCONFLICT_NONE)
2016  elog(ERROR, "unexpected ON CONFLICT specification: %d",
2017  (int) onConflictAction);
2018  }
2019 
2020  /*
2021  * If the foreign table is a partition, we need to create a new RTE
2022  * describing the foreign table for use by deparseInsertSql and
2023  * create_foreign_modify() below, after first copying the parent's
2024  * RTE and modifying some fields to describe the foreign partition to
2025  * work on. However, if this is invoked by UPDATE, the existing RTE
2026  * may already correspond to this partition if it is one of the
2027  * UPDATE subplan target rels; in that case, we can just use the
2028  * existing RTE as-is.
2029  */
2030  rte = list_nth(estate->es_range_table, resultRelation - 1);
2031  if (rte->relid != RelationGetRelid(rel))
2032  {
2033  rte = copyObject(rte);
2034  rte->relid = RelationGetRelid(rel);
2035  rte->relkind = RELKIND_FOREIGN_TABLE;
2036 
2037  /*
2038  * For UPDATE, we must use the RT index of the first subplan
2039  * target rel's RTE, because the core code would have built
2040  * expressions for the partition, such as RETURNING, using that
2041  * RT index as varno of Vars contained in those expressions.
2042  */
2043  if (plan && plan->operation == CMD_UPDATE &&
2044  resultRelation == plan->nominalRelation)
2045  resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
2046  }
2047 
2048  /* Construct the SQL command string. */
2049  deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing,
2050  resultRelInfo->ri_returningList, &retrieved_attrs);
2051 
2052  /* Construct an execution state. */
2053  fmstate = create_foreign_modify(mtstate->ps.state,
2054  rte,
2055  resultRelInfo,
2056  CMD_INSERT,
2057  NULL,
2058  sql.data,
2059  targetAttrs,
2060  retrieved_attrs != NIL,
2061  retrieved_attrs);
2062 
2063  resultRelInfo->ri_FdwState = fmstate;
2064 }
#define NIL
Definition: pg_list.h:69
Relation ri_RelationDesc
Definition: execnodes.h:397
static PgFdwModifyState * create_foreign_modify(EState *estate, RangeTblEntry *rte, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, bool has_returning, List *retrieved_attrs)
Index nominalRelation
Definition: plannodes.h:222
#define RelationGetDescr(relation)
Definition: rel.h:433