PostgreSQL Source Code  git master
postgres_fdw.c File Reference
#include "postgres.h"
#include "postgres_fdw.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "catalog/pg_class.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/vacuum.h"
#include "foreign/fdwapi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/cost.h"
#include "optimizer/clauses.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/var.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
#include "utils/selfuncs.h"
Include dependency graph for postgres_fdw.c:

Go to the source code of this file.

Data Structures

struct  PgFdwScanState
 
struct  PgFdwModifyState
 
struct  PgFdwDirectModifyState
 
struct  PgFdwAnalyzeState
 
struct  ConversionLocation
 
struct  ec_member_foreign_arg
 

Macros

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

Typedefs

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

Enumerations

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

Functions

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

Variables

 PG_MODULE_MAGIC
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 54 of file postgres_fdw.c.

Referenced by estimate_path_cost_size().

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 48 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.01

Definition at line 51 of file postgres_fdw.c.

Referenced by postgresGetForeignRelSize().

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 110 of file postgres_fdw.c.

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 89 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 63 of file postgres_fdw.c.

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

Function Documentation

◆ add_foreign_grouping_paths()

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

Definition at line 4842 of file postgres_fdw.c.

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

Referenced by postgresGetForeignUpperPaths().

4844 {
4845  Query *parse = root->parse;
4846  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
4847  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
4848  ForeignPath *grouppath;
4849  PathTarget *grouping_target;
4850  double rows;
4851  int width;
4852  Cost startup_cost;
4853  Cost total_cost;
4854 
4855  /* Nothing to be done, if there is no grouping or aggregation required. */
4856  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
4857  !root->hasHavingQual)
4858  return;
4859 
4860  grouping_target = root->upper_targets[UPPERREL_GROUP_AGG];
4861 
4862  /* save the input_rel as outerrel in fpinfo */
4863  fpinfo->outerrel = input_rel;
4864 
4865  /*
4866  * Copy foreign table, foreign server, user mapping, FDW options etc.
4867  * details from the input relation's fpinfo.
4868  */
4869  fpinfo->table = ifpinfo->table;
4870  fpinfo->server = ifpinfo->server;
4871  fpinfo->user = ifpinfo->user;
4872  merge_fdw_options(fpinfo, ifpinfo, NULL);
4873 
4874  /* Assess if it is safe to push down aggregation and grouping. */
4875  if (!foreign_grouping_ok(root, grouped_rel))
4876  return;
4877 
4878  /* Estimate the cost of push down */
4879  estimate_path_cost_size(root, grouped_rel, NIL, NIL, &rows,
4880  &width, &startup_cost, &total_cost);
4881 
4882  /* Now update this information in the fpinfo */
4883  fpinfo->rows = rows;
4884  fpinfo->width = width;
4885  fpinfo->startup_cost = startup_cost;
4886  fpinfo->total_cost = total_cost;
4887 
4888  /* Create and add foreign path to the grouping relation. */
4889  grouppath = create_foreignscan_path(root,
4890  grouped_rel,
4891  grouping_target,
4892  rows,
4893  startup_cost,
4894  total_cost,
4895  NIL, /* no pathkeys */
4896  NULL, /* no required_outer */
4897  NULL,
4898  NIL); /* no fdw_private */
4899 
4900  /* Add generated path into grouped_rel by add_path(). */
4901  add_path(grouped_rel, (Path *) grouppath);
4902 }
#define NIL
Definition: pg_list.h:69
Query * parse
Definition: relation.h:155
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
bool hasAggs
Definition: parsenodes.h:123
ForeignServer * server
Definition: postgres_fdw.h:76
List * groupingSets
Definition: parsenodes.h:148
RelOptInfo * outerrel
Definition: postgres_fdw.h:89
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
ForeignPath * create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_private)
Definition: pathnode.c:2032
UserMapping * user
Definition: postgres_fdw.h:77
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
void * fdw_private
Definition: relation.h:637
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *baserel, List *join_conds, List *pathkeys, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
ForeignTable * table
Definition: postgres_fdw.h:75
List * groupClause
Definition: parsenodes.h:146
bool hasHavingQual
Definition: relation.h:305
double Cost
Definition: nodes.h:643
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:649
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: relation.h:278

◆ add_paths_with_pathkeys_for_rel()

static void add_paths_with_pathkeys_for_rel ( PlannerInfo root,
RelOptInfo rel,
Path epq_path 
)
static

Definition at line 4317 of file postgres_fdw.c.

References add_path(), create_foreignscan_path(), estimate_path_cost_size(), get_useful_pathkeys_for_relation(), lfirst, and NIL.

Referenced by postgresGetForeignJoinPaths(), and postgresGetForeignPaths().

4319 {
4320  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
4321  ListCell *lc;
4322 
4323  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
4324 
4325  /* Create one path for each set of pathkeys we found above. */
4326  foreach(lc, useful_pathkeys_list)
4327  {
4328  double rows;
4329  int width;
4330  Cost startup_cost;
4331  Cost total_cost;
4332  List *useful_pathkeys = lfirst(lc);
4333 
4334  estimate_path_cost_size(root, rel, NIL, useful_pathkeys,
4335  &rows, &width, &startup_cost, &total_cost);
4336 
4337  add_path(rel, (Path *)
4338  create_foreignscan_path(root, rel,
4339  NULL,
4340  rows,
4341  startup_cost,
4342  total_cost,
4343  useful_pathkeys,
4344  NULL,
4345  epq_path,
4346  NIL));
4347  }
4348 }
#define NIL
Definition: pg_list.h:69
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:778
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:2032
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *baserel, List *join_conds, List *pathkeys, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
#define lfirst(lc)
Definition: pg_list.h:106
Definition: pg_list.h:45
double Cost
Definition: nodes.h:643

◆ analyze_row_processor()

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

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

3713 {
3714  int targrows = astate->targrows;
3715  int pos; /* array index to store tuple in */
3716  MemoryContext oldcontext;
3717 
3718  /* Always increment sample row counter. */
3719  astate->samplerows += 1;
3720 
3721  /*
3722  * Determine the slot where this sample row should be stored. Set pos to
3723  * negative value to indicate the row should be skipped.
3724  */
3725  if (astate->numrows < targrows)
3726  {
3727  /* First targrows rows are always included into the sample */
3728  pos = astate->numrows++;
3729  }
3730  else
3731  {
3732  /*
3733  * Now we start replacing tuples in the sample until we reach the end
3734  * of the relation. Same algorithm as in acquire_sample_rows in
3735  * analyze.c; see Jeff Vitter's paper.
3736  */
3737  if (astate->rowstoskip < 0)
3738  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
3739 
3740  if (astate->rowstoskip <= 0)
3741  {
3742  /* Choose a random reservoir element to replace. */
3743  pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate));
3744  Assert(pos >= 0 && pos < targrows);
3745  heap_freetuple(astate->rows[pos]);
3746  }
3747  else
3748  {
3749  /* Skip this tuple. */
3750  pos = -1;
3751  }
3752 
3753  astate->rowstoskip -= 1;
3754  }
3755 
3756  if (pos >= 0)
3757  {
3758  /*
3759  * Create sample tuple from current result row, and store it in the
3760  * position determined above. The tuple has to be created in anl_cxt.
3761  */
3762  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
3763 
3764  astate->rows[pos] = make_tuple_from_result_row(res, row,
3765  astate->rel,
3766  astate->attinmeta,
3767  astate->retrieved_attrs,
3768  NULL,
3769  astate->temp_cxt);
3770 
3771  MemoryContextSwitchTo(oldcontext);
3772  }
3773 }
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:228
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
double sampler_random_fract(SamplerRandomState randstate)
Definition: sampling.c:238
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
ReservoirStateData rstate
Definition: postgres_fdw.c:235
AttInMetadata * attinmeta
Definition: postgres_fdw.c:224
MemoryContext temp_cxt
Definition: postgres_fdw.c:239
#define Assert(condition)
Definition: c.h:670
MemoryContext anl_cxt
Definition: postgres_fdw.c:238
SamplerRandomState randstate
Definition: sampling.h:50
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:142

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

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

4357 {
4358  ListCell *lc;
4359 
4360  foreach(lc, fpinfo->server->options)
4361  {
4362  DefElem *def = (DefElem *) lfirst(lc);
4363 
4364  if (strcmp(def->defname, "use_remote_estimate") == 0)
4365  fpinfo->use_remote_estimate = defGetBoolean(def);
4366  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
4367  fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
4368  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
4369  fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
4370  else if (strcmp(def->defname, "extensions") == 0)
4371  fpinfo->shippable_extensions =
4372  ExtractExtensionList(defGetString(def), false);
4373  else if (strcmp(def->defname, "fetch_size") == 0)
4374  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
4375  }
4376 }
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:719
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 4384 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().

4385 {
4386  ListCell *lc;
4387 
4388  foreach(lc, fpinfo->table->options)
4389  {
4390  DefElem *def = (DefElem *) lfirst(lc);
4391 
4392  if (strcmp(def->defname, "use_remote_estimate") == 0)
4393  fpinfo->use_remote_estimate = defGetBoolean(def);
4394  else if (strcmp(def->defname, "fetch_size") == 0)
4395  fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
4396  }
4397 }
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:719

◆ close_cursor()

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

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

3117 {
3118  char sql[64];
3119  PGresult *res;
3120 
3121  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3122 
3123  /*
3124  * We don't use a PG_TRY block here, so be careful not to throw error
3125  * without releasing the PGresult.
3126  */
3127  res = pgfdw_exec_query(conn, sql);
3128  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3129  pgfdw_report_error(ERROR, res, conn, true, sql);
3130  PQclear(res);
3131 }
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 5083 of file postgres_fdw.c.

References castNode, ConversionLocation::cur_attno, errcontext, EState::es_range_table, TargetEntry::expr, ForeignScan::fdw_scan_tlist, ConversionLocation::fsstate, get_rel_name(), get_relid_attribute_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().

5084 {
5085  const char *attname = NULL;
5086  const char *relname = NULL;
5087  bool is_wholerow = false;
5089 
5090  if (errpos->rel)
5091  {
5092  /* error occurred in a scan against a foreign table */
5093  TupleDesc tupdesc = RelationGetDescr(errpos->rel);
5094  Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
5095 
5096  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
5097  attname = NameStr(attr->attname);
5098  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
5099  attname = "ctid";
5100  else if (errpos->cur_attno == ObjectIdAttributeNumber)
5101  attname = "oid";
5102 
5103  relname = RelationGetRelationName(errpos->rel);
5104  }
5105  else
5106  {
5107  /* error occurred in a scan against a foreign join */
5108  ForeignScanState *fsstate = errpos->fsstate;
5109  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
5110  EState *estate = fsstate->ss.ps.state;
5111  TargetEntry *tle;
5112 
5113  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
5114  errpos->cur_attno - 1);
5115 
5116  /*
5117  * Target list can have Vars and expressions. For Vars, we can get
5118  * it's relation, however for expressions we can't. Thus for
5119  * expressions, just show generic context message.
5120  */
5121  if (IsA(tle->expr, Var))
5122  {
5123  RangeTblEntry *rte;
5124  Var *var = (Var *) tle->expr;
5125 
5126  rte = rt_fetch(var->varno, estate->es_range_table);
5127 
5128  if (var->varattno == 0)
5129  is_wholerow = true;
5130  else
5131  attname = get_relid_attribute_name(rte->relid, var->varattno);
5132 
5133  relname = get_rel_name(rte->relid);
5134  }
5135  else
5136  errcontext("processing expression at position %d in select list",
5137  errpos->cur_attno);
5138  }
5139 
5140  if (relname)
5141  {
5142  if (is_wholerow)
5143  errcontext("whole-row reference to foreign table \"%s\"", relname);
5144  else if (attname)
5145  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
5146  }
5147 }
ScanState ss
Definition: execnodes.h:1564
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
#define RelationGetDescr(relation)
Definition: rel.h:437
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
AttrNumber varattno
Definition: primnodes.h:168
List * fdw_scan_tlist
Definition: plannodes.h:603
EState * state
Definition: execnodes.h:852
List * es_range_table
Definition: execnodes.h:432
Definition: primnodes.h:163
int natts
Definition: tupdesc.h:79
PlanState ps
Definition: execnodes.h:1113
ForeignScanState * fsstate
Definition: postgres_fdw.c:256
#define list_nth_node(type, list, n)
Definition: pg_list.h:227
#define RelationGetRelationName(relation)
Definition: rel.h:445
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
Index varno
Definition: primnodes.h:166
char * get_relid_attribute_name(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:801
Plan * plan
Definition: execnodes.h:850
Expr * expr
Definition: primnodes.h:1375
#define errcontext
Definition: elog.h:164
#define NameStr(name)
Definition: c.h:547
void * arg
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
AttrNumber cur_attno
Definition: postgres_fdw.c:248
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1745

◆ convert_prep_stmt_params()

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

Definition at line 3188 of file postgres_fdw.c.

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

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

3191 {
3192  const char **p_values;
3193  int pindex = 0;
3194  MemoryContext oldcontext;
3195 
3196  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
3197 
3198  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums);
3199 
3200  /* 1st parameter should be ctid, if it's in use */
3201  if (tupleid != NULL)
3202  {
3203  /* don't need set_transmission_modes for TID output */
3204  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3205  PointerGetDatum(tupleid));
3206  pindex++;
3207  }
3208 
3209  /* get following parameters from slot */
3210  if (slot != NULL && fmstate->target_attrs != NIL)
3211  {
3212  int nestlevel;
3213  ListCell *lc;
3214 
3215  nestlevel = set_transmission_modes();
3216 
3217  foreach(lc, fmstate->target_attrs)
3218  {
3219  int attnum = lfirst_int(lc);
3220  Datum value;
3221  bool isnull;
3222 
3223  value = slot_getattr(slot, attnum, &isnull);
3224  if (isnull)
3225  p_values[pindex] = NULL;
3226  else
3227  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
3228  value);
3229  pindex++;
3230  }
3231 
3232  reset_transmission_modes(nestlevel);
3233  }
3234 
3235  Assert(pindex == fmstate->p_nums);
3236 
3237  MemoryContextSwitchTo(oldcontext);
3238 
3239  return p_values;
3240 }
#define NIL
Definition: pg_list.h:69
#define PointerGetDatum(X)
Definition: postgres.h:562
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int set_transmission_modes(void)
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1662
#define lfirst_int(lc)
Definition: pg_list.h:107
static struct @121 value
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:182
uintptr_t Datum
Definition: postgres.h:372
MemoryContext temp_cxt
Definition: postgres_fdw.c:185
#define Assert(condition)
Definition: c.h:670
void reset_transmission_modes(int nestlevel)
void * palloc(Size size)
Definition: mcxt.c:848
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1142

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

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

