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.01
 
#define DEFAULT_FDW_SORT_MULTIPLIER   1.2
 

Typedefs

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

Enumerations

enum  FdwScanPrivateIndex { FdwScanPrivateSelectSql , FdwScanPrivateRetrievedAttrs , FdwScanPrivateFetchSize , FdwScanPrivateRelations }
 
enum  FdwModifyPrivateIndex {
  FdwModifyPrivateUpdateSql , FdwModifyPrivateTargetAttnums , 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)
 
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)
 
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.01

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

6886 {
6887  Query *parse = root->parse;
6888  PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6889  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
6890  bool has_final_sort = false;
6891  List *pathkeys = NIL;
6892  PgFdwPathExtraData *fpextra;
6893  bool save_use_remote_estimate = false;
6894  double rows;
6895  int width;
6896  Cost startup_cost;
6897  Cost total_cost;
6898  List *fdw_private;
6899  ForeignPath *final_path;
6900 
6901  /*
6902  * Currently, we only support this for SELECT commands
6903  */
6904  if (parse->commandType != CMD_SELECT)
6905  return;
6906 
6907  /*
6908  * No work if there is no FOR UPDATE/SHARE clause and if there is no need
6909  * to add a LIMIT node
6910  */
6911  if (!parse->rowMarks && !extra->limit_needed)
6912  return;
6913 
6914  /* We don't support cases where there are any SRFs in the targetlist */
6915  if (parse->hasTargetSRFs)
6916  return;
6917 
6918  /* Save the input_rel as outerrel in fpinfo */
6919  fpinfo->outerrel = input_rel;
6920 
6921  /*
6922  * Copy foreign table, foreign server, user mapping, FDW options etc.
6923  * details from the input relation's fpinfo.
6924  */
6925  fpinfo->table = ifpinfo->table;
6926  fpinfo->server = ifpinfo->server;
6927  fpinfo->user = ifpinfo->user;
6928  merge_fdw_options(fpinfo, ifpinfo, NULL);
6929 
6930  /*
6931  * If there is no need to add a LIMIT node, there might be a ForeignPath
6932  * in the input_rel's pathlist that implements all behavior of the query.
6933  * Note: we would already have accounted for the query's FOR UPDATE/SHARE
6934  * (if any) before we get here.
6935  */
6936  if (!extra->limit_needed)
6937  {
6938  ListCell *lc;
6939 
6940  Assert(parse->rowMarks);
6941 
6942  /*
6943  * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
6944  * so the input_rel should be a base, join, or ordered relation; and
6945  * if it's an ordered relation, its input relation should be a base or
6946  * join relation.
6947  */
6948  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
6949  input_rel->reloptkind == RELOPT_JOINREL ||
6950  (input_rel->reloptkind == RELOPT_UPPER_REL &&
6951  ifpinfo->stage == UPPERREL_ORDERED &&
6952  (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
6953  ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
6954 
6955  foreach(lc, input_rel->pathlist)
6956  {
6957  Path *path = (Path *) lfirst(lc);
6958 
6959  /*
6960  * apply_scanjoin_target_to_paths() uses create_projection_path()
6961  * to adjust each of its input paths if needed, whereas
6962  * create_ordered_paths() uses apply_projection_to_path() to do
6963  * that. So the former might have put a ProjectionPath on top of
6964  * the ForeignPath; look through ProjectionPath and see if the
6965  * path underneath it is ForeignPath.
6966  */
6967  if (IsA(path, ForeignPath) ||
6968  (IsA(path, ProjectionPath) &&
6969  IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
6970  {
6971  /*
6972  * Create foreign final path; this gets rid of a
6973  * no-longer-needed outer plan (if any), which makes the
6974  * EXPLAIN output look cleaner
6975  */
6976  final_path = create_foreign_upper_path(root,
6977  path->parent,
6978  path->pathtarget,
6979  path->rows,
6980  path->startup_cost,
6981  path->total_cost,
6982  path->pathkeys,
6983  NULL, /* no extra plan */
6984  NULL); /* no fdw_private */
6985 
6986  /* and add it to the final_rel */
6987  add_path(final_rel, (Path *) final_path);
6988 
6989  /* Safe to push down */
6990  fpinfo->pushdown_safe = true;
6991 
6992  return;
6993  }
6994  }
6995 
6996  /*
6997  * If we get here it means no ForeignPaths; since we would already
6998  * have considered pushing down all operations for the query to the
6999  * remote server, give up on it.
7000  */
7001  return;
7002  }
7003 
7004  Assert(extra->limit_needed);
7005 
7006  /*
7007  * If the input_rel is an ordered relation, replace the input_rel with its
7008  * input relation
7009  */
7010  if (input_rel->reloptkind == RELOPT_UPPER_REL &&
7011  ifpinfo->stage == UPPERREL_ORDERED)
7012  {
7013  input_rel = ifpinfo->outerrel;
7014  ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
7015  has_final_sort = true;
7016  pathkeys = root->sort_pathkeys;
7017  }
7018 
7019  /* The input_rel should be a base, join, or grouping relation */
7020  Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7021  input_rel->reloptkind == RELOPT_JOINREL ||
7022  (input_rel->reloptkind == RELOPT_UPPER_REL &&
7023  ifpinfo->stage == UPPERREL_GROUP_AGG));
7024 
7025  /*
7026  * We try to create a path below by extending a simple foreign path for
7027  * the underlying base, join, or grouping relation to perform the final
7028  * sort (if has_final_sort) and the LIMIT restriction remotely, which is
7029  * stored into the fdw_private list of the resulting path. (We
7030  * re-estimate the costs of sorting the underlying relation, if
7031  * has_final_sort.)
7032  */
7033 
7034  /*
7035  * Assess if it is safe to push down the LIMIT and OFFSET to the remote
7036  * server
7037  */
7038 
7039  /*
7040  * If the underlying relation has any local conditions, the LIMIT/OFFSET
7041  * cannot be pushed down.
7042  */
7043  if (ifpinfo->local_conds)
7044  return;
7045 
7046  /*
7047  * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
7048  * not safe to remote.
7049  */
7050  if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
7051  !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
7052  return;
7053 
7054  /* Safe to push down */
7055  fpinfo->pushdown_safe = true;
7056 
7057  /* Construct PgFdwPathExtraData */
7058  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
7059  fpextra->target = root->upper_targets[UPPERREL_FINAL];
7060  fpextra->has_final_sort = has_final_sort;
7061  fpextra->has_limit = extra->limit_needed;
7062  fpextra->limit_tuples = extra->limit_tuples;
7063  fpextra->count_est = extra->count_est;
7064  fpextra->offset_est = extra->offset_est;
7065 
7066  /*
7067  * Estimate the costs of performing the final sort and the LIMIT
7068  * restriction remotely. If has_final_sort is false, we wouldn't need to
7069  * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
7070  * roughly estimated using the costs we already have for the underlying
7071  * relation, in the same way as when use_remote_estimate is false. Since
7072  * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
7073  * false in that case.
7074  */
7075  if (!fpextra->has_final_sort)
7076  {
7077  save_use_remote_estimate = ifpinfo->use_remote_estimate;
7078  ifpinfo->use_remote_estimate = false;
7079  }
7080  estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
7081  &rows, &width, &startup_cost, &total_cost);
7082  if (!fpextra->has_final_sort)
7083  ifpinfo->use_remote_estimate = save_use_remote_estimate;
7084 
7085  /*
7086  * Build the fdw_private list that will be used by postgresGetForeignPlan.
7087  * Items in the list must match order in enum FdwPathPrivateIndex.
7088  */
7089  fdw_private = list_make2(makeBoolean(has_final_sort),
7090  makeBoolean(extra->limit_needed));
7091 
7092  /*
7093  * Create foreign final path; this gets rid of a no-longer-needed outer
7094  * plan (if any), which makes the EXPLAIN output look cleaner
7095  */
7096  final_path = create_foreign_upper_path(root,
7097  input_rel,
7098  root->upper_targets[UPPERREL_FINAL],
7099  rows,
7100  startup_cost,
7101  total_cost,
7102  pathkeys,
7103  NULL, /* no extra plan */
7104  fdw_private);
7105 
7106  /* and add it to the final_rel */
7107  add_path(final_rel, (Path *) final_path);
7108 }
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:240
Assert(fmt[strlen(fmt) - 1] !='\n')
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:241
void * palloc0(Size size)
Definition: mcxt.c:1241
#define IsA(nodeptr, _type_)
Definition: nodes.h:179
double Cost
Definition: nodes.h:262
@ CMD_SELECT
Definition: nodes.h:276
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_private)
Definition: pathnode.c:2320
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
@ UPPERREL_GROUP_AGG
Definition: pathnodes.h:74
@ UPPERREL_FINAL
Definition: pathnodes.h:79
@ UPPERREL_ORDERED
Definition: pathnodes.h:78
@ RELOPT_BASEREL
Definition: pathnodes.h:818
@ RELOPT_UPPER_REL
Definition: pathnodes.h:822
@ RELOPT_JOINREL
Definition: pathnodes.h:819
#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)
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:717
Cardinality limit_tuples
Definition: pathnodes.h:3250
Definition: pg_list.h:54
List * pathkeys
Definition: pathnodes.h:1639
Cardinality rows
Definition: pathnodes.h:1634
Cost startup_cost
Definition: pathnodes.h:1635
Cost total_cost
Definition: pathnodes.h:1636
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 * sort_pathkeys
Definition: pathnodes.h:402
Query * parse
Definition: pathnodes.h:202
List * pathlist
Definition: pathnodes.h:889
RelOptKind reloptkind
Definition: pathnodes.h:856
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(), PlannerInfo::parse, Path::pathkeys, RelOptInfo::pathlist, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, Path::rows, PgFdwRelationInfo::server, PlannerInfo::sort_pathkeys, 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 6659 of file postgres_fdw.c.

6662 {
6663  Query *parse = root->parse;
6664  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6665  PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
6666  ForeignPath *grouppath;
6667  double rows;
6668  int width;
6669  Cost startup_cost;
6670  Cost total_cost;
6671 
6672  /* Nothing to be done, if there is no grouping or aggregation required. */
6673  if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
6674  !root->hasHavingQual)
6675  return;
6676 
6679 
6680  /* save the input_rel as outerrel in fpinfo */
6681  fpinfo->outerrel = input_rel;
6682 
6683  /*
6684  * Copy foreign table, foreign server, user mapping, FDW options etc.
6685  * details from the input relation's fpinfo.
6686  */
6687  fpinfo->table = ifpinfo->table;
6688  fpinfo->server = ifpinfo->server;
6689  fpinfo->user = ifpinfo->user;
6690  merge_fdw_options(fpinfo, ifpinfo, NULL);
6691 
6692  /*
6693  * Assess if it is safe to push down aggregation and grouping.
6694  *
6695  * Use HAVING qual from extra. In case of child partition, it will have
6696  * translated Vars.
6697  */
6698  if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
6699  return;
6700 
6701  /*
6702  * Compute the selectivity and cost of the local_conds, so we don't have
6703  * to do it over again for each path. (Currently we create just a single
6704  * path here, but in future it would be possible that we build more paths
6705  * such as pre-sorted paths as in postgresGetForeignPaths and
6706  * postgresGetForeignJoinPaths.) The best we can do for these conditions
6707  * is to estimate selectivity on the basis of local statistics.
6708  */
6709  fpinfo->local_conds_sel = clauselist_selectivity(root,
6710  fpinfo->local_conds,
6711  0,
6712  JOIN_INNER,
6713  NULL);
6714 
6715  cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6716 
6717  /* Estimate the cost of push down */
6718  estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
6719  &rows, &width, &startup_cost, &total_cost);
6720 
6721  /* Now update this information in the fpinfo */
6722  fpinfo->rows = rows;
6723  fpinfo->width = width;
6724  fpinfo->startup_cost = startup_cost;
6725  fpinfo->total_cost = total_cost;
6726 
6727  /* Create and add foreign path to the grouping relation. */
6728  grouppath = create_foreign_upper_path(root,
6729  grouped_rel,
6730  grouped_rel->reltarget,
6731  rows,
6732  startup_cost,
6733  total_cost,
6734  NIL, /* no pathkeys */
6735  NULL,
6736  NIL); /* no fdw_private */
6737 
6738  /* Add generated path into grouped_rel by add_path(). */
6739  add_path(grouped_rel, (Path *) grouppath);
6740 }
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:102
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition: costsize.c:4370
@ JOIN_INNER
Definition: nodes.h:304
@ PARTITIONWISE_AGGREGATE_FULL
Definition: pathnodes.h:3205
@ PARTITIONWISE_AGGREGATE_NONE
Definition: pathnodes.h:3204
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
PartitionwiseAggregateType patype
Definition: pathnodes.h:3234
Selectivity local_conds_sel
Definition: postgres_fdw.h:57
QualCost local_conds_cost
Definition: postgres_fdw.h:56
bool hasHavingQual
Definition: pathnodes.h:496
struct PathTarget * reltarget
Definition: pathnodes.h:884

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

