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_opfamily.h"
#include "commands/defrem.h"
#include "commands/explain_format.h"
#include "commands/explain_state.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/cost.h"
#include "optimizer/inherit.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "postgres_fdw.h"
#include "storage/latch.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
#include "utils/selfuncs.h"
Include dependency graph for postgres_fdw.c:

Go to the source code of this file.

Data Structures

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

Macros

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

Typedefs

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

Enumerations

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

Functions

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

Variables

 PG_MODULE_MAGIC
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 61 of file postgres_fdw.c.

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 55 of file postgres_fdw.c.

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.2

Definition at line 58 of file postgres_fdw.c.

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 121 of file postgres_fdw.c.

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 98 of file postgres_fdw.c.

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

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 281 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 70 of file postgres_fdw.c.

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

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

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

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

Referenced by postgresGetForeignUpperPaths().

◆ add_foreign_grouping_paths()

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

Definition at line 6779 of file postgres_fdw.c.

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

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

Referenced by postgresGetForeignUpperPaths().

◆ add_foreign_ordered_paths()

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

Definition at line 6875 of file postgres_fdw.c.

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

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

Referenced by postgresGetForeignUpperPaths().

◆ add_paths_with_pathkeys_for_rel()

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

Definition at line 6098 of file postgres_fdw.c.

6100{
6101 List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6102 ListCell *lc;
6103
6104 useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
6105
6106 /*
6107 * Before creating sorted paths, arrange for the passed-in EPQ path, if
6108 * any, to return columns needed by the parent ForeignScan node so that
6109 * they will propagate up through Sort nodes injected below, if necessary.
6110 */
6111 if (epq_path != NULL && useful_pathkeys_list != NIL)
6112 {
6113 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6114 PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6115
6116 /* Include columns required for evaluating PHVs in the tlist. */
6118 pull_var_clause((Node *) target->exprs,
6120
6121 /* Include columns required for evaluating the local conditions. */
6122 foreach(lc, fpinfo->local_conds)
6123 {
6125
6127 pull_var_clause((Node *) rinfo->clause,
6129 }
6130
6131 /*
6132 * If we have added any new columns, adjust the tlist of the EPQ path.
6133 *
6134 * Note: the plan created using this path will only be used to execute
6135 * EPQ checks, where accuracy of the plan cost and width estimates
6136 * would not be important, so we do not do set_pathtarget_cost_width()
6137 * for the new pathtarget here. See also postgresGetForeignPlan().
6138 */
6139 if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6140 {
6141 /* The EPQ path is a join path, so it is projection-capable. */
6143
6144 /*
6145 * Use create_projection_path() here, so as to avoid modifying it
6146 * in place.
6147 */
6148 epq_path = (Path *) create_projection_path(root,
6149 rel,
6150 epq_path,
6151 target);
6152 }
6153 }
6154
6155 /* Create one path for each set of pathkeys we found above. */
6156 foreach(lc, useful_pathkeys_list)
6157 {
6158 double rows;
6159 int width;
6160 int disabled_nodes;
6161 Cost startup_cost;
6162 Cost total_cost;
6163 List *useful_pathkeys = lfirst(lc);
6164 Path *sorted_epq_path;
6165
6166 estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
6167 &rows, &width, &disabled_nodes,
6168 &startup_cost, &total_cost);
6169
6170 /*
6171 * The EPQ path must be at least as well sorted as the path itself, in
6172 * case it gets used as input to a mergejoin.
6173 */
6174 sorted_epq_path = epq_path;
6175 if (sorted_epq_path != NULL &&
6176 !pathkeys_contained_in(useful_pathkeys,
6177 sorted_epq_path->pathkeys))
6178 sorted_epq_path = (Path *)
6180 rel,
6181 sorted_epq_path,
6182 useful_pathkeys,
6183 -1.0);
6184
6185 if (IS_SIMPLE_REL(rel))
6186 add_path(rel, (Path *)
6188 NULL,
6189 rows,
6190 disabled_nodes,
6191 startup_cost,
6192 total_cost,
6193 useful_pathkeys,
6194 rel->lateral_relids,
6195 sorted_epq_path,
6196 NIL, /* no fdw_restrictinfo
6197 * list */
6198 NIL));
6199 else
6200 add_path(rel, (Path *)
6202 NULL,
6203 rows,
6204 disabled_nodes,
6205 startup_cost,
6206 total_cost,
6207 useful_pathkeys,
6208 rel->lateral_relids,
6209 sorted_epq_path,
6210 restrictlist,
6211 NIL));
6212 }
6213}
bool is_projection_capable_path(Path *path)
Definition: createplan.c:7307
#define PVC_RECURSE_PLACEHOLDERS
Definition: optimizer.h:192
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:343
ProjectionPath * create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target)
Definition: pathnode.c:2763
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:3082
ForeignPath * create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, int disabled_nodes, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_restrictinfo, List *fdw_private)
Definition: pathnode.c:2307
ForeignPath * create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, int disabled_nodes, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_restrictinfo, List *fdw_private)
Definition: pathnode.c:2355
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:866
#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:906
Definition: nodes.h:131
Relids lateral_relids
Definition: pathnodes.h:940
Expr * clause
Definition: pathnodes.h:2602
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:653

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

Referenced by postgresGetForeignJoinPaths(), and postgresGetForeignPaths().

◆ adjust_foreign_grouping_path_cost()

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

Definition at line 3643 of file postgres_fdw.c.

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

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

Referenced by estimate_path_cost_size().

◆ analyze_row_processor()

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

Definition at line 5388 of file postgres_fdw.c.