2921 {
2922  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
2923  ExprContext *econtext = node->ss.ps.ps_ExprContext;
2924  int numParams = fsstate->numParams;
2925  const char **values = fsstate->param_values;
2926  PGconn *conn = fsstate->conn;
2928  PGresult *res;
2929 
2930  /*
2931  * Construct array of query parameter values in text format. We do the
2932  * conversions in the short-lived per-tuple context, so as not to cause a
2933  * memory leak over repeated scans.
2934  */
2935  if (numParams > 0)
2936  {
2937  MemoryContext oldcontext;
2938 
2939  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
2940 
2941  process_query_params(econtext,
2942  fsstate->param_flinfo,
2943  fsstate->param_exprs,
2944  values);
2945 
2946  MemoryContextSwitchTo(oldcontext);
2947  }
2948 
2949  /* Construct the DECLARE CURSOR command */
2950  initStringInfo(&buf);
2951  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
2952  fsstate->cursor_number, fsstate->query);
2953 
2954  /*
2955  * Notice that we pass NULL for paramTypes, thus forcing the remote server
2956  * to infer types for all parameters. Since we explicitly cast every
2957  * parameter (see deparse.c), the "inference" is trivial and will produce
2958  * the desired result. This allows us to avoid assuming that the remote
2959  * server has the same OIDs we do for the parameters' types.
2960  */
2961  if (!PQsendQueryParams(conn, buf.data, numParams,
2962  NULL, values, NULL, NULL, 0))
2963  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
2964 
2965  /*
2966  * Get the result, and check for success.
2967  *
2968  * We don't use a PG_TRY block here, so be careful not to throw error
2969  * without releasing the PGresult.
2970  */
2971  res = pgfdw_get_result(conn, buf.data);
2972  if (PQresultStatus(res) != PGRES_COMMAND_OK)
2973  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
2974  PQclear(res);
2975 
2976  /* Mark the cursor as created, and show no tuples have been retrieved */
2977  fsstate->cursor_exists = true;
2978  fsstate->tuples = NULL;
2979  fsstate->num_tuples = 0;
2980  fsstate->next_tuple = 0;
2981  fsstate->fetch_ct_2 = 0;
2982  fsstate->eof_reached = false;
2983 
2984  /* Clean up */
2985  pfree(buf.data);
2986 }
ScanState ss
Definition: execnodes.h:1564
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:884
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:1113
void pfree(void *pointer)
Definition: mcxt.c:949
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define ERROR
Definition: elog.h:43
const char ** param_values
Definition: postgres_fdw.c:143
PGconn * conn
Definition: streamutil.c:46
static char * buf
Definition: pg_test_fsync.c:67
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
FmgrInfo * param_flinfo
Definition: postgres_fdw.c:141
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
void PQclear(PGresult *res)
Definition: fe-exec.c:671
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:532
HeapTuple * tuples
Definition: postgres_fdw.c:146
static Datum values[MAXATTR]
Definition: bootstrap.c:164

◆ ec_member_matches_foreign()

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

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

2894 {
2896  Expr *expr = em->em_expr;
2897 
2898  /*
2899  * If we've identified what we're processing in the current scan, we only
2900  * want to match that expression.
2901  */
2902  if (state->current != NULL)
2903  return equal(expr, state->current);
2904 
2905  /*
2906  * Otherwise, ignore anything we've already processed.
2907  */
2908  if (list_member(state->already_used, expr))
2909  return false;
2910 
2911  /* This is the new target to process. */
2912  state->current = expr;
2913  return true;
2914 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2984
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 baserel,
List join_conds,
List pathkeys,
double *  p_rows,
int *  p_width,
Cost p_startup_cost,
Cost p_total_cost 
)
static

Definition at line 2498 of file postgres_fdw.c.

References AGGSPLIT_SIMPLE, appendStringInfoString(), Assert, RelOptInfo::baserestrictcost, build_tlist_to_deparse(), clamp_row_est(), classifyConditions(), clauselist_selectivity(), PgFdwScanState::conn, PathTarget::cost, cost_qual_eval(), cpu_operator_cost, cpu_tuple_cost, StringInfoData::data, DEFAULT_FDW_SORT_MULTIPLIER, deparseSelectStmtForRel(), estimate_num_groups(), RelOptInfo::fdw_private, PgFdwRelationInfo::fdw_startup_cost, PgFdwRelationInfo::fdw_tuple_cost, AggClauseCosts::finalCost, get_agg_clause_costs(), get_remote_estimate(), get_sortgrouplist_exprs(), GetConnection(), Query::groupClause, PgFdwRelationInfo::grouped_tlist, Query::hasAggs, Query::havingQual, initStringInfo(), PgFdwRelationInfo::innerrel, IS_JOIN_REL, IS_UPPER_REL, JOIN_INNER, PgFdwRelationInfo::joinclause_sel, PgFdwRelationInfo::joinclauses, list_concat(), list_copy(), list_length(), PgFdwRelationInfo::local_conds_cost, PgFdwRelationInfo::local_conds_sel, MemSet, Min, NIL, PgFdwRelationInfo::outerrel, RelOptInfo::pages, PlannerInfo::parse, QualCost::per_tuple, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, ReleaseConnection(), RelOptInfo::relid, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, PgFdwScanState::retrieved_attrs, PgFdwRelationInfo::rows, RelOptInfo::rows, seq_page_cost, QualCost::startup, AggClauseCosts::transCost, RelOptInfo::tuples, PlannerInfo::upper_targets, UPPERREL_GROUP_AGG, PgFdwRelationInfo::use_remote_estimate, PgFdwRelationInfo::user, PgFdwRelationInfo::width, and PathTarget::width.

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

