PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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_MODULE_MAGIC_EXT (.name="postgres_fdw",.version=PG_VERSION)
 
 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 *p_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)
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 64 of file postgres_fdw.c.

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 58 of file postgres_fdw.c.

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.2

Definition at line 61 of file postgres_fdw.c.

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 124 of file postgres_fdw.c.

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 101 of file postgres_fdw.c.

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

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 284 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 73 of file postgres_fdw.c.

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

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

6938{
6939 Query *parse = root->parse;
6940 PgFdwRelationInfo *ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
6941 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) final_rel->fdw_private;
6942 bool has_final_sort = false;
6943 List *pathkeys = NIL;
6944 PgFdwPathExtraData *fpextra;
6945 bool save_use_remote_estimate = false;
6946 double rows;
6947 int width;
6948 int disabled_nodes;
6949 Cost startup_cost;
6950 Cost total_cost;
6951 List *fdw_private;
6952 ForeignPath *final_path;
6953
6954 /*
6955 * Currently, we only support this for SELECT commands
6956 */
6957 if (parse->commandType != CMD_SELECT)
6958 return;
6959
6960 /*
6961 * No work if there is no FOR UPDATE/SHARE clause and if there is no need
6962 * to add a LIMIT node
6963 */
6964 if (!parse->rowMarks && !extra->limit_needed)
6965 return;
6966
6967 /* We don't support cases where there are any SRFs in the targetlist */
6968 if (parse->hasTargetSRFs)
6969 return;
6970
6971 /* Save the input_rel as outerrel in fpinfo */
6972 fpinfo->outerrel = input_rel;
6973
6974 /*
6975 * Copy foreign table, foreign server, user mapping, FDW options etc.
6976 * details from the input relation's fpinfo.
6977 */
6978 fpinfo->table = ifpinfo->table;
6979 fpinfo->server = ifpinfo->server;
6980 fpinfo->user = ifpinfo->user;
6981 merge_fdw_options(fpinfo, ifpinfo, NULL);
6982
6983 /*
6984 * If there is no need to add a LIMIT node, there might be a ForeignPath
6985 * in the input_rel's pathlist that implements all behavior of the query.
6986 * Note: we would already have accounted for the query's FOR UPDATE/SHARE
6987 * (if any) before we get here.
6988 */
6989 if (!extra->limit_needed)
6990 {
6991 ListCell *lc;
6992
6993 Assert(parse->rowMarks);
6994
6995 /*
6996 * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
6997 * so the input_rel should be a base, join, or ordered relation; and
6998 * if it's an ordered relation, its input relation should be a base or
6999 * join relation.
7000 */
7001 Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7002 input_rel->reloptkind == RELOPT_JOINREL ||
7003 (input_rel->reloptkind == RELOPT_UPPER_REL &&
7004 ifpinfo->stage == UPPERREL_ORDERED &&
7005 (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
7006 ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
7007
7008 foreach(lc, input_rel->pathlist)
7009 {
7010 Path *path = (Path *) lfirst(lc);
7011
7012 /*
7013 * apply_scanjoin_target_to_paths() uses create_projection_path()
7014 * to adjust each of its input paths if needed, whereas
7015 * create_ordered_paths() uses apply_projection_to_path() to do
7016 * that. So the former might have put a ProjectionPath on top of
7017 * the ForeignPath; look through ProjectionPath and see if the
7018 * path underneath it is ForeignPath.
7019 */
7020 if (IsA(path, ForeignPath) ||
7021 (IsA(path, ProjectionPath) &&
7022 IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
7023 {
7024 /*
7025 * Create foreign final path; this gets rid of a
7026 * no-longer-needed outer plan (if any), which makes the
7027 * EXPLAIN output look cleaner
7028 */
7029 final_path = create_foreign_upper_path(root,
7030 path->parent,
7031 path->pathtarget,
7032 path->rows,
7033 path->disabled_nodes,
7034 path->startup_cost,
7035 path->total_cost,
7036 path->pathkeys,
7037 NULL, /* no extra plan */
7038 NIL, /* no fdw_restrictinfo
7039 * list */
7040 NIL); /* no fdw_private */
7041
7042 /* and add it to the final_rel */
7043 add_path(final_rel, (Path *) final_path);
7044
7045 /* Safe to push down */
7046 fpinfo->pushdown_safe = true;
7047
7048 return;
7049 }
7050 }
7051
7052 /*
7053 * If we get here it means no ForeignPaths; since we would already
7054 * have considered pushing down all operations for the query to the
7055 * remote server, give up on it.
7056 */
7057 return;
7058 }
7059
7060 Assert(extra->limit_needed);
7061
7062 /*
7063 * If the input_rel is an ordered relation, replace the input_rel with its
7064 * input relation
7065 */
7066 if (input_rel->reloptkind == RELOPT_UPPER_REL &&
7067 ifpinfo->stage == UPPERREL_ORDERED)
7068 {
7069 input_rel = ifpinfo->outerrel;
7070 ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
7071 has_final_sort = true;
7072 pathkeys = root->sort_pathkeys;
7073 }
7074
7075 /* The input_rel should be a base, join, or grouping relation */
7076 Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7077 input_rel->reloptkind == RELOPT_JOINREL ||
7078 (input_rel->reloptkind == RELOPT_UPPER_REL &&
7079 ifpinfo->stage == UPPERREL_GROUP_AGG));
7080
7081 /*
7082 * We try to create a path below by extending a simple foreign path for
7083 * the underlying base, join, or grouping relation to perform the final
7084 * sort (if has_final_sort) and the LIMIT restriction remotely, which is
7085 * stored into the fdw_private list of the resulting path. (We
7086 * re-estimate the costs of sorting the underlying relation, if
7087 * has_final_sort.)
7088 */
7089
7090 /*
7091 * Assess if it is safe to push down the LIMIT and OFFSET to the remote
7092 * server
7093 */
7094
7095 /*
7096 * If the underlying relation has any local conditions, the LIMIT/OFFSET
7097 * cannot be pushed down.
7098 */
7099 if (ifpinfo->local_conds)
7100 return;
7101
7102 /*
7103 * If the query has FETCH FIRST .. WITH TIES, 1) it must have ORDER BY as
7104 * well, which is used to determine which additional rows tie for the last
7105 * place in the result set, and 2) ORDER BY must already have been
7106 * determined to be safe to push down before we get here. So in that case
7107 * the FETCH clause is safe to push down with ORDER BY if the remote
7108 * server is v13 or later, but if not, the remote query will fail entirely
7109 * for lack of support for it. Since we do not currently have a way to do
7110 * a remote-version check (without accessing the remote server), disable
7111 * pushing the FETCH clause for now.
7112 */
7113 if (parse->limitOption == LIMIT_OPTION_WITH_TIES)
7114 return;
7115
7116 /*
7117 * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
7118 * not safe to remote.
7119 */
7120 if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
7121 !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
7122 return;
7123
7124 /* Safe to push down */
7125 fpinfo->pushdown_safe = true;
7126
7127 /* Construct PgFdwPathExtraData */
7128 fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
7129 fpextra->target = root->upper_targets[UPPERREL_FINAL];
7130 fpextra->has_final_sort = has_final_sort;
7131 fpextra->has_limit = extra->limit_needed;
7132 fpextra->limit_tuples = extra->limit_tuples;
7133 fpextra->count_est = extra->count_est;
7134 fpextra->offset_est = extra->offset_est;
7135
7136 /*
7137 * Estimate the costs of performing the final sort and the LIMIT
7138 * restriction remotely. If has_final_sort is false, we wouldn't need to
7139 * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
7140 * roughly estimated using the costs we already have for the underlying
7141 * relation, in the same way as when use_remote_estimate is false. Since
7142 * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
7143 * false in that case.
7144 */
7145 if (!fpextra->has_final_sort)
7146 {
7147 save_use_remote_estimate = ifpinfo->use_remote_estimate;
7148 ifpinfo->use_remote_estimate = false;
7149 }
7150 estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
7151 &rows, &width, &disabled_nodes,
7152 &startup_cost, &total_cost);
7153 if (!fpextra->has_final_sort)
7154 ifpinfo->use_remote_estimate = save_use_remote_estimate;
7155
7156 /*
7157 * Build the fdw_private list that will be used by postgresGetForeignPlan.
7158 * Items in the list must match order in enum FdwPathPrivateIndex.
7159 */
7160 fdw_private = list_make2(makeBoolean(has_final_sort),
7161 makeBoolean(extra->limit_needed));
7162
7163 /*
7164 * Create foreign final path; this gets rid of a no-longer-needed outer
7165 * plan (if any), which makes the EXPLAIN output look cleaner
7166 */
7167 final_path = create_foreign_upper_path(root,
7168 input_rel,
7169 root->upper_targets[UPPERREL_FINAL],
7170 rows,
7171 disabled_nodes,
7172 startup_cost,
7173 total_cost,
7174 pathkeys,
7175 NULL, /* no extra plan */
7176 NIL, /* no fdw_restrictinfo list */
7177 fdw_private);
7178
7179 /* and add it to the final_rel */
7180 add_path(final_rel, (Path *) final_path);
7181}
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:244
Assert(PointerIsAligned(start, uint64))
Datum subpath(PG_FUNCTION_ARGS)
Definition: ltree_op.c:311
void * palloc0(Size size)
Definition: mcxt.c:1395
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
double Cost
Definition: nodes.h:261
@ CMD_SELECT
Definition: nodes.h:275
@ LIMIT_OPTION_WITH_TIES
Definition: nodes.h:442
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:2167
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:883
@ RELOPT_UPPER_REL
Definition: pathnodes.h:887
@ RELOPT_JOINREL
Definition: pathnodes.h:884
#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:3581
Definition: pg_list.h:54
List * pathkeys
Definition: pathnodes.h:1912
Cardinality rows
Definition: pathnodes.h:1906
Cost startup_cost
Definition: pathnodes.h:1908
int disabled_nodes
Definition: pathnodes.h:1907
Cost total_cost
Definition: pathnodes.h:1909
PathTarget * target
Definition: postgres_fdw.c:295
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:954
RelOptKind reloptkind
Definition: pathnodes.h:921
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 6701 of file postgres_fdw.c.

6704{
6705 Query *parse = root->parse;
6706 PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6707 PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
6708 ForeignPath *grouppath;
6709 double rows;
6710 int width;
6711 int disabled_nodes;
6712 Cost startup_cost;
6713 Cost total_cost;
6714
6715 /* Nothing to be done, if there is no grouping or aggregation required. */
6716 if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
6717 !root->hasHavingQual)
6718 return;
6719
6722
6723 /* save the input_rel as outerrel in fpinfo */
6724 fpinfo->outerrel = input_rel;
6725
6726 /*
6727 * Copy foreign table, foreign server, user mapping, FDW options etc.
6728 * details from the input relation's fpinfo.
6729 */
6730 fpinfo->table = ifpinfo->table;
6731 fpinfo->server = ifpinfo->server;
6732 fpinfo->user = ifpinfo->user;
6733 merge_fdw_options(fpinfo, ifpinfo, NULL);
6734
6735 /*
6736 * Assess if it is safe to push down aggregation and grouping.
6737 *
6738 * Use HAVING qual from extra. In case of child partition, it will have
6739 * translated Vars.
6740 */
6741 if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
6742 return;
6743
6744 /*
6745 * Compute the selectivity and cost of the local_conds, so we don't have
6746 * to do it over again for each path. (Currently we create just a single
6747 * path here, but in future it would be possible that we build more paths
6748 * such as pre-sorted paths as in postgresGetForeignPaths and
6749 * postgresGetForeignJoinPaths.) The best we can do for these conditions
6750 * is to estimate selectivity on the basis of local statistics.
6751 */
6753 fpinfo->local_conds,
6754 0,
6755 JOIN_INNER,
6756 NULL);
6757
6758 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6759
6760 /* Estimate the cost of push down */
6761 estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
6762 &rows, &width, &disabled_nodes,
6763 &startup_cost, &total_cost);
6764
6765 /* Now update this information in the fpinfo */
6766 fpinfo->rows = rows;
6767 fpinfo->width = width;
6768 fpinfo->disabled_nodes = disabled_nodes;
6769 fpinfo->startup_cost = startup_cost;
6770 fpinfo->total_cost = total_cost;
6771
6772 /* Create and add foreign path to the grouping relation. */
6773 grouppath = create_foreign_upper_path(root,
6774 grouped_rel,
6775 grouped_rel->reltarget,
6776 rows,
6777 disabled_nodes,
6778 startup_cost,
6779 total_cost,
6780 NIL, /* no pathkeys */
6781 NULL,
6782 NIL, /* no fdw_restrictinfo list */
6783 NIL); /* no fdw_private */
6784
6785 /* Add generated path into grouped_rel by add_path(). */
6786 add_path(grouped_rel, (Path *) grouppath);
6787}
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:4765
@ JOIN_INNER
Definition: nodes.h:303
@ PARTITIONWISE_AGGREGATE_FULL
Definition: pathnodes.h:3536
@ PARTITIONWISE_AGGREGATE_NONE
Definition: pathnodes.h:3535
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
PartitionwiseAggregateType patype
Definition: pathnodes.h:3565
Selectivity local_conds_sel
Definition: postgres_fdw.h:57
QualCost local_conds_cost
Definition: postgres_fdw.h:56
struct PathTarget * reltarget
Definition: pathnodes.h:949

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