5389{
5390 int targrows = astate->targrows;
5391 int pos; /* array index to store tuple in */
5392 MemoryContext oldcontext;
5393
5394 /* Always increment sample row counter. */
5395 astate->samplerows += 1;
5396
5397 /*
5398 * Determine the slot where this sample row should be stored. Set pos to
5399 * negative value to indicate the row should be skipped.
5400 */
5401 if (astate->numrows < targrows)
5402 {
5403 /* First targrows rows are always included into the sample */
5404 pos = astate->numrows++;
5405 }
5406 else
5407 {
5408 /*
5409 * Now we start replacing tuples in the sample until we reach the end
5410 * of the relation. Same algorithm as in acquire_sample_rows in
5411 * analyze.c; see Jeff Vitter's paper.
5412 */
5413 if (astate->rowstoskip < 0)
5414 astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5415
5416 if (astate->rowstoskip <= 0)
5417 {
5418 /* Choose a random reservoir element to replace. */
5419 pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5420 Assert(pos >= 0 && pos < targrows);
5421 heap_freetuple(astate->rows[pos]);
5422 }
5423 else
5424 {
5425 /* Skip this tuple. */
5426 pos = -1;
5427 }
5428
5429 astate->rowstoskip -= 1;
5430 }
5431
5432 if (pos >= 0)
5433 {
5434 /*
5435 * Create sample tuple from current result row, and store it in the
5436 * position determined above. The tuple has to be created in anl_cxt.
5437 */
5438 oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5439
5440 astate->rows[pos] = make_tuple_from_result_row(res, row,
5441 astate->rel,
5442 astate->attinmeta,
5443 astate->retrieved_attrs,
5444 NULL,
5445 astate->temp_cxt);
5446
5447 MemoryContextSwitchTo(oldcontext);
5448 }
5449}
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
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:267
AttInMetadata * attinmeta
Definition: postgres_fdw.c:256
MemoryContext anl_cxt
Definition: postgres_fdw.c:270
HeapTuple * rows
Definition: postgres_fdw.c:260
MemoryContext temp_cxt
Definition: postgres_fdw.c:271
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, 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 4757 of file postgres_fdw.c.

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

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

Referenced by get_returning_data().

◆ apply_server_options()

static void apply_server_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 6221 of file postgres_fdw.c.

6222{
6223 ListCell *lc;
6224
6225 foreach(lc, fpinfo->server->options)
6226 {
6227 DefElem *def = (DefElem *) lfirst(lc);
6228
6229 if (strcmp(def->defname, "use_remote_estimate") == 0)
6230 fpinfo->use_remote_estimate = defGetBoolean(def);
6231 else if (strcmp(def->defname, "fdw_startup_cost") == 0)
6232 (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
6233 NULL);
6234 else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
6235 (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
6236 NULL);
6237 else if (strcmp(def->defname, "extensions") == 0)
6238 fpinfo->shippable_extensions =
6240 else if (strcmp(def->defname, "fetch_size") == 0)
6241 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6242 else if (strcmp(def->defname, "async_capable") == 0)
6243 fpinfo->async_capable = defGetBoolean(def);
6244 }
6245}
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:447
char * defGetString(DefElem *def)
Definition: define.c:35
bool defGetBoolean(DefElem *def)
Definition: define.c:94
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition: guc.c:2871
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:2961
char * defname
Definition: parsenodes.h:826
List * options
Definition: foreign.h:42
List * shippable_extensions
Definition: postgres_fdw.h:82

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

6254{
6255 ListCell *lc;
6256
6257 foreach(lc, fpinfo->table->options)
6258 {
6259 DefElem *def = (DefElem *) lfirst(lc);
6260
6261 if (strcmp(def->defname, "use_remote_estimate") == 0)
6262 fpinfo->use_remote_estimate = defGetBoolean(def);
6263 else if (strcmp(def->defname, "fetch_size") == 0)
6264 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6265 else if (strcmp(def->defname, "async_capable") == 0)
6266 fpinfo->async_capable = defGetBoolean(def);
6267 }
6268}
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 4430 of file postgres_fdw.c.

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

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

3954{
3955 char sql[64];
3956 PGresult *res;
3957
3958 snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3959
3960 /*
3961 * We don't use a PG_TRY block here, so be careful not to throw error
3962 * without releasing the PGresult.
3963 */
3964 res = pgfdw_exec_query(conn, sql, conn_state);
3965 if (PQresultStatus(res) != PGRES_COMMAND_OK)
3966 pgfdw_report_error(ERROR, res, conn, true, sql);
3967 PQclear(res);
3968}
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:920
void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, bool clear, const char *sql)
Definition: connection.c:956
static unsigned int cursor_number
Definition: connection.c:81
#define ERROR
Definition: elog.h:39
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3411
void PQclear(PGresult *res)
Definition: fe-exec.c:721
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:124
#define snprintf
Definition: port.h:239
PGconn * conn
Definition: streamutil.c:52

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

Referenced by postgresAcquireSampleRowsFunc(), and postgresEndForeignScan().

◆ complete_pending_request()

static void complete_pending_request ( AsyncRequest areq)
static

Definition at line 7535 of file postgres_fdw.c.

7536{
7537 /* The request would have been pending for a callback */
7538 Assert(areq->callback_pending);
7539
7540 /* Unlike AsyncNotify, we unset callback_pending ourselves */
7541 areq->callback_pending = false;
7542
7543 /* We begin a fetch afterwards if necessary; don't fetch */
7544 produce_tuple_asynchronously(areq, false);
7545
7546 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7547 ExecAsyncResponse(areq);
7548
7549 /* Also, we do instrumentation ourselves, if required */
7550 if (areq->requestee->instrument)
7552 TupIsNull(areq->result) ? 0.0 : 1.0);
7553}
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:635
bool callback_pending
Definition: execnodes.h:633
struct PlanState * requestee
Definition: execnodes.h:631
Instrumentation * instrument
Definition: execnodes.h:1163
#define TupIsNull(slot)
Definition: tuptable.h:306

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