2504 {
2505  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2506  double rows;
2507  double retrieved_rows;
2508  int width;
2509  Cost startup_cost;
2510  Cost total_cost;
2511  Cost cpu_per_tuple;
2512 
2513  /*
2514  * If the table or the server is configured to use remote estimates,
2515  * connect to the foreign server and execute EXPLAIN to estimate the
2516  * number of rows selected by the restriction+join clauses. Otherwise,
2517  * estimate rows using whatever statistics we have locally, in a way
2518  * similar to ordinary tables.
2519  */
2520  if (fpinfo->use_remote_estimate)
2521  {
2522  List *remote_param_join_conds;
2523  List *local_param_join_conds;
2524  StringInfoData sql;
2525  PGconn *conn;
2526  Selectivity local_sel;
2527  QualCost local_cost;
2528  List *fdw_scan_tlist = NIL;
2529  List *remote_conds;
2530 
2531  /* Required only to be passed to deparseSelectStmtForRel */
2532  List *retrieved_attrs;
2533 
2534  /*
2535  * param_join_conds might contain both clauses that are safe to send
2536  * across, and clauses that aren't.
2537  */
2538  classifyConditions(root, foreignrel, param_join_conds,
2539  &remote_param_join_conds, &local_param_join_conds);
2540 
2541  /* Build the list of columns to be fetched from the foreign server. */
2542  if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
2543  fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
2544  else
2545  fdw_scan_tlist = NIL;
2546 
2547  /*
2548  * The complete list of remote conditions includes everything from
2549  * baserestrictinfo plus any extra join_conds relevant to this
2550  * particular path.
2551  */
2552  remote_conds = list_concat(list_copy(remote_param_join_conds),
2553  fpinfo->remote_conds);
2554 
2555  /*
2556  * Construct EXPLAIN query including the desired SELECT, FROM, and
2557  * WHERE clauses. Params and other-relation Vars are replaced by dummy
2558  * values, so don't request params_list.
2559  */
2560  initStringInfo(&sql);
2561  appendStringInfoString(&sql, "EXPLAIN ");
2562  deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
2563  remote_conds, pathkeys, false,
2564  &retrieved_attrs, NULL);
2565 
2566  /* Get the remote estimate */
2567  conn = GetConnection(fpinfo->user, false);
2568  get_remote_estimate(sql.data, conn, &rows, &width,
2569  &startup_cost, &total_cost);
2570  ReleaseConnection(conn);
2571 
2572  retrieved_rows = rows;
2573 
2574  /* Factor in the selectivity of the locally-checked quals */
2575  local_sel = clauselist_selectivity(root,
2576  local_param_join_conds,
2577  foreignrel->relid,
2578  JOIN_INNER,
2579  NULL);
2580  local_sel *= fpinfo->local_conds_sel;
2581 
2582  rows = clamp_row_est(rows * local_sel);
2583 
2584  /* Add in the eval cost of the locally-checked quals */
2585  startup_cost += fpinfo->local_conds_cost.startup;
2586  total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
2587  cost_qual_eval(&local_cost, local_param_join_conds, root);
2588  startup_cost += local_cost.startup;
2589  total_cost += local_cost.per_tuple * retrieved_rows;
2590  }
2591  else
2592  {
2593  Cost run_cost = 0;
2594 
2595  /*
2596  * We don't support join conditions in this mode (hence, no
2597  * parameterized paths can be made).
2598  */
2599  Assert(param_join_conds == NIL);
2600 
2601  /*
2602  * Use rows/width estimates made by set_baserel_size_estimates() for
2603  * base foreign relations and set_joinrel_size_estimates() for join
2604  * between foreign relations.
2605  */
2606  rows = foreignrel->rows;
2607  width = foreignrel->reltarget->width;
2608 
2609  /* Back into an estimate of the number of retrieved rows. */
2610  retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
2611 
2612  /*
2613  * We will come here again and again with different set of pathkeys
2614  * that caller wants to cost. We don't need to calculate the cost of
2615  * bare scan each time. Instead, use the costs if we have cached them
2616  * already.
2617  */
2618  if (fpinfo->rel_startup_cost > 0 && fpinfo->rel_total_cost > 0)
2619  {
2620  startup_cost = fpinfo->rel_startup_cost;
2621  run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
2622  }
2623  else if (IS_JOIN_REL(foreignrel))
2624  {
2625  PgFdwRelationInfo *fpinfo_i;
2626  PgFdwRelationInfo *fpinfo_o;
2627  QualCost join_cost;
2628  QualCost remote_conds_cost;
2629  double nrows;
2630 
2631  /* For join we expect inner and outer relations set */
2632  Assert(fpinfo->innerrel && fpinfo->outerrel);
2633 
2634  fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
2635  fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
2636 
2637  /* Estimate of number of rows in cross product */
2638  nrows = fpinfo_i->rows * fpinfo_o->rows;
2639  /* Clamp retrieved rows estimate to at most size of cross product */
2640  retrieved_rows = Min(retrieved_rows, nrows);
2641 
2642  /*
2643  * The cost of foreign join is estimated as cost of generating
2644  * rows for the joining relations + cost for applying quals on the
2645  * rows.
2646  */
2647 
2648  /*
2649  * Calculate the cost of clauses pushed down to the foreign server
2650  */
2651  cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
2652  /* Calculate the cost of applying join clauses */
2653  cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
2654 
2655  /*
2656  * Startup cost includes startup cost of joining relations and the
2657  * startup cost for join and other clauses. We do not include the
2658  * startup cost specific to join strategy (e.g. setting up hash
2659  * tables) since we do not know what strategy the foreign server
2660  * is going to use.
2661  */
2662  startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
2663  startup_cost += join_cost.startup;
2664  startup_cost += remote_conds_cost.startup;
2665  startup_cost += fpinfo->local_conds_cost.startup;
2666 
2667  /*
2668  * Run time cost includes:
2669  *
2670  * 1. Run time cost (total_cost - startup_cost) of relations being
2671  * joined
2672  *
2673  * 2. Run time cost of applying join clauses on the cross product
2674  * of the joining relations.
2675  *
2676  * 3. Run time cost of applying pushed down other clauses on the
2677  * result of join
2678  *
2679  * 4. Run time cost of applying nonpushable other clauses locally
2680  * on the result fetched from the foreign server.
2681  */
2682  run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
2683  run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
2684  run_cost += nrows * join_cost.per_tuple;
2685  nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
2686  run_cost += nrows * remote_conds_cost.per_tuple;
2687  run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
2688  }
2689  else if (IS_UPPER_REL(foreignrel))
2690  {
2691  PgFdwRelationInfo *ofpinfo;
2692  PathTarget *ptarget = root->upper_targets[UPPERREL_GROUP_AGG];
2693  AggClauseCosts aggcosts;
2694  double input_rows;
2695  int numGroupCols;
2696  double numGroups = 1;
2697 
2698  /*
2699  * This cost model is mixture of costing done for sorted and
2700  * hashed aggregates in cost_agg(). We are not sure which
2701  * strategy will be considered at remote side, thus for
2702  * simplicity, we put all startup related costs in startup_cost
2703  * and all finalization and run cost are added in total_cost.
2704  *
2705  * Also, core does not care about costing HAVING expressions and
2706  * adding that to the costs. So similarly, here too we are not
2707  * considering remote and local conditions for costing.
2708  */
2709 
2710  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
2711 
2712  /* Get rows and width from input rel */
2713  input_rows = ofpinfo->rows;
2714  width = ofpinfo->width;
2715 
2716  /* Collect statistics about aggregates for estimating costs. */
2717  MemSet(&aggcosts, 0, sizeof(AggClauseCosts));
2718  if (root->parse->hasAggs)
2719  {
2720  get_agg_clause_costs(root, (Node *) fpinfo->grouped_tlist,
2721  AGGSPLIT_SIMPLE, &aggcosts);
2722  get_agg_clause_costs(root, (Node *) root->parse->havingQual,
2723  AGGSPLIT_SIMPLE, &aggcosts);
2724  }
2725 
2726  /* Get number of grouping columns and possible number of groups */
2727  numGroupCols = list_length(root->parse->groupClause);
2728  numGroups = estimate_num_groups(root,
2730  fpinfo->grouped_tlist),
2731  input_rows, NULL);
2732 
2733  /*
2734  * Number of rows expected from foreign server will be same as
2735  * that of number of groups.
2736  */
2737  rows = retrieved_rows = numGroups;
2738 
2739  /*-----
2740  * Startup cost includes:
2741  * 1. Startup cost for underneath input * relation
2742  * 2. Cost of performing aggregation, per cost_agg()
2743  * 3. Startup cost for PathTarget eval
2744  *-----
2745  */
2746  startup_cost = ofpinfo->rel_startup_cost;
2747  startup_cost += aggcosts.transCost.startup;
2748  startup_cost += aggcosts.transCost.per_tuple * input_rows;
2749  startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
2750  startup_cost += ptarget->cost.startup;
2751 
2752  /*-----
2753  * Run time cost includes:
2754  * 1. Run time cost of underneath input relation
2755  * 2. Run time cost of performing aggregation, per cost_agg()
2756  * 3. PathTarget eval cost for each output row
2757  *-----
2758  */
2759  run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
2760  run_cost += aggcosts.finalCost * numGroups;
2761  run_cost += cpu_tuple_cost * numGroups;
2762  run_cost += ptarget->cost.per_tuple * numGroups;
2763  }
2764  else
2765  {
2766  /* Clamp retrieved rows estimates to at most foreignrel->tuples. */
2767  retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
2768 
2769  /*
2770  * Cost as though this were a seqscan, which is pessimistic. We
2771  * effectively imagine the local_conds are being evaluated
2772  * remotely, too.
2773  */
2774  startup_cost = 0;
2775  run_cost = 0;
2776  run_cost += seq_page_cost * foreignrel->pages;
2777 
2778  startup_cost += foreignrel->baserestrictcost.startup;
2779  cpu_per_tuple = cpu_tuple_cost + foreignrel->baserestrictcost.per_tuple;
2780  run_cost += cpu_per_tuple * foreignrel->tuples;
2781  }
2782 
2783  /*
2784  * Without remote estimates, we have no real way to estimate the cost
2785  * of generating sorted output. It could be free if the query plan
2786  * the remote side would have chosen generates properly-sorted output
2787  * anyway, but in most cases it will cost something. Estimate a value
2788  * high enough that we won't pick the sorted path when the ordering
2789  * isn't locally useful, but low enough that we'll err on the side of
2790  * pushing down the ORDER BY clause when it's useful to do so.
2791  */
2792  if (pathkeys != NIL)
2793  {
2794  startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
2795  run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
2796  }
2797 
2798  total_cost = startup_cost + run_cost;
2799  }
2800 
2801  /*
2802  * Cache the costs for scans without any pathkeys or parameterization
2803  * before adding the costs for transferring data from the foreign server.
2804  * These costs are useful for costing the join between this relation and
2805  * another foreign relation or to calculate the costs of paths with
2806  * pathkeys for this relation, when the costs can not be obtained from the
2807  * foreign server. This function will be called at least once for every
2808  * foreign relation without pathkeys and parameterization.
2809  */
2810  if (pathkeys == NIL && param_join_conds == NIL)
2811  {
2812  fpinfo->rel_startup_cost = startup_cost;
2813  fpinfo->rel_total_cost = total_cost;
2814  }
2815 
2816  /*
2817  * Add some additional cost factors to account for connection overhead
2818  * (fdw_startup_cost), transferring data across the network
2819  * (fdw_tuple_cost per retrieved row), and local manipulation of the data
2820  * (cpu_tuple_cost per retrieved row).
2821  */
2822  startup_cost += fpinfo->fdw_startup_cost;
2823  total_cost += fpinfo->fdw_startup_cost;
2824  total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
2825  total_cost += cpu_tuple_cost * retrieved_rows;
2826 
2827  /* Return results. */
2828  *p_rows = rows;
2829  *p_width = width;
2830  *p_startup_cost = startup_cost;
2831  *p_total_cost = total_cost;
2832 }
#define NIL
Definition: pg_list.h:69
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset)
Definition: selfuncs.c:3398
Query * parse
Definition: relation.h:155
void get_agg_clause_costs(PlannerInfo *root, Node *clause, AggSplit aggsplit, AggClauseCosts *costs)
Definition: clauses.c:467
bool hasAggs
Definition: parsenodes.h:123
#define Min(x, y)
Definition: c.h:802
#define IS_UPPER_REL(rel)
Definition: relation.h:571
#define IS_JOIN_REL(rel)
Definition: relation.h:566
void classifyConditions(PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds, List **local_conds)
Definition: deparse.c:200
List * list_copy(const List *oldlist)
Definition: list.c:1160
Definition: nodes.h:512
#define MemSet(start, val, len)
Definition: c.h:853
List * list_concat(List *list1, List *list2)
Definition: list.c:321
double Selectivity
Definition: nodes.h:642
QualCost transCost
Definition: relation.h:62
RelOptInfo * outerrel
Definition: postgres_fdw.h:89
Cost startup
Definition: relation.h:45
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
Cost per_tuple
Definition: relation.h:46
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:3672
PGconn * conn
Definition: streamutil.c:46
Selectivity local_conds_sel
Definition: postgres_fdw.h:54
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:157
Selectivity joinclause_sel
Definition: postgres_fdw.h:57
double cpu_operator_cost
Definition: costsize.c:108
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:925
Cost finalCost
Definition: relation.h:63
void * fdw_private
Definition: relation.h:637
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
#define Assert(condition)
Definition: c.h:670
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:54
QualCost cost
Definition: relation.h:974
static int list_length(const List *l)
Definition: pg_list.h:89
List * get_sortgrouplist_exprs(List *sgClauses, List *targetList)
Definition: tlist.c:395
double cpu_tuple_cost
Definition: costsize.c:106
static void get_remote_estimate(const char *sql, PGconn *conn, double *rows, int *width, Cost *startup_cost, Cost *total_cost)
RelOptInfo * innerrel
Definition: postgres_fdw.h:90
List * build_tlist_to_deparse(RelOptInfo *foreignrel)
Definition: deparse.c:868
List * groupClause
Definition: parsenodes.h:146
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:99
Node * havingQual
Definition: parsenodes.h:150
double clamp_row_est(double nrows)
Definition: costsize.c:177
double seq_page_cost
Definition: costsize.c:104
Definition: pg_list.h:45
double Cost
Definition: nodes.h:643
QualCost local_conds_cost
Definition: postgres_fdw.h:53
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: relation.h:278

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

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

3280 {
3282  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3283  int numParams = dmstate->numParams;
3284  const char **values = dmstate->param_values;
3285 
3286  /*
3287  * Construct array of query parameter values in text format.
3288  */
3289  if (numParams > 0)
3290  process_query_params(econtext,
3291  dmstate->param_flinfo,
3292  dmstate->param_exprs,
3293  values);
3294 
3295  /*
3296  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3297  * to infer types for all parameters. Since we explicitly cast every
3298  * parameter (see deparse.c), the "inference" is trivial and will produce
3299  * the desired result. This allows us to avoid assuming that the remote
3300  * server has the same OIDs we do for the parameters' types.
3301  */
3302  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
3303  NULL, values, NULL, NULL, 0))
3304  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
3305 
3306  /*
3307  * Get the result, and check for success.
3308  *
3309  * We don't use a PG_TRY block here, so be careful not to throw error
3310  * without releasing the PGresult.
3311  */
3312  dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
3313  if (PQresultStatus(dmstate->result) !=
3315  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
3316  dmstate->query);
3317 
3318  /* Get the number of rows affected. */
3319  if (dmstate->has_returning)
3320  dmstate->num_tuples = PQntuples(dmstate->result);
3321  else
3322  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
3323 }
ScanState ss
Definition: execnodes.h:1564
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:884
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:1113
#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 2992 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().

2993 {
2994  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
2995  PGresult *volatile res = NULL;
2996  MemoryContext oldcontext;
2997 
2998  /*
2999  * We'll store the tuples in the batch_cxt. First, flush the previous
3000  * batch.
3001  */
3002  fsstate->tuples = NULL;
3003  MemoryContextReset(fsstate->batch_cxt);
3004  oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3005 
3006  /* PGresult must be released before leaving this function. */
3007  PG_TRY();
3008  {
3009  PGconn *conn = fsstate->conn;
3010  char sql[64];
3011  int numrows;
3012  int i;
3013 
3014  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3015  fsstate->fetch_size, fsstate->cursor_number);
3016 
3017  res = pgfdw_exec_query(conn, sql);
3018  /* On error, report the original query, not the FETCH. */
3019  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3020  pgfdw_report_error(ERROR, res, conn, false, fsstate->query);
3021 
3022  /* Convert the data into HeapTuples */
3023  numrows = PQntuples(res);
3024  fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3025  fsstate->num_tuples = numrows;
3026  fsstate->next_tuple = 0;
3027 
3028  for (i = 0; i < numrows; i++)
3029  {
3030  Assert(IsA(node->ss.ps.plan, ForeignScan));
3031 
3032  fsstate->tuples[i] =
3034  fsstate->rel,
3035  fsstate->attinmeta,
3036  fsstate->retrieved_attrs,
3037  node,
3038  fsstate->temp_cxt);
3039  }
3040 
3041  /* Update fetch_ct_2 */
3042  if (fsstate->fetch_ct_2 < 2)
3043  fsstate->fetch_ct_2++;
3044 
3045  /* Must be EOF if we didn't get as many tuples as we asked for. */
3046  fsstate->eof_reached = (numrows < fsstate->fetch_size);
3047 
3048  PQclear(res);
3049  res = NULL;
3050  }
3051  PG_CATCH();
3052  {
3053  if (res)
3054  PQclear(res);
3055  PG_RE_THROW();
3056  }
3057  PG_END_TRY();
3058 
3059  MemoryContextSwitchTo(oldcontext);
3060 }
ScanState ss
Definition: execnodes.h:1564
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
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:135
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:1113
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:46
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
AttInMetadata * attinmeta
Definition: postgres_fdw.c:130
void * palloc0(Size size)
Definition: mcxt.c:877
MemoryContext temp_cxt
Definition: postgres_fdw.c:156
Plan * plan
Definition: execnodes.h:850
void PQclear(PGresult *res)
Definition: fe-exec.c:671
#define PG_CATCH()
Definition: elog.h:293
#define Assert(condition)
Definition: c.h:670
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 5154 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().