6799{
6800 Query *parse = root->parse;
6801 PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
6802 PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
6803 PgFdwPathExtraData *fpextra;
6804 double rows;
6805 int width;
6806 int disabled_nodes;
6807 Cost startup_cost;
6808 Cost total_cost;
6809 List *fdw_private;
6810 ForeignPath *ordered_path;
6811 ListCell *lc;
6812
6813 /* Shouldn't get here unless the query has ORDER BY */
6814 Assert(parse->sortClause);
6815
6816 /* We don't support cases where there are any SRFs in the targetlist */
6817 if (parse->hasTargetSRFs)
6818 return;
6819
6820 /* Save the input_rel as outerrel in fpinfo */
6821 fpinfo->outerrel = input_rel;
6822
6823 /*
6824 * Copy foreign table, foreign server, user mapping, FDW options etc.
6825 * details from the input relation's fpinfo.
6826 */
6827 fpinfo->table = ifpinfo->table;
6828 fpinfo->server = ifpinfo->server;
6829 fpinfo->user = ifpinfo->user;
6830 merge_fdw_options(fpinfo, ifpinfo, NULL);
6831
6832 /*
6833 * If the input_rel is a base or join relation, we would already have
6834 * considered pushing down the final sort to the remote server when
6835 * creating pre-sorted foreign paths for that relation, because the
6836 * query_pathkeys is set to the root->sort_pathkeys in that case (see
6837 * standard_qp_callback()).
6838 */
6839 if (input_rel->reloptkind == RELOPT_BASEREL ||
6840 input_rel->reloptkind == RELOPT_JOINREL)
6841 {
6842 Assert(root->query_pathkeys == root->sort_pathkeys);
6843
6844 /* Safe to push down if the query_pathkeys is safe to push down */
6845 fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
6846
6847 return;
6848 }
6849
6850 /* The input_rel should be a grouping relation */
6851 Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
6852 ifpinfo->stage == UPPERREL_GROUP_AGG);
6853
6854 /*
6855 * We try to create a path below by extending a simple foreign path for
6856 * the underlying grouping relation to perform the final sort remotely,
6857 * which is stored into the fdw_private list of the resulting path.
6858 */
6859
6860 /* Assess if it is safe to push down the final sort */
6861 foreach(lc, root->sort_pathkeys)
6862 {
6863 PathKey *pathkey = (PathKey *) lfirst(lc);
6864 EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
6865
6866 /*
6867 * is_foreign_expr would detect volatile expressions as well, but
6868 * checking ec_has_volatile here saves some cycles.
6869 */
6870 if (pathkey_ec->ec_has_volatile)
6871 return;
6872
6873 /*
6874 * Can't push down the sort if pathkey's opfamily is not shippable.
6875 */
6876 if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId,
6877 fpinfo))
6878 return;
6879
6880 /*
6881 * The EC must contain a shippable EM that is computed in input_rel's
6882 * reltarget, else we can't push down the sort.
6883 */
6885 pathkey_ec,
6886 input_rel) == NULL)
6887 return;
6888 }
6889
6890 /* Safe to push down */
6891 fpinfo->pushdown_safe = true;
6892
6893 /* Construct PgFdwPathExtraData */
6894 fpextra = (PgFdwPathExtraData *) palloc0(sizeof(PgFdwPathExtraData));
6895 fpextra->target = root->upper_targets[UPPERREL_ORDERED];
6896 fpextra->has_final_sort = true;
6897
6898 /* Estimate the costs of performing the final sort remotely */
6899 estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
6900 &rows, &width, &disabled_nodes,
6901 &startup_cost, &total_cost);
6902
6903 /*
6904 * Build the fdw_private list that will be used by postgresGetForeignPlan.
6905 * Items in the list must match order in enum FdwPathPrivateIndex.
6906 */
6907 fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
6908
6909 /* Create foreign ordering path */
6910 ordered_path = create_foreign_upper_path(root,
6911 input_rel,
6912 root->upper_targets[UPPERREL_ORDERED],
6913 rows,
6914 disabled_nodes,
6915 startup_cost,
6916 total_cost,
6917 root->sort_pathkeys,
6918 NULL, /* no extra plan */
6919 NIL, /* no fdw_restrictinfo
6920 * list */
6921 fdw_private);
6922
6923 /* and add it to the ordered_rel */
6924 add_path(ordered_rel, (Path *) ordered_path);
6925}
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:1715

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

6022{
6023 List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6024 ListCell *lc;
6025
6026 useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
6027
6028 /*
6029 * Before creating sorted paths, arrange for the passed-in EPQ path, if
6030 * any, to return columns needed by the parent ForeignScan node so that
6031 * they will propagate up through Sort nodes injected below, if necessary.
6032 */
6033 if (epq_path != NULL && useful_pathkeys_list != NIL)
6034 {
6035 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6036 PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6037
6038 /* Include columns required for evaluating PHVs in the tlist. */
6040 pull_var_clause((Node *) target->exprs,
6042
6043 /* Include columns required for evaluating the local conditions. */
6044 foreach(lc, fpinfo->local_conds)
6045 {
6047
6049 pull_var_clause((Node *) rinfo->clause,
6051 }
6052
6053 /*
6054 * If we have added any new columns, adjust the tlist of the EPQ path.
6055 *
6056 * Note: the plan created using this path will only be used to execute
6057 * EPQ checks, where accuracy of the plan cost and width estimates
6058 * would not be important, so we do not do set_pathtarget_cost_width()
6059 * for the new pathtarget here. See also postgresGetForeignPlan().
6060 */
6061 if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6062 {
6063 /* The EPQ path is a join path, so it is projection-capable. */
6065
6066 /*
6067 * Use create_projection_path() here, so as to avoid modifying it
6068 * in place.
6069 */
6070 epq_path = (Path *) create_projection_path(root,
6071 rel,
6072 epq_path,
6073 target);
6074 }
6075 }
6076
6077 /* Create one path for each set of pathkeys we found above. */
6078 foreach(lc, useful_pathkeys_list)
6079 {
6080 double rows;
6081 int width;
6082 int disabled_nodes;
6083 Cost startup_cost;
6084 Cost total_cost;
6085 List *useful_pathkeys = lfirst(lc);
6086 Path *sorted_epq_path;
6087
6088 estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
6089 &rows, &width, &disabled_nodes,
6090 &startup_cost, &total_cost);
6091
6092 /*
6093 * The EPQ path must be at least as well sorted as the path itself, in
6094 * case it gets used as input to a mergejoin.
6095 */
6096 sorted_epq_path = epq_path;
6097 if (sorted_epq_path != NULL &&
6098 !pathkeys_contained_in(useful_pathkeys,
6099 sorted_epq_path->pathkeys))
6100 sorted_epq_path = (Path *)
6102 rel,
6103 sorted_epq_path,
6104 useful_pathkeys,
6105 -1.0);
6106
6107 if (IS_SIMPLE_REL(rel))
6108 add_path(rel, (Path *)
6110 NULL,
6111 rows,
6112 disabled_nodes,
6113 startup_cost,
6114 total_cost,
6115 useful_pathkeys,
6116 rel->lateral_relids,
6117 sorted_epq_path,
6118 NIL, /* no fdw_restrictinfo
6119 * list */
6120 NIL));
6121 else
6122 add_path(rel, (Path *)
6124 NULL,
6125 rows,
6126 disabled_nodes,
6127 startup_cost,
6128 total_cost,
6129 useful_pathkeys,
6130 rel->lateral_relids,
6131 sorted_epq_path,
6132 restrictlist,
6133 NIL));
6134 }
6135}
bool is_projection_capable_path(Path *path)
Definition: createplan.c:7217
#define PVC_RECURSE_PLACEHOLDERS
Definition: optimizer.h:190
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:2524
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition: pathnode.c:2841
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:2065
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:2113
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:895
#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:909
Definition: nodes.h:135
Relids lateral_relids
Definition: pathnodes.h:968
Expr * clause
Definition: pathnodes.h:2791
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 *  p_disabled_nodes,
Cost p_startup_cost,
Cost p_run_cost 
)
static

Definition at line 3640 of file postgres_fdw.c.

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

5307{
5308 int targrows = astate->targrows;
5309 int pos; /* array index to store tuple in */
5310 MemoryContext oldcontext;
5311
5312 /* Always increment sample row counter. */
5313 astate->samplerows += 1;
5314
5315 /*
5316 * Determine the slot where this sample row should be stored. Set pos to
5317 * negative value to indicate the row should be skipped.
5318 */
5319 if (astate->numrows < targrows)
5320 {
5321 /* First targrows rows are always included into the sample */
5322 pos = astate->numrows++;
5323 }
5324 else
5325 {
5326 /*
5327 * Now we start replacing tuples in the sample until we reach the end
5328 * of the relation. Same algorithm as in acquire_sample_rows in
5329 * analyze.c; see Jeff Vitter's paper.
5330 */
5331 if (astate->rowstoskip < 0)
5332 astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5333
5334 if (astate->rowstoskip <= 0)
5335 {
5336 /* Choose a random reservoir element to replace. */
5337 pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5338 Assert(pos >= 0 && pos < targrows);
5339 heap_freetuple(astate->rows[pos]);
5340 }
5341 else
5342 {
5343 /* Skip this tuple. */
5344 pos = -1;
5345 }
5346
5347 astate->rowstoskip -= 1;
5348 }
5349
5350 if (pos >= 0)
5351 {
5352 /*
5353 * Create sample tuple from current result row, and store it in the
5354 * position determined above. The tuple has to be created in anl_cxt.
5355 */
5356 oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5357
5358 astate->rows[pos] = make_tuple_from_result_row(res, row,
5359 astate->rel,
5360 astate->attinmeta,
5361 astate->retrieved_attrs,
5362 NULL,
5363 astate->temp_cxt);
5364
5365 MemoryContextSwitchTo(oldcontext);
5366 }
5367}
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:270
AttInMetadata * attinmeta
Definition: postgres_fdw.c:259
MemoryContext anl_cxt
Definition: postgres_fdw.c:273
HeapTuple * rows
Definition: postgres_fdw.c:263
MemoryContext temp_cxt
Definition: postgres_fdw.c:274
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 4705 of file postgres_fdw.c.

4709{
4710 TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4711 TupleTableSlot *resultSlot;
4712 Datum *values;
4713 bool *isnull;
4714 Datum *old_values;
4715 bool *old_isnull;
4716 int i;
4717
4718 /*
4719 * Use the return tuple slot as a place to store the result tuple.
4720 */
4721 resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4722
4723 /*
4724 * Extract all the values of the scan tuple.
4725 */
4726 slot_getallattrs(slot);
4727 old_values = slot->tts_values;
4728 old_isnull = slot->tts_isnull;
4729
4730 /*
4731 * Prepare to build the result tuple.
4732 */
4733 ExecClearTuple(resultSlot);
4734 values = resultSlot->tts_values;
4735 isnull = resultSlot->tts_isnull;
4736
4737 /*
4738 * Transpose data into proper fields of the result tuple.
4739 */
4740 for (i = 0; i < resultTupType->natts; i++)
4741 {
4742 int j = dmstate->attnoMap[i];
4743
4744 if (j == 0)
4745 {
4746 values[i] = (Datum) 0;
4747 isnull[i] = true;
4748 }
4749 else
4750 {
4751 values[i] = old_values[j - 1];
4752 isnull[i] = old_isnull[j - 1];
4753 }
4754 }
4755
4756 /*
4757 * Build the virtual tuple.
4758 */
4759 ExecStoreVirtualTuple(resultSlot);
4760
4761 /*
4762 * If we have any system columns to return, materialize a heap tuple in
4763 * the slot from column values set above and install system columns in
4764 * that tuple.
4765 */
4766 if (dmstate->hasSystemCols)
4767 {
4768 HeapTuple resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
4769
4770 /* ctid */
4771 if (dmstate->ctidAttno)
4772 {
4773 ItemPointer ctid = NULL;
4774
4775 ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4776 resultTup->t_self = *ctid;
4777 }
4778
4779 /*
4780 * And remaining columns
4781 *
4782 * Note: since we currently don't allow the target relation to appear
4783 * on the nullable side of an outer join, any system columns wouldn't
4784 * go to NULL.
4785 *
4786 * Note: no need to care about tableoid here because it will be
4787 * initialized in ExecProcessReturning().
4788 */
4792 }
4793
4794 /*
4795 * And return the result tuple.
4796 */
4797 return resultSlot;
4798}
static Datum values[MAXATTR]
Definition: bootstrap.c:153
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1741
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1833
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1248
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:78
int i
Definition: isn.c:77
ItemPointerData * ItemPointer
Definition: itemptr.h:49
uint64_t Datum
Definition: postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
#define RelationGetDescr(relation)
Definition: rel.h:541
ItemPointerData t_self
Definition: htup.h:65
HeapTupleHeader t_data
Definition: htup.h:68
bool * tts_isnull
Definition: tuptable.h:126
Datum * tts_values
Definition: tuptable.h:124
#define InvalidTransactionId
Definition: transam.h:31
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:457
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:371

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