Referenced by postgresForeignAsyncConfigureWait().

◆ conversion_error_callback()

static void conversion_error_callback ( void *  arg)
static

Definition at line 7726 of file postgres_fdw.c.

7727{
7729 Relation rel = errpos->rel;
7730 ForeignScanState *fsstate = errpos->fsstate;
7731 const char *attname = NULL;
7732 const char *relname = NULL;
7733 bool is_wholerow = false;
7734
7735 /*
7736 * If we're in a scan node, always use aliases from the rangetable, for
7737 * consistency between the simple-relation and remote-join cases. Look at
7738 * the relation's tupdesc only if we're not in a scan node.
7739 */
7740 if (fsstate)
7741 {
7742 /* ForeignScan case */
7743 ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7744 int varno = 0;
7745 AttrNumber colno = 0;
7746
7747 if (fsplan->scan.scanrelid > 0)
7748 {
7749 /* error occurred in a scan against a foreign table */
7750 varno = fsplan->scan.scanrelid;
7751 colno = errpos->cur_attno;
7752 }
7753 else
7754 {
7755 /* error occurred in a scan against a foreign join */
7756 TargetEntry *tle;
7757
7759 errpos->cur_attno - 1);
7760
7761 /*
7762 * Target list can have Vars and expressions. For Vars, we can
7763 * get some information, however for expressions we can't. Thus
7764 * for expressions, just show generic context message.
7765 */
7766 if (IsA(tle->expr, Var))
7767 {
7768 Var *var = (Var *) tle->expr;
7769
7770 varno = var->varno;
7771 colno = var->varattno;
7772 }
7773 }
7774
7775 if (varno > 0)
7776 {
7777 EState *estate = fsstate->ss.ps.state;
7778 RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7779
7780 relname = rte->eref->aliasname;
7781
7782 if (colno == 0)
7783 is_wholerow = true;
7784 else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7785 attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7786 else if (colno == SelfItemPointerAttributeNumber)
7787 attname = "ctid";
7788 }
7789 }
7790 else if (rel)
7791 {
7792 /* Non-ForeignScan case (we should always have a rel here) */
7793 TupleDesc tupdesc = RelationGetDescr(rel);
7794
7796 if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7797 {
7798 Form_pg_attribute attr = TupleDescAttr(tupdesc,
7799 errpos->cur_attno - 1);
7800
7801 attname = NameStr(attr->attname);
7802 }
7803 else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7804 attname = "ctid";
7805 }
7806
7807 if (relname && is_wholerow)
7808 errcontext("whole-row reference to foreign table \"%s\"", relname);
7809 else if (relname && attname)
7810 errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7811 else
7812 errcontext("processing expression at position %d in select list",
7813 errpos->cur_attno);
7814}
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:717
#define errcontext
Definition: elog.h:196
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:678
#define castNode(_type_, nodeptr)
Definition: nodes.h:178
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:547
AttrNumber cur_attno
Definition: postgres_fdw.c:305
ForeignScanState * fsstate
Definition: postgres_fdw.c:307
ScanState ss
Definition: execnodes.h:2099
List * fdw_scan_tlist
Definition: plannodes.h:842
Plan * plan
Definition: execnodes.h:1153
EState * state
Definition: execnodes.h:1155
PlanState ps
Definition: execnodes.h:1609
Index scanrelid
Definition: plannodes.h:480
Expr * expr
Definition: primnodes.h:2219
#define strVal(v)
Definition: value.h:82

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

Referenced by make_tuple_from_result_row().

◆ convert_prep_stmt_params()

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

Definition at line 4271 of file postgres_fdw.c.

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

References Assert(), CompactAttribute::attgenerated, 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, TupleDescCompactAttr(), and value.

Referenced by execute_foreign_modify().

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

Definition at line 3728 of file postgres_fdw.c.

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

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

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

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

4402{
4403 char sql[64];
4404 PGresult *res;
4405
4406 /* do nothing if the query is not allocated */
4407 if (!fmstate->p_name)
4408 return;
4409
4410 snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4411
4412 /*
4413 * We don't use a PG_TRY block here, so be careful not to throw error
4414 * without releasing the PGresult.
4415 */
4416 res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4417 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4418 pgfdw_report_error(ERROR, res, fmstate->conn, true, sql);
4419 PQclear(res);
4420 pfree(fmstate->p_name);
4421 fmstate->p_name = NULL;
4422}

References PgFdwModifyState::conn, PgFdwModifyState::conn_state, ERROR, PgFdwModifyState::p_name, pfree(), pgfdw_exec_query(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear(), PQresultStatus(), 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 3699 of file postgres_fdw.c.

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

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

Referenced by postgresGetForeignPaths().

◆ estimate_path_cost_size()

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

Definition at line 3090 of file postgres_fdw.c.

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

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

◆ execute_dml_stmt()

static void execute_dml_stmt ( ForeignScanState node)
static

Definition at line 4557 of file postgres_fdw.c.

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

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

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

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

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

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

7476{
7478 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7479 char sql[64];
7480
7481 Assert(!fsstate->conn_state->pendingAreq);
7482
7483 /* Create the cursor synchronously. */
7484 if (!fsstate->cursor_exists)
7485 create_cursor(node);
7486
7487 /* We will send this query, but not wait for the response. */
7488 snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7489 fsstate->fetch_size, fsstate->cursor_number);
7490
7491 if (!PQsendQuery(fsstate->conn, sql))
7492 pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7493
7494 /* Remember that the request is in process */
7495 fsstate->conn_state->pendingAreq = areq;
7496}
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1416
static void create_cursor(ForeignScanState *node)

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