5155 {
5156  ListCell *lc_em;
5157 
5158  foreach(lc_em, ec->ec_members)
5159  {
5160  EquivalenceMember *em = lfirst(lc_em);
5161 
5162  if (bms_is_subset(em->em_relids, rel->relids))
5163  {
5164  /*
5165  * If there is more than one equivalence member whose Vars are
5166  * taken entirely from this relation, we'll be content to choose
5167  * any one of those.
5168  */
5169  return em->em_expr;
5170  }
5171  }
5172 
5173  /* We didn't find any suitable equivalence class expression */
5174  return NULL;
5175 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:308
Relids relids
Definition: relation.h:585
Relids em_relids
Definition: relation.h:911
#define lfirst(lc)
Definition: pg_list.h:106
List * ec_members
Definition: relation.h:862

◆ foreign_grouping_ok()

static bool foreign_grouping_ok ( PlannerInfo root,
RelOptInfo grouped_rel 
)
static

Definition at line 4591 of file postgres_fdw.c.

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

Referenced by add_foreign_grouping_paths().

4592 {
4593  Query *query = root->parse;
4594  PathTarget *grouping_target;
4595  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
4596  PgFdwRelationInfo *ofpinfo;
4597  List *aggvars;
4598  ListCell *lc;
4599  int i;
4600  List *tlist = NIL;
4601 
4602  /* Grouping Sets are not pushable */
4603  if (query->groupingSets)
4604  return false;
4605 
4606  /* Get the fpinfo of the underlying scan relation. */
4607  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
4608 
4609  /*
4610  * If underneath input relation has any local conditions, those conditions
4611  * are required to be applied before performing aggregation. Hence the
4612  * aggregate cannot be pushed down.
4613  */
4614  if (ofpinfo->local_conds)
4615  return false;
4616 
4617  /*
4618  * The targetlist expected from this node and the targetlist pushed down
4619  * to the foreign server may be different. The latter requires
4620  * sortgrouprefs to be set to push down GROUP BY clause, but should not
4621  * have those arising from ORDER BY clause. These sortgrouprefs may be
4622  * different from those in the plan's targetlist. Use a copy of path
4623  * target to record the new sortgrouprefs.
4624  */
4625  grouping_target = copy_pathtarget(root->upper_targets[UPPERREL_GROUP_AGG]);
4626 
4627  /*
4628  * Evaluate grouping targets and check whether they are safe to push down
4629  * to the foreign side. All GROUP BY expressions will be part of the
4630  * grouping target and thus there is no need to evaluate it separately.
4631  * While doing so, add required expressions into target list which can
4632  * then be used to pass to foreign server.
4633  */
4634  i = 0;
4635  foreach(lc, grouping_target->exprs)
4636  {
4637  Expr *expr = (Expr *) lfirst(lc);
4638  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
4639  ListCell *l;
4640 
4641  /* Check whether this expression is part of GROUP BY clause */
4642  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
4643  {
4644  /*
4645  * If any of the GROUP BY expression is not shippable we can not
4646  * push down aggregation to the foreign server.
4647  */
4648  if (!is_foreign_expr(root, grouped_rel, expr))
4649  return false;
4650 
4651  /* Pushable, add to tlist */
4652  tlist = add_to_flat_tlist(tlist, list_make1(expr));
4653  }
4654  else
4655  {
4656  /* Check entire expression whether it is pushable or not */
4657  if (is_foreign_expr(root, grouped_rel, expr))
4658  {
4659  /* Pushable, add to tlist */
4660  tlist = add_to_flat_tlist(tlist, list_make1(expr));
4661  }
4662  else
4663  {
4664  /*
4665  * If we have sortgroupref set, then it means that we have an
4666  * ORDER BY entry pointing to this expression. Since we are
4667  * not pushing ORDER BY with GROUP BY, clear it.
4668  */
4669  if (sgref)
4670  grouping_target->sortgrouprefs[i] = 0;
4671 
4672  /* Not matched exactly, pull the var with aggregates then */
4673  aggvars = pull_var_clause((Node *) expr,
4675 
4676  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
4677  return false;
4678 
4679  /*
4680  * Add aggregates, if any, into the targetlist. Plain var
4681  * nodes should be either same as some GROUP BY expression or
4682  * part of some GROUP BY expression. In later case, the query
4683  * cannot refer plain var nodes without the surrounding
4684  * expression. In both the cases, they are already part of
4685  * the targetlist and thus no need to add them again. In fact
4686  * adding pulled plain var nodes in SELECT clause will cause
4687  * an error on the foreign server if they are not same as some
4688  * GROUP BY expression.
4689  */
4690  foreach(l, aggvars)
4691  {
4692  Expr *expr = (Expr *) lfirst(l);
4693 
4694  if (IsA(expr, Aggref))
4695  tlist = add_to_flat_tlist(tlist, list_make1(expr));
4696  }
4697  }
4698  }
4699 
4700  i++;
4701  }
4702 
4703  /*
4704  * Classify the pushable and non-pushable having clauses and save them in
4705  * remote_conds and local_conds of the grouped rel's fpinfo.
4706  */
4707  if (root->hasHavingQual && query->havingQual)
4708  {
4709  ListCell *lc;
4710 
4711  foreach(lc, (List *) query->havingQual)
4712  {
4713  Expr *expr = (Expr *) lfirst(lc);
4714  RestrictInfo *rinfo;
4715 
4716  /*
4717  * Currently, the core code doesn't wrap havingQuals in
4718  * RestrictInfos, so we must make our own.
4719  */
4720  Assert(!IsA(expr, RestrictInfo));
4721  rinfo = make_restrictinfo(expr,
4722  true,
4723  false,
4724  false,
4725  root->qual_security_level,
4726  grouped_rel->relids,
4727  NULL,
4728  NULL);
4729  if (is_foreign_expr(root, grouped_rel, expr))
4730  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
4731  else
4732  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
4733  }
4734  }
4735 
4736  /*
4737  * If there are any local conditions, pull Vars and aggregates from it and
4738  * check whether they are safe to pushdown or not.
4739  */
4740  if (fpinfo->local_conds)
4741  {
4742  List *aggvars = NIL;
4743  ListCell *lc;
4744 
4745  foreach(lc, fpinfo->local_conds)
4746  {
4747  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
4748 
4749  aggvars = list_concat(aggvars,
4750  pull_var_clause((Node *) rinfo->clause,
4752  }
4753 
4754  foreach(lc, aggvars)
4755  {
4756  Expr *expr = (Expr *) lfirst(lc);
4757 
4758  /*
4759  * If aggregates within local conditions are not safe to push
4760  * down, then we cannot push down the query. Vars are already
4761  * part of GROUP BY clause which are checked above, so no need to
4762  * access them again here.
4763  */
4764  if (IsA(expr, Aggref))
4765  {
4766  if (!is_foreign_expr(root, grouped_rel, expr))
4767  return false;
4768 
4769  tlist = add_to_flat_tlist(tlist, list_make1(expr));
4770  }
4771  }
4772  }
4773 
4774  /* Transfer any sortgroupref data to the replacement tlist */
4775  apply_pathtarget_labeling_to_tlist(tlist, grouping_target);
4776 
4777  /* Store generated targetlist */
4778  fpinfo->grouped_tlist = tlist;
4779 
4780  /* Safe to pushdown */
4781  fpinfo->pushdown_safe = true;
4782 
4783  /*
4784  * Set cached relation costs to some negative value, so that we can detect
4785  * when they are set to some sensible costs, during one (usually the
4786  * first) of the calls to estimate_path_cost_size().
4787  */
4788  fpinfo->rel_startup_cost = -1;
4789  fpinfo->rel_total_cost = -1;
4790 
4791  /*
4792  * Set the string describing this grouped relation to be used in EXPLAIN
4793  * output of corresponding ForeignScan.
4794  */
4795  fpinfo->relation_name = makeStringInfo();
4796  appendStringInfo(fpinfo->relation_name, "Aggregate on (%s)",
4797  ofpinfo->relation_name->data);
4798 
4799  return true;
4800 }
#define NIL
Definition: pg_list.h:69
PathTarget * copy_pathtarget(PathTarget *src)
Definition: tlist.c:629
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
Query * parse
Definition: relation.h:155
RestrictInfo * make_restrictinfo(Expr *clause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids)
Definition: restrictinfo.c:57
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
List * groupingSets
Definition: parsenodes.h:148
Definition: nodes.h:512
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
Index * sortgrouprefs
Definition: relation.h:973
Relids relids
Definition: relation.h:585
#define get_pathtarget_sortgroupref(target, colno)
Definition: relation.h:979
List * lappend(List *list, void *datum)
Definition: list.c:128
Expr * clause
Definition: relation.h:1841
List * exprs
Definition: relation.h:972
void apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target)
Definition: tlist.c:736
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:446
unsigned int Index
Definition: c.h:413
StringInfo relation_name
Definition: postgres_fdw.h:86
void * fdw_private
Definition: relation.h:637
#define Assert(condition)
Definition: c.h:670
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition: tlist.c:135
#define lfirst(lc)
Definition: pg_list.h:106
Index qual_security_level
Definition: relation.h:297
List * groupClause
Definition: parsenodes.h:146
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:226
int i
bool hasHavingQual
Definition: relation.h:305
Node * havingQual
Definition: parsenodes.h:150
Definition: pg_list.h:45
struct PathTarget * upper_targets[UPPERREL_FINAL+1]
Definition: relation.h:278

◆ foreign_join_ok()

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

Definition at line 4062 of file postgres_fdw.c.

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

Referenced by postgresGetForeignJoinPaths().

4065 {
4066  PgFdwRelationInfo *fpinfo;
4067  PgFdwRelationInfo *fpinfo_o;
4068  PgFdwRelationInfo *fpinfo_i;
4069  ListCell *lc;
4070  List *joinclauses;
4071 
4072  /*
4073  * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
4074  * Constructing queries representing SEMI and ANTI joins is hard, hence
4075  * not considered right now.
4076  */
4077  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
4078  jointype != JOIN_RIGHT && jointype != JOIN_FULL)
4079  return false;
4080 
4081  /*
4082  * If either of the joining relations is marked as unsafe to pushdown, the
4083  * join can not be pushed down.
4084  */
4085  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
4086  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
4087  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
4088  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
4089  !fpinfo_i || !fpinfo_i->pushdown_safe)
4090  return false;
4091 
4092  /*
4093  * If joining relations have local conditions, those conditions are
4094  * required to be applied before joining the relations. Hence the join can
4095  * not be pushed down.
4096  */
4097  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
4098  return false;
4099 
4100  /*
4101  * Merge FDW options. We might be tempted to do this after we have deemed
4102  * the foreign join to be OK. But we must do this beforehand so that we
4103  * know which quals can be evaluated on the foreign server, which might
4104  * depend on shippable_extensions.
4105  */
4106  fpinfo->server = fpinfo_o->server;
4107  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
4108 
4109  /*
4110  * Separate restrict list into join quals and pushed-down (other) quals.
4111  *
4112  * Join quals belonging to an outer join must all be shippable, else we
4113  * cannot execute the join remotely. Add such quals to 'joinclauses'.
4114  *
4115  * Add other quals to fpinfo->remote_conds if they are shippable, else to
4116  * fpinfo->local_conds. In an inner join it's okay to execute conditions
4117  * either locally or remotely; the same is true for pushed-down conditions
4118  * at an outer join.
4119  *
4120  * Note we might return failure after having already scribbled on
4121  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
4122  * won't consult those lists again if we deem the join unshippable.
4123  */
4124  joinclauses = NIL;
4125  foreach(lc, extra->restrictlist)
4126  {
4127  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
4128  bool is_remote_clause = is_foreign_expr(root, joinrel,
4129  rinfo->clause);
4130 
4131  if (IS_OUTER_JOIN(jointype) && !rinfo->is_pushed_down)
4132  {
4133  if (!is_remote_clause)
4134  return false;
4135  joinclauses = lappend(joinclauses, rinfo);
4136  }
4137  else
4138  {
4139  if (is_remote_clause)
4140  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
4141  else
4142  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
4143  }
4144  }
4145 
4146  /*
4147  * deparseExplicitTargetList() isn't smart enough to handle anything other
4148  * than a Var. In particular, if there's some PlaceHolderVar that would
4149  * need to be evaluated within this join tree (because there's an upper
4150  * reference to a quantity that may go to NULL as a result of an outer
4151  * join), then we can't try to push the join down because we'll fail when
4152  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
4153  * needs to be evaluated *at the top* of this join tree is OK, because we
4154  * can do that locally after fetching the results from the remote side.
4155  */
4156  foreach(lc, root->placeholder_list)
4157  {
4158  PlaceHolderInfo *phinfo = lfirst(lc);
4159  Relids relids = joinrel->relids;
4160 
4161  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
4162  bms_nonempty_difference(relids, phinfo->ph_eval_at))
4163  return false;
4164  }
4165 
4166  /* Save the join clauses, for later use. */
4167  fpinfo->joinclauses = joinclauses;
4168 
4169  fpinfo->outerrel = outerrel;
4170  fpinfo->innerrel = innerrel;
4171  fpinfo->jointype = jointype;
4172 
4173  /*
4174  * By default, both the input relations are not required to be deparsed as
4175  * subqueries, but there might be some relations covered by the input
4176  * relations that are required to be deparsed as subqueries, so save the
4177  * relids of those relations for later use by the deparser.
4178  */
4179  fpinfo->make_outerrel_subquery = false;
4180  fpinfo->make_innerrel_subquery = false;
4181  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
4182  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
4184  fpinfo_i->lower_subquery_rels);
4185 
4186  /*
4187  * Pull the other remote conditions from the joining relations into join
4188  * clauses or other remote clauses (remote_conds) of this relation
4189  * wherever possible. This avoids building subqueries at every join step.
4190  *
4191  * For an inner join, clauses from both the relations are added to the
4192  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
4193  * the outer side are added to remote_conds since those can be evaluated
4194  * after the join is evaluated. The clauses from inner side are added to
4195  * the joinclauses, since they need to be evaluated while constructing the
4196  * join.
4197  *
4198  * For a FULL OUTER JOIN, the other clauses from either relation can not
4199  * be added to the joinclauses or remote_conds, since each relation acts
4200  * as an outer relation for the other.
4201  *
4202  * The joining sides can not have local conditions, thus no need to test
4203  * shippability of the clauses being pulled up.
4204  */
4205  switch (jointype)
4206  {
4207  case JOIN_INNER:
4208  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4209  list_copy(fpinfo_i->remote_conds));
4210  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4211  list_copy(fpinfo_o->remote_conds));
4212  break;
4213 
4214  case JOIN_LEFT:
4215  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
4216  list_copy(fpinfo_i->remote_conds));
4217  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4218  list_copy(fpinfo_o->remote_conds));
4219  break;
4220 
4221  case JOIN_RIGHT:
4222  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
4223  list_copy(fpinfo_o->remote_conds));
4224  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
4225  list_copy(fpinfo_i->remote_conds));
4226  break;
4227 
4228  case JOIN_FULL:
4229 
4230  /*
4231  * In this case, if any of the input relations has conditions, we
4232  * need to deparse that relation as a subquery so that the
4233  * conditions can be evaluated before the join. Remember it in
4234  * the fpinfo of this relation so that the deparser can take
4235  * appropriate action. Also, save the relids of base relations
4236  * covered by that relation for later use by the deparser.
4237  */
4238  if (fpinfo_o->remote_conds)
4239  {
4240  fpinfo->make_outerrel_subquery = true;
4241  fpinfo->lower_subquery_rels =
4243  outerrel->relids);
4244  }
4245  if (fpinfo_i->remote_conds)
4246  {
4247  fpinfo->make_innerrel_subquery = true;
4248  fpinfo->lower_subquery_rels =
4250  innerrel->relids);
4251  }
4252  break;
4253 
4254  default:
4255  /* Should not happen, we have just checked this above */
4256  elog(ERROR, "unsupported join type %d", jointype);
4257  }
4258 
4259  /*
4260  * For an inner join, all restrictions can be treated alike. Treating the
4261  * pushed down conditions as join conditions allows a top level full outer
4262  * join to be deparsed without requiring subqueries.
4263  */
4264  if (jointype == JOIN_INNER)
4265  {
4266  Assert(!fpinfo->joinclauses);
4267  fpinfo->joinclauses = fpinfo->remote_conds;
4268  fpinfo->remote_conds = NIL;
4269  }
4270 
4271  /* Mark that this join can be pushed down safely */
4272  fpinfo->pushdown_safe = true;
4273 
4274  /* Get user mapping */
4275  if (fpinfo->use_remote_estimate)
4276  {
4277  if (fpinfo_o->use_remote_estimate)
4278  fpinfo->user = fpinfo_o->user;
4279  else
4280  fpinfo->user = fpinfo_i->user;
4281  }
4282  else
4283  fpinfo->user = NULL;
4284 
4285  /*
4286  * Set cached relation costs to some negative value, so that we can detect
4287  * when they are set to some sensible costs, during one (usually the
4288  * first) of the calls to estimate_path_cost_size().
4289  */
4290  fpinfo->rel_startup_cost = -1;
4291  fpinfo->rel_total_cost = -1;
4292 
4293  /*
4294  * Set the string describing this join relation to be used in EXPLAIN
4295  * output of corresponding ForeignScan.
4296  */
4297  fpinfo->relation_name = makeStringInfo();
4298  appendStringInfo(fpinfo->relation_name, "(%s) %s JOIN (%s)",
4299  fpinfo_o->relation_name->data,
4300  get_jointype_name(fpinfo->jointype),
4301  fpinfo_i->relation_name->data);
4302 
4303  /*
4304  * Set the relation index. This is defined as the position of this
4305  * joinrel in the join_rel_list list plus the length of the rtable list.
4306  * Note that since this joinrel is at the end of the join_rel_list list
4307  * when we are called, we can get the position by list_length.
4308  */
4309  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
4310  fpinfo->relation_index =
4312 
4313  return true;
4314 }
#define NIL
Definition: pg_list.h:69
Query * parse
Definition: relation.h:155
Relids ph_eval_at
Definition: relation.h:2160
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
ForeignServer * server
Definition: postgres_fdw.h:76
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:725
List * list_copy(const List *oldlist)
Definition: list.c:1160
Relids lower_subquery_rels
Definition: postgres_fdw.h:103
List * join_rel_list
Definition: relation.h:215
List * list_concat(List *list1, List *list2)
Definition: list.c:321
RelOptInfo * outerrel
Definition: postgres_fdw.h:89
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
bool make_outerrel_subquery
Definition: postgres_fdw.h:99
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:308
#define lfirst_node(type, lc)
Definition: pg_list.h:109
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1311
Relids relids
Definition: relation.h:585
List * lappend(List *list, void *datum)
Definition: list.c:128
Expr * clause
Definition: relation.h:1841
UserMapping * user
Definition: postgres_fdw.h:77
List * restrictlist
Definition: relation.h:2276
bool is_pushed_down
Definition: relation.h:1843
StringInfo relation_name
Definition: postgres_fdw.h:86
void * fdw_private
Definition: relation.h:637
#define Assert(condition)
Definition: c.h:670
#define lfirst(lc)
Definition: pg_list.h:106
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:218
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:226
List * placeholder_list
Definition: relation.h:258
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:755
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:494

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