6144{
6145 ListCell *lc;
6146
6147 foreach(lc, fpinfo->server->options)
6148 {
6149 DefElem *def = (DefElem *) lfirst(lc);
6150
6151 if (strcmp(def->defname, "use_remote_estimate") == 0)
6152 fpinfo->use_remote_estimate = defGetBoolean(def);
6153 else if (strcmp(def->defname, "fdw_startup_cost") == 0)
6154 (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
6155 NULL);
6156 else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
6157 (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
6158 NULL);
6159 else if (strcmp(def->defname, "extensions") == 0)
6160 fpinfo->shippable_extensions =
6162 else if (strcmp(def->defname, "fetch_size") == 0)
6163 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6164 else if (strcmp(def->defname, "async_capable") == 0)
6165 fpinfo->async_capable = defGetBoolean(def);
6166 }
6167}
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:445
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:2743
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition: guc.c:2833
char * defname
Definition: parsenodes.h:843
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 6175 of file postgres_fdw.c.

6176{
6177 ListCell *lc;
6178
6179 foreach(lc, fpinfo->table->options)
6180 {
6181 DefElem *def = (DefElem *) lfirst(lc);
6182
6183 if (strcmp(def->defname, "use_remote_estimate") == 0)
6184 fpinfo->use_remote_estimate = defGetBoolean(def);
6185 else if (strcmp(def->defname, "fetch_size") == 0)
6186 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
6187 else if (strcmp(def->defname, "async_capable") == 0)
6188 fpinfo->async_capable = defGetBoolean(def);
6189 }
6190}
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 4388 of file postgres_fdw.c.

4389{
4390 bool have_wholerow = false;
4391 List *tlist = NIL;
4392 List *vars;
4393 ListCell *lc;
4394
4395 Assert(returningList);
4396
4397 vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4398
4399 /*
4400 * If there's a whole-row reference to the target relation, then we'll
4401 * need all the columns of the relation.
4402 */
4403 foreach(lc, vars)
4404 {
4405 Var *var = (Var *) lfirst(lc);
4406
4407 if (IsA(var, Var) &&
4408 var->varno == rtindex &&
4410 {
4411 have_wholerow = true;
4412 break;
4413 }
4414 }
4415
4416 if (have_wholerow)
4417 {
4418 TupleDesc tupdesc = RelationGetDescr(rel);
4419 int i;
4420
4421 for (i = 1; i <= tupdesc->natts; i++)
4422 {
4423 Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4424 Var *var;
4425
4426 /* Ignore dropped attributes. */
4427 if (attr->attisdropped)
4428 continue;
4429
4430 var = makeVar(rtindex,
4431 i,
4432 attr->atttypid,
4433 attr->atttypmod,
4434 attr->attcollation,
4435 0);
4436
4437 tlist = lappend(tlist,
4438 makeTargetEntry((Expr *) var,
4439 list_length(tlist) + 1,
4440 NULL,
4441 false));
4442 }
4443 }
4444
4445 /* Now add any remaining columns to tlist. */
4446 foreach(lc, vars)
4447 {
4448 Var *var = (Var *) lfirst(lc);
4449
4450 /*
4451 * No need for whole-row references to the target relation. We don't
4452 * need system columns other than ctid and oid either, since those are
4453 * set locally.
4454 */
4455 if (IsA(var, Var) &&
4456 var->varno == rtindex &&
4457 var->varattno <= InvalidAttrNumber &&
4459 continue; /* don't need it */
4460
4461 if (tlist_member((Expr *) var, tlist))
4462 continue; /* already got it */
4463
4464 tlist = lappend(tlist,
4465 makeTargetEntry((Expr *) var,
4466 list_length(tlist) + 1,
4467 NULL,
4468 false));
4469 }
4470
4471 list_free(vars);
4472
4473 return tlist;
4474}
#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:189
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
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:160

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

3940{
3941 char sql[64];
3942 PGresult *res;
3943
3944 snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
3945 res = pgfdw_exec_query(conn, sql, conn_state);
3946 if (PQresultStatus(res) != PGRES_COMMAND_OK)
3947 pgfdw_report_error(res, conn, sql);
3948 PQclear(res);
3949}
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
Definition: connection.c:947
void pgfdw_report_error(PGresult *res, PGconn *conn, const char *sql)
Definition: connection.c:988
static unsigned int cursor_number
Definition: connection.c:82
#define PQclear
Definition: libpq-be-fe.h:245
#define PQresultStatus
Definition: libpq-be-fe.h:247
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:125
#define snprintf
Definition: port.h:239
PGconn * conn
Definition: streamutil.c:52

References conn, cursor_number, 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 7457 of file postgres_fdw.c.

7458{
7459 /* The request would have been pending for a callback */
7460 Assert(areq->callback_pending);
7461
7462 /* Unlike AsyncNotify, we unset callback_pending ourselves */
7463 areq->callback_pending = false;
7464
7465 /* We begin a fetch afterwards if necessary; don't fetch */
7466 produce_tuple_asynchronously(areq, false);
7467
7468 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7469 ExecAsyncResponse(areq);
7470
7471 /* Also, we do instrumentation ourselves, if required */
7472 if (areq->requestee->instrument)
7474 TupIsNull(areq->result) ? 0.0 : 1.0);
7475}
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:644
bool callback_pending
Definition: execnodes.h:642
PlanState * requestee
Definition: execnodes.h:640
Instrumentation * instrument
Definition: execnodes.h:1175
#define TupIsNull(slot)
Definition: tuptable.h:309

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

7649{
7651 Relation rel = errpos->rel;
7652 ForeignScanState *fsstate = errpos->fsstate;
7653 const char *attname = NULL;
7654 const char *relname = NULL;
7655 bool is_wholerow = false;
7656
7657 /*
7658 * If we're in a scan node, always use aliases from the rangetable, for
7659 * consistency between the simple-relation and remote-join cases. Look at
7660 * the relation's tupdesc only if we're not in a scan node.
7661 */
7662 if (fsstate)
7663 {
7664 /* ForeignScan case */
7665 ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
7666 int varno = 0;
7667 AttrNumber colno = 0;
7668
7669 if (fsplan->scan.scanrelid > 0)
7670 {
7671 /* error occurred in a scan against a foreign table */
7672 varno = fsplan->scan.scanrelid;
7673 colno = errpos->cur_attno;
7674 }
7675 else
7676 {
7677 /* error occurred in a scan against a foreign join */
7678 TargetEntry *tle;
7679
7681 errpos->cur_attno - 1);
7682
7683 /*
7684 * Target list can have Vars and expressions. For Vars, we can
7685 * get some information, however for expressions we can't. Thus
7686 * for expressions, just show generic context message.
7687 */
7688 if (IsA(tle->expr, Var))
7689 {
7690 Var *var = (Var *) tle->expr;
7691
7692 varno = var->varno;
7693 colno = var->varattno;
7694 }
7695 }
7696
7697 if (varno > 0)
7698 {
7699 EState *estate = fsstate->ss.ps.state;
7700 RangeTblEntry *rte = exec_rt_fetch(varno, estate);
7701
7702 relname = rte->eref->aliasname;
7703
7704 if (colno == 0)
7705 is_wholerow = true;
7706 else if (colno > 0 && colno <= list_length(rte->eref->colnames))
7707 attname = strVal(list_nth(rte->eref->colnames, colno - 1));
7708 else if (colno == SelfItemPointerAttributeNumber)
7709 attname = "ctid";
7710 }
7711 }
7712 else if (rel)
7713 {
7714 /* Non-ForeignScan case (we should always have a rel here) */
7715 TupleDesc tupdesc = RelationGetDescr(rel);
7716
7718 if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
7719 {
7720 Form_pg_attribute attr = TupleDescAttr(tupdesc,
7721 errpos->cur_attno - 1);
7722
7723 attname = NameStr(attr->attname);
7724 }
7725 else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
7726 attname = "ctid";
7727 }
7728
7729 if (relname && is_wholerow)
7730 errcontext("whole-row reference to foreign table \"%s\"", relname);
7731 else if (relname && attname)
7732 errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
7733 else
7734 errcontext("processing expression at position %d in select list",
7735 errpos->cur_attno);
7736}
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:755
#define errcontext
Definition: elog.h:198
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:697
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
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:549
AttrNumber cur_attno
Definition: postgres_fdw.c:308
ForeignScanState * fsstate
Definition: postgres_fdw.c:310
ScanState ss
Definition: execnodes.h:2111
List * fdw_scan_tlist
Definition: plannodes.h:885
Plan * plan
Definition: execnodes.h:1165
EState * state
Definition: execnodes.h:1167
PlanState ps
Definition: execnodes.h:1621
Index scanrelid
Definition: plannodes.h:523
Expr * expr
Definition: primnodes.h:2239
#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 4246 of file postgres_fdw.c.

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

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

3726{
3727 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3728 ExprContext *econtext = node->ss.ps.ps_ExprContext;
3729 int numParams = fsstate->numParams;
3730 const char **values = fsstate->param_values;
3731 PGconn *conn = fsstate->conn;
3733 PGresult *res;
3734
3735 /* First, process a pending asynchronous request, if any. */
3736 if (fsstate->conn_state->pendingAreq)
3738
3739 /*
3740 * Construct array of query parameter values in text format. We do the
3741 * conversions in the short-lived per-tuple context, so as not to cause a
3742 * memory leak over repeated scans.
3743 */
3744 if (numParams > 0)
3745 {
3746 MemoryContext oldcontext;
3747
3748 oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3749
3750 process_query_params(econtext,
3751 fsstate->param_flinfo,
3752 fsstate->param_exprs,
3753 values);
3754
3755 MemoryContextSwitchTo(oldcontext);
3756 }
3757
3758 /* Construct the DECLARE CURSOR command */
3760 appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3761 fsstate->cursor_number, fsstate->query);
3762
3763 /*
3764 * Notice that we pass NULL for paramTypes, thus forcing the remote server
3765 * to infer types for all parameters. Since we explicitly cast every
3766 * parameter (see deparse.c), the "inference" is trivial and will produce
3767 * the desired result. This allows us to avoid assuming that the remote
3768 * server has the same OIDs we do for the parameters' types.
3769 */
3770 if (!PQsendQueryParams(conn, buf.data, numParams,
3771 NULL, values, NULL, NULL, 0))
3772 pgfdw_report_error(NULL, conn, buf.data);
3773
3774 /*
3775 * Get the result, and check for success.
3776 */
3777 res = pgfdw_get_result(conn);
3778 if (PQresultStatus(res) != PGRES_COMMAND_OK)
3779 pgfdw_report_error(res, conn, fsstate->query);
3780 PQclear(res);
3781
3782 /* Mark the cursor as created, and show no tuples have been retrieved */
3783 fsstate->cursor_exists = true;
3784 fsstate->tuples = NULL;
3785 fsstate->num_tuples = 0;
3786 fsstate->next_tuple = 0;
3787 fsstate->fetch_ct_2 = 0;
3788 fsstate->eof_reached = false;
3789
3790 /* Clean up */
3791 pfree(buf.data);
3792}
PGresult * pgfdw_get_result(PGconn *conn)
Definition: connection.c:964
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:1508
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
void pfree(void *pointer)
Definition: mcxt.c:1594
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:156
const char ** param_values
Definition: postgres_fdw.c:158
unsigned int cursor_number
Definition: postgres_fdw.c:153
List * param_exprs
Definition: postgres_fdw.c:157
PgFdwConnState * conn_state
Definition: postgres_fdw.c:152
HeapTuple * tuples
Definition: postgres_fdw.c:161
ExprContext * ps_ExprContext
Definition: execnodes.h:1204

References appendStringInfo(), buf, PgFdwScanState::conn, conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, 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 3957 of file postgres_fdw.c.

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

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

4365{
4366 char sql[64];
4367 PGresult *res;
4368
4369 /* do nothing if the query is not allocated */
4370 if (!fmstate->p_name)
4371 return;
4372
4373 snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4374 res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4375 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4376 pgfdw_report_error(res, fmstate->conn, sql);
4377 PQclear(res);
4378 pfree(fmstate->p_name);
4379 fmstate->p_name = NULL;
4380}

References PgFdwModifyState::conn, PgFdwModifyState::conn_state, 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 3696 of file postgres_fdw.c.

3699{
3701 Expr *expr = em->em_expr;
3702
3703 /*
3704 * If we've identified what we're processing in the current scan, we only
3705 * want to match that expression.
3706 */
3707 if (state->current != NULL)
3708 return equal(expr, state->current);
3709
3710 /*
3711 * Otherwise, ignore anything we've already processed.
3712 */
3713 if (list_member(state->already_used, expr))
3714 return false;
3715
3716 /* This is the new target to process. */
3717 state->current = expr;
3718 return true;
3719}
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 3089 of file postgres_fdw.c.

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