Referenced by postgresForeignAsyncConfigureWait(), and produce_tuple_asynchronously().

◆ find_em_for_rel()

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

Definition at line 7829 of file postgres_fdw.c.

7830{
7831 ListCell *lc;
7832
7833 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7834
7835 foreach(lc, ec->ec_members)
7836 {
7838
7839 /*
7840 * Note we require !bms_is_empty, else we'd accept constant
7841 * expressions which are not suitable for the purpose.
7842 */
7843 if (bms_is_subset(em->em_relids, rel->relids) &&
7844 !bms_is_empty(em->em_relids) &&
7846 is_foreign_expr(root, rel, em->em_expr))
7847 return em;
7848 }
7849
7850 return NULL;
7851}
Bitmapset * bms_intersect(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:292
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:412
#define bms_is_empty(a)
Definition: bitmapset.h:118
Relids hidden_subquery_rels
Definition: postgres_fdw.h:122
Relids relids
Definition: pathnodes.h:898

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

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

◆ find_em_for_rel_target()

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

Definition at line 7865 of file postgres_fdw.c.

7867{
7868 PathTarget *target = rel->reltarget;
7869 ListCell *lc1;
7870 int i;
7871
7872 i = 0;
7873 foreach(lc1, target->exprs)
7874 {
7875 Expr *expr = (Expr *) lfirst(lc1);
7876 Index sgref = get_pathtarget_sortgroupref(target, i);
7877 ListCell *lc2;
7878
7879 /* Ignore non-sort expressions */
7880 if (sgref == 0 ||
7882 root->parse->sortClause) == NULL)
7883 {
7884 i++;
7885 continue;
7886 }
7887
7888 /* We ignore binary-compatible relabeling on both ends */
7889 while (expr && IsA(expr, RelabelType))
7890 expr = ((RelabelType *) expr)->arg;
7891
7892 /* Locate an EquivalenceClass member matching this expr, if any */
7893 foreach(lc2, ec->ec_members)
7894 {
7896 Expr *em_expr;
7897
7898 /* Don't match constants */
7899 if (em->em_is_const)
7900 continue;
7901
7902 /* Ignore child members */
7903 if (em->em_is_child)
7904 continue;
7905
7906 /* Match if same expression (after stripping relabel) */
7907 em_expr = em->em_expr;
7908 while (em_expr && IsA(em_expr, RelabelType))
7909 em_expr = ((RelabelType *) em_expr)->arg;
7910
7911 if (!equal(em_expr, expr))
7912 continue;
7913
7914 /* Check that expression (including relabels!) is shippable */
7915 if (is_foreign_expr(root, rel, em->em_expr))
7916 return em;
7917 }
7918
7919 i++;
7920 }
7921
7922 return NULL;
7923}
unsigned int Index
Definition: c.h:585
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1587
List * exprs
Definition: pathnodes.h:1571
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:443

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

Referenced by add_foreign_ordered_paths(), and appendOrderByClause().

◆ find_modifytable_subplan()

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

Definition at line 2389 of file postgres_fdw.c.

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

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

Referenced by postgresPlanDirectModify().

◆ finish_foreign_modify()

static void finish_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4383 of file postgres_fdw.c.

4384{
4385 Assert(fmstate != NULL);
4386
4387 /* If we created a prepared statement, destroy it */
4388 deallocate_query(fmstate);
4389
4390 /* Release remote connection */
4391 ReleaseConnection(fmstate->conn);
4392 fmstate->conn = NULL;
4393}

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

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

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

Referenced by add_foreign_grouping_paths().

◆ foreign_join_ok()

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

Definition at line 5793 of file postgres_fdw.c.

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

References Assert(), bms_add_members(), bms_is_empty, bms_is_subset(), bms_nonempty_difference(), bms_union(), RestrictInfo::clause, elog, ERROR, get_jointype_name(), PgFdwRelationInfo::hidden_subquery_rels, if(), PgFdwRelationInfo::innerrel, is_foreign_expr(), IS_OTHER_REL, IS_OUTER_JOIN, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, PgFdwRelationInfo::joinclauses, PgFdwRelationInfo::jointype, lappend(), lfirst, lfirst_node, list_concat(), list_copy(), list_length(), PgFdwRelationInfo::local_conds, PgFdwRelationInfo::lower_subquery_rels, PgFdwRelationInfo::make_innerrel_subquery, PgFdwRelationInfo::make_outerrel_subquery, merge_fdw_options(), NIL, PgFdwRelationInfo::outerrel, PlaceHolderInfo::ph_eval_at, psprintf(), PgFdwRelationInfo::pushdown_safe, PgFdwRelationInfo::rel_startup_cost, PgFdwRelationInfo::rel_total_cost, PgFdwRelationInfo::relation_index, PgFdwRelationInfo::relation_name, RelOptInfo::relids, PgFdwRelationInfo::remote_conds, JoinPathExtraData::restrictlist, PgFdwRelationInfo::retrieved_rows, RINFO_IS_PUSHED_DOWN, root, semijoin_target_ok(), PgFdwRelationInfo::server, RelOptInfo::top_parent_relids, PgFdwRelationInfo::use_remote_estimate, and PgFdwRelationInfo::user.

Referenced by postgresGetForeignJoinPaths().

◆ get_batch_size_option()