Referenced by postgresGetForeignUpperPaths().

◆ add_foreign_ordered_paths()

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

Definition at line 6750 of file postgres_fdw.c.

6752 {
6753  Query *parse = root->parse;
6754  PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6755  PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
6756  PgFdwPathExtraData *fpextra;
6757  double rows;
6758  int width;
6759  Cost startup_cost;
6760  Cost total_cost;
6761  List *fdw_private;
6762  ForeignPath *ordered_path;
6763  ListCell *lc;
6764 
6765  /* Shouldn't get here unless the query has ORDER BY */
6766  Assert(parse->sortClause);
6767 
6768  /* We don't support cases where there are any SRFs in the targetlist */
6769  if (parse->hasTargetSRFs)
6770  return;
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  * If the input_rel is a base or join relation, we would already have
6786  * considered pushing down the final sort to the remote server when
6787  * creating pre-sorted foreign paths for that relation, because the
6788  * query_pathkeys is set to the root->sort_pathkeys in that case (see
6789  * standard_qp_callback()).
6790  */
6791  if (input_rel->reloptkind == RELOPT_BASEREL ||
6792  input_rel->reloptkind == RELOPT_JOINREL)
6793  {
6794  Assert(root->query_pathkeys == root->sort_pathkeys);
6795 
6796  /* Safe to push down if the query_pathkeys is safe to push down */
6797  fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
6798 
6799  return;
6800  }
6801 
6802  /* The input_rel should be a grouping relation */
6803  Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
6804  ifpinfo->stage == UPPERREL_GROUP_AGG);
6805 
6806  /*
6807  * We try to create a path below by extending a simple foreign path for
6808  * the underlying grouping relation to perform the final sort remotely,
6809  * which is stored into the fdw_private list of the resulting path.
6810  */
6811 
6812  /* Assess if it is safe to push down the final sort */
6813  foreach(lc, root->sort_pathkeys)
6814  {
6815  PathKey *pathkey = (PathKey *) lfirst(lc);
6816  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6817 
6818  /*
6819  * is_foreign_expr would detect volatile expressions as well, but
6820  * checking ec_has_volatile here saves some cycles.
6821  */
6822  if (pathkey_ec->ec_has_volatile)
6823  return;
6824 
6825  /*
6826  * Can't push down the sort if pathkey's opfamily is not shippable.
6827  */
6828  if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId,
6829  fpinfo))
6830  return;
6831 
6832  /*
6833  * The EC must contain a shippable EM that is computed in input_rel's
6834  * reltarget, else we can't push down the sort.
6835  */
6836  if (find_em_for_rel_target(root,
6837  pathkey_ec,
6838  input_rel) == NULL)
6839  return;
6840  }
6841 
6842  /* Safe to push down */
6843  fpinfo->pushdown_safe = true;
6844 
6845  /* Construct PgFdwPathExtraData */
6846  fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6847  fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6848  fpextra->has_final_sort = true;
6849 
6850  /* Estimate the costs of performing the final sort remotely */
6851  estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6852  &rows, &width, &startup_cost, &total_cost);
6853 
6854  /*
6855  * Build the fdw_private list that will be used by postgresGetForeignPlan.
6856  * Items in the list must match order in enum FdwPathPrivateIndex.
6857  */
6858  fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
6859 
6860  /* Create foreign ordering path */
6861  ordered_path = create_foreign_upper_path(root,
6862  input_rel,
6863  root->upper_targets[UPPERREL_ORDERED],
6864  rows,
6865  startup_cost,
6866  total_cost,
6867  root->sort_pathkeys,
6868  NULL, /* no extra plan */
6869  fdw_private);
6870 
6871  /* and add it to the ordered_rel */
6872  add_path(ordered_rel, (Path *) ordered_path);
6873 }
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:1460
List * query_pathkeys
Definition: pathnodes.h:385

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(), PlannerInfo::parse, PathKey::pk_opfamily, PgFdwRelationInfo::pushdown_safe, PgFdwRelationInfo::qp_is_pushdown_safe, PlannerInfo::query_pathkeys, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, PgFdwRelationInfo::server, PlannerInfo::sort_pathkeys, 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 
)
static