4516{
4518 ExprContext *econtext = node->ss.ps.ps_ExprContext;
4519 int numParams = dmstate->numParams;
4520 const char **values = dmstate->param_values;
4521
4522 /* First, process a pending asynchronous request, if any. */
4523 if (dmstate->conn_state->pendingAreq)
4525
4526 /*
4527 * Construct array of query parameter values in text format.
4528 */
4529 if (numParams > 0)
4530 process_query_params(econtext,
4531 dmstate->param_flinfo,
4532 dmstate->param_exprs,
4533 values);
4534
4535 /*
4536 * Notice that we pass NULL for paramTypes, thus forcing the remote server
4537 * to infer types for all parameters. Since we explicitly cast every
4538 * parameter (see deparse.c), the "inference" is trivial and will produce
4539 * the desired result. This allows us to avoid assuming that the remote
4540 * server has the same OIDs we do for the parameters' types.
4541 */
4542 if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4543 NULL, values, NULL, NULL, 0))
4544 pgfdw_report_error(NULL, dmstate->conn, dmstate->query);
4545
4546 /*
4547 * Get the result, and check for success.
4548 */
4549 dmstate->result = pgfdw_get_result(dmstate->conn);
4550 if (PQresultStatus(dmstate->result) !=
4552 pgfdw_report_error(dmstate->result, dmstate->conn,
4553 dmstate->query);
4554
4555 /*
4556 * The result potentially needs to survive across multiple executor row
4557 * cycles, so move it to the context where the dmstate is.
4558 */
4559 dmstate->result = libpqsrv_PGresultSetParent(dmstate->result,
4560 GetMemoryChunkContext(dmstate));
4561
4562 /* Get the number of rows affected. */
4563 if (dmstate->has_returning)
4564 dmstate->num_tuples = PQntuples(dmstate->result);
4565 else
4566 dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4567}
static libpqsrv_PGresult * libpqsrv_PGresultSetParent(libpqsrv_PGresult *bres, MemoryContext ctx)
Definition: libpq-be-fe.h:109
#define PQcmdTuples
Definition: libpq-be-fe.h:257
#define PQntuples
Definition: libpq-be-fe.h:251
@ PGRES_TUPLES_OK
Definition: libpq-fe.h:128
MemoryContext GetMemoryChunkContext(void *pointer)
Definition: mcxt.c:753
PgFdwConnState * conn_state
Definition: postgres_fdw.c:233
const char ** param_values
Definition: postgres_fdw.c:237

References PgFdwDirectModifyState::conn, PgFdwDirectModifyState::conn_state, ForeignScanState::fdw_state, GetMemoryChunkContext(), PgFdwDirectModifyState::has_returning, if(), libpqsrv_PGresultSetParent(), 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 4077 of file postgres_fdw.c.

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

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

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

References Assert(), PgFdwScanState::async_capable, PgFdwScanState::attinmeta, PgFdwScanState::batch_cxt, PgFdwScanState::conn, conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, 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, 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 7397 of file postgres_fdw.c.

7398{
7400 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7401 char sql[64];
7402
7403 Assert(!fsstate->conn_state->pendingAreq);
7404
7405 /* Create the cursor synchronously. */
7406 if (!fsstate->cursor_exists)
7407 create_cursor(node);
7408
7409 /* We will send this query, but not wait for the response. */
7410 snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
7411 fsstate->fetch_size, fsstate->cursor_number);
7412
7413 if (!PQsendQuery(fsstate->conn, sql))
7414 pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
7415
7416 /* Remember that the request is in process */
7417 fsstate->conn_state->pendingAreq = areq;
7418}
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1432
static void create_cursor(ForeignScanState *node)

References Assert(), create_cursor(), 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 7751 of file postgres_fdw.c.

7752{
7753 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7756
7757 setup_eclass_member_iterator(&it, ec, rel->relids);
7758 while ((em = eclass_member_iterator_next(&it)) != NULL)
7759 {
7760 /*
7761 * Note we require !bms_is_empty, else we'd accept constant
7762 * expressions which are not suitable for the purpose.
7763 */
7764 if (bms_is_subset(em->em_relids, rel->relids) &&
7765 !bms_is_empty(em->em_relids) &&
7766 bms_is_empty(bms_intersect(em->em_relids, fpinfo->hidden_subquery_rels)) &&
7767 is_foreign_expr(root, rel, em->em_expr))
7768 return em;
7769 }
7770
7771 return NULL;
7772}
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
void setup_eclass_member_iterator(EquivalenceMemberIterator *it, EquivalenceClass *ec, Relids child_relids)
Definition: equivclass.c:3156
EquivalenceMember * eclass_member_iterator_next(EquivalenceMemberIterator *it)
Definition: equivclass.c:3175
Relids hidden_subquery_rels
Definition: postgres_fdw.h:122
Relids relids
Definition: pathnodes.h:927

References bms_intersect(), bms_is_empty, bms_is_subset(), eclass_member_iterator_next(), PgFdwRelationInfo::hidden_subquery_rels, is_foreign_expr(), RelOptInfo::relids, root, and setup_eclass_member_iterator().

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

7788{
7789 PathTarget *target = rel->reltarget;
7790 ListCell *lc1;
7791 int i;
7792
7793 i = 0;
7794 foreach(lc1, target->exprs)
7795 {
7796 Expr *expr = (Expr *) lfirst(lc1);
7797 Index sgref = get_pathtarget_sortgroupref(target, i);
7798 ListCell *lc2;
7799
7800 /* Ignore non-sort expressions */
7801 if (sgref == 0 ||
7803 root->parse->sortClause) == NULL)
7804 {
7805 i++;
7806 continue;
7807 }
7808
7809 /* We ignore binary-compatible relabeling on both ends */
7810 while (expr && IsA(expr, RelabelType))
7811 expr = ((RelabelType *) expr)->arg;
7812
7813 /*
7814 * Locate an EquivalenceClass member matching this expr, if any.
7815 * Ignore child members.
7816 */
7817 foreach(lc2, ec->ec_members)
7818 {
7820 Expr *em_expr;
7821
7822 /* Don't match constants */
7823 if (em->em_is_const)
7824 continue;
7825
7826 /* Child members should not exist in ec_members */
7827 Assert(!em->em_is_child);
7828
7829 /* Match if same expression (after stripping relabel) */
7830 em_expr = em->em_expr;
7831 while (em_expr && IsA(em_expr, RelabelType))
7832 em_expr = ((RelabelType *) em_expr)->arg;
7833
7834 if (!equal(em_expr, expr))
7835 continue;
7836
7837 /* Check that expression (including relabels!) is shippable */
7838 if (is_foreign_expr(root, rel, em->em_expr))
7839 return em;
7840 }
7841
7842 i++;
7843 }
7844
7845 return NULL;
7846}
unsigned int Index
Definition: c.h:623
#define get_pathtarget_sortgroupref(target, colno)
Definition: pathnodes.h:1795
List * exprs
Definition: pathnodes.h:1779
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition: tlist.c:443

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

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

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

4347{
4348 Assert(fmstate != NULL);
4349
4350 /* If we created a prepared statement, destroy it */
4351 deallocate_query(fmstate);
4352
4353 /* Release remote connection */
4354 ReleaseConnection(fmstate->conn);
4355 fmstate->conn = NULL;
4356}

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

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

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

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

7854{
7855 Oid foreigntableid = RelationGetRelid(rel);
7857 ForeignServer *server;
7858 List *options;
7859 ListCell *lc;
7860
7861 /* we use 1 by default, which means "no batching" */
7862 int batch_size = 1;
7863
7864 /*
7865 * Load options for table and server. We append server options after table
7866 * options, because table options take precedence.
7867 */
7868 table = GetForeignTable(foreigntableid);
7869 server = GetForeignServer(table->serverid);
7870
7871 options = NIL;
7872 options = list_concat(options, table->options);
7873 options = list_concat(options, server->options);
7874
7875 /* See if either table or server specifies batch_size. */
7876 foreach(lc, options)
7877 {
7878 DefElem *def = (DefElem *) lfirst(lc);
7879
7880 if (strcmp(def->defname, "batch_size") == 0)
7881 {
7882 (void) parse_int(defGetString(def), &batch_size, 0, NULL);
7883 break;
7884 }
7885 }
7886
7887 return batch_size;
7888}
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:112
static char ** options

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

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

3606{
3607 PGresult *res;
3608 char *line;
3609 char *p;
3610 int n;
3611
3612 /*
3613 * Execute EXPLAIN remotely.
3614 */
3615 res = pgfdw_exec_query(conn, sql, NULL);
3616 if (PQresultStatus(res) != PGRES_TUPLES_OK)
3617 pgfdw_report_error(res, conn, sql);
3618
3619 /*
3620 * Extract cost numbers for topmost plan node. Note we search for a left
3621 * paren from the end of the line to avoid being confused by other uses of
3622 * parentheses.
3623 */
3624 line = PQgetvalue(res, 0, 0);
3625 p = strrchr(line, '(');
3626 if (p == NULL)
3627 elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3628 n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3629 startup_cost, total_cost, rows, width);
3630 if (n != 4)
3631 elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3632 PQclear(res);
3633}
#define PQgetvalue
Definition: libpq-be-fe.h:253

References conn, elog, ERROR, 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 4573 of file postgres_fdw.c.

4574{
4576 EState *estate = node->ss.ps.state;
4577 ResultRelInfo *resultRelInfo = node->resultRelInfo;
4578 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4579 TupleTableSlot *resultSlot;
4580
4581 Assert(resultRelInfo->ri_projectReturning);
4582
4583 /* If we didn't get any tuples, must be end of data. */
4584 if (dmstate->next_tuple >= dmstate->num_tuples)
4585 return ExecClearTuple(slot);
4586
4587 /* Increment the command es_processed count if necessary. */
4588 if (dmstate->set_processed)
4589 estate->es_processed += 1;
4590
4591 /*
4592 * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4593 * tuple. (has_returning is false when the local query is of the form
4594 * "UPDATE/DELETE .. RETURNING 1" for example.)
4595 */
4596 if (!dmstate->has_returning)
4597 {
4599 resultSlot = slot;
4600 }
4601 else
4602 {
4603 HeapTuple newtup;
4604
4605 newtup = make_tuple_from_result_row(dmstate->result,
4606 dmstate->next_tuple,
4607 dmstate->rel,
4608 dmstate->attinmeta,
4609 dmstate->retrieved_attrs,
4610 node,
4611 dmstate->temp_cxt);
4612 ExecStoreHeapTuple(newtup, slot, false);
4613 /* Get the updated/deleted tuple. */
4614 if (dmstate->rel)
4615 resultSlot = slot;
4616 else
4617 resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4618 }
4619 dmstate->next_tuple++;
4620
4621 /* Make slot available for evaluation of the local query RETURNING list. */
4622 resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple =
4623 resultSlot;
4624
4625 return slot;
4626}
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1765
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1541
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
ResultRelInfo * resultRelInfo
Definition: execnodes.h:2114
MemoryContext temp_cxt
Definition: postgres_fdw.c:250
AttInMetadata * attinmeta
Definition: postgres_fdw.c:223
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1624

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

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

References CreateTupleDescCopy(), ForeignScan::fdw_scan_tlist, get_rel_type_id(), i, IsA, list_nth(), list_nth_node, OidIsValid, PlanState::plan, ScanState::ps, 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 813 of file postgres_fdw.c.

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

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

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

References COMPARE_LT, 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 4632 of file postgres_fdw.c.

4635{
4636 TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
4637 ListCell *lc;
4638 int i;
4639
4640 /*
4641 * Calculate the mapping between the fdw_scan_tlist's entries and the
4642 * result tuple's attributes.
4643 *
4644 * The "map" is an array of indexes of the result tuple's attributes in
4645 * fdw_scan_tlist, i.e., one entry for every attribute of the result
4646 * tuple. We store zero for any attributes that don't have the
4647 * corresponding entries in that list, marking that a NULL is needed in
4648 * the result tuple.
4649 *
4650 * Also get the indexes of the entries for ctid and oid if any.
4651 */
4652 dmstate->attnoMap = (AttrNumber *)
4653 palloc0(resultTupType->natts * sizeof(AttrNumber));
4654
4655 dmstate->ctidAttno = dmstate->oidAttno = 0;
4656
4657 i = 1;
4658 dmstate->hasSystemCols = false;
4659 foreach(lc, fdw_scan_tlist)
4660 {
4661 TargetEntry *tle = (TargetEntry *) lfirst(lc);
4662 Var *var = (Var *) tle->expr;
4663
4664 Assert(IsA(var, Var));
4665
4666 /*
4667 * If the Var is a column of the target relation to be retrieved from
4668 * the foreign server, get the index of the entry.
4669 */
4670 if (var->varno == rtindex &&
4672 {
4673 int attrno = var->varattno;
4674
4675 if (attrno < 0)
4676 {
4677 /*
4678 * We don't retrieve system columns other than ctid and oid.
4679 */
4680 if (attrno == SelfItemPointerAttributeNumber)
4681 dmstate->ctidAttno = i;
4682 else
4683 Assert(false);
4684 dmstate->hasSystemCols = true;
4685 }
4686 else
4687 {
4688 /*
4689 * We don't retrieve whole-row references to the target
4690 * relation either.
4691 */
4692 Assert(attrno > 0);
4693
4694 dmstate->attnoMap[attrno - 1] = i;
4695 }
4696 }
4697 i++;
4698 }
4699}
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 7491 of file postgres_fdw.c.

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

6205{
6206 /* We must always have fpinfo_o. */
6207 Assert(fpinfo_o);
6208
6209 /* fpinfo_i may be NULL, but if present the servers must both match. */
6210 Assert(!fpinfo_i ||
6211 fpinfo_i->server->serverid == fpinfo_o->server->serverid);
6212
6213 /*
6214 * Copy the server specific FDW options. (For a join, both relations come
6215 * from the same server, so the server options should have the same value
6216 * for both relations.)
6217 */
6218 fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
6219 fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
6220 fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
6221 fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
6222 fpinfo->fetch_size = fpinfo_o->fetch_size;
6223 fpinfo->async_capable = fpinfo_o->async_capable;
6224
6225 /* Merge the table level options from either side of the join. */
6226 if (fpinfo_i)
6227 {
6228 /*
6229 * We'll prefer to use remote estimates for this join if any table
6230 * from either side of the join is using remote estimates. This is
6231 * most likely going to be preferred since they're already willing to
6232 * pay the price of a round trip to get the remote EXPLAIN. In any
6233 * case it's not entirely clear how we might otherwise handle this
6234 * best.
6235 */
6236 fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
6237 fpinfo_i->use_remote_estimate;
6238
6239 /*
6240 * Set fetch size to maximum of the joining sides, since we are
6241 * expecting the rows returned by the join to be proportional to the
6242 * relation sizes.
6243 */
6244 fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
6245
6246 /*
6247 * We'll prefer to consider this join async-capable if any table from
6248 * either side of the join is considered async-capable. This would be
6249 * reasonable because in that case the foreign server would have its
6250 * own resources to scan that table asynchronously, and the join could
6251 * also be computed asynchronously using the resources.
6252 */
6253 fpinfo->async_capable = fpinfo_o->async_capable ||
6254 fpinfo_i->async_capable;
6255 }
6256}
#define Max(x, y)
Definition: c.h:1001
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  )