2842 {
2843  PGresult *volatile res = NULL;
2844 
2845  /* PGresult must be released before leaving this function. */
2846  PG_TRY();
2847  {
2848  char *line;
2849  char *p;
2850  int n;
2851 
2852  /*
2853  * Execute EXPLAIN remotely.
2854  */
2855  res = pgfdw_exec_query(conn, sql);
2856  if (PQresultStatus(res) != PGRES_TUPLES_OK)
2857  pgfdw_report_error(ERROR, res, conn, false, sql);
2858 
2859  /*
2860  * Extract cost numbers for topmost plan node. Note we search for a
2861  * left paren from the end of the line to avoid being confused by
2862  * other uses of parentheses.
2863  */
2864  line = PQgetvalue(res, 0, 0);
2865  p = strrchr(line, '(');
2866  if (p == NULL)
2867  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
2868  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
2869  startup_cost, total_cost, rows, width);
2870  if (n != 4)
2871  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
2872 
2873  PQclear(res);
2874  res = NULL;
2875  }
2876  PG_CATCH();
2877  {
2878  if (res)
2879  PQclear(res);
2880  PG_RE_THROW();
2881  }
2882  PG_END_TRY();
2883 }
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 3329 of file postgres_fdw.c.

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

3330 {
3332  EState *estate = node->ss.ps.state;
3333  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
3334  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
3335 
3336  Assert(resultRelInfo->ri_projectReturning);
3337 
3338  /* If we didn't get any tuples, must be end of data. */
3339  if (dmstate->next_tuple >= dmstate->num_tuples)
3340  return ExecClearTuple(slot);
3341 
3342  /* Increment the command es_processed count if necessary. */
3343  if (dmstate->set_processed)
3344  estate->es_processed += 1;
3345 
3346  /*
3347  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
3348  * tuple. (has_returning is false when the local query is of the form
3349  * "UPDATE/DELETE .. RETURNING 1" for example.)
3350  */
3351  if (!dmstate->has_returning)
3352  ExecStoreAllNullTuple(slot);
3353  else
3354  {
3355  /*
3356  * On error, be sure to release the PGresult on the way out. Callers
3357  * do not have PG_TRY blocks to ensure this happens.
3358  */
3359  PG_TRY();
3360  {
3361  HeapTuple newtup;
3362 
3363  newtup = make_tuple_from_result_row(dmstate->result,
3364  dmstate->next_tuple,
3365  dmstate->rel,
3366  dmstate->attinmeta,
3367  dmstate->retrieved_attrs,
3368  NULL,
3369  dmstate->temp_cxt);
3370  ExecStoreTuple(newtup, slot, InvalidBuffer, false);
3371  }
3372  PG_CATCH();
3373  {
3374  if (dmstate->result)
3375  PQclear(dmstate->result);
3376  PG_RE_THROW();
3377  }
3378  PG_END_TRY();
3379  }
3380  dmstate->next_tuple++;
3381 
3382  /* Make slot available for evaluation of the local query RETURNING list. */
3383  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple = slot;
3384 
3385  return slot;
3386 }
ScanState ss
Definition: execnodes.h:1564
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:320
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:512
AttInMetadata * attinmeta
Definition: postgres_fdw.c:194
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:439
#define InvalidBuffer
Definition: buf.h:25
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1116
EState * state
Definition: execnodes.h:852
PlanState ps
Definition: execnodes.h:1113
MemoryContext temp_cxt
Definition: postgres_fdw.c:215
void PQclear(PGresult *res)
Definition: fe-exec.c:671
#define PG_CATCH()
Definition: elog.h:293
#define Assert(condition)
Definition: c.h:670
#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 682 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().

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

◆ get_useful_pathkeys_for_relation()

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

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

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

◆ 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 4913 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, ForeignScanState::fdw_state, 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, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleData::t_self, tidin(), PgFdwScanState::tupdesc, and values.

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

