PostgreSQL Source Code  git master
postgres_fdw.c File Reference
#include "postgres.h"
#include <limits.h>
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/pg_class.h"
#include "catalog/pg_opfamily.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/vacuum.h"
#include "executor/execAsync.h"
#include "foreign/fdwapi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/inherit.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "postgres_fdw.h"
#include "storage/latch.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
#include "utils/selfuncs.h"
Include dependency graph for postgres_fdw.c:

Go to the source code of this file.

Data Structures

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

Macros

#define DEFAULT_FDW_STARTUP_COST   100.0
 
#define DEFAULT_FDW_TUPLE_COST   0.2
 
#define DEFAULT_FDW_SORT_MULTIPLIER   1.2
 

Typedefs

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

Enumerations

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

Functions

 PG_FUNCTION_INFO_V1 (postgres_fdw_handler)
 
static void postgresGetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static void postgresGetForeignPaths (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
 
static ForeignScanpostgresGetForeignPlan (PlannerInfo *root, RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
 
static void postgresBeginForeignScan (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateForeignScan (ForeignScanState *node)
 
static void postgresReScanForeignScan (ForeignScanState *node)
 
static void postgresEndForeignScan (ForeignScanState *node)
 
static void postgresAddForeignUpdateTargets (PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation)
 
static ListpostgresPlanForeignModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginForeignModify (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
 
static TupleTableSlotpostgresExecForeignInsert (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlot ** postgresExecForeignBatchInsert (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
 
static int postgresGetForeignModifyBatchSize (ResultRelInfo *resultRelInfo)
 
static TupleTableSlotpostgresExecForeignUpdate (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static TupleTableSlotpostgresExecForeignDelete (EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
 
static void postgresEndForeignModify (EState *estate, ResultRelInfo *resultRelInfo)
 
static void postgresBeginForeignInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static void postgresEndForeignInsert (EState *estate, ResultRelInfo *resultRelInfo)
 
static int postgresIsForeignRelUpdatable (Relation rel)
 
static bool postgresPlanDirectModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
 
static void postgresBeginDirectModify (ForeignScanState *node, int eflags)
 
static TupleTableSlotpostgresIterateDirectModify (ForeignScanState *node)
 
static void postgresEndDirectModify (ForeignScanState *node)
 
static void postgresExplainForeignScan (ForeignScanState *node, ExplainState *es)
 
static void postgresExplainForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
 
static void postgresExplainDirectModify (ForeignScanState *node, ExplainState *es)
 
static void postgresExecForeignTruncate (List *rels, DropBehavior behavior, bool restart_seqs)
 
static bool postgresAnalyzeForeignTable (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
 
static ListpostgresImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid)
 
static void postgresGetForeignJoinPaths (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static bool postgresRecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot)
 
static void postgresGetForeignUpperPaths (PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra)
 
static bool postgresIsForeignPathAsyncCapable (ForeignPath *path)
 
static void postgresForeignAsyncRequest (AsyncRequest *areq)
 
static void postgresForeignAsyncConfigureWait (AsyncRequest *areq)
 
static void postgresForeignAsyncNotify (AsyncRequest *areq)
 
static void estimate_path_cost_size (PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
 
static void get_remote_estimate (const char *sql, PGconn *conn, double *rows, int *width, Cost *startup_cost, Cost *total_cost)
 
static void adjust_foreign_grouping_path_cost (PlannerInfo *root, List *pathkeys, double retrieved_rows, double width, double limit_tuples, Cost *p_startup_cost, Cost *p_run_cost)
 
static bool ec_member_matches_foreign (PlannerInfo *root, RelOptInfo *rel, EquivalenceClass *ec, EquivalenceMember *em, void *arg)
 
static void create_cursor (ForeignScanState *node)
 
static void fetch_more_data (ForeignScanState *node)
 
static void close_cursor (PGconn *conn, unsigned int cursor_number, PgFdwConnState *conn_state)
 
static PgFdwModifyStatecreate_foreign_modify (EState *estate, RangeTblEntry *rte, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, int values_end, bool has_returning, List *retrieved_attrs)
 
static TupleTableSlot ** execute_foreign_modify (EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
 
static void prepare_foreign_modify (PgFdwModifyState *fmstate)
 
static const char ** convert_prep_stmt_params (PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot **slots, int numSlots)
 
static void store_returning_result (PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
 
static void finish_foreign_modify (PgFdwModifyState *fmstate)
 
static void deallocate_query (PgFdwModifyState *fmstate)
 
static Listbuild_remote_returning (Index rtindex, Relation rel, List *returningList)
 
static void rebuild_fdw_scan_tlist (ForeignScan *fscan, List *tlist)
 
static void execute_dml_stmt (ForeignScanState *node)
 
static TupleTableSlotget_returning_data (ForeignScanState *node)
 
static void init_returning_filter (PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
 
static TupleTableSlotapply_returning_filter (PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
 
static void prepare_query_params (PlanState *node, List *fdw_exprs, int numParams, FmgrInfo **param_flinfo, List **param_exprs, const char ***param_values)
 
static void process_query_params (ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
 
static int postgresAcquireSampleRowsFunc (Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
 
static void analyze_row_processor (PGresult *res, int row, PgFdwAnalyzeState *astate)
 
static void produce_tuple_asynchronously (AsyncRequest *areq, bool fetch)
 
static void fetch_more_data_begin (AsyncRequest *areq)
 
static void complete_pending_request (AsyncRequest *areq)
 
static HeapTuple make_tuple_from_result_row (PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
 
static void conversion_error_callback (void *arg)
 
static bool foreign_join_ok (PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra)
 
static bool foreign_grouping_ok (PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
 
static Listget_useful_pathkeys_for_relation (PlannerInfo *root, RelOptInfo *rel)
 
static Listget_useful_ecs_for_relation (PlannerInfo *root, RelOptInfo *rel)
 
static void add_paths_with_pathkeys_for_rel (PlannerInfo *root, RelOptInfo *rel, Path *epq_path, List *restrictlist)
 
static void add_foreign_grouping_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra)
 
static void add_foreign_ordered_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel)
 
static void add_foreign_final_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *final_rel, FinalPathExtraData *extra)
 
static void apply_server_options (PgFdwRelationInfo *fpinfo)
 
static void apply_table_options (PgFdwRelationInfo *fpinfo)
 
static void merge_fdw_options (PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
 
static int get_batch_size_option (Relation rel)
 
Datum postgres_fdw_handler (PG_FUNCTION_ARGS)
 
static TupleDesc get_tupdesc_for_join_scan_tuples (ForeignScanState *node)
 
static ForeignScanfind_modifytable_subplan (PlannerInfo *root, ModifyTable *plan, Index rtindex, int subplan_index)
 
int set_transmission_modes (void)
 
void reset_transmission_modes (int nestlevel)
 
static double postgresGetAnalyzeInfoForForeignTable (Relation relation, bool *can_tablesample)
 
static bool semijoin_target_ok (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel)
 
void process_pending_request (AsyncRequest *areq)
 
EquivalenceMemberfind_em_for_rel (PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 
EquivalenceMemberfind_em_for_rel_target (PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 

Variables

 PG_MODULE_MAGIC
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 63 of file postgres_fdw.c.

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 57 of file postgres_fdw.c.

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.2

Definition at line 60 of file postgres_fdw.c.

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 123 of file postgres_fdw.c.

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 100 of file postgres_fdw.c.

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

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 283 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 72 of file postgres_fdw.c.

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

Function Documentation

◆ add_foreign_final_paths()

static void add_foreign_final_paths ( PlannerInfo root,
RelOptInfo input_rel,
RelOptInfo final_rel,
FinalPathExtraData extra 
)
static

Definition at line 6988 of file postgres_fdw.c.

6991 {
6992  Query *parse = root->parse;
6993  PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6994  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
6995  bool has_final_sort = false;
6996  List *pathkeys = NIL;
6997  PgFdwPathExtraData *fpextra;
6998  bool save_use_remote_estimate = false;
6999  double rows;
7000  int width;
7001  Cost startup_cost;
7002  Cost total_cost;
7003  List *fdw_private;
7004  ForeignPath *final_path;
7005 
7006  /*
7007  * Currently, we only support this for SELECT commands
7008  */
7009  if (parse->commandType != CMD_SELECT)
7010  return;
7011 
7012  /*
7013  * No work if there is no FOR UPDATE/SHARE clause and if there is no need
7014  * to add a LIMIT node
7015  */
7016  if (!parse->rowMarks && !extra->limit_needed)
7017  return;
7018 
7019  /* We don't support cases where there are any SRFs in the targetlist */
7020  if (parse->hasTargetSRFs)
7021  return;
7022 
7023  /* Save the input_rel as outerrel in fpinfo */
7024  fpinfo->outerrel = input_rel;
7025 
7026  /*
7027  * Copy foreign table, foreign server, user mapping, FDW options etc.
7028  * details from the input relation's fpinfo.
7029  */
7030  fpinfo->table = ifpinfo->table;
7031  fpinfo->server = ifpinfo->server;
7032  fpinfo->user = ifpinfo->user;
7033  merge_fdw_options(fpinfo, ifpinfo, NULL);
7034 
7035  /*
7036  * If there is no need to add a LIMIT node, there might be a ForeignPath
7037  * in the input_rel's pathlist that implements all behavior of the query.
7038  * Note: we would already have accounted for the query's FOR UPDATE/SHARE
7039  * (if any) before we get here.
7040  */
7041  if (!extra->limit_needed)
7042  {
7043  ListCell *lc;
7044 
7045  Assert(parse->rowMarks);
7046 
7047  /*
7048  * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
7049  * so the input_rel should be a base, join, or ordered relation; and
7050  * if it's an ordered relation, its input relation should be a base or
7051  * join relation.
7052  */
7053  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7054  input_rel->reloptkind == RELOPT_JOINREL ||
7055  (input_rel->reloptkind == RELOPT_UPPER_REL &&
7056  ifpinfo->stage == UPPERREL_ORDERED &&
7057  (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
7058  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
7059 
7060  foreach(lc, input_rel->pathlist)
7061  {
7062  Path *path = (Path *) lfirst(lc);
7063 
7064  /*
7065  * apply_scanjoin_target_to_paths() uses create_projection_path()
7066  * to adjust each of its input paths if needed, whereas
7067  * create_ordered_paths() uses apply_projection_to_path() to do
7068  * that. So the former might have put a ProjectionPath on top of
7069  * the ForeignPath; look through ProjectionPath and see if the
7070  * path underneath it is ForeignPath.
7071  */
7072  if (IsA(path, ForeignPath) ||
7073  (IsA(path, ProjectionPath) &&
7074  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
7075  {
7076  /*
7077  * Create foreign final path; this gets rid of a
7078  * no-longer-needed outer plan (if any), which makes the
7079  * EXPLAIN output look cleaner
7080  */
7081  final_path = create_foreign_upper_path(root,
7082  path->parent,
7083  path->pathtarget,
7084  path->rows,
7085  path->startup_cost,
7086  path->total_cost,
7087  path->pathkeys,
7088  NULL, /* no extra plan */
7089  NIL, /* no fdw_restrictinfo
7090  * list */
7091  NIL); /* no fdw_private */
7092 
7093  /* and add it to the final_rel */
7094  add_path(final_rel, (Path *) final_path);
7095 
7096  /* Safe to push down */
7097  fpinfo->pushdown_safe = true;
7098 
7099  return;
7100  }
7101  }
7102 
7103  /*
7104  * If we get here it means no ForeignPaths; since we would already
7105  * have considered pushing down all operations for the query to the
7106  * remote server, give up on it.
7107  */
7108  return;
7109  }
7110 
7111  Assert(extra->limit_needed);
7112 
7113  /*
7114  * If the input_rel is an ordered relation, replace the input_rel with its
7115  * input relation
7116  */
7117  if (input_rel->reloptkind == RELOPT_UPPER_REL &&
7118  ifpinfo->stage == UPPERREL_ORDERED)
7119  {
7120  input_rel = ifpinfo->outerrel;
7121  ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
7122  has_final_sort = true;
7123  pathkeys = root->sort_pathkeys;
7124  }
7125 
7126  /* The input_rel should be a base, join, or grouping relation */
7127  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7128  input_rel->reloptkind == RELOPT_JOINREL ||
7129  (input_rel->reloptkind == RELOPT_UPPER_REL &&
7130  ifpinfo->stage == UPPERREL_GROUP_AGG));
7131 
7132  /*
7133  * We try to create a path below by extending a simple foreign path for
7134  * the underlying base, join, or grouping relation to perform the final
7135  * sort (if has_final_sort) and the LIMIT restriction remotely, which is
7136  * stored into the fdw_private list of the resulting path. (We
7137  * re-estimate the costs of sorting the underlying relation, if
7138  * has_final_sort.)
7139  */
7140 
7141  /*
7142  * Assess if it is safe to push down the LIMIT and OFFSET to the remote
7143  * server
7144  */
7145 
7146  /*
7147  * If the underlying relation has any local conditions, the LIMIT/OFFSET
7148  * cannot be pushed down.
7149  */
7150  if (ifpinfo->local_conds)
7151  return;
7152 
7153  /*
7154  * If the query has FETCH FIRST .. WITH TIES, 1) it must have ORDER BY as
7155  * well, which is used to determine which additional rows tie for the last
7156  * place in the result set, and 2) ORDER BY must already have been
7157  * determined to be safe to push down before we get here. So in that case
7158  * the FETCH clause is safe to push down with ORDER BY if the remote
7159  * server is v13 or later, but if not, the remote query will fail entirely
7160  * for lack of support for it. Since we do not currently have a way to do
7161  * a remote-version check (without accessing the remote server), disable
7162  * pushing the FETCH clause for now.
7163  */
7164  if (parse->limitOption == LIMIT_OPTION_WITH_TIES)
7165  return;
7166 
7167  /*
7168  * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
7169  * not safe to remote.
7170  */
7171  if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
7172  !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
7173  return;
7174 
7175  /* Safe to push down */
7176  fpinfo->pushdown_safe = true;
7177 
7178  /* Construct PgFdwPathExtraData */
7179  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
7180  fpextra->target = root->upper_targets[UPPERREL_FINAL];
7181  fpextra->has_final_sort = has_final_sort;
7182  fpextra->has_limit = extra->limit_needed;
7183  fpextra->limit_tuples = extra->limit_tuples;
7184  fpextra->count_est = extra->count_est;
7185  fpextra->offset_est = extra->offset_est;
7186 
7187  /*
7188  * Estimate the costs of performing the final sort and the LIMIT
7189  * restriction remotely. If has_final_sort is false, we wouldn't need to
7190  * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
7191  * roughly estimated using the costs we already have for the underlying
7192  * relation, in the same way as when use_remote_estimate is false. Since
7193  * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
7194  * false in that case.
7195  */
7196  if (!fpextra->has_final_sort)
7197  {
7198  save_use_remote_estimate = ifpinfo->use_remote_estimate;
7199  ifpinfo->use_remote_estimate = false;
7200  }
7201  estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
7202  &rows, &width, &startup_cost, &total_cost);
7203  if (!fpextra->has_final_sort)
7204  ifpinfo->use_remote_estimate = save_use_remote_estimate;
7205 
7206  /*
7207  * Build the fdw_private list that will be used by postgresGetForeignPlan.
7208  * Items in the list must match order in enum FdwPathPrivateIndex.
7209  */
7210  fdw_private = list_make2(makeBoolean(has_final_sort),
7211  makeBoolean(extra->limit_needed));
7212 
7213  /*
7214  * Create foreign final path; this gets rid of a no-longer-needed outer
7215  * plan (if any), which makes the EXPLAIN output look cleaner
7216  */
7217  final_path = create_foreign_upper_path(root,
7218  input_rel,
7219  root->upper_targets[UPPERREL_FINAL],
7220  rows,
7221  startup_cost,
7222  total_cost,
7223  pathkeys,
7224  NULL, /* no extra plan */
7225  NIL, /* no fdw_restrictinfo list */
7226  fdw_private);
7227 
7228  /* and add it to the final_rel */
7229  add_path(final_rel, (Path *) final_path);
7230 }
#define Assert(condition)
Definition: c.h:858
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:244
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:310
void * palloc0(Size size)
Definition: mcxt.c:1347
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
double Cost
Definition: nodes.h:251
@ CMD_SELECT
Definition: nodes.h:265
@ LIMIT_OPTION_WITH_TIES
Definition: nodes.h:432
ForeignPath * create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Path *fdw_outerpath, List *fdw_restrictinfo, List *fdw_private)
Definition: pathnode.c:2321
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:420
@ UPPERREL_GROUP_AGG
Definition: pathnodes.h:74
@ UPPERREL_FINAL
Definition: pathnodes.h:79
@ UPPERREL_ORDERED
Definition: pathnodes.h:78
@ RELOPT_BASEREL
Definition: pathnodes.h:821
@ RELOPT_UPPER_REL
Definition: pathnodes.h:825
@ RELOPT_JOINREL
Definition: pathnodes.h:822
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
#define list_make2(x1, x2)
Definition: pg_list.h:214
static void estimate_path_cost_size(PlannerInfo *root, RelOptInfo *foreignrel, List *param_join_conds, List *pathkeys, PgFdwPathExtraData *fpextra, double *p_rows, int *p_width, Cost *p_startup_cost, Cost *p_total_cost)
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
tree ctl root
Definition: radixtree.h:1886
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:715
Cardinality limit_tuples
Definition: pathnodes.h:3316
Definition: pg_list.h:54
List * pathkeys
Definition: pathnodes.h:1665
Cardinality rows
Definition: pathnodes.h:1660
Cost startup_cost
Definition: pathnodes.h:1661
Cost total_cost
Definition: pathnodes.h:1662
PathTarget * target
Definition: postgres_fdw.c:294
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
ForeignTable * table
Definition: postgres_fdw.h:85
UserMapping * user
Definition: postgres_fdw.h:87
ForeignServer * server
Definition: postgres_fdw.h:86
UpperRelationKind stage
Definition: postgres_fdw.h:109
List * pathlist
Definition: pathnodes.h:892
RelOptKind reloptkind
Definition: pathnodes.h:859
Boolean * makeBoolean(bool val)
Definition: value.c:49

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

Referenced by postgresGetForeignUpperPaths().

◆ add_foreign_grouping_paths()

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

Definition at line 6761 of file postgres_fdw.c.

6764 {
6765  Query *parse = root->parse;
6766  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6767  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
6768  ForeignPath *grouppath;
6769  double rows;
6770  int width;
6771  Cost startup_cost;
6772  Cost total_cost;
6773 
6774  /* Nothing to be done, if there is no grouping or aggregation required. */
6775  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
6776  !root->hasHavingQual)
6777  return;
6778 
6781 
6782  /* save the input_rel as outerrel in fpinfo */
6783  fpinfo->outerrel = input_rel;
6784 
6785  /*
6786  * Copy foreign table, foreign server, user mapping, FDW options etc.
6787  * details from the input relation's fpinfo.
6788  */
6789  fpinfo->table = ifpinfo->table;
6790  fpinfo->server = ifpinfo->server;
6791  fpinfo->user = ifpinfo->user;
6792  merge_fdw_options(fpinfo, ifpinfo, NULL);
6793 
6794  /*
6795  * Assess if it is safe to push down aggregation and grouping.
6796  *
6797  * Use HAVING qual from extra. In case of child partition, it will have
6798  * translated Vars.
6799  */
6800  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
6801  return;
6802 
6803  /*
6804  * Compute the selectivity and cost of the local_conds, so we don't have
6805  * to do it over again for each path. (Currently we create just a single
6806  * path here, but in future it would be possible that we build more paths
6807  * such as pre-sorted paths as in postgresGetForeignPaths and
6808  * postgresGetForeignJoinPaths.) The best we can do for these conditions
6809  * is to estimate selectivity on the basis of local statistics.
6810  */
6812  fpinfo->local_conds,
6813  0,
6814  JOIN_INNER,
6815  NULL);
6816 
6817  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6818 
6819  /* Estimate the cost of push down */
6820  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
6821  &rows, &width, &startup_cost, &total_cost);
6822 
6823  /* Now update this information in the fpinfo */
6824  fpinfo->rows = rows;
6825  fpinfo->width = width;
6826  fpinfo->startup_cost = startup_cost;
6827  fpinfo->total_cost = total_cost;
6828 
6829  /* Create and add foreign path to the grouping relation. */
6830  grouppath = create_foreign_upper_path(root,
6831  grouped_rel,
6832  grouped_rel->reltarget,
6833  rows,
6834  startup_cost,
6835  total_cost,
6836  NIL, /* no pathkeys */
6837  NULL,
6838  NIL, /* no fdw_restrictinfo list */
6839  NIL); /* no fdw_private */
6840 
6841  /* Add generated path into grouped_rel by add_path(). */
6842  add_path(grouped_rel, (Path *) grouppath);
6843 }
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:100
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:4619
@ JOIN_INNER
Definition: nodes.h:293
@ PARTITIONWISE_AGGREGATE_FULL
Definition: pathnodes.h:3271
@ PARTITIONWISE_AGGREGATE_NONE
Definition: pathnodes.h:3270
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
PartitionwiseAggregateType patype
Definition: pathnodes.h:3300
Selectivity local_conds_sel
Definition: postgres_fdw.h:57
QualCost local_conds_cost
Definition: postgres_fdw.h:56
struct PathTarget * reltarget
Definition: pathnodes.h:887

References add_path(), Assert, clauselist_selectivity(), cost_qual_eval(), create_foreign_upper_path(), estimate_path_cost_size(), foreign_grouping_ok(), GroupPathExtraData::havingQual, JOIN_INNER, PgFdwRelationInfo::local_conds, PgFdwRelationInfo::local_conds_cost, PgFdwRelationInfo::local_conds_sel, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, parse(), PARTITIONWISE_AGGREGATE_FULL, PARTITIONWISE_AGGREGATE_NONE, GroupPathExtraData::patype, RelOptInfo::reltarget, root, PgFdwRelationInfo::rows, PgFdwRelationInfo::server, PgFdwRelationInfo::startup_cost, PgFdwRelationInfo::table, PgFdwRelationInfo::total_cost, PgFdwRelationInfo::user, and PgFdwRelationInfo::width.

Referenced by postgresGetForeignUpperPaths().

◆ add_foreign_ordered_paths()

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

Definition at line 6853 of file postgres_fdw.c.

6855 {
6856  Query *parse = root->parse;
6857  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6858  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
6859  PgFdwPathExtraData *fpextra;
6860  double rows;
6861  int width;
6862  Cost startup_cost;
6863  Cost total_cost;
6864  List *fdw_private;
6865  ForeignPath *ordered_path;
6866  ListCell *lc;
6867 
6868  /* Shouldn't get here unless the query has ORDER BY */
6869  Assert(parse->sortClause);
6870 
6871  /* We don't support cases where there are any SRFs in the targetlist */
6872  if (parse->hasTargetSRFs)
6873  return;
6874 
6875  /* Save the input_rel as outerrel in fpinfo */
6876  fpinfo->outerrel = input_rel;
6877 
6878  /*
6879  * Copy foreign table, foreign server, user mapping, FDW options etc.
6880  * details from the input relation's fpinfo.
6881  */
6882  fpinfo->table = ifpinfo->table;
6883  fpinfo->server = ifpinfo->server;
6884  fpinfo->user = ifpinfo->user;
6885  merge_fdw_options(fpinfo, ifpinfo, NULL);
6886 
6887  /*
6888  * If the input_rel is a base or join relation, we would already have
6889  * considered pushing down the final sort to the remote server when
6890  * creating pre-sorted foreign paths for that relation, because the
6891  * query_pathkeys is set to the root->sort_pathkeys in that case (see
6892  * standard_qp_callback()).
6893  */
6894  if (input_rel->reloptkind == RELOPT_BASEREL ||
6895  input_rel->reloptkind == RELOPT_JOINREL)
6896  {
6897  Assert(root->query_pathkeys == root->sort_pathkeys);
6898 
6899  /* Safe to push down if the query_pathkeys is safe to push down */
6900  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
6901 
6902  return;
6903  }
6904 
6905  /* The input_rel should be a grouping relation */
6906  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
6907  ifpinfo->stage == UPPERREL_GROUP_AGG);
6908 
6909  /*
6910  * We try to create a path below by extending a simple foreign path for
6911  * the underlying grouping relation to perform the final sort remotely,
6912  * which is stored into the fdw_private list of the resulting path.
6913  */
6914 
6915  /* Assess if it is safe to push down the final sort */
6916  foreach(lc, root->sort_pathkeys)
6917  {
6918  PathKey *pathkey = (PathKey *) lfirst(lc);
6919  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6920 
6921  /*
6922  * is_foreign_expr would detect volatile expressions as well, but
6923  * checking ec_has_volatile here saves some cycles.
6924  */
6925  if (pathkey_ec->ec_has_volatile)
6926  return;
6927 
6928  /*
6929  * Can't push down the sort if pathkey's opfamily is not shippable.
6930  */
6931  if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId,
6932  fpinfo))
6933  return;
6934 
6935  /*
6936  * The EC must contain a shippable EM that is computed in input_rel's
6937  * reltarget, else we can't push down the sort.
6938  */
6940  pathkey_ec,
6941  input_rel) == NULL)
6942  return;
6943  }
6944 
6945  /* Safe to push down */
6946  fpinfo->pushdown_safe = true;
6947 
6948  /* Construct PgFdwPathExtraData */
6949  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6950  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6951  fpextra->has_final_sort = true;
6952 
6953  /* Estimate the costs of performing the final sort remotely */
6954  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6955  &rows, &width, &startup_cost, &total_cost);
6956 
6957  /*
6958  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6959  * Items in the list must match order in enum FdwPathPrivateIndex.
6960  */
6961  fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
6962 
6963  /* Create foreign ordering path */
6964  ordered_path = create_foreign_upper_path(root,
6965  input_rel,
6966  root->upper_targets[UPPERREL_ORDERED],
6967  rows,
6968  startup_cost,
6969  total_cost,
6970  root->sort_pathkeys,
6971  NULL, /* no extra plan */
6972  NIL, /* no fdw_restrictinfo
6973  * list */
6974  fdw_private);
6975 
6976  /* and add it to the ordered_rel */
6977  add_path(ordered_rel, (Path *) ordered_path);
6978 }
EquivalenceMember * find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
bool is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
Definition: shippable.c:162
Oid pk_opfamily
Definition: pathnodes.h:1469

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

Referenced by postgresGetForeignUpperPaths().

◆ add_paths_with_pathkeys_for_rel()

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

Definition at line 6088 of file postgres_fdw.c.

6090 {
6091  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6092  ListCell *lc;
6093 
6094  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
6095 
6096  /*
6097  * Before creating sorted paths, arrange for the passed-in EPQ path, if
6098  * any, to return columns needed by the parent ForeignScan node so that
6099  * they will propagate up through Sort nodes injected below, if necessary.
6100  */
6101  if (epq_path != NULL && useful_pathkeys_list != NIL)
6102  {
6103  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6104  PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6105 
6106  /* Include columns required for evaluating PHVs in the tlist. */
6108  pull_var_clause((Node *) target->exprs,
6110 
6111  /* Include columns required for evaluating the local conditions. */
6112  foreach(lc, fpinfo->local_conds)
6113  {
6114  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6115 
6117  pull_var_clause((Node *) rinfo->clause,
6119  }
6120 
6121  /*
6122  * If we have added any new columns, adjust the tlist of the EPQ path.
6123  *
6124  * Note: the plan created using this path will only be used to execute
6125  * EPQ checks, where accuracy of the plan cost and width estimates
6126  * would not be important, so we do not do set_pathtarget_cost_width()
6127  * for the new pathtarget here. See also postgresGetForeignPlan().
6128  */
6129  if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6130  {
6131  /* The EPQ path is a join path, so it is projection-capable. */
6133 
6134  /*
6135  * Use create_projection_path() here, so as to avoid modifying it
6136  * in place.
6137  */
6138  epq_path = (Path *) create_projection_path(root,
6139  rel,
6140  epq_path,
6141  target);
6142  }
6143  }
6144 
6145  /* Create one path for each set of pathkeys we found above. */
6146  foreach(lc, useful_pathkeys_list)
6147  {
6148  double rows;
6149  int width;
6150  Cost startup_cost;
6151  Cost total_cost;
6152  List *useful_pathkeys = lfirst(lc);
6153  Path *sorted_epq_path;
6154 
6155  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
6156  &rows, &width, &startup_cost, &total_cost);
6157 
6158  /*
6159  * The EPQ path must be at least as well sorted as the path itself, in
6160  * case it gets used as input to a mergejoin.
6161  */
6162  sorted_epq_path = epq_path;
6163  if (sorted_epq_path != NULL &&
6164  !pathkeys_contained_in(useful_pathkeys,
6165  sorted_epq_path->pathkeys))
6166  sorted_epq_path = (Path *)
6168  rel,
6169  sorted_epq_path,
6170  useful_pathkeys,
6171  -1.0);
6172 
6173  if (IS_SIMPLE_REL(rel))
6174  add_path(rel, (Path *)
6176  NULL,
6177  rows,
6178  startup_cost,
6179  total_cost,
6180  useful_pathkeys,
6181  rel->lateral_relids,
6182  sorted_epq_path,
6183  NIL, /* no fdw_restrictinfo
6184  * list */
6185  NIL));
6186  else
6187  add_path(rel, (Path *)
6189  NULL,
6190  rows,
6191  startup_cost,
6192  total_cost,
6193  useful_pathkeys,
6194  rel->lateral_relids,
6195  sorted_epq_path,
6196  restrictlist,
6197  NIL));
6198  }
6199 }
bool is_projection_capable_path(Path *path)
Definition: createplan.c:7202
#define PVC_RECURSE_PLACEHOLDERS
Definition: optimizer.h:191
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:341
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2988
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2673
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_restrictinfo, List *fdw_private)
Definition: pathnode.c:2223
ForeignPath * create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_restrictinfo, List *fdw_private)
Definition: pathnode.c:2269
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:833
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
Definition: postgres_fdw.c:904
Definition: nodes.h:129
Relids lateral_relids
Definition: pathnodes.h:907
Expr * clause
Definition: pathnodes.h:2564
PathTarget * copy_pathtarget(PathTarget *src)
Definition: tlist.c:657
void add_new_columns_to_pathtarget(PathTarget *target, List *exprs)
Definition: tlist.c:752
List * pull_var_clause(Node *node, int flags)
Definition: var.c:607

References add_new_columns_to_pathtarget(), add_path(), Assert, RestrictInfo::clause, copy_pathtarget(), create_foreign_join_path(), create_foreignscan_path(), create_projection_path(), create_sort_path(), estimate_path_cost_size(), get_useful_pathkeys_for_relation(), is_projection_capable_path(), IS_SIMPLE_REL, RelOptInfo::lateral_relids, lfirst, lfirst_node, list_length(), PgFdwRelationInfo::local_conds, NIL, Path::pathkeys, pathkeys_contained_in(), pull_var_clause(), PVC_RECURSE_PLACEHOLDERS, and root.

Referenced by postgresGetForeignJoinPaths(), and postgresGetForeignPaths().

◆ adjust_foreign_grouping_path_cost()

static void adjust_foreign_grouping_path_cost ( PlannerInfo root,
List pathkeys,
double  retrieved_rows,
double  width,
double  limit_tuples,
Cost p_startup_cost,
Cost p_run_cost 
)
static

Definition at line 3635 of file postgres_fdw.c.

3642 {
3643  /*
3644  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3645  * side is unlikely to generate properly-sorted output, so it would need
3646  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3647  * if the GROUP BY clause is sort-able but isn't a superset of the given
3648  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3649  * costs by applying the same heuristic as for the scan or join case.
3650  */
3651  if (!grouping_is_sortable(root->processed_groupClause) ||
3652  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3653  {
3654  Path sort_path; /* dummy for result of cost_sort */
3655 
3656  cost_sort(&sort_path,
3657  root,
3658  pathkeys,
3659  *p_startup_cost + *p_run_cost,
3660  retrieved_rows,
3661  width,
3662  0.0,
3663  work_mem,
3664  limit_tuples);
3665 
3666  *p_startup_cost = sort_path.startup_cost;
3667  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3668  }
3669  else
3670  {
3671  /*
3672  * The default extra cost seems too large for foreign-grouping cases;
3673  * add 1/4th of that default.
3674  */
3675  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3676  - 1.0) * 0.25;
3677 
3678  *p_startup_cost *= sort_multiplier;
3679  *p_run_cost *= sort_multiplier;
3680  }
3681 }
void cost_sort(Path *path, PlannerInfo *root, List *pathkeys, Cost input_cost, double tuples, int width, Cost comparison_cost, int sort_mem, double limit_tuples)
Definition: costsize.c:2103
int work_mem
Definition: globals.c:129
#define DEFAULT_FDW_SORT_MULTIPLIER
Definition: postgres_fdw.c:63
bool grouping_is_sortable(List *groupClause)
Definition: tlist.c:540

References cost_sort(), DEFAULT_FDW_SORT_MULTIPLIER, grouping_is_sortable(), pathkeys_contained_in(), root, Path::startup_cost, Path::total_cost, and work_mem.

Referenced by estimate_path_cost_size().

◆ analyze_row_processor()

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

Definition at line 5378 of file postgres_fdw.c.

5379 {
5380  int targrows = astate->targrows;
5381  int pos; /* array index to store tuple in */
5382  MemoryContext oldcontext;
5383 
5384  /* Always increment sample row counter. */
5385  astate->samplerows += 1;
5386 
5387  /*
5388  * Determine the slot where this sample row should be stored. Set pos to
5389  * negative value to indicate the row should be skipped.
5390  */
5391  if (astate->numrows < targrows)
5392  {
5393  /* First targrows rows are always included into the sample */
5394  pos = astate->numrows++;
5395  }
5396  else
5397  {
5398  /*
5399  * Now we start replacing tuples in the sample until we reach the end
5400  * of the relation. Same algorithm as in acquire_sample_rows in
5401  * analyze.c; see Jeff Vitter's paper.
5402  */
5403  if (astate->rowstoskip < 0)
5404  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5405 
5406  if (astate->rowstoskip <= 0)
5407  {
5408  /* Choose a random reservoir element to replace. */
5409  pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5410  Assert(pos >= 0 && pos < targrows);
5411  heap_freetuple(astate->rows[pos]);
5412  }
5413  else
5414  {
5415  /* Skip this tuple. */
5416  pos = -1;
5417  }
5418 
5419  astate->rowstoskip -= 1;
5420  }
5421 
5422  if (pos >= 0)
5423  {
5424  /*
5425  * Create sample tuple from current result row, and store it in the
5426  * position determined above. The tuple has to be created in anl_cxt.
5427  */
5428  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5429 
5430  astate->rows[pos] = make_tuple_from_result_row(res, row,
5431  astate->rel,
5432  astate->attinmeta,
5433  astate->retrieved_attrs,
5434  NULL,
5435  astate->temp_cxt);
5436 
5437  MemoryContextSwitchTo(oldcontext);
5438  }
5439 }
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
MemoryContextSwitchTo(old_ctx)
double sampler_random_fract(pg_prng_state *randstate)
Definition: sampling.c:241
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition: sampling.c:147
ReservoirStateData rstate
Definition: postgres_fdw.c:269
AttInMetadata * attinmeta
Definition: postgres_fdw.c:258
MemoryContext anl_cxt
Definition: postgres_fdw.c:272
HeapTuple * rows
Definition: postgres_fdw.c:262
MemoryContext temp_cxt
Definition: postgres_fdw.c:273
pg_prng_state randstate
Definition: sampling.h:49

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

Referenced by postgresAcquireSampleRowsFunc().

◆ apply_returning_filter()

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

Definition at line 4747 of file postgres_fdw.c.

4751 {
4752  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4753  TupleTableSlot *resultSlot;
4754  Datum *values;
4755  bool *isnull;
4756  Datum *old_values;
4757  bool *old_isnull;
4758  int i;
4759 
4760  /*
4761  * Use the return tuple slot as a place to store the result tuple.
4762  */
4763  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4764 
4765  /*
4766  * Extract all the values of the scan tuple.
4767  */
4768  slot_getallattrs(slot);
4769  old_values = slot->tts_values;
4770  old_isnull = slot->tts_isnull;
4771 
4772  /*
4773  * Prepare to build the result tuple.
4774  */
4775  ExecClearTuple(resultSlot);
4776  values = resultSlot->tts_values;
4777  isnull = resultSlot->tts_isnull;
4778 
4779  /*
4780  * Transpose data into proper fields of the result tuple.
4781  */
4782  for (i = 0; i < resultTupType->natts; i++)
4783  {
4784  int j = dmstate->attnoMap[i];
4785 
4786  if (j == 0)
4787  {
4788  values[i] = (Datum) 0;
4789  isnull[i] = true;
4790  }
4791  else
4792  {
4793  values[i] = old_values[j - 1];
4794  isnull[i] = old_isnull[j - 1];
4795  }
4796  }
4797 
4798  /*
4799  * Build the virtual tuple.
4800  */
4801  ExecStoreVirtualTuple(resultSlot);
4802 
4803  /*
4804  * If we have any system columns to return, materialize a heap tuple in
4805  * the slot from column values set above and install system columns in
4806  * that tuple.
4807  */
4808  if (dmstate->hasSystemCols)
4809  {
4810  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4811 
4812  /* ctid */
4813  if (dmstate->ctidAttno)
4814  {
4815  ItemPointer ctid = NULL;
4816 
4817  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4818  resultTup->t_self = *ctid;
4819  }
4820 
4821  /*
4822  * And remaining columns
4823  *
4824  * Note: since we currently don't allow the target relation to appear
4825  * on the nullable side of an outer join, any system columns wouldn't
4826  * go to NULL.
4827  *
4828  * Note: no need to care about tableoid here because it will be
4829  * initialized in ExecProcessReturning().
4830  */
4834  }
4835 
4836  /*
4837  * And return the result tuple.
4838  */
4839  return resultSlot;
4840 }
static Datum values[MAXATTR]
Definition: bootstrap.c:150
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1639
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1731
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1182
#define HeapTupleHeaderSetXmin(tup, xid)
Definition: htup_details.h:315
#define HeapTupleHeaderSetXmax(tup, xid)
Definition: htup_details.h:376
#define HeapTupleHeaderSetCmin(tup, cid)
Definition: htup_details.h:393
int j
Definition: isn.c:74
int i
Definition: isn.c:73
ItemPointerData * ItemPointer
Definition: itemptr.h:49
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define RelationGetDescr(relation)
Definition: rel.h:531
ItemPointerData t_self
Definition: htup.h:65
HeapTupleHeader t_data
Definition: htup.h:68
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
#define InvalidTransactionId
Definition: transam.h:31
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:368

References PgFdwDirectModifyState::attnoMap, PgFdwDirectModifyState::ctidAttno, DatumGetPointer(), ExecClearTuple(), ExecFetchSlotHeapTuple(), ExecGetReturningSlot(), ExecStoreVirtualTuple(), PgFdwDirectModifyState::hasSystemCols, HeapTupleHeaderSetCmin, HeapTupleHeaderSetXmax, HeapTupleHeaderSetXmin, i, InvalidTransactionId, j, TupleDescData::natts, RelationGetDescr, PgFdwDirectModifyState::resultRel, slot_getallattrs(), HeapTupleData::t_data, HeapTupleData::t_self, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, and values.

Referenced by get_returning_data().

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 6207 of file postgres_fdw.c.

6208 {
6209  ListCell *lc;
6210 
6211  foreach(lc, fpinfo->server->options)
6212  {
6213  DefElem *def = (DefElem *) lfirst(lc);
6214 
6215  if (strcmp(def->defname, "use_remote_estimate") == 0)
6216  fpinfo->use_remote_estimate = defGetBoolean(def);
6217  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
6218  (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
6219  NULL);
6220  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
6221  (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
6222  NULL);
6223  else if (strcmp(def->defname, "extensions") == 0)
6224  fpinfo->shippable_extensions =
6225  ExtractExtensionList(defGetString(def), false);
6226  else if (strcmp(def->defname, "fetch_size") == 0)
6227  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6228  else if (strcmp(def->defname, "async_capable") == 0)
6229  fpinfo->async_capable = defGetBoolean(def);
6230  }
6231 }
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:445
bool defGetBoolean(DefElem *def)
Definition: define.c:107
char * defGetString(DefElem *def)
Definition: define.c:48
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:2870
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:2960
char * defname
Definition: parsenodes.h:815
List * options
Definition: foreign.h:42
List * shippable_extensions
Definition: postgres_fdw.h:81

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

Referenced by postgresGetForeignRelSize().

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 6239 of file postgres_fdw.c.

6240 {
6241  ListCell *lc;
6242 
6243  foreach(lc, fpinfo->table->options)
6244  {
6245  DefElem *def = (DefElem *) lfirst(lc);
6246 
6247  if (strcmp(def->defname, "use_remote_estimate") == 0)
6248  fpinfo->use_remote_estimate = defGetBoolean(def);
6249  else if (strcmp(def->defname, "fetch_size") == 0)
6250  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6251  else if (strcmp(def->defname, "async_capable") == 0)
6252  fpinfo->async_capable = defGetBoolean(def);
6253  }
6254 }
List * options
Definition: foreign.h:57

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

Referenced by postgresGetForeignRelSize().

◆ build_remote_returning()

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

Definition at line 4420 of file postgres_fdw.c.

4421 {
4422  bool have_wholerow = false;
4423  List *tlist = NIL;
4424  List *vars;
4425  ListCell *lc;
4426 
4427  Assert(returningList);
4428 
4429  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4430 
4431  /*
4432  * If there's a whole-row reference to the target relation, then we'll
4433  * need all the columns of the relation.
4434  */
4435  foreach(lc, vars)
4436  {
4437  Var *var = (Var *) lfirst(lc);
4438 
4439  if (IsA(var, Var) &&
4440  var->varno == rtindex &&
4441  var->varattno == InvalidAttrNumber)
4442  {
4443  have_wholerow = true;
4444  break;
4445  }
4446  }
4447 
4448  if (have_wholerow)
4449  {
4450  TupleDesc tupdesc = RelationGetDescr(rel);
4451  int i;
4452 
4453  for (i = 1; i <= tupdesc->natts; i++)
4454  {
4455  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4456  Var *var;
4457 
4458  /* Ignore dropped attributes. */
4459  if (attr->attisdropped)
4460  continue;
4461 
4462  var = makeVar(rtindex,
4463  i,
4464  attr->atttypid,
4465  attr->atttypmod,
4466  attr->attcollation,
4467  0);
4468 
4469  tlist = lappend(tlist,
4470  makeTargetEntry((Expr *) var,
4471  list_length(tlist) + 1,
4472  NULL,
4473  false));
4474  }
4475  }
4476 
4477  /* Now add any remaining columns to tlist. */
4478  foreach(lc, vars)
4479  {
4480  Var *var = (Var *) lfirst(lc);
4481 
4482  /*
4483  * No need for whole-row references to the target relation. We don't
4484  * need system columns other than ctid and oid either, since those are
4485  * set locally.
4486  */
4487  if (IsA(var, Var) &&
4488  var->varno == rtindex &&
4489  var->varattno <= InvalidAttrNumber &&
4491  continue; /* don't need it */
4492 
4493  if (tlist_member((Expr *) var, tlist))
4494  continue; /* already got it */
4495 
4496  tlist = lappend(tlist,
4497  makeTargetEntry((Expr *) var,
4498  list_length(tlist) + 1,
4499  NULL,
4500  false));
4501  }
4502 
4503  list_free(vars);
4504 
4505  return tlist;
4506 }
#define InvalidAttrNumber
Definition: attnum.h:23
List * lappend(List *list, void *datum)
Definition: list.c:339
void list_free(List *list)
Definition: list.c:1546
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:240
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
#define PVC_INCLUDE_PLACEHOLDERS
Definition: optimizer.h:190
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
Definition: primnodes.h:248
AttrNumber varattno
Definition: primnodes.h:260
int varno
Definition: primnodes.h:255
Definition: regcomp.c:281
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition: tlist.c:79
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

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

Referenced by postgresPlanDirectModify().

◆ close_cursor()

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

Definition at line 3942 of file postgres_fdw.c.

3944 {
3945  char sql[64];
3946  PGresult *res;
3947 
3948  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3949 
3950  /*
3951  * We don't use a PG_TRY block here, so be careful not to throw error
3952  * without releasing the PGresult.
3953  */
3954  res = pgfdw_exec_query(conn, sql, conn_state);
3956  pgfdw_report_error(ERROR, res, conn, true, sql);
3957  PQclear(res);
3958 }
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:889
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:853
static unsigned int cursor_number
Definition: connection.c:84
#define ERROR
Definition: elog.h:39
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3411
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:99
#define snprintf
Definition: port.h:238
PGconn * conn
Definition: streamutil.c:55

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

Referenced by postgresAcquireSampleRowsFunc(), and postgresEndForeignScan().

◆ complete_pending_request()

static void complete_pending_request ( AsyncRequest areq)
static

Definition at line 7506 of file postgres_fdw.c.

7507 {
7508  /* The request would have been pending for a callback */
7509  Assert(areq->callback_pending);
7510 
7511  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7512  areq->callback_pending = false;
7513 
7514  /* We begin a fetch afterwards if necessary; don't fetch */
7515  produce_tuple_asynchronously(areq, false);
7516 
7517  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7518  ExecAsyncResponse(areq);
7519 
7520  /* Also, we do instrumentation ourselves, if required */
7521  if (areq->requestee->instrument)
7523  TupIsNull(areq->result) ? 0.0 : 1.0);
7524 }
void ExecAsyncResponse(AsyncRequest *areq)
Definition: execAsync.c:117
void InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
Definition: instrument.c:132
static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch)
TupleTableSlot * result
Definition: execnodes.h:608
bool callback_pending
Definition: execnodes.h:606
struct PlanState * requestee
Definition: execnodes.h:604
Instrumentation * instrument
Definition: execnodes.h:1126
#define TupIsNull(slot)
Definition: tuptable.h:306

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

Referenced by postgresForeignAsyncConfigureWait().

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

Definition at line 7697 of file postgres_fdw.c.

7698 {
7700  Relation rel = errpos->rel;
7701  ForeignScanState *fsstate = errpos->fsstate;
7702  const char *attname = NULL;
7703  const char *relname = NULL;
7704  bool is_wholerow = false;
7705 
7706  /*
7707  * If we're in a scan node, always use aliases from the rangetable, for
7708  * consistency between the simple-relation and remote-join cases. Look at
7709  * the relation's tupdesc only if we're not in a scan node.
7710  */
7711  if (fsstate)
7712  {
7713  /* ForeignScan case */
7714  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7715  int varno = 0;
7716  AttrNumber colno = 0;
7717 
7718  if (fsplan->scan.scanrelid > 0)
7719  {
7720  /* error occurred in a scan against a foreign table */
7721  varno = fsplan->scan.scanrelid;
7722  colno = errpos->cur_attno;
7723  }
7724  else
7725  {
7726  /* error occurred in a scan against a foreign join */
7727  TargetEntry *tle;
7728 
7729  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7730  errpos->cur_attno - 1);
7731 
7732  /*
7733  * Target list can have Vars and expressions. For Vars, we can
7734  * get some information, however for expressions we can't. Thus
7735  * for expressions, just show generic context message.
7736  */
7737  if (IsA(tle->expr, Var))
7738  {
7739  Var *var = (Var *) tle->expr;
7740 
7741  varno = var->varno;
7742  colno = var->varattno;
7743  }
7744  }
7745 
7746  if (varno > 0)
7747  {
7748  EState *estate = fsstate->ss.ps.state;
7749  RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7750 
7751  relname = rte->eref->aliasname;
7752 
7753  if (colno == 0)
7754  is_wholerow = true;
7755  else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7756  attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7757  else if (colno == SelfItemPointerAttributeNumber)
7758  attname = "ctid";
7759  }
7760  }
7761  else if (rel)
7762  {
7763  /* Non-ForeignScan case (we should always have a rel here) */
7764  TupleDesc tupdesc = RelationGetDescr(rel);
7765 
7767  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7768  {
7769  Form_pg_attribute attr = TupleDescAttr(tupdesc,
7770  errpos->cur_attno - 1);
7771 
7772  attname = NameStr(attr->attname);
7773  }
7774  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7775  attname = "ctid";
7776  }
7777 
7778  if (relname && is_wholerow)
7779  errcontext("whole-row reference to foreign table \"%s\"", relname);
7780  else if (relname && attname)
7781  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7782  else
7783  errcontext("processing expression at position %d in select list",
7784  errpos->cur_attno);
7785 }
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:746
#define errcontext
Definition: elog.h:196
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:587
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
NameData attname
Definition: pg_attribute.h:41
void * arg
NameData relname
Definition: pg_class.h:38
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
#define RelationGetRelationName(relation)
Definition: rel.h:539
AttrNumber cur_attno
Definition: postgres_fdw.c:307
ForeignScanState * fsstate
Definition: postgres_fdw.c:309
ScanState ss
Definition: execnodes.h:2064
List * fdw_scan_tlist
Definition: plannodes.h:717
Plan * plan
Definition: execnodes.h:1116
EState * state
Definition: execnodes.h:1118
PlanState ps
Definition: execnodes.h:1563
Index scanrelid
Definition: plannodes.h:389
Expr * expr
Definition: primnodes.h:2186
#define strVal(v)
Definition: value.h:82

References arg, attname, castNode, ConversionLocation::cur_attno, errcontext, exec_rt_fetch(), TargetEntry::expr, ForeignScan::fdw_scan_tlist, ConversionLocation::fsstate, IsA, list_length(), list_nth(), list_nth_node, NameStr, TupleDescData::natts, PlanState::plan, ScanState::ps, ConversionLocation::rel, RelationGetDescr, RelationGetRelationName, relname, ForeignScan::scan, Scan::scanrelid, SelfItemPointerAttributeNumber, ForeignScanState::ss, PlanState::state, strVal, TupleDescAttr, Var::varattno, and Var::varno.

Referenced by make_tuple_from_result_row().

◆ convert_prep_stmt_params()

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

Definition at line 4261 of file postgres_fdw.c.

4265 {
4266  const char **p_values;
4267  int i;
4268  int j;
4269  int pindex = 0;
4270  MemoryContext oldcontext;
4271 
4272  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4273 
4274  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4275 
4276  /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4277  Assert(!(tupleid != NULL && numSlots > 1));
4278 
4279  /* 1st parameter should be ctid, if it's in use */
4280  if (tupleid != NULL)
4281  {
4282  Assert(numSlots == 1);
4283  /* don't need set_transmission_modes for TID output */
4284  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
4285  PointerGetDatum(tupleid));
4286  pindex++;
4287  }
4288 
4289  /* get following parameters from slots */
4290  if (slots != NULL && fmstate->target_attrs != NIL)
4291  {
4292  TupleDesc tupdesc = RelationGetDescr(fmstate->rel);
4293  int nestlevel;
4294  ListCell *lc;
4295 
4296  nestlevel = set_transmission_modes();
4297 
4298  for (i = 0; i < numSlots; i++)
4299  {
4300  j = (tupleid != NULL) ? 1 : 0;
4301  foreach(lc, fmstate->target_attrs)
4302  {
4303  int attnum = lfirst_int(lc);
4304  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4305  Datum value;
4306  bool isnull;
4307 
4308  /* Ignore generated columns; they are set to DEFAULT */
4309  if (attr->attgenerated)
4310  continue;
4311  value = slot_getattr(slots[i], attnum, &isnull);
4312  if (isnull)
4313  p_values[pindex] = NULL;
4314  else
4315  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[j],
4316  value);
4317  pindex++;
4318  j++;
4319  }
4320  }
4321 
4322  reset_transmission_modes(nestlevel);
4323  }
4324 
4325  Assert(pindex == fmstate->p_nums * numSlots);
4326 
4327  MemoryContextSwitchTo(oldcontext);
4328 
4329  return p_values;
4330 }
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
static struct @155 value
void * palloc(Size size)
Definition: mcxt.c:1317
int16 attnum
Definition: pg_attribute.h:74
#define lfirst_int(lc)
Definition: pg_list.h:173
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
void reset_transmission_modes(int nestlevel)
int set_transmission_modes(void)
MemoryContext temp_cxt
Definition: postgres_fdw.c:209
FmgrInfo * p_flinfo
Definition: postgres_fdw.c:203
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:395

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

Referenced by execute_foreign_modify().

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

Definition at line 3718 of file postgres_fdw.c.

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

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

Referenced by fetch_more_data_begin(), and postgresIterateForeignScan().

◆ create_foreign_modify()

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

Definition at line 3966 of file postgres_fdw.c.

3976 {
3977  PgFdwModifyState *fmstate;
3978  Relation rel = resultRelInfo->ri_RelationDesc;
3979  TupleDesc tupdesc = RelationGetDescr(rel);
3980  Oid userid;
3981  ForeignTable *table;
3982  UserMapping *user;
3983  AttrNumber n_params;
3984  Oid typefnoid;
3985  bool isvarlena;
3986  ListCell *lc;
3987 
3988  /* Begin constructing PgFdwModifyState. */
3989  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3990  fmstate->rel = rel;
3991 
3992  /* Identify which user to do the remote access as. */
3993  userid = ExecGetResultRelCheckAsUser(resultRelInfo, estate);
3994 
3995  /* Get info about foreign table. */
3996  table = GetForeignTable(RelationGetRelid(rel));
3997  user = GetUserMapping(userid, table->serverid);
3998 
3999  /* Open connection; report that we'll create a prepared statement. */
4000  fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
4001  fmstate->p_name = NULL; /* prepared statement not made yet */
4002 
4003  /* Set up remote query information. */
4004  fmstate->query = query;
4005  if (operation == CMD_INSERT)
4006  {
4007  fmstate->query = pstrdup(fmstate->query);
4008  fmstate->orig_query = pstrdup(fmstate->query);
4009  }
4010  fmstate->target_attrs = target_attrs;
4011  fmstate->values_end = values_end;
4012  fmstate->has_returning = has_returning;
4013  fmstate->retrieved_attrs = retrieved_attrs;
4014 
4015  /* Create context for per-tuple temp workspace. */
4016  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
4017  "postgres_fdw temporary data",
4019 
4020  /* Prepare for input conversion of RETURNING results. */
4021  if (fmstate->has_returning)
4022  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
4023 
4024  /* Prepare for output conversion of parameters used in prepared stmt. */
4025  n_params = list_length(fmstate->target_attrs) + 1;
4026  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
4027  fmstate->p_nums = 0;
4028 
4029  if (operation == CMD_UPDATE || operation == CMD_DELETE)
4030  {
4031  Assert(subplan != NULL);
4032 
4033  /* Find the ctid resjunk column in the subplan's result */
4035  "ctid");
4036  if (!AttributeNumberIsValid(fmstate->ctidAttno))
4037  elog(ERROR, "could not find junk ctid column");
4038 
4039  /* First transmittable parameter will be ctid */
4040  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
4041  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4042  fmstate->p_nums++;
4043  }
4044 
4045  if (operation == CMD_INSERT || operation == CMD_UPDATE)
4046  {
4047  /* Set up for remaining transmittable parameters */
4048  foreach(lc, fmstate->target_attrs)
4049  {
4050  int attnum = lfirst_int(lc);
4051  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4052 
4053  Assert(!attr->attisdropped);
4054 
4055  /* Ignore generated columns; they are set to DEFAULT */
4056  if (attr->attgenerated)
4057  continue;
4058  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
4059  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4060  fmstate->p_nums++;
4061  }
4062  }
4063 
4064  Assert(fmstate->p_nums <= n_params);
4065 
4066  /* Set batch_size from foreign server/table options. */
4067  if (operation == CMD_INSERT)
4068  fmstate->batch_size = get_batch_size_option(rel);
4069 
4070  fmstate->num_slots = 1;
4071 
4072  /* Initialize auxiliary state */
4073  fmstate->aux_fmstate = NULL;
4074 
4075  return fmstate;
4076 }
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
Definition: connection.c:195
#define elog(elevel,...)
Definition: elog.h:224
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2173
Oid ExecGetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate)
Definition: execUtils.c:1395
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:253
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition: foreign.c:199
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2907
char * pstrdup(const char *in)
Definition: mcxt.c:1696
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
static char * user
Definition: pg_regress.c:120
unsigned int Oid
Definition: postgres_ext.h:31
static int get_batch_size_option(Relation rel)
#define RelationGetRelid(relation)
Definition: rel.h:505
MemoryContext es_query_cxt
Definition: execnodes.h:667
Definition: fmgr.h:57
Oid serverid
Definition: foreign.h:56
AttInMetadata * attinmeta
Definition: postgres_fdw.c:184
PgFdwConnState * conn_state
Definition: postgres_fdw.c:188
AttrNumber ctidAttno
Definition: postgres_fdw.c:201
struct PgFdwModifyState * aux_fmstate
Definition: postgres_fdw.c:212
List * retrieved_attrs
Definition: postgres_fdw.c:198
List * targetlist
Definition: plannodes.h:152
Relation ri_RelationDesc
Definition: execnodes.h:456

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, Assert, PgFdwModifyState::attinmeta, attnum, AttributeNumberIsValid, PgFdwModifyState::aux_fmstate, PgFdwModifyState::batch_size, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, PgFdwModifyState::conn_state, PgFdwModifyState::ctidAttno, elog, ERROR, EState::es_query_cxt, ExecFindJunkAttributeInTlist(), ExecGetResultRelCheckAsUser(), fmgr_info(), get_batch_size_option(), GetConnection(), GetForeignTable(), getTypeOutputInfo(), GetUserMapping(), PgFdwModifyState::has_returning, lfirst_int, list_length(), PgFdwModifyState::num_slots, PgFdwModifyState::orig_query, PgFdwModifyState::p_flinfo, PgFdwModifyState::p_name, PgFdwModifyState::p_nums, palloc0(), pstrdup(), PgFdwModifyState::query, PgFdwModifyState::rel, RelationGetDescr, RelationGetRelid, PgFdwModifyState::retrieved_attrs, ResultRelInfo::ri_RelationDesc, ForeignTable::serverid, PgFdwModifyState::target_attrs, Plan::targetlist, PgFdwModifyState::temp_cxt, TupleDescAttr, TupleDescGetAttInMetadata(), user, and PgFdwModifyState::values_end.

Referenced by postgresBeginForeignInsert(), and postgresBeginForeignModify().

◆ deallocate_query()

static void deallocate_query ( PgFdwModifyState fmstate)
static

Definition at line 4391 of file postgres_fdw.c.

4392 {
4393  char sql[64];
4394  PGresult *res;
4395 
4396  /* do nothing if the query is not allocated */
4397  if (!fmstate->p_name)
4398  return;
4399 
4400  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4401 
4402  /*
4403  * We don't use a PG_TRY block here, so be careful not to throw error
4404  * without releasing the PGresult.
4405  */
4406  res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4408  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
4409  PQclear(res);
4410  pfree(fmstate->p_name);
4411  fmstate->p_name = NULL;
4412 }

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

Referenced by execute_foreign_modify(), and finish_foreign_modify().

◆ ec_member_matches_foreign()

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

Definition at line 3689 of file postgres_fdw.c.

3692 {
3694  Expr *expr = em->em_expr;
3695 
3696  /*
3697  * If we've identified what we're processing in the current scan, we only
3698  * want to match that expression.
3699  */
3700  if (state->current != NULL)
3701  return equal(expr, state->current);
3702 
3703  /*
3704  * Otherwise, ignore anything we've already processed.
3705  */
3706  if (list_member(state->already_used, expr))
3707  return false;
3708 
3709  /* This is the new target to process. */
3710  state->current = expr;
3711  return true;
3712 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
bool list_member(const List *list, const void *datum)
Definition: list.c:661
Definition: regguts.h:323

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

Referenced by postgresGetForeignPaths().

◆ estimate_path_cost_size()

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

Definition at line 3085 of file postgres_fdw.c.

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

References adjust_foreign_grouping_path_cost(), adjust_limit_rows_costs(), AGGSPLIT_SIMPLE, appendStringInfoString(), Assert, RelOptInfo::baserestrictcost, build_tlist_to_deparse(), clamp_row_est(), classifyConditions(), clauselist_selectivity(), conn, PathTarget::cost, cost_qual_eval(), PgFdwPathExtraData::count_est, cpu_operator_cost, cpu_tuple_cost, StringInfoData::data, DEFAULT_FDW_SORT_MULTIPLIER, deparseSelectStmtForRel(), estimate_num_groups(), PgFdwRelationInfo::fdw_startup_cost, PgFdwRelationInfo::fdw_tuple_cost, AggClauseCosts::finalCost, get_agg_clause_costs(), get_remote_estimate(), get_sortgrouplist_exprs(), GetConnection(), PgFdwRelationInfo::grouped_tlist, PgFdwPathExtraData::has_final_sort, PgFdwPathExtraData::has_limit, initStringInfo(), PgFdwRelationInfo::innerrel, IS_JOIN_REL, IS_UPPER_REL, JOIN_INNER, PgFdwRelationInfo::joinclause_sel, PgFdwRelationInfo::joinclauses, PgFdwPathExtraData::limit_tuples, list_concat(), list_length(), PgFdwRelationInfo::local_conds_cost, PgFdwRelationInfo::local_conds_sel, MemSet, Min, NIL, PgFdwPathExtraData::offset_est, PgFdwRelationInfo::outerrel, RelOptInfo::pages, QualCost::per_tuple, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, ReleaseConnection(), RelOptInfo::relid, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, PgFdwRelationInfo::retrieved_rows, root, PgFdwRelationInfo::rows, RelOptInfo::rows, seq_page_cost, PgFdwRelationInfo::stage, QualCost::startup, PgFdwPathExtraData::target, AggClauseCosts::transCost, RelOptInfo::tuples, UPPERREL_GROUP_AGG, PgFdwRelationInfo::use_remote_estimate, PgFdwRelationInfo::user, PgFdwRelationInfo::width, and PathTarget::width.

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

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

Definition at line 4547 of file postgres_fdw.c.

4548 {
4550  ExprContext *econtext = node->ss.ps.ps_ExprContext;
4551  int numParams = dmstate->numParams;
4552  const char **values = dmstate->param_values;
4553 
4554  /* First, process a pending asynchronous request, if any. */
4555  if (dmstate->conn_state->pendingAreq)
4557 
4558  /*
4559  * Construct array of query parameter values in text format.
4560  */
4561  if (numParams > 0)
4562  process_query_params(econtext,
4563  dmstate->param_flinfo,
4564  dmstate->param_exprs,
4565  values);
4566 
4567  /*
4568  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4569  * to infer types for all parameters. Since we explicitly cast every
4570  * parameter (see deparse.c), the "inference" is trivial and will produce
4571  * the desired result. This allows us to avoid assuming that the remote
4572  * server has the same OIDs we do for the parameters' types.
4573  */
4574  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4575  NULL, values, NULL, NULL, 0))
4576  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4577 
4578  /*
4579  * Get the result, and check for success.
4580  *
4581  * We don't use a PG_TRY block here, so be careful not to throw error
4582  * without releasing the PGresult.
4583  */
4584  dmstate->result = pgfdw_get_result(dmstate->conn);
4585  if (PQresultStatus(dmstate->result) !=
4587  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4588  dmstate->query);
4589 
4590  /* Get the number of rows affected. */
4591  if (dmstate->has_returning)
4592  dmstate->num_tuples = PQntuples(dmstate->result);
4593  else
4594  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4595 }
char * PQcmdTuples(PGresult *res)
Definition: fe-exec.c:3822
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3481
@ PGRES_TUPLES_OK
Definition: libpq-fe.h:102
PgFdwConnState * conn_state
Definition: postgres_fdw.c:232
const char ** param_values
Definition: postgres_fdw.c:236

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

Referenced by postgresIterateDirectModify().

◆ execute_foreign_modify()

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

Definition at line 4086 of file postgres_fdw.c.

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

References Assert, CMD_DELETE, CMD_INSERT, CMD_UPDATE, PgFdwModifyState::conn, PgFdwModifyState::conn_state, convert_prep_stmt_params(), PgFdwModifyState::ctidAttno, DatumGetPointer(), deallocate_query(), elog, ERROR, ExecGetJunkAttribute(), PgFdwModifyState::has_returning, initStringInfo(), MemoryContextReset(), PgFdwModifyState::num_slots, PgFdwModifyState::orig_query, PgFdwModifyState::p_name, PgFdwModifyState::p_nums, PgFdwConnState::pendingAreq, pfree(), pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQcmdTuples(), PQntuples(), PQresultStatus(), PQsendQueryPrepared(), prepare_foreign_modify(), process_pending_request(), PgFdwModifyState::query, rebuildInsertSql(), PgFdwModifyState::rel, res, ResultRelInfo::ri_FdwState, store_returning_result(), PgFdwModifyState::target_attrs, PgFdwModifyState::temp_cxt, and PgFdwModifyState::values_end.

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

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3794 of file postgres_fdw.c.

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

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

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

◆ fetch_more_data_begin()

static void fetch_more_data_begin ( AsyncRequest areq)
static

Definition at line 7446 of file postgres_fdw.c.

7447 {
7448  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7449  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7450  char sql[64];
7451 
7452  Assert(!fsstate->conn_state->pendingAreq);
7453 
7454  /* Create the cursor synchronously. */
7455  if (!fsstate->cursor_exists)
7456  create_cursor(node);
7457 
7458  /* We will send this query, but not wait for the response. */
7459  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7460  fsstate->fetch_size, fsstate->cursor_number);
7461 
7462  if (!PQsendQuery(fsstate->conn, sql))
7463  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7464 
7465  /* Remember that the request is in process */
7466  fsstate->conn_state->pendingAreq = areq;
7467 }
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1416
static void create_cursor(ForeignScanState *node)

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

Referenced by postgresForeignAsyncConfigureWait(), and produce_tuple_asynchronously().

◆ find_em_for_rel()

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

Definition at line 7800 of file postgres_fdw.c.

7801 {
7802  ListCell *lc;
7803 
7804  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7805 
7806  foreach(lc, ec->ec_members)
7807  {
7809 
7810  /*
7811  * Note we require !bms_is_empty, else we'd accept constant
7812  * expressions which are not suitable for the purpose.
7813  */
7814  if (bms_is_subset(em->em_relids, rel->relids) &&
7815  !bms_is_empty(em->em_relids) &&
7817  is_foreign_expr(root, rel, em->em_expr))
7818  return em;
7819  }
7820 
7821  return NULL;
7822 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:412
Bitmapset * bms_intersect(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:292
#define bms_is_empty(a)
Definition: bitmapset.h:118
Relids hidden_subquery_rels
Definition: postgres_fdw.h:121
Relids relids
Definition: pathnodes.h:865

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

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

◆ find_em_for_rel_target()

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

Definition at line 7836 of file postgres_fdw.c.

7838 {
7839  PathTarget *target = rel->reltarget;
7840  ListCell *lc1;
7841  int i;
7842 
7843  i = 0;
7844  foreach(lc1, target->exprs)
7845  {
7846  Expr *expr = (Expr *) lfirst(lc1);
7847  Index sgref = get_pathtarget_sortgroupref(target, i);
7848  ListCell *lc2;
7849 
7850  /* Ignore non-sort expressions */
7851  if (sgref == 0 ||
7853  root->parse->sortClause) == NULL)
7854  {
7855  i++;
7856  continue;
7857  }
7858 
7859  /* We ignore binary-compatible relabeling on both ends */
7860  while (expr && IsA(expr, RelabelType))
7861  expr = ((RelabelType *) expr)->arg;
7862 
7863  /* Locate an EquivalenceClass member matching this expr, if any */
7864  foreach(lc2, ec->ec_members)
7865  {
7867  Expr *em_expr;
7868 
7869  /* Don't match constants */
7870  if (em->em_is_const)
7871  continue;
7872 
7873  /* Ignore child members */
7874  if (em->em_is_child)
7875  continue;
7876 
7877  /* Match if same expression (after stripping relabel) */
7878  em_expr = em->em_expr;
7879  while (em_expr && IsA(em_expr, RelabelType))
7880  em_expr = ((RelabelType *) em_expr)->arg;
7881 
7882  if (!equal(em_expr, expr))
7883  continue;
7884 
7885  /* Check that expression (including relabels!) is shippable */
7886  if (is_foreign_expr(root, rel, em->em_expr))
7887  return em;
7888  }
7889 
7890  i++;
7891  }
7892 
7893  return NULL;
7894 }
unsigned int Index
Definition: c.h:614
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1549
List * exprs
Definition: pathnodes.h:1533
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:443

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

Referenced by add_foreign_ordered_paths(), and appendOrderByClause().

◆ find_modifytable_subplan()

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

Definition at line 2384 of file postgres_fdw.c.

2388 {
2389  Plan *subplan = outerPlan(plan);
2390 
2391  /*
2392  * The cases we support are (1) the desired ForeignScan is the immediate
2393  * child of ModifyTable, or (2) it is the subplan_index'th child of an
2394  * Append node that is the immediate child of ModifyTable. There is no
2395  * point in looking further down, as that would mean that local joins are
2396  * involved, so we can't do the update directly.
2397  *
2398  * There could be a Result atop the Append too, acting to compute the
2399  * UPDATE targetlist values. We ignore that here; the tlist will be
2400  * checked by our caller.
2401  *
2402  * In principle we could examine all the children of the Append, but it's
2403  * currently unlikely that the core planner would generate such a plan
2404  * with the children out-of-order. Moreover, such a search risks costing
2405  * O(N^2) time when there are a lot of children.
2406  */
2407  if (IsA(subplan, Append))
2408  {
2409  Append *appendplan = (Append *) subplan;
2410 
2411  if (subplan_index < list_length(appendplan->appendplans))
2412  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2413  }
2414  else if (IsA(subplan, Result) &&
2415  outerPlan(subplan) != NULL &&
2416  IsA(outerPlan(subplan), Append))
2417  {
2418  Append *appendplan = (Append *) outerPlan(subplan);
2419 
2420  if (subplan_index < list_length(appendplan->appendplans))
2421  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2422  }
2423 
2424  /* Now, have we got a ForeignScan on the desired rel? */
2425  if (IsA(subplan, ForeignScan))
2426  {
2427  ForeignScan *fscan = (ForeignScan *) subplan;
2428 
2429  if (bms_is_member(rtindex, fscan->fs_base_relids))
2430  return fscan;
2431  }
2432 
2433  return NULL;
2434 }
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
#define plan(x)
Definition: pg_regress.c:162
#define outerPlan(node)
Definition: plannodes.h:182
List * appendplans
Definition: plannodes.h:269
Bitmapset * fs_base_relids
Definition: plannodes.h:720

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

Referenced by postgresPlanDirectModify().

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4373 of file postgres_fdw.c.

4374 {
4375  Assert(fmstate != NULL);
4376 
4377  /* If we created a prepared statement, destroy it */
4378  deallocate_query(fmstate);
4379 
4380  /* Release remote connection */
4381  ReleaseConnection(fmstate->conn);
4382  fmstate->conn = NULL;
4383 }

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

Referenced by postgresEndForeignInsert(), and postgresEndForeignModify().

◆ foreign_grouping_ok()

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

Definition at line 6468 of file postgres_fdw.c.

6470 {
6471  Query *query = root->parse;
6472  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
6473  PathTarget *grouping_target = grouped_rel->reltarget;
6474  PgFdwRelationInfo *ofpinfo;
6475  ListCell *lc;
6476  int i;
6477  List *tlist = NIL;
6478 
6479  /* We currently don't support pushing Grouping Sets. */
6480  if (query->groupingSets)
6481  return false;
6482 
6483  /* Get the fpinfo of the underlying scan relation. */
6484  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
6485 
6486  /*
6487  * If underlying scan relation has any local conditions, those conditions
6488  * are required to be applied before performing aggregation. Hence the
6489  * aggregate cannot be pushed down.
6490  */
6491  if (ofpinfo->local_conds)
6492  return false;
6493 
6494  /*
6495  * Examine grouping expressions, as well as other expressions we'd need to
6496  * compute, and check whether they are safe to push down to the foreign
6497  * server. All GROUP BY expressions will be part of the grouping target
6498  * and thus there is no need to search for them separately. Add grouping
6499  * expressions into target list which will be passed to foreign server.
6500  *
6501  * A tricky fine point is that we must not put any expression into the
6502  * target list that is just a foreign param (that is, something that
6503  * deparse.c would conclude has to be sent to the foreign server). If we
6504  * do, the expression will also appear in the fdw_exprs list of the plan
6505  * node, and setrefs.c will get confused and decide that the fdw_exprs
6506  * entry is actually a reference to the fdw_scan_tlist entry, resulting in
6507  * a broken plan. Somewhat oddly, it's OK if the expression contains such
6508  * a node, as long as it's not at top level; then no match is possible.
6509  */
6510  i = 0;
6511  foreach(lc, grouping_target->exprs)
6512  {
6513  Expr *expr = (Expr *) lfirst(lc);
6514  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
6515  ListCell *l;
6516 
6517  /*
6518  * Check whether this expression is part of GROUP BY clause. Note we
6519  * check the whole GROUP BY clause not just processed_groupClause,
6520  * because we will ship all of it, cf. appendGroupByClause.
6521  */
6522  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
6523  {
6524  TargetEntry *tle;
6525 
6526  /*
6527  * If any GROUP BY expression is not shippable, then we cannot
6528  * push down aggregation to the foreign server.
6529  */
6530  if (!is_foreign_expr(root, grouped_rel, expr))
6531  return false;
6532 
6533  /*
6534  * If it would be a foreign param, we can't put it into the tlist,
6535  * so we have to fail.
6536  */
6537  if (is_foreign_param(root, grouped_rel, expr))
6538  return false;
6539 
6540  /*
6541  * Pushable, so add to tlist. We need to create a TLE for this
6542  * expression and apply the sortgroupref to it. We cannot use
6543  * add_to_flat_tlist() here because that avoids making duplicate
6544  * entries in the tlist. If there are duplicate entries with
6545  * distinct sortgrouprefs, we have to duplicate that situation in
6546  * the output tlist.
6547  */
6548  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
6549  tle->ressortgroupref = sgref;
6550  tlist = lappend(tlist, tle);
6551  }
6552  else
6553  {
6554  /*
6555  * Non-grouping expression we need to compute. Can we ship it
6556  * as-is to the foreign server?
6557  */
6558  if (is_foreign_expr(root, grouped_rel, expr) &&
6559  !is_foreign_param(root, grouped_rel, expr))
6560  {
6561  /* Yes, so add to tlist as-is; OK to suppress duplicates */
6562  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6563  }
6564  else
6565  {
6566  /* Not pushable as a whole; extract its Vars and aggregates */
6567  List *aggvars;
6568 
6569  aggvars = pull_var_clause((Node *) expr,
6571 
6572  /*
6573  * If any aggregate expression is not shippable, then we
6574  * cannot push down aggregation to the foreign server. (We
6575  * don't have to check is_foreign_param, since that certainly
6576  * won't return true for any such expression.)
6577  */
6578  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
6579  return false;
6580 
6581  /*
6582  * Add aggregates, if any, into the targetlist. Plain Vars
6583  * outside an aggregate can be ignored, because they should be
6584  * either same as some GROUP BY column or part of some GROUP
6585  * BY expression. In either case, they are already part of
6586  * the targetlist and thus no need to add them again. In fact
6587  * including plain Vars in the tlist when they do not match a
6588  * GROUP BY column would cause the foreign server to complain
6589  * that the shipped query is invalid.
6590  */
6591  foreach(l, aggvars)
6592  {
6593  Expr *aggref = (Expr *) lfirst(l);
6594 
6595  if (IsA(aggref, Aggref))
6596  tlist = add_to_flat_tlist(tlist, list_make1(aggref));
6597  }
6598  }
6599  }
6600 
6601  i++;
6602  }
6603 
6604  /*
6605  * Classify the pushable and non-pushable HAVING clauses and save them in
6606  * remote_conds and local_conds of the grouped rel's fpinfo.
6607  */
6608  if (havingQual)
6609  {
6610  foreach(lc, (List *) havingQual)
6611  {
6612  Expr *expr = (Expr *) lfirst(lc);
6613  RestrictInfo *rinfo;
6614 
6615  /*
6616  * Currently, the core code doesn't wrap havingQuals in
6617  * RestrictInfos, so we must make our own.
6618  */
6619  Assert(!IsA(expr, RestrictInfo));
6620  rinfo = make_restrictinfo(root,
6621  expr,
6622  true,
6623  false,
6624  false,
6625  false,
6626  root->qual_security_level,
6627  grouped_rel->relids,
6628  NULL,
6629  NULL);
6630  if (is_foreign_expr(root, grouped_rel, expr))
6631  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
6632  else
6633  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
6634  }
6635  }
6636 
6637  /*
6638  * If there are any local conditions, pull Vars and aggregates from it and
6639  * check whether they are safe to pushdown or not.
6640  */
6641  if (fpinfo->local_conds)
6642  {
6643  List *aggvars = NIL;
6644 
6645  foreach(lc, fpinfo->local_conds)
6646  {
6647  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6648 
6649  aggvars = list_concat(aggvars,
6650  pull_var_clause((Node *) rinfo->clause,
6652  }
6653 
6654  foreach(lc, aggvars)
6655  {
6656  Expr *expr = (Expr *) lfirst(lc);
6657 
6658  /*
6659  * If aggregates within local conditions are not safe to push
6660  * down, then we cannot push down the query. Vars are already
6661  * part of GROUP BY clause which are checked above, so no need to
6662  * access them again here. Again, we need not check
6663  * is_foreign_param for a foreign aggregate.
6664  */
6665  if (IsA(expr, Aggref))
6666  {
6667  if (!is_foreign_expr(root, grouped_rel, expr))
6668  return false;
6669 
6670  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6671  }
6672  }
6673  }
6674 
6675  /* Store generated targetlist */
6676  fpinfo->grouped_tlist = tlist;
6677 
6678  /* Safe to pushdown */
6679  fpinfo->pushdown_safe = true;
6680 
6681  /*
6682  * Set # of retrieved rows and cached relation costs to some negative
6683  * value, so that we can detect when they are set to some sensible values,
6684  * during one (usually the first) of the calls to estimate_path_cost_size.
6685  */
6686  fpinfo->retrieved_rows = -1;
6687  fpinfo->rel_startup_cost = -1;
6688  fpinfo->rel_total_cost = -1;
6689 
6690  /*
6691  * Set the string describing this grouped relation to be used in EXPLAIN
6692  * output of corresponding ForeignScan. Note that the decoration we add
6693  * to the base relation name mustn't include any digits, or it'll confuse
6694  * postgresExplainForeignScan.
6695  */
6696  fpinfo->relation_name = psprintf("Aggregate on (%s)",
6697  ofpinfo->relation_name);
6698 
6699  return true;
6700 }
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:1082
#define PVC_INCLUDE_AGGREGATES
Definition: optimizer.h:186
#define list_make1(x1)
Definition: pg_list.h:212
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
RestrictInfo * make_restrictinfo(PlannerInfo *root, Expr *clause, bool is_pushed_down, bool has_clone, bool is_clone, bool pseudoconstant, Index security_level, Relids required_relids, Relids incompatible_relids, Relids outer_relids)
Definition: restrictinfo.c:63
List * groupClause
Definition: parsenodes.h:200
List * groupingSets
Definition: parsenodes.h:203
Index ressortgroupref
Definition: primnodes.h:2192
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition: tlist.c:132

References add_to_flat_tlist(), Assert, RestrictInfo::clause, get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), Query::groupClause, PgFdwRelationInfo::grouped_tlist, Query::groupingSets, i, if(), is_foreign_expr(), is_foreign_param(), IsA, lappend(), lfirst, lfirst_node, list_concat(), list_length(), list_make1, PgFdwRelationInfo::local_conds, make_restrictinfo(), makeTargetEntry(), NIL, PgFdwRelationInfo::outerrel, psprintf(), pull_var_clause(), PgFdwRelationInfo::pushdown_safe, PVC_INCLUDE_AGGREGATES, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_name, RelOptInfo::relids, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, TargetEntry::ressortgroupref, PgFdwRelationInfo::retrieved_rows, and root.

Referenced by add_foreign_grouping_paths().

◆ foreign_join_ok()

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

Definition at line 5783 of file postgres_fdw.c.

5786 {
5787  PgFdwRelationInfo *fpinfo;
5788  PgFdwRelationInfo *fpinfo_o;
5789  PgFdwRelationInfo *fpinfo_i;
5790  ListCell *lc;
5791  List *joinclauses;
5792 
5793  /*
5794  * We support pushing down INNER, LEFT, RIGHT, FULL OUTER and SEMI joins.
5795  * Constructing queries representing ANTI joins is hard, hence not
5796  * considered right now.
5797  */
5798  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
5799  jointype != JOIN_RIGHT && jointype != JOIN_FULL &&
5800  jointype != JOIN_SEMI)
5801  return false;
5802 
5803  /*
5804  * We can't push down semi-join if its reltarget is not safe
5805  */
5806  if ((jointype == JOIN_SEMI) && !semijoin_target_ok(root, joinrel, outerrel, innerrel))
5807  return false;
5808 
5809  /*
5810  * If either of the joining relations is marked as unsafe to pushdown, the
5811  * join can not be pushed down.
5812  */
5813  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
5814  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
5815  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
5816  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
5817  !fpinfo_i || !fpinfo_i->pushdown_safe)
5818  return false;
5819 
5820  /*
5821  * If joining relations have local conditions, those conditions are
5822  * required to be applied before joining the relations. Hence the join can
5823  * not be pushed down.
5824  */
5825  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
5826  return false;
5827 
5828  /*
5829  * Merge FDW options. We might be tempted to do this after we have deemed
5830  * the foreign join to be OK. But we must do this beforehand so that we
5831  * know which quals can be evaluated on the foreign server, which might
5832  * depend on shippable_extensions.
5833  */
5834  fpinfo->server = fpinfo_o->server;
5835  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
5836 
5837  /*
5838  * Separate restrict list into join quals and pushed-down (other) quals.
5839  *
5840  * Join quals belonging to an outer join must all be shippable, else we
5841  * cannot execute the join remotely. Add such quals to 'joinclauses'.
5842  *
5843  * Add other quals to fpinfo->remote_conds if they are shippable, else to
5844  * fpinfo->local_conds. In an inner join it's okay to execute conditions
5845  * either locally or remotely; the same is true for pushed-down conditions
5846  * at an outer join.
5847  *
5848  * Note we might return failure after having already scribbled on
5849  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
5850  * won't consult those lists again if we deem the join unshippable.
5851  */
5852  joinclauses = NIL;
5853  foreach(lc, extra->restrictlist)
5854  {
5855  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5856  bool is_remote_clause = is_foreign_expr(root, joinrel,
5857  rinfo->clause);
5858 
5859  if (IS_OUTER_JOIN(jointype) &&
5860  !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
5861  {
5862  if (!is_remote_clause)
5863  return false;
5864  joinclauses = lappend(joinclauses, rinfo);
5865  }
5866  else
5867  {
5868  if (is_remote_clause)
5869  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5870  else
5871  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5872  }
5873  }
5874 
5875  /*
5876  * deparseExplicitTargetList() isn't smart enough to handle anything other
5877  * than a Var. In particular, if there's some PlaceHolderVar that would
5878  * need to be evaluated within this join tree (because there's an upper
5879  * reference to a quantity that may go to NULL as a result of an outer
5880  * join), then we can't try to push the join down because we'll fail when
5881  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
5882  * needs to be evaluated *at the top* of this join tree is OK, because we
5883  * can do that locally after fetching the results from the remote side.
5884  */
5885  foreach(lc, root->placeholder_list)
5886  {
5887  PlaceHolderInfo *phinfo = lfirst(lc);
5888  Relids relids;
5889 
5890  /* PlaceHolderInfo refers to parent relids, not child relids. */
5891  relids = IS_OTHER_REL(joinrel) ?
5892  joinrel->top_parent_relids : joinrel->relids;
5893 
5894  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
5895  bms_nonempty_difference(relids, phinfo->ph_eval_at))
5896  return false;
5897  }
5898 
5899  /* Save the join clauses, for later use. */
5900  fpinfo->joinclauses = joinclauses;
5901 
5902  fpinfo->outerrel = outerrel;
5903  fpinfo->innerrel = innerrel;
5904  fpinfo->jointype = jointype;
5905 
5906  /*
5907  * By default, both the input relations are not required to be deparsed as
5908  * subqueries, but there might be some relations covered by the input
5909  * relations that are required to be deparsed as subqueries, so save the
5910  * relids of those relations for later use by the deparser.
5911  */
5912  fpinfo->make_outerrel_subquery = false;
5913  fpinfo->make_innerrel_subquery = false;
5914  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
5915  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
5917  fpinfo_i->lower_subquery_rels);
5919  fpinfo_i->hidden_subquery_rels);
5920 
5921  /*
5922  * Pull the other remote conditions from the joining relations into join
5923  * clauses or other remote clauses (remote_conds) of this relation
5924  * wherever possible. This avoids building subqueries at every join step.
5925  *
5926  * For an inner join, clauses from both the relations are added to the
5927  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
5928  * the outer side are added to remote_conds since those can be evaluated
5929  * after the join is evaluated. The clauses from inner side are added to
5930  * the joinclauses, since they need to be evaluated while constructing the
5931  * join.
5932  *
5933  * For SEMI-JOIN clauses from inner relation can not be added to
5934  * remote_conds, but should be treated as join clauses (as they are
5935  * deparsed to EXISTS subquery, where inner relation can be referred). A
5936  * list of relation ids, which can't be referred to from higher levels, is
5937  * preserved as a hidden_subquery_rels list.
5938  *
5939  * For a FULL OUTER JOIN, the other clauses from either relation can not
5940  * be added to the joinclauses or remote_conds, since each relation acts
5941  * as an outer relation for the other.
5942  *
5943  * The joining sides can not have local conditions, thus no need to test
5944  * shippability of the clauses being pulled up.
5945  */
5946  switch (jointype)
5947  {
5948  case JOIN_INNER:
5949  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5950  fpinfo_i->remote_conds);
5951  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5952  fpinfo_o->remote_conds);
5953  break;
5954 
5955  case JOIN_LEFT:
5956  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5957  fpinfo_i->remote_conds);
5958  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5959  fpinfo_o->remote_conds);
5960  break;
5961 
5962  case JOIN_RIGHT:
5963  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5964  fpinfo_o->remote_conds);
5965  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5966  fpinfo_i->remote_conds);
5967  break;
5968 
5969  case JOIN_SEMI:
5970  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5971  fpinfo_i->remote_conds);
5972  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5973  fpinfo->remote_conds);
5974  fpinfo->remote_conds = list_copy(fpinfo_o->remote_conds);
5976  innerrel->relids);
5977  break;
5978 
5979  case JOIN_FULL:
5980 
5981  /*
5982  * In this case, if any of the input relations has conditions, we
5983  * need to deparse that relation as a subquery so that the
5984  * conditions can be evaluated before the join. Remember it in
5985  * the fpinfo of this relation so that the deparser can take
5986  * appropriate action. Also, save the relids of base relations
5987  * covered by that relation for later use by the deparser.
5988  */
5989  if (fpinfo_o->remote_conds)
5990  {
5991  fpinfo->make_outerrel_subquery = true;
5992  fpinfo->lower_subquery_rels =
5994  outerrel->relids);
5995  }
5996  if (fpinfo_i->remote_conds)
5997  {
5998  fpinfo->make_innerrel_subquery = true;
5999  fpinfo->lower_subquery_rels =
6001  innerrel->relids);
6002  }
6003  break;
6004 
6005  default:
6006  /* Should not happen, we have just checked this above */
6007  elog(ERROR, "unsupported join type %d", jointype);
6008  }
6009 
6010  /*
6011  * For an inner join, all restrictions can be treated alike. Treating the
6012  * pushed down conditions as join conditions allows a top level full outer
6013  * join to be deparsed without requiring subqueries.
6014  */
6015  if (jointype == JOIN_INNER)
6016  {
6017  Assert(!fpinfo->joinclauses);
6018  fpinfo->joinclauses = fpinfo->remote_conds;
6019  fpinfo->remote_conds = NIL;
6020  }
6021  else if (jointype == JOIN_LEFT || jointype == JOIN_RIGHT || jointype == JOIN_FULL)
6022  {
6023  /*
6024  * Conditions, generated from semi-joins, should be evaluated before
6025  * LEFT/RIGHT/FULL join.
6026  */
6027  if (!bms_is_empty(fpinfo_o->hidden_subquery_rels))
6028  {
6029  fpinfo->make_outerrel_subquery = true;
6030  fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, outerrel->relids);
6031  }
6032 
6033  if (!bms_is_empty(fpinfo_i->hidden_subquery_rels))
6034  {
6035  fpinfo->make_innerrel_subquery = true;
6036  fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, innerrel->relids);
6037  }
6038  }
6039 
6040  /* Mark that this join can be pushed down safely */
6041  fpinfo->pushdown_safe = true;
6042 
6043  /* Get user mapping */
6044  if (fpinfo->use_remote_estimate)
6045  {
6046  if (fpinfo_o->use_remote_estimate)
6047  fpinfo->user = fpinfo_o->user;
6048  else
6049  fpinfo->user = fpinfo_i->user;
6050  }
6051  else
6052  fpinfo->user = NULL;
6053 
6054  /*
6055  * Set # of retrieved rows and cached relation costs to some negative
6056  * value, so that we can detect when they are set to some sensible values,
6057  * during one (usually the first) of the calls to estimate_path_cost_size.
6058  */
6059  fpinfo->retrieved_rows = -1;
6060  fpinfo->rel_startup_cost = -1;
6061  fpinfo->rel_total_cost = -1;
6062 
6063  /*
6064  * Set the string describing this join relation to be used in EXPLAIN
6065  * output of corresponding ForeignScan. Note that the decoration we add
6066  * to the base relation names mustn't include any digits, or it'll confuse
6067  * postgresExplainForeignScan.
6068  */
6069  fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
6070  fpinfo_o->relation_name,
6071  get_jointype_name(fpinfo->jointype),
6072  fpinfo_i->relation_name);
6073 
6074  /*
6075  * Set the relation index. This is defined as the position of this
6076  * joinrel in the join_rel_list list plus the length of the rtable list.
6077  * Note that since this joinrel is at the end of the join_rel_list list
6078  * when we are called, we can get the position by list_length.
6079  */
6080  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
6081  fpinfo->relation_index =
6082  list_length(root->parse->rtable) + list_length(root->join_rel_list);
6083 
6084  return true;
6085 }
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:251
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:641
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1641
List * list_copy(const List *oldlist)
Definition: list.c:1573
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:338
@ JOIN_SEMI
Definition: nodes.h:307
@ JOIN_FULL
Definition: nodes.h:295
@ JOIN_RIGHT
Definition: nodes.h:296
@ JOIN_LEFT
Definition: nodes.h:294
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: pathnodes.h:2721
#define IS_OTHER_REL(rel)
Definition: pathnodes.h:848
static bool semijoin_target_ok(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel)
Relids lower_subquery_rels
Definition: postgres_fdw.h:119
Relids ph_eval_at
Definition: pathnodes.h:3088
Relids top_parent_relids
Definition: pathnodes.h:1003

References Assert, bms_add_members(), bms_is_empty, bms_is_subset(), bms_nonempty_difference(), bms_union(), RestrictInfo::clause, elog, ERROR, get_jointype_name(), PgFdwRelationInfo::hidden_subquery_rels, if(), PgFdwRelationInfo::innerrel, is_foreign_expr(), IS_OTHER_REL, IS_OUTER_JOIN, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, 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, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, PlaceHolderInfo::ph_eval_at, psprintf(), PgFdwRelationInfo::pushdown_safe, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_index, PgFdwRelationInfo::relation_name, RelOptInfo::relids, PgFdwRelationInfo::remote_conds, JoinPathExtraData::restrictlist, PgFdwRelationInfo::retrieved_rows, RINFO_IS_PUSHED_DOWN, root, semijoin_target_ok(), PgFdwRelationInfo::server, RelOptInfo::top_parent_relids, PgFdwRelationInfo::use_remote_estimate, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignJoinPaths().

◆ get_batch_size_option()

static int get_batch_size_option ( Relation  rel)
static

Definition at line 7901 of file postgres_fdw.c.

7902 {
7903  Oid foreigntableid = RelationGetRelid(rel);
7904  ForeignTable *table;
7905  ForeignServer *server;
7906  List *options;
7907  ListCell *lc;
7908 
7909  /* we use 1 by default, which means "no batching" */
7910  int batch_size = 1;
7911 
7912  /*
7913  * Load options for table and server. We append server options after table
7914  * options, because table options take precedence.
7915  */
7916  table = GetForeignTable(foreigntableid);
7917  server = GetForeignServer(table->serverid);
7918 
7919  options = NIL;
7920  options = list_concat(options, table->options);
7921  options = list_concat(options, server->options);
7922 
7923  /* See if either table or server specifies batch_size. */
7924  foreach(lc, options)
7925  {
7926  DefElem *def = (DefElem *) lfirst(lc);
7927 
7928  if (strcmp(def->defname, "batch_size") == 0)
7929  {
7930  (void) parse_int(defGetString(def), &batch_size, 0, NULL);
7931  break;
7932  }
7933  }
7934 
7935  return batch_size;
7936 }
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:110
static char ** options

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

Referenced by create_foreign_modify(), and postgresGetForeignModifyBatchSize().

◆ get_remote_estimate()

static void get_remote_estimate ( const char *  sql,
PGconn conn,
double *  rows,
int *  width,
Cost startup_cost,
Cost total_cost 
)
static

Definition at line 3589 of file postgres_fdw.c.

3592 {
3593  PGresult *volatile res = NULL;
3594 
3595  /* PGresult must be released before leaving this function. */
3596  PG_TRY();
3597  {
3598  char *line;
3599  char *p;
3600  int n;
3601 
3602  /*
3603  * Execute EXPLAIN remotely.
3604  */
3605  res = pgfdw_exec_query(conn, sql, NULL);
3607  pgfdw_report_error(ERROR, res, conn, false, sql);
3608 
3609  /*
3610  * Extract cost numbers for topmost plan node. Note we search for a
3611  * left paren from the end of the line to avoid being confused by
3612  * other uses of parentheses.
3613  */
3614  line = PQgetvalue(res, 0, 0);
3615  p = strrchr(line, '(');
3616  if (p == NULL)
3617  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3618  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3619  startup_cost, total_cost, rows, width);
3620  if (n != 4)
3621  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3622  }
3623  PG_FINALLY();
3624  {
3625  PQclear(res);
3626  }
3627  PG_END_TRY();
3628 }
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3876

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

Referenced by estimate_path_cost_size().

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 4601 of file postgres_fdw.c.

4602 {
4604  EState *estate = node->ss.ps.state;
4605  ResultRelInfo *resultRelInfo = node->resultRelInfo;
4606  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4607  TupleTableSlot *resultSlot;
4608 
4609  Assert(resultRelInfo->ri_projectReturning);
4610 
4611  /* If we didn't get any tuples, must be end of data. */
4612  if (dmstate->next_tuple >= dmstate->num_tuples)
4613  return ExecClearTuple(slot);
4614 
4615  /* Increment the command es_processed count if necessary. */
4616  if (dmstate->set_processed)
4617  estate->es_processed += 1;
4618 
4619  /*
4620  * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4621  * tuple. (has_returning is false when the local query is of the form
4622  * "UPDATE/DELETE .. RETURNING 1" for example.)
4623  */
4624  if (!dmstate->has_returning)
4625  {
4626  ExecStoreAllNullTuple(slot);
4627  resultSlot = slot;
4628  }
4629  else
4630  {
4631  /*
4632  * On error, be sure to release the PGresult on the way out. Callers
4633  * do not have PG_TRY blocks to ensure this happens.
4634  */
4635  PG_TRY();
4636  {
4637  HeapTuple newtup;
4638 
4639  newtup = make_tuple_from_result_row(dmstate->result,
4640  dmstate->next_tuple,
4641  dmstate->rel,
4642  dmstate->attinmeta,
4643  dmstate->retrieved_attrs,
4644  node,
4645  dmstate->temp_cxt);
4646  ExecStoreHeapTuple(newtup, slot, false);
4647  }
4648  PG_CATCH();
4649  {
4650  PQclear(dmstate->result);
4651  PG_RE_THROW();
4652  }
4653  PG_END_TRY();
4654 
4655  /* Get the updated/deleted tuple. */
4656  if (dmstate->rel)
4657  resultSlot = slot;
4658  else
4659  resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4660  }
4661  dmstate->next_tuple++;
4662 
4663  /* Make slot available for evaluation of the local query RETURNING list. */
4664  resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4665  resultSlot;
4666 
4667  return slot;
4668 }
#define PG_RE_THROW()
Definition: elog.h:411
#define PG_CATCH(...)
Definition: elog.h:380
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1663
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1439
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
ResultRelInfo * resultRelInfo
Definition: execnodes.h:2067
MemoryContext temp_cxt
Definition: postgres_fdw.c:249
AttInMetadata * attinmeta
Definition: postgres_fdw.c:222
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1566

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

Referenced by postgresIterateDirectModify().

◆ get_tupdesc_for_join_scan_tuples()

static TupleDesc get_tupdesc_for_join_scan_tuples ( ForeignScanState node)
static

Definition at line 1439 of file postgres_fdw.c.

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

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

Referenced by postgresBeginDirectModify(), and postgresBeginForeignScan().

◆ get_useful_ecs_for_relation()

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

Definition at line 808 of file postgres_fdw.c.

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

References Assert, bms_is_empty, bms_overlap(), eclass_useful_for_merging(), RelOptInfo::has_eclass_joins, IS_OTHER_REL, RelOptInfo::joininfo, lappend(), lfirst, list_append_unique_ptr(), NIL, RelOptInfo::relids, root, RelOptInfo::top_parent_relids, and update_mergeclause_eclasses().

Referenced by get_useful_pathkeys_for_relation().

◆ get_useful_pathkeys_for_relation()

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

Definition at line 904 of file postgres_fdw.c.

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

References BTLessStrategyNumber, EquivalenceClass::ec_opfamilies, find_em_for_rel(), get_useful_ecs_for_relation(), if(), is_foreign_pathkey(), is_shippable(), lappend(), lfirst, linitial, linitial_oid, list_copy(), list_length(), list_make1, make_canonical_pathkey(), NIL, PgFdwRelationInfo::qp_is_pushdown_safe, root, and PgFdwRelationInfo::use_remote_estimate.

Referenced by add_paths_with_pathkeys_for_rel().

◆ init_returning_filter()

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

Definition at line 4674 of file postgres_fdw.c.

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

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

Referenced by postgresBeginDirectModify().

◆ make_tuple_from_result_row()

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

Definition at line 7540 of file postgres_fdw.c.

7547 {
7548  HeapTuple tuple;
7549  TupleDesc tupdesc;
7550  Datum *values;
7551  bool *nulls;
7552  ItemPointer ctid = NULL;
7553  ConversionLocation errpos;
7554  ErrorContextCallback errcallback;
7555  MemoryContext oldcontext;
7556  ListCell *lc;
7557  int j;
7558 
7559  Assert(row < PQntuples(res));
7560 
7561  /*
7562  * Do the following work in a temp context that we reset after each tuple.
7563  * This cleans up not only the data we have direct access to, but any
7564  * cruft the I/O functions might leak.
7565  */
7566  oldcontext = MemoryContextSwitchTo(temp_context);
7567 
7568  /*
7569  * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
7570  * provided, otherwise look to the scan node's ScanTupleSlot.
7571  */
7572  if (rel)
7573  tupdesc = RelationGetDescr(rel);
7574  else
7575  {
7576  Assert(fsstate);
7577  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
7578  }
7579 
7580  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
7581  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
7582  /* Initialize to nulls for any columns not present in result */
7583  memset(nulls, true, tupdesc->natts * sizeof(bool));
7584 
7585  /*
7586  * Set up and install callback to report where conversion error occurs.
7587  */
7588  errpos.cur_attno = 0;
7589  errpos.rel = rel;
7590  errpos.fsstate = fsstate;
7591  errcallback.callback = conversion_error_callback;
7592  errcallback.arg = (void *) &errpos;
7593  errcallback.previous = error_context_stack;
7594  error_context_stack = &errcallback;
7595 
7596  /*
7597  * i indexes columns in the relation, j indexes columns in the PGresult.
7598  */
7599  j = 0;
7600  foreach(lc, retrieved_attrs)
7601  {
7602  int i = lfirst_int(lc);
7603  char *valstr;
7604 
7605  /* fetch next column's textual value */
7606  if (PQgetisnull(res, row, j))
7607  valstr = NULL;
7608  else
7609  valstr = PQgetvalue(res, row, j);
7610 
7611  /*
7612  * convert value to internal representation
7613  *
7614  * Note: we ignore system columns other than ctid and oid in result
7615  */
7616  errpos.cur_attno = i;
7617  if (i > 0)
7618  {
7619  /* ordinary column */
7620  Assert(i <= tupdesc->natts);
7621  nulls[i - 1] = (valstr == NULL);
7622  /* Apply the input function even to nulls, to support domains */
7623  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
7624  valstr,
7625  attinmeta->attioparams[i - 1],
7626  attinmeta->atttypmods[i - 1]);
7627  }
7628  else if (i == SelfItemPointerAttributeNumber)
7629  {
7630  /* ctid */
7631  if (valstr != NULL)
7632  {
7633  Datum datum;
7634 
7635  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
7636  ctid = (ItemPointer) DatumGetPointer(datum);
7637  }
7638  }
7639  errpos.cur_attno = 0;
7640 
7641  j++;
7642  }
7643 
7644  /* Uninstall error context callback. */
7645  error_context_stack = errcallback.previous;
7646 
7647  /*
7648  * Check we got the expected number of columns. Note: j == 0 and
7649  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
7650  */
7651  if (j > 0 && j != PQnfields(res))
7652  elog(ERROR, "remote query result does not match the foreign table");
7653 
7654  /*
7655  * Build the result tuple in caller's memory context.
7656  */
7657  MemoryContextSwitchTo(oldcontext);
7658 
7659  tuple = heap_form_tuple(tupdesc, values, nulls);
7660 
7661  /*
7662  * If we have a CTID to return, install it in both t_self and t_ctid.
7663  * t_self is the normal place, but if the tuple is converted to a
7664  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
7665  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
7666  */
7667  if (ctid)
7668  tuple->t_self = tuple->t_data->t_ctid = *ctid;
7669 
7670  /*
7671  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
7672  * heap_form_tuple. heap_form_tuple actually creates the tuple with
7673  * DatumTupleFields, not HeapTupleFields, but the executor expects
7674  * HeapTupleFields and will happily extract system columns on that
7675  * assumption. If we don't do this then, for example, the tuple length
7676  * ends up in the xmin field, which isn't what we want.
7677  */
7681 
7682  /* Clean up */
7683  MemoryContextReset(temp_context);
7684 
7685  return tuple;
7686 }
ErrorContextCallback * error_context_stack
Definition: elog.c:94
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3901
int PQnfields(const PGresult *res)
Definition: fe-exec.c:3489
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1530
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static void conversion_error_callback(void *arg)
FmgrInfo * attinfuncs
Definition: funcapi.h:41
Oid * attioparams
Definition: funcapi.h:44
int32 * atttypmods
Definition: funcapi.h:47
struct ErrorContextCallback * previous
Definition: elog.h:295
void(* callback)(void *arg)
Definition: elog.h:296
ItemPointerData t_ctid
Definition: htup_details.h:161
Datum tidin(PG_FUNCTION_ARGS)
Definition: tid.c:52

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

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

◆ merge_fdw_options()

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

Definition at line 6266 of file postgres_fdw.c.

6269 {
6270  /* We must always have fpinfo_o. */
6271  Assert(fpinfo_o);
6272 
6273  /* fpinfo_i may be NULL, but if present the servers must both match. */
6274  Assert(!fpinfo_i ||
6275  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
6276 
6277  /*
6278  * Copy the server specific FDW options. (For a join, both relations come
6279  * from the same server, so the server options should have the same value
6280  * for both relations.)
6281  */
6282  fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
6283  fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
6284  fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
6285  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
6286  fpinfo->fetch_size = fpinfo_o->fetch_size;
6287  fpinfo->async_capable = fpinfo_o->async_capable;
6288 
6289  /* Merge the table level options from either side of the join. */
6290  if (fpinfo_i)
6291  {
6292  /*
6293  * We'll prefer to use remote estimates for this join if any table
6294  * from either side of the join is using remote estimates. This is
6295  * most likely going to be preferred since they're already willing to
6296  * pay the price of a round trip to get the remote EXPLAIN. In any
6297  * case it's not entirely clear how we might otherwise handle this
6298  * best.
6299  */
6300  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
6301  fpinfo_i->use_remote_estimate;
6302 
6303  /*
6304  * Set fetch size to maximum of the joining sides, since we are
6305  * expecting the rows returned by the join to be proportional to the
6306  * relation sizes.
6307  */
6308  fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
6309 
6310  /*
6311  * We'll prefer to consider this join async-capable if any table from
6312  * either side of the join is considered async-capable. This would be
6313  * reasonable because in that case the foreign server would have its
6314  * own resources to scan that table asynchronously, and the join could
6315  * also be computed asynchronously using the resources.
6316  */
6317  fpinfo->async_capable = fpinfo_o->async_capable ||
6318  fpinfo_i->async_capable;
6319  }
6320 }
#define Max(x, y)
Definition: c.h:998
Oid serverid
Definition: foreign.h:36

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

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

◆ PG_FUNCTION_INFO_V1()

PG_FUNCTION_INFO_V1 ( postgres_fdw_handler  )

◆ postgres_fdw_handler()

Datum postgres_fdw_handler ( PG_FUNCTION_ARGS  )

Definition at line 552 of file postgres_fdw.c.

553 {
554  FdwRoutine *routine = makeNode(FdwRoutine);
555 
556  /* Functions for scanning foreign tables */
564 
565  /* Functions for updating foreign tables */
582 
583  /* Function for EvalPlanQual rechecks */
585  /* Support functions for EXPLAIN */
589 
590  /* Support function for TRUNCATE */
592 
593  /* Support functions for ANALYZE */
595 
596  /* Support functions for IMPORT FOREIGN SCHEMA */
598 
599  /* Support functions for join push-down */
601 
602  /* Support functions for upper relation push-down */
604 
605  /* Support functions for asynchronous execution */
610 
611  PG_RETURN_POINTER(routine);
612 }
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define makeNode(_type_)
Definition: nodes.h:155
static int postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
static void postgresBeginForeignScan(ForeignScanState *node, int eflags)
static bool postgresIsForeignPathAsyncCapable(ForeignPath *path)
static void postgresExecForeignTruncate(List *rels, DropBehavior behavior, bool restart_seqs)
static void postgresExplainForeignModify(ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es)
static TupleTableSlot ** postgresExecForeignBatchInsert(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
static void postgresGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
static void postgresExplainForeignScan(ForeignScanState *node, ExplainState *es)
static TupleTableSlot * postgresExecForeignUpdate(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
static bool postgresPlanDirectModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
static void postgresReScanForeignScan(ForeignScanState *node)
static void postgresForeignAsyncRequest(AsyncRequest *areq)
static int postgresIsForeignRelUpdatable(Relation rel)
static ForeignScan * postgresGetForeignPlan(PlannerInfo *root, RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
static void postgresEndForeignScan(ForeignScanState *node)
static void postgresGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
static List * postgresPlanForeignModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
static void postgresEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo)
static void postgresAddForeignUpdateTargets(PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation)
static void postgresGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
Definition: postgres_fdw.c:622
static void postgresEndDirectModify(ForeignScanState *node)
static void postgresForeignAsyncConfigureWait(AsyncRequest *areq)
static void postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra)
static void postgresForeignAsyncNotify(AsyncRequest *areq)
static TupleTableSlot * postgresIterateForeignScan(ForeignScanState *node)
static TupleTableSlot * postgresExecForeignDelete(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
static bool postgresRecheckForeignScan(ForeignScanState *node, TupleTableSlot *slot)
static List * postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
static bool postgresAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
static void postgresBeginForeignModify(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
static void postgresBeginForeignInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static TupleTableSlot * postgresIterateDirectModify(ForeignScanState *node)
static void postgresBeginDirectModify(ForeignScanState *node, int eflags)
static TupleTableSlot * postgresExecForeignInsert(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
static void postgresEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo)
static void postgresExplainDirectModify(ForeignScanState *node, ExplainState *es)
EndForeignInsert_function EndForeignInsert
Definition: fdwapi.h:239
ReScanForeignScan_function ReScanForeignScan
Definition: fdwapi.h:214
BeginForeignInsert_function BeginForeignInsert
Definition: fdwapi.h:238
RecheckForeignScan_function RecheckForeignScan
Definition: fdwapi.h:249
AddForeignUpdateTargets_function AddForeignUpdateTargets
Definition: fdwapi.h:229
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:231
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
BeginDirectModify_function BeginDirectModify
Definition: fdwapi.h:242
PlanForeignModify_function PlanForeignModify
Definition: fdwapi.h:230
PlanDirectModify_function PlanDirectModify
Definition: fdwapi.h:241
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:232
BeginForeignScan_function BeginForeignScan
Definition: fdwapi.h:212
ForeignAsyncRequest_function ForeignAsyncRequest
Definition: fdwapi.h:278
IterateDirectModify_function IterateDirectModify
Definition: fdwapi.h:243
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:235
GetForeignJoinPaths_function GetForeignJoinPaths
Definition: fdwapi.h:223
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
GetForeignPaths_function GetForeignPaths
Definition: fdwapi.h:210
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition: fdwapi.h:234
GetForeignRelSize_function GetForeignRelSize
Definition: fdwapi.h:209
ExplainForeignScan_function ExplainForeignScan
Definition: fdwapi.h:252
EndForeignScan_function EndForeignScan
Definition: fdwapi.h:215
AnalyzeForeignTable_function AnalyzeForeignTable
Definition: fdwapi.h:257
EndDirectModify_function EndDirectModify
Definition: fdwapi.h:244
ExplainForeignModify_function ExplainForeignModify
Definition: fdwapi.h:253
IsForeignPathAsyncCapable_function IsForeignPathAsyncCapable
Definition: fdwapi.h:277
IterateForeignScan_function IterateForeignScan
Definition: fdwapi.h:213
ForeignAsyncNotify_function ForeignAsyncNotify
Definition: fdwapi.h:280
ImportForeignSchema_function ImportForeignSchema
Definition: fdwapi.h:260
GetForeignPlan_function GetForeignPlan
Definition: fdwapi.h:211
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:236
ExecForeignTruncate_function ExecForeignTruncate
Definition: fdwapi.h:263
ExplainDirectModify_function ExplainDirectModify
Definition: fdwapi.h:254
IsForeignRelUpdatable_function IsForeignRelUpdatable
Definition: fdwapi.h:240
GetForeignUpperPaths_function GetForeignUpperPaths
Definition: fdwapi.h:226
ForeignAsyncConfigureWait_function ForeignAsyncConfigureWait
Definition: fdwapi.h:279

References FdwRoutine::AddForeignUpdateTargets, FdwRoutine::AnalyzeForeignTable, FdwRoutine::BeginDirectModify, FdwRoutine::BeginForeignInsert, FdwRoutine::BeginForeignModify, FdwRoutine::BeginForeignScan, FdwRoutine::EndDirectModify, FdwRoutine::EndForeignInsert, FdwRoutine::EndForeignModify, FdwRoutine::EndForeignScan, FdwRoutine::ExecForeignBatchInsert, FdwRoutine::ExecForeignDelete, FdwRoutine::ExecForeignInsert, FdwRoutine::ExecForeignTruncate, FdwRoutine::ExecForeignUpdate, FdwRoutine::ExplainDirectModify, FdwRoutine::ExplainForeignModify, FdwRoutine::ExplainForeignScan, FdwRoutine::ForeignAsyncConfigureWait, FdwRoutine::ForeignAsyncNotify, FdwRoutine::ForeignAsyncRequest, FdwRoutine::GetForeignJoinPaths, FdwRoutine::GetForeignModifyBatchSize, FdwRoutine::GetForeignPaths, FdwRoutine::GetForeignPlan, FdwRoutine::GetForeignRelSize, FdwRoutine::GetForeignUpperPaths, FdwRoutine::ImportForeignSchema, FdwRoutine::IsForeignPathAsyncCapable, FdwRoutine::IsForeignRelUpdatable, FdwRoutine::IterateDirectModify, FdwRoutine::IterateForeignScan, makeNode, PG_RETURN_POINTER, FdwRoutine::PlanDirectModify, FdwRoutine::PlanForeignModify, postgresAddForeignUpdateTargets(), postgresAnalyzeForeignTable(), postgresBeginDirectModify(), postgresBeginForeignInsert(), postgresBeginForeignModify(), postgresBeginForeignScan(), postgresEndDirectModify(), postgresEndForeignInsert(), postgresEndForeignModify(), postgresEndForeignScan(), postgresExecForeignBatchInsert(), postgresExecForeignDelete(), postgresExecForeignInsert(), postgresExecForeignTruncate(), postgresExecForeignUpdate(), postgresExplainDirectModify(), postgresExplainForeignModify(), postgresExplainForeignScan(), postgresForeignAsyncConfigureWait(), postgresForeignAsyncNotify(), postgresForeignAsyncRequest(), postgresGetForeignJoinPaths(), postgresGetForeignModifyBatchSize(), postgresGetForeignPaths(), postgresGetForeignPlan(), postgresGetForeignRelSize(), postgresGetForeignUpperPaths(), postgresImportForeignSchema(), postgresIsForeignPathAsyncCapable(), postgresIsForeignRelUpdatable(), postgresIterateDirectModify(), postgresIterateForeignScan(), postgresPlanDirectModify(), postgresPlanForeignModify(), postgresRecheckForeignScan(), postgresReScanForeignScan(), FdwRoutine::RecheckForeignScan, and FdwRoutine::ReScanForeignScan.

◆ postgresAcquireSampleRowsFunc()

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

Definition at line 5067 of file postgres_fdw.c.

5071 {
5072  PgFdwAnalyzeState astate;
5073  ForeignTable *table;
5074  ForeignServer *server;
5075  UserMapping *user;
5076  PGconn *conn;
5077  int server_version_num;
5078  PgFdwSamplingMethod method = ANALYZE_SAMPLE_AUTO; /* auto is default */
5079  double sample_frac = -1.0;
5080  double reltuples;
5081  unsigned int cursor_number;
5082  StringInfoData sql;
5083  PGresult *volatile res = NULL;
5084  ListCell *lc;
5085 
5086  /* Initialize workspace state */
5087  astate.rel = relation;
5089 
5090  astate.rows = rows;
5091  astate.targrows = targrows;
5092  astate.numrows = 0;
5093  astate.samplerows = 0;
5094  astate.rowstoskip = -1; /* -1 means not set yet */
5095  reservoir_init_selection_state(&astate.rstate, targrows);
5096 
5097  /* Remember ANALYZE context, and create a per-tuple temp context */
5098  astate.anl_cxt = CurrentMemoryContext;
5100  "postgres_fdw temporary data",
5102 
5103  /*
5104  * Get the connection to use. We do the remote access as the table's
5105  * owner, even if the ANALYZE was started by some other user.
5106  */
5107  table = GetForeignTable(RelationGetRelid(relation));
5108  server = GetForeignServer(table->serverid);
5109  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5110  conn = GetConnection(user, false, NULL);
5111 
5112  /* We'll need server version, so fetch it now. */
5114 
5115  /*
5116  * What sampling method should we use?
5117  */
5118  foreach(lc, server->options)
5119  {
5120  DefElem *def = (DefElem *) lfirst(lc);
5121 
5122  if (strcmp(def->defname, "analyze_sampling") == 0)
5123  {
5124  char *value = defGetString(def);
5125 
5126  if (strcmp(value, "off") == 0)
5127  method = ANALYZE_SAMPLE_OFF;
5128  else if (strcmp(value, "auto") == 0)
5129  method = ANALYZE_SAMPLE_AUTO;
5130  else if (strcmp(value, "random") == 0)
5131  method = ANALYZE_SAMPLE_RANDOM;
5132  else if (strcmp(value, "system") == 0)
5133  method = ANALYZE_SAMPLE_SYSTEM;
5134  else if (strcmp(value, "bernoulli") == 0)
5135  method = ANALYZE_SAMPLE_BERNOULLI;
5136 
5137  break;
5138  }
5139  }
5140 
5141  foreach(lc, table->options)
5142  {
5143  DefElem *def = (DefElem *) lfirst(lc);
5144 
5145  if (strcmp(def->defname, "analyze_sampling") == 0)
5146  {
5147  char *value = defGetString(def);
5148 
5149  if (strcmp(value, "off") == 0)
5150  method = ANALYZE_SAMPLE_OFF;
5151  else if (strcmp(value, "auto") == 0)
5152  method = ANALYZE_SAMPLE_AUTO;
5153  else if (strcmp(value, "random") == 0)
5154  method = ANALYZE_SAMPLE_RANDOM;
5155  else if (strcmp(value, "system") == 0)
5156  method = ANALYZE_SAMPLE_SYSTEM;
5157  else if (strcmp(value, "bernoulli") == 0)
5158  method = ANALYZE_SAMPLE_BERNOULLI;
5159 
5160  break;
5161  }
5162  }
5163 
5164  /*
5165  * Error-out if explicitly required one of the TABLESAMPLE methods, but
5166  * the server does not support it.
5167  */
5168  if ((server_version_num < 95000) &&
5169  (method == ANALYZE_SAMPLE_SYSTEM ||
5170  method == ANALYZE_SAMPLE_BERNOULLI))
5171  ereport(ERROR,
5172  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5173  errmsg("remote server does not support TABLESAMPLE feature")));
5174 
5175  /*
5176  * If we've decided to do remote sampling, calculate the sampling rate. We
5177  * need to get the number of tuples from the remote server, but skip that
5178  * network round-trip if not needed.
5179  */
5180  if (method != ANALYZE_SAMPLE_OFF)
5181  {
5182  bool can_tablesample;
5183 
5184  reltuples = postgresGetAnalyzeInfoForForeignTable(relation,
5185  &can_tablesample);
5186 
5187  /*
5188  * Make sure we're not choosing TABLESAMPLE when the remote relation
5189  * does not support that. But only do this for "auto" - if the user
5190  * explicitly requested BERNOULLI/SYSTEM, it's better to fail.
5191  */
5192  if (!can_tablesample && (method == ANALYZE_SAMPLE_AUTO))
5193  method = ANALYZE_SAMPLE_RANDOM;
5194 
5195  /*
5196  * Remote's reltuples could be 0 or -1 if the table has never been
5197  * vacuumed/analyzed. In that case, disable sampling after all.
5198  */
5199  if ((reltuples <= 0) || (targrows >= reltuples))
5200  method = ANALYZE_SAMPLE_OFF;
5201  else
5202  {
5203  /*
5204  * All supported sampling methods require sampling rate, not
5205  * target rows directly, so we calculate that using the remote
5206  * reltuples value. That's imperfect, because it might be off a
5207  * good deal, but that's not something we can (or should) address
5208  * here.
5209  *
5210  * If reltuples is too low (i.e. when table grew), we'll end up
5211  * sampling more rows - but then we'll apply the local sampling,
5212  * so we get the expected sample size. This is the same outcome as
5213  * without remote sampling.
5214  *
5215  * If reltuples is too high (e.g. after bulk DELETE), we will end
5216  * up sampling too few rows.
5217  *
5218  * We can't really do much better here - we could try sampling a
5219  * bit more rows, but we don't know how off the reltuples value is
5220  * so how much is "a bit more"?
5221  *
5222  * Furthermore, the targrows value for partitions is determined
5223  * based on table size (relpages), which can be off in different
5224  * ways too. Adjusting the sampling rate here might make the issue
5225  * worse.
5226  */
5227  sample_frac = targrows / reltuples;
5228 
5229  /*
5230  * We should never get sampling rate outside the valid range
5231  * (between 0.0 and 1.0), because those cases should be covered by
5232  * the previous branch that sets ANALYZE_SAMPLE_OFF.
5233  */
5234  Assert(sample_frac >= 0.0 && sample_frac <= 1.0);
5235  }
5236  }
5237 
5238  /*
5239  * For "auto" method, pick the one we believe is best. For servers with
5240  * TABLESAMPLE support we pick BERNOULLI, for old servers we fall-back to
5241  * random() to at least reduce network transfer.
5242  */
5243  if (method == ANALYZE_SAMPLE_AUTO)
5244  {
5245  if (server_version_num < 95000)
5246  method = ANALYZE_SAMPLE_RANDOM;
5247  else
5248  method = ANALYZE_SAMPLE_BERNOULLI;
5249  }
5250 
5251  /*
5252  * Construct cursor that retrieves whole rows from remote.
5253  */
5255  initStringInfo(&sql);
5256  appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
5257 
5258  deparseAnalyzeSql(&sql, relation, method, sample_frac, &astate.retrieved_attrs);
5259 
5260  /* In what follows, do not risk leaking any PGresults. */
5261  PG_TRY();
5262  {
5263  char fetch_sql[64];
5264  int fetch_size;
5265 
5266  res = pgfdw_exec_query(conn, sql.data, NULL);
5268  pgfdw_report_error(ERROR, res, conn, false, sql.data);
5269  PQclear(res);
5270  res = NULL;
5271 
5272  /*
5273  * Determine the fetch size. The default is arbitrary, but shouldn't
5274  * be enormous.
5275  */
5276  fetch_size = 100;
5277  foreach(lc, server->options)
5278  {
5279  DefElem *def = (DefElem *) lfirst(lc);
5280 
5281  if (strcmp(def->defname, "fetch_size") == 0)
5282  {
5283  (void) parse_int(defGetString(def), &fetch_size, 0, NULL);
5284  break;
5285  }
5286  }
5287  foreach(lc, table->options)
5288  {
5289  DefElem *def = (DefElem *) lfirst(lc);
5290 
5291  if (strcmp(def->defname, "fetch_size") == 0)
5292  {
5293  (void) parse_int(defGetString(def), &fetch_size, 0, NULL);
5294  break;
5295  }
5296  }
5297 
5298  /* Construct command to fetch rows from remote. */
5299  snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
5301 
5302  /* Retrieve and process rows a batch at a time. */
5303  for (;;)
5304  {
5305  int numrows;
5306  int i;
5307 
5308  /* Allow users to cancel long query */
5310 
5311  /*
5312  * XXX possible future improvement: if rowstoskip is large, we
5313  * could issue a MOVE rather than physically fetching the rows,
5314  * then just adjust rowstoskip and samplerows appropriately.
5315  */
5316 
5317  /* Fetch some rows */
5318  res = pgfdw_exec_query(conn, fetch_sql, NULL);
5319  /* On error, report the original query, not the FETCH. */
5321  pgfdw_report_error(ERROR, res, conn, false, sql.data);
5322 
5323  /* Process whatever we got. */
5324  numrows = PQntuples(res);
5325  for (i = 0; i < numrows; i++)
5326  analyze_row_processor(res, i, &astate);
5327 
5328  PQclear(res);
5329  res = NULL;
5330 
5331  /* Must be EOF if we didn't get all the rows requested. */
5332  if (numrows < fetch_size)
5333  break;
5334  }
5335 
5336  /* Close the cursor, just to be tidy. */
5338  }
5339  PG_CATCH();
5340  {
5341  PQclear(res);
5342  PG_RE_THROW();
5343  }
5344  PG_END_TRY();
5345 
5347 
5348  /* We assume that we have no dead tuple. */
5349  *totaldeadrows = 0.0;
5350 
5351  /*
5352  * Without sampling, we've retrieved all living tuples from foreign
5353  * server, so report that as totalrows. Otherwise use the reltuples
5354  * estimate we got from the remote side.
5355  */
5356  if (method == ANALYZE_SAMPLE_OFF)
5357  *totalrows = astate.samplerows;
5358  else
5359  *totalrows = reltuples;
5360 
5361  /*
5362  * Emit some interesting relation info
5363  */
5364  ereport(elevel,
5365  (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
5366  RelationGetRelationName(relation),
5367  *totalrows, astate.numrows)));
5368 
5369  return astate.numrows;
5370 }
unsigned int GetCursorNumber(PGconn *conn)
Definition: connection.c:824
void deparseAnalyzeSql(StringInfo buf, Relation rel, PgFdwSamplingMethod sample_method, double sample_frac, List **retrieved_attrs)
Definition: deparse.c:2561
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereport(elevel,...)
Definition: elog.h:149
int PQserverVersion(const PGconn *conn)
Definition: fe-connect.c:7161
static int server_version_num
Definition: guc_tables.c:571
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
uint64 fetch_size
Definition: pg_rewind.c:85
static void analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
static double postgresGetAnalyzeInfoForForeignTable(Relation relation, bool *can_tablesample)
static void close_cursor(PGconn *conn, unsigned int cursor_number, PgFdwConnState *conn_state)
PgFdwSamplingMethod
Definition: postgres_fdw.h:145
@ ANALYZE_SAMPLE_AUTO
Definition: postgres_fdw.h:147
@ ANALYZE_SAMPLE_OFF
Definition: postgres_fdw.h:146
@ ANALYZE_SAMPLE_BERNOULLI
Definition: postgres_fdw.h:150
@ ANALYZE_SAMPLE_SYSTEM
Definition: postgres_fdw.h:149
@ ANALYZE_SAMPLE_RANDOM
Definition: postgres_fdw.h:148
void reservoir_init_selection_state(ReservoirState rs, int n)
Definition: sampling.c:133
Form_pg_class rd_rel
Definition: rel.h:111

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

Referenced by postgresAnalyzeForeignTable().

◆ postgresAddForeignUpdateTargets()

static void postgresAddForeignUpdateTargets ( PlannerInfo root,
Index  rtindex,
RangeTblEntry target_rte,
Relation  target_relation 
)
static

Definition at line 1744 of file postgres_fdw.c.

1748 {
1749  Var *var;
1750 
1751  /*
1752  * In postgres_fdw, what we need is the ctid, same as for a regular table.
1753  */
1754 
1755  /* Make a Var representing the desired value */
1756  var = makeVar(rtindex,
1758  TIDOID,
1759  -1,
1760  InvalidOid,
1761  0);
1762 
1763  /* Register it as a row-identity column needed by this target rel */
1764  add_row_identity_var(root, var, rtindex, "ctid");
1765 }
void add_row_identity_var(PlannerInfo *root, Var *orig_var, Index rtindex, const char *rowid_name)
Definition: appendinfo.c:793
#define InvalidOid
Definition: postgres_ext.h:36

References add_row_identity_var(), InvalidOid, makeVar(), root, and SelfItemPointerAttributeNumber.

Referenced by postgres_fdw_handler().

◆ postgresAnalyzeForeignTable()

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

Definition at line 4932 of file postgres_fdw.c.

4935 {
4936  ForeignTable *table;
4937  UserMapping *user;
4938  PGconn *conn;
4939  StringInfoData sql;
4940  PGresult *volatile res = NULL;
4941 
4942  /* Return the row-analysis function pointer */
4944 
4945  /*
4946  * Now we have to get the number of pages. It's annoying that the ANALYZE
4947  * API requires us to return that now, because it forces some duplication
4948  * of effort between this routine and postgresAcquireSampleRowsFunc. But
4949  * it's probably not worth redefining that API at this point.
4950  */
4951 
4952  /*
4953  * Get the connection to use. We do the remote access as the table's
4954  * owner, even if the ANALYZE was started by some other user.
4955  */
4956  table = GetForeignTable(RelationGetRelid(relation));
4957  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4958  conn = GetConnection(user, false, NULL);
4959 
4960  /*
4961  * Construct command to get page count for relation.
4962  */
4963  initStringInfo(&sql);
4964  deparseAnalyzeSizeSql(&sql, relation);
4965 
4966  /* In what follows, do not risk leaking any PGresults. */
4967  PG_TRY();
4968  {
4969  res = pgfdw_exec_query(conn, sql.data, NULL);
4971  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4972 
4973  if (PQntuples(res) != 1 || PQnfields(res) != 1)
4974  elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
4975  *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
4976  }
4977  PG_FINALLY();
4978  {
4979  PQclear(res);
4980  }
4981  PG_END_TRY();
4982 
4984 
4985  return true;
4986 }
void deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
Definition: deparse.c:2499
static int postgresAcquireSampleRowsFunc(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)

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

Referenced by postgres_fdw_handler().

◆ postgresBeginDirectModify()

static void postgresBeginDirectModify ( ForeignScanState node,
int  eflags 
)
static

Definition at line 2642 of file postgres_fdw.c.

2643 {
2644  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
2645  EState *estate = node->ss.ps.state;
2646  PgFdwDirectModifyState *dmstate;
2647  Index rtindex;
2648  Oid userid;
2649  ForeignTable *table;
2650  UserMapping *user;
2651  int numParams;
2652 
2653  /*
2654  * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
2655  */
2656  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2657  return;
2658 
2659  /*
2660  * We'll save private state in node->fdw_state.
2661  */
2662  dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
2663  node->fdw_state = (void *) dmstate;
2664 
2665  /*
2666  * Identify which user to do the remote access as. This should match what
2667  * ExecCheckPermissions() does.
2668  */
2669  userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
2670 
2671  /* Get info about foreign table. */
2672  rtindex = node->resultRelInfo->ri_RangeTableIndex;
2673  if (fsplan->scan.scanrelid == 0)
2674  dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags);
2675  else
2676  dmstate->rel = node->ss.ss_currentRelation;
2677  table = GetForeignTable(RelationGetRelid(dmstate->rel));
2678  user = GetUserMapping(userid, table->serverid);
2679 
2680  /*
2681  * Get connection to the foreign server. Connection manager will
2682  * establish new connection if necessary.
2683  */
2684  dmstate->conn = GetConnection(user, false, &dmstate->conn_state);
2685 
2686  /* Update the foreign-join-related fields. */
2687  if (fsplan->scan.scanrelid == 0)
2688  {
2689  /* Save info about foreign table. */
2690  dmstate->resultRel = dmstate->rel;
2691 
2692  /*
2693  * Set dmstate->rel to NULL to teach get_returning_data() and
2694  * make_tuple_from_result_row() that columns fetched from the remote
2695  * server are described by fdw_scan_tlist of the foreign-scan plan
2696  * node, not the tuple descriptor for the target relation.
2697  */
2698  dmstate->rel = NULL;
2699  }
2700 
2701  /* Initialize state variable */
2702  dmstate->num_tuples = -1; /* -1 means not set yet */
2703 
2704  /* Get private info created by planner functions. */
2705  dmstate->query = strVal(list_nth(fsplan->fdw_private,
2707  dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
2709  dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
2711  dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
2713 
2714  /* Create context for per-tuple temp workspace. */
2715  dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
2716  "postgres_fdw temporary data",
2718 
2719  /* Prepare for input conversion of RETURNING results. */
2720  if (dmstate->has_returning)
2721  {
2722  TupleDesc tupdesc;
2723 
2724  if (fsplan->scan.scanrelid == 0)
2725  tupdesc = get_tupdesc_for_join_scan_tuples(node);
2726  else
2727  tupdesc = RelationGetDescr(dmstate->rel);
2728 
2729  dmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
2730 
2731  /*
2732  * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2733  * initialize a filter to extract an updated/deleted tuple from a scan
2734  * tuple.
2735  */
2736  if (fsplan->scan.scanrelid == 0)
2737  init_returning_filter(dmstate, fsplan->fdw_scan_tlist, rtindex);
2738  }
2739 
2740  /*
2741  * Prepare for processing of parameters used in remote query, if any.
2742  */
2743  numParams = list_length(fsplan->fdw_exprs);
2744  dmstate->numParams = numParams;
2745  if (numParams > 0)
2747  fsplan->fdw_exprs,
2748  numParams,
2749  &dmstate->param_flinfo,
2750  &dmstate->param_exprs,
2751  &dmstate->param_values);
2752 }
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:697
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:65
Oid GetUserId(void)
Definition: miscinit.c:514
static TupleDesc get_tupdesc_for_join_scan_tuples(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 init_returning_filter(PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
Oid checkAsUser
Definition: plannodes.h:712
List * fdw_exprs
Definition: plannodes.h:715
List * fdw_private
Definition: plannodes.h:716
Index ri_RangeTableIndex
Definition: execnodes.h:453
Relation ss_currentRelation
Definition: execnodes.h:1564
#define boolVal(v)
Definition: value.h:81

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, boolVal, ForeignScan::checkAsUser, EXEC_FLAG_EXPLAIN_ONLY, ExecOpenScanRelation(), ForeignScan::fdw_exprs, ForeignScan::fdw_private, ForeignScan::fdw_scan_tlist, ForeignScanState::fdw_state, FdwDirectModifyPrivateHasReturning, FdwDirectModifyPrivateRetrievedAttrs, FdwDirectModifyPrivateSetProcessed, FdwDirectModifyPrivateUpdateSql, get_tupdesc_for_join_scan_tuples(), GetConnection(), GetForeignTable(), GetUserId(), GetUserMapping(), if(), init_returning_filter(), list_length(), list_nth(), OidIsValid, palloc0(), PlanState::plan, prepare_query_params(), ScanState::ps, RelationGetDescr, RelationGetRelid, ForeignScanState::resultRelInfo, ResultRelInfo::ri_RangeTableIndex, ForeignScan::scan, Scan::scanrelid, ForeignScanState::ss, ScanState::ss_currentRelation, PlanState::state, strVal, TupleDescGetAttInMetadata(), and user.

Referenced by postgres_fdw_handler().

◆ postgresBeginForeignInsert()

static void postgresBeginForeignInsert ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2153 of file postgres_fdw.c.

2155 {
2156  PgFdwModifyState *fmstate;
2157  ModifyTable *plan = castNode(ModifyTable, mtstate->ps.plan);
2158  EState *estate = mtstate->ps.state;
2159  Index resultRelation;
2160  Relation rel = resultRelInfo->ri_RelationDesc;
2161  RangeTblEntry *rte;
2162  TupleDesc tupdesc = RelationGetDescr(rel);
2163  int attnum;
2164  int values_end_len;
2165  StringInfoData sql;
2166  List *targetAttrs = NIL;
2167  List *retrieved_attrs = NIL;
2168  bool doNothing = false;
2169 
2170  /*
2171  * If the foreign table we are about to insert routed rows into is also an
2172  * UPDATE subplan result rel that will be updated later, proceeding with
2173  * the INSERT will result in the later UPDATE incorrectly modifying those
2174  * routed rows, so prevent the INSERT --- it would be nice if we could
2175  * handle this case; but for now, throw an error for safety.
2176  */
2177  if (plan && plan->operation == CMD_UPDATE &&
2178  (resultRelInfo->ri_usesFdwDirectModify ||
2179  resultRelInfo->ri_FdwState))
2180  ereport(ERROR,
2181  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2182  errmsg("cannot route tuples into foreign table to be updated \"%s\"",
2183  RelationGetRelationName(rel))));
2184 
2185  initStringInfo(&sql);
2186 
2187  /* We transmit all columns that are defined in the foreign table. */
2188  for (attnum = 1; attnum <= tupdesc->natts; attnum++)
2189  {
2190  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2191 
2192  if (!attr->attisdropped)
2193  targetAttrs = lappend_int(targetAttrs, attnum);
2194  }
2195 
2196  /* Check if we add the ON CONFLICT clause to the remote query. */
2197  if (plan)
2198  {
2199  OnConflictAction onConflictAction = plan->onConflictAction;
2200 
2201  /* We only support DO NOTHING without an inference specification. */
2202  if (onConflictAction == ONCONFLICT_NOTHING)
2203  doNothing = true;
2204  else if (onConflictAction != ONCONFLICT_NONE)
2205  elog(ERROR, "unexpected ON CONFLICT specification: %d",
2206  (int) onConflictAction);
2207  }
2208 
2209  /*
2210  * If the foreign table is a partition that doesn't have a corresponding
2211  * RTE entry, we need to create a new RTE describing the foreign table for
2212  * use by deparseInsertSql and create_foreign_modify() below, after first
2213  * copying the parent's RTE and modifying some fields to describe the
2214  * foreign partition to work on. However, if this is invoked by UPDATE,
2215  * the existing RTE may already correspond to this partition if it is one
2216  * of the UPDATE subplan target rels; in that case, we can just use the
2217  * existing RTE as-is.
2218  */
2219  if (resultRelInfo->ri_RangeTableIndex == 0)
2220  {
2221  ResultRelInfo *rootResultRelInfo = resultRelInfo->ri_RootResultRelInfo;
2222 
2223  rte = exec_rt_fetch(rootResultRelInfo->ri_RangeTableIndex, estate);
2224  rte = copyObject(rte);
2225  rte->relid = RelationGetRelid(rel);
2226  rte->relkind = RELKIND_FOREIGN_TABLE;
2227 
2228  /*
2229  * For UPDATE, we must use the RT index of the first subplan target
2230  * rel's RTE, because the core code would have built expressions for
2231  * the partition, such as RETURNING, using that RT index as varno of
2232  * Vars contained in those expressions.
2233  */
2234  if (plan && plan->operation == CMD_UPDATE &&
2235  rootResultRelInfo->ri_RangeTableIndex == plan->rootRelation)
2236  resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
2237  else
2238  resultRelation = rootResultRelInfo->ri_RangeTableIndex;
2239  }
2240  else
2241  {
2242  resultRelation = resultRelInfo->ri_RangeTableIndex;
2243  rte = exec_rt_fetch(resultRelation, estate);
2244  }
2245 
2246  /* Construct the SQL command string. */
2247  deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing,
2248  resultRelInfo->ri_WithCheckOptions,
2249  resultRelInfo->ri_returningList,
2250  &retrieved_attrs, &values_end_len);
2251 
2252  /* Construct an execution state. */
2253  fmstate = create_foreign_modify(mtstate->ps.state,
2254  rte,
2255  resultRelInfo,
2256  CMD_INSERT,
2257  NULL,
2258  sql.data,
2259  targetAttrs,
2260  values_end_len,
2261  retrieved_attrs != NIL,
2262  retrieved_attrs);
2263 
2264  /*
2265  * If the given resultRelInfo already has PgFdwModifyState set, it means
2266  * the foreign table is an UPDATE subplan result rel; in which case, store
2267  * the resulting state into the aux_fmstate of the PgFdwModifyState.
2268  */
2269  if (resultRelInfo->ri_FdwState)
2270  {
2271  Assert(plan && plan->operation == CMD_UPDATE);
2272  Assert(resultRelInfo->ri_usesFdwDirectModify == false);
2273  ((PgFdwModifyState *) resultRelInfo->ri_FdwState)->aux_fmstate = fmstate;
2274  }
2275  else
2276  resultRelInfo->ri_FdwState = fmstate;
2277 }
void deparseInsertSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, bool doNothing, List *withCheckOptionList, List *returningList, List **retrieved_attrs, int *values_end_len)
Definition: deparse.c:2083
List * lappend_int(List *list, int datum)
Definition: list.c:357
#define copyObject(obj)
Definition: nodes.h:224
OnConflictAction
Definition: nodes.h:417
@ ONCONFLICT_NONE
Definition: nodes.h:418
@ ONCONFLICT_NOTHING
Definition: nodes.h:419
static PgFdwModifyState * create_foreign_modify(EState *estate, RangeTblEntry *rte, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, int values_end, bool has_returning, List *retrieved_attrs)
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1359
PlanState ps
Definition: execnodes.h:1354
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:582
List * ri_WithCheckOptions
Definition: execnodes.h:519
List * ri_returningList
Definition: execnodes.h:536
bool ri_usesFdwDirectModify
Definition: execnodes.h:509

References Assert, attnum, castNode, CMD_INSERT, CMD_UPDATE, copyObject, create_foreign_modify(), StringInfoData::data, deparseInsertSql(), elog, ereport, errcode(), errmsg(), ERROR, exec_rt_fetch(), initStringInfo(), lappend_int(), TupleDescData::natts, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, PlanState::plan, plan, ModifyTableState::ps, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RangeTblEntry::relid, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwState, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_returningList, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_usesFdwDirectModify, ResultRelInfo::ri_WithCheckOptions, PlanState::state, and TupleDescAttr.

Referenced by postgres_fdw_handler().

◆ postgresBeginForeignModify()

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

Definition at line 1911 of file postgres_fdw.c.

1916 {
1917  PgFdwModifyState *fmstate;
1918  char *query;
1919  List *target_attrs;
1920  bool has_returning;
1921  int values_end_len;
1922  List *retrieved_attrs;
1923  RangeTblEntry *rte;
1924 
1925  /*
1926  * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
1927  * stays NULL.
1928  */
1929  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1930  return;
1931 
1932  /* Deconstruct fdw_private data. */
1933  query = strVal(list_nth(fdw_private,
1935  target_attrs = (List *) list_nth(fdw_private,
1937  values_end_len = intVal(list_nth(fdw_private,
1939  has_returning = boolVal(list_nth(fdw_private,
1941  retrieved_attrs = (List *) list_nth(fdw_private,
1943 
1944  /* Find RTE. */
1945  rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex,
1946  mtstate->ps.state);
1947 
1948  /* Construct an execution state. */
1949  fmstate = create_foreign_modify(mtstate->ps.state,
1950  rte,
1951  resultRelInfo,
1952  mtstate->operation,
1953  outerPlanState(mtstate)->plan,
1954  query,
1955  target_attrs,
1956  values_end_len,
1957  has_returning,
1958  retrieved_attrs);
1959 
1960  resultRelInfo->ri_FdwState = fmstate;
1961 }
#define outerPlanState(node)
Definition: execnodes.h:1212
CmdType operation
Definition: execnodes.h:1355
#define intVal(v)
Definition: value.h:79

References boolVal, create_foreign_modify(), EXEC_FLAG_EXPLAIN_ONLY, exec_rt_fetch(), FdwModifyPrivateHasReturning, FdwModifyPrivateLen, FdwModifyPrivateRetrievedAttrs, FdwModifyPrivateTargetAttnums, FdwModifyPrivateUpdateSql, intVal, list_nth(), ModifyTableState::operation, outerPlanState, plan, ModifyTableState::ps, ResultRelInfo::ri_FdwState, ResultRelInfo::ri_RangeTableIndex, PlanState::state, and strVal.

Referenced by postgres_fdw_handler().

◆ postgresBeginForeignScan()

static void postgresBeginForeignScan ( ForeignScanState node,
int  eflags 
)
static

Definition at line 1491 of file postgres_fdw.c.

1492 {
1493  ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1494  EState *estate = node->ss.ps.state;
1495  PgFdwScanState *fsstate;
1496  RangeTblEntry *rte;
1497  Oid userid;
1498  ForeignTable *table;
1499  UserMapping *user;
1500  int rtindex;
1501  int numParams;
1502 
1503  /*
1504  * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
1505  */
1506  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1507  return;
1508 
1509  /*
1510  * We'll save private state in node->fdw_state.
1511  */
1512  fsstate = (PgFdwScanState *) palloc0(sizeof(PgFdwScanState));
1513  node->fdw_state = (void *) fsstate;
1514 
1515  /*
1516  * Identify which user to do the remote access as. This should match what
1517  * ExecCheckPermissions() does.
1518  */
1519  userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
1520  if (fsplan->scan.scanrelid > 0)
1521  rtindex = fsplan->scan.scanrelid;
1522  else
1523  rtindex = bms_next_member(fsplan->fs_base_relids, -1);
1524  rte = exec_rt_fetch(rtindex, estate);
1525 
1526  /* Get info about foreign table. */
1527  table = GetForeignTable(rte->relid);
1528  user = GetUserMapping(userid, table->serverid);
1529 
1530  /*
1531  * Get connection to the foreign server. Connection manager will
1532  * establish new connection if necessary.
1533  */
1534  fsstate->conn = GetConnection(user, false, &fsstate->conn_state);
1535 
1536  /* Assign a unique ID for my cursor */
1537  fsstate->cursor_number = GetCursorNumber(fsstate->conn);
1538  fsstate->cursor_exists = false;
1539 
1540  /* Get private info created by planner functions. */
1541  fsstate->query = strVal(list_nth(fsplan->fdw_private,
1543  fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
1545  fsstate->fetch_size = intVal(list_nth(fsplan->fdw_private,
1547 
1548  /* Create contexts for batches of tuples and per-tuple temp workspace. */
1549  fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
1550  "postgres_fdw tuple data",
1552  fsstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
1553  "postgres_fdw temporary data",
1555 
1556  /*
1557  * Get info we'll need for converting data fetched from the foreign server
1558  * into local representation and error reporting during that process.
1559  */
1560  if (fsplan->scan.scanrelid > 0)
1561  {
1562  fsstate->rel = node->ss.ss_currentRelation;
1563  fsstate->tupdesc = RelationGetDescr(fsstate->rel);
1564  }
1565  else
1566  {
1567  fsstate->rel = NULL;
1568  fsstate->tupdesc = get_tupdesc_for_join_scan_tuples(node);
1569  }
1570 
1571  fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
1572 
1573  /*
1574  * Prepare for processing of parameters used in remote query, if any.
1575  */
1576  numParams = list_length(fsplan->fdw_exprs);
1577  fsstate->numParams = numParams;
1578  if (numParams > 0)
1580  fsplan->fdw_exprs,
1581  numParams,
1582  &fsstate->param_flinfo,
1583  &fsstate->param_exprs,
1584  &fsstate->param_values);
1585 
1586  /* Set the async-capable flag */
1587  fsstate->async_capable = node->ss.ps.async_capable;
1588 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
bool async_capable
Definition: execnodes.h:1158

References ALLOCSET_DEFAULT_SIZES, ALLOCSET_SMALL_SIZES, AllocSetContextCreate, PlanState::async_capable, bms_next_member(), ForeignScan::checkAsUser, EXEC_FLAG_EXPLAIN_ONLY, exec_rt_fetch(), ForeignScan::fdw_exprs, ForeignScan::fdw_private, ForeignScanState::fdw_state, FdwScanPrivateFetchSize, FdwScanPrivateRetrievedAttrs, FdwScanPrivateSelectSql, ForeignScan::fs_base_relids, get_tupdesc_for_join_scan_tuples(), GetConnection(), GetCursorNumber(), GetForeignTable(), GetUserId(), GetUserMapping(), if(), intVal, list_length(), list_nth(), OidIsValid, palloc0(), PlanState::plan, prepare_query_params(), ScanState::ps, RelationGetDescr, ForeignScan::scan, Scan::scanrelid, ForeignScanState::ss, ScanState::ss_currentRelation, PlanState::state, strVal, TupleDescGetAttInMetadata(), and user.

Referenced by postgres_fdw_handler().

◆ postgresEndDirectModify()

static void postgresEndDirectModify ( ForeignScanState node)
static

Definition at line 2803 of file postgres_fdw.c.

2804 {
2806 
2807  /* if dmstate is NULL, we are in EXPLAIN; nothing to do */
2808  if (dmstate == NULL)
2809  return;
2810 
2811  /* Release PGresult */
2812  PQclear(dmstate->result);
2813 
2814  /* Release remote connection */
2815  ReleaseConnection(dmstate->conn);
2816  dmstate->conn = NULL;
2817 
2818  /* MemoryContext will be deleted automatically. */
2819 }

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

Referenced by postgres_fdw_handler().

◆ postgresEndForeignInsert()

static void postgresEndForeignInsert ( EState estate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2284 of file postgres_fdw.c.

2286 {
2287  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2288 
2289  Assert(fmstate != NULL);
2290 
2291  /*
2292  * If the fmstate has aux_fmstate set, get the aux_fmstate (see
2293  * postgresBeginForeignInsert())
2294  */
2295  if (fmstate->aux_fmstate)
2296  fmstate = fmstate->aux_fmstate;
2297 
2298  /* Destroy the execution state */
2299  finish_foreign_modify(fmstate);
2300 }
static void finish_foreign_modify(PgFdwModifyState *fmstate)

References Assert, PgFdwModifyState::aux_fmstate, finish_foreign_modify(), and ResultRelInfo::ri_FdwState.

Referenced by postgres_fdw_handler().

◆ postgresEndForeignModify()

static void postgresEndForeignModify ( EState estate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2135 of file postgres_fdw.c.

2137 {
2138  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2139 
2140  /* If fmstate is NULL, we are in EXPLAIN; nothing to do */
2141  if (fmstate == NULL)
2142  return;
2143 
2144  /* Destroy the execution state */
2145  finish_foreign_modify(fmstate);
2146 }

References finish_foreign_modify(), if(), and ResultRelInfo::ri_FdwState.

Referenced by postgres_fdw_handler().

◆ postgresEndForeignScan()

static void postgresEndForeignScan ( ForeignScanState node)
static

Definition at line 1719 of file postgres_fdw.c.

1720 {
1721  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1722 
1723  /* if fsstate is NULL, we are in EXPLAIN; nothing to do */
1724  if (fsstate == NULL)
1725  return;
1726 
1727  /* Close the cursor if open, to prevent accumulation of cursors */
1728  if (fsstate->cursor_exists)
1729  close_cursor(fsstate->conn, fsstate->cursor_number,
1730  fsstate->conn_state);
1731 
1732  /* Release remote connection */
1733  ReleaseConnection(fsstate->conn);
1734  fsstate->conn = NULL;
1735 
1736  /* MemoryContexts will be deleted automatically. */
1737 }

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

Referenced by postgres_fdw_handler().

◆ postgresExecForeignBatchInsert()

static TupleTableSlot ** postgresExecForeignBatchInsert ( EState estate,
ResultRelInfo resultRelInfo,
TupleTableSlot **  slots,
TupleTableSlot **  planSlots,
int *  numSlots 
)
static

Definition at line 1997 of file postgres_fdw.c.

2002 {
2003  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2004  TupleTableSlot **rslot;
2005 
2006  /*
2007  * If the fmstate has aux_fmstate set, use the aux_fmstate (see
2008  * postgresBeginForeignInsert())
2009  */
2010  if (fmstate->aux_fmstate)
2011  resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2012  rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2013  slots, planSlots, numSlots);
2014  /* Revert that change */
2015  if (fmstate->aux_fmstate)
2016  resultRelInfo->ri_FdwState = fmstate;
2017 
2018  return rslot;
2019 }
static TupleTableSlot ** execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)

References PgFdwModifyState::aux_fmstate, CMD_INSERT, execute_foreign_modify(), if(), and ResultRelInfo::ri_FdwState.

Referenced by postgres_fdw_handler().

◆ postgresExecForeignDelete()

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

Definition at line 2116 of file postgres_fdw.c.

2120 {
2121  TupleTableSlot **rslot;
2122  int numSlots = 1;
2123 
2124  rslot = execute_foreign_modify(estate, resultRelInfo, CMD_DELETE,
2125  &slot, &planSlot, &numSlots);
2126 
2127  return rslot ? rslot[0] : NULL;
2128 }

References CMD_DELETE, and execute_foreign_modify().

Referenced by postgres_fdw_handler().

◆ postgresExecForeignInsert()

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

Definition at line 1968 of file postgres_fdw.c.

1972 {
1973  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
1974  TupleTableSlot **rslot;
1975  int numSlots = 1;
1976 
1977  /*
1978  * If the fmstate has aux_fmstate set, use the aux_fmstate (see
1979  * postgresBeginForeignInsert())
1980  */
1981  if (fmstate->aux_fmstate)
1982  resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
1983  rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
1984  &slot, &planSlot, &numSlots);
1985  /* Revert that change */
1986  if (fmstate->aux_fmstate)
1987  resultRelInfo->ri_FdwState = fmstate;
1988 
1989  return rslot ? *rslot : NULL;
1990 }

References PgFdwModifyState::aux_fmstate, CMD_INSERT, execute_foreign_modify(), if(), and ResultRelInfo::ri_FdwState.

Referenced by postgres_fdw_handler().

◆ postgresExecForeignTruncate()

static void postgresExecForeignTruncate ( List rels,
DropBehavior  behavior,
bool  restart_seqs 
)
static

Definition at line 2979 of file postgres_fdw.c.

2982 {
2983  Oid serverid = InvalidOid;
2984  UserMapping *user = NULL;
2985  PGconn *conn = NULL;
2986  StringInfoData sql;
2987  ListCell *lc;
2988  bool server_truncatable = true;
2989 
2990  /*
2991  * By default, all postgres_fdw foreign tables are assumed truncatable.
2992  * This can be overridden by a per-server setting, which in turn can be
2993  * overridden by a per-table setting.
2994  */
2995  foreach(lc, rels)
2996  {
2997  ForeignServer *server = NULL;
2998  Relation rel = lfirst(lc);
3000  ListCell *cell;
3001  bool truncatable;
3002 
3003  /*
3004  * First time through, determine whether the foreign server allows
3005  * truncates. Since all specified foreign tables are assumed to belong
3006  * to the same foreign server, this result can be used for other
3007  * foreign tables.
3008  */
3009  if (!OidIsValid(serverid))
3010  {
3011  serverid = table->serverid;
3012  server = GetForeignServer(serverid);
3013 
3014  foreach(cell, server->options)
3015  {
3016  DefElem *defel = (DefElem *) lfirst(cell);
3017 
3018  if (strcmp(defel->defname, "truncatable") == 0)
3019  {
3020  server_truncatable = defGetBoolean(defel);
3021  break;
3022  }
3023  }
3024  }
3025 
3026  /*
3027  * Confirm that all specified foreign tables belong to the same
3028  * foreign server.
3029  */
3030  Assert(table->serverid == serverid);
3031 
3032  /* Determine whether this foreign table allows truncations */
3033  truncatable = server_truncatable;
3034  foreach(cell, table->options)
3035  {
3036  DefElem *defel = (DefElem *) lfirst(cell);
3037 
3038  if (strcmp(defel->defname, "truncatable") == 0)
3039  {
3040  truncatable = defGetBoolean(defel);
3041  break;
3042  }
3043  }
3044 
3045  if (!truncatable)
3046  ereport(ERROR,
3047  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3048  errmsg("foreign table \"%s\" does not allow truncates",
3049  RelationGetRelationName(rel))));
3050  }
3051  Assert(OidIsValid(serverid));
3052 
3053  /*
3054  * Get connection to the foreign server. Connection manager will
3055  * establish new connection if necessary.
3056  */
3057  user = GetUserMapping(GetUserId(), serverid);
3058  conn = GetConnection(user, false, NULL);
3059 
3060  /* Construct the TRUNCATE command string */
3061  initStringInfo(&sql);
3062  deparseTruncateSql(&sql, rels, behavior, restart_seqs);
3063 
3064  /* Issue the TRUNCATE command to remote server */
3065  do_sql_command(conn, sql.data);
3066 
3067  pfree(sql.data);
3068 }
void do_sql_command(PGconn *conn, const char *sql)
Definition: connection.c:717
void deparseTruncateSql(StringInfo buf, List *rels, DropBehavior behavior, bool restart_seqs)
Definition: deparse.c:2646

References Assert, conn, StringInfoData::data, defGetBoolean(), DefElem::defname, deparseTruncateSql(), do_sql_command(), ereport, errcode(), errmsg(), ERROR, GetConnection(), GetForeignServer(), GetForeignTable(), GetUserId(), GetUserMapping(), initStringInfo(), InvalidOid, lfirst, OidIsValid, ForeignServer::options, ForeignTable::options, pfree(), RelationGetRelationName, RelationGetRelid, ForeignTable::serverid, and user.

Referenced by postgres_fdw_handler().

◆ postgresExecForeignUpdate()

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

Definition at line 2097 of file postgres_fdw.c.

2101 {
2102  TupleTableSlot **rslot;
2103  int numSlots = 1;
2104 
2105  rslot = execute_foreign_modify(estate, resultRelInfo, CMD_UPDATE,
2106  &slot, &planSlot, &numSlots);
2107 
2108  return rslot ? rslot[0] : NULL;
2109 }

References CMD_UPDATE, and execute_foreign_modify().

Referenced by postgres_fdw_handler().

◆ postgresExplainDirectModify()

static void postgresExplainDirectModify ( ForeignScanState node,
ExplainState es 
)
static

Definition at line 2961 of file postgres_fdw.c.

2962 {
2963  List *fdw_private;
2964  char *sql;
2965 
2966  if (es->verbose)
2967  {
2968  fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
2969  sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql));
2970  ExplainPropertyText("Remote SQL", sql, es);
2971  }
2972 }
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
Definition: explain.c:4883
bool verbose
Definition: explain.h:48

References ExplainPropertyText(), FdwDirectModifyPrivateUpdateSql, list_nth(), PlanState::plan, ScanState::ps, ForeignScanState::ss, strVal, and ExplainState::verbose.

Referenced by postgres_fdw_handler().

◆ postgresExplainForeignModify()

static void postgresExplainForeignModify ( ModifyTableState mtstate,
ResultRelInfo rinfo,
List fdw_private,
int  subplan_index,
ExplainState es 
)
static

Definition at line 2933 of file postgres_fdw.c.

2938 {
2939  if (es->verbose)
2940  {
2941  char *sql = strVal(list_nth(fdw_private,
2943 
2944  ExplainPropertyText("Remote SQL", sql, es);
2945 
2946  /*
2947  * For INSERT we should always have batch size >= 1, but UPDATE and
2948  * DELETE don't support batching so don't show the property.
2949  */
2950  if (rinfo->ri_BatchSize > 0)
2951  ExplainPropertyInteger("Batch Size", NULL, rinfo->ri_BatchSize, es);
2952  }
2953 }
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
Definition: explain.c:4892
int ri_BatchSize
Definition: execnodes.h:514

References ExplainPropertyInteger(), ExplainPropertyText(), FdwModifyPrivateUpdateSql, list_nth(), ResultRelInfo::ri_BatchSize, strVal, and ExplainState::verbose.

Referenced by postgres_fdw_handler().

◆ postgresExplainForeignScan()

static void postgresExplainForeignScan ( ForeignScanState node,
ExplainState es 
)
static

Definition at line 2826 of file postgres_fdw.c.

2827 {
2829  List *fdw_private = plan->fdw_private;
2830 
2831  /*
2832  * Identify foreign scans that are really joins or upper relations. The
2833  * input looks something like "(1) LEFT JOIN (2)", and we must replace the
2834  * digit string(s), which are RT indexes, with the correct relation names.
2835  * We do that here, not when the plan is created, because we can't know
2836  * what aliases ruleutils.c will assign at plan creation time.
2837  */
2838  if (list_length(fdw_private) > FdwScanPrivateRelations)
2839  {
2840  StringInfo relations;
2841  char *rawrelations;
2842  char *ptr;
2843  int minrti,
2844  rtoffset;
2845 
2846  rawrelations = strVal(list_nth(fdw_private, FdwScanPrivateRelations));
2847 
2848  /*
2849  * A difficulty with using a string representation of RT indexes is
2850  * that setrefs.c won't update the string when flattening the
2851  * rangetable. To find out what rtoffset was applied, identify the
2852  * minimum RT index appearing in the string and compare it to the
2853  * minimum member of plan->fs_base_relids. (We expect all the relids
2854  * in the join will have been offset by the same amount; the Asserts
2855  * below should catch it if that ever changes.)
2856  */
2857  minrti = INT_MAX;
2858  ptr = rawrelations;
2859  while (*ptr)
2860  {
2861  if (isdigit((unsigned char) *ptr))
2862  {
2863  int rti = strtol(ptr, &ptr, 10);
2864 
2865  if (rti < minrti)
2866  minrti = rti;
2867  }
2868  else
2869  ptr++;
2870  }
2871  rtoffset = bms_next_member(plan->fs_base_relids, -1) - minrti;
2872 
2873  /* Now we can translate the string */
2874  relations = makeStringInfo();
2875  ptr = rawrelations;
2876  while (*ptr)
2877  {
2878  if (isdigit((unsigned char) *ptr))
2879  {
2880  int rti = strtol(ptr, &ptr, 10);
2881  RangeTblEntry *rte;
2882  char *relname;
2883  char *refname;
2884 
2885  rti += rtoffset;
2886  Assert(bms_is_member(rti, plan->fs_base_relids));
2887  rte = rt_fetch(rti, es->rtable);
2888  Assert(rte->rtekind == RTE_RELATION);
2889  /* This logic should agree with explain.c's ExplainTargetRel */
2890  relname = get_rel_name(rte->relid);
2891  if (es->verbose)
2892  {
2893  char *namespace;
2894 
2895  namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid));
2896  appendStringInfo(relations, "%s.%s",
2897  quote_identifier(namespace),
2899  }
2900  else
2901  appendStringInfoString(relations,
2903  refname = (char *) list_nth(es->rtable_names, rti - 1);
2904  if (refname == NULL)
2905  refname = rte->eref->aliasname;
2906  if (strcmp(refname, relname) != 0)
2907  appendStringInfo(relations, " %s",
2908  quote_identifier(refname));
2909  }
2910  else
2911  appendStringInfoChar(relations, *ptr++);
2912  }
2913  ExplainPropertyText("Relations", relations->data, es);
2914  }
2915 
2916  /*
2917  * Add remote query, when VERBOSE option is specified.
2918  */
2919  if (es->verbose)
2920  {
2921  char *sql;
2922 
2923  sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
2924  ExplainPropertyText("Remote SQL", sql, es);
2925  }
2926 }
char * get_namespace_name_or_temp(Oid nspid)
Definition: lsyscache.c:3390
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1952
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1928
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:12596
StringInfo makeStringInfo(void)
Definition: stringinfo.c:41
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:194
List * rtable_names
Definition: explain.h:66
List * rtable
Definition: explain.h:65

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, bms_is_member(), bms_next_member(), castNode, StringInfoData::data, ExplainPropertyText(), FdwScanPrivateRelations, FdwScanPrivateSelectSql, get_namespace_name_or_temp(), get_rel_name(), get_rel_namespace(), list_length(), list_nth(), makeStringInfo(), PlanState::plan, plan, ScanState::ps, quote_identifier(), RangeTblEntry::relid, relname, rt_fetch, ExplainState::rtable, ExplainState::rtable_names, RTE_RELATION, RangeTblEntry::rtekind, ForeignScanState::ss, strVal, and ExplainState::verbose.

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncConfigureWait()

static void postgresForeignAsyncConfigureWait ( AsyncRequest areq)
static

Definition at line 7260 of file postgres_fdw.c.

7261 {
7262  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7263  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7264  AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
7265  AppendState *requestor = (AppendState *) areq->requestor;
7266  WaitEventSet *set = requestor->as_eventset;
7267 
7268  /* This should not be called unless callback_pending */
7269  Assert(areq->callback_pending);
7270 
7271  /*
7272  * If process_pending_request() has been invoked on the given request
7273  * before we get here, we might have some tuples already; in which case
7274  * complete the request
7275  */
7276  if (fsstate->next_tuple < fsstate->num_tuples)
7277  {
7279  if (areq->request_complete)
7280  return;
7281  Assert(areq->callback_pending);
7282  }
7283 
7284  /* We must have run out of tuples */
7285  Assert(fsstate->next_tuple >= fsstate->num_tuples);
7286 
7287  /* The core code would have registered postmaster death event */
7289 
7290  /* Begin an asynchronous data fetch if not already done */
7291  if (!pendingAreq)
7292  fetch_more_data_begin(areq);
7293  else if (pendingAreq->requestor != areq->requestor)
7294  {
7295  /*
7296  * This is the case when the in-process request was made by another
7297  * Append. Note that it might be useless to process the request made
7298  * by that Append, because the query might not need tuples from that
7299  * Append anymore; so we avoid processing it to begin a fetch for the
7300  * given request if possible. If there are any child subplans of the
7301  * same parent that are ready for new requests, skip the given
7302  * request. Likewise, if there are any configured events other than
7303  * the postmaster death event, skip it. Otherwise, process the
7304  * in-process request, then begin a fetch to configure the event
7305  * below, because we might otherwise end up with no configured events
7306  * other than the postmaster death event.
7307  */
7308  if (!bms_is_empty(requestor->as_needrequest))
7309  return;
7310  if (GetNumRegisteredWaitEvents(set) > 1)
7311  return;
7312  process_pending_request(pendingAreq);
7313  fetch_more_data_begin(areq);
7314  }
7315  else if (pendingAreq->requestee != areq->requestee)
7316  {
7317  /*
7318  * This is the case when the in-process request was made by the same
7319  * parent but for a different child. Since we configure only the
7320  * event for the request made for that child, skip the given request.
7321  */
7322  return;
7323  }
7324  else
7325  Assert(pendingAreq == areq);
7326 
7327  AddWaitEventToSet(set, WL_SOCKET_READABLE, PQsocket(fsstate->conn),
7328  NULL, areq);
7329 }
int PQsocket(const PGconn *conn)
Definition: fe-connect.c:7197
int GetNumRegisteredWaitEvents(WaitEventSet *set)
Definition: latch.c:2262
int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch, void *user_data)
Definition: latch.c:963
#define WL_SOCKET_READABLE
Definition: latch.h:128
static void fetch_more_data_begin(AsyncRequest *areq)
static void complete_pending_request(AsyncRequest *areq)
Bitmapset * as_needrequest
Definition: execnodes.h:1451
struct WaitEventSet * as_eventset
Definition: execnodes.h:1452
struct PlanState * requestor
Definition: execnodes.h:603
bool request_complete
Definition: execnodes.h:607

References AddWaitEventToSet(), AppendState::as_eventset, AppendState::as_needrequest, Assert, bms_is_empty, AsyncRequest::callback_pending, complete_pending_request(), ForeignScanState::fdw_state, fetch_more_data_begin(), GetNumRegisteredWaitEvents(), PQsocket(), process_pending_request(), AsyncRequest::request_complete, AsyncRequest::requestee, AsyncRequest::requestor, and WL_SOCKET_READABLE.

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncNotify()

static void postgresForeignAsyncNotify ( AsyncRequest areq)
static

Definition at line 7337 of file postgres_fdw.c.

7338 {
7339  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7340  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7341 
7342  /* The core code would have initialized the callback_pending flag */
7343  Assert(!areq->callback_pending);
7344 
7345  /*
7346  * If process_pending_request() has been invoked on the given request
7347  * before we get here, we might have some tuples already; in which case
7348  * produce the next tuple
7349  */
7350  if (fsstate->next_tuple < fsstate->num_tuples)
7351  {
7352  produce_tuple_asynchronously(areq, true);
7353  return;
7354  }
7355 
7356  /* We must have run out of tuples */
7357  Assert(fsstate->next_tuple >= fsstate->num_tuples);
7358 
7359  /* The request should be currently in-process */
7360  Assert(fsstate->conn_state->pendingAreq == areq);
7361 
7362  /* On error, report the original query, not the FETCH. */
7363  if (!PQconsumeInput(fsstate->conn))
7364  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7365 
7366  fetch_more_data(node);
7367 
7368  produce_tuple_asynchronously(areq, true);
7369 }
int PQconsumeInput(PGconn *conn)
Definition: fe-exec.c:1984
static void fetch_more_data(ForeignScanState *node)

References Assert, AsyncRequest::callback_pending, ERROR, ForeignScanState::fdw_state, fetch_more_data(), pgfdw_report_error(), PQconsumeInput(), produce_tuple_asynchronously(), and AsyncRequest::requestee.

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncRequest()

static void postgresForeignAsyncRequest ( AsyncRequest areq)
static

Definition at line 7250 of file postgres_fdw.c.

7251 {
7252  produce_tuple_asynchronously(areq, true);
7253 }

References produce_tuple_asynchronously().

Referenced by postgres_fdw_handler().

◆ postgresGetAnalyzeInfoForForeignTable()

static double postgresGetAnalyzeInfoForForeignTable ( Relation  relation,
bool can_tablesample 
)
static

Definition at line 4996 of file postgres_fdw.c.

4997 {
4998  ForeignTable *table;
4999  UserMapping *user;
5000  PGconn *conn;
5001  StringInfoData sql;
5002  PGresult *volatile res = NULL;
5003  volatile double reltuples = -1;
5004  volatile char relkind = 0;
5005 
5006  /* assume the remote relation does not support TABLESAMPLE */
5007  *can_tablesample = false;
5008 
5009  /*
5010  * Get the connection to use. We do the remote access as the table's
5011  * owner, even if the ANALYZE was started by some other user.
5012  */
5013  table = GetForeignTable(RelationGetRelid(relation));
5014  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5015  conn = GetConnection(user, false, NULL);
5016 
5017  /*
5018  * Construct command to get page count for relation.
5019  */
5020  initStringInfo(&sql);
5021  deparseAnalyzeInfoSql(&sql, relation);
5022 
5023  /* In what follows, do not risk leaking any PGresults. */
5024  PG_TRY();
5025  {
5026  res = pgfdw_exec_query(conn, sql.data, NULL);
5028  pgfdw_report_error(ERROR, res, conn, false, sql.data);
5029 
5030  if (PQntuples(res) != 1 || PQnfields(res) != 2)
5031  elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
5032  reltuples = strtod(PQgetvalue(res, 0, 0), NULL);
5033  relkind = *(PQgetvalue(res, 0, 1));
5034  }
5035  PG_FINALLY();
5036  {
5037  if (res)
5038  PQclear(res);
5039  }
5040  PG_END_TRY();
5041 
5043 
5044  /* TABLESAMPLE is supported only for regular tables and matviews */
5045  *can_tablesample = (relkind == RELKIND_RELATION ||
5046  relkind == RELKIND_MATVIEW ||
5047  relkind == RELKIND_PARTITIONED_TABLE);
5048 
5049  return reltuples;
5050 }
void deparseAnalyzeInfoSql(StringInfo buf, Relation rel)
Definition: deparse.c:2521

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

Referenced by postgresAcquireSampleRowsFunc().

◆ postgresGetForeignJoinPaths()

static void postgresGetForeignJoinPaths ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 6327 of file postgres_fdw.c.

6333 {
6334  PgFdwRelationInfo *fpinfo;
6335  ForeignPath *joinpath;
6336  double rows;
6337  int width;
6338  Cost startup_cost;
6339  Cost total_cost;
6340  Path *epq_path; /* Path to create plan to be executed when
6341  * EvalPlanQual gets triggered. */
6342 
6343  /*
6344  * Skip if this join combination has been considered already.
6345  */
6346  if (joinrel->fdw_private)
6347  return;
6348 
6349  /*
6350  * This code does not work for joins with lateral references, since those
6351  * must have parameterized paths, which we don't generate yet.
6352  */
6353  if (!bms_is_empty(joinrel->lateral_relids))
6354  return;
6355 
6356  /*
6357  * Create unfinished PgFdwRelationInfo entry which is used to indicate
6358  * that the join relation is already considered, so that we won't waste
6359  * time in judging safety of join pushdown and adding the same paths again
6360  * if found safe. Once we know that this join can be pushed down, we fill
6361  * the entry.
6362  */
6363  fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo));
6364  fpinfo->pushdown_safe = false;
6365  joinrel->fdw_private = fpinfo;
6366  /* attrs_used is only for base relations. */
6367  fpinfo->attrs_used = NULL;
6368 
6369  /*
6370  * If there is a possibility that EvalPlanQual will be executed, we need
6371  * to be able to reconstruct the row using scans of the base relations.
6372  * GetExistingLocalJoinPath will find a suitable path for this purpose in
6373  * the path list of the joinrel, if one exists. We must be careful to
6374  * call it before adding any ForeignPath, since the ForeignPath might
6375  * dominate the only suitable local path available. We also do it before
6376  * calling foreign_join_ok(), since that function updates fpinfo and marks
6377  * it as pushable if the join is found to be pushable.
6378  */
6379  if (root->parse->commandType == CMD_DELETE ||
6380  root->parse->commandType == CMD_UPDATE ||
6381  root->rowMarks)
6382  {
6383  epq_path = GetExistingLocalJoinPath(joinrel);
6384  if (!epq_path)
6385  {
6386  elog(DEBUG3, "could not push down foreign join because a local path suitable for EPQ checks was not found");
6387  return;
6388  }
6389  }
6390  else
6391  epq_path = NULL;
6392 
6393  if (!foreign_join_ok(root, joinrel, jointype, outerrel, innerrel, extra))
6394  {
6395  /* Free path required for EPQ if we copied one; we don't need it now */
6396  if (epq_path)
6397  pfree(epq_path);
6398  return;
6399  }
6400 
6401  /*
6402  * Compute the selectivity and cost of the local_conds, so we don't have
6403  * to do it over again for each path. The best we can do for these
6404  * conditions is to estimate selectivity on the basis of local statistics.
6405  * The local conditions are applied after the join has been computed on
6406  * the remote side like quals in WHERE clause, so pass jointype as
6407  * JOIN_INNER.
6408  */
6410  fpinfo->local_conds,
6411  0,
6412  JOIN_INNER,
6413  NULL);
6414  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6415 
6416  /*
6417  * If we are going to estimate costs locally, estimate the join clause
6418  * selectivity here while we have special join info.
6419  */
6420  if (!fpinfo->use_remote_estimate)
6422  0, fpinfo->jointype,
6423  extra->sjinfo);
6424 
6425  /* Estimate costs for bare join relation */
6426  estimate_path_cost_size(root, joinrel, NIL, NIL, NULL,
6427  &rows, &width, &startup_cost, &total_cost);
6428  /* Now update this information in the joinrel */
6429  joinrel->rows = rows;
6430  joinrel->reltarget->width = width;
6431  fpinfo->rows = rows;
6432  fpinfo->width = width;
6433  fpinfo->startup_cost = startup_cost;
6434  fpinfo->total_cost = total_cost;
6435 
6436  /*
6437  * Create a new join path and add it to the joinrel which represents a
6438  * join between foreign tables.
6439  */
6440  joinpath = create_foreign_join_path(root,
6441  joinrel,
6442  NULL, /* default pathtarget */
6443  rows,
6444  startup_cost,
6445  total_cost,
6446  NIL, /* no pathkeys */
6447  joinrel->lateral_relids,
6448  epq_path,
6449  extra->restrictlist,
6450  NIL); /* no fdw_private */
6451 
6452  /* Add generated path into joinrel by add_path(). */
6453  add_path(joinrel, (Path *) joinpath);
6454 
6455  /* Consider pathkeys for the join relation */
6456  add_paths_with_pathkeys_for_rel(root, joinrel, epq_path,
6457  extra->restrictlist);
6458 
6459  /* XXX Consider parameterized paths for the join relation */
6460 }
#define DEBUG3
Definition: elog.h:28
Path * GetExistingLocalJoinPath(RelOptInfo *joinrel)
Definition: foreign.c:731
static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra)
static void add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel, Path *epq_path, List *restrictlist)
SpecialJoinInfo * sjinfo
Definition: pathnodes.h:3233
Bitmapset * attrs_used
Definition: postgres_fdw.h:50

References add_path(), add_paths_with_pathkeys_for_rel(), PgFdwRelationInfo::attrs_used, bms_is_empty, clauselist_selectivity(), CMD_DELETE, CMD_UPDATE, cost_qual_eval(), create_foreign_join_path(), DEBUG3, elog, estimate_path_cost_size(), foreign_join_ok(), GetExistingLocalJoinPath(), JOIN_INNER, PgFdwRelationInfo::joinclause_sel, PgFdwRelationInfo::joinclauses, PgFdwRelationInfo::jointype, RelOptInfo::lateral_relids, PgFdwRelationInfo::local_conds, PgFdwRelationInfo::local_conds_cost, PgFdwRelationInfo::local_conds_sel, NIL, palloc0(), pfree(), PgFdwRelationInfo::pushdown_safe, RelOptInfo::reltarget, JoinPathExtraData::restrictlist, root, PgFdwRelationInfo::rows, RelOptInfo::rows, JoinPathExtraData::sjinfo, PgFdwRelationInfo::startup_cost, PgFdwRelationInfo::total_cost, PgFdwRelationInfo::use_remote_estimate, PgFdwRelationInfo::width, and PathTarget::width.

Referenced by postgres_fdw_handler().

◆ postgresGetForeignModifyBatchSize()

static int postgresGetForeignModifyBatchSize ( ResultRelInfo resultRelInfo)
static

Definition at line 2030 of file postgres_fdw.c.

2031 {
2032  int batch_size;
2033  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2034 
2035  /* should be called only once */
2036  Assert(resultRelInfo->ri_BatchSize == 0);
2037 
2038  /*
2039  * Should never get called when the insert is being performed on a table
2040  * that is also among the target relations of an UPDATE operation, because
2041  * postgresBeginForeignInsert() currently rejects such insert attempts.
2042  */
2043  Assert(fmstate == NULL || fmstate->aux_fmstate == NULL);
2044 
2045  /*
2046  * In EXPLAIN without ANALYZE, ri_FdwState is NULL, so we have to lookup
2047  * the option directly in server/table options. Otherwise just use the
2048  * value we determined earlier.
2049  */
2050  if (fmstate)
2051  batch_size = fmstate->batch_size;
2052  else
2053  batch_size = get_batch_size_option(resultRelInfo->ri_RelationDesc);
2054 
2055  /*
2056  * Disable batching when we have to use RETURNING, there are any
2057  * BEFORE/AFTER ROW INSERT triggers on the foreign table, or there are any
2058  * WITH CHECK OPTION constraints from parent views.
2059  *
2060  * When there are any BEFORE ROW INSERT triggers on the table, we can't
2061  * support it, because such triggers might query the table we're inserting
2062  * into and act differently if the tuples that have already been processed
2063  * and prepared for insertion are not there.
2064  */
2065  if (resultRelInfo->ri_projectReturning != NULL ||
2066  resultRelInfo->ri_WithCheckOptions != NIL ||
2067  (resultRelInfo->ri_TrigDesc &&
2068  (resultRelInfo->ri_TrigDesc->trig_insert_before_row ||
2069  resultRelInfo->ri_TrigDesc->trig_insert_after_row)))
2070  return 1;
2071 
2072  /*
2073  * If the foreign table has no columns, disable batching as the INSERT
2074  * syntax doesn't allow batching multiple empty rows into a zero-column
2075  * table in a single statement. This is needed for COPY FROM, in which
2076  * case fmstate must be non-NULL.
2077  */
2078  if (fmstate && list_length(fmstate->target_attrs) == 0)
2079  return 1;
2080 
2081  /*
2082  * Otherwise use the batch size specified for server/table. The number of
2083  * parameters in a batch is limited to 65535 (uint16), so make sure we
2084  * don't exceed this limit by using the maximum batch_size possible.
2085  */
2086  if (fmstate && fmstate->p_nums > 0)
2087  batch_size = Min(batch_size, PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);
2088 
2089  return batch_size;
2090 }
#define PQ_QUERY_PARAM_MAX_LIMIT
Definition: libpq-fe.h:473
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:486
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:539
bool trig_insert_after_row
Definition: reltrigger.h:57
bool trig_insert_before_row
Definition: reltrigger.h:56

References Assert, PgFdwModifyState::aux_fmstate, PgFdwModifyState::batch_size, get_batch_size_option(), list_length(), Min, NIL, PgFdwModifyState::p_nums, PQ_QUERY_PARAM_MAX_LIMIT, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwState, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, PgFdwModifyState::target_attrs, TriggerDesc::trig_insert_after_row, and TriggerDesc::trig_insert_before_row.

Referenced by postgres_fdw_handler().

◆ postgresGetForeignPaths()

static void postgresGetForeignPaths ( PlannerInfo root,
RelOptInfo baserel,
Oid  foreigntableid 
)
static

Definition at line 1011 of file postgres_fdw.c.

1014 {
1015  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) baserel->fdw_private;
1016  ForeignPath *path;
1017  List *ppi_list;
1018  ListCell *lc;
1019 
1020  /*
1021  * Create simplest ForeignScan path node and add it to baserel. This path
1022  * corresponds to SeqScan path of regular tables (though depending on what
1023  * baserestrict conditions we were able to send to remote, there might
1024  * actually be an indexscan happening there). We already did all the work
1025  * to estimate cost and size of this path.
1026  *
1027  * Although this path uses no join clauses, it could still have required
1028  * parameterization due to LATERAL refs in its tlist.
1029  */
1030  path = create_foreignscan_path(root, baserel,
1031  NULL, /* default pathtarget */
1032  fpinfo->rows,
1033  fpinfo->startup_cost,
1034  fpinfo->total_cost,
1035  NIL, /* no pathkeys */
1036  baserel->lateral_relids,
1037  NULL, /* no extra plan */
1038  NIL, /* no fdw_restrictinfo list */
1039  NIL); /* no fdw_private list */
1040  add_path(baserel, (Path *) path);
1041 
1042  /* Add paths with pathkeys */
1043  add_paths_with_pathkeys_for_rel(root, baserel, NULL, NIL);
1044 
1045  /*
1046  * If we're not using remote estimates, stop here. We have no way to
1047  * estimate whether any join clauses would be worth sending across, so
1048  * don't bother building parameterized paths.
1049  */
1050  if (!fpinfo->use_remote_estimate)
1051  return;
1052 
1053  /*
1054  * Thumb through all join clauses for the rel to identify which outer
1055  * relations could supply one or more safe-to-send-to-remote join clauses.
1056  * We'll build a parameterized path for each such outer relation.
1057  *
1058  * It's convenient to manage this by representing each candidate outer
1059  * relation by the ParamPathInfo node for it. We can then use the
1060  * ppi_clauses list in the ParamPathInfo node directly as a list of the
1061  * interesting join clauses for that rel. This takes care of the
1062  * possibility that there are multiple safe join clauses for such a rel,
1063  * and also ensures that we account for unsafe join clauses that we'll
1064  * still have to enforce locally (since the parameterized-path machinery
1065  * insists that we handle all movable clauses).
1066  */
1067  ppi_list = NIL;
1068  foreach(lc, baserel->joininfo)
1069  {
1070  RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
1071  Relids required_outer;
1072  ParamPathInfo *param_info;
1073 
1074  /* Check if clause can be moved to this rel */
1075  if (!join_clause_is_movable_to(rinfo, baserel))
1076  continue;
1077 
1078  /* See if it is safe to send to remote */
1079  if (!is_foreign_expr(root, baserel, rinfo->clause))
1080  continue;
1081 
1082  /* Calculate required outer rels for the resulting path */
1083  required_outer = bms_union(rinfo->clause_relids,
1084  baserel->lateral_relids);
1085  /* We do not want the foreign rel itself listed in required_outer */
1086  required_outer = bms_del_member(required_outer, baserel->relid);
1087 
1088  /*
1089  * required_outer probably can't be empty here, but if it were, we
1090  * couldn't make a parameterized path.
1091  */
1092  if (bms_is_empty(required_outer))
1093  continue;
1094 
1095  /* Get the ParamPathInfo */
1096  param_info = get_baserel_parampathinfo(root, baserel,
1097  required_outer);
1098  Assert(param_info != NULL);
1099 
1100  /*
1101  * Add it to list unless we already have it. Testing pointer equality
1102  * is OK since get_baserel_parampathinfo won't make duplicates.
1103  */
1104  ppi_list = list_append_unique_ptr(ppi_list, param_info);
1105  }
1106 
1107  /*
1108  * The above scan examined only "generic" join clauses, not those that
1109  * were absorbed into EquivalenceClauses. See if we can make anything out
1110  * of EquivalenceClauses.
1111  */
1112  if (baserel->has_eclass_joins)
1113  {
1114  /*
1115  * We repeatedly scan the eclass list looking for column references
1116  * (or expressions) belonging to the foreign rel. Each time we find
1117  * one, we generate a list of equivalence joinclauses for it, and then
1118  * see if any are safe to send to the remote. Repeat till there are
1119  * no more candidate EC members.
1120  */
1122 
1123  arg.already_used = NIL;
1124  for (;;)
1125  {
1126  List *clauses;
1127 
1128  /* Make clauses, skipping any that join to lateral_referencers */
1129  arg.current = NULL;
1131  baserel,
1133  (void *) &arg,
1134  baserel->lateral_referencers);
1135 
1136  /* Done if there are no more expressions in the foreign rel */
1137  if (arg.current == NULL)
1138  {
1139  Assert(clauses == NIL);
1140  break;
1141  }
1142 
1143  /* Scan the extracted join clauses */
1144  foreach(lc, clauses)
1145  {
1146  RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
1147  Relids required_outer;
1148  ParamPathInfo *param_info;
1149 
1150  /* Check if clause can be moved to this rel */
1151  if (!join_clause_is_movable_to(rinfo, baserel))
1152  continue;
1153 
1154  /* See if it is safe to send to remote */
1155  if (!is_foreign_expr(root, baserel, rinfo->clause))
1156  continue;
1157 
1158  /* Calculate required outer rels for the resulting path */
1159  required_outer = bms_union(rinfo->clause_relids,
1160  baserel->lateral_relids);
1161  required_outer = bms_del_member(required_outer, baserel->relid);
1162  if (bms_is_empty(required_outer))
1163  continue;
1164 
1165  /* Get the ParamPathInfo */
1166  param_info = get_baserel_parampathinfo(root, baserel,
1167  required_outer);
1168  Assert(param_info != NULL);
1169 
1170  /* Add it to list unless we already have it */
1171  ppi_list = list_append_unique_ptr(ppi_list, param_info);
1172  }
1173 
1174  /* Try again, now ignoring the expression we found this time */
1175  arg.already_used = lappend(arg.already_used, arg.current);
1176  }
1177  }
1178 
1179  /*
1180  * Now build a path for each useful outer relation.
1181  */
1182  foreach(lc, ppi_list)
1183  {
1184  ParamPathInfo *param_info = (ParamPathInfo *) lfirst(lc);
1185  double rows;
1186  int width;
1187  Cost startup_cost;
1188  Cost total_cost;
1189 
1190  /* Get a cost estimate from the remote */
1191  estimate_path_cost_size(root, baserel,
1192  param_info->ppi_clauses, NIL, NULL,
1193  &rows, &width,
1194  &startup_cost, &total_cost);
1195 
1196  /*
1197  * ppi_rows currently won't get looked at by anything, but still we
1198  * may as well ensure that it matches our idea of the rowcount.
1199  */
1200  param_info->ppi_rows = rows;
1201 
1202  /* Make the path */
1203  path = create_foreignscan_path(root, baserel,
1204  NULL, /* default pathtarget */
1205  rows,
1206  startup_cost,
1207  total_cost,
1208  NIL, /* no pathkeys */
1209  param_info->ppi_req_outer,
1210  NULL,
1211  NIL, /* no fdw_restrictinfo list */
1212  NIL); /* no fdw_private list */
1213  add_path(baserel, (Path *) path);
1214  }
1215 }
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:868
List * generate_implied_equalities_for_column(PlannerInfo *root, RelOptInfo *rel, ec_matches_callback_type callback, void *callback_arg, Relids prohibited_rels)
Definition: equivclass.c:2960
static bool ec_member_matches_foreign(PlannerInfo *root, RelOptInfo *rel, EquivalenceClass *ec, EquivalenceMember *em, void *arg)
ParamPathInfo * get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel, Relids required_outer)
Definition: relnode.c:1557
bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel)
Definition: restrictinfo.c:584
Cardinality ppi_rows
Definition: pathnodes.h:1580
List * ppi_clauses
Definition: pathnodes.h:1581
Relids ppi_req_outer
Definition: pathnodes.h:1579
Relids lateral_referencers
Definition: pathnodes.h:936

References add_path(), add_paths_with_pathkeys_for_rel(), arg, Assert, bms_del_member(), bms_is_empty, bms_union(), RestrictInfo::clause, create_foreignscan_path(), ec_member_matches_foreign(), estimate_path_cost_size(), generate_implied_equalities_for_column(), get_baserel_parampathinfo(), RelOptInfo::has_eclass_joins, is_foreign_expr(), join_clause_is_movable_to(), RelOptInfo::joininfo, lappend(), RelOptInfo::lateral_referencers, RelOptInfo::lateral_relids, lfirst, list_append_unique_ptr(), NIL, ParamPathInfo::ppi_clauses, ParamPathInfo::ppi_req_outer, ParamPathInfo::ppi_rows, RelOptInfo::relid, root, PgFdwRelationInfo::rows, PgFdwRelationInfo::startup_cost, PgFdwRelationInfo::total_cost, and PgFdwRelationInfo::use_remote_estimate.

Referenced by postgres_fdw_handler().

◆ postgresGetForeignPlan()

static ForeignScan * postgresGetForeignPlan ( PlannerInfo root,
RelOptInfo foreignrel,
Oid  foreigntableid,
ForeignPath best_path,
List tlist,
List scan_clauses,
Plan outer_plan 
)
static

Definition at line 1222 of file postgres_fdw.c.

1229 {
1230  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
1231  Index scan_relid;
1232  List *fdw_private;
1233  List *remote_exprs = NIL;
1234  List *local_exprs = NIL;
1235  List *params_list = NIL;
1236  List *fdw_scan_tlist = NIL;
1237  List *fdw_recheck_quals = NIL;
1238  List *retrieved_attrs;
1239  StringInfoData sql;
1240  bool has_final_sort = false;
1241  bool has_limit = false;
1242  ListCell *lc;
1243 
1244  /*
1245  * Get FDW private data created by postgresGetForeignUpperPaths(), if any.
1246  */
1247  if (best_path->fdw_private)
1248  {
1249  has_final_sort = boolVal(list_nth(best_path->fdw_private,
1251  has_limit = boolVal(list_nth(best_path->fdw_private,
1253  }
1254 
1255  if (IS_SIMPLE_REL(foreignrel))
1256  {
1257  /*
1258  * For base relations, set scan_relid as the relid of the relation.
1259  */
1260  scan_relid = foreignrel->relid;
1261 
1262  /*
1263  * In a base-relation scan, we must apply the given scan_clauses.
1264  *
1265  * Separate the scan_clauses into those that can be executed remotely
1266  * and those that can't. baserestrictinfo clauses that were
1267  * previously determined to be safe or unsafe by classifyConditions
1268  * are found in fpinfo->remote_conds and fpinfo->local_conds. Anything
1269  * else in the scan_clauses list will be a join clause, which we have
1270  * to check for remote-safety.
1271  *
1272  * Note: the join clauses we see here should be the exact same ones
1273  * previously examined by postgresGetForeignPaths. Possibly it'd be
1274  * worth passing forward the classification work done then, rather
1275  * than repeating it here.
1276  *
1277  * This code must match "extract_actual_clauses(scan_clauses, false)"
1278  * except for the additional decision about remote versus local
1279  * execution.
1280  */
1281  foreach(lc, scan_clauses)
1282  {
1283  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
1284 
1285  /* Ignore any pseudoconstants, they're dealt with elsewhere */
1286  if (rinfo->pseudoconstant)
1287  continue;
1288 
1289  if (list_member_ptr(fpinfo->remote_conds, rinfo))
1290  remote_exprs = lappend(remote_exprs, rinfo->clause);
1291  else if (list_member_ptr(fpinfo->local_conds, rinfo))
1292  local_exprs = lappend(local_exprs, rinfo->clause);
1293  else if (is_foreign_expr(root, foreignrel, rinfo->clause))
1294  remote_exprs = lappend(remote_exprs, rinfo->clause);
1295  else
1296  local_exprs = lappend(local_exprs, rinfo->clause);
1297  }
1298 
1299  /*
1300  * For a base-relation scan, we have to support EPQ recheck, which
1301  * should recheck all the remote quals.
1302  */
1303  fdw_recheck_quals = remote_exprs;
1304  }
1305  else
1306  {
1307  /*
1308  * Join relation or upper relation - set scan_relid to 0.
1309  */
1310  scan_relid = 0;
1311 
1312  /*
1313  * For a join rel, baserestrictinfo is NIL and we are not considering
1314  * parameterization right now, so there should be no scan_clauses for
1315  * a joinrel or an upper rel either.
1316  */
1317  Assert(!scan_clauses);
1318 
1319  /*
1320  * Instead we get the conditions to apply from the fdw_private
1321  * structure.
1322  */
1323  remote_exprs = extract_actual_clauses(fpinfo->remote_conds, false);
1324  local_exprs = extract_actual_clauses(fpinfo->local_conds, false);
1325 
1326  /*
1327  * We leave fdw_recheck_quals empty in this case, since we never need
1328  * to apply EPQ recheck clauses. In the case of a joinrel, EPQ
1329  * recheck is handled elsewhere --- see postgresGetForeignJoinPaths().
1330  * If we're planning an upperrel (ie, remote grouping or aggregation)
1331  * then there's no EPQ to do because SELECT FOR UPDATE wouldn't be
1332  * allowed, and indeed we *can't* put the remote clauses into
1333  * fdw_recheck_quals because the unaggregated Vars won't be available
1334  * locally.
1335  */
1336 
1337  /* Build the list of columns to be fetched from the foreign server. */
1338  fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
1339 
1340  /*
1341  * Ensure that the outer plan produces a tuple whose descriptor
1342  * matches our scan tuple slot. Also, remove the local conditions
1343  * from outer plan's quals, lest they be evaluated twice, once by the
1344  * local plan and once by the scan.
1345  */
1346  if (outer_plan)
1347  {
1348  /*
1349  * Right now, we only consider grouping and aggregation beyond
1350  * joins. Queries involving aggregates or grouping do not require
1351  * EPQ mechanism, hence should not have an outer plan here.
1352  */
1353  Assert(!IS_UPPER_REL(foreignrel));
1354 
1355  /*
1356  * First, update the plan's qual list if possible. In some cases
1357  * the quals might be enforced below the topmost plan level, in
1358  * which case we'll fail to remove them; it's not worth working
1359  * harder than this.
1360  */
1361  foreach(lc, local_exprs)
1362  {
1363  Node *qual = lfirst(lc);
1364 
1365  outer_plan->qual = list_delete(outer_plan->qual, qual);
1366 
1367  /*
1368  * For an inner join the local conditions of foreign scan plan
1369  * can be part of the joinquals as well. (They might also be
1370  * in the mergequals or hashquals, but we can't touch those
1371  * without breaking the plan.)
1372  */
1373  if (IsA(outer_plan, NestLoop) ||
1374  IsA(outer_plan, MergeJoin) ||
1375  IsA(outer_plan, HashJoin))
1376  {
1377  Join *join_plan = (Join *) outer_plan;
1378 
1379  if (join_plan->jointype == JOIN_INNER)
1380  join_plan->joinqual = list_delete(join_plan->joinqual,
1381  qual);
1382  }
1383  }
1384 
1385  /*
1386  * Now fix the subplan's tlist --- this might result in inserting
1387  * a Result node atop the plan tree.
1388  */
1389  outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist,
1390  best_path->path.parallel_safe);
1391  }
1392  }
1393 
1394  /*
1395  * Build the query string to be sent for execution, and identify
1396  * expressions to be sent as parameters.
1397  */
1398  initStringInfo(&sql);
1399  deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
1400  remote_exprs, best_path->path.pathkeys,
1401  has_final_sort, has_limit, false,
1402  &retrieved_attrs, &params_list);
1403 
1404  /* Remember remote_exprs for possible use by postgresPlanDirectModify */
1405  fpinfo->final_remote_exprs = remote_exprs;
1406 
1407  /*
1408  * Build the fdw_private list that will be available to the executor.
1409  * Items in the list must match order in enum FdwScanPrivateIndex.
1410  */
1411  fdw_private = list_make3(makeString(sql.data),
1412  retrieved_attrs,
1413  makeInteger(fpinfo->fetch_size));
1414  if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
1415  fdw_private = lappend(fdw_private,
1416  makeString(fpinfo->relation_name));
1417 
1418  /*
1419  * Create the ForeignScan node for the given relation.
1420  *
1421  * Note that the remote parameter expressions are stored in the fdw_exprs
1422  * field of the finished plan node; we can't keep them in private state
1423  * because then they wouldn't be subject to later planner processing.
1424  */
1425  return make_foreignscan(tlist,
1426  local_exprs,
1427  scan_relid,
1428  params_list,
1429  fdw_private,
1430  fdw_scan_tlist,
1431  fdw_recheck_quals,
1432  outer_plan);
1433 }
ForeignScan * make_foreignscan(List *qptlist, List *qpqual, Index scanrelid, List *fdw_exprs, List *fdw_private, List *fdw_scan_tlist, List *fdw_recheck_quals, Plan *outer_plan)
Definition: createplan.c:5817
Plan * change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe)
Definition: createplan.c:2147
List * list_delete(List *list, void *datum)
Definition: list.c:853
bool list_member_ptr(const List *list, const void *datum)
Definition: list.c:682
#define list_make3(x1, x2, x3)
Definition: pg_list.h:216
List * extract_actual_clauses(List *restrictinfo_list, bool pseudoconstant)
Definition: restrictinfo.c:494
List * fdw_private
Definition: pathnodes.h:1872
List * joinqual
Definition: plannodes.h:793
JoinType jointype
Definition: plannodes.h:791
bool parallel_safe
Definition: pathnodes.h:1655
List * final_remote_exprs
Definition: postgres_fdw.h:47
List * qual
Definition: plannodes.h:153
String * makeString(char *str)
Definition: value.c:63
Integer * makeInteger(int i)
Definition: value.c:23

References Assert, boolVal, build_tlist_to_deparse(), change_plan_targetlist(), RestrictInfo::clause, deparseSelectStmtForRel(), extract_actual_clauses(), ForeignPath::fdw_private, FdwPathPrivateHasFinalSort, FdwPathPrivateHasLimit, PgFdwRelationInfo::fetch_size, PgFdwRelationInfo::final_remote_exprs, if(), initStringInfo(), is_foreign_expr(), IS_JOIN_REL, IS_SIMPLE_REL, IS_UPPER_REL, IsA, JOIN_INNER, Join::joinqual, Join::jointype, lappend(), lfirst, lfirst_node, list_delete(), list_make3, list_member_ptr(), list_nth(), PgFdwRelationInfo::local_conds, make_foreignscan(), makeInteger(), makeString(), NIL, Path::parallel_safe, ForeignPath::path, Path::pathkeys, Plan::qual, PgFdwRelationInfo::relation_name, RelOptInfo::relid, PgFdwRelationInfo::remote_conds, and root.

Referenced by postgres_fdw_handler().

◆ postgresGetForeignRelSize()

static void postgresGetForeignRelSize ( PlannerInfo root,
RelOptInfo baserel,
Oid  foreigntableid 
)
static

Definition at line 622 of file postgres_fdw.c.

625 {
626  PgFdwRelationInfo *fpinfo;
627  ListCell *lc;
628 
629  /*
630  * We use PgFdwRelationInfo to pass various information to subsequent
631  * functions.
632  */
633  fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo));
634  baserel->fdw_private = (void *) fpinfo;
635 
636  /* Base foreign tables need to be pushed down always. */
637  fpinfo->pushdown_safe = true;
638 
639  /* Look up foreign-table catalog info. */
640  fpinfo->table = GetForeignTable(foreigntableid);
641  fpinfo->server = GetForeignServer(fpinfo->table->serverid);
642 
643  /*
644  * Extract user-settable option values. Note that per-table settings of
645  * use_remote_estimate, fetch_size and async_capable override per-server
646  * settings of them, respectively.
647  */
648  fpinfo->use_remote_estimate = false;
651  fpinfo->shippable_extensions = NIL;
652  fpinfo->fetch_size = 100;
653  fpinfo->async_capable = false;
654 
655  apply_server_options(fpinfo);
656  apply_table_options(fpinfo);
657 
658  /*
659  * If the table or the server is configured to use remote estimates,
660  * identify which user to do remote access as during planning. This
661  * should match what ExecCheckPermissions() does. If we fail due to lack
662  * of permissions, the query would have failed at runtime anyway.
663  */
664  if (fpinfo->use_remote_estimate)
665  {
666  Oid userid;
667 
668  userid = OidIsValid(baserel->userid) ? baserel->userid : GetUserId();
669  fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid);
670  }
671  else
672  fpinfo->user = NULL;
673 
674  /*
675  * Identify which baserestrictinfo clauses can be sent to the remote
676  * server and which can't.
677  */
678  classifyConditions(root, baserel, baserel->baserestrictinfo,
679  &fpinfo->remote_conds, &fpinfo->local_conds);
680 
681  /*
682  * Identify which attributes will need to be retrieved from the remote
683  * server. These include all attrs needed for joins or final output, plus
684  * all attrs used in the local_conds. (Note: if we end up using a
685  * parameterized scan, it's possible that some of the join clauses will be
686  * sent to the remote and thus we wouldn't really need to retrieve the
687  * columns used in them. Doesn't seem worth detecting that case though.)
688  */
689  fpinfo->attrs_used = NULL;
690  pull_varattnos((Node *) baserel->reltarget->exprs, baserel->relid,
691  &fpinfo->attrs_used);
692  foreach(lc, fpinfo->local_conds)
693  {
694  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
695 
696  pull_varattnos((Node *) rinfo->clause, baserel->relid,
697  &fpinfo->attrs_used);
698  }
699 
700  /*
701  * Compute the selectivity and cost of the local_conds, so we don't have
702  * to do it over again for each path. The best we can do for these
703  * conditions is to estimate selectivity on the basis of local statistics.
704  */
706  fpinfo->local_conds,
707  baserel->relid,
708  JOIN_INNER,
709  NULL);
710 
711  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
712 
713  /*
714  * Set # of retrieved rows and cached relation costs to some negative
715  * value, so that we can detect when they are set to some sensible values,
716  * during one (usually the first) of the calls to estimate_path_cost_size.
717  */
718  fpinfo->retrieved_rows = -1;
719  fpinfo->rel_startup_cost = -1;
720  fpinfo->rel_total_cost = -1;
721 
722  /*
723  * If the table or the server is configured to use remote estimates,
724  * connect to the foreign server and execute EXPLAIN to estimate the
725  * number of rows selected by the restriction clauses, as well as the
726  * average row width. Otherwise, estimate using whatever statistics we
727  * have locally, in a way similar to ordinary tables.
728  */
729  if (fpinfo->use_remote_estimate)
730  {
731  /*
732  * Get cost/size estimates with help of remote server. Save the
733  * values in fpinfo so we don't need to do it again to generate the
734  * basic foreign path.
735  */
736  estimate_path_cost_size(root, baserel, NIL, NIL, NULL,
737  &fpinfo->rows, &fpinfo->width,
738  &fpinfo->startup_cost, &fpinfo->total_cost);
739 
740  /* Report estimated baserel size to planner. */
741  baserel->rows = fpinfo->rows;
742  baserel->reltarget->width = fpinfo->width;
743  }
744  else
745  {
746  /*
747  * If the foreign table has never been ANALYZEd, it will have
748  * reltuples < 0, meaning "unknown". We can't do much if we're not
749  * allowed to consult the remote server, but we can use a hack similar
750  * to plancat.c's treatment of empty relations: use a minimum size
751  * estimate of 10 pages, and divide by the column-datatype-based width
752  * estimate to get the corresponding number of tuples.
753  */
754  if (baserel->tuples < 0)
755  {
756  baserel->pages = 10;
757  baserel->tuples =
758  (10 * BLCKSZ) / (baserel->reltarget->width +
760  }
761 
762  /* Estimate baserel size as best we can with local statistics. */
764 
765  /* Fill in basically-bogus cost estimates for use later. */
766  estimate_path_cost_size(root, baserel, NIL, NIL, NULL,
767  &fpinfo->rows, &fpinfo->width,
768  &fpinfo->startup_cost, &fpinfo->total_cost);
769  }
770 
771  /*
772  * fpinfo->relation_name gets the numeric rangetable index of the foreign
773  * table RTE. (If this query gets EXPLAIN'd, we'll convert that to a
774  * human-readable string at that time.)
775  */
776  fpinfo->relation_name = psprintf("%u", baserel->relid);
777 
778  /* No outer and inner relations. */
779  fpinfo->make_outerrel_subquery = false;
780  fpinfo->make_innerrel_subquery = false;
781  fpinfo->lower_subquery_rels = NULL;
782  fpinfo->hidden_subquery_rels = NULL;
783  /* Set the relation index. */
784  fpinfo->relation_index = baserel->relid;
785 }
#define MAXALIGN(LEN)
Definition: c.h:811
void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition: costsize.c:5213
#define SizeofHeapTupleHeader
Definition: htup_details.h:185
static void apply_server_options(PgFdwRelationInfo *fpinfo)
#define DEFAULT_FDW_TUPLE_COST
Definition: postgres_fdw.c:60
static void apply_table_options(PgFdwRelationInfo *fpinfo)
#define DEFAULT_FDW_STARTUP_COST
Definition: postgres_fdw.c:57
List * baserestrictinfo
Definition: pathnodes.h:979
Oid userid
Definition: pathnodes.h:960
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:291

References apply_server_options(), apply_table_options(), PgFdwRelationInfo::async_capable, PgFdwRelationInfo::attrs_used, RelOptInfo::baserestrictinfo, classifyConditions(), RestrictInfo::clause, clauselist_selectivity(), cost_qual_eval(), DEFAULT_FDW_STARTUP_COST, DEFAULT_FDW_TUPLE_COST, estimate_path_cost_size(), PathTarget::exprs, PgFdwRelationInfo::fdw_startup_cost, PgFdwRelationInfo::fdw_tuple_cost, PgFdwRelationInfo::fetch_size, GetForeignServer(), GetForeignTable(), GetUserId(), GetUserMapping(), PgFdwRelationInfo::hidden_subquery_rels, JOIN_INNER, lfirst_node, PgFdwRelationInfo::local_conds, PgFdwRelationInfo::local_conds_cost, PgFdwRelationInfo::local_conds_sel, PgFdwRelationInfo::lower_subquery_rels, PgFdwRelationInfo::make_innerrel_subquery, PgFdwRelationInfo::make_outerrel_subquery, MAXALIGN, NIL, OidIsValid, RelOptInfo::pages, palloc0(), psprintf(), pull_varattnos(), PgFdwRelationInfo::pushdown_safe, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_index, PgFdwRelationInfo::relation_name, RelOptInfo::relid, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, PgFdwRelationInfo::retrieved_rows, root, PgFdwRelationInfo::rows, RelOptInfo::rows, PgFdwRelationInfo::server, ForeignServer::serverid, ForeignTable::serverid, set_baserel_size_estimates(), PgFdwRelationInfo::shippable_extensions, SizeofHeapTupleHeader, PgFdwRelationInfo::startup_cost, PgFdwRelationInfo::table, PgFdwRelationInfo::total_cost, RelOptInfo::tuples, PgFdwRelationInfo::use_remote_estimate, PgFdwRelationInfo::user, RelOptInfo::userid, PgFdwRelationInfo::width, and PathTarget::width.

Referenced by postgres_fdw_handler().

◆ postgresGetForeignUpperPaths()

static void postgresGetForeignUpperPaths ( PlannerInfo root,
UpperRelationKind  stage,
RelOptInfo input_rel,
RelOptInfo output_rel,
void *  extra 
)
static

Definition at line 6708 of file postgres_fdw.c.

6711 {
6712  PgFdwRelationInfo *fpinfo;
6713 
6714  /*
6715  * If input rel is not safe to pushdown, then simply return as we cannot
6716  * perform any post-join operations on the foreign server.
6717  */
6718  if (!input_rel->fdw_private ||
6719  !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
6720  return;
6721 
6722  /* Ignore stages we don't support; and skip any duplicate calls. */
6723  if ((stage != UPPERREL_GROUP_AGG &&
6724  stage != UPPERREL_ORDERED &&
6725  stage != UPPERREL_FINAL) ||
6726  output_rel->fdw_private)
6727  return;
6728 
6729  fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo));
6730  fpinfo->pushdown_safe = false;
6731  fpinfo->stage = stage;
6732  output_rel->fdw_private = fpinfo;
6733 
6734  switch (stage)
6735  {
6736  case UPPERREL_GROUP_AGG:
6737  add_foreign_grouping_paths(root, input_rel, output_rel,
6738  (GroupPathExtraData *) extra);
6739  break;
6740  case UPPERREL_ORDERED:
6741  add_foreign_ordered_paths(root, input_rel, output_rel);
6742  break;
6743  case UPPERREL_FINAL:
6744  add_foreign_final_paths(root, input_rel, output_rel,
6745  (FinalPathExtraData *) extra);
6746  break;
6747  default:
6748  elog(ERROR, "unexpected upper relation: %d", (int) stage);
6749  break;
6750  }
6751 }
static void add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra)
static void add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel)
static void add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *final_rel, FinalPathExtraData *extra)

References add_foreign_final_paths(), add_foreign_grouping_paths(), add_foreign_ordered_paths(), elog, ERROR, palloc0(), PgFdwRelationInfo::pushdown_safe, root, PgFdwRelationInfo::stage, UPPERREL_FINAL, UPPERREL_GROUP_AGG, and UPPERREL_ORDERED.

Referenced by postgres_fdw_handler().

◆ postgresImportForeignSchema()

static List * postgresImportForeignSchema ( ImportForeignSchemaStmt stmt,
Oid  serverOid 
)
static

Definition at line 5445 of file postgres_fdw.c.

5446 {
5447  List *commands = NIL;
5448  bool import_collate = true;
5449  bool import_default = false;
5450  bool import_generated = true;
5451  bool import_not_null = true;
5452  ForeignServer *server;
5453  UserMapping *mapping;
5454  PGconn *conn;
5456  PGresult *volatile res = NULL;
5457  int numrows,
5458  i;
5459  ListCell *lc;
5460 
5461  /* Parse statement options */
5462  foreach(lc, stmt->options)
5463  {
5464  DefElem *def = (DefElem *) lfirst(lc);
5465 
5466  if (strcmp(def->defname, "import_collate") == 0)
5467  import_collate = defGetBoolean(def);
5468  else if (strcmp(def->defname, "import_default") == 0)
5469  import_default = defGetBoolean(def);
5470  else if (strcmp(def->defname, "import_generated") == 0)
5471  import_generated = defGetBoolean(def);
5472  else if (strcmp(def->defname, "import_not_null") == 0)
5473  import_not_null = defGetBoolean(def);
5474  else
5475  ereport(ERROR,
5476  (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
5477  errmsg("invalid option \"%s\"", def->defname)));
5478  }
5479 
5480  /*
5481  * Get connection to the foreign server. Connection manager will
5482  * establish new connection if necessary.
5483  */
5484  server = GetForeignServer(serverOid);
5485  mapping = GetUserMapping(GetUserId(), server->serverid);
5486  conn = GetConnection(mapping, false, NULL);
5487 
5488  /* Don't attempt to import collation if remote server hasn't got it */
5489  if (PQserverVersion(conn) < 90100)
5490  import_collate = false;
5491 
5492  /* Create workspace for strings */
5493  initStringInfo(&buf);
5494 
5495  /* In what follows, do not risk leaking any PGresults. */
5496  PG_TRY();
5497  {
5498  /* Check that the schema really exists */
5499  appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
5500  deparseStringLiteral(&buf, stmt->remote_schema);
5501 
5502  res = pgfdw_exec_query(conn, buf.data, NULL);
5504  pgfdw_report_error(ERROR, res, conn, false, buf.data);
5505 
5506  if (PQntuples(res) != 1)
5507  ereport(ERROR,
5508  (errcode(ERRCODE_FDW_SCHEMA_NOT_FOUND),
5509  errmsg("schema \"%s\" is not present on foreign server \"%s\"",
5510  stmt->remote_schema, server->servername)));
5511 
5512  PQclear(res);
5513  res = NULL;
5514  resetStringInfo(&buf);
5515 
5516  /*
5517  * Fetch all table data from this schema, possibly restricted by
5518  * EXCEPT or LIMIT TO. (We don't actually need to pay any attention
5519  * to EXCEPT/LIMIT TO here, because the core code will filter the
5520  * statements we return according to those lists anyway. But it
5521  * should save a few cycles to not process excluded tables in the
5522  * first place.)
5523  *
5524  * Import table data for partitions only when they are explicitly
5525  * specified in LIMIT TO clause. Otherwise ignore them and only
5526  * include the definitions of the root partitioned tables to allow
5527  * access to the complete remote data set locally in the schema
5528  * imported.
5529  *
5530  * Note: because we run the connection with search_path restricted to
5531  * pg_catalog, the format_type() and pg_get_expr() outputs will always
5532  * include a schema name for types/functions in other schemas, which
5533  * is what we want.
5534  */
5536  "SELECT relname, "
5537  " attname, "
5538  " format_type(atttypid, atttypmod), "
5539  " attnotnull, "
5540  " pg_get_expr(adbin, adrelid), ");
5541 
5542  /* Generated columns are supported since Postgres 12 */
5543  if (PQserverVersion(conn) >= 120000)
5545  " attgenerated, ");
5546  else
5548  " NULL, ");
5549 
5550  if (import_collate)
5552  " collname, "
5553  " collnsp.nspname ");
5554  else
5556  " NULL, NULL ");
5557 
5559  "FROM pg_class c "
5560  " JOIN pg_namespace n ON "
5561  " relnamespace = n.oid "
5562  " LEFT JOIN pg_attribute a ON "
5563  " attrelid = c.oid AND attnum > 0 "
5564  " AND NOT attisdropped "
5565  " LEFT JOIN pg_attrdef ad ON "
5566  " adrelid = c.oid AND adnum = attnum ");
5567 
5568  if (import_collate)
5570  " LEFT JOIN pg_collation coll ON "
5571  " coll.oid = attcollation "
5572  " LEFT JOIN pg_namespace collnsp ON "
5573  " collnsp.oid = collnamespace ");
5574 
5576  "WHERE c.relkind IN ("
5577  CppAsString2(RELKIND_RELATION) ","
5578  CppAsString2(RELKIND_VIEW) ","
5579  CppAsString2(RELKIND_FOREIGN_TABLE) ","
5580  CppAsString2(RELKIND_MATVIEW) ","
5581  CppAsString2(RELKIND_PARTITIONED_TABLE) ") "
5582  " AND n.nspname = ");
5583  deparseStringLiteral(&buf, stmt->remote_schema);
5584 
5585  /* Partitions are supported since Postgres 10 */
5586  if (PQserverVersion(conn) >= 100000 &&
5587  stmt->list_type != FDW_IMPORT_SCHEMA_LIMIT_TO)
5588  appendStringInfoString(&buf, " AND NOT c.relispartition ");
5589 
5590  /* Apply restrictions for LIMIT TO and EXCEPT */
5591  if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
5592  stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
5593  {
5594  bool first_item = true;
5595 
5596  appendStringInfoString(&buf, " AND c.relname ");
5597  if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
5598  appendStringInfoString(&buf, "NOT ");
5599  appendStringInfoString(&buf, "IN (");
5600 
5601  /* Append list of table names within IN clause */
5602  foreach(lc, stmt->table_list)
5603  {
5604  RangeVar *rv = (RangeVar *) lfirst(lc);
5605 
5606  if (first_item)
5607  first_item = false;
5608  else
5609  appendStringInfoString(&buf, ", ");
5611  }
5612  appendStringInfoChar(&buf, ')');
5613  }
5614 
5615  /* Append ORDER BY at the end of query to ensure output ordering */
5616  appendStringInfoString(&buf, " ORDER BY c.relname, a.attnum");
5617 
5618  /* Fetch the data */
5619  res = pgfdw_exec_query(conn, buf.data, NULL);
5621  pgfdw_report_error(ERROR, res, conn, false, buf.data);
5622 
5623  /* Process results */
5624  numrows = PQntuples(res);
5625  /* note: incrementation of i happens in inner loop's while() test */
5626  for (i = 0; i < numrows;)
5627  {
5628  char *tablename = PQgetvalue(res, i, 0);
5629  bool first_item = true;
5630 
5631  resetStringInfo(&buf);
5632  appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
5633  quote_identifier(tablename));
5634 
5635  /* Scan all rows for this table */
5636  do
5637  {
5638  char *attname;
5639  char *typename;
5640  char *attnotnull;
5641  char *attgenerated;
5642  char *attdefault;
5643  char *collname;
5644  char *collnamespace;
5645 
5646  /* If table has no columns, we'll see nulls here */
5647  if (PQgetisnull(res, i, 1))
5648  continue;
5649 
5650  attname = PQgetvalue(res, i, 1);
5651  typename = PQgetvalue(res, i, 2);
5652  attnotnull = PQgetvalue(res, i, 3);
5653  attdefault = PQgetisnull(res, i, 4) ? (char *) NULL :
5654  PQgetvalue(res, i, 4);
5655  attgenerated = PQgetisnull(res, i, 5) ? (char *) NULL :
5656  PQgetvalue(res, i, 5);
5657  collname = PQgetisnull(res, i, 6) ? (char *) NULL :
5658  PQgetvalue(res, i, 6);
5659  collnamespace = PQgetisnull(res, i, 7) ? (char *) NULL :
5660  PQgetvalue(res, i, 7);
5661 
5662  if (first_item)
5663  first_item = false;
5664  else
5665  appendStringInfoString(&buf, ",\n");
5666 
5667  /* Print column name and type */
5668  appendStringInfo(&buf, " %s %s",
5670  typename);
5671 
5672  /*
5673  * Add column_name option so that renaming the foreign table's
5674  * column doesn't break the association to the underlying
5675  * column.
5676  */
5677  appendStringInfoString(&buf, " OPTIONS (column_name ");
5679  appendStringInfoChar(&buf, ')');
5680 
5681  /* Add COLLATE if needed */
5682  if (import_collate && collname != NULL && collnamespace != NULL)
5683  appendStringInfo(&buf, " COLLATE %s.%s",
5684  quote_identifier(collnamespace),
5685  quote_identifier(collname));
5686 
5687  /* Add DEFAULT if needed */
5688  if (import_default && attdefault != NULL &&
5689  (!attgenerated || !attgenerated[0]))
5690  appendStringInfo(&buf, " DEFAULT %s", attdefault);
5691 
5692  /* Add GENERATED if needed */
5693  if (import_generated && attgenerated != NULL &&
5694  attgenerated[0] == ATTRIBUTE_GENERATED_STORED)
5695  {
5696  Assert(attdefault != NULL);
5698  " GENERATED ALWAYS AS (%s) STORED",
5699  attdefault);
5700  }
5701 
5702  /* Add NOT NULL if needed */
5703  if (import_not_null && attnotnull[0] == 't')
5704  appendStringInfoString(&buf, " NOT NULL");
5705  }
5706  while (++i < numrows &&
5707  strcmp(PQgetvalue(res, i, 0), tablename) == 0);
5708 
5709  /*
5710  * Add server name and table-level options. We specify remote
5711  * schema and table name as options (the latter to ensure that
5712  * renaming the foreign table doesn't break the association).
5713  */
5714  appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
5715  quote_identifier(server->servername));
5716 
5717  appendStringInfoString(&buf, "schema_name ");
5718  deparseStringLiteral(&buf, stmt->remote_schema);
5719  appendStringInfoString(&buf, ", table_name ");
5720  deparseStringLiteral(&buf, tablename);
5721 
5722  appendStringInfoString(&buf, ");");
5723 
5724  commands = lappend(commands, pstrdup(buf.data));
5725  }
5726  }
5727  PG_FINALLY();
5728  {
5729  PQclear(res);
5730  }
5731  PG_END_TRY();
5732 
5734 
5735  return commands;
5736 }
#define CppAsString2(x)
Definition: c.h:327
void deparseStringLiteral(StringInfo buf, const char *val)
Definition: deparse.c:2849
#define stmt
Definition: indent_codes.h:59
@ FDW_IMPORT_SCHEMA_LIMIT_TO
Definition: parsenodes.h:2945
@ FDW_IMPORT_SCHEMA_EXCEPT
Definition: parsenodes.h:2946
bool attnotnull
Definition: pg_attribute.h:130
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:78
char * servername
Definition: foreign.h:39
char * relname
Definition: primnodes.h:82

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attname, attnotnull, buf, conn, CppAsString2, defGetBoolean(), DefElem::defname, deparseStringLiteral(), ereport, errcode(), errmsg(), ERROR, FDW_IMPORT_SCHEMA_EXCEPT, FDW_IMPORT_SCHEMA_LIMIT_TO, GetConnection(), GetForeignServer(), GetUserId(), GetUserMapping(), i, initStringInfo(), lappend(), lfirst, NIL, PG_END_TRY, PG_FINALLY, PG_TRY, pgfdw_exec_query(), pgfdw_report_error(), PGRES_TUPLES_OK, PQclear(), PQgetisnull(), PQgetvalue(), PQntuples(), PQresultStatus(), PQserverVersion(), pstrdup(), quote_identifier(), ReleaseConnection(), RangeVar::relname, res, resetStringInfo(), ForeignServer::serverid, ForeignServer::servername, and stmt.

Referenced by postgres_fdw_handler().

◆ postgresIsForeignPathAsyncCapable()

static bool postgresIsForeignPathAsyncCapable ( ForeignPath path)
static

Definition at line 7237 of file postgres_fdw.c.

7238 {
7239  RelOptInfo *rel = ((Path *) path)->parent;
7240  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7241 
7242  return fpinfo->async_capable;
7243 }

References PgFdwRelationInfo::async_capable.

Referenced by postgres_fdw_handler().

◆ postgresIsForeignRelUpdatable()

static int postgresIsForeignRelUpdatable ( Relation  rel)
static

Definition at line 2308 of file postgres_fdw.c.

2309 {
2310  bool updatable;
2311  ForeignTable *table;
2312  ForeignServer *server;
2313  ListCell *lc;
2314 
2315  /*
2316  * By default, all postgres_fdw foreign tables are assumed updatable. This
2317  * can be overridden by a per-server setting, which in turn can be
2318  * overridden by a per-table setting.
2319  */
2320  updatable = true;
2321 
2322  table = GetForeignTable(RelationGetRelid(rel));
2323  server = GetForeignServer(table->serverid);
2324 
2325  foreach(lc, server->options)
2326  {
2327  DefElem *def = (DefElem *) lfirst(lc);
2328 
2329  if (strcmp(def->defname, "updatable") == 0)
2330  updatable = defGetBoolean(def);
2331  }
2332  foreach(lc, table->options)
2333  {
2334  DefElem *def = (DefElem *) lfirst(lc);
2335 
2336  if (strcmp(def->defname, "updatable") == 0)
2337  updatable = defGetBoolean(def);
2338  }
2339 
2340  /*
2341  * Currently "updatable" means support for INSERT, UPDATE and DELETE.
2342  */
2343  return updatable ?
2344  (1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE) : 0;
2345 }

References CMD_DELETE, CMD_INSERT, CMD_UPDATE, defGetBoolean(), DefElem::defname, GetForeignServer(), GetForeignTable(), lfirst, ForeignServer::options, ForeignTable::options, RelationGetRelid, and ForeignTable::serverid.

Referenced by postgres_fdw_handler().

◆ postgresIterateDirectModify()

static TupleTableSlot * postgresIterateDirectModify ( ForeignScanState node)
static

Definition at line 2759 of file postgres_fdw.c.

2760 {
2762  EState *estate = node->ss.ps.state;
2763  ResultRelInfo *resultRelInfo = node->resultRelInfo;
2764 
2765  /*
2766  * If this is the first call after Begin, execute the statement.
2767  */
2768  if (dmstate->num_tuples == -1)
2769  execute_dml_stmt(node);
2770 
2771  /*
2772  * If the local query doesn't specify RETURNING, just clear tuple slot.
2773  */
2774  if (!resultRelInfo->ri_projectReturning)
2775  {
2776  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
2777  Instrumentation *instr = node->ss.ps.instrument;
2778 
2779  Assert(!dmstate->has_returning);
2780 
2781  /* Increment the command es_processed count if necessary. */
2782  if (dmstate->set_processed)
2783  estate->es_processed += dmstate->num_tuples;
2784 
2785  /* Increment the tuple count for EXPLAIN ANALYZE if necessary. */
2786  if (instr)
2787  instr->tuplecount += dmstate->num_tuples;
2788 
2789  return ExecClearTuple(slot);
2790  }
2791 
2792  /*
2793  * Get the next RETURNING tuple.
2794  */
2795  return get_returning_data(node);
2796 }
static void execute_dml_stmt(ForeignScanState *node)
static TupleTableSlot * get_returning_data(ForeignScanState *node)
double tuplecount
Definition: instrument.h:80

References Assert, ExecClearTuple(), execute_dml_stmt(), ForeignScanState::fdw_state, get_returning_data(), PgFdwDirectModifyState::has_returning, if(), PlanState::instrument, PgFdwDirectModifyState::num_tuples, ScanState::ps, ForeignScanState::resultRelInfo, PgFdwDirectModifyState::set_processed, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, and Instrumentation::tuplecount.

Referenced by postgres_fdw_handler().

◆ postgresIterateForeignScan()

static TupleTableSlot * postgresIterateForeignScan ( ForeignScanState node)
static

Definition at line 1596 of file postgres_fdw.c.

1597 {
1598  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1599  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
1600 
1601  /*
1602  * In sync mode, if this is the first call after Begin or ReScan, we need
1603  * to create the cursor on the remote side. In async mode, we would have
1604  * already created the cursor before we get here, even if this is the
1605  * first call after Begin or ReScan.
1606  */
1607  if (!fsstate->cursor_exists)
1608  create_cursor(node);
1609 
1610  /*
1611  * Get some more tuples, if we've run out.
1612  */
1613  if (fsstate->next_tuple >= fsstate->num_tuples)
1614  {
1615  /* In async mode, just clear tuple slot. */
1616  if (fsstate->async_capable)
1617  return ExecClearTuple(slot);
1618  /* No point in another fetch if we already detected EOF, though. */
1619  if (!fsstate->eof_reached)
1620  fetch_more_data(node);
1621  /* If we didn't get any tuples, must be end of data. */
1622  if (fsstate->next_tuple >= fsstate->num_tuples)
1623  return ExecClearTuple(slot);
1624  }
1625 
1626  /*
1627  * Return the next tuple.
1628  */
1629  ExecStoreHeapTuple(fsstate->tuples[fsstate->next_tuple++],
1630  slot,
1631  false);
1632 
1633  return slot;
1634 }

References PgFdwScanState::async_capable, create_cursor(), PgFdwScanState::cursor_exists, PgFdwScanState::eof_reached, ExecClearTuple(), ExecStoreHeapTuple(), ForeignScanState::fdw_state, fetch_more_data(), if(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, and PgFdwScanState::tuples.

Referenced by postgres_fdw_handler().

◆ postgresPlanDirectModify()

static bool postgresPlanDirectModify ( PlannerInfo root,
ModifyTable plan,
Index  resultRelation,
int  subplan_index 
)
static

Definition at line 2444 of file postgres_fdw.c.

2448 {
2449  CmdType operation = plan->operation;
2450  RelOptInfo *foreignrel;
2451  RangeTblEntry *rte;
2452  PgFdwRelationInfo *fpinfo;
2453  Relation rel;
2454  StringInfoData sql;
2455  ForeignScan *fscan;
2456  List *processed_tlist = NIL;
2457  List *targetAttrs = NIL;
2458  List *remote_exprs;
2459  List *params_list = NIL;
2460  List *returningList = NIL;
2461  List *retrieved_attrs = NIL;
2462 
2463  /*
2464  * Decide whether it is safe to modify a foreign table directly.
2465  */
2466 
2467  /*
2468  * The table modification must be an UPDATE or DELETE.
2469  */
2470  if (operation != CMD_UPDATE && operation != CMD_DELETE)
2471  return false;
2472 
2473  /*
2474  * Try to locate the ForeignScan subplan that's scanning resultRelation.
2475  */
2476  fscan = find_modifytable_subplan(root, plan, resultRelation, subplan_index);
2477  if (!fscan)
2478  return false;
2479 
2480  /*
2481  * It's unsafe to modify a foreign table directly if there are any quals
2482  * that should be evaluated locally.
2483  */
2484  if (fscan->scan.plan.qual != NIL)
2485  return false;
2486 
2487  /* Safe to fetch data about the target foreign rel */
2488  if (fscan->scan.scanrelid == 0)
2489  {
2490  foreignrel = find_join_rel(root, fscan->fs_relids);
2491  /* We should have a rel for this foreign join. */
2492  Assert(foreignrel);
2493  }
2494  else
2495  foreignrel = root->simple_rel_array[resultRelation];
2496  rte = root->simple_rte_array[resultRelation];
2497  fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2498 
2499  /*
2500  * It's unsafe to update a foreign table directly, if any expressions to
2501  * assign to the target columns are unsafe to evaluate remotely.
2502  */
2503  if (operation == CMD_UPDATE)
2504  {
2505  ListCell *lc,
2506  *lc2;
2507 
2508  /*
2509  * The expressions of concern are the first N columns of the processed
2510  * targetlist, where N is the length of the rel's update_colnos.
2511  */
2512  get_translated_update_targetlist(root, resultRelation,
2513  &processed_tlist, &targetAttrs);
2514  forboth(lc, processed_tlist, lc2, targetAttrs)
2515  {
2516  TargetEntry *tle = lfirst_node(TargetEntry, lc);
2517  AttrNumber attno = lfirst_int(lc2);
2518 
2519  /* update's new-value expressions shouldn't be resjunk */
2520  Assert(!tle->resjunk);
2521 
2522  if (attno <= InvalidAttrNumber) /* shouldn't happen */
2523  elog(ERROR, "system-column update is not supported");
2524 
2525  if (!is_foreign_expr(root, foreignrel, (Expr *) tle->expr))
2526  return false;
2527  }
2528  }
2529 
2530  /*
2531  * Ok, rewrite subplan so as to modify the foreign table directly.
2532  */
2533  initStringInfo(&sql);
2534 
2535  /*
2536  * Core code already has some lock on each rel being planned, so we can
2537  * use NoLock here.
2538  */
2539  rel = table_open(rte->relid, NoLock);
2540 
2541  /*
2542  * Recall the qual clauses that must be evaluated remotely. (These are
2543  * bare clauses not RestrictInfos, but deparse.c's appendConditions()
2544  * doesn't care.)
2545  */
2546  remote_exprs = fpinfo->final_remote_exprs;
2547 
2548  /*
2549  * Extract the relevant RETURNING list if any.
2550  */
2551  if (plan->returningLists)
2552  {
2553  returningList = (List *) list_nth(plan->returningLists, subplan_index);
2554 
2555  /*
2556  * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2557  * we fetch from the foreign server any Vars specified in RETURNING
2558  * that refer not only to the target relation but to non-target
2559  * relations. So we'll deparse them into the RETURNING clause of the
2560  * remote query; use a targetlist consisting of them instead, which
2561  * will be adjusted to be new fdw_scan_tlist of the foreign-scan plan
2562  * node below.
2563  */
2564  if (fscan->scan.scanrelid == 0)
2565  returningList = build_remote_returning(resultRelation, rel,
2566  returningList);
2567  }
2568 
2569  /*
2570  * Construct the SQL command string.
2571  */
2572  switch (operation)
2573  {
2574  case CMD_UPDATE:
2575  deparseDirectUpdateSql(&sql, root, resultRelation, rel,
2576  foreignrel,
2577  processed_tlist,
2578  targetAttrs,
2579  remote_exprs, &params_list,
2580  returningList, &retrieved_attrs);
2581  break;
2582  case CMD_DELETE:
2583  deparseDirectDeleteSql(&sql, root, resultRelation, rel,
2584  foreignrel,
2585  remote_exprs, &params_list,
2586  returningList, &retrieved_attrs);
2587  break;
2588  default:
2589  elog(ERROR, "unexpected operation: %d", (int) operation);
2590  break;
2591  }
2592 
2593  /*
2594  * Update the operation and target relation info.
2595  */
2596  fscan->operation = operation;
2597  fscan->resultRelation = resultRelation;
2598 
2599  /*
2600  * Update the fdw_exprs list that will be available to the executor.
2601  */
2602  fscan->fdw_exprs = params_list;
2603 
2604  /*
2605  * Update the fdw_private list that will be available to the executor.
2606  * Items in the list must match enum FdwDirectModifyPrivateIndex, above.
2607  */
2608  fscan->fdw_private = list_make4(makeString(sql.data),
2609  makeBoolean((retrieved_attrs != NIL)),
2610  retrieved_attrs,
2611  makeBoolean(plan->canSetTag));
2612 
2613  /*
2614  * Update the foreign-join-related fields.
2615  */
2616  if (fscan->scan.scanrelid == 0)
2617  {
2618  /* No need for the outer subplan. */
2619  fscan->scan.plan.lefttree = NULL;
2620 
2621  /* Build new fdw_scan_tlist if UPDATE/DELETE .. RETURNING. */
2622  if (returningList)
2623  rebuild_fdw_scan_tlist(fscan, returningList);
2624  }
2625 
2626  /*
2627  * Finally, unset the async-capable flag if it is set, as we currently
2628  * don't support asynchronous execution of direct modifications.
2629  */
2630  if (fscan->scan.plan.async_capable)
2631  fscan->scan.plan.async_capable = false;
2632 
2633  table_close(rel, NoLock);
2634  return true;
2635 }
void get_translated_update_targetlist(PlannerInfo *root, Index relid, List **processed_tlist, List **update_colnos)
Definition: appendinfo.c:694
void deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, RelOptInfo *foreignrel, List *remote_conds, List **params_list, List *returningList, List **retrieved_attrs)
Definition: deparse.c:2391
void deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, RelOptInfo *foreignrel, List *targetlist, List *targetAttrs, List *remote_conds, List **params_list, List *returningList, List **retrieved_attrs)
Definition: deparse.c:2276
#define NoLock
Definition: lockdefs.h:34
CmdType
Definition: nodes.h:263
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define list_make4(x1, x2, x3, x4)
Definition: pg_list.h:219
static ForeignScan * find_modifytable_subplan(PlannerInfo *root, ModifyTable *plan, Index rtindex, int subplan_index)
static void rebuild_fdw_scan_tlist(ForeignScan *fscan, List *tlist)
static List * build_remote_returning(Index rtindex, Relation rel, List *returningList)
RelOptInfo * find_join_rel(PlannerInfo *root, Relids relids)
Definition: relnode.c:527
CmdType operation
Definition: plannodes.h:710
Bitmapset * fs_relids
Definition: plannodes.h:719
Index resultRelation
Definition: plannodes.h:711
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

References Assert, build_remote_returning(), CMD_DELETE, CMD_UPDATE, StringInfoData::data, deparseDirectDeleteSql(), deparseDirectUpdateSql(), elog, ERROR, TargetEntry::expr, ForeignScan::fdw_exprs, ForeignScan::fdw_private, PgFdwRelationInfo::final_remote_exprs, find_join_rel(), find_modifytable_subplan(), forboth, ForeignScan::fs_relids, get_translated_update_targetlist(), if(), initStringInfo(), InvalidAttrNumber, is_foreign_expr(), lfirst_int, lfirst_node, list_make4, list_nth(), makeBoolean(), makeString(), NIL, NoLock, ForeignScan::operation, plan, rebuild_fdw_scan_tlist(), RangeTblEntry::relid, ForeignScan::resultRelation, root, ForeignScan::scan, Scan::scanrelid, table_close(), and table_open().

Referenced by postgres_fdw_handler().

◆ postgresPlanForeignModify()

static List * postgresPlanForeignModify ( PlannerInfo root,
ModifyTable plan,
Index  resultRelation,
int  subplan_index 
)
static

Definition at line 1772 of file postgres_fdw.c.

1776 {
1777  CmdType operation = plan->operation;
1778  RangeTblEntry *rte = planner_rt_fetch(resultRelation, root);
1779  Relation rel;
1780  StringInfoData sql;
1781  List *targetAttrs = NIL;
1782  List *withCheckOptionList = NIL;
1783  List *returningList = NIL;
1784  List *retrieved_attrs = NIL;
1785  bool doNothing = false;
1786  int values_end_len = -1;
1787 
1788  initStringInfo(&sql);
1789 
1790  /*
1791  * Core code already has some lock on each rel being planned, so we can
1792  * use NoLock here.
1793  */
1794  rel = table_open(rte->relid, NoLock);
1795 
1796  /*
1797  * In an INSERT, we transmit all columns that are defined in the foreign
1798  * table. In an UPDATE, if there are BEFORE ROW UPDATE triggers on the
1799  * foreign table, we transmit all columns like INSERT; else we transmit
1800  * only columns that were explicitly targets of the UPDATE, so as to avoid
1801  * unnecessary data transmission. (We can't do that for INSERT since we
1802  * would miss sending default values for columns not listed in the source
1803  * statement, and for UPDATE if there are BEFORE ROW UPDATE triggers since
1804  * those triggers might change values for non-target columns, in which
1805  * case we would miss sending changed values for those columns.)
1806  */
1807  if (operation == CMD_INSERT ||
1808  (operation == CMD_UPDATE &&
1809  rel->trigdesc &&
1811  {
1812  TupleDesc tupdesc = RelationGetDescr(rel);
1813  int attnum;
1814 
1815  for (attnum = 1; attnum <= tupdesc->natts; attnum++)
1816  {
1817  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
1818 
1819  if (!attr->attisdropped)
1820  targetAttrs = lappend_int(targetAttrs, attnum);
1821  }
1822  }
1823  else if (operation == CMD_UPDATE)
1824  {
1825  int col;
1826  RelOptInfo *rel = find_base_rel(root, resultRelation);
1827  Bitmapset *allUpdatedCols = get_rel_all_updated_cols(root, rel);
1828 
1829  col = -1;
1830  while ((col = bms_next_member(allUpdatedCols, col)) >= 0)
1831  {
1832  /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
1834 
1835  if (attno <= InvalidAttrNumber) /* shouldn't happen */
1836  elog(ERROR, "system-column update is not supported");
1837  targetAttrs = lappend_int(targetAttrs, attno);
1838  }
1839  }
1840 
1841  /*
1842  * Extract the relevant WITH CHECK OPTION list if any.
1843  */
1844  if (plan->withCheckOptionLists)
1845  withCheckOptionList = (List *) list_nth(plan->withCheckOptionLists,
1846  subplan_index);
1847 
1848  /*
1849  * Extract the relevant RETURNING list if any.
1850  */
1851  if (plan->returningLists)
1852  returningList = (List *) list_nth(plan->returningLists, subplan_index);
1853 
1854  /*
1855  * ON CONFLICT DO UPDATE and DO NOTHING case with inference specification
1856  * should have already been rejected in the optimizer, as presently there
1857  * is no way to recognize an arbiter index on a foreign table. Only DO
1858  * NOTHING is supported without an inference specification.
1859  */
1860  if (plan->onConflictAction == ONCONFLICT_NOTHING)
1861  doNothing = true;
1862  else if (plan->onConflictAction != ONCONFLICT_NONE)
1863  elog(ERROR, "unexpected ON CONFLICT specification: %d",
1864  (int) plan->onConflictAction);
1865 
1866  /*
1867  * Construct the SQL command string.
1868  */
1869  switch (operation)
1870  {
1871  case CMD_INSERT:
1872  deparseInsertSql(&sql, rte, resultRelation, rel,
1873  targetAttrs, doNothing,
1874  withCheckOptionList, returningList,
1875  &retrieved_attrs, &values_end_len);
1876  break;
1877  case CMD_UPDATE:
1878  deparseUpdateSql(&sql, rte, resultRelation, rel,
1879  targetAttrs,
1880  withCheckOptionList, returningList,
1881  &retrieved_attrs);
1882  break;
1883  case CMD_DELETE:
1884  deparseDeleteSql(&sql, rte, resultRelation, rel,
1885  returningList,
1886  &retrieved_attrs);
1887  break;
1888  default:
1889  elog(ERROR, "unexpected operation: %d", (int) operation);
1890  break;
1891  }
1892 
1893  table_close(rel, NoLock);
1894 
1895  /*
1896  * Build the fdw_private list that will be available to the executor.
1897  * Items in the list must match enum FdwModifyPrivateIndex, above.
1898  */
1899  return list_make5(makeString(sql.data),
1900  targetAttrs,
1901  makeInteger(values_end_len),
1902  makeBoolean((retrieved_attrs != NIL)),
1903  retrieved_attrs);
1904 }
void deparseUpdateSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, List *withCheckOptionList, List *returningList, List **retrieved_attrs)
Definition: deparse.c:2216
void deparseDeleteSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *returningList, List **retrieved_attrs)
Definition: deparse.c:2362
Bitmapset * get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel)
Definition: inherit.c:648
#define planner_rt_fetch(rti, root)
Definition: pathnodes.h:564
#define list_make5(x1, x2, x3, x4, x5)
Definition: pg_list.h:222
RelOptInfo * find_base_rel(PlannerInfo *root, int relid)
Definition: relnode.c:414
TriggerDesc * trigdesc
Definition: rel.h:117
bool trig_update_before_row
Definition: reltrigger.h:61
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27

References attnum, bms_next_member(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, StringInfoData::data, deparseDeleteSql(), deparseInsertSql(), deparseUpdateSql(), elog, ERROR, find_base_rel(), FirstLowInvalidHeapAttributeNumber, get_rel_all_updated_cols(), initStringInfo(), InvalidAttrNumber, lappend_int(), list_make5, list_nth(), makeBoolean(), makeInteger(), makeString(), TupleDescData::natts, NIL, NoLock, ONCONFLICT_NONE, ONCONFLICT_NOTHING, plan, planner_rt_fetch, RelationGetDescr, RangeTblEntry::relid, root, table_close(), table_open(), TriggerDesc::trig_update_before_row, RelationData::trigdesc, and TupleDescAttr.

Referenced by postgres_fdw_handler().

◆ postgresRecheckForeignScan()

static bool postgresRecheckForeignScan ( ForeignScanState node,
TupleTableSlot slot 
)
static

Definition at line 2352 of file postgres_fdw.c.

2353 {
2354  Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
2356  TupleTableSlot *result;
2357 
2358  /* For base foreign relations, it suffices to set fdw_recheck_quals */
2359  if (scanrelid > 0)
2360  return true;
2361 
2362  Assert(outerPlan != NULL);
2363 
2364  /* Execute a local join execution plan */
2365  result = ExecProcNode(outerPlan);
2366  if (TupIsNull(result))
2367  return false;
2368 
2369  /* Store result in the given slot */
2370  ExecCopySlot(slot, result);
2371 
2372  return true;
2373 }
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:269
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:509

References Assert, ExecCopySlot(), ExecProcNode(), outerPlan, outerPlanState, PlanState::plan, ScanState::ps, ForeignScanState::ss, and TupIsNull.

Referenced by postgres_fdw_handler().

◆ postgresReScanForeignScan()

static void postgresReScanForeignScan ( ForeignScanState node)
static

Definition at line 1641 of file postgres_fdw.c.

1642 {
1643  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1644  char sql[64];
1645  PGresult *res;
1646 
1647  /* If we haven't created the cursor yet, nothing to do. */
1648  if (!fsstate->cursor_exists)
1649  return;
1650 
1651  /*
1652  * If the node is async-capable, and an asynchronous fetch for it has
1653  * begun, the asynchronous fetch might not have yet completed. Check if
1654  * the node is async-capable, and an asynchronous fetch for it is still in
1655  * progress; if so, complete the asynchronous fetch before restarting the
1656  * scan.
1657  */
1658  if (fsstate->async_capable &&
1659  fsstate->conn_state->pendingAreq &&
1660  fsstate->conn_state->pendingAreq->requestee == (PlanState *) node)
1661  fetch_more_data(node);
1662 
1663  /*
1664  * If any internal parameters affecting this node have changed, we'd
1665  * better destroy and recreate the cursor. Otherwise, if the remote
1666  * server is v14 or older, rewinding it should be good enough; if not,
1667  * rewind is only allowed for scrollable cursors, but we don't have a way
1668  * to check the scrollability of it, so destroy and recreate it in any
1669  * case. If we've only fetched zero or one batch, we needn't even rewind
1670  * the cursor, just rescan what we have.
1671  */
1672  if (node->ss.ps.chgParam != NULL)
1673  {
1674  fsstate->cursor_exists = false;
1675  snprintf(sql, sizeof(sql), "CLOSE c%u",
1676  fsstate->cursor_number);
1677  }
1678  else if (fsstate->fetch_ct_2 > 1)
1679  {
1680  if (PQserverVersion(fsstate->conn) < 150000)
1681  snprintf(sql, sizeof(sql), "MOVE BACKWARD ALL IN c%u",
1682  fsstate->cursor_number);
1683  else
1684  {
1685  fsstate->cursor_exists = false;
1686  snprintf(sql, sizeof(sql), "CLOSE c%u",
1687  fsstate->cursor_number);
1688  }
1689  }
1690  else
1691  {
1692  /* Easy: just rescan what we already have in memory, if anything */
1693  fsstate->next_tuple = 0;
1694  return;
1695  }
1696 
1697  /*
1698  * We don't use a PG_TRY block here, so be careful not to throw error
1699  * without releasing the PGresult.
1700  */
1701  res = pgfdw_exec_query(fsstate->conn, sql, fsstate->conn_state);
1703  pgfdw_report_error(ERROR, res, fsstate->conn, true, sql);
1704  PQclear(res);
1705 
1706  /* Now force a fresh FETCH. */
1707  fsstate->tuples = NULL;
1708  fsstate->num_tuples = 0;
1709  fsstate->next_tuple = 0;
1710  fsstate->fetch_ct_2 = 0;
1711  fsstate->eof_reached = false;
1712 }
Bitmapset * chgParam
Definition: execnodes.h:1148

References PgFdwScanState::async_capable, PlanState::chgParam, PgFdwScanState::conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, ERROR, ForeignScanState::fdw_state, PgFdwScanState::fetch_ct_2, fetch_more_data(), if(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, PgFdwConnState::pendingAreq, pgfdw_exec_query(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), PQserverVersion(), ScanState::ps, AsyncRequest::requestee, res, snprintf, ForeignScanState::ss, and PgFdwScanState::tuples.

Referenced by postgres_fdw_handler().

◆ prepare_foreign_modify()

static void prepare_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4206 of file postgres_fdw.c.

4207 {
4208  char prep_name[NAMEDATALEN];
4209  char *p_name;
4210  PGresult *res;
4211 
4212  /*
4213  * The caller would already have processed a pending asynchronous request
4214  * if any, so no need to do it here.
4215  */
4216 
4217  /* Construct name we'll use for the prepared statement. */
4218  snprintf(prep_name, sizeof(prep_name), "pgsql_fdw_prep_%u",
4219  GetPrepStmtNumber(fmstate->conn));
4220  p_name = pstrdup(prep_name);
4221 
4222  /*
4223  * We intentionally do not specify parameter types here, but leave the
4224  * remote server to derive them by default. This avoids possible problems
4225  * with the remote server using different type OIDs than we do. All of
4226  * the prepared statements we use in this module are simple enough that
4227  * the remote server will make the right choices.
4228  */
4229  if (!PQsendPrepare(fmstate->conn,
4230  p_name,
4231  fmstate->query,
4232  0,
4233  NULL))
4234  pgfdw_report_error(ERROR, NULL, fmstate->conn, false, fmstate->query);
4235 
4236  /*
4237  * Get the result, and check for success.
4238  *
4239  * We don't use a PG_TRY block here, so be careful not to throw error
4240  * without releasing the PGresult.
4241  */
4242  res = pgfdw_get_result(fmstate->conn);
4244  pgfdw_report_error(ERROR, res, fmstate->conn, true, fmstate->query);
4245  PQclear(res);
4246 
4247  /* This action shows that the prepare has been done. */
4248  fmstate->p_name = p_name;
4249 }
unsigned int GetPrepStmtNumber(PGconn *conn)
Definition: connection.c:838
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
Definition: fe-exec.c:1536
#define NAMEDATALEN

References PgFdwModifyState::conn, ERROR, GetPrepStmtNumber(), NAMEDATALEN, PgFdwModifyState::p_name, pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), PQsendPrepare(), pstrdup(), PgFdwModifyState::query, res, and snprintf.

Referenced by execute_foreign_modify().

◆ prepare_query_params()

static void prepare_query_params ( PlanState node,
List fdw_exprs,
int  numParams,
FmgrInfo **  param_flinfo,
List **  param_exprs,
const char ***  param_values 
)
static

Definition at line 4846 of file postgres_fdw.c.

4852 {
4853  int i;
4854  ListCell *lc;
4855 
4856  Assert(numParams > 0);
4857 
4858  /* Prepare for output conversion of parameters used in remote query. */
4859  *param_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * numParams);
4860 
4861  i = 0;
4862  foreach(lc, fdw_exprs)
4863  {
4864  Node *param_expr = (Node *) lfirst(lc);
4865  Oid typefnoid;
4866  bool isvarlena;
4867 
4868  getTypeOutputInfo(exprType(param_expr), &typefnoid, &isvarlena);
4869  fmgr_info(typefnoid, &(*param_flinfo)[i]);
4870  i++;
4871  }
4872 
4873  /*
4874  * Prepare remote-parameter expressions for evaluation. (Note: in
4875  * practice, we expect that all these expressions will be just Params, so
4876  * we could possibly do something more efficient than using the full
4877  * expression-eval machinery for this. But probably there would be little
4878  * benefit, and it'd require postgres_fdw to know more than is desirable
4879  * about Param evaluation.)
4880  */
4881  *param_exprs = ExecInitExprList(fdw_exprs, node);
4882 
4883  /* Allocate buffer for text form of query parameters. */
4884  *param_values = (const char **) palloc0(numParams * sizeof(char *));
4885 }
List * ExecInitExprList(List *nodes, PlanState *parent)
Definition: execExpr.c:326
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42

References Assert, ExecInitExprList(), exprType(), fmgr_info(), getTypeOutputInfo(), i, lfirst, and palloc0().

Referenced by postgresBeginDirectModify(), and postgresBeginForeignScan().

◆ process_pending_request()

void process_pending_request ( AsyncRequest areq)

Definition at line 7473 of file postgres_fdw.c.

7474 {
7475  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7476  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7477 
7478  /* The request would have been pending for a callback */
7479  Assert(areq->callback_pending);
7480 
7481  /* The request should be currently in-process */
7482  Assert(fsstate->conn_state->pendingAreq == areq);
7483 
7484  fetch_more_data(node);
7485 
7486  /*
7487  * If we didn't get any tuples, must be end of data; complete the request
7488  * now. Otherwise, we postpone completing the request until we are called
7489  * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
7490  */
7491  if (fsstate->next_tuple >= fsstate->num_tuples)
7492  {
7493  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7494  areq->callback_pending = false;
7495  /* Mark the request as complete */
7496  ExecAsyncRequestDone(areq, NULL);
7497  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7498  ExecAsyncResponse(areq);
7499  }
7500 }
void ExecAsyncRequestDone(AsyncRequest *areq, TupleTableSlot *result)
Definition: execAsync.c:137

References Assert, AsyncRequest::callback_pending, ExecAsyncRequestDone(), ExecAsyncResponse(), ForeignScanState::fdw_state, fetch_more_data(), and AsyncRequest::requestee.

Referenced by create_cursor(), execute_dml_stmt(), execute_foreign_modify(), GetConnection(), pgfdw_exec_query(), and postgresForeignAsyncConfigureWait().

◆ process_query_params()

static void process_query_params ( ExprContext econtext,
FmgrInfo param_flinfo,
List param_exprs,
const char **  param_values 
)
static

Definition at line 4891 of file postgres_fdw.c.

4895 {
4896  int nestlevel;
4897  int i;
4898  ListCell *lc;
4899 
4900  nestlevel = set_transmission_modes();
4901 
4902  i = 0;
4903  foreach(lc, param_exprs)
4904  {
4905  ExprState *expr_state = (ExprState *) lfirst(lc);
4906  Datum expr_value;
4907  bool isNull;
4908 
4909  /* Evaluate the parameter expression */
4910  expr_value = ExecEvalExpr(expr_state, econtext, &isNull);
4911 
4912  /*
4913  * Get string representation of each parameter value by invoking
4914  * type-specific output function, unless the value is null.
4915  */
4916  if (isNull)
4917  param_values[i] = NULL;
4918  else
4919  param_values[i] = OutputFunctionCall(&param_flinfo[i], expr_value);
4920 
4921  i++;
4922  }
4923 
4924  reset_transmission_modes(nestlevel);
4925 }
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:333

References ExecEvalExpr(), i, lfirst, OutputFunctionCall(), reset_transmission_modes(), and set_transmission_modes().

Referenced by create_cursor(), and execute_dml_stmt().

◆ produce_tuple_asynchronously()

static void produce_tuple_asynchronously ( AsyncRequest areq,
bool  fetch 
)
static

Definition at line 7375 of file postgres_fdw.c.

7376 {
7377  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7378  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7379  AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
7380  TupleTableSlot *result;
7381 
7382  /* This should not be called if the request is currently in-process */
7383  Assert(areq != pendingAreq);
7384 
7385  /* Fetch some more tuples, if we've run out */
7386  if (fsstate->next_tuple >= fsstate->num_tuples)
7387  {
7388  /* No point in another fetch if we already detected EOF, though */
7389  if (!fsstate->eof_reached)
7390  {
7391  /* Mark the request as pending for a callback */
7393  /* Begin another fetch if requested and if no pending request */
7394  if (fetch && !pendingAreq)
7395  fetch_more_data_begin(areq);
7396  }
7397  else
7398  {
7399  /* There's nothing more to do; just return a NULL pointer */
7400  result = NULL;
7401  /* Mark the request as complete */
7402  ExecAsyncRequestDone(areq, result);
7403  }
7404  return;
7405  }
7406 
7407  /* Get a tuple from the ForeignScan node */
7408  result = areq->requestee->ExecProcNodeReal(areq->requestee);
7409  if (!TupIsNull(result))
7410  {
7411  /* Mark the request as complete */
7412  ExecAsyncRequestDone(areq, result);
7413  return;
7414  }
7415 
7416  /* We must have run out of tuples */
7417  Assert(fsstate->next_tuple >= fsstate->num_tuples);
7418 
7419  /* Fetch some more tuples, if we've not detected EOF yet */
7420  if (!fsstate->eof_reached)
7421  {
7422  /* Mark the request as pending for a callback */
7424  /* Begin another fetch if requested and if no pending request */
7425  if (fetch && !pendingAreq)
7426  fetch_more_data_begin(areq);
7427  }
7428  else
7429  {
7430  /* There's nothing more to do; just return a NULL pointer */
7431  result = NULL;
7432  /* Mark the request as complete */
7433  ExecAsyncRequestDone(areq, result);
7434  }
7435 }
void ExecAsyncRequestPending(AsyncRequest *areq)
Definition: execAsync.c:149
ExecProcNodeMtd ExecProcNodeReal
Definition: execnodes.h:1123

References Assert, ExecAsyncRequestDone(), ExecAsyncRequestPending(), PlanState::ExecProcNodeReal, ForeignScanState::fdw_state, fetch_more_data_begin(), AsyncRequest::requestee, and TupIsNull.

Referenced by complete_pending_request(), postgresForeignAsyncNotify(), and postgresForeignAsyncRequest().

◆ rebuild_fdw_scan_tlist()

static void rebuild_fdw_scan_tlist ( ForeignScan fscan,
List tlist 
)
static

Definition at line 4521 of file postgres_fdw.c.

4522 {
4523  List *new_tlist = tlist;
4524  List *old_tlist = fscan->fdw_scan_tlist;
4525  ListCell *lc;
4526 
4527  foreach(lc, old_tlist)
4528  {
4529  TargetEntry *tle = (TargetEntry *) lfirst(lc);
4530 
4531  if (tlist_member(tle->expr, new_tlist))
4532  continue; /* already got it */
4533 
4534  new_tlist = lappend(new_tlist,
4535  makeTargetEntry(tle->expr,
4536  list_length(new_tlist) + 1,
4537  NULL,
4538  false));
4539  }
4540  fscan->fdw_scan_tlist = new_tlist;
4541 }

References TargetEntry::expr, ForeignScan::fdw_scan_tlist, lappend(), lfirst, list_length(), makeTargetEntry(), and tlist_member().

Referenced by postgresPlanDirectModify().

◆ reset_transmission_modes()

void reset_transmission_modes ( int  nestlevel)

Definition at line 3933 of file postgres_fdw.c.

3934 {
3935  AtEOXact_GUC(true, nestlevel);
3936 }
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2261

References AtEOXact_GUC().

Referenced by appendConditions(), appendLimitClause(), appendOrderByClause(), convert_prep_stmt_params(), deparseDirectUpdateSql(), and process_query_params().

◆ semijoin_target_ok()

static bool semijoin_target_ok ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel 
)
static

Definition at line 5744 of file postgres_fdw.c.

5745 {
5746  List *vars;
5747  ListCell *lc;
5748  bool ok = true;
5749 
5750  Assert(joinrel->reltarget);
5751 
5753 
5754  foreach(lc, vars)
5755  {
5756  Var *var = (Var *) lfirst(lc);
5757 
5758  if (!IsA(var, Var))
5759  continue;
5760 
5761  if (bms_is_member(var->varno, innerrel->relids) &&
5762  !bms_is_member(var->varno, outerrel->relids))
5763  {
5764  /*
5765  * The planner can create semi-join, which refers to inner rel
5766  * vars in its target list. However, we deparse semi-join as an
5767  * exists() subquery, so can't handle references to inner rel in
5768  * the target list.
5769  */
5770  ok = false;
5771  break;
5772  }
5773  }
5774  return ok;
5775 }

References Assert, bms_is_member(), PathTarget::exprs, IsA, lfirst, pull_var_clause(), PVC_INCLUDE_PLACEHOLDERS, RelOptInfo::relids, RelOptInfo::reltarget, and Var::varno.

Referenced by foreign_join_ok().

◆ set_transmission_modes()

int set_transmission_modes ( void  )

Definition at line 3897 of file postgres_fdw.c.

3898 {
3899  int nestlevel = NewGUCNestLevel();
3900 
3901  /*
3902  * The values set here should match what pg_dump does. See also
3903  * configure_remote_session in connection.c.
3904  */
3905  if (DateStyle != USE_ISO_DATES)
3906  (void) set_config_option("datestyle", "ISO",
3908  GUC_ACTION_SAVE, true, 0, false);
3910  (void) set_config_option("intervalstyle", "postgres",
3912  GUC_ACTION_SAVE, true, 0, false);
3913  if (extra_float_digits < 3)
3914  (void) set_config_option("extra_float_digits", "3",
3916  GUC_ACTION_SAVE, true, 0, false);
3917 
3918  /*
3919  * In addition force restrictive search_path, in case there are any
3920  * regproc or similar constants to be printed.
3921  */
3922  (void) set_config_option("search_path", "pg_catalog",
3924  GUC_ACTION_SAVE, true, 0, false);
3925 
3926  return nestlevel;
3927 }
int extra_float_digits
Definition: float.c:41
int DateStyle
Definition: globals.c:123
int IntervalStyle
Definition: globals.c:125
int NewGUCNestLevel(void)
Definition: guc.c:2234
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3340
@ GUC_ACTION_SAVE
Definition: guc.h:201
@ PGC_S_SESSION
Definition: guc.h:122
@ PGC_USERSET
Definition: guc.h:75
#define USE_ISO_DATES
Definition: miscadmin.h:235
#define INTSTYLE_POSTGRES
Definition: miscadmin.h:255

References DateStyle, extra_float_digits, GUC_ACTION_SAVE, IntervalStyle, INTSTYLE_POSTGRES, NewGUCNestLevel(), PGC_S_SESSION, PGC_USERSET, set_config_option(), and USE_ISO_DATES.

Referenced by appendConditions(), appendLimitClause(), appendOrderByClause(), convert_prep_stmt_params(), deparseDirectUpdateSql(), and process_query_params().

◆ store_returning_result()

static void store_returning_result ( PgFdwModifyState fmstate,
TupleTableSlot slot,
PGresult res 
)
static

Definition at line 4340 of file postgres_fdw.c.

4342 {
4343  PG_TRY();
4344  {
4345  HeapTuple newtup;
4346 
4347  newtup = make_tuple_from_result_row(res, 0,
4348  fmstate->rel,
4349  fmstate->attinmeta,
4350  fmstate->retrieved_attrs,
4351  NULL,
4352  fmstate->temp_cxt);
4353 
4354  /*
4355  * The returning slot will not necessarily be suitable to store
4356  * heaptuples directly, so allow for conversion.
4357  */
4358  ExecForceStoreHeapTuple(newtup, slot, true);
4359  }
4360  PG_CATCH();
4361  {
4362  PQclear(res);
4363  PG_RE_THROW();
4364  }
4365  PG_END_TRY();
4366 }
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1556

References PgFdwModifyState::attinmeta, ExecForceStoreHeapTuple(), make_tuple_from_result_row(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PQclear(), PgFdwModifyState::rel, res, PgFdwModifyState::retrieved_attrs, and PgFdwModifyState::temp_cxt.

Referenced by execute_foreign_modify().

Variable Documentation

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 54 of file postgres_fdw.c.