◆ PG_MODULE_MAGIC_EXT()

PG_MODULE_MAGIC_EXT ( name = "postgres_fdw",
version = PG_VERSION 
)

◆ postgres_fdw_handler()

Datum postgres_fdw_handler ( PG_FUNCTION_ARGS  )

Definition at line 555 of file postgres_fdw.c.

556{
557 FdwRoutine *routine = makeNode(FdwRoutine);
558
559 /* Functions for scanning foreign tables */
567
568 /* Functions for updating foreign tables */
585
586 /* Function for EvalPlanQual rechecks */
588 /* Support functions for EXPLAIN */
592
593 /* Support function for TRUNCATE */
595
596 /* Support functions for ANALYZE */
598
599 /* Support functions for IMPORT FOREIGN SCHEMA */
601
602 /* Support functions for join push-down */
604
605 /* Support functions for upper relation push-down */
607
608 /* Support functions for asynchronous execution */
613
614 PG_RETURN_POINTER(routine);
615}
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define makeNode(_type_)
Definition: nodes.h:161
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:625
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 5008 of file postgres_fdw.c.

5012{
5013 PgFdwAnalyzeState astate;
5015 ForeignServer *server;
5017 PGconn *conn;
5019 PgFdwSamplingMethod method = ANALYZE_SAMPLE_AUTO; /* auto is default */
5020 double sample_frac = -1.0;
5021 double reltuples = -1.0;
5022 unsigned int cursor_number;
5023 StringInfoData sql;
5024 PGresult *res;
5025 char fetch_sql[64];
5026 int fetch_size;
5027 ListCell *lc;
5028
5029 /* Initialize workspace state */
5030 astate.rel = relation;
5032
5033 astate.rows = rows;
5034 astate.targrows = targrows;
5035 astate.numrows = 0;
5036 astate.samplerows = 0;
5037 astate.rowstoskip = -1; /* -1 means not set yet */
5038 reservoir_init_selection_state(&astate.rstate, targrows);
5039
5040 /* Remember ANALYZE context, and create a per-tuple temp context */
5043 "postgres_fdw temporary data",
5045
5046 /*
5047 * Get the connection to use. We do the remote access as the table's
5048 * owner, even if the ANALYZE was started by some other user.
5049 */
5051 server = GetForeignServer(table->serverid);
5052 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5053 conn = GetConnection(user, false, NULL);
5054
5055 /* We'll need server version, so fetch it now. */
5057
5058 /*
5059 * What sampling method should we use?
5060 */
5061 foreach(lc, server->options)
5062 {
5063 DefElem *def = (DefElem *) lfirst(lc);
5064
5065 if (strcmp(def->defname, "analyze_sampling") == 0)
5066 {
5067 char *value = defGetString(def);
5068
5069 if (strcmp(value, "off") == 0)
5070 method = ANALYZE_SAMPLE_OFF;
5071 else if (strcmp(value, "auto") == 0)
5072 method = ANALYZE_SAMPLE_AUTO;
5073 else if (strcmp(value, "random") == 0)
5074 method = ANALYZE_SAMPLE_RANDOM;
5075 else if (strcmp(value, "system") == 0)
5076 method = ANALYZE_SAMPLE_SYSTEM;
5077 else if (strcmp(value, "bernoulli") == 0)
5078 method = ANALYZE_SAMPLE_BERNOULLI;
5079
5080 break;
5081 }
5082 }
5083
5084 foreach(lc, table->options)
5085 {
5086 DefElem *def = (DefElem *) lfirst(lc);
5087
5088 if (strcmp(def->defname, "analyze_sampling") == 0)
5089 {
5090 char *value = defGetString(def);
5091
5092 if (strcmp(value, "off") == 0)
5093 method = ANALYZE_SAMPLE_OFF;
5094 else if (strcmp(value, "auto") == 0)
5095 method = ANALYZE_SAMPLE_AUTO;
5096 else if (strcmp(value, "random") == 0)
5097 method = ANALYZE_SAMPLE_RANDOM;
5098 else if (strcmp(value, "system") == 0)
5099 method = ANALYZE_SAMPLE_SYSTEM;
5100 else if (strcmp(value, "bernoulli") == 0)
5101 method = ANALYZE_SAMPLE_BERNOULLI;
5102
5103 break;
5104 }
5105 }
5106
5107 /*
5108 * Error-out if explicitly required one of the TABLESAMPLE methods, but
5109 * the server does not support it.
5110 */
5111 if ((server_version_num < 95000) &&
5112 (method == ANALYZE_SAMPLE_SYSTEM ||
5113 method == ANALYZE_SAMPLE_BERNOULLI))
5114 ereport(ERROR,
5115 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5116 errmsg("remote server does not support TABLESAMPLE feature")));
5117
5118 /*
5119 * If we've decided to do remote sampling, calculate the sampling rate. We
5120 * need to get the number of tuples from the remote server, but skip that
5121 * network round-trip if not needed.
5122 */
5123 if (method != ANALYZE_SAMPLE_OFF)
5124 {
5125 bool can_tablesample;
5126
5127 reltuples = postgresGetAnalyzeInfoForForeignTable(relation,
5128 &can_tablesample);
5129
5130 /*
5131 * Make sure we're not choosing TABLESAMPLE when the remote relation
5132 * does not support that. But only do this for "auto" - if the user
5133 * explicitly requested BERNOULLI/SYSTEM, it's better to fail.
5134 */
5135 if (!can_tablesample && (method == ANALYZE_SAMPLE_AUTO))
5136 method = ANALYZE_SAMPLE_RANDOM;
5137
5138 /*
5139 * Remote's reltuples could be 0 or -1 if the table has never been
5140 * vacuumed/analyzed. In that case, disable sampling after all.
5141 */
5142 if ((reltuples <= 0) || (targrows >= reltuples))
5143 method = ANALYZE_SAMPLE_OFF;
5144 else
5145 {
5146 /*
5147 * All supported sampling methods require sampling rate, not
5148 * target rows directly, so we calculate that using the remote
5149 * reltuples value. That's imperfect, because it might be off a
5150 * good deal, but that's not something we can (or should) address
5151 * here.
5152 *
5153 * If reltuples is too low (i.e. when table grew), we'll end up
5154 * sampling more rows - but then we'll apply the local sampling,
5155 * so we get the expected sample size. This is the same outcome as
5156 * without remote sampling.
5157 *
5158 * If reltuples is too high (e.g. after bulk DELETE), we will end
5159 * up sampling too few rows.
5160 *
5161 * We can't really do much better here - we could try sampling a
5162 * bit more rows, but we don't know how off the reltuples value is
5163 * so how much is "a bit more"?
5164 *
5165 * Furthermore, the targrows value for partitions is determined
5166 * based on table size (relpages), which can be off in different
5167 * ways too. Adjusting the sampling rate here might make the issue
5168 * worse.
5169 */
5170 sample_frac = targrows / reltuples;
5171
5172 /*
5173 * We should never get sampling rate outside the valid range
5174 * (between 0.0 and 1.0), because those cases should be covered by
5175 * the previous branch that sets ANALYZE_SAMPLE_OFF.
5176 */
5177 Assert(sample_frac >= 0.0 && sample_frac <= 1.0);
5178 }
5179 }
5180
5181 /*
5182 * For "auto" method, pick the one we believe is best. For servers with
5183 * TABLESAMPLE support we pick BERNOULLI, for old servers we fall-back to
5184 * random() to at least reduce network transfer.
5185 */
5186 if (method == ANALYZE_SAMPLE_AUTO)
5187 {
5188 if (server_version_num < 95000)
5189 method = ANALYZE_SAMPLE_RANDOM;
5190 else
5191 method = ANALYZE_SAMPLE_BERNOULLI;
5192 }
5193
5194 /*
5195 * Construct cursor that retrieves whole rows from remote.
5196 */
5198 initStringInfo(&sql);
5199 appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
5200
5201 deparseAnalyzeSql(&sql, relation, method, sample_frac, &astate.retrieved_attrs);
5202
5203 res = pgfdw_exec_query(conn, sql.data, NULL);
5204 if (PQresultStatus(res) != PGRES_COMMAND_OK)
5205 pgfdw_report_error(res, conn, sql.data);
5206 PQclear(res);
5207
5208 /*
5209 * Determine the fetch size. The default is arbitrary, but shouldn't be
5210 * enormous.
5211 */
5212 fetch_size = 100;
5213 foreach(lc, server->options)
5214 {
5215 DefElem *def = (DefElem *) lfirst(lc);
5216
5217 if (strcmp(def->defname, "fetch_size") == 0)
5218 {
5219 (void) parse_int(defGetString(def), &fetch_size, 0, NULL);
5220 break;
5221 }
5222 }
5223 foreach(lc, table->options)
5224 {
5225 DefElem *def = (DefElem *) lfirst(lc);
5226
5227 if (strcmp(def->defname, "fetch_size") == 0)
5228 {
5229 (void) parse_int(defGetString(def), &fetch_size, 0, NULL);
5230 break;
5231 }
5232 }
5233
5234 /* Construct command to fetch rows from remote. */
5235 snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
5237
5238 /* Retrieve and process rows a batch at a time. */
5239 for (;;)
5240 {
5241 int numrows;
5242 int i;
5243
5244 /* Allow users to cancel long query */
5246
5247 /*
5248 * XXX possible future improvement: if rowstoskip is large, we could
5249 * issue a MOVE rather than physically fetching the rows, then just
5250 * adjust rowstoskip and samplerows appropriately.
5251 */
5252
5253 /* Fetch some rows */
5254 res = pgfdw_exec_query(conn, fetch_sql, NULL);
5255 /* On error, report the original query, not the FETCH. */
5256 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5257 pgfdw_report_error(res, conn, sql.data);
5258
5259 /* Process whatever we got. */
5260 numrows = PQntuples(res);
5261 for (i = 0; i < numrows; i++)
5262 analyze_row_processor(res, i, &astate);
5263
5264 PQclear(res);
5265
5266 /* Must be EOF if we didn't get all the rows requested. */
5267 if (numrows < fetch_size)
5268 break;
5269 }
5270
5271 /* Close the cursor, just to be tidy. */
5273
5275
5276 /* We assume that we have no dead tuple. */
5277 *totaldeadrows = 0.0;
5278
5279 /*
5280 * Without sampling, we've retrieved all living tuples from foreign
5281 * server, so report that as totalrows. Otherwise use the reltuples
5282 * estimate we got from the remote side.
5283 */
5284 if (method == ANALYZE_SAMPLE_OFF)
5285 *totalrows = astate.samplerows;
5286 else
5287 *totalrows = reltuples;
5288
5289 /*
5290 * Emit some interesting relation info
5291 */
5292 ereport(elevel,
5293 (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
5294 RelationGetRelationName(relation),
5295 *totalrows, astate.numrows)));
5296
5297 return astate.numrows;
5298}
unsigned int GetCursorNumber(PGconn *conn)
Definition: connection.c:918
void deparseAnalyzeSql(StringInfo buf, Relation rel, PgFdwSamplingMethod sample_method, double sample_frac, List **retrieved_attrs)
Definition: deparse.c:2592
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ereport(elevel,...)
Definition: elog.h:150
int PQserverVersion(const PGconn *conn)
Definition: fe-connect.c:7694
static int server_version_num
Definition: guc_tables.c:595
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
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, parse_int(), 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, snprintf, table, 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 1748 of file postgres_fdw.c.

1752{
1753 Var *var;
1754
1755 /*
1756 * In postgres_fdw, what we need is the ctid, same as for a regular table.
1757 */
1758
1759 /* Make a Var representing the desired value */
1760 var = makeVar(rtindex,
1762 TIDOID,
1763 -1,
1764 InvalidOid,
1765 0);
1766
1767 /* Register it as a row-identity column needed by this target rel */
1768 add_row_identity_var(root, var, rtindex, "ctid");
1769}
void add_row_identity_var(PlannerInfo *root, Var *orig_var, Index rtindex, const char *rowid_name)
Definition: appendinfo.c:860
#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 4890 of file postgres_fdw.c.