4920 {
4921  HeapTuple tuple;
4922  TupleDesc tupdesc;
4923  Datum *values;
4924  bool *nulls;
4925  ItemPointer ctid = NULL;
4926  Oid oid = InvalidOid;
4927  ConversionLocation errpos;
4928  ErrorContextCallback errcallback;
4929  MemoryContext oldcontext;
4930  ListCell *lc;
4931  int j;
4932 
4933  Assert(row < PQntuples(res));
4934 
4935  /*
4936  * Do the following work in a temp context that we reset after each tuple.
4937  * This cleans up not only the data we have direct access to, but any
4938  * cruft the I/O functions might leak.
4939  */
4940  oldcontext = MemoryContextSwitchTo(temp_context);
4941 
4942  if (rel)
4943  tupdesc = RelationGetDescr(rel);
4944  else
4945  {
4946  PgFdwScanState *fdw_sstate;
4947 
4948  Assert(fsstate);
4949  fdw_sstate = (PgFdwScanState *) fsstate->fdw_state;
4950  tupdesc = fdw_sstate->tupdesc;
4951  }
4952 
4953  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
4954  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
4955  /* Initialize to nulls for any columns not present in result */
4956  memset(nulls, true, tupdesc->natts * sizeof(bool));
4957 
4958  /*
4959  * Set up and install callback to report where conversion error occurs.
4960  */
4961  errpos.rel = rel;
4962  errpos.cur_attno = 0;
4963  errpos.fsstate = fsstate;
4964  errcallback.callback = conversion_error_callback;
4965  errcallback.arg = (void *) &errpos;
4966  errcallback.previous = error_context_stack;
4967  error_context_stack = &errcallback;
4968 
4969  /*
4970  * i indexes columns in the relation, j indexes columns in the PGresult.
4971  */
4972  j = 0;
4973  foreach(lc, retrieved_attrs)
4974  {
4975  int i = lfirst_int(lc);
4976  char *valstr;
4977 
4978  /* fetch next column's textual value */
4979  if (PQgetisnull(res, row, j))
4980  valstr = NULL;
4981  else
4982  valstr = PQgetvalue(res, row, j);
4983 
4984  /*
4985  * convert value to internal representation
4986  *
4987  * Note: we ignore system columns other than ctid and oid in result
4988  */
4989  errpos.cur_attno = i;
4990  if (i > 0)
4991  {
4992  /* ordinary column */
4993  Assert(i <= tupdesc->natts);
4994  nulls[i - 1] = (valstr == NULL);
4995  /* Apply the input function even to nulls, to support domains */
4996  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
4997  valstr,
4998  attinmeta->attioparams[i - 1],
4999  attinmeta->atttypmods[i - 1]);
5000  }
5001  else if (i == SelfItemPointerAttributeNumber)
5002  {
5003  /* ctid */
5004  if (valstr != NULL)
5005  {
5006  Datum datum;
5007 
5008  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
5009  ctid = (ItemPointer) DatumGetPointer(datum);
5010  }
5011  }
5012  else if (i == ObjectIdAttributeNumber)
5013  {
5014  /* oid */
5015  if (valstr != NULL)
5016  {
5017  Datum datum;
5018 
5019  datum = DirectFunctionCall1(oidin, CStringGetDatum(valstr));
5020  oid = DatumGetObjectId(datum);
5021  }
5022  }
5023  errpos.cur_attno = 0;
5024 
5025  j++;
5026  }
5027 
5028  /* Uninstall error context callback. */
5029  error_context_stack = errcallback.previous;
5030 
5031  /*
5032  * Check we got the expected number of columns. Note: j == 0 and
5033  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
5034  */
5035  if (j > 0 && j != PQnfields(res))
5036  elog(ERROR, "remote query result does not match the foreign table");
5037 
5038  /*
5039  * Build the result tuple in caller's memory context.
5040  */
5041  MemoryContextSwitchTo(oldcontext);
5042 
5043  tuple = heap_form_tuple(tupdesc, values, nulls);
5044 
5045  /*
5046  * If we have a CTID to return, install it in both t_self and t_ctid.
5047  * t_self is the normal place, but if the tuple is converted to a
5048  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
5049  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
5050  */
5051  if (ctid)
5052  tuple->t_self = tuple->t_data->t_ctid = *ctid;
5053 
5054  /*
5055  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
5056  * heap_form_tuple. heap_form_tuple actually creates the tuple with
5057  * DatumTupleFields, not HeapTupleFields, but the executor expects
5058  * HeapTupleFields and will happily extract system columns on that
5059  * assumption. If we don't do this then, for example, the tuple length
5060  * ends up in the xmin field, which isn't what we want.
5061  */
5065 
5066  /*
5067  * If we have an OID to return, install it.
5068  */
5069  if (OidIsValid(oid))
5070  HeapTupleSetOid(tuple, oid);
5071 
5072  /* Clean up */
5073  MemoryContextReset(temp_context);
5074 
5075  return tuple;
5076 }
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2732
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3118
#define RelationGetDescr(relation)
Definition: rel.h:437
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
#define DatumGetObjectId(X)
Definition: postgres.h:506
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int32 * atttypmods
Definition: funcapi.h:48
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:695
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:585
unsigned int Oid
Definition: postgres_ext.h:31
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
void(* callback)(void *arg)
Definition: elog.h:239
struct ErrorContextCallback * previous
Definition: elog.h:238
#define OidIsValid(objectId)
Definition: c.h:576
Oid * attioparams
Definition: funcapi.h:45
int natts
Definition: tupdesc.h:79
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:703
ErrorContextCallback * error_context_stack
Definition: elog.c:88
ForeignScanState * fsstate
Definition: postgres_fdw.c:256
#define ERROR
Definition: elog.h:43
#define lfirst_int(lc)
Definition: pg_list.h:107
ItemPointerData t_ctid
Definition: htup_details.h:155
ItemPointerData t_self
Definition: htup.h:65
#define CStringGetDatum(X)
Definition: postgres.h:584
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:379
#define InvalidTransactionId
Definition: transam.h:31
void * palloc0(Size size)
Definition: mcxt.c:877
static void conversion_error_callback(void *arg)
uintptr_t Datum
Definition: postgres.h:372
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1618
#define InvalidOid
Definition: postgres_ext.h:36
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:53
#define Assert(condition)
Definition: c.h:670
TupleDesc tupdesc
Definition: postgres_fdw.c:129
#define DatumGetPointer(X)
Definition: postgres.h:555
static Datum values[MAXATTR]
Definition: bootstrap.c:164
void * palloc(Size size)
Definition: mcxt.c:848
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:248
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 4409 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().

4412 {
4413  /* We must always have fpinfo_o. */
4414  Assert(fpinfo_o);
4415 
4416  /* fpinfo_i may be NULL, but if present the servers must both match. */
4417  Assert(!fpinfo_i ||
4418  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
4419 
4420  /*
4421  * Copy the server specific FDW options. (For a join, both relations come
4422  * from the same server, so the server options should have the same value
4423  * for both relations.)
4424  */
4425  fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
4426  fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
4427  fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
4428  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
4429  fpinfo->fetch_size = fpinfo_o->fetch_size;
4430 
4431  /* Merge the table level options from either side of the join. */
4432  if (fpinfo_i)
4433  {
4434  /*
4435  * We'll prefer to use remote estimates for this join if any table
4436  * from either side of the join is using remote estimates. This is
4437  * most likely going to be preferred since they're already willing to
4438  * pay the price of a round trip to get the remote EXPLAIN. In any
4439  * case it's not entirely clear how we might otherwise handle this
4440  * best.
4441  */
4442  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
4443  fpinfo_i->use_remote_estimate;
4444 
4445  /*
4446  * Set fetch size to maximum of the joining sides, since we are
4447  * expecting the rows returned by the join to be proportional to the
4448  * relation sizes.
4449  */
4450  fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
4451  }
4452 }
ForeignServer * server
Definition: postgres_fdw.h:76
#define Max(x, y)
Definition: c.h:796
#define Assert(condition)
Definition: c.h:670
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 429 of file postgres_fdw.c.

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

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

◆ postgresAcquireSampleRowsFunc()

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

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

3560 {
3561  PgFdwAnalyzeState astate;
3562  ForeignTable *table;
3563  ForeignServer *server;
3564  UserMapping *user;
3565  PGconn *conn;
3566  unsigned int cursor_number;
3567  StringInfoData sql;
3568  PGresult *volatile res = NULL;
3569 
3570  /* Initialize workspace state */
3571  astate.rel = relation;
3573 
3574  astate.rows = rows;
3575  astate.targrows = targrows;
3576  astate.numrows = 0;
3577  astate.samplerows = 0;
3578  astate.rowstoskip = -1; /* -1 means not set yet */
3579  reservoir_init_selection_state(&astate.rstate, targrows);
3580 
3581  /* Remember ANALYZE context, and create a per-tuple temp context */
3582  astate.anl_cxt = CurrentMemoryContext;
3584  "postgres_fdw temporary data",
3586 
3587  /*
3588  * Get the connection to use. We do the remote access as the table's
3589  * owner, even if the ANALYZE was started by some other user.
3590  */
3591  table = GetForeignTable(RelationGetRelid(relation));
3592  server = GetForeignServer(table->serverid);
3593  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
3594  conn = GetConnection(user, false);
3595 
3596  /*
3597  * Construct cursor that retrieves whole rows from remote.
3598  */
3599  cursor_number = GetCursorNumber(conn);
3600  initStringInfo(&sql);
3601  appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
3602  deparseAnalyzeSql(&sql, relation, &astate.retrieved_attrs);
3603 
3604  /* In what follows, do not risk leaking any PGresults. */
3605  PG_TRY();
3606  {
3607  res = pgfdw_exec_query(conn, sql.data);
3608  if (PQresultStatus(res) != PGRES_COMMAND_OK)
3609  pgfdw_report_error(ERROR, res, conn, false, sql.data);
3610  PQclear(res);
3611  res = NULL;
3612 
3613  /* Retrieve and process rows a batch at a time. */
3614  for (;;)
3615  {
3616  char fetch_sql[64];
3617  int fetch_size;
3618  int numrows;
3619  int i;
3620  ListCell *lc;
3621 
3622  /* Allow users to cancel long query */
3624 
3625  /*
3626  * XXX possible future improvement: if rowstoskip is large, we
3627  * could issue a MOVE rather than physically fetching the rows,
3628  * then just adjust rowstoskip and samplerows appropriately.
3629  */
3630 
3631  /* The fetch size is arbitrary, but shouldn't be enormous. */
3632  fetch_size = 100;
3633  foreach(lc, server->options)
3634  {
3635  DefElem *def = (DefElem *) lfirst(lc);
3636 
3637  if (strcmp(def->defname, "fetch_size") == 0)
3638  {
3639  fetch_size = strtol(defGetString(def), NULL, 10);
3640  break;
3641  }
3642  }
3643  foreach(lc, table->options)
3644  {
3645  DefElem *def = (DefElem *) lfirst(lc);
3646 
3647  if (strcmp(def->defname, "fetch_size") == 0)
3648  {
3649  fetch_size = strtol(defGetString(def), NULL, 10);
3650  break;
3651  }
3652  }
3653 
3654  /* Fetch some rows */
3655  snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
3656  fetch_size, cursor_number);
3657 
3658  res = pgfdw_exec_query(conn, fetch_sql);
3659  /* On error, report the original query, not the FETCH. */
3660  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3661  pgfdw_report_error(ERROR, res, conn, false, sql.data);
3662 
3663  /* Process whatever we got. */
3664  numrows = PQntuples(res);
3665  for (i = 0; i < numrows; i++)
3666  analyze_row_processor(res, i, &astate);
3667 
3668  PQclear(res);
3669  res = NULL;
3670 
3671  /* Must be EOF if we didn't get all the rows requested. */
3672  if (numrows < fetch_size)
3673  break;
3674  }
3675 
3676  /* Close the cursor, just to be tidy. */
3677  close_cursor(conn, cursor_number);
3678  }
3679  PG_CATCH();
3680  {
3681  if (res)
3682  PQclear(res);
3683  PG_RE_THROW();
3684  }
3685  PG_END_TRY();
3686 
3687  ReleaseConnection(conn);
3688 
3689  /* We assume that we have no dead tuple. */
3690  *totaldeadrows = 0.0;
3691 
3692  /* We've retrieved all living tuples from foreign server. */
3693  *totalrows = astate.samplerows;
3694 
3695  /*
3696  * Emit some interesting relation info
3697  */
3698  ereport(elevel,
3699  (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
3700  RelationGetRelationName(relation),
3701  astate.samplerows, astate.numrows)));
3702 
3703  return astate.numrows;
3704 }
HeapTuple * rows
Definition: postgres_fdw.c:228
#define RelationGetDescr(relation)
Definition: rel.h:437
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:180
uint64 fetch_size
Definition: logging.c:21
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
void reservoir_init_selection_state(ReservoirState rs, int n)
Definition: sampling.c:129
Form_pg_class rd_rel
Definition: rel.h:114
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
static void close_cursor(PGconn *conn, unsigned int cursor_number)
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:46
char * defGetString(DefElem *def)
Definition: define.c:49
#define RelationGetRelationName(relation)
Definition: rel.h:445
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
static unsigned int cursor_number
Definition: connection.c:69
#define ereport(elevel, rest)
Definition: elog.h:122
ReservoirStateData rstate
Definition: postgres_fdw.c:235
void deparseAnalyzeSql(StringInfo buf, Relation rel, List **retrieved_attrs)
Definition: deparse.c:1838
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
AttInMetadata * attinmeta
Definition: postgres_fdw.c:224
static int elevel
Definition: vacuumlazy.c:136
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:342
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:93
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1069
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:239
#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:238
#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:719
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define PG_TRY()
Definition: elog.h:284
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
List * options
Definition: foreign.h:53
#define RelationGetRelid(relation)
Definition: rel.h:425
#define PG_END_TRY()
Definition: elog.h:300