static int get_batch_size_option ( Relation  rel)
static

Definition at line 7930 of file postgres_fdw.c.

7931{
7932 Oid foreigntableid = RelationGetRelid(rel);
7933 ForeignTable *table;
7934 ForeignServer *server;
7935 List *options;
7936 ListCell *lc;
7937
7938 /* we use 1 by default, which means "no batching" */
7939 int batch_size = 1;
7940
7941 /*
7942 * Load options for table and server. We append server options after table
7943 * options, because table options take precedence.
7944 */
7945 table = GetForeignTable(foreigntableid);
7946 server = GetForeignServer(table->serverid);
7947
7948 options = NIL;
7950 options = list_concat(options, server->options);
7951
7952 /* See if either table or server specifies batch_size. */
7953 foreach(lc, options)
7954 {
7955 DefElem *def = (DefElem *) lfirst(lc);
7956
7957 if (strcmp(def->defname, "batch_size") == 0)
7958 {
7959 (void) parse_int(defGetString(def), &batch_size, 0, NULL);
7960 break;
7961 }
7962 }
7963
7964 return batch_size;
7965}
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 3597 of file postgres_fdw.c.

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

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

Referenced by estimate_path_cost_size().

◆ get_returning_data()

static TupleTableSlot * get_returning_data ( ForeignScanState node)
static

Definition at line 4611 of file postgres_fdw.c.

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

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

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

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

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

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

Referenced by get_useful_pathkeys_for_relation().

◆ get_useful_pathkeys_for_relation()

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

Definition at line 906 of file postgres_fdw.c.

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

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

Referenced by add_paths_with_pathkeys_for_rel().

◆ init_returning_filter()

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

Definition at line 4684 of file postgres_fdw.c.

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

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

Referenced by postgresBeginDirectModify().

◆ make_tuple_from_result_row()

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

Definition at line 7569 of file postgres_fdw.c.

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

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

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

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

◆ PG_FUNCTION_INFO_V1()

PG_FUNCTION_INFO_V1 ( postgres_fdw_handler  )

◆ postgres_fdw_handler()

Datum postgres_fdw_handler ( PG_FUNCTION_ARGS  )

Definition at line 552 of file postgres_fdw.c.

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

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

◆ postgresAcquireSampleRowsFunc()

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

Definition at line 5077 of file postgres_fdw.c.

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

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

Referenced by postgresAnalyzeForeignTable().

◆ postgresAddForeignUpdateTargets()

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

Definition at line 1749 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresAnalyzeForeignTable()

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

Definition at line 4942 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresBeginDirectModify()

static void postgresBeginDirectModify ( ForeignScanState node,
int  eflags 
)
static

Definition at line 2647 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresBeginForeignInsert()

static void postgresBeginForeignInsert ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2158 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresBeginForeignModify()

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

Definition at line 1916 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresBeginForeignScan()

static void postgresBeginForeignScan ( ForeignScanState node,
int  eflags 
)
static

Definition at line 1496 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresEndDirectModify()

static void postgresEndDirectModify ( ForeignScanState node)
static

Definition at line 2808 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresEndForeignInsert()

static void postgresEndForeignInsert ( EState estate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2289 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresEndForeignModify()

static void postgresEndForeignModify ( EState estate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2140 of file postgres_fdw.c.

2142{
2143 PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2144
2145 /* If fmstate is NULL, we are in EXPLAIN; nothing to do */
2146 if (fmstate == NULL)
2147 return;
2148
2149 /* Destroy the execution state */
2150 finish_foreign_modify(fmstate);
2151}

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

Referenced by postgres_fdw_handler().

◆ postgresEndForeignScan()

static void postgresEndForeignScan ( ForeignScanState node)
static

Definition at line 1724 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresExecForeignBatchInsert()

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

Definition at line 2002 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresExecForeignDelete()

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

Definition at line 2121 of file postgres_fdw.c.

2125{
2126 TupleTableSlot **rslot;
2127 int numSlots = 1;
2128
2129 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_DELETE,
2130 &slot, &planSlot, &numSlots);
2131
2132 return rslot ? rslot[0] : NULL;
2133}

References CMD_DELETE, and execute_foreign_modify().

Referenced by postgres_fdw_handler().

◆ postgresExecForeignInsert()

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

Definition at line 1973 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresExecForeignTruncate()

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

Definition at line 2984 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresExecForeignUpdate()

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

Definition at line 2102 of file postgres_fdw.c.

2106{
2107 TupleTableSlot **rslot;
2108 int numSlots = 1;
2109
2110 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_UPDATE,
2111 &slot, &planSlot, &numSlots);
2112
2113 return rslot ? rslot[0] : NULL;
2114}

References CMD_UPDATE, and execute_foreign_modify().

Referenced by postgres_fdw_handler().

◆ postgresExplainDirectModify()

static void postgresExplainDirectModify ( ForeignScanState node,
ExplainState es 
)
static

Definition at line 2966 of file postgres_fdw.c.

2967{
2968 List *fdw_private;
2969 char *sql;
2970
2971 if (es->verbose)
2972 {
2973 fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
2974 sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql));
2975 ExplainPropertyText("Remote SQL", sql, es);
2976 }
2977}
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)

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

Referenced by postgres_fdw_handler().

◆ postgresExplainForeignModify()

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

Definition at line 2938 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresExplainForeignScan()

static void postgresExplainForeignScan ( ForeignScanState node,
ExplainState es 
)
static

Definition at line 2831 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncConfigureWait()

static void postgresForeignAsyncConfigureWait ( AsyncRequest areq)
static

Definition at line 7289 of file postgres_fdw.c.

7290{
7292 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7293 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
7294 AppendState *requestor = (AppendState *) areq->requestor;
7295 WaitEventSet *set = requestor->as_eventset;
7296
7297 /* This should not be called unless callback_pending */
7298 Assert(areq->callback_pending);
7299
7300 /*
7301 * If process_pending_request() has been invoked on the given request
7302 * before we get here, we might have some tuples already; in which case
7303 * complete the request
7304 */
7305 if (fsstate->next_tuple < fsstate->num_tuples)
7306 {
7308 if (areq->request_complete)
7309 return;
7310 Assert(areq->callback_pending);
7311 }
7312
7313 /* We must have run out of tuples */
7314 Assert(fsstate->next_tuple >= fsstate->num_tuples);
7315
7316 /* The core code would have registered postmaster death event */
7318
7319 /* Begin an asynchronous data fetch if not already done */
7320 if (!pendingAreq)
7322 else if (pendingAreq->requestor != areq->requestor)
7323 {
7324 /*
7325 * This is the case when the in-process request was made by another
7326 * Append. Note that it might be useless to process the request made
7327 * by that Append, because the query might not need tuples from that
7328 * Append anymore; so we avoid processing it to begin a fetch for the
7329 * given request if possible. If there are any child subplans of the
7330 * same parent that are ready for new requests, skip the given
7331 * request. Likewise, if there are any configured events other than
7332 * the postmaster death event, skip it. Otherwise, process the
7333 * in-process request, then begin a fetch to configure the event
7334 * below, because we might otherwise end up with no configured events
7335 * other than the postmaster death event.
7336 */
7337 if (!bms_is_empty(requestor->as_needrequest))
7338 return;
7339 if (GetNumRegisteredWaitEvents(set) > 1)
7340 return;
7341 process_pending_request(pendingAreq);
7343 }
7344 else if (pendingAreq->requestee != areq->requestee)
7345 {
7346 /*
7347 * This is the case when the in-process request was made by the same
7348 * parent but for a different child. Since we configure only the
7349 * event for the request made for that child, skip the given request.
7350 */
7351 return;
7352 }
7353 else
7354 Assert(pendingAreq == areq);
7355
7356 AddWaitEventToSet(set, WL_SOCKET_READABLE, PQsocket(fsstate->conn),
7357 NULL, areq);
7358}
int PQsocket(const PGconn *conn)
Definition: fe-connect.c:7579
static void fetch_more_data_begin(AsyncRequest *areq)
static void complete_pending_request(AsyncRequest *areq)
Bitmapset * as_needrequest
Definition: execnodes.h:1497
struct WaitEventSet * as_eventset
Definition: execnodes.h:1498
struct PlanState * requestor
Definition: execnodes.h:630
bool request_complete
Definition: execnodes.h:634
int GetNumRegisteredWaitEvents(WaitEventSet *set)
int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch, void *user_data)
Definition: waiteventset.c:569
#define WL_SOCKET_READABLE
Definition: waiteventset.h:35

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

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncNotify()

static void postgresForeignAsyncNotify ( AsyncRequest areq)
static

Definition at line 7366 of file postgres_fdw.c.

7367{
7369 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7370
7371 /* The core code would have initialized the callback_pending flag */
7372 Assert(!areq->callback_pending);
7373
7374 /*
7375 * If process_pending_request() has been invoked on the given request
7376 * before we get here, we might have some tuples already; in which case
7377 * produce the next tuple
7378 */
7379 if (fsstate->next_tuple < fsstate->num_tuples)
7380 {
7381 produce_tuple_asynchronously(areq, true);
7382 return;
7383 }
7384
7385 /* We must have run out of tuples */
7386 Assert(fsstate->next_tuple >= fsstate->num_tuples);
7387
7388 /* The request should be currently in-process */
7389 Assert(fsstate->conn_state->pendingAreq == areq);
7390
7391 /* On error, report the original query, not the FETCH. */
7392 if (!PQconsumeInput(fsstate->conn))
7393 pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
7394
7395 fetch_more_data(node);
7396
7397 produce_tuple_asynchronously(areq, true);
7398}
int PQconsumeInput(PGconn *conn)
Definition: fe-exec.c:1984
static void fetch_more_data(ForeignScanState *node)

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

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncRequest()

static void postgresForeignAsyncRequest ( AsyncRequest areq)
static

Definition at line 7279 of file postgres_fdw.c.

7280{
7281 produce_tuple_asynchronously(areq, true);
7282}

References produce_tuple_asynchronously().

Referenced by postgres_fdw_handler().

◆ postgresGetAnalyzeInfoForForeignTable()

static double postgresGetAnalyzeInfoForForeignTable ( Relation  relation,
bool *  can_tablesample 
)
static

Definition at line 5006 of file postgres_fdw.c.

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

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

Referenced by postgresAcquireSampleRowsFunc().

◆ postgresGetForeignJoinPaths()

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

Definition at line 6341 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresGetForeignModifyBatchSize()

static int postgresGetForeignModifyBatchSize ( ResultRelInfo resultRelInfo)
static

Definition at line 2035 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresGetForeignPaths()

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

Definition at line 1013 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresGetForeignPlan()

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

Definition at line 1227 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresGetForeignRelSize()

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

Definition at line 622 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresGetForeignUpperPaths()

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

Definition at line 6726 of file postgres_fdw.c.

6729{
6730 PgFdwRelationInfo *fpinfo;
6731
6732 /*
6733 * If input rel is not safe to pushdown, then simply return as we cannot
6734 * perform any post-join operations on the foreign server.
6735 */
6736 if (!input_rel->fdw_private ||
6737 !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
6738 return;
6739
6740 /* Ignore stages we don't support; and skip any duplicate calls. */
6741 if ((stage != UPPERREL_GROUP_AGG &&
6742 stage != UPPERREL_ORDERED &&
6743 stage != UPPERREL_FINAL) ||
6744 output_rel->fdw_private)
6745 return;
6746
6747 fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo));
6748 fpinfo->pushdown_safe = false;
6749 fpinfo->stage = stage;
6750 output_rel->fdw_private = fpinfo;
6751
6752 switch (stage)
6753 {
6754 case UPPERREL_GROUP_AGG:
6755 add_foreign_grouping_paths(root, input_rel, output_rel,
6756 (GroupPathExtraData *) extra);
6757 break;
6758 case UPPERREL_ORDERED:
6759 add_foreign_ordered_paths(root, input_rel, output_rel);
6760 break;
6761 case UPPERREL_FINAL:
6762 add_foreign_final_paths(root, input_rel, output_rel,
6763 (FinalPathExtraData *) extra);
6764 break;
6765 default:
6766 elog(ERROR, "unexpected upper relation: %d", (int) stage);
6767 break;
6768 }
6769}
static void add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra)
static void add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel)
static void add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *final_rel, FinalPathExtraData *extra)

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

Referenced by postgres_fdw_handler().

◆ postgresImportForeignSchema()

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

Definition at line 5455 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresIsForeignPathAsyncCapable()

static bool postgresIsForeignPathAsyncCapable ( ForeignPath path)
static

Definition at line 7266 of file postgres_fdw.c.

7267{
7268 RelOptInfo *rel = ((Path *) path)->parent;
7269 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7270
7271 return fpinfo->async_capable;
7272}

References PgFdwRelationInfo::async_capable.

Referenced by postgres_fdw_handler().

◆ postgresIsForeignRelUpdatable()

static int postgresIsForeignRelUpdatable ( Relation  rel)
static

Definition at line 2313 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresIterateDirectModify()

static TupleTableSlot * postgresIterateDirectModify ( ForeignScanState node)
static

Definition at line 2764 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresIterateForeignScan()

static TupleTableSlot * postgresIterateForeignScan ( ForeignScanState node)
static

Definition at line 1601 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresPlanDirectModify()

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

Definition at line 2449 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresPlanForeignModify()

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

Definition at line 1777 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresRecheckForeignScan()

static bool postgresRecheckForeignScan ( ForeignScanState node,
TupleTableSlot slot 
)
static

Definition at line 2357 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresReScanForeignScan()

static void postgresReScanForeignScan ( ForeignScanState node)
static

Definition at line 1646 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ prepare_foreign_modify()

static void prepare_foreign_modify ( PgFdwModifyState fmstate)
static

Definition at line 4216 of file postgres_fdw.c.

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

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

Referenced by execute_foreign_modify().

◆ prepare_query_params()

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

Definition at line 4856 of file postgres_fdw.c.

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

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

Referenced by postgresBeginDirectModify(), and postgresBeginForeignScan().

◆ process_pending_request()

void process_pending_request ( AsyncRequest areq)

Definition at line 7502 of file postgres_fdw.c.

7503{
7505 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7506
7507 /* The request would have been pending for a callback */
7508 Assert(areq->callback_pending);
7509
7510 /* The request should be currently in-process */
7511 Assert(fsstate->conn_state->pendingAreq == areq);
7512
7513 fetch_more_data(node);
7514
7515 /*
7516 * If we didn't get any tuples, must be end of data; complete the request
7517 * now. Otherwise, we postpone completing the request until we are called
7518 * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
7519 */
7520 if (fsstate->next_tuple >= fsstate->num_tuples)
7521 {
7522 /* Unlike AsyncNotify, we unset callback_pending ourselves */
7523 areq->callback_pending = false;
7524 /* Mark the request as complete */
7525 ExecAsyncRequestDone(areq, NULL);
7526 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7527 ExecAsyncResponse(areq);
7528 }
7529}
void ExecAsyncRequestDone(AsyncRequest *areq, TupleTableSlot *result)
Definition: execAsync.c:137

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

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

◆ process_query_params()

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

Definition at line 4901 of file postgres_fdw.c.

4905{
4906 int nestlevel;
4907 int i;
4908 ListCell *lc;
4909
4910 nestlevel = set_transmission_modes();
4911
4912 i = 0;
4913 foreach(lc, param_exprs)
4914 {
4915 ExprState *expr_state = (ExprState *) lfirst(lc);
4916 Datum expr_value;
4917 bool isNull;
4918
4919 /* Evaluate the parameter expression */
4920 expr_value = ExecEvalExpr(expr_state, econtext, &isNull);
4921
4922 /*
4923 * Get string representation of each parameter value by invoking
4924 * type-specific output function, unless the value is null.
4925 */
4926 if (isNull)
4927 param_values[i] = NULL;
4928 else
4929 param_values[i] = OutputFunctionCall(&param_flinfo[i], expr_value);
4930
4931 i++;
4932 }
4933
4934 reset_transmission_modes(nestlevel);
4935}
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:374

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

Referenced by create_cursor(), and execute_dml_stmt().

◆ produce_tuple_asynchronously()

static void produce_tuple_asynchronously ( AsyncRequest areq,
bool  fetch 
)
static

Definition at line 7404 of file postgres_fdw.c.

7405{
7407 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7408 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
7409 TupleTableSlot *result;
7410
7411 /* This should not be called if the request is currently in-process */
7412 Assert(areq != pendingAreq);
7413
7414 /* Fetch some more tuples, if we've run out */
7415 if (fsstate->next_tuple >= fsstate->num_tuples)
7416 {
7417 /* No point in another fetch if we already detected EOF, though */
7418 if (!fsstate->eof_reached)
7419 {
7420 /* Mark the request as pending for a callback */
7422 /* Begin another fetch if requested and if no pending request */
7423 if (fetch && !pendingAreq)
7425 }
7426 else
7427 {
7428 /* There's nothing more to do; just return a NULL pointer */
7429 result = NULL;
7430 /* Mark the request as complete */
7431 ExecAsyncRequestDone(areq, result);
7432 }
7433 return;
7434 }
7435
7436 /* Get a tuple from the ForeignScan node */
7437 result = areq->requestee->ExecProcNodeReal(areq->requestee);
7438 if (!TupIsNull(result))
7439 {
7440 /* Mark the request as complete */
7441 ExecAsyncRequestDone(areq, result);
7442 return;
7443 }
7444
7445 /* We must have run out of tuples */
7446 Assert(fsstate->next_tuple >= fsstate->num_tuples);
7447
7448 /* Fetch some more tuples, if we've not detected EOF yet */
7449 if (!fsstate->eof_reached)
7450 {
7451 /* Mark the request as pending for a callback */
7453 /* Begin another fetch if requested and if no pending request */
7454 if (fetch && !pendingAreq)
7456 }
7457 else
7458 {
7459 /* There's nothing more to do; just return a NULL pointer */
7460 result = NULL;
7461 /* Mark the request as complete */
7462 ExecAsyncRequestDone(areq, result);
7463 }
7464}
void ExecAsyncRequestPending(AsyncRequest *areq)
Definition: execAsync.c:149
ExecProcNodeMtd ExecProcNodeReal
Definition: execnodes.h:1160

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

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

◆ rebuild_fdw_scan_tlist()

static void rebuild_fdw_scan_tlist ( ForeignScan fscan,
List tlist 
)
static

Definition at line 4531 of file postgres_fdw.c.

4532{
4533 List *new_tlist = tlist;
4534 List *old_tlist = fscan->fdw_scan_tlist;
4535 ListCell *lc;
4536
4537 foreach(lc, old_tlist)
4538 {
4539 TargetEntry *tle = (TargetEntry *) lfirst(lc);
4540
4541 if (tlist_member(tle->expr, new_tlist))
4542 continue; /* already got it */
4543
4544 new_tlist = lappend(new_tlist,
4545 makeTargetEntry(tle->expr,
4546 list_length(new_tlist) + 1,
4547 NULL,
4548 false));
4549 }
4550 fscan->fdw_scan_tlist = new_tlist;
4551}

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

Referenced by postgresPlanDirectModify().

◆ reset_transmission_modes()

void reset_transmission_modes ( int  nestlevel)

Definition at line 3943 of file postgres_fdw.c.

3944{
3945 AtEOXact_GUC(true, nestlevel);
3946}
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2262

References AtEOXact_GUC().

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

◆ semijoin_target_ok()

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

Definition at line 5754 of file postgres_fdw.c.

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

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

Referenced by foreign_join_ok().

◆ set_transmission_modes()

int set_transmission_modes ( void  )

Definition at line 3907 of file postgres_fdw.c.

3908{
3909 int nestlevel = NewGUCNestLevel();
3910
3911 /*
3912 * The values set here should match what pg_dump does. See also
3913 * configure_remote_session in connection.c.
3914 */
3915 if (DateStyle != USE_ISO_DATES)
3916 (void) set_config_option("datestyle", "ISO",
3918 GUC_ACTION_SAVE, true, 0, false);
3920 (void) set_config_option("intervalstyle", "postgres",
3922 GUC_ACTION_SAVE, true, 0, false);
3923 if (extra_float_digits < 3)
3924 (void) set_config_option("extra_float_digits", "3",
3926 GUC_ACTION_SAVE, true, 0, false);
3927
3928 /*
3929 * In addition force restrictive search_path, in case there are any
3930 * regproc or similar constants to be printed.
3931 */
3932 (void) set_config_option("search_path", "pg_catalog",
3934 GUC_ACTION_SAVE, true, 0, false);
3935
3936 return nestlevel;
3937}
int extra_float_digits
Definition: float.c:40
int DateStyle
Definition: globals.c:124
int IntervalStyle
Definition: globals.c:126
int NewGUCNestLevel(void)
Definition: guc.c:2235
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3342
@ GUC_ACTION_SAVE
Definition: guc.h:205
@ PGC_S_SESSION
Definition: guc.h:126
@ PGC_USERSET
Definition: guc.h:79
#define USE_ISO_DATES
Definition: miscadmin.h:236
#define INTSTYLE_POSTGRES
Definition: miscadmin.h:256

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

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

◆ store_returning_result()

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

Definition at line 4350 of file postgres_fdw.c.

4352{
4353 PG_TRY();
4354 {
4355 HeapTuple newtup;
4356
4357 newtup = make_tuple_from_result_row(res, 0,
4358 fmstate->rel,
4359 fmstate->attinmeta,
4360 fmstate->retrieved_attrs,
4361 NULL,
4362 fmstate->temp_cxt);
4363
4364 /*
4365 * The returning slot will not necessarily be suitable to store
4366 * heaptuples directly, so allow for conversion.
4367 */
4368 ExecForceStoreHeapTuple(newtup, slot, true);
4369 }
4370 PG_CATCH();
4371 {
4372 PQclear(res);
4373 PG_RE_THROW();
4374 }
4375 PG_END_TRY();
4376}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1656

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

Referenced by execute_foreign_modify().

Variable Documentation

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 52 of file postgres_fdw.c.