Definition at line 5994 of file postgres_fdw.c.

5996 {
5997  List *useful_pathkeys_list = NIL; /* List of all pathkeys */
5998  ListCell *lc;
5999 
6000  useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
6001 
6002  /*
6003  * Before creating sorted paths, arrange for the passed-in EPQ path, if
6004  * any, to return columns needed by the parent ForeignScan node so that
6005  * they will propagate up through Sort nodes injected below, if necessary.
6006  */
6007  if (epq_path != NULL && useful_pathkeys_list != NIL)
6008  {
6009  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6010  PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6011 
6012  /* Include columns required for evaluating PHVs in the tlist. */
6014  pull_var_clause((Node *) target->exprs,
6016 
6017  /* Include columns required for evaluating the local conditions. */
6018  foreach(lc, fpinfo->local_conds)
6019  {
6020  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6021 
6023  pull_var_clause((Node *) rinfo->clause,
6025  }
6026 
6027  /*
6028  * If we have added any new columns, adjust the tlist of the EPQ path.
6029  *
6030  * Note: the plan created using this path will only be used to execute
6031  * EPQ checks, where accuracy of the plan cost and width estimates
6032  * would not be important, so we do not do set_pathtarget_cost_width()
6033  * for the new pathtarget here. See also postgresGetForeignPlan().
6034  */
6035  if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6036  {
6037  /* The EPQ path is a join path, so it is projection-capable. */
6039 
6040  /*
6041  * Use create_projection_path() here, so as to avoid modifying it
6042  * in place.
6043  */
6044  epq_path = (Path *) create_projection_path(root,
6045  rel,
6046  epq_path,
6047  target);
6048  }
6049  }
6050 
6051  /* Create one path for each set of pathkeys we found above. */
6052  foreach(lc, useful_pathkeys_list)
6053  {
6054  double rows;
6055  int width;
6056  Cost startup_cost;
6057  Cost total_cost;
6058  List *useful_pathkeys = lfirst(lc);
6059  Path *sorted_epq_path;
6060 
6061  estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
6062  &rows, &width, &startup_cost, &total_cost);
6063 
6064  /*
6065  * The EPQ path must be at least as well sorted as the path itself, in
6066  * case it gets used as input to a mergejoin.
6067  */
6068  sorted_epq_path = epq_path;
6069  if (sorted_epq_path != NULL &&
6070  !pathkeys_contained_in(useful_pathkeys,
6071  sorted_epq_path->pathkeys))
6072  sorted_epq_path = (Path *)
6073  create_sort_path(root,
6074  rel,
6075  sorted_epq_path,
6076  useful_pathkeys,
6077  -1.0);
6078 
6079  if (IS_SIMPLE_REL(rel))
6080  add_path(rel, (Path *)
6081  create_foreignscan_path(root, rel,
6082  NULL,
6083  rows,
6084  startup_cost,
6085  total_cost,
6086  useful_pathkeys,
6087  rel->lateral_relids,
6088  sorted_epq_path,
6089  NIL));
6090  else
6091  add_path(rel, (Path *)
6092  create_foreign_join_path(root, rel,
6093  NULL,
6094  rows,
6095  startup_cost,
6096  total_cost,
6097  useful_pathkeys,
6098  rel->lateral_relids,
6099  sorted_epq_path,
6100  NIL));
6101  }
6102 }
bool is_projection_capable_path(Path *path)
Definition: createplan.c:7158
#define PVC_RECURSE_PLACEHOLDERS
Definition: optimizer.h:188
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:340
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2953
ForeignPath * create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_private)
Definition: pathnode.c:2226
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2638
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_private)
Definition: pathnode.c:2270
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:830
#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:903
Definition: nodes.h:129
Relids lateral_relids
Definition: pathnodes.h:904
Expr * clause
Definition: pathnodes.h:2513
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(), and PVC_RECURSE_PLACEHOLDERS.

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

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

References cost_sort(), DEFAULT_FDW_SORT_MULTIPLIER, PlannerInfo::group_pathkeys, grouping_is_sortable(), pathkeys_contained_in(), PlannerInfo::processed_groupClause, 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 5366 of file postgres_fdw.c.

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

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

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

6111 {
6112  ListCell *lc;
6113 
6114  foreach(lc, fpinfo->server->options)
6115  {
6116  DefElem *def = (DefElem *) lfirst(lc);
6117 
6118  if (strcmp(def->defname, "use_remote_estimate") == 0)
6119  fpinfo->use_remote_estimate = defGetBoolean(def);
6120  else if (strcmp(def->defname, "fdw_startup_cost") == 0)
6121  (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
6122  NULL);
6123  else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
6124  (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
6125  NULL);
6126  else if (strcmp(def->defname, "extensions") == 0)
6127  fpinfo->shippable_extensions =
6128  ExtractExtensionList(defGetString(def), false);
6129  else if (strcmp(def->defname, "fetch_size") == 0)
6130  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6131  else if (strcmp(def->defname, "async_capable") == 0)
6132  fpinfo->async_capable = defGetBoolean(def);
6133  }
6134 }
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:437
bool defGetBoolean(DefElem *def)
Definition: define.c:108
char * defGetString(DefElem *def)
Definition: define.c:49
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:2824
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:2914
char * defname
Definition: parsenodes.h:810
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 6142 of file postgres_fdw.c.

6143 {
6144  ListCell *lc;
6145 
6146  foreach(lc, fpinfo->table->options)
6147  {
6148  DefElem *def = (DefElem *) lfirst(lc);
6149 
6150  if (strcmp(def->defname, "use_remote_estimate") == 0)
6151  fpinfo->use_remote_estimate = defGetBoolean(def);
6152  else if (strcmp(def->defname, "fetch_size") == 0)
6153  (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6154  else if (strcmp(def->defname, "async_capable") == 0)
6155  fpinfo->async_capable = defGetBoolean(def);
6156  }
6157 }
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 4408 of file postgres_fdw.c.

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

3932 {
3933  char sql[64];
3934  PGresult *res;
3935 
3936  snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3937 
3938  /*
3939  * We don't use a PG_TRY block here, so be careful not to throw error
3940  * without releasing the PGresult.
3941  */
3942  res = pgfdw_exec_query(conn, sql, conn_state);
3944  pgfdw_report_error(ERROR, res, conn, true, sql);
3945  PQclear(res);
3946 }
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:834
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:741
static unsigned int cursor_number
Definition: connection.c:78
#define ERROR
Definition: elog.h:39
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3240
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:97
#define snprintf
Definition: port.h:238
PGconn * conn
Definition: streamutil.c:54

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

7383 {
7384  /* The request would have been pending for a callback */
7385  Assert(areq->callback_pending);
7386 
7387  /* Unlike AsyncNotify, we unset callback_pending ourselves */
7388  areq->callback_pending = false;
7389 
7390  /* We begin a fetch afterwards if necessary; don't fetch */
7391  produce_tuple_asynchronously(areq, false);
7392 
7393  /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7394  ExecAsyncResponse(areq);
7395 
7396  /* Also, we do instrumentation ourselves, if required */
7397  if (areq->requestee->instrument)
7399  TupIsNull(areq->result) ? 0.0 : 1.0);
7400 }
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:600
bool callback_pending
Definition: execnodes.h:598
struct PlanState * requestee
Definition: execnodes.h:596
Instrumentation * instrument
Definition: execnodes.h:1045
#define TupIsNull(slot)
Definition: tuptable.h:300

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

7574 {
7576  Relation rel = errpos->rel;
7577  ForeignScanState *fsstate = errpos->fsstate;
7578  const char *attname = NULL;
7579  const char *relname = NULL;
7580  bool is_wholerow = false;
7581 
7582  /*
7583  * If we're in a scan node, always use aliases from the rangetable, for
7584  * consistency between the simple-relation and remote-join cases. Look at
7585  * the relation's tupdesc only if we're not in a scan node.
7586  */
7587  if (fsstate)
7588  {
7589  /* ForeignScan case */
7590  ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7591  int varno = 0;
7592  AttrNumber colno = 0;
7593 
7594  if (fsplan->scan.scanrelid > 0)
7595  {
7596  /* error occurred in a scan against a foreign table */
7597  varno = fsplan->scan.scanrelid;
7598  colno = errpos->cur_attno;
7599  }
7600  else
7601  {
7602  /* error occurred in a scan against a foreign join */
7603  TargetEntry *tle;
7604 
7605  tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
7606  errpos->cur_attno - 1);
7607 
7608  /*
7609  * Target list can have Vars and expressions. For Vars, we can
7610  * get some information, however for expressions we can't. Thus
7611  * for expressions, just show generic context message.
7612  */
7613  if (IsA(tle->expr, Var))
7614  {
7615  Var *var = (Var *) tle->expr;
7616 
7617  varno = var->varno;
7618  colno = var->varattno;
7619  }
7620  }
7621 
7622  if (varno > 0)
7623  {
7624  EState *estate = fsstate->ss.ps.state;
7625  RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7626 
7627  relname = rte->eref->aliasname;
7628 
7629  if (colno == 0)
7630  is_wholerow = true;
7631  else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7632  attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7633  else if (colno == SelfItemPointerAttributeNumber)
7634  attname = "ctid";
7635  }
7636  }
7637  else if (rel)
7638  {
7639  /* Non-ForeignScan case (we should always have a rel here) */
7640  TupleDesc tupdesc = RelationGetDescr(rel);
7641 
7643  if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7644  {
7645  Form_pg_attribute attr = TupleDescAttr(tupdesc,
7646  errpos->cur_attno - 1);
7647 
7648  attname = NameStr(attr->attname);
7649  }
7650  else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7651  attname = "ctid";
7652  }
7653 
7654  if (relname && is_wholerow)
7655  errcontext("whole-row reference to foreign table \"%s\"", relname);
7656  else if (relname && attname)
7657  errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7658  else
7659  errcontext("processing expression at position %d in select list",
7660  errpos->cur_attno);
7661 }
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:730
#define errcontext
Definition: elog.h:196
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:586
#define castNode(_type_, nodeptr)
Definition: nodes.h:197
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:537
char * aliasname
Definition: primnodes.h:42
List * colnames
Definition: primnodes.h:43
AttrNumber cur_attno
Definition: postgres_fdw.c:307
ForeignScanState * fsstate
Definition: postgres_fdw.c:309
ScanState ss
Definition: execnodes.h:1941
List * fdw_scan_tlist
Definition: plannodes.h:718
Plan * plan
Definition: execnodes.h:1035
EState * state
Definition: execnodes.h:1037
Alias * eref
Definition: parsenodes.h:1200
PlanState ps
Definition: execnodes.h:1461
Index scanrelid
Definition: plannodes.h:390
Expr * expr
Definition: primnodes.h:1731
#define strVal(v)
Definition: value.h:82

References Alias::aliasname, arg, attname, castNode, Alias::colnames, ConversionLocation::cur_attno, RangeTblEntry::eref, 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 4249 of file postgres_fdw.c.

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

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

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

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

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

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

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

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

3680 {
3682  Expr *expr = em->em_expr;
3683 
3684  /*
3685  * If we've identified what we're processing in the current scan, we only
3686  * want to match that expression.
3687  */
3688  if (state->current != NULL)
3689  return equal(expr, state->current);
3690 
3691  /*
3692  * Otherwise, ignore anything we've already processed.
3693  */
3694  if (list_member(state->already_used, expr))
3695  return false;
3696 
3697  /* This is the new target to process. */
3698  state->current = expr;
3699  return true;
3700 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
bool list_member(const List *list, const void *datum)
Definition: list.c:660
Definition: regguts.h:318

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

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

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

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

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

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

7323 {
7324  ForeignScanState *node = (ForeignScanState *) areq->requestee;
7325  PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7326  char sql[64];
7327 
7328  Assert(!fsstate->conn_state->pendingAreq);
7329 
7330  /* Create the cursor synchronously. */
7331  if (!fsstate->cursor_exists)
7332  create_cursor(node);
7333 
7334  /* We will send this query, but not wait for the response. */
7335  snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7336  fsstate->fetch_size, fsstate->cursor_number);
7337 
7338  if (!PQsendQuery(fsstate->conn, sql))
7339  pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7340 
7341  /* Remember that the request is in process */
7342  fsstate->conn_state->pendingAreq = areq;
7343 }
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1418
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 7676 of file postgres_fdw.c.

7677 {
7678  ListCell *lc;
7679 
7680  foreach(lc, ec->ec_members)
7681  {
7683 
7684  /*
7685  * Note we require !bms_is_empty, else we'd accept constant
7686  * expressions which are not suitable for the purpose.
7687  */
7688  if (bms_is_subset(em->em_relids, rel->relids) &&
7689  !bms_is_empty(em->em_relids) &&
7690  is_foreign_expr(root, rel, em->em_expr))
7691  return em;
7692  }
7693 
7694  return NULL;
7695 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:332
#define bms_is_empty(a)
Definition: bitmapset.h:105
Relids relids
Definition: pathnodes.h:862

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

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

7711 {
7712  PathTarget *target = rel->reltarget;
7713  ListCell *lc1;
7714  int i;
7715 
7716  i = 0;
7717  foreach(lc1, target->exprs)
7718  {
7719  Expr *expr = (Expr *) lfirst(lc1);
7720  Index sgref = get_pathtarget_sortgroupref(target, i);
7721  ListCell *lc2;
7722 
7723  /* Ignore non-sort expressions */
7724  if (sgref == 0 ||
7726  root->parse->sortClause) == NULL)
7727  {
7728  i++;
7729  continue;
7730  }
7731 
7732  /* We ignore binary-compatible relabeling on both ends */
7733  while (expr && IsA(expr, RelabelType))
7734  expr = ((RelabelType *) expr)->arg;
7735 
7736  /* Locate an EquivalenceClass member matching this expr, if any */
7737  foreach(lc2, ec->ec_members)
7738  {
7740  Expr *em_expr;
7741 
7742  /* Don't match constants */
7743  if (em->em_is_const)
7744  continue;
7745 
7746  /* Ignore child members */
7747  if (em->em_is_child)
7748  continue;
7749 
7750  /* Match if same expression (after stripping relabel) */
7751  em_expr = em->em_expr;
7752  while (em_expr && IsA(em_expr, RelabelType))
7753  em_expr = ((RelabelType *) em_expr)->arg;
7754 
7755  if (!equal(em_expr, expr))
7756  continue;
7757 
7758  /* Check that expression (including relabels!) is shippable */
7759  if (is_foreign_expr(root, rel, em->em_expr))
7760  return em;
7761  }
7762 
7763  i++;
7764  }
7765 
7766  return NULL;
7767 }
unsigned int Index
Definition: c.h:598
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1523
List * exprs
Definition: pathnodes.h:1507
List * sortClause
Definition: parsenodes.h:209
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, PlannerInfo::parse, RelOptInfo::reltarget, and Query::sortClause.

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

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

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

Referenced by postgresPlanDirectModify().

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4361 of file postgres_fdw.c.

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

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

6371 {
6372  Query *query = root->parse;
6373  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
6374  PathTarget *grouping_target = grouped_rel->reltarget;
6375  PgFdwRelationInfo *ofpinfo;
6376  ListCell *lc;
6377  int i;
6378  List *tlist = NIL;
6379 
6380  /* We currently don't support pushing Grouping Sets. */
6381  if (query->groupingSets)
6382  return false;
6383 
6384  /* Get the fpinfo of the underlying scan relation. */
6385  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
6386 
6387  /*
6388  * If underlying scan relation has any local conditions, those conditions
6389  * are required to be applied before performing aggregation. Hence the
6390  * aggregate cannot be pushed down.
6391  */
6392  if (ofpinfo->local_conds)
6393  return false;
6394 
6395  /*
6396  * Examine grouping expressions, as well as other expressions we'd need to
6397  * compute, and check whether they are safe to push down to the foreign
6398  * server. All GROUP BY expressions will be part of the grouping target
6399  * and thus there is no need to search for them separately. Add grouping
6400  * expressions into target list which will be passed to foreign server.
6401  *
6402  * A tricky fine point is that we must not put any expression into the
6403  * target list that is just a foreign param (that is, something that
6404  * deparse.c would conclude has to be sent to the foreign server). If we
6405  * do, the expression will also appear in the fdw_exprs list of the plan
6406  * node, and setrefs.c will get confused and decide that the fdw_exprs
6407  * entry is actually a reference to the fdw_scan_tlist entry, resulting in
6408  * a broken plan. Somewhat oddly, it's OK if the expression contains such
6409  * a node, as long as it's not at top level; then no match is possible.
6410  */
6411  i = 0;
6412  foreach(lc, grouping_target->exprs)
6413  {
6414  Expr *expr = (Expr *) lfirst(lc);
6415  Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
6416  ListCell *l;
6417 
6418  /*
6419  * Check whether this expression is part of GROUP BY clause. Note we
6420  * check the whole GROUP BY clause not just processed_groupClause,
6421  * because we will ship all of it, cf. appendGroupByClause.
6422  */
6423  if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
6424  {
6425  TargetEntry *tle;
6426 
6427  /*
6428  * If any GROUP BY expression is not shippable, then we cannot
6429  * push down aggregation to the foreign server.
6430  */
6431  if (!is_foreign_expr(root, grouped_rel, expr))
6432  return false;
6433 
6434  /*
6435  * If it would be a foreign param, we can't put it into the tlist,
6436  * so we have to fail.
6437  */
6438  if (is_foreign_param(root, grouped_rel, expr))
6439  return false;
6440 
6441  /*
6442  * Pushable, so add to tlist. We need to create a TLE for this
6443  * expression and apply the sortgroupref to it. We cannot use
6444  * add_to_flat_tlist() here because that avoids making duplicate
6445  * entries in the tlist. If there are duplicate entries with
6446  * distinct sortgrouprefs, we have to duplicate that situation in
6447  * the output tlist.
6448  */
6449  tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
6450  tle->ressortgroupref = sgref;
6451  tlist = lappend(tlist, tle);
6452  }
6453  else
6454  {
6455  /*
6456  * Non-grouping expression we need to compute. Can we ship it
6457  * as-is to the foreign server?
6458  */
6459  if (is_foreign_expr(root, grouped_rel, expr) &&
6460  !is_foreign_param(root, grouped_rel, expr))
6461  {
6462  /* Yes, so add to tlist as-is; OK to suppress duplicates */
6463  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6464  }
6465  else
6466  {
6467  /* Not pushable as a whole; extract its Vars and aggregates */
6468  List *aggvars;
6469 
6470  aggvars = pull_var_clause((Node *) expr,
6472 
6473  /*
6474  * If any aggregate expression is not shippable, then we
6475  * cannot push down aggregation to the foreign server. (We
6476  * don't have to check is_foreign_param, since that certainly
6477  * won't return true for any such expression.)
6478  */
6479  if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
6480  return false;
6481 
6482  /*
6483  * Add aggregates, if any, into the targetlist. Plain Vars
6484  * outside an aggregate can be ignored, because they should be
6485  * either same as some GROUP BY column or part of some GROUP
6486  * BY expression. In either case, they are already part of
6487  * the targetlist and thus no need to add them again. In fact
6488  * including plain Vars in the tlist when they do not match a
6489  * GROUP BY column would cause the foreign server to complain
6490  * that the shipped query is invalid.
6491  */
6492  foreach(l, aggvars)
6493  {
6494  Expr *aggref = (Expr *) lfirst(l);
6495 
6496  if (IsA(aggref, Aggref))
6497  tlist = add_to_flat_tlist(tlist, list_make1(aggref));
6498  }
6499  }
6500  }
6501 
6502  i++;
6503  }
6504 
6505  /*
6506  * Classify the pushable and non-pushable HAVING clauses and save them in
6507  * remote_conds and local_conds of the grouped rel's fpinfo.
6508  */
6509  if (havingQual)
6510  {
6511  foreach(lc, (List *) havingQual)
6512  {
6513  Expr *expr = (Expr *) lfirst(lc);
6514  RestrictInfo *rinfo;
6515 
6516  /*
6517  * Currently, the core code doesn't wrap havingQuals in
6518  * RestrictInfos, so we must make our own.
6519  */
6520  Assert(!IsA(expr, RestrictInfo));
6521  rinfo = make_restrictinfo(root,
6522  expr,
6523  true,
6524  false,
6525  root->qual_security_level,
6526  grouped_rel->relids,
6527  NULL);
6528  if (is_foreign_expr(root, grouped_rel, expr))
6529  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
6530  else
6531  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
6532  }
6533  }
6534 
6535  /*
6536  * If there are any local conditions, pull Vars and aggregates from it and
6537  * check whether they are safe to pushdown or not.
6538  */
6539  if (fpinfo->local_conds)
6540  {
6541  List *aggvars = NIL;
6542 
6543  foreach(lc, fpinfo->local_conds)
6544  {
6545  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
6546 
6547  aggvars = list_concat(aggvars,
6548  pull_var_clause((Node *) rinfo->clause,
6550  }
6551 
6552  foreach(lc, aggvars)
6553  {
6554  Expr *expr = (Expr *) lfirst(lc);
6555 
6556  /*
6557  * If aggregates within local conditions are not safe to push
6558  * down, then we cannot push down the query. Vars are already
6559  * part of GROUP BY clause which are checked above, so no need to
6560  * access them again here. Again, we need not check
6561  * is_foreign_param for a foreign aggregate.
6562  */
6563  if (IsA(expr, Aggref))
6564  {
6565  if (!is_foreign_expr(root, grouped_rel, expr))
6566  return false;
6567 
6568  tlist = add_to_flat_tlist(tlist, list_make1(expr));
6569  }
6570  }
6571  }
6572 
6573  /* Store generated targetlist */
6574  fpinfo->grouped_tlist = tlist;
6575 
6576  /* Safe to pushdown */
6577  fpinfo->pushdown_safe = true;
6578 
6579  /*
6580  * Set # of retrieved rows and cached relation costs to some negative
6581  * value, so that we can detect when they are set to some sensible values,
6582  * during one (usually the first) of the calls to estimate_path_cost_size.
6583  */
6584  fpinfo->retrieved_rows = -1;
6585  fpinfo->rel_startup_cost = -1;
6586  fpinfo->rel_total_cost = -1;
6587 
6588  /*
6589  * Set the string describing this grouped relation to be used in EXPLAIN
6590  * output of corresponding ForeignScan. Note that the decoration we add
6591  * to the base relation name mustn't include any digits, or it'll confuse
6592  * postgresExplainForeignScan.
6593  */
6594  fpinfo->relation_name = psprintf("Aggregate on (%s)",
6595  ofpinfo->relation_name);
6596 
6597  return true;
6598 }
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:1078
#define PVC_INCLUDE_AGGREGATES
Definition: optimizer.h:183
#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 pseudoconstant, Index security_level, Relids required_relids, Relids outer_relids)
Definition: restrictinfo.c:61
Index qual_security_level
Definition: pathnodes.h:489
List * groupClause
Definition: parsenodes.h:198
List * groupingSets
Definition: parsenodes.h:201
Index ressortgroupref
Definition: primnodes.h:1737
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, PlannerInfo::parse, psprintf(), pull_var_clause(), PgFdwRelationInfo::pushdown_safe, PVC_INCLUDE_AGGREGATES, PlannerInfo::qual_security_level, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_name, RelOptInfo::relids, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, TargetEntry::ressortgroupref, and PgFdwRelationInfo::retrieved_rows.

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

5735 {
5736  PgFdwRelationInfo *fpinfo;
5737  PgFdwRelationInfo *fpinfo_o;
5738  PgFdwRelationInfo *fpinfo_i;
5739  ListCell *lc;
5740  List *joinclauses;
5741 
5742  /*
5743  * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
5744  * Constructing queries representing SEMI and ANTI joins is hard, hence
5745  * not considered right now.
5746  */
5747  if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
5748  jointype != JOIN_RIGHT && jointype != JOIN_FULL)
5749  return false;
5750 
5751  /*
5752  * If either of the joining relations is marked as unsafe to pushdown, the
5753  * join can not be pushed down.
5754  */
5755  fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
5756  fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
5757  fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
5758  if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
5759  !fpinfo_i || !fpinfo_i->pushdown_safe)
5760  return false;
5761 
5762  /*
5763  * If joining relations have local conditions, those conditions are
5764  * required to be applied before joining the relations. Hence the join can
5765  * not be pushed down.
5766  */
5767  if (fpinfo_o->local_conds || fpinfo_i->local_conds)
5768  return false;
5769 
5770  /*
5771  * Merge FDW options. We might be tempted to do this after we have deemed
5772  * the foreign join to be OK. But we must do this beforehand so that we
5773  * know which quals can be evaluated on the foreign server, which might
5774  * depend on shippable_extensions.
5775  */
5776  fpinfo->server = fpinfo_o->server;
5777  merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
5778 
5779  /*
5780  * Separate restrict list into join quals and pushed-down (other) quals.
5781  *
5782  * Join quals belonging to an outer join must all be shippable, else we
5783  * cannot execute the join remotely. Add such quals to 'joinclauses'.
5784  *
5785  * Add other quals to fpinfo->remote_conds if they are shippable, else to
5786  * fpinfo->local_conds. In an inner join it's okay to execute conditions
5787  * either locally or remotely; the same is true for pushed-down conditions
5788  * at an outer join.
5789  *
5790  * Note we might return failure after having already scribbled on
5791  * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
5792  * won't consult those lists again if we deem the join unshippable.
5793  */
5794  joinclauses = NIL;
5795  foreach(lc, extra->restrictlist)
5796  {
5797  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
5798  bool is_remote_clause = is_foreign_expr(root, joinrel,
5799  rinfo->clause);
5800 
5801  if (IS_OUTER_JOIN(jointype) &&
5802  !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
5803  {
5804  if (!is_remote_clause)
5805  return false;
5806  joinclauses = lappend(joinclauses, rinfo);
5807  }
5808  else
5809  {
5810  if (is_remote_clause)
5811  fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
5812  else
5813  fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
5814  }
5815  }
5816 
5817  /*
5818  * deparseExplicitTargetList() isn't smart enough to handle anything other
5819  * than a Var. In particular, if there's some PlaceHolderVar that would
5820  * need to be evaluated within this join tree (because there's an upper
5821  * reference to a quantity that may go to NULL as a result of an outer
5822  * join), then we can't try to push the join down because we'll fail when
5823  * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
5824  * needs to be evaluated *at the top* of this join tree is OK, because we
5825  * can do that locally after fetching the results from the remote side.
5826  */
5827  foreach(lc, root->placeholder_list)
5828  {
5829  PlaceHolderInfo *phinfo = lfirst(lc);
5830  Relids relids;
5831 
5832  /* PlaceHolderInfo refers to parent relids, not child relids. */
5833  relids = IS_OTHER_REL(joinrel) ?
5834  joinrel->top_parent_relids : joinrel->relids;
5835 
5836  if (bms_is_subset(phinfo->ph_eval_at, relids) &&
5837  bms_nonempty_difference(relids, phinfo->ph_eval_at))
5838  return false;
5839  }
5840 
5841  /* Save the join clauses, for later use. */
5842  fpinfo->joinclauses = joinclauses;
5843 
5844  fpinfo->outerrel = outerrel;
5845  fpinfo->innerrel = innerrel;
5846  fpinfo->jointype = jointype;
5847 
5848  /*
5849  * By default, both the input relations are not required to be deparsed as
5850  * subqueries, but there might be some relations covered by the input
5851  * relations that are required to be deparsed as subqueries, so save the
5852  * relids of those relations for later use by the deparser.
5853  */
5854  fpinfo->make_outerrel_subquery = false;
5855  fpinfo->make_innerrel_subquery = false;
5856  Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
5857  Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
5859  fpinfo_i->lower_subquery_rels);
5860 
5861  /*
5862  * Pull the other remote conditions from the joining relations into join
5863  * clauses or other remote clauses (remote_conds) of this relation
5864  * wherever possible. This avoids building subqueries at every join step.
5865  *
5866  * For an inner join, clauses from both the relations are added to the
5867  * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
5868  * the outer side are added to remote_conds since those can be evaluated
5869  * after the join is evaluated. The clauses from inner side are added to
5870  * the joinclauses, since they need to be evaluated while constructing the
5871  * join.
5872  *
5873  * For a FULL OUTER JOIN, the other clauses from either relation can not
5874  * be added to the joinclauses or remote_conds, since each relation acts
5875  * as an outer relation for the other.
5876  *
5877  * The joining sides can not have local conditions, thus no need to test
5878  * shippability of the clauses being pulled up.
5879  */
5880  switch (jointype)
5881  {
5882  case JOIN_INNER:
5883  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5884  fpinfo_i->remote_conds);
5885  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5886  fpinfo_o->remote_conds);
5887  break;
5888 
5889  case JOIN_LEFT:
5890  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5891  fpinfo_i->remote_conds);
5892  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5893  fpinfo_o->remote_conds);
5894  break;
5895 
5896  case JOIN_RIGHT:
5897  fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
5898  fpinfo_o->remote_conds);
5899  fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
5900  fpinfo_i->remote_conds);
5901  break;
5902 
5903  case JOIN_FULL:
5904 
5905  /*
5906  * In this case, if any of the input relations has conditions, we
5907  * need to deparse that relation as a subquery so that the
5908  * conditions can be evaluated before the join. Remember it in
5909  * the fpinfo of this relation so that the deparser can take
5910  * appropriate action. Also, save the relids of base relations
5911  * covered by that relation for later use by the deparser.
5912  */
5913  if (fpinfo_o->remote_conds)
5914  {
5915  fpinfo->make_outerrel_subquery = true;
5916  fpinfo->lower_subquery_rels =
5918  outerrel->relids);
5919  }
5920  if (fpinfo_i->remote_conds)
5921  {
5922  fpinfo->make_innerrel_subquery = true;
5923  fpinfo->lower_subquery_rels =
5925  innerrel->relids);
5926  }
5927  break;
5928 
5929  default:
5930  /* Should not happen, we have just checked this above */
5931  elog(ERROR, "unsupported join type %d", jointype);
5932  }
5933 
5934  /*
5935  * For an inner join, all restrictions can be treated alike. Treating the
5936  * pushed down conditions as join conditions allows a top level full outer
5937  * join to be deparsed without requiring subqueries.
5938  */
5939  if (jointype == JOIN_INNER)
5940  {
5941  Assert(!fpinfo->joinclauses);
5942  fpinfo->joinclauses = fpinfo->remote_conds;
5943  fpinfo->remote_conds = NIL;
5944  }
5945 
5946  /* Mark that this join can be pushed down safely */
5947  fpinfo->pushdown_safe = true;
5948 
5949  /* Get user mapping */
5950  if (fpinfo->use_remote_estimate)
5951  {
5952  if (fpinfo_o->use_remote_estimate)
5953  fpinfo->user = fpinfo_o->user;
5954  else
5955  fpinfo->user = fpinfo_i->user;
5956  }
5957  else
5958  fpinfo->user = NULL;
5959 
5960  /*
5961  * Set # of retrieved rows and cached relation costs to some negative
5962  * value, so that we can detect when they are set to some sensible values,
5963  * during one (usually the first) of the calls to estimate_path_cost_size.
5964  */
5965  fpinfo->retrieved_rows = -1;
5966  fpinfo->rel_startup_cost = -1;
5967  fpinfo->rel_total_cost = -1;
5968 
5969  /*
5970  * Set the string describing this join relation to be used in EXPLAIN
5971  * output of corresponding ForeignScan. Note that the decoration we add
5972  * to the base relation names mustn't include any digits, or it'll confuse
5973  * postgresExplainForeignScan.
5974  */
5975  fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
5976  fpinfo_o->relation_name,
5977  get_jointype_name(fpinfo->jointype),
5978  fpinfo_i->relation_name);
5979 
5980  /*
5981  * Set the relation index. This is defined as the position of this
5982  * joinrel in the join_rel_list list plus the length of the rtable list.
5983  * Note that since this joinrel is at the end of the join_rel_list list
5984  * when we are called, we can get the position by list_length.
5985  */
5986  Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
5987  fpinfo->relation_index =
5989 
5990  return true;
5991 }
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:226
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:818
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:564
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1603
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:347
@ JOIN_FULL
Definition: nodes.h:306
@ JOIN_RIGHT
Definition: nodes.h:307
@ JOIN_LEFT
Definition: nodes.h:305
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: pathnodes.h:2664
#define IS_OTHER_REL(rel)
Definition: pathnodes.h:845
Relids lower_subquery_rels
Definition: postgres_fdw.h:119
Relids ph_eval_at
Definition: pathnodes.h:3022
List * join_rel_list
Definition: pathnodes.h:280
List * placeholder_list
Definition: pathnodes.h:374
List * rtable
Definition: parsenodes.h:175
Relids top_parent_relids
Definition: pathnodes.h:994

References Assert(), bms_add_members(), bms_is_subset(), bms_nonempty_difference(), bms_union(), RestrictInfo::clause, elog(), ERROR, get_jointype_name(), if(), PgFdwRelationInfo::innerrel, is_foreign_expr(), IS_OTHER_REL, IS_OUTER_JOIN, JOIN_FULL, JOIN_INNER, JOIN_LEFT, PlannerInfo::join_rel_list, JOIN_RIGHT, PgFdwRelationInfo::joinclauses, PgFdwRelationInfo::jointype, lappend(), lfirst, lfirst_node, list_concat(), list_length(), PgFdwRelationInfo::local_conds, PgFdwRelationInfo::lower_subquery_rels, PgFdwRelationInfo::make_innerrel_subquery, PgFdwRelationInfo::make_outerrel_subquery, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, PlannerInfo::parse, PlaceHolderInfo::ph_eval_at, PlannerInfo::placeholder_list, 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, Query::rtable, 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 7774 of file postgres_fdw.c.

7775 {
7776  Oid foreigntableid = RelationGetRelid(rel);
7777  ForeignTable *table;
7778  ForeignServer *server;
7779  List *options;
7780  ListCell *lc;
7781 
7782  /* we use 1 by default, which means "no batching" */
7783  int batch_size = 1;
7784 
7785  /*
7786  * Load options for table and server. We append server options after table
7787  * options, because table options take precedence.
7788  */
7789  table = GetForeignTable(foreigntableid);
7790  server = GetForeignServer(table->serverid);
7791 
7792  options = NIL;
7793  options = list_concat(options, table->options);
7794  options = list_concat(options, server->options);
7795 
7796  /* See if either table or server specifies batch_size. */
7797  foreach(lc, options)
7798  {
7799  DefElem *def = (DefElem *) lfirst(lc);
7800 
7801  if (strcmp(def->defname, "batch_size") == 0)
7802  {
7803  (void) parse_int(defGetString(def), &batch_size, 0, NULL);
7804  break;
7805  }
7806  }
7807 
7808  return batch_size;
7809 }
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:111
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 3577 of file postgres_fdw.c.

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

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

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

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

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

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

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

References Assert(), bms_is_empty, bms_overlap(), eclass_useful_for_merging(), PlannerInfo::eq_classes, RelOptInfo::has_eclass_joins, IS_OTHER_REL, RelOptInfo::joininfo, lappend(), lfirst, list_append_unique_ptr(), NIL, RelOptInfo::relids, 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 903 of file postgres_fdw.c.

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

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, PlannerInfo::query_pathkeys, 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 4662 of file postgres_fdw.c.

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

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

7423 {
7424  HeapTuple tuple;
7425  TupleDesc tupdesc;
7426  Datum *values;
7427  bool *nulls;
7428  ItemPointer ctid = NULL;
7429  ConversionLocation errpos;
7430  ErrorContextCallback errcallback;
7431  MemoryContext oldcontext;
7432  ListCell *lc;
7433  int j;
7434 
7435  Assert(row < PQntuples(res));
7436 
7437  /*
7438  * Do the following work in a temp context that we reset after each tuple.
7439  * This cleans up not only the data we have direct access to, but any
7440  * cruft the I/O functions might leak.
7441  */
7442  oldcontext = MemoryContextSwitchTo(temp_context);
7443 
7444  /*
7445  * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
7446  * provided, otherwise look to the scan node's ScanTupleSlot.
7447  */
7448  if (rel)
7449  tupdesc = RelationGetDescr(rel);
7450  else
7451  {
7452  Assert(fsstate);
7453  tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
7454  }
7455 
7456  values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
7457  nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
7458  /* Initialize to nulls for any columns not present in result */
7459  memset(nulls, true, tupdesc->natts * sizeof(bool));
7460 
7461  /*
7462  * Set up and install callback to report where conversion error occurs.
7463  */
7464  errpos.cur_attno = 0;
7465  errpos.rel = rel;
7466  errpos.fsstate = fsstate;
7467  errcallback.callback = conversion_error_callback;
7468  errcallback.arg = (void *) &errpos;
7469  errcallback.previous = error_context_stack;
7470  error_context_stack = &errcallback;
7471 
7472  /*
7473  * i indexes columns in the relation, j indexes columns in the PGresult.
7474  */
7475  j = 0;
7476  foreach(lc, retrieved_attrs)
7477  {
7478  int i = lfirst_int(lc);
7479  char *valstr;
7480 
7481  /* fetch next column's textual value */
7482  if (PQgetisnull(res, row, j))
7483  valstr = NULL;
7484  else
7485  valstr = PQgetvalue(res, row, j);
7486 
7487  /*
7488  * convert value to internal representation
7489  *
7490  * Note: we ignore system columns other than ctid and oid in result
7491  */
7492  errpos.cur_attno = i;
7493  if (i > 0)
7494  {
7495  /* ordinary column */
7496  Assert(i <= tupdesc->natts);
7497  nulls[i - 1] = (valstr == NULL);
7498  /* Apply the input function even to nulls, to support domains */
7499  values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
7500  valstr,
7501  attinmeta->attioparams[i - 1],
7502  attinmeta->atttypmods[i - 1]);
7503  }
7504  else if (i == SelfItemPointerAttributeNumber)
7505  {
7506  /* ctid */
7507  if (valstr != NULL)
7508  {
7509  Datum datum;
7510 
7511  datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
7512  ctid = (ItemPointer) DatumGetPointer(datum);
7513  }
7514  }
7515  errpos.cur_attno = 0;
7516 
7517  j++;
7518  }
7519 
7520  /* Uninstall error context callback. */
7521  error_context_stack = errcallback.previous;
7522 
7523  /*
7524  * Check we got the expected number of columns. Note: j == 0 and
7525  * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
7526  */
7527  if (j > 0 && j != PQnfields(res))
7528  elog(ERROR, "remote query result does not match the foreign table");
7529 
7530  /*
7531  * Build the result tuple in caller's memory context.
7532  */
7533  MemoryContextSwitchTo(oldcontext);
7534 
7535  tuple = heap_form_tuple(tupdesc, values, nulls);
7536 
7537  /*
7538  * If we have a CTID to return, install it in both t_self and t_ctid.
7539  * t_self is the normal place, but if the tuple is converted to a
7540  * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
7541  * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
7542  */
7543  if (ctid)
7544  tuple->t_self = tuple->t_data->t_ctid = *ctid;
7545 
7546  /*
7547  * Stomp on the xmin, xmax, and cmin fields from the tuple created by
7548  * heap_form_tuple. heap_form_tuple actually creates the tuple with
7549  * DatumTupleFields, not HeapTupleFields, but the executor expects
7550  * HeapTupleFields and will happily extract system columns on that
7551  * assumption. If we don't do this then, for example, the tuple length
7552  * ends up in the xmin field, which isn't what we want.
7553  */
7557 
7558  /* Clean up */
7559  MemoryContextReset(temp_context);
7560 
7561  return tuple;
7562 }
ErrorContextCallback * error_context_stack
Definition: elog.c:95
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3730
int PQnfields(const PGresult *res)
Definition: fe-exec.c:3318
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1517
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
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 6169 of file postgres_fdw.c.

6172 {
6173  /* We must always have fpinfo_o. */
6174  Assert(fpinfo_o);
6175 
6176  /* fpinfo_i may be NULL, but if present the servers must both match. */
6177  Assert(!fpinfo_i ||
6178  fpinfo_i->server->serverid == fpinfo_o->server->serverid);
6179 
6180  /*
6181  * Copy the server specific FDW options. (For a join, both relations come
6182  * from the same server, so the server options should have the same value
6183  * for both relations.)
6184  */
6185  fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
6186  fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
6187  fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
6188  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
6189  fpinfo->fetch_size = fpinfo_o->fetch_size;
6190  fpinfo->async_capable = fpinfo_o->async_capable;
6191 
6192  /* Merge the table level options from either side of the join. */
6193  if (fpinfo_i)
6194  {
6195  /*
6196  * We'll prefer to use remote estimates for this join if any table
6197  * from either side of the join is using remote estimates. This is
6198  * most likely going to be preferred since they're already willing to
6199  * pay the price of a round trip to get the remote EXPLAIN. In any
6200  * case it's not entirely clear how we might otherwise handle this
6201  * best.
6202  */
6203  fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
6204  fpinfo_i->use_remote_estimate;
6205 
6206  /*
6207  * Set fetch size to maximum of the joining sides, since we are
6208  * expecting the rows returned by the join to be proportional to the
6209  * relation sizes.
6210  */
6211  fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
6212 
6213  /*
6214  * We'll prefer to consider this join async-capable if any table from
6215  * either side of the join is considered async-capable. This would be
6216  * reasonable because in that case the foreign server would have its
6217  * own resources to scan that table asynchronously, and the join could
6218  * also be computed asynchronously using the resources.
6219  */
6220  fpinfo->async_capable = fpinfo_o->async_capable ||
6221  fpinfo_i->async_capable;
6222  }
6223 }
#define Max(x, y)
Definition: c.h:982
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