4893{
4896 PGconn *conn;
4897 StringInfoData sql;
4898 PGresult *res;
4899
4900 /* Return the row-analysis function pointer */
4902
4903 /*
4904 * Now we have to get the number of pages. It's annoying that the ANALYZE
4905 * API requires us to return that now, because it forces some duplication
4906 * of effort between this routine and postgresAcquireSampleRowsFunc. But
4907 * it's probably not worth redefining that API at this point.
4908 */
4909
4910 /*
4911 * Get the connection to use. We do the remote access as the table's
4912 * owner, even if the ANALYZE was started by some other user.
4913 */
4915 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4916 conn = GetConnection(user, false, NULL);
4917
4918 /*
4919 * Construct command to get page count for relation.
4920 */
4921 initStringInfo(&sql);
4922 deparseAnalyzeSizeSql(&sql, relation);
4923
4924 res = pgfdw_exec_query(conn, sql.data, NULL);
4925 if (PQresultStatus(res) != PGRES_TUPLES_OK)
4926 pgfdw_report_error(res, conn, sql.data);
4927
4928 if (PQntuples(res) != 1 || PQnfields(res) != 1)
4929 elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
4930 *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
4931 PQclear(res);
4932
4934
4935 return true;
4936}
void deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
Definition: deparse.c:2530
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(), pgfdw_exec_query(), pgfdw_report_error(), PGRES_TUPLES_OK, postgresAcquireSampleRowsFunc(), PQclear, PQgetvalue, PQnfields, PQntuples, PQresultStatus, RelationData::rd_rel, RelationGetRelid, ReleaseConnection(), table, and user.

Referenced by postgres_fdw_handler().

◆ postgresBeginDirectModify()

static void postgresBeginDirectModify ( ForeignScanState node,
int  eflags 
)
static

Definition at line 2646 of file postgres_fdw.c.

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

Referenced by postgres_fdw_handler().

◆ postgresBeginForeignInsert()

static void postgresBeginForeignInsert ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2157 of file postgres_fdw.c.

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

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

1920{
1921 PgFdwModifyState *fmstate;
1922 char *query;
1923 List *target_attrs;
1924 bool has_returning;
1925 int values_end_len;
1926 List *retrieved_attrs;
1927 RangeTblEntry *rte;
1928
1929 /*
1930 * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
1931 * stays NULL.
1932 */
1933 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1934 return;
1935
1936 /* Deconstruct fdw_private data. */
1937 query = strVal(list_nth(fdw_private,
1939 target_attrs = (List *) list_nth(fdw_private,
1941 values_end_len = intVal(list_nth(fdw_private,
1943 has_returning = boolVal(list_nth(fdw_private,
1945 retrieved_attrs = (List *) list_nth(fdw_private,
1947
1948 /* Find RTE. */
1949 rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex,
1950 mtstate->ps.state);
1951
1952 /* Construct an execution state. */
1953 fmstate = create_foreign_modify(mtstate->ps.state,
1954 rte,
1955 resultRelInfo,
1956 mtstate->operation,
1957 outerPlanState(mtstate)->plan,
1958 query,
1959 target_attrs,
1960 values_end_len,
1961 has_returning,
1962 retrieved_attrs);
1963
1964 resultRelInfo->ri_FdwState = fmstate;
1965}
#define outerPlanState(node)
Definition: execnodes.h:1261
CmdType operation
Definition: execnodes.h:1404
#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 1499 of file postgres_fdw.c.

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

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, table, TupleDescGetAttInMetadata(), and user.

Referenced by postgres_fdw_handler().

◆ postgresEndDirectModify()

static void postgresEndDirectModify ( ForeignScanState node)
static

Definition at line 2807 of file postgres_fdw.c.

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

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

2290{
2291 PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2292
2293 Assert(fmstate != NULL);
2294
2295 /*
2296 * If the fmstate has aux_fmstate set, get the aux_fmstate (see
2297 * postgresBeginForeignInsert())
2298 */
2299 if (fmstate->aux_fmstate)
2300 fmstate = fmstate->aux_fmstate;
2301
2302 /* Destroy the execution state */
2303 finish_foreign_modify(fmstate);
2304}
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 2139 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresEndForeignScan()

static void postgresEndForeignScan ( ForeignScanState node)
static

Definition at line 1723 of file postgres_fdw.c.

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

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

2006{
2007 PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
2008 TupleTableSlot **rslot;
2009
2010 /*
2011 * If the fmstate has aux_fmstate set, use the aux_fmstate (see
2012 * postgresBeginForeignInsert())
2013 */
2014 if (fmstate->aux_fmstate)
2015 resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2016 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2017 slots, planSlots, numSlots);
2018 /* Revert that change */
2019 if (fmstate->aux_fmstate)
2020 resultRelInfo->ri_FdwState = fmstate;
2021
2022 return rslot;
2023}
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 2120 of file postgres_fdw.c.

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

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

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

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

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

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, pfree(), RelationGetRelationName, RelationGetRelid, table, and user.

Referenced by postgres_fdw_handler().

◆ postgresExecForeignUpdate()

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

Definition at line 2101 of file postgres_fdw.c.

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

References CMD_UPDATE, and execute_foreign_modify().

Referenced by postgres_fdw_handler().

◆ postgresExplainDirectModify()

static void postgresExplainDirectModify ( ForeignScanState node,
ExplainState es 
)
static

Definition at line 2965 of file postgres_fdw.c.

2966{
2967 List *fdw_private;
2968 char *sql;
2969
2970 if (es->verbose)
2971 {
2972 fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
2973 sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql));
2974 ExplainPropertyText("Remote SQL", sql, es);
2975 }
2976}
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 2937 of file postgres_fdw.c.

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

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

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

7212{
7214 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7215 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
7216 AppendState *requestor = (AppendState *) areq->requestor;
7217 WaitEventSet *set = requestor->as_eventset;
7218
7219 /* This should not be called unless callback_pending */
7220 Assert(areq->callback_pending);
7221
7222 /*
7223 * If process_pending_request() has been invoked on the given request
7224 * before we get here, we might have some tuples already; in which case
7225 * complete the request
7226 */
7227 if (fsstate->next_tuple < fsstate->num_tuples)
7228 {
7230 if (areq->request_complete)
7231 return;
7232 Assert(areq->callback_pending);
7233 }
7234
7235 /* We must have run out of tuples */
7236 Assert(fsstate->next_tuple >= fsstate->num_tuples);
7237
7238 /* The core code would have registered postmaster death event */
7240
7241 /* Begin an asynchronous data fetch if not already done */
7242 if (!pendingAreq)
7244 else if (pendingAreq->requestor != areq->requestor)
7245 {
7246 /*
7247 * This is the case when the in-process request was made by another
7248 * Append. Note that it might be useless to process the request made
7249 * by that Append, because the query might not need tuples from that
7250 * Append anymore; so we avoid processing it to begin a fetch for the
7251 * given request if possible. If there are any child subplans of the
7252 * same parent that are ready for new requests, skip the given
7253 * request. Likewise, if there are any configured events other than
7254 * the postmaster death event, skip it. Otherwise, process the
7255 * in-process request, then begin a fetch to configure the event
7256 * below, because we might otherwise end up with no configured events
7257 * other than the postmaster death event.
7258 */
7259 if (!bms_is_empty(requestor->as_needrequest))
7260 return;
7261 if (GetNumRegisteredWaitEvents(set) > 1)
7262 return;
7263 process_pending_request(pendingAreq);
7265 }
7266 else if (pendingAreq->requestee != areq->requestee)
7267 {
7268 /*
7269 * This is the case when the in-process request was made by the same
7270 * parent but for a different child. Since we configure only the
7271 * event for the request made for that child, skip the given request.
7272 */
7273 return;
7274 }
7275 else
7276 Assert(pendingAreq == areq);
7277
7278 AddWaitEventToSet(set, WL_SOCKET_READABLE, PQsocket(fsstate->conn),
7279 NULL, areq);
7280}
int PQsocket(const PGconn *conn)
Definition: fe-connect.c:7730
static void fetch_more_data_begin(AsyncRequest *areq)
static void complete_pending_request(AsyncRequest *areq)
Bitmapset * as_needrequest
Definition: execnodes.h:1509
struct WaitEventSet * as_eventset
Definition: execnodes.h:1510
PlanState * requestor
Definition: execnodes.h:639
bool request_complete
Definition: execnodes.h:643
int GetNumRegisteredWaitEvents(WaitEventSet *set)
int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch, void *user_data)
Definition: waiteventset.c:570
#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 7288 of file postgres_fdw.c.

7289{
7291 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7292
7293 /* The core code would have initialized the callback_pending flag */
7294 Assert(!areq->callback_pending);
7295
7296 /*
7297 * If process_pending_request() has been invoked on the given request
7298 * before we get here, we might have some tuples already; in which case
7299 * produce the next tuple
7300 */
7301 if (fsstate->next_tuple < fsstate->num_tuples)
7302 {
7303 produce_tuple_asynchronously(areq, true);
7304 return;
7305 }
7306
7307 /* We must have run out of tuples */
7308 Assert(fsstate->next_tuple >= fsstate->num_tuples);
7309
7310 /* The request should be currently in-process */
7311 Assert(fsstate->conn_state->pendingAreq == areq);
7312
7313 /* On error, report the original query, not the FETCH. */
7314 if (!PQconsumeInput(fsstate->conn))
7315 pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
7316
7317 fetch_more_data(node);
7318
7319 produce_tuple_asynchronously(areq, true);
7320}
int PQconsumeInput(PGconn *conn)
Definition: fe-exec.c:2000
static void fetch_more_data(ForeignScanState *node)

References Assert(), AsyncRequest::callback_pending, 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 7201 of file postgres_fdw.c.

7202{
7203 produce_tuple_asynchronously(areq, true);
7204}

References produce_tuple_asynchronously().

Referenced by postgres_fdw_handler().

◆ postgresGetAnalyzeInfoForForeignTable()

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

Definition at line 4946 of file postgres_fdw.c.

4947{
4950 PGconn *conn;
4951 StringInfoData sql;
4952 PGresult *res;
4953 double reltuples;
4954 char relkind;
4955
4956 /* assume the remote relation does not support TABLESAMPLE */
4957 *can_tablesample = false;
4958
4959 /*
4960 * Get the connection to use. We do the remote access as the table's
4961 * owner, even if the ANALYZE was started by some other user.
4962 */
4964 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
4965 conn = GetConnection(user, false, NULL);
4966
4967 /*
4968 * Construct command to get page count for relation.
4969 */
4970 initStringInfo(&sql);
4971 deparseAnalyzeInfoSql(&sql, relation);
4972
4973 res = pgfdw_exec_query(conn, sql.data, NULL);
4974 if (PQresultStatus(res) != PGRES_TUPLES_OK)
4975 pgfdw_report_error(res, conn, sql.data);
4976
4977 if (PQntuples(res) != 1 || PQnfields(res) != 2)
4978 elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
4979 reltuples = strtod(PQgetvalue(res, 0, 0), NULL);
4980 relkind = *(PQgetvalue(res, 0, 1));
4981 PQclear(res);
4982
4984
4985 /* TABLESAMPLE is supported only for regular tables and matviews */
4986 *can_tablesample = (relkind == RELKIND_RELATION ||
4987 relkind == RELKIND_MATVIEW ||
4988 relkind == RELKIND_PARTITIONED_TABLE);
4989
4990 return reltuples;
4991}
void deparseAnalyzeInfoSql(StringInfo buf, Relation rel)
Definition: deparse.c:2552

References conn, StringInfoData::data, deparseAnalyzeInfoSql(), elog, ERROR, GetConnection(), GetForeignTable(), GetUserMapping(), initStringInfo(), pgfdw_exec_query(), pgfdw_report_error(), PGRES_TUPLES_OK, PQclear, PQgetvalue, PQnfields, PQntuples, PQresultStatus, RelationData::rd_rel, RelationGetRelid, ReleaseConnection(), table, 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 6263 of file postgres_fdw.c.

6269{
6270 PgFdwRelationInfo *fpinfo;
6271 ForeignPath *joinpath;
6272 double rows;
6273 int width;
6274 int disabled_nodes;
6275 Cost startup_cost;
6276 Cost total_cost;
6277 Path *epq_path; /* Path to create plan to be executed when
6278 * EvalPlanQual gets triggered. */
6279
6280 /*
6281 * Skip if this join combination has been considered already.
6282 */
6283 if (joinrel->fdw_private)
6284 return;
6285
6286 /*
6287 * This code does not work for joins with lateral references, since those
6288 * must have parameterized paths, which we don't generate yet.
6289 */
6290 if (!bms_is_empty(joinrel->lateral_relids))
6291 return;
6292
6293 /*
6294 * Create unfinished PgFdwRelationInfo entry which is used to indicate
6295 * that the join relation is already considered, so that we won't waste
6296 * time in judging safety of join pushdown and adding the same paths again
6297 * if found safe. Once we know that this join can be pushed down, we fill
6298 * the entry.
6299 */
6300 fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo));
6301 fpinfo->pushdown_safe = false;
6302 joinrel->fdw_private = fpinfo;
6303 /* attrs_used is only for base relations. */
6304 fpinfo->attrs_used = NULL;
6305
6306 /*
6307 * If there is a possibility that EvalPlanQual will be executed, we need
6308 * to be able to reconstruct the row using scans of the base relations.
6309 * GetExistingLocalJoinPath will find a suitable path for this purpose in
6310 * the path list of the joinrel, if one exists. We must be careful to
6311 * call it before adding any ForeignPath, since the ForeignPath might
6312 * dominate the only suitable local path available. We also do it before
6313 * calling foreign_join_ok(), since that function updates fpinfo and marks
6314 * it as pushable if the join is found to be pushable.
6315 */
6316 if (root->parse->commandType == CMD_DELETE ||
6317 root->parse->commandType == CMD_UPDATE ||
6318 root->rowMarks)
6319 {
6320 epq_path = GetExistingLocalJoinPath(joinrel);
6321 if (!epq_path)
6322 {
6323 elog(DEBUG3, "could not push down foreign join because a local path suitable for EPQ checks was not found");
6324 return;
6325 }
6326 }
6327 else
6328 epq_path = NULL;
6329
6330 if (!foreign_join_ok(root, joinrel, jointype, outerrel, innerrel, extra))
6331 {
6332 /* Free path required for EPQ if we copied one; we don't need it now */
6333 if (epq_path)
6334 pfree(epq_path);
6335 return;
6336 }
6337
6338 /*
6339 * Compute the selectivity and cost of the local_conds, so we don't have
6340 * to do it over again for each path. The best we can do for these
6341 * conditions is to estimate selectivity on the basis of local statistics.
6342 * The local conditions are applied after the join has been computed on
6343 * the remote side like quals in WHERE clause, so pass jointype as
6344 * JOIN_INNER.
6345 */
6347 fpinfo->local_conds,
6348 0,
6349 JOIN_INNER,
6350 NULL);
6351 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
6352
6353 /*
6354 * If we are going to estimate costs locally, estimate the join clause
6355 * selectivity here while we have special join info.
6356 */
6357 if (!fpinfo->use_remote_estimate)
6359 0, fpinfo->jointype,
6360 extra->sjinfo);
6361
6362 /* Estimate costs for bare join relation */
6363 estimate_path_cost_size(root, joinrel, NIL, NIL, NULL,
6364 &rows, &width, &disabled_nodes,
6365 &startup_cost, &total_cost);
6366 /* Now update this information in the joinrel */
6367 joinrel->rows = rows;
6368 joinrel->reltarget->width = width;
6369 fpinfo->rows = rows;
6370 fpinfo->width = width;
6371 fpinfo->disabled_nodes = disabled_nodes;
6372 fpinfo->startup_cost = startup_cost;
6373 fpinfo->total_cost = total_cost;
6374
6375 /*
6376 * Create a new join path and add it to the joinrel which represents a
6377 * join between foreign tables.
6378 */
6379 joinpath = create_foreign_join_path(root,
6380 joinrel,
6381 NULL, /* default pathtarget */
6382 rows,
6383 disabled_nodes,
6384 startup_cost,
6385 total_cost,
6386 NIL, /* no pathkeys */
6387 joinrel->lateral_relids,
6388 epq_path,
6389 extra->restrictlist,
6390 NIL); /* no fdw_private */
6391
6392 /* Add generated path into joinrel by add_path(). */
6393 add_path(joinrel, (Path *) joinpath);
6394
6395 /* Consider pathkeys for the join relation */
6396 add_paths_with_pathkeys_for_rel(root, joinrel, epq_path,
6397 extra->restrictlist);
6398
6399 /* XXX Consider parameterized paths for the join relation */
6400}
#define DEBUG3
Definition: elog.h:28
Path * GetExistingLocalJoinPath(RelOptInfo *joinrel)
Definition: foreign.c:742
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:3498
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 2034 of file postgres_fdw.c.

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

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

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

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

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

6651{
6652 PgFdwRelationInfo *fpinfo;
6653
6654 /*
6655 * If input rel is not safe to pushdown, then simply return as we cannot
6656 * perform any post-join operations on the foreign server.
6657 */
6658 if (!input_rel->fdw_private ||
6659 !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
6660 return;
6661
6662 /* Ignore stages we don't support; and skip any duplicate calls. */
6663 if ((stage != UPPERREL_GROUP_AGG &&
6664 stage != UPPERREL_ORDERED &&
6665 stage != UPPERREL_FINAL) ||
6666 output_rel->fdw_private)
6667 return;
6668
6669 fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo));
6670 fpinfo->pushdown_safe = false;
6671 fpinfo->stage = stage;
6672 output_rel->fdw_private = fpinfo;
6673
6674 switch (stage)
6675 {
6676 case UPPERREL_GROUP_AGG:
6677 add_foreign_grouping_paths(root, input_rel, output_rel,
6678 (GroupPathExtraData *) extra);
6679 break;
6680 case UPPERREL_ORDERED:
6681 add_foreign_ordered_paths(root, input_rel, output_rel);
6682 break;
6683 case UPPERREL_FINAL:
6684 add_foreign_final_paths(root, input_rel, output_rel,
6685 (FinalPathExtraData *) extra);
6686 break;
6687 default:
6688 elog(ERROR, "unexpected upper relation: %d", (int) stage);
6689 break;
6690 }
6691}
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 5373 of file postgres_fdw.c.

5374{
5375 List *commands = NIL;
5376 bool import_collate = true;
5377 bool import_default = false;
5378 bool import_generated = true;
5379 bool import_not_null = true;
5380 ForeignServer *server;
5381 UserMapping *mapping;
5382 PGconn *conn;
5384 PGresult *res;
5385 int numrows,
5386 i;
5387 ListCell *lc;
5388
5389 /* Parse statement options */
5390 foreach(lc, stmt->options)
5391 {
5392 DefElem *def = (DefElem *) lfirst(lc);
5393
5394 if (strcmp(def->defname, "import_collate") == 0)
5395 import_collate = defGetBoolean(def);
5396 else if (strcmp(def->defname, "import_default") == 0)
5397 import_default = defGetBoolean(def);
5398 else if (strcmp(def->defname, "import_generated") == 0)
5399 import_generated = defGetBoolean(def);
5400 else if (strcmp(def->defname, "import_not_null") == 0)
5401 import_not_null = defGetBoolean(def);
5402 else
5403 ereport(ERROR,
5404 (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
5405 errmsg("invalid option \"%s\"", def->defname)));
5406 }
5407
5408 /*
5409 * Get connection to the foreign server. Connection manager will
5410 * establish new connection if necessary.
5411 */
5412 server = GetForeignServer(serverOid);
5413 mapping = GetUserMapping(GetUserId(), server->serverid);
5414 conn = GetConnection(mapping, false, NULL);
5415
5416 /* Don't attempt to import collation if remote server hasn't got it */
5417 if (PQserverVersion(conn) < 90100)
5418 import_collate = false;
5419
5420 /* Create workspace for strings */
5422
5423 /* Check that the schema really exists */
5424 appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
5425 deparseStringLiteral(&buf, stmt->remote_schema);
5426
5427 res = pgfdw_exec_query(conn, buf.data, NULL);
5428 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5429 pgfdw_report_error(res, conn, buf.data);
5430
5431 if (PQntuples(res) != 1)
5432 ereport(ERROR,
5433 (errcode(ERRCODE_FDW_SCHEMA_NOT_FOUND),
5434 errmsg("schema \"%s\" is not present on foreign server \"%s\"",
5435 stmt->remote_schema, server->servername)));
5436
5437 PQclear(res);
5439
5440 /*
5441 * Fetch all table data from this schema, possibly restricted by EXCEPT or
5442 * LIMIT TO. (We don't actually need to pay any attention to EXCEPT/LIMIT
5443 * TO here, because the core code will filter the statements we return
5444 * according to those lists anyway. But it should save a few cycles to
5445 * not process excluded tables in the first place.)
5446 *
5447 * Import table data for partitions only when they are explicitly
5448 * specified in LIMIT TO clause. Otherwise ignore them and only include
5449 * the definitions of the root partitioned tables to allow access to the
5450 * complete remote data set locally in the schema imported.
5451 *
5452 * Note: because we run the connection with search_path restricted to
5453 * pg_catalog, the format_type() and pg_get_expr() outputs will always
5454 * include a schema name for types/functions in other schemas, which is
5455 * what we want.
5456 */
5458 "SELECT relname, "
5459 " attname, "
5460 " format_type(atttypid, atttypmod), "
5461 " attnotnull, "
5462 " pg_get_expr(adbin, adrelid), ");
5463
5464 /* Generated columns are supported since Postgres 12 */
5465 if (PQserverVersion(conn) >= 120000)
5467 " attgenerated, ");
5468 else
5470 " NULL, ");
5471
5472 if (import_collate)
5474 " collname, "
5475 " collnsp.nspname ");
5476 else
5478 " NULL, NULL ");
5479
5481 "FROM pg_class c "
5482 " JOIN pg_namespace n ON "
5483 " relnamespace = n.oid "
5484 " LEFT JOIN pg_attribute a ON "
5485 " attrelid = c.oid AND attnum > 0 "
5486 " AND NOT attisdropped "
5487 " LEFT JOIN pg_attrdef ad ON "
5488 " adrelid = c.oid AND adnum = attnum ");
5489
5490 if (import_collate)
5492 " LEFT JOIN pg_collation coll ON "
5493 " coll.oid = attcollation "
5494 " LEFT JOIN pg_namespace collnsp ON "
5495 " collnsp.oid = collnamespace ");
5496
5498 "WHERE c.relkind IN ("
5499 CppAsString2(RELKIND_RELATION) ","
5500 CppAsString2(RELKIND_VIEW) ","
5501 CppAsString2(RELKIND_FOREIGN_TABLE) ","
5502 CppAsString2(RELKIND_MATVIEW) ","
5503 CppAsString2(RELKIND_PARTITIONED_TABLE) ") "
5504 " AND n.nspname = ");
5505 deparseStringLiteral(&buf, stmt->remote_schema);
5506
5507 /* Partitions are supported since Postgres 10 */
5508 if (PQserverVersion(conn) >= 100000 &&
5509 stmt->list_type != FDW_IMPORT_SCHEMA_LIMIT_TO)
5510 appendStringInfoString(&buf, " AND NOT c.relispartition ");
5511
5512 /* Apply restrictions for LIMIT TO and EXCEPT */
5513 if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
5514 stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
5515 {
5516 bool first_item = true;
5517
5518 appendStringInfoString(&buf, " AND c.relname ");
5519 if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
5520 appendStringInfoString(&buf, "NOT ");
5521 appendStringInfoString(&buf, "IN (");
5522
5523 /* Append list of table names within IN clause */
5524 foreach(lc, stmt->table_list)
5525 {
5526 RangeVar *rv = (RangeVar *) lfirst(lc);
5527
5528 if (first_item)
5529 first_item = false;
5530 else
5533 }
5535 }
5536
5537 /* Append ORDER BY at the end of query to ensure output ordering */
5538 appendStringInfoString(&buf, " ORDER BY c.relname, a.attnum");
5539
5540 /* Fetch the data */
5541 res = pgfdw_exec_query(conn, buf.data, NULL);
5542 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5543 pgfdw_report_error(res, conn, buf.data);
5544
5545 /* Process results */
5546 numrows = PQntuples(res);
5547 /* note: incrementation of i happens in inner loop's while() test */
5548 for (i = 0; i < numrows;)
5549 {
5550 char *tablename = PQgetvalue(res, i, 0);
5551 bool first_item = true;
5552
5554 appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
5555 quote_identifier(tablename));
5556
5557 /* Scan all rows for this table */
5558 do
5559 {
5560 char *attname;
5561 char *typename;
5562 char *attnotnull;
5563 char *attgenerated;
5564 char *attdefault;
5565 char *collname;
5566 char *collnamespace;
5567
5568 /* If table has no columns, we'll see nulls here */
5569 if (PQgetisnull(res, i, 1))
5570 continue;
5571
5572 attname = PQgetvalue(res, i, 1);
5573 typename = PQgetvalue(res, i, 2);
5574 attnotnull = PQgetvalue(res, i, 3);
5575 attdefault = PQgetisnull(res, i, 4) ? NULL :
5576 PQgetvalue(res, i, 4);
5577 attgenerated = PQgetisnull(res, i, 5) ? NULL :
5578 PQgetvalue(res, i, 5);
5579 collname = PQgetisnull(res, i, 6) ? NULL :
5580 PQgetvalue(res, i, 6);
5581 collnamespace = PQgetisnull(res, i, 7) ? NULL :
5582 PQgetvalue(res, i, 7);
5583
5584 if (first_item)
5585 first_item = false;
5586 else
5587 appendStringInfoString(&buf, ",\n");
5588
5589 /* Print column name and type */
5590 appendStringInfo(&buf, " %s %s",
5592 typename);
5593
5594 /*
5595 * Add column_name option so that renaming the foreign table's
5596 * column doesn't break the association to the underlying column.
5597 */
5598 appendStringInfoString(&buf, " OPTIONS (column_name ");
5601
5602 /* Add COLLATE if needed */
5603 if (import_collate && collname != NULL && collnamespace != NULL)
5604 appendStringInfo(&buf, " COLLATE %s.%s",
5605 quote_identifier(collnamespace),
5606 quote_identifier(collname));
5607
5608 /* Add DEFAULT if needed */
5609 if (import_default && attdefault != NULL &&
5610 (!attgenerated || !attgenerated[0]))
5611 appendStringInfo(&buf, " DEFAULT %s", attdefault);
5612
5613 /* Add GENERATED if needed */
5614 if (import_generated && attgenerated != NULL &&
5615 attgenerated[0] == ATTRIBUTE_GENERATED_STORED)
5616 {
5617 Assert(attdefault != NULL);
5619 " GENERATED ALWAYS AS (%s) STORED",
5620 attdefault);
5621 }
5622
5623 /* Add NOT NULL if needed */
5624 if (import_not_null && attnotnull[0] == 't')
5625 appendStringInfoString(&buf, " NOT NULL");
5626 }
5627 while (++i < numrows &&
5628 strcmp(PQgetvalue(res, i, 0), tablename) == 0);
5629
5630 /*
5631 * Add server name and table-level options. We specify remote schema
5632 * and table name as options (the latter to ensure that renaming the
5633 * foreign table doesn't break the association).
5634 */
5635 appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
5636 quote_identifier(server->servername));
5637
5638 appendStringInfoString(&buf, "schema_name ");
5639 deparseStringLiteral(&buf, stmt->remote_schema);
5640 appendStringInfoString(&buf, ", table_name ");
5641 deparseStringLiteral(&buf, tablename);
5642
5644
5645 commands = lappend(commands, pstrdup(buf.data));
5646 }
5647 PQclear(res);
5648
5650
5651 return commands;
5652}
#define CppAsString2(x)
Definition: c.h:422
void deparseStringLiteral(StringInfo buf, const char *val)
Definition: deparse.c:2880
#define stmt
Definition: indent_codes.h:59
@ FDW_IMPORT_SCHEMA_LIMIT_TO
Definition: parsenodes.h:3045
@ FDW_IMPORT_SCHEMA_EXCEPT
Definition: parsenodes.h:3046
bool attnotnull
Definition: pg_attribute.h:123
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, 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 7188 of file postgres_fdw.c.

7189{
7190 RelOptInfo *rel = ((Path *) path)->parent;
7191 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
7192
7193 return fpinfo->async_capable;
7194}

References PgFdwRelationInfo::async_capable.

Referenced by postgres_fdw_handler().

◆ postgresIsForeignRelUpdatable()

static int postgresIsForeignRelUpdatable ( Relation  rel)
static

Definition at line 2312 of file postgres_fdw.c.

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

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

Referenced by postgres_fdw_handler().

◆ postgresIterateDirectModify()

static TupleTableSlot * postgresIterateDirectModify ( ForeignScanState node)
static

Definition at line 2763 of file postgres_fdw.c.

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

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

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

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

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

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

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

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

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

References PgFdwScanState::async_capable, PlanState::chgParam, PgFdwScanState::conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, 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 4194 of file postgres_fdw.c.

4195{
4196 char prep_name[NAMEDATALEN];
4197 char *p_name;
4198 PGresult *res;
4199
4200 /*
4201 * The caller would already have processed a pending asynchronous request
4202 * if any, so no need to do it here.
4203 */
4204
4205 /* Construct name we'll use for the prepared statement. */
4206 snprintf(prep_name, sizeof(prep_name), "pgsql_fdw_prep_%u",
4207 GetPrepStmtNumber(fmstate->conn));
4208 p_name = pstrdup(prep_name);
4209
4210 /*
4211 * We intentionally do not specify parameter types here, but leave the
4212 * remote server to derive them by default. This avoids possible problems
4213 * with the remote server using different type OIDs than we do. All of
4214 * the prepared statements we use in this module are simple enough that
4215 * the remote server will make the right choices.
4216 */
4217 if (!PQsendPrepare(fmstate->conn,
4218 p_name,
4219 fmstate->query,
4220 0,
4221 NULL))
4222 pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
4223
4224 /*
4225 * Get the result, and check for success.
4226 */
4227 res = pgfdw_get_result(fmstate->conn);
4228 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4229 pgfdw_report_error(res, fmstate->conn, fmstate->query);
4230 PQclear(res);
4231
4232 /* This action shows that the prepare has been done. */
4233 fmstate->p_name = p_name;
4234}
unsigned int GetPrepStmtNumber(PGconn *conn)
Definition: connection.c:932
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
Definition: fe-exec.c:1552
#define NAMEDATALEN

References PgFdwModifyState::conn, 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 4804 of file postgres_fdw.c.

4810{
4811 int i;
4812 ListCell *lc;
4813
4814 Assert(numParams > 0);
4815
4816 /* Prepare for output conversion of parameters used in remote query. */
4817 *param_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * numParams);
4818
4819 i = 0;
4820 foreach(lc, fdw_exprs)
4821 {
4822 Node *param_expr = (Node *) lfirst(lc);
4823 Oid typefnoid;
4824 bool isvarlena;
4825
4826 getTypeOutputInfo(exprType(param_expr), &typefnoid, &isvarlena);
4827 fmgr_info(typefnoid, &(*param_flinfo)[i]);
4828 i++;
4829 }
4830
4831 /*
4832 * Prepare remote-parameter expressions for evaluation. (Note: in
4833 * practice, we expect that all these expressions will be just Params, so
4834 * we could possibly do something more efficient than using the full
4835 * expression-eval machinery for this. But probably there would be little
4836 * benefit, and it'd require postgres_fdw to know more than is desirable
4837 * about Param evaluation.)
4838 */
4839 *param_exprs = ExecInitExprList(fdw_exprs, node);
4840
4841 /* Allocate buffer for text form of query parameters. */
4842 *param_values = (const char **) palloc0(numParams * sizeof(char *));
4843}
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 7424 of file postgres_fdw.c.

7425{
7427 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7428
7429 /* The request would have been pending for a callback */
7430 Assert(areq->callback_pending);
7431
7432 /* The request should be currently in-process */
7433 Assert(fsstate->conn_state->pendingAreq == areq);
7434
7435 fetch_more_data(node);
7436
7437 /*
7438 * If we didn't get any tuples, must be end of data; complete the request
7439 * now. Otherwise, we postpone completing the request until we are called
7440 * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
7441 */
7442 if (fsstate->next_tuple >= fsstate->num_tuples)
7443 {
7444 /* Unlike AsyncNotify, we unset callback_pending ourselves */
7445 areq->callback_pending = false;
7446 /* Mark the request as complete */
7447 ExecAsyncRequestDone(areq, NULL);
7448 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
7449 ExecAsyncResponse(areq);
7450 }
7451}
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 4849 of file postgres_fdw.c.

4853{
4854 int nestlevel;
4855 int i;
4856 ListCell *lc;
4857
4858 nestlevel = set_transmission_modes();
4859
4860 i = 0;
4861 foreach(lc, param_exprs)
4862 {
4863 ExprState *expr_state = (ExprState *) lfirst(lc);
4864 Datum expr_value;
4865 bool isNull;
4866
4867 /* Evaluate the parameter expression */
4868 expr_value = ExecEvalExpr(expr_state, econtext, &isNull);
4869
4870 /*
4871 * Get string representation of each parameter value by invoking
4872 * type-specific output function, unless the value is null.
4873 */
4874 if (isNull)
4875 param_values[i] = NULL;
4876 else
4877 param_values[i] = OutputFunctionCall(&param_flinfo[i], expr_value);
4878
4879 i++;
4880 }
4881
4882 reset_transmission_modes(nestlevel);
4883}
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:393

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

7327{
7329 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
7330 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
7331 TupleTableSlot *result;
7332
7333 /* This should not be called if the request is currently in-process */
7334 Assert(areq != pendingAreq);
7335
7336 /* Fetch some more tuples, if we've run out */
7337 if (fsstate->next_tuple >= fsstate->num_tuples)
7338 {
7339 /* No point in another fetch if we already detected EOF, though */
7340 if (!fsstate->eof_reached)
7341 {
7342 /* Mark the request as pending for a callback */
7344 /* Begin another fetch if requested and if no pending request */
7345 if (fetch && !pendingAreq)
7347 }
7348 else
7349 {
7350 /* There's nothing more to do; just return a NULL pointer */
7351 result = NULL;
7352 /* Mark the request as complete */
7353 ExecAsyncRequestDone(areq, result);
7354 }
7355 return;
7356 }
7357
7358 /* Get a tuple from the ForeignScan node */
7359 result = areq->requestee->ExecProcNodeReal(areq->requestee);
7360 if (!TupIsNull(result))
7361 {
7362 /* Mark the request as complete */
7363 ExecAsyncRequestDone(areq, result);
7364 return;
7365 }
7366
7367 /* We must have run out of tuples */
7368 Assert(fsstate->next_tuple >= fsstate->num_tuples);
7369
7370 /* Fetch some more tuples, if we've not detected EOF yet */
7371 if (!fsstate->eof_reached)
7372 {
7373 /* Mark the request as pending for a callback */
7375 /* Begin another fetch if requested and if no pending request */
7376 if (fetch && !pendingAreq)
7378 }
7379 else
7380 {
7381 /* There's nothing more to do; just return a NULL pointer */
7382 result = NULL;
7383 /* Mark the request as complete */
7384 ExecAsyncRequestDone(areq, result);
7385 }
7386}
void ExecAsyncRequestPending(AsyncRequest *areq)
Definition: execAsync.c:149
ExecProcNodeMtd ExecProcNodeReal
Definition: execnodes.h:1172

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

4490{
4491 List *new_tlist = tlist;
4492 List *old_tlist = fscan->fdw_scan_tlist;
4493 ListCell *lc;
4494
4495 foreach(lc, old_tlist)
4496 {
4497 TargetEntry *tle = (TargetEntry *) lfirst(lc);
4498
4499 if (tlist_member(tle->expr, new_tlist))
4500 continue; /* already got it */
4501
4502 new_tlist = lappend(new_tlist,
4503 makeTargetEntry(tle->expr,
4504 list_length(new_tlist) + 1,
4505 NULL,
4506 false));
4507 }
4508 fscan->fdw_scan_tlist = new_tlist;
4509}

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

3930{
3931 AtEOXact_GUC(true, nestlevel);
3932}
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2137

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

5661{
5662 List *vars;
5663 ListCell *lc;
5664 bool ok = true;
5665
5666 Assert(joinrel->reltarget);
5667
5669
5670 foreach(lc, vars)
5671 {
5672 Var *var = (Var *) lfirst(lc);
5673
5674 if (!IsA(var, Var))
5675 continue;
5676
5677 if (bms_is_member(var->varno, innerrel->relids))
5678 {
5679 /*
5680 * The planner can create semi-join, which refers to inner rel
5681 * vars in its target list. However, we deparse semi-join as an
5682 * exists() subquery, so can't handle references to inner rel in
5683 * the target list.
5684 */
5685 Assert(!bms_is_member(var->varno, outerrel->relids));
5686 ok = false;
5687 break;
5688 }
5689 }
5690 return ok;
5691}

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

3894{
3895 int nestlevel = NewGUCNestLevel();
3896
3897 /*
3898 * The values set here should match what pg_dump does. See also
3899 * configure_remote_session in connection.c.
3900 */
3901 if (DateStyle != USE_ISO_DATES)
3902 (void) set_config_option("datestyle", "ISO",
3904 GUC_ACTION_SAVE, true, 0, false);
3906 (void) set_config_option("intervalstyle", "postgres",
3908 GUC_ACTION_SAVE, true, 0, false);
3909 if (extra_float_digits < 3)
3910 (void) set_config_option("extra_float_digits", "3",
3912 GUC_ACTION_SAVE, true, 0, false);
3913
3914 /*
3915 * In addition force restrictive search_path, in case there are any
3916 * regproc or similar constants to be printed.
3917 */
3918 (void) set_config_option("search_path", "pg_catalog",
3920 GUC_ACTION_SAVE, true, 0, false);
3921
3922 return nestlevel;
3923}
int extra_float_digits
Definition: float.c:40
int DateStyle
Definition: globals.c:125
int IntervalStyle
Definition: globals.c:127
int NewGUCNestLevel(void)
Definition: guc.c:2110
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:3205
@ 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:237
#define INTSTYLE_POSTGRES
Definition: miscadmin.h:257

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

4324{
4325 HeapTuple newtup;
4326
4327 newtup = make_tuple_from_result_row(res, 0,
4328 fmstate->rel,
4329 fmstate->attinmeta,
4330 fmstate->retrieved_attrs,
4331 NULL,
4332 fmstate->temp_cxt);
4333
4334 /*
4335 * The returning slot will not necessarily be suitable to store heaptuples
4336 * directly, so allow for conversion.
4337 */
4338 ExecForceStoreHeapTuple(newtup, slot, true);
4339}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1658

References PgFdwModifyState::attinmeta, ExecForceStoreHeapTuple(), make_tuple_from_result_row(), PgFdwModifyState::rel, PgFdwModifyState::retrieved_attrs, and PgFdwModifyState::temp_cxt.

Referenced by execute_foreign_modify().