◆ postgresAddForeignUpdateTargets()

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

Definition at line 1505 of file postgres_fdw.c.

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

Referenced by postgres_fdw_handler().

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

◆ postgresAnalyzeForeignTable()

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

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

3481 {
3482  ForeignTable *table;
3483  UserMapping *user;
3484  PGconn *conn;
3485  StringInfoData sql;
3486  PGresult *volatile res = NULL;
3487 
3488  /* Return the row-analysis function pointer */
3490 
3491  /*
3492  * Now we have to get the number of pages. It's annoying that the ANALYZE
3493  * API requires us to return that now, because it forces some duplication
3494  * of effort between this routine and postgresAcquireSampleRowsFunc. But
3495  * it's probably not worth redefining that API at this point.
3496  */
3497 
3498  /*
3499  * Get the connection to use. We do the remote access as the table's
3500  * owner, even if the ANALYZE was started by some other user.
3501  */
3502  table = GetForeignTable(RelationGetRelid(relation));
3503  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
3504  conn = GetConnection(user, false);
3505 
3506  /*
3507  * Construct command to get page count for relation.
3508  */
3509  initStringInfo(&sql);
3510  deparseAnalyzeSizeSql(&sql, relation);
3511 
3512  /* In what follows, do not risk leaking any PGresults. */
3513  PG_TRY();
3514  {
3515  res = pgfdw_exec_query(conn, sql.data);
3516  if (PQresultStatus(res) != PGRES_TUPLES_OK)
3517  pgfdw_report_error(ERROR, res, conn, false, sql.data);
3518 
3519  if (PQntuples(res) != 1 || PQnfields(res) != 1)
3520  elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
3521  *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
3522 
3523  PQclear(res);
3524  res = NULL;
3525  }
3526  PG_CATCH();
3527  {
3528  if (res)
3529  PQclear(res);
3530  PG_RE_THROW();
3531  }
3532  PG_END_TRY();
3533 
3534  ReleaseConnection(conn);
3535 
3536  return true;
3537 }
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2732
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3118
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
Form_pg_class rd_rel
Definition: rel.h:114
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2647
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
#define ERROR
Definition: elog.h:43
PGconn * conn
Definition: streamutil.c:46
static int postgresAcquireSampleRowsFunc(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:596
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
void PQclear(PGresult *res)
Definition: fe-exec.c:671
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
#define PG_CATCH()
Definition: elog.h:293
void deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
Definition: deparse.c:1818
Oid serverid
Definition: foreign.h:67
#define PG_RE_THROW()
Definition: elog.h:314
static char * user
Definition: pg_regress.c:93
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:166
#define elog
Definition: elog.h:219
#define PG_TRY()
Definition: elog.h:284
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508
#define RelationGetRelid(relation)
Definition: rel.h:425
#define PG_END_TRY()
Definition: elog.h:300

◆ postgresBeginDirectModify()

static void postgresBeginDirectModify ( ForeignScanState node,
int  eflags 
)
static

Definition at line 2267 of file postgres_fdw.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate(), EXEC_FLAG_EXPLAIN_ONLY, ForeignScan::fdw_exprs, ForeignScan::fdw_private, ForeignScanState::fdw_state, FdwDirectModifyPrivateHasReturning, FdwDirectModifyPrivateRetrievedAttrs, FdwDirectModifyPrivateSetProcessed, FdwDirectModifyPrivateUpdateSql, GetConnection(), GetForeignTable(), GetUserId(), GetUserMapping(), 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, PlanState::state, strVal, TupleDescGetAttInMetadata(), and user.

Referenced by postgres_fdw_handler().

2268 {
2269  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
2270  EState *estate = node->ss.ps.state;
2271  PgFdwDirectModifyState *dmstate;
2272  RangeTblEntry *rte;
2273  Oid userid;
2274  ForeignTable *table;
2275  UserMapping *user;
2276  int numParams;
2277 
2278  /*
2279  * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
2280  */
2281  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2282  return;
2283 
2284  /*
2285  * We'll save private state in node->fdw_state.
2286  */
2287  dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
2288  node->fdw_state = (void *) dmstate;
2289 
2290  /*
2291  * Identify which user to do the remote access as. This should match what
2292  * ExecCheckRTEPerms() does.
2293  */
2294  rte = rt_fetch(fsplan->scan.scanrelid, estate->es_range_table);
2295  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
2296 
2297  /* Get info about foreign table. */
2298  dmstate->rel = node->ss.ss_currentRelation;
2299  table = GetForeignTable(RelationGetRelid(dmstate->rel));
2300  user = GetUserMapping(userid, table->serverid);
2301 
2302  /*
2303  * Get connection to the foreign server. Connection manager will
2304  * establish new connection if necessary.
2305  */
2306  dmstate->conn = GetConnection(user, false);
2307 
2308  /* Initialize state variable */
2309  dmstate->num_tuples = -1; /* -1 means not set yet */
2310 
2311  /* Get private info created by planner functions. */
2312  dmstate->query = strVal(list_nth(fsplan->fdw_private,
2314  dmstate->has_returning = intVal(list_nth(fsplan->fdw_private,
2316  dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
2318  dmstate->set_processed = intVal(list_nth(fsplan->fdw_private,
2320 
2321  /* Create context for per-tuple temp workspace. */
2322  dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
2323  "postgres_fdw temporary data",
2325 
2326  /* Prepare for input conversion of RETURNING results. */
2327  if (dmstate->has_returning)
2328  dmstate->attinmeta = TupleDescGetAttInMetadata(RelationGetDescr(dmstate->rel));
2329 
2330  /*
2331  * Prepare for processing of parameters used in remote query, if any.
2332  */
2333  numParams = list_length(fsplan->fdw_exprs);
2334  dmstate->numParams = numParams;
2335  if (numParams > 0)
2337  fsplan->fdw_exprs,
2338  numParams,
2339  &dmstate->param_flinfo,
2340  &dmstate->param_exprs,
2341  &dmstate->param_values);
2342 }
ScanState ss
Definition: execnodes.h:1564
Index scanrelid
Definition: plannodes.h:330
#define RelationGetDescr(relation)
Definition: rel.h:437
Oid GetUserId(void)
Definition: miscinit.c:284
static void prepare_query_params(PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:180
List * fdw_exprs
Definition: plannodes.h:601
List * fdw_private
Definition: plannodes.h:602
#define strVal(v)
Definition: value.h:54
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
Relation ss_currentRelation
Definition: execnodes.h:1114
EState * state
Definition: execnodes.h:852
unsigned int Oid
Definition: postgres_ext.h:31
PlanState ps
Definition: execnodes.h:1113
void * list_nth(const List *list, int n)
Definition: list.c:410
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:342
void * palloc0(Size size)
Definition: mcxt.c:877
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1069
Plan * plan
Definition: execnodes.h:850
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:425

◆ postgresBeginForeignModify()

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

Definition at line 1660 of file postgres_fdw.c.

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

Referenced by postgres_fdw_handler().

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

◆ postgresBeginForeignScan()

static void postgresBeginForeignScan ( ForeignScanState node,
int  eflags 
)
static

Definition at line 1280 of file postgres_fdw.c.

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

Referenced by postgres_fdw_handler().

1281 {
1282  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1283  EState *estate = node->ss.ps.state;
1284  PgFdwScanState *fsstate;
1285  RangeTblEntry *rte;
1286  Oid userid;
1287  ForeignTable *table;
1288  UserMapping *user;
1289  int rtindex;
1290  int numParams;
1291 
1292  /*
1293  * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
1294  */
1295  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1296  return;
1297 
1298  /*
1299  * We'll save private state in node->fdw_state.
1300  */
1301  fsstate = (PgFdwScanState *) palloc0(sizeof(PgFdwScanState));
1302  node->fdw_state = (void *) fsstate;
1303 
1304  /*
1305  * Identify which user to do the remote access as. This should match what
1306  * ExecCheckRTEPerms() does. In case of a join or aggregate, use the
1307  * lowest-numbered member RTE as a representative; we would get the same
1308  * result from any.
1309  */
1310  if (fsplan->scan.scanrelid > 0)
1311  rtindex = fsplan->scan.scanrelid;
1312  else
1313  rtindex = bms_next_member(fsplan->fs_relids, -1);
1314  rte = rt_fetch(rtindex, estate->es_range_table);
1315  userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
1316 
1317  /* Get info about foreign table. */
1318  table = GetForeignTable(rte->relid);
1319  user = GetUserMapping(userid, table->serverid);
1320 
1321  /*
1322  * Get connection to the foreign server. Connection manager will
1323  * establish new connection if necessary.
1324  */
1325  fsstate->conn = GetConnection(user, false);
1326 
1327  /* Assign a unique ID for my cursor */
1328  fsstate->cursor_number = GetCursorNumber(fsstate->conn);
1329  fsstate->cursor_exists = false;
1330 
1331  /* Get private info created by planner functions. */
1332  fsstate->query = strVal(list_nth(fsplan->fdw_private,
1334  fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
1336  fsstate->fetch_size = intVal(list_nth(fsplan->fdw_private,
1338 
1339  /* Create contexts for batches of tuples and per-tuple temp workspace. */
1340  fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
1341  "postgres_fdw tuple data",
1343  fsstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
1344  "postgres_fdw temporary data",
1346 
1347  /*
1348  * Get info we'll need for converting data fetched from the foreign server
1349  * into local representation and error reporting during that process.
1350  */
1351  if (fsplan->scan.scanrelid > 0)
1352  {
1353  fsstate->rel = node->ss.ss_currentRelation;
1354  fsstate->tupdesc = RelationGetDescr(fsstate->rel);
1355  }
1356  else
1357  {
1358  fsstate->rel = NULL;
1359  fsstate->tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
1360  }
1361 
1362  fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
1363 
1364  /*
1365  * Prepare for processing of parameters used in remote query, if any.
1366  */
1367  numParams = list_length(fsplan->fdw_exprs);
1368  fsstate->numParams = numParams;
1369  if (numParams > 0)
1371  fsplan->fdw_exprs,
1372  numParams,
1373  &fsstate->param_flinfo,
1374  &fsstate->param_exprs,
1375  &fsstate->param_values);
1376 }
ScanState ss
Definition: execnodes.h:1564
Index scanrelid
Definition: plannodes.h:330
#define RelationGetDescr(relation)
Definition: rel.h:437
Oid GetUserId(void)
Definition: miscinit.c:284
static void prepare_query_params(PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:180
List * fdw_exprs
Definition: plannodes.h:601
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1009
List * fdw_private
Definition: plannodes.h:602
#define strVal(v)
Definition: value.h:54
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:216
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1116
Relation ss_currentRelation
Definition: execnodes.h:1114
EState * state
Definition: execnodes.h:852
unsigned int Oid
Definition: postgres_ext.h:31
PlanState ps
Definition: execnodes.h:1113
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:170
void * list_nth(const List *list, int n)
Definition: list.c:410
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:121
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:342
void * palloc0(Size size)
Definition: mcxt.c:877
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1069
Plan * plan
Definition: execnodes.h:850
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt)
Definition: connection.c:107
unsigned int GetCursorNumber(PGconn *conn)
Definition: connection.c:481
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
Bitmapset * fs_relids
Definition: plannodes.h:605

◆ postgresEndDirectModify()

static void postgresEndDirectModify ( ForeignScanState node)
static

Definition at line 2393 of file postgres_fdw.c.

References PgFdwDirectModifyState::conn, ForeignScanState::fdw_state, PQclear(), ReleaseConnection(), and PgFdwDirectModifyState::result.

Referenced by postgres_fdw_handler().

2394 {
2396 
2397  /* if dmstate is NULL, we are in EXPLAIN; nothing to do */
2398  if (dmstate == NULL)
2399  return;
2400 
2401  /* Release PGresult */
2402  if (dmstate->result)
2403  PQclear(dmstate->result);
2404 
2405  /* Release remote connection */
2406  ReleaseConnection(dmstate->conn);
2407  dmstate->conn = NULL;
2408 
2409  /* MemoryContext will be deleted automatically. */
2410 }
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460
void PQclear(PGresult *res)
Definition: fe-exec.c:671

◆ postgresEndForeignModify()

static void postgresEndForeignModify ( EState estate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 1988 of file postgres_fdw.c.

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

Referenced by postgres_fdw_handler().

1990 {
1991  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
1992 
1993  /* If fmstate is NULL, we are in EXPLAIN; nothing to do */
1994  if (fmstate == NULL)
1995  return;
1996 
1997  /* If we created a prepared statement, destroy it */
1998  if (fmstate->p_name)
1999  {
2000  char sql[64];
2001  PGresult *res;
2002 
2003  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
2004 
2005  /*
2006  * We don't use a PG_TRY block here, so be careful not to throw error
2007  * without releasing the PGresult.
2008  */
2009  res = pgfdw_exec_query(fmstate->conn, sql);
2010  if (PQresultStatus(res) != PGRES_COMMAND_OK)
2011  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
2012  PQclear(res);
2013  fmstate->p_name = NULL;
2014  }
2015 
2016  /* Release remote connection */
2017  ReleaseConnection(fmstate->conn);
2018  fmstate->conn = NULL;
2019 }
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 * ri_FdwState
Definition: execnodes.h:382
void PQclear(PGresult *res)
Definition: fe-exec.c:671
PGresult * pgfdw_exec_query(PGconn *conn, const char *query)
Definition: connection.c:508

◆ postgresEndForeignScan()

static void postgresEndForeignScan ( ForeignScanState node)
static

Definition at line 1481 of file postgres_fdw.c.

References close_cursor(), PgFdwScanState::conn, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, ForeignScanState::fdw_state, and ReleaseConnection().

Referenced by postgres_fdw_handler().

1482 {
1483  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1484 
1485  /* if fsstate is NULL, we are in EXPLAIN; nothing to do */
1486  if (fsstate == NULL)
1487  return;
1488 
1489  /* Close the cursor if open, to prevent accumulation of cursors */
1490  if (fsstate->cursor_exists)
1491  close_cursor(fsstate->conn, fsstate->cursor_number);
1492 
1493  /* Release remote connection */
1494  ReleaseConnection(fsstate->conn);
1495  fsstate->conn = NULL;
1496 
1497  /* MemoryContexts will be deleted automatically. */
1498 }
static void close_cursor(PGconn *conn, unsigned int cursor_number)
unsigned int cursor_number
Definition: postgres_fdw.c:138
void ReleaseConnection(PGconn *conn)
Definition: connection.c:460

◆ postgresExecForeignDelete()

static TupleTableSlot * postgresExecForeignDelete ( EState estate,
ResultRelInfo resultRelInfo,
TupleTableSlot slot,
TupleTableSlot planSlot 
)
static

Definition at line 1912 of file postgres_fdw.c.

References PgFdwModifyState::conn, convert_prep_stmt_params(), PgFdwModifyState::ctidAttno, DatumGetPointer, elog, ERROR, ExecGetJunkAttribute(), PgFdwModifyState::has_returning, MemoryContextReset(), PgFdwModifyState::p_name, PgFdwModifyState::p_nums, pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQcmdTuples(), PQntuples(), PQresultStatus(), PQsendQueryPrepared(), prepare_foreign_modify(), PgFdwModifyState::query, ResultRelInfo::ri_FdwState, store_returning_result(), and PgFdwModifyState::temp_cxt.

Referenced by postgres_fdw_handler().

1916 {
1917  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
1918  Datum datum;
1919  bool isNull;
1920  const char **p_values;
1921  PGresult *res;
1922  int n_rows;
1923 
1924  /* Set up the prepared statement on the remote server, if we didn't yet */
1925  if (!fmstate->p_name)
1926  prepare_foreign_modify(fmstate);
1927 
1928  /* Get the ctid that was passed up as a resjunk column */
1929  datum = ExecGetJunkAttribute(planSlot,
1930  fmstate->ctidAttno,
1931  &isNull);
1932  /* shouldn't ever get a null result... */
1933  if (isNull)
1934  elog(ERROR, "ctid is NULL");
1935 
1936  /* Convert parameters needed by prepared statement to text form */
1937  p_values = convert_prep_stmt_params(fmstate,
1938  (ItemPointer) DatumGetPointer(datum),
1939  NULL);
1940 
1941  /*
1942  * Execute the prepared statement.
1943  */
1944  if (!PQsendQueryPrepared(fmstate->conn,
1945  fmstate->p_name,
1946  fmstate->p_nums,
1947  p_values,
1948  NULL,
1949  NULL,
1950  0))
1951  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
1952 
1953  /*
1954  * Get the result, and check for success.
1955  *
1956  * We don't use a PG_TRY block here, so be careful not to throw error
1957  * without releasing the PGresult.
1958  */
1959  res = pgfdw_get_result(fmstate->conn, fmstate->query);
1960  if (PQresultStatus(res) !=
1962  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
1963 
1964  /* Check number of rows affected, and fetch RETURNING tuple if any */
1965  if (fmstate->has_returning)
1966  {
1967  n_rows = PQntuples(res);
1968  if (n_rows > 0)
1969  store_returning_result(fmstate, slot, res);
1970  }
1971  else
1972  n_rows = atoi(PQcmdTuples(res));
1973 
1974  /* And clean up */
1975  PQclear(res);
1976 
1977  MemoryContextReset(fmstate->temp_cxt);
1978 
1979  /* Return NULL if nothing was deleted on the remote end */
1980  return (n_rows > 0) ? slot : NULL;
1981 }
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3065
static void store_returning_result(PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
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
AttrNumber ctidAttno
Definition: postgres_fdw.c:180
uintptr_t Datum
Definition: postgres.h:372
void * ri_FdwState
Definition: execnodes.h:382
void PQclear(PGresult *res)
Definition: fe-exec.c:671
MemoryContext temp_cxt
Definition: postgres_fdw.c:185
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:532
static const char ** convert_prep_stmt_params(PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot *slot)
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition: fe-exec.c:1376
#define DatumGetPointer(X)
Definition: postgres.h:555
static void prepare_foreign_modify(PgFdwModifyState *fmstate)
Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: execJunk.c:248
#define elog
Definition: elog.h:219

◆ postgresExecForeignInsert()

static TupleTableSlot * postgresExecForeignInsert ( EState estate,
ResultRelInfo resultRelInfo,
TupleTableSlot slot,
TupleTableSlot planSlot 
)
static

Definition at line 1772 of file postgres_fdw.c.

References PgFdwModifyState::conn, convert_prep_stmt_params(), ERROR, PgFdwModifyState::has_returning, MemoryContextReset(), PgFdwModifyState::p_name, PgFdwModifyState::p_nums, pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQcmdTuples(), PQntuples(), PQresultStatus(), PQsendQueryPrepared(), prepare_foreign_modify(), PgFdwModifyState::query, ResultRelInfo::ri_FdwState, store_returning_result(), and PgFdwModifyState::temp_cxt.

Referenced by postgres_fdw_handler().

1776 {
1777  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
1778  const char **p_values;
1779  PGresult *res;
1780  int n_rows;
1781 
1782  /* Set up the prepared statement on the remote server, if we didn't yet */
1783  if (!fmstate->p_name)
1784  prepare_foreign_modify(fmstate);
1785 
1786  /* Convert parameters needed by prepared statement to text form */
1787  p_values = convert_prep_stmt_params(fmstate, NULL, slot);
1788 
1789  /*
1790  * Execute the prepared statement.
1791  */
1792  if (!PQsendQueryPrepared(fmstate->conn,
1793  fmstate->p_name,
1794  fmstate->p_nums,
1795  p_values,
1796  NULL,
1797  NULL,
1798  0))
1799  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
1800 
1801  /*
1802  * Get the result, and check for success.
1803  *
1804  * We don't use a PG_TRY block here, so be careful not to throw error
1805  * without releasing the PGresult.
1806  */
1807  res = pgfdw_get_result(fmstate->conn, fmstate->query);
1808  if (PQresultStatus(res) !=
1810  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
1811 
1812  /* Check number of rows affected, and fetch RETURNING tuple if any */
1813  if (fmstate->has_returning)
1814  {
1815  n_rows = PQntuples(res);
1816  if (n_rows > 0)
1817  store_returning_result(fmstate, slot, res);
1818  }
1819  else
1820  n_rows = atoi(PQcmdTuples(res));
1821 
1822  /* And clean up */
1823  PQclear(res);
1824 
1825  MemoryContextReset(fmstate->temp_cxt);
1826 
1827  /* Return NULL if nothing was inserted on the remote end */
1828  return (n_rows > 0) ? slot : NULL;
1829 }
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3065
static void store_returning_result(PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
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 * ri_FdwState
Definition: execnodes.h:382
void PQclear(PGresult *res)
Definition: fe-exec.c:671
MemoryContext temp_cxt
Definition: postgres_fdw.c:185
PGresult * pgfdw_get_result(PGconn *conn, const char *query)
Definition: connection.c:532
static const char ** convert_prep_stmt_params(PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot *slot)
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition: fe-exec.c:1376
static void prepare_foreign_modify(PgFdwModifyState *fmstate)

◆ postgresExecForeignUpdate()

static TupleTableSlot * postgresExecForeignUpdate ( EState estate,
ResultRelInfo resultRelInfo,
TupleTableSlot slot,
TupleTableSlot planSlot 
)
static

Definition at line 1836 of file postgres_fdw.c.

References PgFdwModifyState::conn, convert_prep_stmt_params(), PgFdwModifyState::ctidAttno, DatumGetPointer, elog, ERROR, ExecGetJunkAttribute(), PgFdwModifyState::has_returning, MemoryContextReset(), PgFdwModifyState::p_name, PgFdwModifyState::p_nums, pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQcmdTuples(), PQntuples(), PQresultStatus(), PQsendQueryPrepared(), prepare_foreign_modify(), PgFdwModifyState::query, ResultRelInfo::ri_FdwState, store_returning_result(), and PgFdwModifyState::temp_cxt.

Referenced by postgres_fdw_handler().

1840 {
1841  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
1842  Datum datum;
1843  bool isNull;
1844  const char **p_values;
1845  PGresult *res;
1846  int n_rows;
1847 
1848  /* Set up the prepared statement on the remote server, if we didn't yet */
1849  if (!fmstate->p_name)
1850  prepare_foreign_modify(fmstate);
1851 
1852  /* Get the ctid that was passed up as a resjunk column */
1853  datum = ExecGetJunkAttribute(planSlot,
1854  fmstate->ctidAttno,
1855  &isNull);
1856  /* shouldn't ever get a null result... */
1857  if (isNull)
1858  elog(ERROR, "ctid is NULL");
1859 
1860  /* Convert parameters needed by prepared statement to text form */
1861  p_values = convert_prep_stmt_params(fmstate,
1862  (ItemPointer) DatumGetPointer(datum),
1863  slot);
1864 
1865  /*
1866  * Execute the prepared statement.
1867  */
1868  if (!PQsendQueryPrepared(fmstate->conn,
1869  fmstate->p_name,
1870  fmstate->p_nums,
1871  p_values,
1872  NULL,
1873  NULL,
1874  0))
1875  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
1876 
1877  /*
1878  * Get the result, and check for success.
1879  *
1880  * We don't use a PG_TRY block here, so be careful not to throw error
1881  * without releasing the PGresult.
1882  */
1883  res = pgfdw_get_result(fmstate->conn, fmstate->query);
1884  if (PQresultStatus(res) !=
1886  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
1887 
1888  /* Check number of rows affected, and fetch RETURNING tuple if any */
1889  if (fmstate->has_returning)
1890  {
1891  n_rows = PQntuples(res);
1892  if (n_rows > 0)
1893  store_returning_result(fmstate, slot, res);
1894  }
1895  else
1896  n_rows = atoi(PQcmdTuples(res));
1897 
1898  /* And clean up */
1899  PQclear(res);
1900 
1901  MemoryContextReset(fmstate->temp_cxt);
1902 
1903  /* Return NULL if nothing was updated on the remote end */
1904  return (n_rows > 0) ? slot : NULL;
1905 }
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3065
static void store_returning_result(PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
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
AttrNumber ctidAttno
Definition: postgres_fdw.c:180
uintptr_t Datum
Definition: postgres.h:372
void * ri_FdwState
Definition: