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

6981 {
6982  Query *parse = root->parse;
6983  PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6984  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
6985  bool has_final_sort = false;
6986  List *pathkeys = NIL;
6987  PgFdwPathExtraData *fpextra;
6988  bool save_use_remote_estimate = false;
6989  double rows;
6990  int width;
6991  Cost startup_cost;
6992  Cost total_cost;
6993  List *fdw_private;
6994  ForeignPath *final_path;
6995 
6996  /*
6997  * Currently, we only support this for SELECT commands
6998  */
6999  if (parse->commandType != CMD_SELECT)
7000  return;
7001 
7002  /*
7003  * No work if there is no FOR UPDATE/SHARE clause and if there is no need
7004  * to add a LIMIT node
7005  */
7006  if (!parse->rowMarks && !extra->limit_needed)
7007  return;
7008 
7009  /* We don't support cases where there are any SRFs in the targetlist */
7010  if (parse->hasTargetSRFs)
7011  return;
7012 
7013  /* Save the input_rel as outerrel in fpinfo */
7014  fpinfo->outerrel = input_rel;
7015 
7016  /*
7017  * Copy foreign table, foreign server, user mapping, FDW options etc.
7018  * details from the input relation's fpinfo.
7019  */
7020  fpinfo->table = ifpinfo->table;
7021  fpinfo->server = ifpinfo->server;
7022  fpinfo->user = ifpinfo->user;
7023  merge_fdw_options(fpinfo, ifpinfo, NULL);
7024 
7025  /*
7026  * If there is no need to add a LIMIT node, there might be a ForeignPath
7027  * in the input_rel's pathlist that implements all behavior of the query.
7028  * Note: we would already have accounted for the query's FOR UPDATE/SHARE
7029  * (if any) before we get here.
7030  */
7031  if (!extra->limit_needed)
7032  {
7033  ListCell *lc;
7034 
7035  Assert(parse->rowMarks);
7036 
7037  /*
7038  * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
7039  * so the input_rel should be a base, join, or ordered relation; and
7040  * if it's an ordered relation, its input relation should be a base or
7041  * join relation.
7042  */
7043  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7044  input_rel->reloptkind == RELOPT_JOINREL ||
7045  (input_rel->reloptkind == RELOPT_UPPER_REL &&
7046  ifpinfo->stage == UPPERREL_ORDERED &&
7047  (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
7048  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
7049 
7050  foreach(lc, input_rel->pathlist)
7051  {
7052  Path *path = (Path *) lfirst(lc);
7053 
7054  /*
7055  * apply_scanjoin_target_to_paths() uses create_projection_path()
7056  * to adjust each of its input paths if needed, whereas
7057  * create_ordered_paths() uses apply_projection_to_path() to do
7058  * that. So the former might have put a ProjectionPath on top of
7059  * the ForeignPath; look through ProjectionPath and see if the
7060  * path underneath it is ForeignPath.
7061  */
7062  if (IsA(path, ForeignPath) ||
7063  (IsA(path, ProjectionPath) &&
7064  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
7065  {
7066  /*
7067  * Create foreign final path; this gets rid of a
7068  * no-longer-needed outer plan (if any), which makes the
7069  * EXPLAIN output look cleaner
7070  */
7071  final_path = create_foreign_upper_path(root,
7072  path->parent,
7073  path->pathtarget,
7074  path->rows,
7075  path->startup_cost,
7076  path->total_cost,
7077  path->pathkeys,
7078  NULL, /* no extra plan */
7079  NIL, /* no fdw_restrictinfo
7080  * list */
7081  NIL); /* no fdw_private */
7082 
7083  /* and add it to the final_rel */
7084  add_path(final_rel, (Path *) final_path);
7085 
7086  /* Safe to push down */
7087  fpinfo->pushdown_safe = true;
7088 
7089  return;
7090  }
7091  }
7092 
7093  /*
7094  * If we get here it means no ForeignPaths; since we would already
7095  * have considered pushing down all operations for the query to the
7096  * remote server, give up on it.
7097  */
7098  return;
7099  }
7100 
7101  Assert(extra->limit_needed);
7102 
7103  /*
7104  * If the input_rel is an ordered relation, replace the input_rel with its
7105  * input relation
7106  */
7107  if (input_rel->reloptkind == RELOPT_UPPER_REL &&
7108  ifpinfo->stage == UPPERREL_ORDERED)
7109  {
7110  input_rel = ifpinfo->outerrel;
7111  ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
7112  has_final_sort = true;
7113  pathkeys = root->sort_pathkeys;
7114  }
7115 
7116  /* The input_rel should be a base, join, or grouping relation */
7117  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7118  input_rel->reloptkind == RELOPT_JOINREL ||
7119  (input_rel->reloptkind == RELOPT_UPPER_REL &&
7120  ifpinfo->stage == UPPERREL_GROUP_AGG));
7121 
7122  /*
7123  * We try to create a path below by extending a simple foreign path for
7124  * the underlying base, join, or grouping relation to perform the final
7125  * sort (if has_final_sort) and the LIMIT restriction remotely, which is
7126  * stored into the fdw_private list of the resulting path. (We
7127  * re-estimate the costs of sorting the underlying relation, if
7128  * has_final_sort.)
7129  */
7130 
7131  /*
7132  * Assess if it is safe to push down the LIMIT and OFFSET to the remote
7133  * server
7134  */
7135 
7136  /*
7137  * If the underlying relation has any local conditions, the LIMIT/OFFSET
7138  * cannot be pushed down.
7139  */
7140  if (ifpinfo->local_conds)
7141  return;
7142 
7143  /*
7144  * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
7145  * not safe to remote.
7146  */
7147  if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
7148  !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
7149  return;
7150 
7151  /* Safe to push down */
7152  fpinfo->pushdown_safe = true;
7153 
7154  /* Construct PgFdwPathExtraData */
7155  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
7156  fpextra->target = root->upper_targets[UPPERREL_FINAL];
7157  fpextra->has_final_sort = has_final_sort;
7158  fpextra->has_limit = extra->limit_needed;
7159  fpextra->limit_tuples = extra->limit_tuples;
7160  fpextra->count_est = extra->count_est;
7161  fpextra->offset_est = extra->offset_est;
7162 
7163  /*
7164  * Estimate the costs of performing the final sort and the LIMIT
7165  * restriction remotely. If has_final_sort is false, we wouldn't need to
7166  * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
7167  * roughly estimated using the costs we already have for the underlying
7168  * relation, in the same way as when use_remote_estimate is false. Since
7169  * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
7170  * false in that case.
7171  */
7172  if (!fpextra->has_final_sort)
7173  {
7174  save_use_remote_estimate = ifpinfo->use_remote_estimate;
7175  ifpinfo->use_remote_estimate = false;
7176  }
7177  estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
7178  &rows, &width, &startup_cost, &total_cost);
7179  if (!fpextra->has_final_sort)
7180  ifpinfo->use_remote_estimate = save_use_remote_estimate;
7181 
7182  /*
7183  * Build the fdw_private list that will be used by postgresGetForeignPlan.
7184  * Items in the list must match order in enum FdwPathPrivateIndex.
7185  */
7186  fdw_private = list_make2(makeBoolean(has_final_sort),
7187  makeBoolean(extra->limit_needed));
7188 
7189  /*
7190  * Create foreign final path; this gets rid of a no-longer-needed outer
7191  * plan (if any), which makes the EXPLAIN output look cleaner
7192  */
7193  final_path = create_foreign_upper_path(root,
7194  input_rel,
7195  root->upper_targets[UPPERREL_FINAL],
7196  rows,
7197  startup_cost,
7198  total_cost,
7199  pathkeys,
7200  NULL, /* no extra plan */
7201  NIL, /* no fdw_restrictinfo list */
7202  fdw_private);
7203 
7204  /* and add it to the final_rel */
7205  add_path(final_rel, (Path *) final_path);
7206 }
#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:1346
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
double Cost
Definition: nodes.h:251
@ CMD_SELECT
Definition: nodes.h:265
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:2333
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:817
@ RELOPT_UPPER_REL
Definition: pathnodes.h:821
@ RELOPT_JOINREL
Definition: pathnodes.h:818
#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:1884
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:3304
Definition: pg_list.h:54
List * pathkeys
Definition: pathnodes.h:1654
Cardinality rows
Definition: pathnodes.h:1649
Cost startup_cost
Definition: pathnodes.h:1650
Cost total_cost
Definition: pathnodes.h:1651
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:888
RelOptKind reloptkind
Definition: pathnodes.h:855
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, 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 6751 of file postgres_fdw.c.

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

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

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

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

6080 {
6081  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6082  ListCell *lc;
6083 
6084  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
6085 
6086  /*
6087  * Before creating sorted paths, arrange for the passed-in EPQ path, if
6088  * any, to return columns needed by the parent ForeignScan node so that
6089  * they will propagate up through Sort nodes injected below, if necessary.
6090  */
6091  if (epq_path != NULL && useful_pathkeys_list != NIL)
6092  {
6093  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6094  PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6095 
6096  /* Include columns required for evaluating PHVs in the tlist. */
6098  pull_var_clause((Node *) target->exprs,
6100 
6101  /* Include columns required for evaluating the local conditions. */
6102  foreach(lc, fpinfo->local_conds)
6103  {
6104  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6105 
6107  pull_var_clause((Node *) rinfo->clause,
6109  }
6110 
6111  /*
6112  * If we have added any new columns, adjust the tlist of the EPQ path.
6113  *
6114  * Note: the plan created using this path will only be used to execute
6115  * EPQ checks, where accuracy of the plan cost and width estimates
6116  * would not be important, so we do not do set_pathtarget_cost_width()
6117  * for the new pathtarget here. See also postgresGetForeignPlan().
6118  */
6119  if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6120  {
6121  /* The EPQ path is a join path, so it is projection-capable. */
6123 
6124  /*
6125  * Use create_projection_path() here, so as to avoid modifying it
6126  * in place.
6127  */
6128  epq_path = (Path *) create_projection_path(root,
6129  rel,
6130  epq_path,
6131  target);
6132  }
6133  }
6134 
6135  /* Create one path for each set of pathkeys we found above. */
6136  foreach(lc, useful_pathkeys_list)
6137  {
6138  double rows;
6139  int width;
6140  Cost startup_cost;
6141  Cost total_cost;
6142  List *useful_pathkeys = lfirst(lc);
6143  Path *sorted_epq_path;
6144 
6145  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
6146  &rows, &width, &startup_cost, &total_cost);
6147 
6148  /*
6149  * The EPQ path must be at least as well sorted as the path itself, in
6150  * case it gets used as input to a mergejoin.
6151  */
6152  sorted_epq_path = epq_path;
6153  if (sorted_epq_path != NULL &&
6154  !pathkeys_contained_in(useful_pathkeys,
6155  sorted_epq_path->pathkeys))
6156  sorted_epq_path = (Path *)
6158  rel,
6159  sorted_epq_path,
6160  useful_pathkeys,
6161  -1.0);
6162 
6163  if (IS_SIMPLE_REL(rel))
6164  add_path(rel, (Path *)
6166  NULL,
6167  rows,
6168  startup_cost,
6169  total_cost,
6170  useful_pathkeys,
6171  rel->lateral_relids,
6172  sorted_epq_path,
6173  NIL, /* no fdw_restrictinfo
6174  * list */
6175  NIL));
6176  else
6177  add_path(rel, (Path *)
6179  NULL,
6180  rows,
6181  startup_cost,
6182  total_cost,
6183  useful_pathkeys,
6184  rel->lateral_relids,
6185  sorted_epq_path,
6186  restrictlist,
6187  NIL));
6188  }
6189 }
bool is_projection_capable_path(Path *path)
Definition: createplan.c:7207
#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:3000
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2685
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:2235
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:2281
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:829
#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:903
Expr * clause
Definition: pathnodes.h:2552
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 3625 of file postgres_fdw.c.

3632 {
3633  /*
3634  * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3635  * side is unlikely to generate properly-sorted output, so it would need
3636  * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3637  * if the GROUP BY clause is sort-able but isn't a superset of the given
3638  * pathkeys, adjust the costs with that function. Otherwise, adjust the
3639  * costs by applying the same heuristic as for the scan or join case.
3640  */
3641  if (!grouping_is_sortable(root->processed_groupClause) ||
3642  !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3643  {
3644  Path sort_path; /* dummy for result of cost_sort */
3645 
3646  cost_sort(&sort_path,
3647  root,
3648  pathkeys,
3649  *p_startup_cost + *p_run_cost,
3650  retrieved_rows,
3651  width,
3652  0.0,
3653  work_mem,
3654  limit_tuples);
3655 
3656  *p_startup_cost = sort_path.startup_cost;
3657  *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3658  }
3659  else
3660  {
3661  /*
3662  * The default extra cost seems too large for foreign-grouping cases;
3663  * add 1/4th of that default.
3664  */
3665  double sort_multiplier = 1.0 + (DEFAULT_FDW_SORT_MULTIPLIER
3666  - 1.0) * 0.25;
3667 
3668  *p_startup_cost *= sort_multiplier;
3669  *p_run_cost *= sort_multiplier;
3670  }
3671 }
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:2124
int work_mem
Definition: globals.c:128
#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 5368 of file postgres_fdw.c.

5369 {
5370  int targrows = astate->targrows;
5371  int pos; /* array index to store tuple in */
5372  MemoryContext oldcontext;
5373 
5374  /* Always increment sample row counter. */
5375  astate->samplerows += 1;
5376 
5377  /*
5378  * Determine the slot where this sample row should be stored. Set pos to
5379  * negative value to indicate the row should be skipped.
5380  */
5381  if (astate->numrows < targrows)
5382  {
5383  /* First targrows rows are always included into the sample */
5384  pos = astate->numrows++;
5385  }
5386  else
5387  {
5388  /*
5389  * Now we start replacing tuples in the sample until we reach the end
5390  * of the relation. Same algorithm as in acquire_sample_rows in
5391  * analyze.c; see Jeff Vitter's paper.
5392  */
5393  if (astate->rowstoskip < 0)
5394  astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5395 
5396  if (astate->rowstoskip <= 0)
5397  {
5398  /* Choose a random reservoir element to replace. */
5399  pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5400  Assert(pos >= 0 && pos < targrows);
5401  heap_freetuple(astate->rows[pos]);
5402  }
5403  else
5404  {
5405  /* Skip this tuple. */
5406  pos = -1;
5407  }
5408 
5409  astate->rowstoskip -= 1;
5410  }
5411 
5412  if (pos >= 0)
5413  {
5414  /*
5415  * Create sample tuple from current result row, and store it in the
5416  * position determined above. The tuple has to be created in anl_cxt.
5417  */
5418  oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5419 
5420  astate->rows[pos] = make_tuple_from_result_row(res, row,
5421  astate->rel,
5422  astate->attinmeta,
5423  astate->retrieved_attrs,
5424  NULL,
5425  astate->temp_cxt);
5426 
5427  MemoryContextSwitchTo(oldcontext);
5428  }
5429 }
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 4737 of file postgres_fdw.c.

4741 {
4742  TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4743  TupleTableSlot *resultSlot;
4744  Datum *values;
4745  bool *isnull;
4746  Datum *old_values;
4747  bool *old_isnull;
4748  int i;
4749 
4750  /*
4751  * Use the return tuple slot as a place to store the result tuple.
4752  */
4753  resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4754 
4755  /*
4756  * Extract all the values of the scan tuple.
4757  */
4758  slot_getallattrs(slot);
4759  old_values = slot->tts_values;
4760  old_isnull = slot->tts_isnull;
4761 
4762  /*
4763  * Prepare to build the result tuple.
4764  */
4765  ExecClearTuple(resultSlot);
4766  values = resultSlot->tts_values;
4767  isnull = resultSlot->tts_isnull;
4768 
4769  /*
4770  * Transpose data into proper fields of the result tuple.
4771  */
4772  for (i = 0; i < resultTupType->natts; i++)
4773  {
4774  int j = dmstate->attnoMap[i];
4775 
4776  if (j == 0)
4777  {
4778  values[i] = (Datum) 0;
4779  isnull[i] = true;
4780  }
4781  else
4782  {
4783  values[i] = old_values[j - 1];
4784  isnull[i] = old_isnull[j - 1];
4785  }
4786  }
4787 
4788  /*
4789  * Build the virtual tuple.
4790  */
4791  ExecStoreVirtualTuple(resultSlot);
4792 
4793  /*
4794  * If we have any system columns to return, materialize a heap tuple in
4795  * the slot from column values set above and install system columns in
4796  * that tuple.
4797  */
4798  if (dmstate->hasSystemCols)
4799  {
4800  HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4801 
4802  /* ctid */
4803  if (dmstate->ctidAttno)
4804  {
4805  ItemPointer ctid = NULL;
4806 
4807  ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4808  resultTup->t_self = *ctid;
4809  }
4810 
4811  /*
4812  * And remaining columns
4813  *
4814  * Note: since we currently don't allow the target relation to appear
4815  * on the nullable side of an outer join, any system columns wouldn't
4816  * go to NULL.
4817  *
4818  * Note: no need to care about tableoid here because it will be
4819  * initialized in ExecProcessReturning().
4820  */
4824  }
4825 
4826  /*
4827  * And return the result tuple.
4828  */
4829  return resultSlot;
4830 }
static Datum values[MAXATTR]
Definition: bootstrap.c:152
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 6197 of file postgres_fdw.c.

6198 {
6199  ListCell *lc;
6200 
6201  foreach(lc, fpinfo->server->options)
6202  {
6203  DefElem *def = (DefElem *) lfirst(lc);
6204 
6205  if (strcmp(def->defname, "use_remote_estimate") == 0)
6206  fpinfo->use_remote_estimate = defGetBoolean(def);
6207  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
6208  (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
6209  NULL);
6210  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
6211  (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
6212  NULL);
6213  else if (strcmp(def->defname, "extensions") == 0)
6214  fpinfo->shippable_extensions =
6215  ExtractExtensionList(defGetString(def), false);
6216  else if (strcmp(def->defname, "fetch_size") == 0)
6217  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6218  else if (strcmp(def->defname, "async_capable") == 0)
6219  fpinfo->async_capable = defGetBoolean(def);
6220  }
6221 }
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:2873
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:2963
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 6229 of file postgres_fdw.c.

6230 {
6231  ListCell *lc;
6232 
6233  foreach(lc, fpinfo->table->options)
6234  {
6235  DefElem *def = (DefElem *) lfirst(lc);
6236 
6237  if (strcmp(def->defname, "use_remote_estimate") == 0)
6238  fpinfo->use_remote_estimate = defGetBoolean(def);
6239  else if (strcmp(def->defname, "fetch_size") == 0)
6240  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6241  else if (strcmp(def->defname, "async_capable") == 0)
6242  fpinfo->async_capable = defGetBoolean(def);
6243  }
6244 }
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 4410 of file postgres_fdw.c.

4411 {
4412  bool have_wholerow = false;
4413  List *tlist = NIL;
4414  List *vars;
4415  ListCell *lc;
4416 
4417  Assert(returningList);
4418 
4419  vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4420 
4421  /*
4422  * If there's a whole-row reference to the target relation, then we'll
4423  * need all the columns of the relation.
4424  */
4425  foreach(lc, vars)
4426  {
4427  Var *var = (Var *) lfirst(lc);
4428 
4429  if (IsA(var, Var) &&
4430  var->varno == rtindex &&
4431  var->varattno == InvalidAttrNumber)
4432  {
4433  have_wholerow = true;
4434  break;
4435  }
4436  }
4437 
4438  if (have_wholerow)
4439  {
4440  TupleDesc tupdesc = RelationGetDescr(rel);
4441  int i;
4442 
4443  for (i = 1; i <= tupdesc->natts; i++)
4444  {
4445  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4446  Var *var;
4447 
4448  /* Ignore dropped attributes. */
4449  if (attr->attisdropped)
4450  continue;
4451 
4452  var = makeVar(rtindex,
4453  i,
4454  attr->atttypid,
4455  attr->atttypmod,
4456  attr->attcollation,
4457  0);
4458 
4459  tlist = lappend(tlist,
4460  makeTargetEntry((Expr *) var,
4461  list_length(tlist) + 1,
4462  NULL,
4463  false));
4464  }
4465  }
4466 
4467  /* Now add any remaining columns to tlist. */
4468  foreach(lc, vars)
4469  {
4470  Var *var = (Var *) lfirst(lc);
4471 
4472  /*
4473  * No need for whole-row references to the target relation. We don't
4474  * need system columns other than ctid and oid either, since those are
4475  * set locally.
4476  */
4477  if (IsA(var, Var) &&
4478  var->varno == rtindex &&
4479  var->varattno <= InvalidAttrNumber &&
4481  continue; /* don't need it */
4482 
4483  if (tlist_member((Expr *) var, tlist))
4484  continue; /* already got it */
4485 
4486  tlist = lappend(tlist,
4487  makeTargetEntry((Expr *) var,
4488  list_length(tlist) + 1,
4489  NULL,
4490  false));
4491  }
4492 
4493  list_free(vars);
4494 
4495  return tlist;
4496 }
#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 3932 of file postgres_fdw.c.

3934 {
3935  char sql[64];
3936  PGresult *res;
3937 
3938  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3939 
3940  /*
3941  * We don't use a PG_TRY block here, so be careful not to throw error
3942  * without releasing the PGresult.
3943  */
3944  res = pgfdw_exec_query(conn, sql, conn_state);
3946  pgfdw_report_error(ERROR, res, conn, true, sql);
3947  PQclear(res);
3948 }
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:871
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:835
static unsigned int cursor_number
Definition: connection.c:80
#define ERROR
Definition: elog.h:39
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3411
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:100
#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 7482 of file postgres_fdw.c.

7483 {
7484  /* The request would have been pending for a callback */
7485  Assert(areq->callback_pending);
7486 
7487  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7488  areq->callback_pending = false;
7489 
7490  /* We begin a fetch afterwards if necessary; don't fetch */
7491  produce_tuple_asynchronously(areq, false);
7492 
7493  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7494  ExecAsyncResponse(areq);
7495 
7496  /* Also, we do instrumentation ourselves, if required */
7497  if (areq->requestee->instrument)
7499  TupIsNull(areq->result) ? 0.0 : 1.0);
7500 }
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:1127
#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 7673 of file postgres_fdw.c.

7674 {
7676  Relation rel = errpos->rel;
7677  ForeignScanState *fsstate = errpos->fsstate;
7678  const char *attname = NULL;
7679  const char *relname = NULL;
7680  bool is_wholerow = false;
7681 
7682  /*
7683  * If we're in a scan node, always use aliases from the rangetable, for
7684  * consistency between the simple-relation and remote-join cases. Look at
7685  * the relation's tupdesc only if we're not in a scan node.
7686  */
7687  if (fsstate)
7688  {
7689  /* ForeignScan case */
7690  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7691  int varno = 0;
7692  AttrNumber colno = 0;
7693 
7694  if (fsplan->scan.scanrelid > 0)
7695  {
7696  /* error occurred in a scan against a foreign table */
7697  varno = fsplan->scan.scanrelid;
7698  colno = errpos->cur_attno;
7699  }
7700  else
7701  {
7702  /* error occurred in a scan against a foreign join */
7703  TargetEntry *tle;
7704 
7705  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7706  errpos->cur_attno - 1);
7707 
7708  /*
7709  * Target list can have Vars and expressions. For Vars, we can
7710  * get some information, however for expressions we can't. Thus
7711  * for expressions, just show generic context message.
7712  */
7713  if (IsA(tle->expr, Var))
7714  {
7715  Var *var = (Var *) tle->expr;
7716 
7717  varno = var->varno;
7718  colno = var->varattno;
7719  }
7720  }
7721 
7722  if (varno > 0)
7723  {
7724  EState *estate = fsstate->ss.ps.state;
7725  RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7726 
7727  relname = rte->eref->aliasname;
7728 
7729  if (colno == 0)
7730  is_wholerow = true;
7731  else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7732  attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7733  else if (colno == SelfItemPointerAttributeNumber)
7734  attname = "ctid";
7735  }
7736  }
7737  else if (rel)
7738  {
7739  /* Non-ForeignScan case (we should always have a rel here) */
7740  TupleDesc tupdesc = RelationGetDescr(rel);
7741 
7743  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7744  {
7745  Form_pg_attribute attr = TupleDescAttr(tupdesc,
7746  errpos->cur_attno - 1);
7747 
7748  attname = NameStr(attr->attname);
7749  }
7750  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7751  attname = "ctid";
7752  }
7753 
7754  if (relname && is_wholerow)
7755  errcontext("whole-row reference to foreign table \"%s\"", relname);
7756  else if (relname && attname)
7757  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7758  else
7759  errcontext("processing expression at position %d in select list",
7760  errpos->cur_attno);
7761 }
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:2034
List * fdw_scan_tlist
Definition: plannodes.h:717
Plan * plan
Definition: execnodes.h:1117
EState * state
Definition: execnodes.h:1119
PlanState ps
Definition: execnodes.h:1564
Index scanrelid
Definition: plannodes.h:389
Expr * expr
Definition: primnodes.h:2162
#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 4251 of file postgres_fdw.c.

4255 {
4256  const char **p_values;
4257  int i;
4258  int j;
4259  int pindex = 0;
4260  MemoryContext oldcontext;
4261 
4262  oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4263 
4264  p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4265 
4266  /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4267  Assert(!(tupleid != NULL && numSlots > 1));
4268 
4269  /* 1st parameter should be ctid, if it's in use */
4270  if (tupleid != NULL)
4271  {
4272  Assert(numSlots == 1);
4273  /* don't need set_transmission_modes for TID output */
4274  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
4275  PointerGetDatum(tupleid));
4276  pindex++;
4277  }
4278 
4279  /* get following parameters from slots */
4280  if (slots != NULL && fmstate->target_attrs != NIL)
4281  {
4282  TupleDesc tupdesc = RelationGetDescr(fmstate->rel);
4283  int nestlevel;
4284  ListCell *lc;
4285 
4286  nestlevel = set_transmission_modes();
4287 
4288  for (i = 0; i < numSlots; i++)
4289  {
4290  j = (tupleid != NULL) ? 1 : 0;
4291  foreach(lc, fmstate->target_attrs)
4292  {
4293  int attnum = lfirst_int(lc);
4294  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4295  Datum value;
4296  bool isnull;
4297 
4298  /* Ignore generated columns; they are set to DEFAULT */
4299  if (attr->attgenerated)
4300  continue;
4301  value = slot_getattr(slots[i], attnum, &isnull);
4302  if (isnull)
4303  p_values[pindex] = NULL;
4304  else
4305  p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[j],
4306  value);
4307  pindex++;
4308  j++;
4309  }
4310  }
4311 
4312  reset_transmission_modes(nestlevel);
4313  }
4314 
4315  Assert(pindex == fmstate->p_nums * numSlots);
4316 
4317  MemoryContextSwitchTo(oldcontext);
4318 
4319  return p_values;
4320 }
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
static struct @155 value
void * palloc(Size size)
Definition: mcxt.c:1316
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 3708 of file postgres_fdw.c.

3709 {
3710  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3711  ExprContext *econtext = node->ss.ps.ps_ExprContext;
3712  int numParams = fsstate->numParams;
3713  const char **values = fsstate->param_values;
3714  PGconn *conn = fsstate->conn;
3716  PGresult *res;
3717 
3718  /* First, process a pending asynchronous request, if any. */
3719  if (fsstate->conn_state->pendingAreq)
3721 
3722  /*
3723  * Construct array of query parameter values in text format. We do the
3724  * conversions in the short-lived per-tuple context, so as not to cause a
3725  * memory leak over repeated scans.
3726  */
3727  if (numParams > 0)
3728  {
3729  MemoryContext oldcontext;
3730 
3731  oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3732 
3733  process_query_params(econtext,
3734  fsstate->param_flinfo,
3735  fsstate->param_exprs,
3736  values);
3737 
3738  MemoryContextSwitchTo(oldcontext);
3739  }
3740 
3741  /* Construct the DECLARE CURSOR command */
3742  initStringInfo(&buf);
3743  appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3744  fsstate->cursor_number, fsstate->query);
3745 
3746  /*
3747  * Notice that we pass NULL for paramTypes, thus forcing the remote server
3748  * to infer types for all parameters. Since we explicitly cast every
3749  * parameter (see deparse.c), the "inference" is trivial and will produce
3750  * the desired result. This allows us to avoid assuming that the remote
3751  * server has the same OIDs we do for the parameters' types.
3752  */
3753  if (!PQsendQueryParams(conn, buf.data, numParams,
3754  NULL, values, NULL, NULL, 0))
3755  pgfdw_report_error(ERROR, NULL, conn, false, buf.data);
3756 
3757  /*
3758  * Get the result, and check for success.
3759  *
3760  * We don't use a PG_TRY block here, so be careful not to throw error
3761  * without releasing the PGresult.
3762  */
3765  pgfdw_report_error(ERROR, res, conn, true, fsstate->query);
3766  PQclear(res);
3767 
3768  /* Mark the cursor as created, and show no tuples have been retrieved */
3769  fsstate->cursor_exists = true;
3770  fsstate->tuples = NULL;
3771  fsstate->num_tuples = 0;
3772  fsstate->next_tuple = 0;
3773  fsstate->fetch_ct_2 = 0;
3774  fsstate->eof_reached = false;
3775 
3776  /* Clean up */
3777  pfree(buf.data);
3778 }
PGresult * pgfdw_get_result(PGconn *conn)
Definition: connection.c:852
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:1520
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:1156

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

3966 {
3967  PgFdwModifyState *fmstate;
3968  Relation rel = resultRelInfo->ri_RelationDesc;
3969  TupleDesc tupdesc = RelationGetDescr(rel);
3970  Oid userid;
3971  ForeignTable *table;
3972  UserMapping *user;
3973  AttrNumber n_params;
3974  Oid typefnoid;
3975  bool isvarlena;
3976  ListCell *lc;
3977 
3978  /* Begin constructing PgFdwModifyState. */
3979  fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState));
3980  fmstate->rel = rel;
3981 
3982  /* Identify which user to do the remote access as. */
3983  userid = ExecGetResultRelCheckAsUser(resultRelInfo, estate);
3984 
3985  /* Get info about foreign table. */
3986  table = GetForeignTable(RelationGetRelid(rel));
3987  user = GetUserMapping(userid, table->serverid);
3988 
3989  /* Open connection; report that we'll create a prepared statement. */
3990  fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
3991  fmstate->p_name = NULL; /* prepared statement not made yet */
3992 
3993  /* Set up remote query information. */
3994  fmstate->query = query;
3995  if (operation == CMD_INSERT)
3996  {
3997  fmstate->query = pstrdup(fmstate->query);
3998  fmstate->orig_query = pstrdup(fmstate->query);
3999  }
4000  fmstate->target_attrs = target_attrs;
4001  fmstate->values_end = values_end;
4002  fmstate->has_returning = has_returning;
4003  fmstate->retrieved_attrs = retrieved_attrs;
4004 
4005  /* Create context for per-tuple temp workspace. */
4006  fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
4007  "postgres_fdw temporary data",
4009 
4010  /* Prepare for input conversion of RETURNING results. */
4011  if (fmstate->has_returning)
4012  fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
4013 
4014  /* Prepare for output conversion of parameters used in prepared stmt. */
4015  n_params = list_length(fmstate->target_attrs) + 1;
4016  fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params);
4017  fmstate->p_nums = 0;
4018 
4019  if (operation == CMD_UPDATE || operation == CMD_DELETE)
4020  {
4021  Assert(subplan != NULL);
4022 
4023  /* Find the ctid resjunk column in the subplan's result */
4025  "ctid");
4026  if (!AttributeNumberIsValid(fmstate->ctidAttno))
4027  elog(ERROR, "could not find junk ctid column");
4028 
4029  /* First transmittable parameter will be ctid */
4030  getTypeOutputInfo(TIDOID, &typefnoid, &isvarlena);
4031  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4032  fmstate->p_nums++;
4033  }
4034 
4035  if (operation == CMD_INSERT || operation == CMD_UPDATE)
4036  {
4037  /* Set up for remaining transmittable parameters */
4038  foreach(lc, fmstate->target_attrs)
4039  {
4040  int attnum = lfirst_int(lc);
4041  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4042 
4043  Assert(!attr->attisdropped);
4044 
4045  /* Ignore generated columns; they are set to DEFAULT */
4046  if (attr->attgenerated)
4047  continue;
4048  getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
4049  fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4050  fmstate->p_nums++;
4051  }
4052  }
4053 
4054  Assert(fmstate->p_nums <= n_params);
4055 
4056  /* Set batch_size from foreign server/table options. */
4057  if (operation == CMD_INSERT)
4058  fmstate->batch_size = get_batch_size_option(rel);
4059 
4060  fmstate->num_slots = 1;
4061 
4062  /* Initialize auxiliary state */
4063  fmstate->aux_fmstate = NULL;
4064 
4065  return fmstate;
4066 }
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
PGconn * GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
Definition: connection.c:177
#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:1695
#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 4381 of file postgres_fdw.c.

4382 {
4383  char sql[64];
4384  PGresult *res;
4385 
4386  /* do nothing if the query is not allocated */
4387  if (!fmstate->p_name)
4388  return;
4389 
4390  snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4391 
4392  /*
4393  * We don't use a PG_TRY block here, so be careful not to throw error
4394  * without releasing the PGresult.
4395  */
4396  res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4398  pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
4399  PQclear(res);
4400  pfree(fmstate->p_name);
4401  fmstate->p_name = NULL;
4402 }

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

3682 {
3684  Expr *expr = em->em_expr;
3685 
3686  /*
3687  * If we've identified what we're processing in the current scan, we only
3688  * want to match that expression.
3689  */
3690  if (state->current != NULL)
3691  return equal(expr, state->current);
3692 
3693  /*
3694  * Otherwise, ignore anything we've already processed.
3695  */
3696  if (list_member(state->already_used, expr))
3697  return false;
3698 
3699  /* This is the new target to process. */
3700  state->current = expr;
3701  return true;
3702 }
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 3075 of file postgres_fdw.c.

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

4538 {
4540  ExprContext *econtext = node->ss.ps.ps_ExprContext;
4541  int numParams = dmstate->numParams;
4542  const char **values = dmstate->param_values;
4543 
4544  /* First, process a pending asynchronous request, if any. */
4545  if (dmstate->conn_state->pendingAreq)
4547 
4548  /*
4549  * Construct array of query parameter values in text format.
4550  */
4551  if (numParams > 0)
4552  process_query_params(econtext,
4553  dmstate->param_flinfo,
4554  dmstate->param_exprs,
4555  values);
4556 
4557  /*
4558  * Notice that we pass NULL for paramTypes, thus forcing the remote server
4559  * to infer types for all parameters. Since we explicitly cast every
4560  * parameter (see deparse.c), the "inference" is trivial and will produce
4561  * the desired result. This allows us to avoid assuming that the remote
4562  * server has the same OIDs we do for the parameters' types.
4563  */
4564  if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4565  NULL, values, NULL, NULL, 0))
4566  pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);
4567 
4568  /*
4569  * Get the result, and check for success.
4570  *
4571  * We don't use a PG_TRY block here, so be careful not to throw error
4572  * without releasing the PGresult.
4573  */
4574  dmstate->result = pgfdw_get_result(dmstate->conn);
4575  if (PQresultStatus(dmstate->result) !=
4577  pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4578  dmstate->query);
4579 
4580  /* Get the number of rows affected. */
4581  if (dmstate->has_returning)
4582  dmstate->num_tuples = PQntuples(dmstate->result);
4583  else
4584  dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4585 }
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:103
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 4076 of file postgres_fdw.c.

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

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

7423 {
7424  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7425  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7426  char sql[64];
7427 
7428  Assert(!fsstate->conn_state->pendingAreq);
7429 
7430  /* Create the cursor synchronously. */
7431  if (!fsstate->cursor_exists)
7432  create_cursor(node);
7433 
7434  /* We will send this query, but not wait for the response. */
7435  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7436  fsstate->fetch_size, fsstate->cursor_number);
7437 
7438  if (!PQsendQuery(fsstate->conn, sql))
7439  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7440 
7441  /* Remember that the request is in process */
7442  fsstate->conn_state->pendingAreq = areq;
7443 }
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 7776 of file postgres_fdw.c.

7777 {
7778  ListCell *lc;
7779 
7780  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7781 
7782  foreach(lc, ec->ec_members)
7783  {
7785 
7786  /*
7787  * Note we require !bms_is_empty, else we'd accept constant
7788  * expressions which are not suitable for the purpose.
7789  */
7790  if (bms_is_subset(em->em_relids, rel->relids) &&
7791  !bms_is_empty(em->em_relids) &&
7793  is_foreign_expr(root, rel, em->em_expr))
7794  return em;
7795  }
7796 
7797  return NULL;
7798 }
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:861

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

7814 {
7815  PathTarget *target = rel->reltarget;
7816  ListCell *lc1;
7817  int i;
7818 
7819  i = 0;
7820  foreach(lc1, target->exprs)
7821  {
7822  Expr *expr = (Expr *) lfirst(lc1);
7823  Index sgref = get_pathtarget_sortgroupref(target, i);
7824  ListCell *lc2;
7825 
7826  /* Ignore non-sort expressions */
7827  if (sgref == 0 ||
7829  root->parse->sortClause) == NULL)
7830  {
7831  i++;
7832  continue;
7833  }
7834 
7835  /* We ignore binary-compatible relabeling on both ends */
7836  while (expr && IsA(expr, RelabelType))
7837  expr = ((RelabelType *) expr)->arg;
7838 
7839  /* Locate an EquivalenceClass member matching this expr, if any */
7840  foreach(lc2, ec->ec_members)
7841  {
7843  Expr *em_expr;
7844 
7845  /* Don't match constants */
7846  if (em->em_is_const)
7847  continue;
7848 
7849  /* Ignore child members */
7850  if (em->em_is_child)
7851  continue;
7852 
7853  /* Match if same expression (after stripping relabel) */
7854  em_expr = em->em_expr;
7855  while (em_expr && IsA(em_expr, RelabelType))
7856  em_expr = ((RelabelType *) em_expr)->arg;
7857 
7858  if (!equal(em_expr, expr))
7859  continue;
7860 
7861  /* Check that expression (including relabels!) is shippable */
7862  if (is_foreign_expr(root, rel, em->em_expr))
7863  return em;
7864  }
7865 
7866  i++;
7867  }
7868 
7869  return NULL;
7870 }
unsigned int Index
Definition: c.h:614
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1538
List * exprs
Definition: pathnodes.h:1522
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 2374 of file postgres_fdw.c.

2378 {
2379  Plan *subplan = outerPlan(plan);
2380 
2381  /*
2382  * The cases we support are (1) the desired ForeignScan is the immediate
2383  * child of ModifyTable, or (2) it is the subplan_index'th child of an
2384  * Append node that is the immediate child of ModifyTable. There is no
2385  * point in looking further down, as that would mean that local joins are
2386  * involved, so we can't do the update directly.
2387  *
2388  * There could be a Result atop the Append too, acting to compute the
2389  * UPDATE targetlist values. We ignore that here; the tlist will be
2390  * checked by our caller.
2391  *
2392  * In principle we could examine all the children of the Append, but it's
2393  * currently unlikely that the core planner would generate such a plan
2394  * with the children out-of-order. Moreover, such a search risks costing
2395  * O(N^2) time when there are a lot of children.
2396  */
2397  if (IsA(subplan, Append))
2398  {
2399  Append *appendplan = (Append *) subplan;
2400 
2401  if (subplan_index < list_length(appendplan->appendplans))
2402  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2403  }
2404  else if (IsA(subplan, Result) &&
2405  outerPlan(subplan) != NULL &&
2406  IsA(outerPlan(subplan), Append))
2407  {
2408  Append *appendplan = (Append *) outerPlan(subplan);
2409 
2410  if (subplan_index < list_length(appendplan->appendplans))
2411  subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2412  }
2413 
2414  /* Now, have we got a ForeignScan on the desired rel? */
2415  if (IsA(subplan, ForeignScan))
2416  {
2417  ForeignScan *fscan = (ForeignScan *) subplan;
2418 
2419  if (bms_is_member(rtindex, fscan->fs_base_relids))
2420  return fscan;
2421  }
2422 
2423  return NULL;
2424 }
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 4363 of file postgres_fdw.c.

4364 {
4365  Assert(fmstate != NULL);
4366 
4367  /* If we created a prepared statement, destroy it */
4368  deallocate_query(fmstate);
4369 
4370  /* Release remote connection */
4371  ReleaseConnection(fmstate->conn);
4372  fmstate->conn = NULL;
4373 }

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

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

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

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

7878 {
7879  Oid foreigntableid = RelationGetRelid(rel);
7880  ForeignTable *table;
7881  ForeignServer *server;
7882  List *options;
7883  ListCell *lc;
7884 
7885  /* we use 1 by default, which means "no batching" */
7886  int batch_size = 1;
7887 
7888  /*
7889  * Load options for table and server. We append server options after table
7890  * options, because table options take precedence.
7891  */
7892  table = GetForeignTable(foreigntableid);
7893  server = GetForeignServer(table->serverid);
7894 
7895  options = NIL;
7896  options = list_concat(options, table->options);
7897  options = list_concat(options, server->options);
7898 
7899  /* See if either table or server specifies batch_size. */
7900  foreach(lc, options)
7901  {
7902  DefElem *def = (DefElem *) lfirst(lc);
7903 
7904  if (strcmp(def->defname, "batch_size") == 0)
7905  {
7906  (void) parse_int(defGetString(def), &batch_size, 0, NULL);
7907  break;
7908  }
7909  }
7910 
7911  return batch_size;
7912 }
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 3579 of file postgres_fdw.c.

3582 {
3583  PGresult *volatile res = NULL;
3584 
3585  /* PGresult must be released before leaving this function. */
3586  PG_TRY();
3587  {
3588  char *line;
3589  char *p;
3590  int n;
3591 
3592  /*
3593  * Execute EXPLAIN remotely.
3594  */
3595  res = pgfdw_exec_query(conn, sql, NULL);
3597  pgfdw_report_error(ERROR, res, conn, false, sql);
3598 
3599  /*
3600  * Extract cost numbers for topmost plan node. Note we search for a
3601  * left paren from the end of the line to avoid being confused by
3602  * other uses of parentheses.
3603  */
3604  line = PQgetvalue(res, 0, 0);
3605  p = strrchr(line, '(');
3606  if (p == NULL)
3607  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3608  n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3609  startup_cost, total_cost, rows, width);
3610  if (n != 4)
3611  elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3612  }
3613  PG_FINALLY();
3614  {
3615  PQclear(res);
3616  }
3617  PG_END_TRY();
3618 }
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 4591 of file postgres_fdw.c.

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

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:3223
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:981
bool has_eclass_joins
Definition: pathnodes.h:983

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:1379

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

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

7523 {
7524  HeapTuple tuple;
7525  TupleDesc tupdesc;
7526  Datum *values;
7527  bool *nulls;
7528  ItemPointer ctid = NULL;
7529  ConversionLocation errpos;
7530  ErrorContextCallback errcallback;
7531  MemoryContext oldcontext;
7532  ListCell *lc;
7533  int j;
7534 
7535  Assert(row < PQntuples(res));
7536 
7537  /*
7538  * Do the following work in a temp context that we reset after each tuple.
7539  * This cleans up not only the data we have direct access to, but any
7540  * cruft the I/O functions might leak.
7541  */
7542  oldcontext = MemoryContextSwitchTo(temp_context);
7543 
7544  /*
7545  * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
7546  * provided, otherwise look to the scan node's ScanTupleSlot.
7547  */
7548  if (rel)
7549  tupdesc = RelationGetDescr(rel);
7550  else
7551  {
7552  Assert(fsstate);
7553  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
7554  }
7555 
7556  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
7557  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
7558  /* Initialize to nulls for any columns not present in result */
7559  memset(nulls, true, tupdesc->natts * sizeof(bool));
7560 
7561  /*
7562  * Set up and install callback to report where conversion error occurs.
7563  */
7564  errpos.cur_attno = 0;
7565  errpos.rel = rel;
7566  errpos.fsstate = fsstate;
7567  errcallback.callback = conversion_error_callback;
7568  errcallback.arg = (void *) &errpos;
7569  errcallback.previous = error_context_stack;
7570  error_context_stack = &errcallback;
7571 
7572  /*
7573  * i indexes columns in the relation, j indexes columns in the PGresult.
7574  */
7575  j = 0;
7576  foreach(lc, retrieved_attrs)
7577  {
7578  int i = lfirst_int(lc);
7579  char *valstr;
7580 
7581  /* fetch next column's textual value */
7582  if (PQgetisnull(res, row, j))
7583  valstr = NULL;
7584  else
7585  valstr = PQgetvalue(res, row, j);
7586 
7587  /*
7588  * convert value to internal representation
7589  *
7590  * Note: we ignore system columns other than ctid and oid in result
7591  */
7592  errpos.cur_attno = i;
7593  if (i > 0)
7594  {
7595  /* ordinary column */
7596  Assert(i <= tupdesc->natts);
7597  nulls[i - 1] = (valstr == NULL);
7598  /* Apply the input function even to nulls, to support domains */
7599  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
7600  valstr,
7601  attinmeta->attioparams[i - 1],
7602  attinmeta->atttypmods[i - 1]);
7603  }
7604  else if (i == SelfItemPointerAttributeNumber)
7605  {
7606  /* ctid */
7607  if (valstr != NULL)
7608  {
7609  Datum datum;
7610 
7611  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
7612  ctid = (ItemPointer) DatumGetPointer(datum);
7613  }
7614  }
7615  errpos.cur_attno = 0;
7616 
7617  j++;
7618  }
7619 
7620  /* Uninstall error context callback. */
7621  error_context_stack = errcallback.previous;
7622 
7623  /*
7624  * Check we got the expected number of columns. Note: j == 0 and
7625  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
7626  */
7627  if (j > 0 && j != PQnfields(res))
7628  elog(ERROR, "remote query result does not match the foreign table");
7629 
7630  /*
7631  * Build the result tuple in caller's memory context.
7632  */
7633  MemoryContextSwitchTo(oldcontext);
7634 
7635  tuple = heap_form_tuple(tupdesc, values, nulls);
7636 
7637  /*
7638  * If we have a CTID to return, install it in both t_self and t_ctid.
7639  * t_self is the normal place, but if the tuple is converted to a
7640  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
7641  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
7642  */
7643  if (ctid)
7644  tuple->t_self = tuple->t_data->t_ctid = *ctid;
7645 
7646  /*
7647  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
7648  * heap_form_tuple. heap_form_tuple actually creates the tuple with
7649  * DatumTupleFields, not HeapTupleFields, but the executor expects
7650  * HeapTupleFields and will happily extract system columns on that
7651  * assumption. If we don't do this then, for example, the tuple length
7652  * ends up in the xmin field, which isn't what we want.
7653  */
7657 
7658  /* Clean up */
7659  MemoryContextReset(temp_context);
7660 
7661  return tuple;
7662 }
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 6256 of file postgres_fdw.c.

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

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

1738 {
1739  Var *var;
1740 
1741  /*
1742  * In postgres_fdw, what we need is the ctid, same as for a regular table.
1743  */
1744 
1745  /* Make a Var representing the desired value */
1746  var = makeVar(rtindex,
1748  TIDOID,
1749  -1,
1750  InvalidOid,
1751  0);
1752 
1753  /* Register it as a row-identity column needed by this target rel */
1754  add_row_identity_var(root, var, rtindex, "ctid");
1755 }
void add_row_identity_var(PlannerInfo *root, Var *orig_var, Index rtindex, const char *rowid_name)
Definition: appendinfo.c:789
#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 4922 of file postgres_fdw.c.

4925 {
4926  ForeignTable *table;
4927  UserMapping *user;
4928  PGconn *conn;
4929  StringInfoData sql;
4930  PGresult *volatile res = NULL;
4931 
4932  /* Return the row-analysis function pointer */
4934 
4935  /*
4936  * Now we have to get the number of pages. It's annoying that the ANALYZE
4937  * API requires us to return that now, because it forces some duplication
4938  * of effort between this routine and postgresAcquireSampleRowsFunc. But
4939  * it's probably not worth redefining that API at this point.
4940  */
4941 
4942  /*
4943  * Get the connection to use. We do the remote access as the table's
4944  * owner, even if the ANALYZE was started by some other user.
4945  */
4946  table = GetForeignTable(RelationGetRelid(relation));
4947  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4948  conn = GetConnection(user, false, NULL);
4949 
4950  /*
4951  * Construct command to get page count for relation.
4952  */
4953  initStringInfo(&sql);
4954  deparseAnalyzeSizeSql(&sql, relation);
4955 
4956  /* In what follows, do not risk leaking any PGresults. */
4957  PG_TRY();
4958  {
4959  res = pgfdw_exec_query(conn, sql.data, NULL);
4961  pgfdw_report_error(ERROR, res, conn, false, sql.data);
4962 
4963  if (PQntuples(res) != 1 || PQnfields(res) != 1)
4964  elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
4965  *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
4966  }
4967  PG_FINALLY();
4968  {
4969  PQclear(res);
4970  }
4971  PG_END_TRY();
4972 
4974 
4975  return true;
4976 }
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 2632 of file postgres_fdw.c.

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

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

1906 {
1907  PgFdwModifyState *fmstate;
1908  char *query;
1909  List *target_attrs;
1910  bool has_returning;
1911  int values_end_len;
1912  List *retrieved_attrs;
1913  RangeTblEntry *rte;
1914 
1915  /*
1916  * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
1917  * stays NULL.
1918  */
1919  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1920  return;
1921 
1922  /* Deconstruct fdw_private data. */
1923  query = strVal(list_nth(fdw_private,
1925  target_attrs = (List *) list_nth(fdw_private,
1927  values_end_len = intVal(list_nth(fdw_private,
1929  has_returning = boolVal(list_nth(fdw_private,
1931  retrieved_attrs = (List *) list_nth(fdw_private,
1933 
1934  /* Find RTE. */
1935  rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex,
1936  mtstate->ps.state);
1937 
1938  /* Construct an execution state. */
1939  fmstate = create_foreign_modify(mtstate->ps.state,
1940  rte,
1941  resultRelInfo,
1942  mtstate->operation,
1943  outerPlanState(mtstate)->plan,
1944  query,
1945  target_attrs,
1946  values_end_len,
1947  has_returning,
1948  retrieved_attrs);
1949 
1950  resultRelInfo->ri_FdwState = fmstate;
1951 }
#define outerPlanState(node)
Definition: execnodes.h:1213
CmdType operation
Definition: execnodes.h:1356
#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:1159

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

2794 {
2796 
2797  /* if dmstate is NULL, we are in EXPLAIN; nothing to do */
2798  if (dmstate == NULL)
2799  return;
2800 
2801  /* Release PGresult */
2802  PQclear(dmstate->result);
2803 
2804  /* Release remote connection */
2805  ReleaseConnection(dmstate->conn);
2806  dmstate->conn = NULL;
2807 
2808  /* MemoryContext will be deleted automatically. */
2809 }

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

2276 {
2277  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2278 
2279  Assert(fmstate != NULL);
2280 
2281  /*
2282  * If the fmstate has aux_fmstate set, get the aux_fmstate (see
2283  * postgresBeginForeignInsert())
2284  */
2285  if (fmstate->aux_fmstate)
2286  fmstate = fmstate->aux_fmstate;
2287 
2288  /* Destroy the execution state */
2289  finish_foreign_modify(fmstate);
2290 }
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 2125 of file postgres_fdw.c.

2127 {
2128  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2129 
2130  /* If fmstate is NULL, we are in EXPLAIN; nothing to do */
2131  if (fmstate == NULL)
2132  return;
2133 
2134  /* Destroy the execution state */
2135  finish_foreign_modify(fmstate);
2136 }

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

Referenced by postgres_fdw_handler().

◆ postgresEndForeignScan()

static void postgresEndForeignScan ( ForeignScanState node)
static

Definition at line 1709 of file postgres_fdw.c.

1710 {
1711  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1712 
1713  /* if fsstate is NULL, we are in EXPLAIN; nothing to do */
1714  if (fsstate == NULL)
1715  return;
1716 
1717  /* Close the cursor if open, to prevent accumulation of cursors */
1718  if (fsstate->cursor_exists)
1719  close_cursor(fsstate->conn, fsstate->cursor_number,
1720  fsstate->conn_state);
1721 
1722  /* Release remote connection */
1723  ReleaseConnection(fsstate->conn);
1724  fsstate->conn = NULL;
1725 
1726  /* MemoryContexts will be deleted automatically. */
1727 }

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

1992 {
1993  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
1994  TupleTableSlot **rslot;
1995 
1996  /*
1997  * If the fmstate has aux_fmstate set, use the aux_fmstate (see
1998  * postgresBeginForeignInsert())
1999  */
2000  if (fmstate->aux_fmstate)
2001  resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2002  rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2003  slots, planSlots, numSlots);
2004  /* Revert that change */
2005  if (fmstate->aux_fmstate)
2006  resultRelInfo->ri_FdwState = fmstate;
2007 
2008  return rslot;
2009 }
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 2106 of file postgres_fdw.c.

2110 {
2111  TupleTableSlot **rslot;
2112  int numSlots = 1;
2113 
2114  rslot = execute_foreign_modify(estate, resultRelInfo, CMD_DELETE,
2115  &slot, &planSlot, &numSlots);
2116 
2117  return rslot ? rslot[0] : NULL;
2118 }

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

1962 {
1963  PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
1964  TupleTableSlot **rslot;
1965  int numSlots = 1;
1966 
1967  /*
1968  * If the fmstate has aux_fmstate set, use the aux_fmstate (see
1969  * postgresBeginForeignInsert())
1970  */
1971  if (fmstate->aux_fmstate)
1972  resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
1973  rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
1974  &slot, &planSlot, &numSlots);
1975  /* Revert that change */
1976  if (fmstate->aux_fmstate)
1977  resultRelInfo->ri_FdwState = fmstate;
1978 
1979  return rslot ? *rslot : NULL;
1980 }

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

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

2091 {
2092  TupleTableSlot **rslot;
2093  int numSlots = 1;
2094 
2095  rslot = execute_foreign_modify(estate, resultRelInfo, CMD_UPDATE,
2096  &slot, &planSlot, &numSlots);
2097 
2098  return rslot ? rslot[0] : NULL;
2099 }

References CMD_UPDATE, and execute_foreign_modify().

Referenced by postgres_fdw_handler().

◆ postgresExplainDirectModify()

static void postgresExplainDirectModify ( ForeignScanState node,
ExplainState es 
)
static

Definition at line 2951 of file postgres_fdw.c.

2952 {
2953  List *fdw_private;
2954  char *sql;
2955 
2956  if (es->verbose)
2957  {
2958  fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
2959  sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql));
2960  ExplainPropertyText("Remote SQL", sql, es);
2961  }
2962 }
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
Definition: explain.c:4795
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 2923 of file postgres_fdw.c.

2928 {
2929  if (es->verbose)
2930  {
2931  char *sql = strVal(list_nth(fdw_private,
2933 
2934  ExplainPropertyText("Remote SQL", sql, es);
2935 
2936  /*
2937  * For INSERT we should always have batch size >= 1, but UPDATE and
2938  * DELETE don't support batching so don't show the property.
2939  */
2940  if (rinfo->ri_BatchSize > 0)
2941  ExplainPropertyInteger("Batch Size", NULL, rinfo->ri_BatchSize, es);
2942  }
2943 }
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
Definition: explain.c:4804
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 2816 of file postgres_fdw.c.

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

7237 {
7238  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7239  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7240  AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
7241  AppendState *requestor = (AppendState *) areq->requestor;
7242  WaitEventSet *set = requestor->as_eventset;
7243 
7244  /* This should not be called unless callback_pending */
7245  Assert(areq->callback_pending);
7246 
7247  /*
7248  * If process_pending_request() has been invoked on the given request
7249  * before we get here, we might have some tuples already; in which case
7250  * complete the request
7251  */
7252  if (fsstate->next_tuple < fsstate->num_tuples)
7253  {
7255  if (areq->request_complete)
7256  return;
7257  Assert(areq->callback_pending);
7258  }
7259 
7260  /* We must have run out of tuples */
7261  Assert(fsstate->next_tuple >= fsstate->num_tuples);
7262 
7263  /* The core code would have registered postmaster death event */
7265 
7266  /* Begin an asynchronous data fetch if not already done */
7267  if (!pendingAreq)
7268  fetch_more_data_begin(areq);
7269  else if (pendingAreq->requestor != areq->requestor)
7270  {
7271  /*
7272  * This is the case when the in-process request was made by another
7273  * Append. Note that it might be useless to process the request made
7274  * by that Append, because the query might not need tuples from that
7275  * Append anymore; so we avoid processing it to begin a fetch for the
7276  * given request if possible. If there are any child subplans of the
7277  * same parent that are ready for new requests, skip the given
7278  * request. Likewise, if there are any configured events other than
7279  * the postmaster death event, skip it. Otherwise, process the
7280  * in-process request, then begin a fetch to configure the event
7281  * below, because we might otherwise end up with no configured events
7282  * other than the postmaster death event.
7283  */
7284  if (!bms_is_empty(requestor->as_needrequest))
7285  return;
7286  if (GetNumRegisteredWaitEvents(set) > 1)
7287  return;
7288  process_pending_request(pendingAreq);
7289  fetch_more_data_begin(areq);
7290  }
7291  else if (pendingAreq->requestee != areq->requestee)
7292  {
7293  /*
7294  * This is the case when the in-process request was made by the same
7295  * parent but for a different child. Since we configure only the
7296  * event for the request made for that child, skip the given request.
7297  */
7298  return;
7299  }
7300  else
7301  Assert(pendingAreq == areq);
7302 
7303  AddWaitEventToSet(set, WL_SOCKET_READABLE, PQsocket(fsstate->conn),
7304  NULL, areq);
7305 }
int PQsocket(const PGconn *conn)
Definition: fe-connect.c:7173
int GetNumRegisteredWaitEvents(WaitEventSet *set)
Definition: latch.c:2230
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:1452
struct WaitEventSet * as_eventset
Definition: execnodes.h:1453
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 7313 of file postgres_fdw.c.

7314 {
7315  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7316  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7317 
7318  /* The core code would have initialized the callback_pending flag */
7319  Assert(!areq->callback_pending);
7320 
7321  /*
7322  * If process_pending_request() has been invoked on the given request
7323  * before we get here, we might have some tuples already; in which case
7324  * produce the next tuple
7325  */
7326  if (fsstate->next_tuple < fsstate->num_tuples)
7327  {
7328  produce_tuple_asynchronously(areq, true);
7329  return;
7330  }
7331 
7332  /* We must have run out of tuples */
7333  Assert(fsstate->next_tuple >= fsstate->num_tuples);
7334 
7335  /* The request should be currently in-process */
7336  Assert(fsstate->conn_state->pendingAreq == areq);
7337 
7338  /* On error, report the original query, not the FETCH. */
7339  if (!PQconsumeInput(fsstate->conn))
7340  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7341 
7342  fetch_more_data(node);
7343 
7344  produce_tuple_asynchronously(areq, true);
7345 }
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 7226 of file postgres_fdw.c.

7227 {
7228  produce_tuple_asynchronously(areq, true);
7229 }

References produce_tuple_asynchronously().

Referenced by postgres_fdw_handler().

◆ postgresGetAnalyzeInfoForForeignTable()

static double postgresGetAnalyzeInfoForForeignTable ( Relation  relation,
bool can_tablesample 
)
static

Definition at line 4986 of file postgres_fdw.c.

4987 {
4988  ForeignTable *table;
4989  UserMapping *user;
4990  PGconn *conn;
4991  StringInfoData sql;
4992  PGresult *volatile res = NULL;
4993  volatile double reltuples = -1;
4994  volatile char relkind = 0;
4995 
4996  /* assume the remote relation does not support TABLESAMPLE */
4997  *can_tablesample = false;
4998 
4999  /*
5000  * Get the connection to use. We do the remote access as the table's
5001  * owner, even if the ANALYZE was started by some other user.
5002  */
5003  table = GetForeignTable(RelationGetRelid(relation));
5004  user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5005  conn = GetConnection(user, false, NULL);
5006 
5007  /*
5008  * Construct command to get page count for relation.
5009  */
5010  initStringInfo(&sql);
5011  deparseAnalyzeInfoSql(&sql, relation);
5012 
5013  /* In what follows, do not risk leaking any PGresults. */
5014  PG_TRY();
5015  {
5016  res = pgfdw_exec_query(conn, sql.data, NULL);
5018  pgfdw_report_error(ERROR, res, conn, false, sql.data);
5019 
5020  if (PQntuples(res) != 1 || PQnfields(res) != 2)
5021  elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
5022  reltuples = strtod(PQgetvalue(res, 0, 0), NULL);
5023  relkind = *(PQgetvalue(res, 0, 1));
5024  }
5025  PG_FINALLY();
5026  {
5027  if (res)
5028  PQclear(res);
5029  }
5030  PG_END_TRY();
5031 
5033 
5034  /* TABLESAMPLE is supported only for regular tables and matviews */
5035  *can_tablesample = (relkind == RELKIND_RELATION ||
5036  relkind == RELKIND_MATVIEW ||
5037  relkind == RELKIND_PARTITIONED_TABLE);
5038 
5039  return reltuples;
5040 }
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 6317 of file postgres_fdw.c.

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

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

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:5822
Plan * change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe)
Definition: createplan.c:2152
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:1861
List * joinqual
Definition: plannodes.h:793
JoinType jointype
Definition: plannodes.h:791
bool parallel_safe
Definition: pathnodes.h:1644
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:5239
#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:975
Oid userid
Definition: pathnodes.h:956
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 6698 of file postgres_fdw.c.

6701 {
6702  PgFdwRelationInfo *fpinfo;
6703 
6704  /*
6705  * If input rel is not safe to pushdown, then simply return as we cannot
6706  * perform any post-join operations on the foreign server.
6707  */
6708  if (!input_rel->fdw_private ||
6709  !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
6710  return;
6711 
6712  /* Ignore stages we don't support; and skip any duplicate calls. */
6713  if ((stage != UPPERREL_GROUP_AGG &&
6714  stage != UPPERREL_ORDERED &&
6715  stage != UPPERREL_FINAL) ||
6716  output_rel->fdw_private)
6717  return;
6718 
6719  fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo));
6720  fpinfo->pushdown_safe = false;
6721  fpinfo->stage = stage;
6722  output_rel->fdw_private = fpinfo;
6723 
6724  switch (stage)
6725  {
6726  case UPPERREL_GROUP_AGG:
6727  add_foreign_grouping_paths(root, input_rel, output_rel,
6728  (GroupPathExtraData *) extra);
6729  break;
6730  case UPPERREL_ORDERED:
6731  add_foreign_ordered_paths(root, input_rel, output_rel);
6732  break;
6733  case UPPERREL_FINAL:
6734  add_foreign_final_paths(root, input_rel, output_rel,
6735  (FinalPathExtraData *) extra);
6736  break;
6737  default:
6738  elog(ERROR, "unexpected upper relation: %d", (int) stage);
6739  break;
6740  }
6741 }
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 5435 of file postgres_fdw.c.

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

7214 {
7215  RelOptInfo *rel = ((Path *) path)->parent;
7216  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7217 
7218  return fpinfo->async_capable;
7219 }

References PgFdwRelationInfo::async_capable.

Referenced by postgres_fdw_handler().

◆ postgresIsForeignRelUpdatable()

static int postgresIsForeignRelUpdatable ( Relation  rel)
static

Definition at line 2298 of file postgres_fdw.c.

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

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

2750 {
2752  EState *estate = node->ss.ps.state;
2753  ResultRelInfo *resultRelInfo = node->resultRelInfo;
2754 
2755  /*
2756  * If this is the first call after Begin, execute the statement.
2757  */
2758  if (dmstate->num_tuples == -1)
2759  execute_dml_stmt(node);
2760 
2761  /*
2762  * If the local query doesn't specify RETURNING, just clear tuple slot.
2763  */
2764  if (!resultRelInfo->ri_projectReturning)
2765  {
2766  TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
2767  Instrumentation *instr = node->ss.ps.instrument;
2768 
2769  Assert(!dmstate->has_returning);
2770 
2771  /* Increment the command es_processed count if necessary. */
2772  if (dmstate->set_processed)
2773  estate->es_processed += dmstate->num_tuples;
2774 
2775  /* Increment the tuple count for EXPLAIN ANALYZE if necessary. */
2776  if (instr)
2777  instr->tuplecount += dmstate->num_tuples;
2778 
2779  return ExecClearTuple(slot);
2780  }
2781 
2782  /*
2783  * Get the next RETURNING tuple.
2784  */
2785  return get_returning_data(node);
2786 }
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 2434 of file postgres_fdw.c.

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

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

2343 {
2344  Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
2346  TupleTableSlot *result;
2347 
2348  /* For base foreign relations, it suffices to set fdw_recheck_quals */
2349  if (scanrelid > 0)
2350  return true;
2351 
2352  Assert(outerPlan != NULL);
2353 
2354  /* Execute a local join execution plan */
2355  result = ExecProcNode(outerPlan);
2356  if (TupIsNull(result))
2357  return false;
2358 
2359  /* Store result in the given slot */
2360  ExecCopySlot(slot, result);
2361 
2362  return true;
2363 }
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, rewinding it should
1666  * be good enough. If we've only fetched zero or one batch, we needn't
1667  * even rewind the cursor, just rescan what we have.
1668  */
1669  if (node->ss.ps.chgParam != NULL)
1670  {
1671  fsstate->cursor_exists = false;
1672  snprintf(sql, sizeof(sql), "CLOSE c%u",
1673  fsstate->cursor_number);
1674  }
1675  else if (fsstate->fetch_ct_2 > 1)
1676  {
1677  snprintf(sql, sizeof(sql), "MOVE BACKWARD ALL IN c%u",
1678  fsstate->cursor_number);
1679  }
1680  else
1681  {
1682  /* Easy: just rescan what we already have in memory, if anything */
1683  fsstate->next_tuple = 0;
1684  return;
1685  }
1686 
1687  /*
1688  * We don't use a PG_TRY block here, so be careful not to throw error
1689  * without releasing the PGresult.
1690  */
1691  res = pgfdw_exec_query(fsstate->conn, sql, fsstate->conn_state);
1693  pgfdw_report_error(ERROR, res, fsstate->conn, true, sql);
1694  PQclear(res);
1695 
1696  /* Now force a fresh FETCH. */
1697  fsstate->tuples = NULL;
1698  fsstate->num_tuples = 0;
1699  fsstate->next_tuple = 0;
1700  fsstate->fetch_ct_2 = 0;
1701  fsstate->eof_reached = false;
1702 }
Bitmapset * chgParam
Definition: execnodes.h:1149

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(), 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 4196 of file postgres_fdw.c.

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

4842 {
4843  int i;
4844  ListCell *lc;
4845 
4846  Assert(numParams > 0);
4847 
4848  /* Prepare for output conversion of parameters used in remote query. */
4849  *param_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * numParams);
4850 
4851  i = 0;
4852  foreach(lc, fdw_exprs)
4853  {
4854  Node *param_expr = (Node *) lfirst(lc);
4855  Oid typefnoid;
4856  bool isvarlena;
4857 
4858  getTypeOutputInfo(exprType(param_expr), &typefnoid, &isvarlena);
4859  fmgr_info(typefnoid, &(*param_flinfo)[i]);
4860  i++;
4861  }
4862 
4863  /*
4864  * Prepare remote-parameter expressions for evaluation. (Note: in
4865  * practice, we expect that all these expressions will be just Params, so
4866  * we could possibly do something more efficient than using the full
4867  * expression-eval machinery for this. But probably there would be little
4868  * benefit, and it'd require postgres_fdw to know more than is desirable
4869  * about Param evaluation.)
4870  */
4871  *param_exprs = ExecInitExprList(fdw_exprs, node);
4872 
4873  /* Allocate buffer for text form of query parameters. */
4874  *param_values = (const char **) palloc0(numParams * sizeof(char *));
4875 }
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 7449 of file postgres_fdw.c.

7450 {
7451  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7452  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7453 
7454  /* The request would have been pending for a callback */
7455  Assert(areq->callback_pending);
7456 
7457  /* The request should be currently in-process */
7458  Assert(fsstate->conn_state->pendingAreq == areq);
7459 
7460  fetch_more_data(node);
7461 
7462  /*
7463  * If we didn't get any tuples, must be end of data; complete the request
7464  * now. Otherwise, we postpone completing the request until we are called
7465  * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
7466  */
7467  if (fsstate->next_tuple >= fsstate->num_tuples)
7468  {
7469  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7470  areq->callback_pending = false;
7471  /* Mark the request as complete */
7472  ExecAsyncRequestDone(areq, NULL);
7473  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7474  ExecAsyncResponse(areq);
7475  }
7476 }
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 4881 of file postgres_fdw.c.

4885 {
4886  int nestlevel;
4887  int i;
4888  ListCell *lc;
4889 
4890  nestlevel = set_transmission_modes();
4891 
4892  i = 0;
4893  foreach(lc, param_exprs)
4894  {
4895  ExprState *expr_state = (ExprState *) lfirst(lc);
4896  Datum expr_value;
4897  bool isNull;
4898 
4899  /* Evaluate the parameter expression */
4900  expr_value = ExecEvalExpr(expr_state, econtext, &isNull);
4901 
4902  /*
4903  * Get string representation of each parameter value by invoking
4904  * type-specific output function, unless the value is null.
4905  */
4906  if (isNull)
4907  param_values[i] = NULL;
4908  else
4909  param_values[i] = OutputFunctionCall(&param_flinfo[i], expr_value);
4910 
4911  i++;
4912  }
4913 
4914  reset_transmission_modes(nestlevel);
4915 }
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 7351 of file postgres_fdw.c.

7352 {
7353  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7354  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7355  AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
7356  TupleTableSlot *result;
7357 
7358  /* This should not be called if the request is currently in-process */
7359  Assert(areq != pendingAreq);
7360 
7361  /* Fetch some more tuples, if we've run out */
7362  if (fsstate->next_tuple >= fsstate->num_tuples)
7363  {
7364  /* No point in another fetch if we already detected EOF, though */
7365  if (!fsstate->eof_reached)
7366  {
7367  /* Mark the request as pending for a callback */
7369  /* Begin another fetch if requested and if no pending request */
7370  if (fetch && !pendingAreq)
7371  fetch_more_data_begin(areq);
7372  }
7373  else
7374  {
7375  /* There's nothing more to do; just return a NULL pointer */
7376  result = NULL;
7377  /* Mark the request as complete */
7378  ExecAsyncRequestDone(areq, result);
7379  }
7380  return;
7381  }
7382 
7383  /* Get a tuple from the ForeignScan node */
7384  result = areq->requestee->ExecProcNodeReal(areq->requestee);
7385  if (!TupIsNull(result))
7386  {
7387  /* Mark the request as complete */
7388  ExecAsyncRequestDone(areq, result);
7389  return;
7390  }
7391 
7392  /* We must have run out of tuples */
7393  Assert(fsstate->next_tuple >= fsstate->num_tuples);
7394 
7395  /* Fetch some more tuples, if we've not detected EOF yet */
7396  if (!fsstate->eof_reached)
7397  {
7398  /* Mark the request as pending for a callback */
7400  /* Begin another fetch if requested and if no pending request */
7401  if (fetch && !pendingAreq)
7402  fetch_more_data_begin(areq);
7403  }
7404  else
7405  {
7406  /* There's nothing more to do; just return a NULL pointer */
7407  result = NULL;
7408  /* Mark the request as complete */
7409  ExecAsyncRequestDone(areq, result);
7410  }
7411 }
void ExecAsyncRequestPending(AsyncRequest *areq)
Definition: execAsync.c:149
ExecProcNodeMtd ExecProcNodeReal
Definition: execnodes.h:1124

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

4512 {
4513  List *new_tlist = tlist;
4514  List *old_tlist = fscan->fdw_scan_tlist;
4515  ListCell *lc;
4516 
4517  foreach(lc, old_tlist)
4518  {
4519  TargetEntry *tle = (TargetEntry *) lfirst(lc);
4520 
4521  if (tlist_member(tle->expr, new_tlist))
4522  continue; /* already got it */
4523 
4524  new_tlist = lappend(new_tlist,
4525  makeTargetEntry(tle->expr,
4526  list_length(new_tlist) + 1,
4527  NULL,
4528  false));
4529  }
4530  fscan->fdw_scan_tlist = new_tlist;
4531 }

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

3924 {
3925  AtEOXact_GUC(true, nestlevel);
3926 }
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2264

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

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

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

3888 {
3889  int nestlevel = NewGUCNestLevel();
3890 
3891  /*
3892  * The values set here should match what pg_dump does. See also
3893  * configure_remote_session in connection.c.
3894  */
3895  if (DateStyle != USE_ISO_DATES)
3896  (void) set_config_option("datestyle", "ISO",
3898  GUC_ACTION_SAVE, true, 0, false);
3900  (void) set_config_option("intervalstyle", "postgres",
3902  GUC_ACTION_SAVE, true, 0, false);
3903  if (extra_float_digits < 3)
3904  (void) set_config_option("extra_float_digits", "3",
3906  GUC_ACTION_SAVE, true, 0, false);
3907 
3908  /*
3909  * In addition force restrictive search_path, in case there are any
3910  * regproc or similar constants to be printed.
3911  */
3912  (void) set_config_option("search_path", "pg_catalog",
3914  GUC_ACTION_SAVE, true, 0, false);
3915 
3916  return nestlevel;
3917 }
int extra_float_digits
Definition: float.c:41
int DateStyle
Definition: globals.c:122
int IntervalStyle
Definition: globals.c:124
int NewGUCNestLevel(void)
Definition: guc.c:2237
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:3343
@ 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 4330 of file postgres_fdw.c.

4332 {
4333  PG_TRY();
4334  {
4335  HeapTuple newtup;
4336 
4337  newtup = make_tuple_from_result_row(res, 0,
4338  fmstate->rel,
4339  fmstate->attinmeta,
4340  fmstate->retrieved_attrs,
4341  NULL,
4342  fmstate->temp_cxt);
4343 
4344  /*
4345  * The returning slot will not necessarily be suitable to store
4346  * heaptuples directly, so allow for conversion.
4347  */
4348  ExecForceStoreHeapTuple(newtup, slot, true);
4349  }
4350  PG_CATCH();
4351  {
4352  PQclear(res);
4353  PG_RE_THROW();
4354  }
4355  PG_END_TRY();
4356 }
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.