PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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 "commands/vacuum.h"
#include "executor/execAsync.h"
#include "executor/instrument.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 "pgstat.h"
#include "postgres_fdw.h"
#include "statistics/statistics.h"
#include "storage/latch.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/fmgroids.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 "utils/timestamp.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
 
struct  RemoteAttributeMapping
 
struct  RemoteStatsResults
 

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 }
 
enum  RelStatsColumns { RELSTATS_RELPAGES = 0 , RELSTATS_RELTUPLES , RELSTATS_RELKIND , RELSTATS_NUM_FIELDS }
 
enum  AttStatsColumns {
  ATTSTATS_ATTNAME = 0 , ATTSTATS_NULL_FRAC , ATTSTATS_AVG_WIDTH , ATTSTATS_N_DISTINCT ,
  ATTSTATS_MOST_COMMON_VALS , ATTSTATS_MOST_COMMON_FREQS , ATTSTATS_HISTOGRAM_BOUNDS , ATTSTATS_CORRELATION ,
  ATTSTATS_MOST_COMMON_ELEMS , ATTSTATS_MOST_COMMON_ELEM_FREQS , ATTSTATS_ELEM_COUNT_HISTOGRAM , ATTSTATS_RANGE_LENGTH_HISTOGRAM ,
  ATTSTATS_RANGE_EMPTY_FRAC , ATTSTATS_RANGE_BOUNDS_HISTOGRAM , ATTSTATS_NUM_FIELDS
}
 

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 bool postgresImportForeignStatistics (Relation relation, List *va_cols, int elevel)
 
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 bool fetch_remote_statistics (Relation relation, List *va_cols, ForeignTable *table, const char *local_schemaname, const char *local_relname, int *p_attrcnt, RemoteAttributeMapping **p_remattrmap, RemoteStatsResults *remstats)
 
static PGresultfetch_relstats (PGconn *conn, Relation relation)
 
static PGresultfetch_attstats (PGconn *conn, int server_version_num, const char *remote_schemaname, const char *remote_relname, const char *column_list)
 
static RemoteAttributeMappingbuild_remattrmap (Relation relation, List *va_cols, int *p_attrcnt, StringInfo column_list)
 
static void free_remattrmap (RemoteAttributeMapping *map, int len)
 
static bool attname_in_list (const char *attname, List *va_cols)
 
static int remattrmap_cmp (const void *v1, const void *v2)
 
static bool match_attrmap (PGresult *res, const char *local_schemaname, const char *local_relname, const char *remote_schemaname, const char *remote_relname, int attrcnt, RemoteAttributeMapping *remattrmap)
 
static bool import_fetched_statistics (Relation relation, const char *schemaname, const char *relname, int attrcnt, const RemoteAttributeMapping *remattrmap, RemoteStatsResults *remstats)
 
static charget_opt_value (PGresult *res, int row, int col)
 
static void set_text_arg (NullableDatum *arg, const char *s)
 
static void set_int32_arg (NullableDatum *arg, const char *s)
 
static void set_uint32_arg (NullableDatum *arg, const char *s)
 
static void set_float_arg (NullableDatum *arg, const char *s)
 
static void set_floatarr_arg (NullableDatum *arg, const char *s)
 
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 70 of file postgres_fdw.c.

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 64 of file postgres_fdw.c.

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.2

Definition at line 67 of file postgres_fdw.c.

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ AttStatsColumns

Enumerator
ATTSTATS_ATTNAME 
ATTSTATS_NULL_FRAC 
ATTSTATS_AVG_WIDTH 
ATTSTATS_N_DISTINCT 
ATTSTATS_MOST_COMMON_VALS 
ATTSTATS_MOST_COMMON_FREQS 
ATTSTATS_HISTOGRAM_BOUNDS 
ATTSTATS_CORRELATION 
ATTSTATS_MOST_COMMON_ELEMS 
ATTSTATS_MOST_COMMON_ELEM_FREQS 
ATTSTATS_ELEM_COUNT_HISTOGRAM 
ATTSTATS_RANGE_LENGTH_HISTOGRAM 
ATTSTATS_RANGE_EMPTY_FRAC 
ATTSTATS_RANGE_BOUNDS_HISTOGRAM 
ATTSTATS_NUM_FIELDS 

Definition at line 355 of file postgres_fdw.c.

356{
372};
@ ATTSTATS_HISTOGRAM_BOUNDS
@ ATTSTATS_MOST_COMMON_ELEM_FREQS
@ ATTSTATS_MOST_COMMON_VALS
@ ATTSTATS_CORRELATION
@ ATTSTATS_MOST_COMMON_ELEMS
@ ATTSTATS_AVG_WIDTH
@ ATTSTATS_NULL_FRAC
@ ATTSTATS_ELEM_COUNT_HISTOGRAM
@ ATTSTATS_N_DISTINCT
@ ATTSTATS_ATTNAME
@ ATTSTATS_RANGE_LENGTH_HISTOGRAM
@ ATTSTATS_MOST_COMMON_FREQS
@ ATTSTATS_NUM_FIELDS
@ ATTSTATS_RANGE_BOUNDS_HISTOGRAM
@ ATTSTATS_RANGE_EMPTY_FRAC

◆ FdwDirectModifyPrivateIndex

Enumerator
FdwDirectModifyPrivateUpdateSql 
FdwDirectModifyPrivateHasReturning 
FdwDirectModifyPrivateRetrievedAttrs 
FdwDirectModifyPrivateSetProcessed 

Definition at line 130 of file postgres_fdw.c.

131{
132 /* SQL statement to execute remotely (as a String node) */
134 /* has-returning flag (as a Boolean node) */
136 /* Integer list of attribute numbers retrieved by RETURNING */
138 /* set-processed flag (as a Boolean node) */
140};
@ FdwDirectModifyPrivateSetProcessed
@ FdwDirectModifyPrivateHasReturning
@ FdwDirectModifyPrivateRetrievedAttrs
@ FdwDirectModifyPrivateUpdateSql

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 107 of file postgres_fdw.c.

108{
109 /* SQL statement to execute remotely (as a String node) */
111 /* Integer list of target attribute numbers for INSERT/UPDATE */
113 /* Length till the end of VALUES clause (as an Integer node) */
115 /* has-returning flag (as a Boolean node) */
117 /* Integer list of attribute numbers retrieved by RETURNING */
119};
@ FdwModifyPrivateLen
@ FdwModifyPrivateUpdateSql
@ FdwModifyPrivateTargetAttnums
@ FdwModifyPrivateRetrievedAttrs
@ FdwModifyPrivateHasReturning

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 290 of file postgres_fdw.c.

291{
292 /* has-final-sort flag (as a Boolean node) */
294 /* has-limit flag (as a Boolean node) */
296};
@ FdwPathPrivateHasLimit
@ FdwPathPrivateHasFinalSort

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 79 of file postgres_fdw.c.

80{
81 /* SQL statement to execute remotely (as a String node) */
83 /* Integer list of attribute numbers retrieved by the SELECT */
85 /* Integer representing the desired fetch_size */
87
88 /*
89 * String describing join i.e. names of relations being joined and types
90 * of join, added when the scan is join
91 */
93};
@ FdwScanPrivateRetrievedAttrs
@ FdwScanPrivateSelectSql
@ FdwScanPrivateFetchSize
@ FdwScanPrivateRelations

◆ RelStatsColumns

Enumerator
RELSTATS_RELPAGES 
RELSTATS_RELTUPLES 
RELSTATS_RELKIND 
RELSTATS_NUM_FIELDS 

Definition at line 346 of file postgres_fdw.c.

347{
352};
@ RELSTATS_RELTUPLES
@ RELSTATS_RELKIND
@ RELSTATS_NUM_FIELDS
@ RELSTATS_RELPAGES

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

7818{
7819 Query *parse = root->parse;
7822 bool has_final_sort = false;
7823 List *pathkeys = NIL;
7825 bool save_use_remote_estimate = false;
7826 double rows;
7827 int width;
7828 int disabled_nodes;
7829 Cost startup_cost;
7830 Cost total_cost;
7831 List *fdw_private;
7833
7834 /*
7835 * Currently, we only support this for SELECT commands
7836 */
7837 if (parse->commandType != CMD_SELECT)
7838 return;
7839
7840 /*
7841 * No work if there is no FOR UPDATE/SHARE clause and if there is no need
7842 * to add a LIMIT node
7843 */
7844 if (!parse->rowMarks && !extra->limit_needed)
7845 return;
7846
7847 /* We don't support cases where there are any SRFs in the targetlist */
7848 if (parse->hasTargetSRFs)
7849 return;
7850
7851 /* Save the input_rel as outerrel in fpinfo */
7852 fpinfo->outerrel = input_rel;
7853
7854 /*
7855 * Copy foreign table, foreign server, user mapping, FDW options etc.
7856 * details from the input relation's fpinfo.
7857 */
7858 fpinfo->table = ifpinfo->table;
7859 fpinfo->server = ifpinfo->server;
7860 fpinfo->user = ifpinfo->user;
7862
7863 /*
7864 * If there is no need to add a LIMIT node, there might be a ForeignPath
7865 * in the input_rel's pathlist that implements all behavior of the query.
7866 * Note: we would already have accounted for the query's FOR UPDATE/SHARE
7867 * (if any) before we get here.
7868 */
7869 if (!extra->limit_needed)
7870 {
7871 ListCell *lc;
7872
7873 Assert(parse->rowMarks);
7874
7875 /*
7876 * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
7877 * so the input_rel should be a base, join, or ordered relation; and
7878 * if it's an ordered relation, its input relation should be a base or
7879 * join relation.
7880 */
7881 Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7882 input_rel->reloptkind == RELOPT_JOINREL ||
7883 (input_rel->reloptkind == RELOPT_UPPER_REL &&
7884 ifpinfo->stage == UPPERREL_ORDERED &&
7885 (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
7886 ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
7887
7888 foreach(lc, input_rel->pathlist)
7889 {
7890 Path *path = (Path *) lfirst(lc);
7891
7892 /*
7893 * apply_scanjoin_target_to_paths() uses create_projection_path()
7894 * to adjust each of its input paths if needed, whereas
7895 * create_ordered_paths() uses apply_projection_to_path() to do
7896 * that. So the former might have put a ProjectionPath on top of
7897 * the ForeignPath; look through ProjectionPath and see if the
7898 * path underneath it is ForeignPath.
7899 */
7900 if (IsA(path, ForeignPath) ||
7901 (IsA(path, ProjectionPath) &&
7902 IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
7903 {
7904 /*
7905 * Create foreign final path; this gets rid of a
7906 * no-longer-needed outer plan (if any), which makes the
7907 * EXPLAIN output look cleaner
7908 */
7910 path->parent,
7911 path->pathtarget,
7912 path->rows,
7913 path->disabled_nodes,
7914 path->startup_cost,
7915 path->total_cost,
7916 path->pathkeys,
7917 NULL, /* no extra plan */
7918 NIL, /* no fdw_restrictinfo
7919 * list */
7920 NIL); /* no fdw_private */
7921
7922 /* and add it to the final_rel */
7924
7925 /* Safe to push down */
7926 fpinfo->pushdown_safe = true;
7927
7928 return;
7929 }
7930 }
7931
7932 /*
7933 * If we get here it means no ForeignPaths; since we would already
7934 * have considered pushing down all operations for the query to the
7935 * remote server, give up on it.
7936 */
7937 return;
7938 }
7939
7940 Assert(extra->limit_needed);
7941
7942 /*
7943 * If the input_rel is an ordered relation, replace the input_rel with its
7944 * input relation
7945 */
7946 if (input_rel->reloptkind == RELOPT_UPPER_REL &&
7947 ifpinfo->stage == UPPERREL_ORDERED)
7948 {
7949 input_rel = ifpinfo->outerrel;
7950 ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
7951 has_final_sort = true;
7952 pathkeys = root->sort_pathkeys;
7953 }
7954
7955 /* The input_rel should be a base, join, or grouping relation */
7956 Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7957 input_rel->reloptkind == RELOPT_JOINREL ||
7958 (input_rel->reloptkind == RELOPT_UPPER_REL &&
7959 ifpinfo->stage == UPPERREL_GROUP_AGG));
7960
7961 /*
7962 * We try to create a path below by extending a simple foreign path for
7963 * the underlying base, join, or grouping relation to perform the final
7964 * sort (if has_final_sort) and the LIMIT restriction remotely, which is
7965 * stored into the fdw_private list of the resulting path. (We
7966 * re-estimate the costs of sorting the underlying relation, if
7967 * has_final_sort.)
7968 */
7969
7970 /*
7971 * Assess if it is safe to push down the LIMIT and OFFSET to the remote
7972 * server
7973 */
7974
7975 /*
7976 * If the underlying relation has any local conditions, the LIMIT/OFFSET
7977 * cannot be pushed down.
7978 */
7979 if (ifpinfo->local_conds)
7980 return;
7981
7982 /*
7983 * If the query has FETCH FIRST .. WITH TIES, 1) it must have ORDER BY as
7984 * well, which is used to determine which additional rows tie for the last
7985 * place in the result set, and 2) ORDER BY must already have been
7986 * determined to be safe to push down before we get here. So in that case
7987 * the FETCH clause is safe to push down with ORDER BY if the remote
7988 * server is v13 or later, but if not, the remote query will fail entirely
7989 * for lack of support for it. Since we do not currently have a way to do
7990 * a remote-version check (without accessing the remote server), disable
7991 * pushing the FETCH clause for now.
7992 */
7993 if (parse->limitOption == LIMIT_OPTION_WITH_TIES)
7994 return;
7995
7996 /*
7997 * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
7998 * not safe to remote.
7999 */
8000 if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
8001 !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
8002 return;
8003
8004 /* Safe to push down */
8005 fpinfo->pushdown_safe = true;
8006
8007 /* Construct PgFdwPathExtraData */
8009 fpextra->target = root->upper_targets[UPPERREL_FINAL];
8010 fpextra->has_final_sort = has_final_sort;
8011 fpextra->has_limit = extra->limit_needed;
8012 fpextra->limit_tuples = extra->limit_tuples;
8013 fpextra->count_est = extra->count_est;
8014 fpextra->offset_est = extra->offset_est;
8015
8016 /*
8017 * Estimate the costs of performing the final sort and the LIMIT
8018 * restriction remotely. If has_final_sort is false, we wouldn't need to
8019 * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
8020 * roughly estimated using the costs we already have for the underlying
8021 * relation, in the same way as when use_remote_estimate is false. Since
8022 * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
8023 * false in that case.
8024 */
8025 if (!fpextra->has_final_sort)
8026 {
8027 save_use_remote_estimate = ifpinfo->use_remote_estimate;
8028 ifpinfo->use_remote_estimate = false;
8029 }
8031 &rows, &width, &disabled_nodes,
8032 &startup_cost, &total_cost);
8033 if (!fpextra->has_final_sort)
8034 ifpinfo->use_remote_estimate = save_use_remote_estimate;
8035
8036 /*
8037 * Build the fdw_private list that will be used by postgresGetForeignPlan.
8038 * Items in the list must match order in enum FdwPathPrivateIndex.
8039 */
8040 fdw_private = list_make2(makeBoolean(has_final_sort),
8041 makeBoolean(extra->limit_needed));
8042
8043 /*
8044 * Create foreign final path; this gets rid of a no-longer-needed outer
8045 * plan (if any), which makes the EXPLAIN output look cleaner
8046 */
8048 input_rel,
8049 root->upper_targets[UPPERREL_FINAL],
8050 rows,
8051 disabled_nodes,
8052 startup_cost,
8053 total_cost,
8054 pathkeys,
8055 NULL, /* no extra plan */
8056 NIL, /* no fdw_restrictinfo list */
8057 fdw_private);
8058
8059 /* and add it to the final_rel */
8061}
#define Assert(condition)
Definition c.h:1002
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition deparse.c:244
#define palloc0_object(type)
Definition fe_memutils.h:90
void parse(int)
Definition parse.c:49
Datum subpath(PG_FUNCTION_ARGS)
Definition ltree_op.c:348
#define IsA(nodeptr, _type_)
Definition nodes.h:162
double Cost
Definition nodes.h:259
@ CMD_SELECT
Definition nodes.h:273
@ LIMIT_OPTION_WITH_TIES
Definition nodes.h:441
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:2230
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition pathnode.c:459
@ UPPERREL_GROUP_AGG
Definition pathnodes.h:147
@ UPPERREL_FINAL
Definition pathnodes.h:152
@ UPPERREL_ORDERED
Definition pathnodes.h:151
@ RELOPT_BASEREL
Definition pathnodes.h:977
@ RELOPT_UPPER_REL
Definition pathnodes.h:981
@ RELOPT_JOINREL
Definition pathnodes.h:978
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
#define list_make2(x1, x2)
Definition pg_list.h:246
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)
static int fb(int x)
tree ctl root
Definition radixtree.h:1857
Cardinality limit_tuples
Definition pathnodes.h:3707
Definition pg_list.h:54
List * pathkeys
Definition pathnodes.h:2011
Cardinality rows
Definition pathnodes.h:2005
Cost startup_cost
Definition pathnodes.h:2007
int disabled_nodes
Definition pathnodes.h:2006
Cost total_cost
Definition pathnodes.h:2008
Boolean * makeBoolean(bool val)
Definition value.c:49

References add_path(), Assert, CMD_SELECT, FinalPathExtraData::count_est, create_foreign_upper_path(), Path::disabled_nodes, estimate_path_cost_size(), fb(), is_foreign_expr(), IsA, lfirst, FinalPathExtraData::limit_needed, LIMIT_OPTION_WITH_TIES, FinalPathExtraData::limit_tuples, list_make2, makeBoolean(), merge_fdw_options(), NIL, FinalPathExtraData::offset_est, palloc0_object, parse(), Path::pathkeys, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, root, Path::rows, Path::startup_cost, subpath(), Path::total_cost, UPPERREL_FINAL, UPPERREL_GROUP_AGG, and UPPERREL_ORDERED.

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

7584{
7585 Query *parse = root->parse;
7586 PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
7587 PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
7589 double rows;
7590 int width;
7591 int disabled_nodes;
7592 Cost startup_cost;
7593 Cost total_cost;
7594
7595 /* Nothing to be done, if there is no grouping or aggregation required. */
7596 if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
7597 !root->hasHavingQual)
7598 return;
7599
7602
7603 /* save the input_rel as outerrel in fpinfo */
7604 fpinfo->outerrel = input_rel;
7605
7606 /*
7607 * Copy foreign table, foreign server, user mapping, FDW options etc.
7608 * details from the input relation's fpinfo.
7609 */
7610 fpinfo->table = ifpinfo->table;
7611 fpinfo->server = ifpinfo->server;
7612 fpinfo->user = ifpinfo->user;
7614
7615 /*
7616 * Assess if it is safe to push down aggregation and grouping.
7617 *
7618 * Use HAVING qual from extra. In case of child partition, it will have
7619 * translated Vars.
7620 */
7621 if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
7622 return;
7623
7624 /*
7625 * Compute the selectivity and cost of the local_conds, so we don't have
7626 * to do it over again for each path. (Currently we create just a single
7627 * path here, but in future it would be possible that we build more paths
7628 * such as pre-sorted paths as in postgresGetForeignPaths and
7629 * postgresGetForeignJoinPaths.) The best we can do for these conditions
7630 * is to estimate selectivity on the basis of local statistics.
7631 */
7632 fpinfo->local_conds_sel = clauselist_selectivity(root,
7633 fpinfo->local_conds,
7634 0,
7635 JOIN_INNER,
7636 NULL);
7637
7638 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
7639
7640 /* Estimate the cost of push down */
7641 estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
7642 &rows, &width, &disabled_nodes,
7643 &startup_cost, &total_cost);
7644
7645 /* Now update this information in the fpinfo */
7646 fpinfo->rows = rows;
7647 fpinfo->width = width;
7648 fpinfo->disabled_nodes = disabled_nodes;
7649 fpinfo->startup_cost = startup_cost;
7650 fpinfo->total_cost = total_cost;
7651
7652 /* Create and add foreign path to the grouping relation. */
7654 grouped_rel,
7655 grouped_rel->reltarget,
7656 rows,
7657 disabled_nodes,
7658 startup_cost,
7659 total_cost,
7660 NIL, /* no pathkeys */
7661 NULL,
7662 NIL, /* no fdw_restrictinfo list */
7663 NIL); /* no fdw_private */
7664
7665 /* Add generated path into grouped_rel by add_path(). */
7666 add_path(grouped_rel, (Path *) grouppath);
7667}
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:4923
@ JOIN_INNER
Definition nodes.h:301
@ PARTITIONWISE_AGGREGATE_FULL
Definition pathnodes.h:3662
@ PARTITIONWISE_AGGREGATE_NONE
Definition pathnodes.h:3661
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
PartitionwiseAggregateType patype
Definition pathnodes.h:3691
struct PathTarget * reltarget
Definition pathnodes.h:1045

References add_path(), Assert, clauselist_selectivity(), cost_qual_eval(), create_foreign_upper_path(), estimate_path_cost_size(), fb(), foreign_grouping_ok(), GroupPathExtraData::havingQual, JOIN_INNER, merge_fdw_options(), NIL, parse(), PARTITIONWISE_AGGREGATE_FULL, PARTITIONWISE_AGGREGATE_NONE, GroupPathExtraData::patype, RelOptInfo::reltarget, and root.

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

7679{
7680 Query *parse = root->parse;
7681 PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
7682 PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
7684 double rows;
7685 int width;
7686 int disabled_nodes;
7687 Cost startup_cost;
7688 Cost total_cost;
7689 List *fdw_private;
7691 ListCell *lc;
7692
7693 /* Shouldn't get here unless the query has ORDER BY */
7694 Assert(parse->sortClause);
7695
7696 /* We don't support cases where there are any SRFs in the targetlist */
7697 if (parse->hasTargetSRFs)
7698 return;
7699
7700 /* Save the input_rel as outerrel in fpinfo */
7701 fpinfo->outerrel = input_rel;
7702
7703 /*
7704 * Copy foreign table, foreign server, user mapping, FDW options etc.
7705 * details from the input relation's fpinfo.
7706 */
7707 fpinfo->table = ifpinfo->table;
7708 fpinfo->server = ifpinfo->server;
7709 fpinfo->user = ifpinfo->user;
7711
7712 /*
7713 * If the input_rel is a base or join relation, we would already have
7714 * considered pushing down the final sort to the remote server when
7715 * creating pre-sorted foreign paths for that relation, because the
7716 * query_pathkeys is set to the root->sort_pathkeys in that case (see
7717 * standard_qp_callback()).
7718 */
7719 if (input_rel->reloptkind == RELOPT_BASEREL ||
7720 input_rel->reloptkind == RELOPT_JOINREL)
7721 {
7722 Assert(root->query_pathkeys == root->sort_pathkeys);
7723
7724 /* Safe to push down if the query_pathkeys is safe to push down */
7725 fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
7726
7727 return;
7728 }
7729
7730 /* The input_rel should be a grouping relation */
7731 Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
7732 ifpinfo->stage == UPPERREL_GROUP_AGG);
7733
7734 /*
7735 * We try to create a path below by extending a simple foreign path for
7736 * the underlying grouping relation to perform the final sort remotely,
7737 * which is stored into the fdw_private list of the resulting path.
7738 */
7739
7740 /* Assess if it is safe to push down the final sort */
7741 foreach(lc, root->sort_pathkeys)
7742 {
7744 EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
7745
7746 /*
7747 * is_foreign_expr would detect volatile expressions as well, but
7748 * checking ec_has_volatile here saves some cycles.
7749 */
7750 if (pathkey_ec->ec_has_volatile)
7751 return;
7752
7753 /*
7754 * Can't push down the sort if pathkey's opfamily is not shippable.
7755 */
7757 fpinfo))
7758 return;
7759
7760 /*
7761 * The EC must contain a shippable EM that is computed in input_rel's
7762 * reltarget, else we can't push down the sort.
7763 */
7765 pathkey_ec,
7766 input_rel) == NULL)
7767 return;
7768 }
7769
7770 /* Safe to push down */
7771 fpinfo->pushdown_safe = true;
7772
7773 /* Construct PgFdwPathExtraData */
7775 fpextra->target = root->upper_targets[UPPERREL_ORDERED];
7776 fpextra->has_final_sort = true;
7777
7778 /* Estimate the costs of performing the final sort remotely */
7780 &rows, &width, &disabled_nodes,
7781 &startup_cost, &total_cost);
7782
7783 /*
7784 * Build the fdw_private list that will be used by postgresGetForeignPlan.
7785 * Items in the list must match order in enum FdwPathPrivateIndex.
7786 */
7787 fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
7788
7789 /* Create foreign ordering path */
7791 input_rel,
7792 root->upper_targets[UPPERREL_ORDERED],
7793 rows,
7794 disabled_nodes,
7795 startup_cost,
7796 total_cost,
7797 root->sort_pathkeys,
7798 NULL, /* no extra plan */
7799 NIL, /* no fdw_restrictinfo
7800 * list */
7801 fdw_private);
7802
7803 /* and add it to the ordered_rel */
7805}
EquivalenceMember * find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
bool is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
Definition shippable.c:163

References add_path(), Assert, create_foreign_upper_path(), estimate_path_cost_size(), fb(), find_em_for_rel_target(), is_shippable(), lfirst, list_make2, makeBoolean(), merge_fdw_options(), NIL, palloc0_object, parse(), RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, root, UPPERREL_GROUP_AGG, and UPPERREL_ORDERED.

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

6902{
6903 List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6904 ListCell *lc;
6905
6907
6908 /*
6909 * Before creating sorted paths, arrange for the passed-in EPQ path, if
6910 * any, to return columns needed by the parent ForeignScan node so that
6911 * they will propagate up through Sort nodes injected below, if necessary.
6912 */
6914 {
6915 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6916 PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6917
6918 /* Include columns required for evaluating PHVs in the tlist. */
6920 pull_var_clause((Node *) target->exprs,
6922
6923 /* Include columns required for evaluating the local conditions. */
6924 foreach(lc, fpinfo->local_conds)
6925 {
6927
6929 pull_var_clause((Node *) rinfo->clause,
6931 }
6932
6933 /*
6934 * If we have added any new columns, adjust the tlist of the EPQ path.
6935 *
6936 * Note: the plan created using this path will only be used to execute
6937 * EPQ checks, where accuracy of the plan cost and width estimates
6938 * would not be important, so we do not do set_pathtarget_cost_width()
6939 * for the new pathtarget here. See also postgresGetForeignPlan().
6940 */
6941 if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6942 {
6943 /* The EPQ path is a join path, so it is projection-capable. */
6945
6946 /*
6947 * Use create_projection_path() here, so as to avoid modifying it
6948 * in place.
6949 */
6951 rel,
6952 epq_path,
6953 target);
6954 }
6955 }
6956
6957 /* Create one path for each set of pathkeys we found above. */
6958 foreach(lc, useful_pathkeys_list)
6959 {
6960 double rows;
6961 int width;
6962 int disabled_nodes;
6963 Cost startup_cost;
6964 Cost total_cost;
6967
6969 &rows, &width, &disabled_nodes,
6970 &startup_cost, &total_cost);
6971
6972 /*
6973 * The EPQ path must be at least as well sorted as the path itself, in
6974 * case it gets used as input to a mergejoin.
6975 */
6977 if (sorted_epq_path != NULL &&
6979 sorted_epq_path->pathkeys))
6980 sorted_epq_path = (Path *)
6982 rel,
6985 -1.0);
6986
6987 if (IS_SIMPLE_REL(rel))
6988 add_path(rel, (Path *)
6990 NULL,
6991 rows,
6992 disabled_nodes,
6993 startup_cost,
6994 total_cost,
6996 rel->lateral_relids,
6998 NIL, /* no fdw_restrictinfo
6999 * list */
7000 NIL));
7001 else
7002 add_path(rel, (Path *)
7004 NULL,
7005 rows,
7006 disabled_nodes,
7007 startup_cost,
7008 total_cost,
7010 rel->lateral_relids,
7012 restrictlist,
7013 NIL));
7014 }
7015}
bool is_projection_capable_path(Path *path)
#define PVC_RECURSE_PLACEHOLDERS
Definition optimizer.h:202
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:2587
SortPath * create_sort_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *pathkeys, double limit_tuples)
Definition pathnode.c:2904
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:2128
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:2176
#define IS_SIMPLE_REL(rel)
Definition pathnodes.h:989
#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 nodes.h:133
List * exprs
Definition pathnodes.h:1878
Relids lateral_relids
Definition pathnodes.h:1064
Expr * clause
Definition pathnodes.h:2901
PathTarget * copy_pathtarget(PathTarget *src)
Definition tlist.c:666
void add_new_columns_to_pathtarget(PathTarget *target, List *exprs)
Definition tlist.c:761
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(), PathTarget::exprs, fb(), get_useful_pathkeys_for_relation(), is_projection_capable_path(), IS_SIMPLE_REL, RelOptInfo::lateral_relids, lfirst, lfirst_node, list_length(), NIL, 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 3734 of file postgres_fdw.c.

3742{
3743 /*
3744 * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3745 * side is unlikely to generate properly-sorted output, so it would need
3746 * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3747 * if the GROUP BY clause is sort-able but isn't a superset of the given
3748 * pathkeys, adjust the costs with that function. Otherwise, adjust the
3749 * costs by applying the same heuristic as for the scan or join case.
3750 */
3751 if (!grouping_is_sortable(root->processed_groupClause) ||
3752 !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3753 {
3754 Path sort_path; /* dummy for result of cost_sort */
3755
3757 root,
3758 pathkeys,
3759 0,
3761 retrieved_rows,
3762 width,
3763 0.0,
3764 work_mem,
3765 limit_tuples);
3766
3767 *p_startup_cost = sort_path.startup_cost;
3768 *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3769 }
3770 else
3771 {
3772 /*
3773 * The default extra cost seems too large for foreign-grouping cases;
3774 * add 1/4th of that default.
3775 */
3777 - 1.0) * 0.25;
3778
3781 }
3782}
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:2202
int work_mem
Definition globals.c:133
#define DEFAULT_FDW_SORT_MULTIPLIER
bool grouping_is_sortable(List *groupClause)
Definition tlist.c:549

References cost_sort(), DEFAULT_FDW_SORT_MULTIPLIER, fb(), grouping_is_sortable(), pathkeys_contained_in(), root, 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 5401 of file postgres_fdw.c.

5402{
5403 int targrows = astate->targrows;
5404 int pos; /* array index to store tuple in */
5405 MemoryContext oldcontext;
5406
5407 /* Always increment sample row counter. */
5408 astate->samplerows += 1;
5409
5410 /*
5411 * Determine the slot where this sample row should be stored. Set pos to
5412 * negative value to indicate the row should be skipped.
5413 */
5414 if (astate->numrows < targrows)
5415 {
5416 /* First targrows rows are always included into the sample */
5417 pos = astate->numrows++;
5418 }
5419 else
5420 {
5421 /*
5422 * Now we start replacing tuples in the sample until we reach the end
5423 * of the relation. Same algorithm as in acquire_sample_rows in
5424 * analyze.c; see Jeff Vitter's paper.
5425 */
5426 if (astate->rowstoskip < 0)
5427 astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5428
5429 if (astate->rowstoskip <= 0)
5430 {
5431 /* Choose a random reservoir element to replace. */
5432 pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5433 Assert(pos >= 0 && pos < targrows);
5434 heap_freetuple(astate->rows[pos]);
5435 }
5436 else
5437 {
5438 /* Skip this tuple. */
5439 pos = -1;
5440 }
5441
5442 astate->rowstoskip -= 1;
5443 }
5444
5445 if (pos >= 0)
5446 {
5447 /*
5448 * Create sample tuple from current result row, and store it in the
5449 * position determined above. The tuple has to be created in anl_cxt.
5450 */
5451 oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5452
5453 astate->rows[pos] = make_tuple_from_result_row(res, row,
5454 astate->rel,
5455 astate->attinmeta,
5456 astate->retrieved_attrs,
5457 NULL,
5458 astate->temp_cxt);
5459
5460 MemoryContextSwitchTo(oldcontext);
5461 }
5462}
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
double sampler_random_fract(pg_prng_state *randstate)
Definition sampling.c:241
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition sampling.c:147
ReservoirStateData rstate
AttInMetadata * attinmeta
MemoryContext anl_cxt
MemoryContext temp_cxt
pg_prng_state randstate
Definition sampling.h:49

References PgFdwAnalyzeState::anl_cxt, Assert, PgFdwAnalyzeState::attinmeta, fb(), 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 4799 of file postgres_fdw.c.

4803{
4806 Datum *values;
4807 bool *isnull;
4809 bool *old_isnull;
4810 int i;
4811
4812 /*
4813 * Use the return tuple slot as a place to store the result tuple.
4814 */
4815 resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4816
4817 /*
4818 * Extract all the values of the scan tuple.
4819 */
4820 slot_getallattrs(slot);
4821 old_values = slot->tts_values;
4822 old_isnull = slot->tts_isnull;
4823
4824 /*
4825 * Prepare to build the result tuple.
4826 */
4828 values = resultSlot->tts_values;
4829 isnull = resultSlot->tts_isnull;
4830
4831 /*
4832 * Transpose data into proper fields of the result tuple.
4833 */
4834 for (i = 0; i < resultTupType->natts; i++)
4835 {
4836 int j = dmstate->attnoMap[i];
4837
4838 if (j == 0)
4839 {
4840 values[i] = (Datum) 0;
4841 isnull[i] = true;
4842 }
4843 else
4844 {
4845 values[i] = old_values[j - 1];
4846 isnull[i] = old_isnull[j - 1];
4847 }
4848 }
4849
4850 /*
4851 * Build the virtual tuple.
4852 */
4854
4855 /*
4856 * If we have any system columns to return, materialize a heap tuple in
4857 * the slot from column values set above and install system columns in
4858 * that tuple.
4859 */
4860 if (dmstate->hasSystemCols)
4861 {
4863
4864 /* ctid */
4865 if (dmstate->ctidAttno)
4866 {
4867 ItemPointer ctid = NULL;
4868
4869 ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4870 resultTup->t_self = *ctid;
4871 }
4872
4873 /*
4874 * And remaining columns
4875 *
4876 * Note: since we currently don't allow the target relation to appear
4877 * on the nullable side of an outer join, any system columns wouldn't
4878 * go to NULL.
4879 *
4880 * Note: no need to care about tableoid here because it will be
4881 * initialized in ExecProcessReturning().
4882 */
4886 }
4887
4888 /*
4889 * And return the result tuple.
4890 */
4891 return resultSlot;
4892}
static Datum values[MAXATTR]
Definition bootstrap.c:190
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1274
static void HeapTupleHeaderSetCmin(HeapTupleHeaderData *tup, CommandId cid)
static void HeapTupleHeaderSetXmin(HeapTupleHeaderData *tup, TransactionId xid)
static void HeapTupleHeaderSetXmax(HeapTupleHeaderData *tup, TransactionId xid)
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:332
#define RelationGetDescr(relation)
Definition rel.h:542
bool * tts_isnull
Definition tuptable.h:133
Datum * tts_values
Definition tuptable.h:131
#define InvalidTransactionId
Definition transam.h:31
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
static void slot_getallattrs(TupleTableSlot *slot)
Definition tuptable.h:390

References DatumGetPointer(), ExecClearTuple(), ExecFetchSlotHeapTuple(), ExecGetReturningSlot(), ExecStoreVirtualTuple(), fb(), HeapTupleHeaderSetCmin(), HeapTupleHeaderSetXmax(), HeapTupleHeaderSetXmin(), i, InvalidTransactionId, j, RelationGetDescr, slot_getallattrs(), 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 7023 of file postgres_fdw.c.

7024{
7025 ListCell *lc;
7026
7027 foreach(lc, fpinfo->server->options)
7028 {
7029 DefElem *def = (DefElem *) lfirst(lc);
7030
7031 if (strcmp(def->defname, "use_remote_estimate") == 0)
7032 fpinfo->use_remote_estimate = defGetBoolean(def);
7033 else if (strcmp(def->defname, "fdw_startup_cost") == 0)
7034 (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
7035 NULL);
7036 else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
7037 (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
7038 NULL);
7039 else if (strcmp(def->defname, "extensions") == 0)
7040 fpinfo->shippable_extensions =
7042 else if (strcmp(def->defname, "fetch_size") == 0)
7043 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
7044 else if (strcmp(def->defname, "async_capable") == 0)
7045 fpinfo->async_capable = defGetBoolean(def);
7046 }
7047}
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition option.c:450
char * defGetString(DefElem *def)
Definition define.c:34
bool defGetBoolean(DefElem *def)
Definition define.c:93
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition guc.c:2775
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition guc.c:2865
char * defname
Definition parsenodes.h:862

References defGetBoolean(), defGetString(), DefElem::defname, ExtractExtensionList(), fb(), lfirst, parse_int(), and parse_real().

Referenced by postgresGetForeignRelSize().

◆ apply_table_options()

static void apply_table_options ( PgFdwRelationInfo fpinfo)
static

Definition at line 7055 of file postgres_fdw.c.

7056{
7057 ListCell *lc;
7058
7059 foreach(lc, fpinfo->table->options)
7060 {
7061 DefElem *def = (DefElem *) lfirst(lc);
7062
7063 if (strcmp(def->defname, "use_remote_estimate") == 0)
7064 fpinfo->use_remote_estimate = defGetBoolean(def);
7065 else if (strcmp(def->defname, "fetch_size") == 0)
7066 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
7067 else if (strcmp(def->defname, "async_capable") == 0)
7068 fpinfo->async_capable = defGetBoolean(def);
7069 }
7070}

References defGetBoolean(), defGetString(), DefElem::defname, fb(), lfirst, and parse_int().

Referenced by postgresGetForeignRelSize().

◆ attname_in_list()

static bool attname_in_list ( const char attname,
List va_cols 
)
static

Definition at line 5891 of file postgres_fdw.c.

5892{
5893 ListCell *lc;
5894
5895 if (va_cols == NIL)
5896 return true;
5897
5898 foreach(lc, va_cols)
5899 {
5900 char *col = strVal(lfirst(lc));
5901
5902 if (strcmp(attname, col) == 0)
5903 return true;
5904 }
5905 return false;
5906}
NameData attname
#define strVal(v)
Definition value.h:82

References attname, fb(), lfirst, NIL, and strVal.

Referenced by build_remattrmap().

◆ build_remattrmap()

static RemoteAttributeMapping * build_remattrmap ( Relation  relation,
List va_cols,
int p_attrcnt,
StringInfo  column_list 
)
static

Definition at line 5805 of file postgres_fdw.c.

5807{
5808 TupleDesc tupdesc = RelationGetDescr(relation);
5810 int attrcnt = 0;
5811
5815 for (int i = 0; i < tupdesc->natts; i++)
5816 {
5817 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
5818 char *attname = NameStr(attr->attname);
5819 AttrNumber attnum = attr->attnum;
5820 char *remote_attname;
5822 ListCell *lc;
5823
5824 /* If a list is specified, exclude any attnames not in it. */
5825 if (!attname_in_list(attname, va_cols))
5826 continue;
5827
5828 if (!attribute_is_analyzable(relation, attnum, attr, NULL))
5829 continue;
5830
5831 /* If the column_name option is not specified, go with attname. */
5832 remote_attname = attname;
5834 foreach(lc, fc_options)
5835 {
5836 DefElem *def = (DefElem *) lfirst(lc);
5837
5838 if (strcmp(def->defname, "column_name") == 0)
5839 {
5840 remote_attname = defGetString(def);
5841 break;
5842 }
5843 }
5844
5845 if (attrcnt > 0)
5847 deparseStringLiteral(column_list, remote_attname);
5848
5849 remattrmap[attrcnt].local_attnum = attnum;
5850 remattrmap[attrcnt].local_attname = pstrdup(attname);
5851 remattrmap[attrcnt].remote_attname = pstrdup(remote_attname);
5852 remattrmap[attrcnt].res_index = -1;
5853 attrcnt++;
5854 }
5856
5857 /* Sort mapping by remote attribute name if needed. */
5858 if (attrcnt > 1)
5860
5861 *p_attrcnt = attrcnt;
5862 return remattrmap;
5863}
int16 AttrNumber
Definition attnum.h:21
#define NameStr(name)
Definition c.h:894
bool attribute_is_analyzable(Relation onerel, int attnum, Form_pg_attribute attr, int *p_attstattarget)
Definition analyze.c:1176
void deparseStringLiteral(StringInfo buf, const char *val)
Definition deparse.c:2900
#define palloc_array(type, count)
Definition fe_memutils.h:91
List * GetForeignColumnOptions(Oid relid, AttrNumber attnum)
Definition foreign.c:324
char * pstrdup(const char *in)
Definition mcxt.c:1910
int16 attnum
FormData_pg_attribute * Form_pg_attribute
#define qsort(a, b, c, d)
Definition port.h:496
static bool attname_in_list(const char *attname, List *va_cols)
static int remattrmap_cmp(const void *v1, const void *v2)
#define RelationGetRelid(relation)
Definition rel.h:516
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178

References appendStringInfoChar(), appendStringInfoString(), attname, attname_in_list(), attnum, attribute_is_analyzable(), defGetString(), DefElem::defname, deparseStringLiteral(), fb(), GetForeignColumnOptions(), i, initStringInfo(), lfirst, NameStr, TupleDescData::natts, palloc_array, pstrdup(), qsort, RelationGetDescr, RelationGetRelid, remattrmap_cmp(), and TupleDescAttr().

Referenced by fetch_remote_statistics().

◆ build_remote_returning()

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

Definition at line 4482 of file postgres_fdw.c.

4483{
4484 bool have_wholerow = false;
4485 List *tlist = NIL;
4486 List *vars;
4487 ListCell *lc;
4488
4489 Assert(returningList);
4490
4491 vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4492
4493 /*
4494 * If there's a whole-row reference to the target relation, then we'll
4495 * need all the columns of the relation.
4496 */
4497 foreach(lc, vars)
4498 {
4499 Var *var = (Var *) lfirst(lc);
4500
4501 if (IsA(var, Var) &&
4502 var->varno == rtindex &&
4504 {
4505 have_wholerow = true;
4506 break;
4507 }
4508 }
4509
4510 if (have_wholerow)
4511 {
4512 TupleDesc tupdesc = RelationGetDescr(rel);
4513 int i;
4514
4515 for (i = 1; i <= tupdesc->natts; i++)
4516 {
4517 Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4518 Var *var;
4519
4520 /* Ignore dropped attributes. */
4521 if (attr->attisdropped)
4522 continue;
4523
4524 var = makeVar(rtindex,
4525 i,
4526 attr->atttypid,
4527 attr->atttypmod,
4528 attr->attcollation,
4529 0);
4530
4531 tlist = lappend(tlist,
4532 makeTargetEntry((Expr *) var,
4533 list_length(tlist) + 1,
4534 NULL,
4535 false));
4536 }
4537 }
4538
4539 /* Now add any remaining columns to tlist. */
4540 foreach(lc, vars)
4541 {
4542 Var *var = (Var *) lfirst(lc);
4543
4544 /*
4545 * No need for whole-row references to the target relation. We don't
4546 * need system columns other than ctid and oid either, since those are
4547 * set locally.
4548 */
4549 if (IsA(var, Var) &&
4550 var->varno == rtindex &&
4551 var->varattno <= InvalidAttrNumber &&
4553 continue; /* don't need it */
4554
4555 if (tlist_member((Expr *) var, tlist))
4556 continue; /* already got it */
4557
4558 tlist = lappend(tlist,
4559 makeTargetEntry((Expr *) var,
4560 list_length(tlist) + 1,
4561 NULL,
4562 false));
4563 }
4564
4565 list_free(vars);
4566
4567 return tlist;
4568}
#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:201
AttrNumber varattno
Definition primnodes.h:275
int varno
Definition primnodes.h:270
#define SelfItemPointerAttributeNumber
Definition sysattr.h:21
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition tlist.c:88

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

4034{
4035 char sql[64];
4036 PGresult *res;
4037
4038 snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
4039 res = pgfdw_exec_query(conn, sql, conn_state);
4040 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4041 pgfdw_report_error(res, conn, sql);
4042 PQclear(res);
4043}
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
void pgfdw_report_error(PGresult *res, PGconn *conn, const char *sql)
static unsigned int cursor_number
Definition connection.c:84
#define PQclear
#define PQresultStatus
@ PGRES_COMMAND_OK
Definition libpq-fe.h:131
#define snprintf
Definition port.h:261
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 8337 of file postgres_fdw.c.

8338{
8339 /* The request would have been pending for a callback */
8340 Assert(areq->callback_pending);
8341
8342 /* Unlike AsyncNotify, we unset callback_pending ourselves */
8343 areq->callback_pending = false;
8344
8345 /* We begin a fetch afterwards if necessary; don't fetch */
8347
8348 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
8350
8351 /* Also, we do instrumentation ourselves, if required */
8352 if (areq->requestee->instrument)
8353 InstrUpdateTupleCount(areq->requestee->instrument,
8354 TupIsNull(areq->result) ? 0.0 : 1.0);
8355}
void ExecAsyncResponse(AsyncRequest *areq)
Definition execAsync.c:118
void InstrUpdateTupleCount(NodeInstrumentation *instr, double nTuples)
Definition instrument.c:196
static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch)
#define TupIsNull(slot)
Definition tuptable.h:325

References Assert, ExecAsyncResponse(), fb(), InstrUpdateTupleCount(), produce_tuple_asynchronously(), and TupIsNull.

Referenced by postgresForeignAsyncConfigureWait().

◆ conversion_error_callback()

static void conversion_error_callback ( void arg)
static

Definition at line 8528 of file postgres_fdw.c.

8529{
8531 Relation rel = errpos->rel;
8532 ForeignScanState *fsstate = errpos->fsstate;
8533 const char *attname = NULL;
8534 const char *relname = NULL;
8535 bool is_wholerow = false;
8536
8537 /*
8538 * If we're in a scan node, always use aliases from the rangetable, for
8539 * consistency between the simple-relation and remote-join cases. Look at
8540 * the relation's tupdesc only if we're not in a scan node.
8541 */
8542 if (fsstate)
8543 {
8544 /* ForeignScan case */
8546 int varno = 0;
8547 AttrNumber colno = 0;
8548
8549 if (fsplan->scan.scanrelid > 0)
8550 {
8551 /* error occurred in a scan against a foreign table */
8552 varno = fsplan->scan.scanrelid;
8553 colno = errpos->cur_attno;
8554 }
8555 else
8556 {
8557 /* error occurred in a scan against a foreign join */
8559
8560 tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
8561 errpos->cur_attno - 1);
8562
8563 /*
8564 * Target list can have Vars and expressions. For Vars, we can
8565 * get some information, however for expressions we can't. Thus
8566 * for expressions, just show generic context message.
8567 */
8568 if (IsA(tle->expr, Var))
8569 {
8570 Var *var = (Var *) tle->expr;
8571
8572 varno = var->varno;
8573 colno = var->varattno;
8574 }
8575 }
8576
8577 if (varno > 0)
8578 {
8579 EState *estate = fsstate->ss.ps.state;
8580 RangeTblEntry *rte = exec_rt_fetch(varno, estate);
8581
8582 relname = rte->eref->aliasname;
8583
8584 if (colno == 0)
8585 is_wholerow = true;
8586 else if (colno > 0 && colno <= list_length(rte->eref->colnames))
8587 attname = strVal(list_nth(rte->eref->colnames, colno - 1));
8588 else if (colno == SelfItemPointerAttributeNumber)
8589 attname = "ctid";
8590 }
8591 }
8592 else if (rel)
8593 {
8594 /* Non-ForeignScan case (we should always have a rel here) */
8595 TupleDesc tupdesc = RelationGetDescr(rel);
8596
8598 if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
8599 {
8600 Form_pg_attribute attr = TupleDescAttr(tupdesc,
8601 errpos->cur_attno - 1);
8602
8603 attname = NameStr(attr->attname);
8604 }
8605 else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
8606 attname = "ctid";
8607 }
8608
8609 if (relname && is_wholerow)
8610 errcontext("whole-row reference to foreign table \"%s\"", relname);
8611 else if (relname && attname)
8612 errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
8613 else
8614 errcontext("processing expression at position %d in select list",
8615 errpos->cur_attno);
8616}
Datum arg
Definition elog.c:1323
#define errcontext
Definition elog.h:200
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition executor.h:708
#define castNode(_type_, nodeptr)
Definition nodes.h:180
NameData relname
Definition pg_class.h:40
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
#define list_nth_node(type, list, n)
Definition pg_list.h:359
#define RelationGetRelationName(relation)
Definition rel.h:550
Plan * plan
Definition execnodes.h:1202
EState * state
Definition execnodes.h:1204
PlanState ps
Definition execnodes.h:1662

References arg, attname, castNode, errcontext, exec_rt_fetch(), fb(), IsA, list_length(), list_nth(), list_nth_node, NameStr, TupleDescData::natts, PlanState::plan, ScanState::ps, RelationGetDescr, RelationGetRelationName, relname, 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 4340 of file postgres_fdw.c.

4344{
4345 const char **p_values;
4346 int i;
4347 int j;
4348 int pindex = 0;
4349 MemoryContext oldcontext;
4350
4351 oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4352
4353 p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4354
4355 /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4356 Assert(!(tupleid != NULL && numSlots > 1));
4357
4358 /* 1st parameter should be ctid, if it's in use */
4359 if (tupleid != NULL)
4360 {
4361 Assert(numSlots == 1);
4362 /* don't need set_transmission_modes for TID output */
4365 pindex++;
4366 }
4367
4368 /* get following parameters from slots */
4369 if (slots != NULL && fmstate->target_attrs != NIL)
4370 {
4371 TupleDesc tupdesc = RelationGetDescr(fmstate->rel);
4372 int nestlevel;
4373 ListCell *lc;
4374
4376
4377 for (i = 0; i < numSlots; i++)
4378 {
4379 j = (tupleid != NULL) ? 1 : 0;
4380 foreach(lc, fmstate->target_attrs)
4381 {
4382 int attnum = lfirst_int(lc);
4383 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
4384 Datum value;
4385 bool isnull;
4386
4387 /* Ignore generated columns; they are set to DEFAULT */
4388 if (attr->attgenerated)
4389 continue;
4390 value = slot_getattr(slots[i], attnum, &isnull);
4391 if (isnull)
4392 p_values[pindex] = NULL;
4393 else
4395 value);
4396 pindex++;
4397 j++;
4398 }
4399 }
4400
4402 }
4403
4404 Assert(pindex == fmstate->p_nums * numSlots);
4405
4406 MemoryContextSwitchTo(oldcontext);
4407
4408 return p_values;
4409}
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition fmgr.c:1684
static struct @175 value
void * palloc(Size size)
Definition mcxt.c:1390
#define lfirst_int(lc)
Definition pg_list.h:173
#define PointerGetDatum(X)
Definition postgres.h:354
void reset_transmission_modes(int nestlevel)
int set_transmission_modes(void)
bool attgenerated
Definition tupdesc.h:79
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:417

References Assert, CompactAttribute::attgenerated, attnum, fb(), i, j, lfirst_int, MemoryContextSwitchTo(), NIL, OutputFunctionCall(), palloc(), PointerGetDatum, RelationGetDescr, reset_transmission_modes(), set_transmission_modes(), slot_getattr(), TupleDescCompactAttr(), and value.

Referenced by execute_foreign_modify().

◆ create_cursor()

static void create_cursor ( ForeignScanState node)
static

Definition at line 3819 of file postgres_fdw.c.

3820{
3821 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3822 ExprContext *econtext = node->ss.ps.ps_ExprContext;
3823 int numParams = fsstate->numParams;
3824 const char **values = fsstate->param_values;
3825 PGconn *conn = fsstate->conn;
3827 PGresult *res;
3828
3829 /* First, process a pending asynchronous request, if any. */
3830 if (fsstate->conn_state->pendingAreq)
3831 process_pending_request(fsstate->conn_state->pendingAreq);
3832
3833 /*
3834 * Construct array of query parameter values in text format. We do the
3835 * conversions in the short-lived per-tuple context, so as not to cause a
3836 * memory leak over repeated scans.
3837 */
3838 if (numParams > 0)
3839 {
3840 MemoryContext oldcontext;
3841
3842 oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3843
3844 process_query_params(econtext,
3845 fsstate->param_flinfo,
3846 fsstate->param_exprs,
3847 values);
3848
3849 MemoryContextSwitchTo(oldcontext);
3850 }
3851
3852 /* Construct the DECLARE CURSOR command */
3854 appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3855 fsstate->cursor_number, fsstate->query);
3856
3857 /*
3858 * Notice that we pass NULL for paramTypes, thus forcing the remote server
3859 * to infer types for all parameters. Since we explicitly cast every
3860 * parameter (see deparse.c), the "inference" is trivial and will produce
3861 * the desired result. This allows us to avoid assuming that the remote
3862 * server has the same OIDs we do for the parameters' types.
3863 */
3864 if (!PQsendQueryParams(conn, buf.data, numParams,
3865 NULL, values, NULL, NULL, 0))
3867
3868 /*
3869 * Get the result, and check for success.
3870 */
3871 res = pgfdw_get_result(conn);
3872 if (PQresultStatus(res) != PGRES_COMMAND_OK)
3873 pgfdw_report_error(res, conn, fsstate->query);
3874 PQclear(res);
3875
3876 /* Mark the cursor as created, and show no tuples have been retrieved */
3877 fsstate->cursor_exists = true;
3878 fsstate->tuples = NULL;
3879 fsstate->num_tuples = 0;
3880 fsstate->next_tuple = 0;
3881 fsstate->fetch_ct_2 = 0;
3882 fsstate->eof_reached = false;
3883
3884 /* Clean up */
3885 pfree(buf.data);
3886}
PGresult * pgfdw_get_result(PGconn *conn)
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:1509
void pfree(void *pointer)
Definition mcxt.c:1619
static char buf[DEFAULT_XLOG_SEG_SIZE]
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
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:295
ExprContext * ps_ExprContext
Definition execnodes.h:1243

References appendStringInfo(), buf, PgFdwScanState::conn, conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, ExprContext::ecxt_per_tuple_memory, PgFdwScanState::eof_reached, fb(), ForeignScanState::fdw_state, PgFdwScanState::fetch_ct_2, 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 4051 of file postgres_fdw.c.

4061{
4063 Relation rel = resultRelInfo->ri_RelationDesc;
4064 TupleDesc tupdesc = RelationGetDescr(rel);
4065 Oid userid;
4069 Oid typefnoid;
4070 bool isvarlena;
4071 ListCell *lc;
4072
4073 /* Begin constructing PgFdwModifyState. */
4075 fmstate->rel = rel;
4076
4077 /* Identify which user to do the remote access as. */
4078 userid = ExecGetResultRelCheckAsUser(resultRelInfo, estate);
4079
4080 /* Get info about foreign table. */
4082 user = GetUserMapping(userid, table->serverid);
4083
4084 /* Open connection; report that we'll create a prepared statement. */
4085 fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
4086 fmstate->p_name = NULL; /* prepared statement not made yet */
4087
4088 /* Set up remote query information. */
4089 fmstate->query = query;
4090 if (operation == CMD_INSERT)
4091 {
4092 fmstate->query = pstrdup(fmstate->query);
4093 fmstate->orig_query = pstrdup(fmstate->query);
4094 }
4095 fmstate->target_attrs = target_attrs;
4096 fmstate->values_end = values_end;
4097 fmstate->has_returning = has_returning;
4098 fmstate->retrieved_attrs = retrieved_attrs;
4099
4100 /* Create context for per-tuple temp workspace. */
4101 fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
4102 "postgres_fdw temporary data",
4104
4105 /* Prepare for input conversion of RETURNING results. */
4106 if (fmstate->has_returning)
4107 fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
4108
4109 /* Prepare for output conversion of parameters used in prepared stmt. */
4110 n_params = list_length(fmstate->target_attrs) + 1;
4111 fmstate->p_flinfo = palloc0_array(FmgrInfo, n_params);
4112 fmstate->p_nums = 0;
4113
4115 {
4116 Assert(subplan != NULL);
4117
4118 /* Find the ctid resjunk column in the subplan's result */
4119 fmstate->ctidAttno = ExecFindJunkAttributeInTlist(subplan->targetlist,
4120 "ctid");
4121 if (!AttributeNumberIsValid(fmstate->ctidAttno))
4122 elog(ERROR, "could not find junk ctid column");
4123
4124 /* First transmittable parameter will be ctid */
4126 fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4127 fmstate->p_nums++;
4128 }
4129
4131 {
4132 /* Set up for remaining transmittable parameters */
4133 foreach(lc, fmstate->target_attrs)
4134 {
4135 int attnum = lfirst_int(lc);
4136 Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4137
4138 Assert(!attr->attisdropped);
4139
4140 /* Ignore generated columns; they are set to DEFAULT */
4141 if (attr->attgenerated)
4142 continue;
4143 getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
4144 fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4145 fmstate->p_nums++;
4146 }
4147 }
4148
4149 Assert(fmstate->p_nums <= n_params);
4150
4151 /* Set batch_size from foreign server/table options. */
4152 if (operation == CMD_INSERT)
4153 fmstate->batch_size = get_batch_size_option(rel);
4154
4155 fmstate->num_slots = 1;
4156
4157 /* Initialize auxiliary state */
4158 fmstate->aux_fmstate = NULL;
4159
4160 return fmstate;
4161}
#define AttributeNumberIsValid(attributeNumber)
Definition attnum.h:34
static DataChecksumsWorkerOperation operation
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition execJunk.c:222
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Oid ExecGetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate)
Definition execUtils.c:1515
#define palloc0_array(type, count)
Definition fe_memutils.h:92
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition fmgr.c:129
ForeignTable * GetForeignTable(Oid relid)
Definition foreign.c:286
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition foreign.c:232
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition lsyscache.c:3223
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
@ CMD_INSERT
Definition nodes.h:275
@ CMD_DELETE
Definition nodes.h:276
@ CMD_UPDATE
Definition nodes.h:274
static const struct lconv_member_info table[]
static char * user
Definition pg_regress.c:121
unsigned int Oid
static int get_batch_size_option(Relation rel)
PGconn * GetConnection(void)
Definition streamutil.c:60
MemoryContext es_query_cxt
Definition execnodes.h:747
List * targetlist
Definition plannodes.h:235
Relation ri_RelationDesc
Definition execnodes.h:514

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, Assert, attnum, AttributeNumberIsValid, CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, EState::es_query_cxt, ExecFindJunkAttributeInTlist(), ExecGetResultRelCheckAsUser(), fb(), fmgr_info(), get_batch_size_option(), GetConnection(), GetForeignTable(), getTypeOutputInfo(), GetUserMapping(), lfirst_int, list_length(), operation, palloc0_array, palloc0_object, pstrdup(), RelationGetDescr, RelationGetRelid, ResultRelInfo::ri_RelationDesc, table, Plan::targetlist, TupleDescAttr(), TupleDescGetAttInMetadata(), and user.

Referenced by postgresBeginForeignInsert(), and postgresBeginForeignModify().

◆ deallocate_query()

static void deallocate_query ( PgFdwModifyState fmstate)
static

Definition at line 4458 of file postgres_fdw.c.

4459{
4460 char sql[64];
4461 PGresult *res;
4462
4463 /* do nothing if the query is not allocated */
4464 if (!fmstate->p_name)
4465 return;
4466
4467 snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4468 res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4469 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4470 pgfdw_report_error(res, fmstate->conn, sql);
4471 PQclear(res);
4472 pfree(fmstate->p_name);
4473 fmstate->p_name = NULL;
4474}

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

3793{
3795 Expr *expr = em->em_expr;
3796
3797 /*
3798 * If we've identified what we're processing in the current scan, we only
3799 * want to match that expression.
3800 */
3801 if (state->current != NULL)
3802 return equal(expr, state->current);
3803
3804 /*
3805 * Otherwise, ignore anything we've already processed.
3806 */
3807 if (list_member(state->already_used, expr))
3808 return false;
3809
3810 /* This is the new target to process. */
3811 state->current = expr;
3812 return true;
3813}
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

References arg, equal(), fb(), 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 3183 of file postgres_fdw.c.

3191{
3192 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
3193 double rows;
3194 double retrieved_rows;
3195 int width;
3196 int disabled_nodes = 0;
3197 Cost startup_cost;
3198 Cost total_cost;
3199
3200 /* Make sure the core code has set up the relation's reltarget */
3201 Assert(foreignrel->reltarget);
3202
3203 /*
3204 * If the table or the server is configured to use remote estimates,
3205 * connect to the foreign server and execute EXPLAIN to estimate the
3206 * number of rows selected by the restriction+join clauses. Otherwise,
3207 * estimate rows using whatever statistics we have locally, in a way
3208 * similar to ordinary tables.
3209 */
3210 if (fpinfo->use_remote_estimate)
3211 {
3214 StringInfoData sql;
3215 PGconn *conn;
3218 List *fdw_scan_tlist = NIL;
3219 List *remote_conds;
3220
3221 /* Required only to be passed to deparseSelectStmtForRel */
3222 List *retrieved_attrs;
3223
3224 /*
3225 * param_join_conds might contain both clauses that are safe to send
3226 * across, and clauses that aren't.
3227 */
3230
3231 /* Build the list of columns to be fetched from the foreign server. */
3232 if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
3233 fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
3234 else
3235 fdw_scan_tlist = NIL;
3236
3237 /*
3238 * The complete list of remote conditions includes everything from
3239 * baserestrictinfo plus any extra join_conds relevant to this
3240 * particular path.
3241 */
3242 remote_conds = list_concat(remote_param_join_conds,
3243 fpinfo->remote_conds);
3244
3245 /*
3246 * Construct EXPLAIN query including the desired SELECT, FROM, and
3247 * WHERE clauses. Params and other-relation Vars are replaced by dummy
3248 * values, so don't request params_list.
3249 */
3250 initStringInfo(&sql);
3251 appendStringInfoString(&sql, "EXPLAIN ");
3252 deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
3253 remote_conds, pathkeys,
3254 fpextra ? fpextra->has_final_sort : false,
3255 fpextra ? fpextra->has_limit : false,
3256 false, &retrieved_attrs, NULL);
3257
3258 /* Get the remote estimate */
3259 conn = GetConnection(fpinfo->user, false, NULL);
3260 get_remote_estimate(sql.data, conn, &rows, &width,
3261 &startup_cost, &total_cost);
3263
3264 retrieved_rows = rows;
3265
3266 /* Factor in the selectivity of the locally-checked quals */
3269 foreignrel->relid,
3270 JOIN_INNER,
3271 NULL);
3272 local_sel *= fpinfo->local_conds_sel;
3273
3274 rows = clamp_row_est(rows * local_sel);
3275
3276 /* Add in the eval cost of the locally-checked quals */
3277 startup_cost += fpinfo->local_conds_cost.startup;
3278 total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3280 startup_cost += local_cost.startup;
3281 total_cost += local_cost.per_tuple * retrieved_rows;
3282
3283 /*
3284 * Add in tlist eval cost for each output row. In case of an
3285 * aggregate, some of the tlist expressions such as grouping
3286 * expressions will be evaluated remotely, so adjust the costs.
3287 */
3288 startup_cost += foreignrel->reltarget->cost.startup;
3289 total_cost += foreignrel->reltarget->cost.startup;
3290 total_cost += foreignrel->reltarget->cost.per_tuple * rows;
3291 if (IS_UPPER_REL(foreignrel))
3292 {
3294
3295 cost_qual_eval(&tlist_cost, fdw_scan_tlist, root);
3296 startup_cost -= tlist_cost.startup;
3297 total_cost -= tlist_cost.startup;
3298 total_cost -= tlist_cost.per_tuple * rows;
3299 }
3300 }
3301 else
3302 {
3303 Cost run_cost = 0;
3304
3305 /*
3306 * We don't support join conditions in this mode (hence, no
3307 * parameterized paths can be made).
3308 */
3310
3311 /*
3312 * We will come here again and again with different set of pathkeys or
3313 * additional post-scan/join-processing steps that caller wants to
3314 * cost. We don't need to calculate the cost/size estimates for the
3315 * underlying scan, join, or grouping each time. Instead, use those
3316 * estimates if we have cached them already.
3317 */
3318 if (fpinfo->rel_startup_cost >= 0 && fpinfo->rel_total_cost >= 0)
3319 {
3320 Assert(fpinfo->retrieved_rows >= 0);
3321
3322 rows = fpinfo->rows;
3323 retrieved_rows = fpinfo->retrieved_rows;
3324 width = fpinfo->width;
3325 startup_cost = fpinfo->rel_startup_cost;
3326 run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
3327
3328 /*
3329 * If we estimate the costs of a foreign scan or a foreign join
3330 * with additional post-scan/join-processing steps, the scan or
3331 * join costs obtained from the cache wouldn't yet contain the
3332 * eval costs for the final scan/join target, which would've been
3333 * updated by apply_scanjoin_target_to_paths(); add the eval costs
3334 * now.
3335 */
3336 if (fpextra && !IS_UPPER_REL(foreignrel))
3337 {
3338 /* Shouldn't get here unless we have LIMIT */
3339 Assert(fpextra->has_limit);
3340 Assert(foreignrel->reloptkind == RELOPT_BASEREL ||
3341 foreignrel->reloptkind == RELOPT_JOINREL);
3342 startup_cost += foreignrel->reltarget->cost.startup;
3343 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3344 }
3345 }
3346 else if (IS_JOIN_REL(foreignrel))
3347 {
3352 double nrows;
3353
3354 /* Use rows/width estimates made by the core code. */
3355 rows = foreignrel->rows;
3356 width = foreignrel->reltarget->width;
3357
3358 /* For join we expect inner and outer relations set */
3359 Assert(fpinfo->innerrel && fpinfo->outerrel);
3360
3361 fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
3362 fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
3363
3364 /* Estimate of number of rows in cross product */
3365 nrows = fpinfo_i->rows * fpinfo_o->rows;
3366
3367 /*
3368 * Back into an estimate of the number of retrieved rows. Just in
3369 * case this is nuts, clamp to at most nrows.
3370 */
3371 retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3372 retrieved_rows = Min(retrieved_rows, nrows);
3373
3374 /*
3375 * The cost of foreign join is estimated as cost of generating
3376 * rows for the joining relations + cost for applying quals on the
3377 * rows.
3378 */
3379
3380 /*
3381 * Calculate the cost of clauses pushed down to the foreign server
3382 */
3383 cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
3384 /* Calculate the cost of applying join clauses */
3385 cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
3386
3387 /*
3388 * Startup cost includes startup cost of joining relations and the
3389 * startup cost for join and other clauses. We do not include the
3390 * startup cost specific to join strategy (e.g. setting up hash
3391 * tables) since we do not know what strategy the foreign server
3392 * is going to use.
3393 */
3394 startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
3395 startup_cost += join_cost.startup;
3396 startup_cost += remote_conds_cost.startup;
3397 startup_cost += fpinfo->local_conds_cost.startup;
3398
3399 /*
3400 * Run time cost includes:
3401 *
3402 * 1. Run time cost (total_cost - startup_cost) of relations being
3403 * joined
3404 *
3405 * 2. Run time cost of applying join clauses on the cross product
3406 * of the joining relations.
3407 *
3408 * 3. Run time cost of applying pushed down other clauses on the
3409 * result of join
3410 *
3411 * 4. Run time cost of applying nonpushable other clauses locally
3412 * on the result fetched from the foreign server.
3413 */
3414 run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
3415 run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
3416 run_cost += nrows * join_cost.per_tuple;
3417 nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
3418 run_cost += nrows * remote_conds_cost.per_tuple;
3419 run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3420
3421 /* Add in tlist eval cost for each output row */
3422 startup_cost += foreignrel->reltarget->cost.startup;
3423 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3424 }
3425 else if (IS_UPPER_REL(foreignrel))
3426 {
3427 RelOptInfo *outerrel = fpinfo->outerrel;
3430 double input_rows;
3431 int numGroupCols;
3432 double numGroups = 1;
3433
3434 /* The upper relation should have its outer relation set */
3435 Assert(outerrel);
3436 /* and that outer relation should have its reltarget set */
3437 Assert(outerrel->reltarget);
3438
3439 /*
3440 * This cost model is mixture of costing done for sorted and
3441 * hashed aggregates in cost_agg(). We are not sure which
3442 * strategy will be considered at remote side, thus for
3443 * simplicity, we put all startup related costs in startup_cost
3444 * and all finalization and run cost are added in total_cost.
3445 */
3446
3447 ofpinfo = (PgFdwRelationInfo *) outerrel->fdw_private;
3448
3449 /* Get rows from input rel */
3450 input_rows = ofpinfo->rows;
3451
3452 /* Collect statistics about aggregates for estimating costs. */
3453 if (root->parse->hasAggs)
3454 {
3456 }
3457
3458 /* Get number of grouping columns and possible number of groups */
3459 numGroupCols = list_length(root->processed_groupClause);
3460 numGroups = estimate_num_groups(root,
3461 get_sortgrouplist_exprs(root->processed_groupClause,
3462 fpinfo->grouped_tlist),
3463 input_rows, NULL, NULL);
3464
3465 /*
3466 * Get the retrieved_rows and rows estimates. If there are HAVING
3467 * quals, account for their selectivity.
3468 */
3469 if (root->hasHavingQual)
3470 {
3471 /* Factor in the selectivity of the remotely-checked quals */
3472 retrieved_rows =
3473 clamp_row_est(numGroups *
3475 fpinfo->remote_conds,
3476 0,
3477 JOIN_INNER,
3478 NULL));
3479 /* Factor in the selectivity of the locally-checked quals */
3480 rows = clamp_row_est(retrieved_rows * fpinfo->local_conds_sel);
3481 }
3482 else
3483 {
3484 rows = retrieved_rows = numGroups;
3485 }
3486
3487 /* Use width estimate made by the core code. */
3488 width = foreignrel->reltarget->width;
3489
3490 /*-----
3491 * Startup cost includes:
3492 * 1. Startup cost for underneath input relation, adjusted for
3493 * tlist replacement by apply_scanjoin_target_to_paths()
3494 * 2. Cost of performing aggregation, per cost_agg()
3495 *-----
3496 */
3497 startup_cost = ofpinfo->rel_startup_cost;
3498 startup_cost += outerrel->reltarget->cost.startup;
3499 startup_cost += aggcosts.transCost.startup;
3500 startup_cost += aggcosts.transCost.per_tuple * input_rows;
3501 startup_cost += aggcosts.finalCost.startup;
3502 startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
3503
3504 /*-----
3505 * Run time cost includes:
3506 * 1. Run time cost of underneath input relation, adjusted for
3507 * tlist replacement by apply_scanjoin_target_to_paths()
3508 * 2. Run time cost of performing aggregation, per cost_agg()
3509 *-----
3510 */
3511 run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
3512 run_cost += outerrel->reltarget->cost.per_tuple * input_rows;
3513 run_cost += aggcosts.finalCost.per_tuple * numGroups;
3514 run_cost += cpu_tuple_cost * numGroups;
3515
3516 /* Account for the eval cost of HAVING quals, if any */
3517 if (root->hasHavingQual)
3518 {
3520
3521 /* Add in the eval cost of the remotely-checked quals */
3522 cost_qual_eval(&remote_cost, fpinfo->remote_conds, root);
3523 startup_cost += remote_cost.startup;
3524 run_cost += remote_cost.per_tuple * numGroups;
3525 /* Add in the eval cost of the locally-checked quals */
3526 startup_cost += fpinfo->local_conds_cost.startup;
3527 run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3528 }
3529
3530 /* Add in tlist eval cost for each output row */
3531 startup_cost += foreignrel->reltarget->cost.startup;
3532 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3533 }
3534 else
3535 {
3537
3538 /* Use rows/width estimates made by set_baserel_size_estimates. */
3539 rows = foreignrel->rows;
3540 width = foreignrel->reltarget->width;
3541
3542 /*
3543 * Back into an estimate of the number of retrieved rows. Just in
3544 * case this is nuts, clamp to at most foreignrel->tuples.
3545 */
3546 retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3547 retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
3548
3549 /*
3550 * Cost as though this were a seqscan, which is pessimistic. We
3551 * effectively imagine the local_conds are being evaluated
3552 * remotely, too.
3553 */
3554 startup_cost = 0;
3555 run_cost = 0;
3556 run_cost += seq_page_cost * foreignrel->pages;
3557
3558 startup_cost += foreignrel->baserestrictcost.startup;
3560 run_cost += cpu_per_tuple * foreignrel->tuples;
3561
3562 /* Add in tlist eval cost for each output row */
3563 startup_cost += foreignrel->reltarget->cost.startup;
3564 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3565 }
3566
3567 /*
3568 * Without remote estimates, we have no real way to estimate the cost
3569 * of generating sorted output. It could be free if the query plan
3570 * the remote side would have chosen generates properly-sorted output
3571 * anyway, but in most cases it will cost something. Estimate a value
3572 * high enough that we won't pick the sorted path when the ordering
3573 * isn't locally useful, but low enough that we'll err on the side of
3574 * pushing down the ORDER BY clause when it's useful to do so.
3575 */
3576 if (pathkeys != NIL)
3577 {
3578 if (IS_UPPER_REL(foreignrel))
3579 {
3580 Assert(foreignrel->reloptkind == RELOPT_UPPER_REL &&
3581 fpinfo->stage == UPPERREL_GROUP_AGG);
3582
3583 /*
3584 * We can only get here when this function is called from
3585 * add_foreign_ordered_paths() or add_foreign_final_paths();
3586 * in which cases, the passed-in fpextra should not be NULL.
3587 */
3588 Assert(fpextra);
3590 retrieved_rows, width,
3591 fpextra->limit_tuples,
3592 &disabled_nodes,
3593 &startup_cost, &run_cost);
3594 }
3595 else
3596 {
3597 startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3598 run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3599 }
3600 }
3601
3602 total_cost = startup_cost + run_cost;
3603
3604 /* Adjust the cost estimates if we have LIMIT */
3605 if (fpextra && fpextra->has_limit)
3606 {
3607 adjust_limit_rows_costs(&rows, &startup_cost, &total_cost,
3608 fpextra->offset_est, fpextra->count_est);
3609 retrieved_rows = rows;
3610 }
3611 }
3612
3613 /*
3614 * If this includes the final sort step, the given target, which will be
3615 * applied to the resulting path, might have different expressions from
3616 * the foreignrel's reltarget (see make_sort_input_target()); adjust tlist
3617 * eval costs.
3618 */
3619 if (fpextra && fpextra->has_final_sort &&
3620 fpextra->target != foreignrel->reltarget)
3621 {
3622 QualCost oldcost = foreignrel->reltarget->cost;
3623 QualCost newcost = fpextra->target->cost;
3624
3625 startup_cost += newcost.startup - oldcost.startup;
3626 total_cost += newcost.startup - oldcost.startup;
3627 total_cost += (newcost.per_tuple - oldcost.per_tuple) * rows;
3628 }
3629
3630 /*
3631 * Cache the retrieved rows and cost estimates for scans, joins, or
3632 * groupings without any parameterization, pathkeys, or additional
3633 * post-scan/join-processing steps, before adding the costs for
3634 * transferring data from the foreign server. These estimates are useful
3635 * for costing remote joins involving this relation or costing other
3636 * remote operations on this relation such as remote sorts and remote
3637 * LIMIT restrictions, when the costs can not be obtained from the foreign
3638 * server. This function will be called at least once for every foreign
3639 * relation without any parameterization, pathkeys, or additional
3640 * post-scan/join-processing steps.
3641 */
3642 if (pathkeys == NIL && param_join_conds == NIL && fpextra == NULL)
3643 {
3644 fpinfo->retrieved_rows = retrieved_rows;
3645 fpinfo->rel_startup_cost = startup_cost;
3646 fpinfo->rel_total_cost = total_cost;
3647 }
3648
3649 /*
3650 * Add some additional cost factors to account for connection overhead
3651 * (fdw_startup_cost), transferring data across the network
3652 * (fdw_tuple_cost per retrieved row), and local manipulation of the data
3653 * (cpu_tuple_cost per retrieved row).
3654 */
3655 startup_cost += fpinfo->fdw_startup_cost;
3656 total_cost += fpinfo->fdw_startup_cost;
3657 total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
3658 total_cost += cpu_tuple_cost * retrieved_rows;
3659
3660 /*
3661 * If we have LIMIT, we should prefer performing the restriction remotely
3662 * rather than locally, as the former avoids extra row fetches from the
3663 * remote that the latter might cause. But since the core code doesn't
3664 * account for such fetches when estimating the costs of the local
3665 * restriction (see create_limit_path()), there would be no difference
3666 * between the costs of the local restriction and the costs of the remote
3667 * restriction estimated above if we don't use remote estimates (except
3668 * for the case where the foreignrel is a grouping relation, the given
3669 * pathkeys is not NIL, and the effects of a bounded sort for that rel is
3670 * accounted for in costing the remote restriction). Tweak the costs of
3671 * the remote restriction to ensure we'll prefer it if LIMIT is a useful
3672 * one.
3673 */
3674 if (!fpinfo->use_remote_estimate &&
3675 fpextra && fpextra->has_limit &&
3676 fpextra->limit_tuples > 0 &&
3677 fpextra->limit_tuples < fpinfo->rows)
3678 {
3679 Assert(fpinfo->rows > 0);
3680 total_cost -= (total_cost - startup_cost) * 0.05 *
3681 (fpinfo->rows - fpextra->limit_tuples) / fpinfo->rows;
3682 }
3683
3684 /* Return results. */
3685 *p_rows = rows;
3686 *p_width = width;
3687 *p_disabled_nodes = disabled_nodes;
3688 *p_startup_cost = startup_cost;
3689 *p_total_cost = total_cost;
3690}
#define Min(x, y)
Definition c.h:1131
void ReleaseConnection(PGconn *conn)
double cpu_operator_cost
Definition costsize.c:135
double cpu_tuple_cost
Definition costsize.c:133
double seq_page_cost
Definition costsize.c:131
double clamp_row_est(double nrows)
Definition costsize.c:215
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:1286
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:1229
#define false
List * list_concat(List *list1, const List *list2)
Definition list.c:561
double Selectivity
Definition nodes.h:258
@ AGGSPLIT_SIMPLE
Definition nodes.h:385
void adjust_limit_rows_costs(double *rows, Cost *startup_cost, Cost *total_cost, int64 offset_est, int64 count_est)
Definition pathnode.c:3869
#define IS_JOIN_REL(rel)
Definition pathnodes.h:994
#define IS_UPPER_REL(rel)
Definition pathnodes.h:999
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:3804
QualCost cost
Definition pathnodes.h:1884
Cost per_tuple
Definition pathnodes.h:121
Cost startup
Definition pathnodes.h:120
Index relid
Definition pathnodes.h:1069
Cardinality tuples
Definition pathnodes.h:1096
BlockNumber pages
Definition pathnodes.h:1095
RelOptKind reloptkind
Definition pathnodes.h:1015
QualCost baserestrictcost
Definition pathnodes.h:1144
Cardinality rows
Definition pathnodes.h:1027
List * get_sortgrouplist_exprs(List *sgClauses, List *targetList)
Definition tlist.c:401

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(), cpu_operator_cost, cpu_tuple_cost, StringInfoData::data, DEFAULT_FDW_SORT_MULTIPLIER, deparseSelectStmtForRel(), estimate_num_groups(), fb(), get_agg_clause_costs(), get_remote_estimate(), get_sortgrouplist_exprs(), GetConnection(), initStringInfo(), IS_JOIN_REL, IS_UPPER_REL, JOIN_INNER, list_concat(), list_length(), Min, NIL, RelOptInfo::pages, QualCost::per_tuple, ReleaseConnection(), RelOptInfo::relid, RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_UPPER_REL, RelOptInfo::reloptkind, RelOptInfo::reltarget, root, RelOptInfo::rows, seq_page_cost, QualCost::startup, RelOptInfo::tuples, UPPERREL_GROUP_AGG, 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 4609 of file postgres_fdw.c.

4610{
4612 ExprContext *econtext = node->ss.ps.ps_ExprContext;
4613 int numParams = dmstate->numParams;
4614 const char **values = dmstate->param_values;
4615
4616 /* First, process a pending asynchronous request, if any. */
4617 if (dmstate->conn_state->pendingAreq)
4618 process_pending_request(dmstate->conn_state->pendingAreq);
4619
4620 /*
4621 * Construct array of query parameter values in text format.
4622 */
4623 if (numParams > 0)
4624 process_query_params(econtext,
4625 dmstate->param_flinfo,
4626 dmstate->param_exprs,
4627 values);
4628
4629 /*
4630 * Notice that we pass NULL for paramTypes, thus forcing the remote server
4631 * to infer types for all parameters. Since we explicitly cast every
4632 * parameter (see deparse.c), the "inference" is trivial and will produce
4633 * the desired result. This allows us to avoid assuming that the remote
4634 * server has the same OIDs we do for the parameters' types.
4635 */
4636 if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4637 NULL, values, NULL, NULL, 0))
4638 pgfdw_report_error(NULL, dmstate->conn, dmstate->query);
4639
4640 /*
4641 * Get the result, and check for success.
4642 */
4643 dmstate->result = pgfdw_get_result(dmstate->conn);
4644 if (PQresultStatus(dmstate->result) !=
4645 (dmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
4647 dmstate->query);
4648
4649 /*
4650 * The result potentially needs to survive across multiple executor row
4651 * cycles, so move it to the context where the dmstate is.
4652 */
4653 dmstate->result = libpqsrv_PGresultSetParent(dmstate->result,
4655
4656 /* Get the number of rows affected. */
4657 if (dmstate->has_returning)
4658 dmstate->num_tuples = PQntuples(dmstate->result);
4659 else
4660 dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4661}
uint32 result
static libpqsrv_PGresult * libpqsrv_PGresultSetParent(libpqsrv_PGresult *bres, MemoryContext ctx)
#define PQcmdTuples
#define PQntuples
@ PGRES_TUPLES_OK
Definition libpq-fe.h:134
MemoryContext GetMemoryChunkContext(void *pointer)
Definition mcxt.c:759

References fb(), ForeignScanState::fdw_state, GetMemoryChunkContext(), libpqsrv_PGresultSetParent(), 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, 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 4171 of file postgres_fdw.c.

4177{
4179 ItemPointer ctid = NULL;
4180 const char **p_values;
4181 PGresult *res;
4182 int n_rows;
4183 StringInfoData sql;
4184
4185 /* The operation should be INSERT, UPDATE, or DELETE */
4187 operation == CMD_UPDATE ||
4189
4190 /* First, process a pending asynchronous request, if any. */
4191 if (fmstate->conn_state->pendingAreq)
4192 process_pending_request(fmstate->conn_state->pendingAreq);
4193
4194 /*
4195 * If the existing query was deparsed and prepared for a different number
4196 * of rows, rebuild it for the proper number.
4197 */
4198 if (operation == CMD_INSERT && fmstate->num_slots != *numSlots)
4199 {
4200 /* Destroy the prepared statement created previously */
4201 if (fmstate->p_name)
4203
4204 /* Build INSERT string with numSlots records in its VALUES clause. */
4205 initStringInfo(&sql);
4206 rebuildInsertSql(&sql, fmstate->rel,
4207 fmstate->orig_query, fmstate->target_attrs,
4208 fmstate->values_end, fmstate->p_nums,
4209 *numSlots - 1);
4210 pfree(fmstate->query);
4211 fmstate->query = sql.data;
4212 fmstate->num_slots = *numSlots;
4213 }
4214
4215 /* Set up the prepared statement on the remote server, if we didn't yet */
4216 if (!fmstate->p_name)
4218
4219 /*
4220 * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
4221 */
4223 {
4224 Datum datum;
4225 bool isNull;
4226
4228 fmstate->ctidAttno,
4229 &isNull);
4230 /* shouldn't ever get a null result... */
4231 if (isNull)
4232 elog(ERROR, "ctid is NULL");
4233 ctid = (ItemPointer) DatumGetPointer(datum);
4234 }
4235
4236 /* Convert parameters needed by prepared statement to text form */
4238
4239 /*
4240 * Execute the prepared statement.
4241 */
4242 if (!PQsendQueryPrepared(fmstate->conn,
4243 fmstate->p_name,
4244 fmstate->p_nums * (*numSlots),
4245 p_values,
4246 NULL,
4247 NULL,
4248 0))
4249 pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
4250
4251 /*
4252 * Get the result, and check for success.
4253 */
4254 res = pgfdw_get_result(fmstate->conn);
4255 if (PQresultStatus(res) !=
4256 (fmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
4257 pgfdw_report_error(res, fmstate->conn, fmstate->query);
4258
4259 /* Check number of rows affected, and fetch RETURNING tuple if any */
4260 if (fmstate->has_returning)
4261 {
4262 Assert(*numSlots == 1);
4263 n_rows = PQntuples(res);
4264 if (n_rows > 0)
4265 store_returning_result(fmstate, slots[0], res);
4266 }
4267 else
4268 n_rows = atoi(PQcmdTuples(res));
4269
4270 /* And clean up */
4271 PQclear(res);
4272
4273 MemoryContextReset(fmstate->temp_cxt);
4274
4275 *numSlots = n_rows;
4276
4277 /*
4278 * Return NULL if nothing was inserted/updated/deleted on the remote end
4279 */
4280 return (n_rows > 0) ? slots : NULL;
4281}
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:2207
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition executor.h:226
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:1650
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
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:570

References Assert, CMD_DELETE, CMD_INSERT, CMD_UPDATE, convert_prep_stmt_params(), StringInfoData::data, DatumGetPointer(), deallocate_query(), elog, ERROR, ExecGetJunkAttribute(), fb(), initStringInfo(), MemoryContextReset(), operation, pfree(), pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear, PQcmdTuples, PQntuples, PQresultStatus, PQsendQueryPrepared(), prepare_foreign_modify(), process_pending_request(), rebuildInsertSql(), ResultRelInfo::ri_FdwState, and store_returning_result().

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

◆ fetch_attstats()

static PGresult * fetch_attstats ( PGconn conn,
int  server_version_num,
const char remote_schemaname,
const char remote_relname,
const char column_list 
)
static

Definition at line 5733 of file postgres_fdw.c.

5736{
5737 StringInfoData sql;
5738 PGresult *res;
5739
5740 initStringInfo(&sql);
5742 "SELECT DISTINCT ON (attname COLLATE \"C\") attname,"
5743 " null_frac,"
5744 " avg_width,"
5745 " n_distinct,"
5746 " most_common_vals,"
5747 " most_common_freqs,"
5748 " histogram_bounds,"
5749 " correlation,");
5750
5751 /* Elements stats are supported since Postgres 9.2 */
5752 if (server_version_num >= 92000)
5754 " most_common_elems,"
5755 " most_common_elem_freqs,"
5756 " elem_count_histogram,");
5757 else
5759 " NULL, NULL, NULL,");
5760
5761 /* Range stats are supported since Postgres 17 */
5762 if (server_version_num >= 170000)
5764 " range_length_histogram,"
5765 " range_empty_frac,"
5766 " range_bounds_histogram");
5767 else
5769 " NULL, NULL, NULL");
5770
5772 " FROM pg_catalog.pg_stats"
5773 " WHERE schemaname = ");
5776 " AND tablename = ");
5778 appendStringInfo(&sql,
5779 " AND attname = ANY(%s)",
5780 column_list);
5781
5782 /* inherited is supported since Postgres 9.0 */
5783 if (server_version_num >= 90000)
5785 " ORDER BY attname COLLATE \"C\", inherited DESC");
5786 else
5788 " ORDER BY attname COLLATE \"C\"");
5789
5790 res = pgfdw_exec_query(conn, sql.data, NULL);
5791 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5792 pgfdw_report_error(res, conn, sql.data);
5793
5794 if (PQnfields(res) != ATTSTATS_NUM_FIELDS)
5795 elog(ERROR, "unexpected result from fetch_attstats query");
5796
5797 return res;
5798}
static int server_version_num
Definition guc_tables.c:624
#define PQnfields

References appendStringInfo(), appendStringInfoString(), ATTSTATS_NUM_FIELDS, conn, StringInfoData::data, deparseStringLiteral(), elog, ERROR, fb(), initStringInfo(), pgfdw_exec_query(), pgfdw_report_error(), PGRES_TUPLES_OK, PQnfields, PQresultStatus, and server_version_num.

Referenced by fetch_remote_statistics().

◆ fetch_more_data()

static void fetch_more_data ( ForeignScanState node)
static

Definition at line 3892 of file postgres_fdw.c.

3893{
3894 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3895 PGconn *conn = fsstate->conn;
3896 PGresult *res;
3897 int numrows;
3898 int i;
3899 MemoryContext oldcontext;
3900
3901 /*
3902 * We'll store the tuples in the batch_cxt. First, flush the previous
3903 * batch.
3904 */
3905 fsstate->tuples = NULL;
3906 MemoryContextReset(fsstate->batch_cxt);
3907 oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3908
3909 if (fsstate->async_capable)
3910 {
3911 Assert(fsstate->conn_state->pendingAreq);
3912
3913 /*
3914 * The query was already sent by an earlier call to
3915 * fetch_more_data_begin. So now we just fetch the result.
3916 */
3917 res = pgfdw_get_result(conn);
3918 /* On error, report the original query, not the FETCH. */
3919 if (PQresultStatus(res) != PGRES_TUPLES_OK)
3920 pgfdw_report_error(res, conn, fsstate->query);
3921
3922 /* Reset per-connection state */
3923 fsstate->conn_state->pendingAreq = NULL;
3924 }
3925 else
3926 {
3927 char sql[64];
3928
3929 /* This is a regular synchronous fetch. */
3930 snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3931 fsstate->fetch_size, fsstate->cursor_number);
3932
3933 res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
3934 /* On error, report the original query, not the FETCH. */
3935 if (PQresultStatus(res) != PGRES_TUPLES_OK)
3936 pgfdw_report_error(res, conn, fsstate->query);
3937 }
3938
3939 /* Convert the data into HeapTuples */
3940 numrows = PQntuples(res);
3941 fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3942 fsstate->num_tuples = numrows;
3943 fsstate->next_tuple = 0;
3944
3945 for (i = 0; i < numrows; i++)
3946 {
3947 Assert(IsA(node->ss.ps.plan, ForeignScan));
3948
3949 fsstate->tuples[i] =
3951 fsstate->rel,
3952 fsstate->attinmeta,
3953 fsstate->retrieved_attrs,
3954 node,
3955 fsstate->temp_cxt);
3956 }
3957
3958 /* Update fetch_ct_2 */
3959 if (fsstate->fetch_ct_2 < 2)
3960 fsstate->fetch_ct_2++;
3961
3962 /* Must be EOF if we didn't get as many tuples as we asked for. */
3963 fsstate->eof_reached = (numrows < fsstate->fetch_size);
3964
3965 PQclear(res);
3966
3967 MemoryContextSwitchTo(oldcontext);
3968}
void * palloc0(Size size)
Definition mcxt.c:1420

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

8278{
8279 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8280 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8281 char sql[64];
8282
8283 Assert(!fsstate->conn_state->pendingAreq);
8284
8285 /* Create the cursor synchronously. */
8286 if (!fsstate->cursor_exists)
8287 create_cursor(node);
8288
8289 /* We will send this query, but not wait for the response. */
8290 snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
8291 fsstate->fetch_size, fsstate->cursor_number);
8292
8293 if (!PQsendQuery(fsstate->conn, sql))
8294 pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
8295
8296 /* Remember that the request is in process */
8297 fsstate->conn_state->pendingAreq = areq;
8298}
int PQsendQuery(PGconn *conn, const char *query)
Definition fe-exec.c:1433
static void create_cursor(ForeignScanState *node)
AsyncRequest * pendingAreq
unsigned int cursor_number
PgFdwConnState * conn_state

References Assert, PgFdwScanState::conn, PgFdwScanState::conn_state, create_cursor(), PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, fb(), ForeignScanState::fdw_state, PgFdwScanState::fetch_size, PgFdwConnState::pendingAreq, pgfdw_report_error(), PQsendQuery(), PgFdwScanState::query, and snprintf.

Referenced by postgresForeignAsyncConfigureWait(), and produce_tuple_asynchronously().

◆ fetch_relstats()

static PGresult * fetch_relstats ( PGconn conn,
Relation  relation 
)
static

Definition at line 5711 of file postgres_fdw.c.

5712{
5713 StringInfoData sql;
5714 PGresult *res;
5715
5716 initStringInfo(&sql);
5717 deparseAnalyzeInfoSql(&sql, relation);
5718
5719 res = pgfdw_exec_query(conn, sql.data, NULL);
5720 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5721 pgfdw_report_error(res, conn, sql.data);
5722
5723 if (PQntuples(res) != 1 || PQnfields(res) != RELSTATS_NUM_FIELDS)
5724 elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
5725
5726 return res;
5727}
void deparseAnalyzeInfoSql(StringInfo buf, Relation rel)
Definition deparse.c:2572

References conn, StringInfoData::data, deparseAnalyzeInfoSql(), elog, ERROR, fb(), initStringInfo(), pgfdw_exec_query(), pgfdw_report_error(), PGRES_TUPLES_OK, PQnfields, PQntuples, PQresultStatus, and RELSTATS_NUM_FIELDS.

Referenced by fetch_remote_statistics().

◆ fetch_remote_statistics()

static bool fetch_remote_statistics ( Relation  relation,
List va_cols,
ForeignTable table,
const char local_schemaname,
const char local_relname,
int p_attrcnt,
RemoteAttributeMapping **  p_remattrmap,
RemoteStatsResults remstats 
)
static

Definition at line 5570 of file postgres_fdw.c.

5578{
5579 const char *remote_schemaname = NULL;
5580 const char *remote_relname = NULL;
5582 PGconn *conn;
5587 int attrcnt = 0;
5588 char relkind;
5589 double reltuples;
5590 bool ok = false;
5591 ListCell *lc;
5592
5593 /*
5594 * Assume the remote schema/relation names are the same as the local name
5595 * unless the foreign table's options tell us otherwise.
5596 */
5599 foreach(lc, table->options)
5600 {
5601 DefElem *def = (DefElem *) lfirst(lc);
5602
5603 if (strcmp(def->defname, "schema_name") == 0)
5605 else if (strcmp(def->defname, "table_name") == 0)
5607 }
5608
5609 /*
5610 * Get connection to the foreign server. Connection manager will
5611 * establish new connection if necessary.
5612 */
5613 user = GetUserMapping(GetUserId(), table->serverid);
5614 conn = GetConnection(user, false, NULL);
5616
5617 /* Fetch relation stats. */
5618 remstats->rel = relstats = fetch_relstats(conn, relation);
5619
5620 /*
5621 * Verify that the remote table is the sort that can have meaningful stats
5622 * in pg_stats.
5623 *
5624 * Note that while relations of kinds RELKIND_INDEX and
5625 * RELKIND_PARTITIONED_INDEX can have rows in pg_stats, they obviously
5626 * can't support a foreign table.
5627 */
5628 relkind = *PQgetvalue(relstats, 0, RELSTATS_RELKIND);
5629 switch (relkind)
5630 {
5631 case RELKIND_RELATION:
5633 case RELKIND_MATVIEW:
5635 break;
5636 default:
5638 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" is of relkind \"%c\" which cannot have statistics",
5641 goto fetch_cleanup;
5642 }
5643
5644 /*
5645 * If the reltuples value > 0, then then we can expect to find attribute
5646 * stats for the remote table.
5647 *
5648 * In v14 or latter, if a reltuples value is -1, it means the table has
5649 * never been analyzed, so we wouldn't expect to find the stats for the
5650 * table; fallback to sampling in that case. If the value is 0, it means
5651 * it was empty; in which case skip the stats and import relation stats
5652 * only.
5653 *
5654 * In versions prior to v14, a value of 0 was ambiguous; it could mean
5655 * that the table had never been analyzed, or that it was empty. Either
5656 * way, we wouldn't expect to find the stats for the table, so we fallback
5657 * to sampling.
5658 */
5660 if (((server_version_num < 140000) && (reltuples == 0)) ||
5661 ((server_version_num >= 140000) && (reltuples == -1)))
5662 {
5664 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" has no relation statistics to import",
5667 goto fetch_cleanup;
5668 }
5669
5670 if (reltuples > 0)
5671 {
5673
5674 *p_remattrmap = remattrmap = build_remattrmap(relation, va_cols,
5675 &attrcnt, &column_list);
5676 *p_attrcnt = attrcnt;
5677
5678 if (attrcnt > 0)
5679 {
5680 /* Fetch attribute stats. */
5685 column_list.data);
5686
5687 /* If any attribute stats are missing, fallback to sampling. */
5692 goto fetch_cleanup;
5693 }
5694 }
5695
5696 /* We assume that we have no dead tuple. */
5697 remstats->deadtuples = 0.0;
5698 remstats->livetuples = reltuples;
5699
5700 ok = true;
5701
5704 return ok;
5705}
#define WARNING
Definition elog.h:37
#define ereport(elevel,...)
Definition elog.h:152
int PQserverVersion(const PGconn *conn)
#define PQgetvalue
Oid GetUserId(void)
Definition miscinit.c:470
static char * errmsg
static RemoteAttributeMapping * build_remattrmap(Relation relation, List *va_cols, int *p_attrcnt, StringInfo column_list)
static PGresult * fetch_attstats(PGconn *conn, int server_version_num, const char *remote_schemaname, const char *remote_relname, const char *column_list)
static PGresult * fetch_relstats(PGconn *conn, Relation relation)
static bool match_attrmap(PGresult *res, const char *local_schemaname, const char *local_relname, const char *remote_schemaname, const char *remote_relname, int attrcnt, RemoteAttributeMapping *remattrmap)

References build_remattrmap(), conn, defGetString(), DefElem::defname, ereport, errmsg, fb(), fetch_attstats(), fetch_relstats(), GetConnection(), GetUserId(), GetUserMapping(), lfirst, match_attrmap(), PQgetvalue, PQserverVersion(), ReleaseConnection(), RELSTATS_RELKIND, RELSTATS_RELTUPLES, server_version_num, table, user, and WARNING.

Referenced by postgresImportForeignStatistics().

◆ find_em_for_rel()

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

Definition at line 8631 of file postgres_fdw.c.

8632{
8633 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
8636
8638 while ((em = eclass_member_iterator_next(&it)) != NULL)
8639 {
8640 /*
8641 * Note we require !bms_is_empty, else we'd accept constant
8642 * expressions which are not suitable for the purpose.
8643 */
8644 if (bms_is_subset(em->em_relids, rel->relids) &&
8645 !bms_is_empty(em->em_relids) &&
8646 bms_is_empty(bms_intersect(em->em_relids, fpinfo->hidden_subquery_rels)) &&
8647 is_foreign_expr(root, rel, em->em_expr))
8648 return em;
8649 }
8650
8651 return NULL;
8652}
Bitmapset * bms_intersect(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:293
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:547
#define bms_is_empty(a)
Definition bitmapset.h:119
void setup_eclass_member_iterator(EquivalenceMemberIterator *it, EquivalenceClass *ec, Relids child_relids)
EquivalenceMember * eclass_member_iterator_next(EquivalenceMemberIterator *it)
Relids relids
Definition pathnodes.h:1021

References bms_intersect(), bms_is_empty, bms_is_subset(), eclass_member_iterator_next(), fb(), 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 8666 of file postgres_fdw.c.

8668{
8669 PathTarget *target = rel->reltarget;
8670 ListCell *lc1;
8671 int i;
8672
8673 i = 0;
8674 foreach(lc1, target->exprs)
8675 {
8676 Expr *expr = (Expr *) lfirst(lc1);
8678 ListCell *lc2;
8679
8680 /* Ignore non-sort expressions */
8681 if (sgref == 0 ||
8683 root->parse->sortClause) == NULL)
8684 {
8685 i++;
8686 continue;
8687 }
8688
8689 /* We ignore binary-compatible relabeling on both ends */
8690 while (expr && IsA(expr, RelabelType))
8691 expr = ((RelabelType *) expr)->arg;
8692
8693 /*
8694 * Locate an EquivalenceClass member matching this expr, if any.
8695 * Ignore child members.
8696 */
8697 foreach(lc2, ec->ec_members)
8698 {
8700 Expr *em_expr;
8701
8702 /* Don't match constants */
8703 if (em->em_is_const)
8704 continue;
8705
8706 /* Child members should not exist in ec_members */
8707 Assert(!em->em_is_child);
8708
8709 /* Match if same expression (after stripping relabel) */
8710 em_expr = em->em_expr;
8711 while (em_expr && IsA(em_expr, RelabelType))
8712 em_expr = ((RelabelType *) em_expr)->arg;
8713
8714 if (!equal(em_expr, expr))
8715 continue;
8716
8717 /* Check that expression (including relabels!) is shippable */
8718 if (is_foreign_expr(root, rel, em->em_expr))
8719 return em;
8720 }
8721
8722 i++;
8723 }
8724
8725 return NULL;
8726}
unsigned int Index
Definition c.h:757
#define get_pathtarget_sortgroupref(target, colno)
Definition pathnodes.h:1894
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition tlist.c:452

References Assert, EquivalenceClass::ec_members, equal(), PathTarget::exprs, fb(), 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 2482 of file postgres_fdw.c.

2486{
2487 Plan *subplan = outerPlan(plan);
2488
2489 /*
2490 * The cases we support are (1) the desired ForeignScan is the immediate
2491 * child of ModifyTable, or (2) it is the subplan_index'th child of an
2492 * Append node that is the immediate child of ModifyTable. There is no
2493 * point in looking further down, as that would mean that local joins are
2494 * involved, so we can't do the update directly.
2495 *
2496 * There could be a Result atop the Append too, acting to compute the
2497 * UPDATE targetlist values. We ignore that here; the tlist will be
2498 * checked by our caller.
2499 *
2500 * In principle we could examine all the children of the Append, but it's
2501 * currently unlikely that the core planner would generate such a plan
2502 * with the children out-of-order. Moreover, such a search risks costing
2503 * O(N^2) time when there are a lot of children.
2504 */
2505 if (IsA(subplan, Append))
2506 {
2507 Append *appendplan = (Append *) subplan;
2508
2509 if (subplan_index < list_length(appendplan->appendplans))
2510 subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2511 }
2512 else if (IsA(subplan, Result) &&
2513 outerPlan(subplan) != NULL &&
2514 IsA(outerPlan(subplan), Append))
2515 {
2516 Append *appendplan = (Append *) outerPlan(subplan);
2517
2518 if (subplan_index < list_length(appendplan->appendplans))
2519 subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2520 }
2521
2522 /* Now, have we got a ForeignScan on the desired rel? */
2523 if (IsA(subplan, ForeignScan))
2524 {
2525 ForeignScan *fscan = (ForeignScan *) subplan;
2526
2527 if (bms_is_member(rtindex, fscan->fs_base_relids))
2528 return fscan;
2529 }
2530
2531 return NULL;
2532}
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:645
#define plan(x)
Definition pg_regress.c:164
#define outerPlan(node)
Definition plannodes.h:267

References bms_is_member(), fb(), 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 4440 of file postgres_fdw.c.

4441{
4442 Assert(fmstate != NULL);
4443
4444 /* If we created a prepared statement, destroy it */
4446
4447 /* Release remote connection */
4449 fmstate->conn = NULL;
4450}

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

7290{
7291 Query *query = root->parse;
7292 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
7293 PathTarget *grouping_target = grouped_rel->reltarget;
7295 ListCell *lc;
7296 int i;
7297 List *tlist = NIL;
7298
7299 /* We currently don't support pushing Grouping Sets. */
7300 if (query->groupingSets)
7301 return false;
7302
7303 /* Get the fpinfo of the underlying scan relation. */
7304 ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
7305
7306 /*
7307 * If underlying scan relation has any local conditions, those conditions
7308 * are required to be applied before performing aggregation. Hence the
7309 * aggregate cannot be pushed down.
7310 */
7311 if (ofpinfo->local_conds)
7312 return false;
7313
7314 /*
7315 * Examine grouping expressions, as well as other expressions we'd need to
7316 * compute, and check whether they are safe to push down to the foreign
7317 * server. All GROUP BY expressions will be part of the grouping target
7318 * and thus there is no need to search for them separately. Add grouping
7319 * expressions into target list which will be passed to foreign server.
7320 *
7321 * A tricky fine point is that we must not put any expression into the
7322 * target list that is just a foreign param (that is, something that
7323 * deparse.c would conclude has to be sent to the foreign server). If we
7324 * do, the expression will also appear in the fdw_exprs list of the plan
7325 * node, and setrefs.c will get confused and decide that the fdw_exprs
7326 * entry is actually a reference to the fdw_scan_tlist entry, resulting in
7327 * a broken plan. Somewhat oddly, it's OK if the expression contains such
7328 * a node, as long as it's not at top level; then no match is possible.
7329 */
7330 i = 0;
7331 foreach(lc, grouping_target->exprs)
7332 {
7333 Expr *expr = (Expr *) lfirst(lc);
7335 ListCell *l;
7336
7337 /*
7338 * Check whether this expression is part of GROUP BY clause. Note we
7339 * check the whole GROUP BY clause not just processed_groupClause,
7340 * because we will ship all of it, cf. appendGroupByClause.
7341 */
7342 if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
7343 {
7345
7346 /*
7347 * If any GROUP BY expression is not shippable, then we cannot
7348 * push down aggregation to the foreign server.
7349 */
7350 if (!is_foreign_expr(root, grouped_rel, expr))
7351 return false;
7352
7353 /*
7354 * If it would be a foreign param, we can't put it into the tlist,
7355 * so we have to fail.
7356 */
7357 if (is_foreign_param(root, grouped_rel, expr))
7358 return false;
7359
7360 /*
7361 * Pushable, so add to tlist. We need to create a TLE for this
7362 * expression and apply the sortgroupref to it. We cannot use
7363 * add_to_flat_tlist() here because that avoids making duplicate
7364 * entries in the tlist. If there are duplicate entries with
7365 * distinct sortgrouprefs, we have to duplicate that situation in
7366 * the output tlist.
7367 */
7368 tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
7369 tle->ressortgroupref = sgref;
7370 tlist = lappend(tlist, tle);
7371 }
7372 else
7373 {
7374 /*
7375 * Non-grouping expression we need to compute. Can we ship it
7376 * as-is to the foreign server?
7377 */
7378 if (is_foreign_expr(root, grouped_rel, expr) &&
7379 !is_foreign_param(root, grouped_rel, expr))
7380 {
7381 /* Yes, so add to tlist as-is; OK to suppress duplicates */
7382 tlist = add_to_flat_tlist(tlist, list_make1(expr));
7383 }
7384 else
7385 {
7386 /* Not pushable as a whole; extract its Vars and aggregates */
7387 List *aggvars;
7388
7389 aggvars = pull_var_clause((Node *) expr,
7391
7392 /*
7393 * If any aggregate expression is not shippable, then we
7394 * cannot push down aggregation to the foreign server. (We
7395 * don't have to check is_foreign_param, since that certainly
7396 * won't return true for any such expression.)
7397 */
7398 if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
7399 return false;
7400
7401 /*
7402 * Add aggregates, if any, into the targetlist. Plain Vars
7403 * outside an aggregate can be ignored, because they should be
7404 * either same as some GROUP BY column or part of some GROUP
7405 * BY expression. In either case, they are already part of
7406 * the targetlist and thus no need to add them again. In fact
7407 * including plain Vars in the tlist when they do not match a
7408 * GROUP BY column would cause the foreign server to complain
7409 * that the shipped query is invalid.
7410 */
7411 foreach(l, aggvars)
7412 {
7413 Expr *aggref = (Expr *) lfirst(l);
7414
7415 if (IsA(aggref, Aggref))
7416 tlist = add_to_flat_tlist(tlist, list_make1(aggref));
7417 }
7418 }
7419 }
7420
7421 i++;
7422 }
7423
7424 /*
7425 * Classify the pushable and non-pushable HAVING clauses and save them in
7426 * remote_conds and local_conds of the grouped rel's fpinfo.
7427 */
7428 if (havingQual)
7429 {
7430 foreach(lc, (List *) havingQual)
7431 {
7432 Expr *expr = (Expr *) lfirst(lc);
7433 RestrictInfo *rinfo;
7434
7435 /*
7436 * Currently, the core code doesn't wrap havingQuals in
7437 * RestrictInfos, so we must make our own.
7438 */
7439 Assert(!IsA(expr, RestrictInfo));
7440 rinfo = make_restrictinfo(root,
7441 expr,
7442 true,
7443 false,
7444 false,
7445 false,
7446 root->qual_security_level,
7447 grouped_rel->relids,
7448 NULL,
7449 NULL);
7450 if (is_foreign_expr(root, grouped_rel, expr))
7451 fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
7452 else
7453 fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
7454 }
7455 }
7456
7457 /*
7458 * If there are any local conditions, pull Vars and aggregates from it and
7459 * check whether they are safe to pushdown or not.
7460 */
7461 if (fpinfo->local_conds)
7462 {
7463 List *aggvars = NIL;
7464
7465 foreach(lc, fpinfo->local_conds)
7466 {
7468
7470 pull_var_clause((Node *) rinfo->clause,
7472 }
7473
7474 foreach(lc, aggvars)
7475 {
7476 Expr *expr = (Expr *) lfirst(lc);
7477
7478 /*
7479 * If aggregates within local conditions are not safe to push
7480 * down, then we cannot push down the query. Vars are already
7481 * part of GROUP BY clause which are checked above, so no need to
7482 * access them again here. Again, we need not check
7483 * is_foreign_param for a foreign aggregate.
7484 */
7485 if (IsA(expr, Aggref))
7486 {
7487 if (!is_foreign_expr(root, grouped_rel, expr))
7488 return false;
7489
7490 tlist = add_to_flat_tlist(tlist, list_make1(expr));
7491 }
7492 }
7493 }
7494
7495 /* Store generated targetlist */
7496 fpinfo->grouped_tlist = tlist;
7497
7498 /* Safe to pushdown */
7499 fpinfo->pushdown_safe = true;
7500
7501 /*
7502 * Set # of retrieved rows and cached relation costs to some negative
7503 * value, so that we can detect when they are set to some sensible values,
7504 * during one (usually the first) of the calls to estimate_path_cost_size.
7505 */
7506 fpinfo->retrieved_rows = -1;
7507 fpinfo->rel_startup_cost = -1;
7508 fpinfo->rel_total_cost = -1;
7509
7510 /*
7511 * Set the string describing this grouped relation to be used in EXPLAIN
7512 * output of corresponding ForeignScan. Note that the decoration we add
7513 * to the base relation name mustn't include any digits, or it'll confuse
7514 * postgresExplainForeignScan.
7515 */
7516 fpinfo->relation_name = psprintf("Aggregate on (%s)",
7517 ofpinfo->relation_name);
7518
7519 return true;
7520}
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition deparse.c:1135
#define PVC_INCLUDE_AGGREGATES
Definition optimizer.h:197
#define list_make1(x1)
Definition pg_list.h:244
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)
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition tlist.c:141

References add_to_flat_tlist(), Assert, RestrictInfo::clause, fb(), get_pathtarget_sortgroupref, get_sortgroupref_clause_noerr(), Query::groupClause, Query::groupingSets, i, is_foreign_expr(), is_foreign_param(), IsA, lappend(), lfirst, lfirst_node, list_concat(), list_length(), list_make1, make_restrictinfo(), makeTargetEntry(), NIL, psprintf(), pull_var_clause(), PVC_INCLUDE_AGGREGATES, RelOptInfo::relids, RelOptInfo::reltarget, 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 6579 of file postgres_fdw.c.

6582{
6586 ListCell *lc;
6587 List *joinclauses;
6588
6589 /*
6590 * We support pushing down INNER, LEFT, RIGHT, FULL OUTER and SEMI joins.
6591 * Constructing queries representing ANTI joins is hard, hence not
6592 * considered right now.
6593 */
6594 if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
6595 jointype != JOIN_RIGHT && jointype != JOIN_FULL &&
6596 jointype != JOIN_SEMI)
6597 return false;
6598
6599 /*
6600 * We can't push down semi-join if its reltarget is not safe
6601 */
6602 if ((jointype == JOIN_SEMI) && !semijoin_target_ok(root, joinrel, outerrel, innerrel))
6603 return false;
6604
6605 /*
6606 * If either of the joining relations is marked as unsafe to pushdown, the
6607 * join can not be pushed down.
6608 */
6609 fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
6610 fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
6611 fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
6612 if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
6613 !fpinfo_i || !fpinfo_i->pushdown_safe)
6614 return false;
6615
6616 /*
6617 * If joining relations have local conditions, those conditions are
6618 * required to be applied before joining the relations. Hence the join can
6619 * not be pushed down.
6620 */
6621 if (fpinfo_o->local_conds || fpinfo_i->local_conds)
6622 return false;
6623
6624 /*
6625 * Merge FDW options. We might be tempted to do this after we have deemed
6626 * the foreign join to be OK. But we must do this beforehand so that we
6627 * know which quals can be evaluated on the foreign server, which might
6628 * depend on shippable_extensions.
6629 */
6630 fpinfo->server = fpinfo_o->server;
6632
6633 /*
6634 * Separate restrict list into join quals and pushed-down (other) quals.
6635 *
6636 * Join quals belonging to an outer join must all be shippable, else we
6637 * cannot execute the join remotely. Add such quals to 'joinclauses'.
6638 *
6639 * Add other quals to fpinfo->remote_conds if they are shippable, else to
6640 * fpinfo->local_conds. In an inner join it's okay to execute conditions
6641 * either locally or remotely; the same is true for pushed-down conditions
6642 * at an outer join.
6643 *
6644 * Note we might return failure after having already scribbled on
6645 * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
6646 * won't consult those lists again if we deem the join unshippable.
6647 */
6648 joinclauses = NIL;
6649 foreach(lc, extra->restrictlist)
6650 {
6652 bool is_remote_clause = is_foreign_expr(root, joinrel,
6653 rinfo->clause);
6654
6655 if (IS_OUTER_JOIN(jointype) &&
6656 !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
6657 {
6658 if (!is_remote_clause)
6659 return false;
6660 joinclauses = lappend(joinclauses, rinfo);
6661 }
6662 else
6663 {
6664 if (is_remote_clause)
6665 fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
6666 else
6667 fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
6668 }
6669 }
6670
6671 /*
6672 * deparseExplicitTargetList() isn't smart enough to handle anything other
6673 * than a Var. In particular, if there's some PlaceHolderVar that would
6674 * need to be evaluated within this join tree (because there's an upper
6675 * reference to a quantity that may go to NULL as a result of an outer
6676 * join), then we can't try to push the join down because we'll fail when
6677 * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
6678 * needs to be evaluated *at the top* of this join tree is OK, because we
6679 * can do that locally after fetching the results from the remote side.
6680 */
6681 foreach(lc, root->placeholder_list)
6682 {
6684 Relids relids;
6685
6686 /* PlaceHolderInfo refers to parent relids, not child relids. */
6687 relids = IS_OTHER_REL(joinrel) ?
6688 joinrel->top_parent_relids : joinrel->relids;
6689
6690 if (bms_is_subset(phinfo->ph_eval_at, relids) &&
6691 bms_nonempty_difference(relids, phinfo->ph_eval_at))
6692 return false;
6693 }
6694
6695 /* Save the join clauses, for later use. */
6696 fpinfo->joinclauses = joinclauses;
6697
6698 fpinfo->outerrel = outerrel;
6699 fpinfo->innerrel = innerrel;
6700 fpinfo->jointype = jointype;
6701
6702 /*
6703 * By default, both the input relations are not required to be deparsed as
6704 * subqueries, but there might be some relations covered by the input
6705 * relations that are required to be deparsed as subqueries, so save the
6706 * relids of those relations for later use by the deparser.
6707 */
6708 fpinfo->make_outerrel_subquery = false;
6709 fpinfo->make_innerrel_subquery = false;
6710 Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
6711 Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
6712 fpinfo->lower_subquery_rels = bms_union(fpinfo_o->lower_subquery_rels,
6713 fpinfo_i->lower_subquery_rels);
6714 fpinfo->hidden_subquery_rels = bms_union(fpinfo_o->hidden_subquery_rels,
6715 fpinfo_i->hidden_subquery_rels);
6716
6717 /*
6718 * Pull the other remote conditions from the joining relations into join
6719 * clauses or other remote clauses (remote_conds) of this relation
6720 * wherever possible. This avoids building subqueries at every join step.
6721 *
6722 * For an inner join, clauses from both the relations are added to the
6723 * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
6724 * the outer side are added to remote_conds since those can be evaluated
6725 * after the join is evaluated. The clauses from inner side are added to
6726 * the joinclauses, since they need to be evaluated while constructing the
6727 * join.
6728 *
6729 * For SEMI-JOIN clauses from inner relation can not be added to
6730 * remote_conds, but should be treated as join clauses (as they are
6731 * deparsed to EXISTS subquery, where inner relation can be referred). A
6732 * list of relation ids, which can't be referred to from higher levels, is
6733 * preserved as a hidden_subquery_rels list.
6734 *
6735 * For a FULL OUTER JOIN, the other clauses from either relation can not
6736 * be added to the joinclauses or remote_conds, since each relation acts
6737 * as an outer relation for the other.
6738 *
6739 * The joining sides can not have local conditions, thus no need to test
6740 * shippability of the clauses being pulled up.
6741 */
6742 switch (jointype)
6743 {
6744 case JOIN_INNER:
6745 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
6746 fpinfo_i->remote_conds);
6747 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
6748 fpinfo_o->remote_conds);
6749 break;
6750
6751 case JOIN_LEFT:
6752
6753 /*
6754 * When semi-join is involved in the inner or outer part of the
6755 * left join, it's deparsed as a subquery, and we can't refer to
6756 * its vars on the upper level.
6757 */
6758 if (bms_is_empty(fpinfo_i->hidden_subquery_rels))
6759 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
6760 fpinfo_i->remote_conds);
6761 if (bms_is_empty(fpinfo_o->hidden_subquery_rels))
6762 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
6763 fpinfo_o->remote_conds);
6764 break;
6765
6766 case JOIN_RIGHT:
6767
6768 /*
6769 * When semi-join is involved in the inner or outer part of the
6770 * right join, it's deparsed as a subquery, and we can't refer to
6771 * its vars on the upper level.
6772 */
6773 if (bms_is_empty(fpinfo_o->hidden_subquery_rels))
6774 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
6775 fpinfo_o->remote_conds);
6776 if (bms_is_empty(fpinfo_i->hidden_subquery_rels))
6777 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
6778 fpinfo_i->remote_conds);
6779 break;
6780
6781 case JOIN_SEMI:
6782 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
6783 fpinfo_i->remote_conds);
6784 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
6785 fpinfo->remote_conds);
6786 fpinfo->remote_conds = list_copy(fpinfo_o->remote_conds);
6787 fpinfo->hidden_subquery_rels = bms_union(fpinfo->hidden_subquery_rels,
6788 innerrel->relids);
6789 break;
6790
6791 case JOIN_FULL:
6792
6793 /*
6794 * In this case, if any of the input relations has conditions, we
6795 * need to deparse that relation as a subquery so that the
6796 * conditions can be evaluated before the join. Remember it in
6797 * the fpinfo of this relation so that the deparser can take
6798 * appropriate action. Also, save the relids of base relations
6799 * covered by that relation for later use by the deparser.
6800 */
6801 if (fpinfo_o->remote_conds)
6802 {
6803 fpinfo->make_outerrel_subquery = true;
6804 fpinfo->lower_subquery_rels =
6805 bms_add_members(fpinfo->lower_subquery_rels,
6806 outerrel->relids);
6807 }
6808 if (fpinfo_i->remote_conds)
6809 {
6810 fpinfo->make_innerrel_subquery = true;
6811 fpinfo->lower_subquery_rels =
6812 bms_add_members(fpinfo->lower_subquery_rels,
6813 innerrel->relids);
6814 }
6815 break;
6816
6817 default:
6818 /* Should not happen, we have just checked this above */
6819 elog(ERROR, "unsupported join type %d", jointype);
6820 }
6821
6822 /*
6823 * For an inner join, all restrictions can be treated alike. Treating the
6824 * pushed down conditions as join conditions allows a top level full outer
6825 * join to be deparsed without requiring subqueries.
6826 */
6827 if (jointype == JOIN_INNER)
6828 {
6829 Assert(!fpinfo->joinclauses);
6830 fpinfo->joinclauses = fpinfo->remote_conds;
6831 fpinfo->remote_conds = NIL;
6832 }
6833 else if (jointype == JOIN_LEFT || jointype == JOIN_RIGHT || jointype == JOIN_FULL)
6834 {
6835 /*
6836 * Conditions, generated from semi-joins, should be evaluated before
6837 * LEFT/RIGHT/FULL join.
6838 */
6839 if (!bms_is_empty(fpinfo_o->hidden_subquery_rels))
6840 {
6841 fpinfo->make_outerrel_subquery = true;
6842 fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, outerrel->relids);
6843 }
6844
6845 if (!bms_is_empty(fpinfo_i->hidden_subquery_rels))
6846 {
6847 fpinfo->make_innerrel_subquery = true;
6848 fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, innerrel->relids);
6849 }
6850 }
6851
6852 /* Mark that this join can be pushed down safely */
6853 fpinfo->pushdown_safe = true;
6854
6855 /* Get user mapping */
6856 if (fpinfo->use_remote_estimate)
6857 {
6858 if (fpinfo_o->use_remote_estimate)
6859 fpinfo->user = fpinfo_o->user;
6860 else
6861 fpinfo->user = fpinfo_i->user;
6862 }
6863 else
6864 fpinfo->user = NULL;
6865
6866 /*
6867 * Set # of retrieved rows and cached relation costs to some negative
6868 * value, so that we can detect when they are set to some sensible values,
6869 * during one (usually the first) of the calls to estimate_path_cost_size.
6870 */
6871 fpinfo->retrieved_rows = -1;
6872 fpinfo->rel_startup_cost = -1;
6873 fpinfo->rel_total_cost = -1;
6874
6875 /*
6876 * Set the string describing this join relation to be used in EXPLAIN
6877 * output of corresponding ForeignScan. Note that the decoration we add
6878 * to the base relation names mustn't include any digits, or it'll confuse
6879 * postgresExplainForeignScan.
6880 */
6881 fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
6882 fpinfo_o->relation_name,
6883 get_jointype_name(fpinfo->jointype),
6884 fpinfo_i->relation_name);
6885
6886 /*
6887 * Set the relation index. This is defined as the position of this
6888 * joinrel in the join_rel_list list plus the length of the rtable list.
6889 * Note that since this joinrel is at the end of the join_rel_list list
6890 * when we are called, we can get the position by list_length.
6891 */
6892 Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
6893 fpinfo->relation_index =
6894 list_length(root->parse->rtable) + list_length(root->join_rel_list);
6895
6896 return true;
6897}
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:1036
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:252
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:769
const char * get_jointype_name(JoinType jointype)
Definition deparse.c:1692
List * list_copy(const List *oldlist)
Definition list.c:1573
#define IS_OUTER_JOIN(jointype)
Definition nodes.h:346
@ JOIN_SEMI
Definition nodes.h:315
@ JOIN_FULL
Definition nodes.h:303
@ JOIN_RIGHT
Definition nodes.h:304
@ JOIN_LEFT
Definition nodes.h:302
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition pathnodes.h:3058
#define IS_OTHER_REL(rel)
Definition pathnodes.h:1004
static bool semijoin_target_ok(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel)
Relids top_parent_relids
Definition pathnodes.h:1174

References Assert, bms_add_members(), bms_is_empty, bms_is_subset(), bms_nonempty_difference(), bms_union(), RestrictInfo::clause, elog, ERROR, fb(), get_jointype_name(), is_foreign_expr(), IS_OTHER_REL, IS_OUTER_JOIN, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, lappend(), lfirst, lfirst_node, list_concat(), list_copy(), list_length(), merge_fdw_options(), NIL, psprintf(), RelOptInfo::relids, JoinPathExtraData::restrictlist, RINFO_IS_PUSHED_DOWN, root, semijoin_target_ok(), and RelOptInfo::top_parent_relids.

Referenced by postgresGetForeignJoinPaths().

◆ free_remattrmap()

static void free_remattrmap ( RemoteAttributeMapping map,
int  len 
)
static

Definition at line 5869 of file postgres_fdw.c.

5870{
5871 if (!map)
5872 return;
5873
5874 for (int i = 0; i < len; i++)
5875 {
5876 Assert(map[i].local_attname);
5877 pfree(map[i].local_attname);
5878 Assert(map[i].remote_attname);
5879 pfree(map[i].remote_attname);
5880 }
5881
5882 pfree(map);
5883}
const void size_t len

References Assert, i, len, and pfree().

Referenced by postgresImportForeignStatistics().

◆ get_batch_size_option()

static int get_batch_size_option ( Relation  rel)
static

Definition at line 8733 of file postgres_fdw.c.

8734{
8737 ForeignServer *server;
8738 List *options;
8739 ListCell *lc;
8740
8741 /* we use 1 by default, which means "no batching" */
8742 int batch_size = 1;
8743
8744 /*
8745 * Load options for table and server. We append server options after table
8746 * options, because table options take precedence.
8747 */
8749 server = GetForeignServer(table->serverid);
8750
8751 options = NIL;
8752 options = list_concat(options, table->options);
8753 options = list_concat(options, server->options);
8754
8755 /* See if either table or server specifies batch_size. */
8756 foreach(lc, options)
8757 {
8758 DefElem *def = (DefElem *) lfirst(lc);
8759
8760 if (strcmp(def->defname, "batch_size") == 0)
8761 {
8762 (void) parse_int(defGetString(def), &batch_size, 0, NULL);
8763 break;
8764 }
8765 }
8766
8767 return batch_size;
8768}
ForeignServer * GetForeignServer(Oid serverid)
Definition foreign.c:114
List * options
Definition foreign.h:43

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

Referenced by create_foreign_modify(), and postgresGetForeignModifyBatchSize().

◆ get_opt_value()

static char * get_opt_value ( PGresult res,
int  row,
int  col 
)
static

Definition at line 6140 of file postgres_fdw.c.

6141{
6142 if (PQgetisnull(res, row, col))
6143 return NULL;
6144 return PQgetvalue(res, row, col);
6145}
#define PQgetisnull

References fb(), PQgetisnull, and PQgetvalue.

Referenced by import_fetched_statistics().

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

3700{
3701 PGresult *res;
3702 char *line;
3703 char *p;
3704 int n;
3705
3706 /*
3707 * Execute EXPLAIN remotely.
3708 */
3709 res = pgfdw_exec_query(conn, sql, NULL);
3710 if (PQresultStatus(res) != PGRES_TUPLES_OK)
3711 pgfdw_report_error(res, conn, sql);
3712
3713 /*
3714 * Extract cost numbers for topmost plan node. Note we search for a left
3715 * paren from the end of the line to avoid being confused by other uses of
3716 * parentheses.
3717 */
3718 line = PQgetvalue(res, 0, 0);
3719 p = strrchr(line, '(');
3720 if (p == NULL)
3721 elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3722 n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3723 startup_cost, total_cost, rows, width);
3724 if (n != 4)
3725 elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3726 PQclear(res);
3727}

References conn, elog, ERROR, fb(), 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 4667 of file postgres_fdw.c.

4668{
4670 EState *estate = node->ss.ps.state;
4671 ResultRelInfo *resultRelInfo = node->resultRelInfo;
4672 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4674
4675 Assert(resultRelInfo->ri_projectReturning);
4676
4677 /* If we didn't get any tuples, must be end of data. */
4678 if (dmstate->next_tuple >= dmstate->num_tuples)
4679 return ExecClearTuple(slot);
4680
4681 /* Increment the command es_processed count if necessary. */
4682 if (dmstate->set_processed)
4683 estate->es_processed += 1;
4684
4685 /*
4686 * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4687 * tuple. (has_returning is false when the local query is of the form
4688 * "UPDATE/DELETE .. RETURNING 1" for example.)
4689 */
4690 if (!dmstate->has_returning)
4691 {
4693 resultSlot = slot;
4694 }
4695 else
4696 {
4698
4700 dmstate->next_tuple,
4701 dmstate->rel,
4702 dmstate->attinmeta,
4703 dmstate->retrieved_attrs,
4704 node,
4705 dmstate->temp_cxt);
4706 ExecStoreHeapTuple(newtup, slot, false);
4707 /* Get the updated/deleted tuple. */
4708 if (dmstate->rel)
4709 resultSlot = slot;
4710 else
4711 resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4712 }
4713 dmstate->next_tuple++;
4714
4715 /* Make slot available for evaluation of the local query RETURNING list. */
4717 resultSlot;
4718
4719 return slot;
4720}
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
uint64 es_processed
Definition execnodes.h:751
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
ResultRelInfo * resultRelInfo
Definition execnodes.h:2101
ExprContext * pi_exprContext
Definition execnodes.h:402
ProjectionInfo * ri_projectReturning
Definition execnodes.h:611
TupleTableSlot * ss_ScanTupleSlot
Definition execnodes.h:1665

References apply_returning_filter(), Assert, ExprContext::ecxt_scantuple, EState::es_processed, ExecClearTuple(), ExecStoreAllNullTuple(), ExecStoreHeapTuple(), fb(), ForeignScanState::fdw_state, make_tuple_from_result_row(), ProjectionInfo::pi_exprContext, ScanState::ps, ForeignScanState::resultRelInfo, ResultRelInfo::ri_projectReturning, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, and PlanState::state.

Referenced by postgresIterateDirectModify().

◆ get_tupdesc_for_join_scan_tuples()

static TupleDesc get_tupdesc_for_join_scan_tuples ( ForeignScanState node)
static

Definition at line 1541 of file postgres_fdw.c.

1542{
1543 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1544 EState *estate = node->ss.ps.state;
1545 TupleDesc tupdesc;
1546
1547 /*
1548 * The core code has already set up a scan tuple slot based on
1549 * fsplan->fdw_scan_tlist, and this slot's tupdesc is mostly good enough,
1550 * but there's one case where it isn't. If we have any whole-row row
1551 * identifier Vars, they may have vartype RECORD, and we need to replace
1552 * that with the associated table's actual composite type. This ensures
1553 * that when we read those ROW() expression values from the remote server,
1554 * we can convert them to a composite type the local server knows.
1555 */
1557 for (int i = 0; i < tupdesc->natts; i++)
1558 {
1559 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
1560 Var *var;
1562 Oid reltype;
1563
1564 /* Nothing to do if it's not a generic RECORD attribute */
1565 if (att->atttypid != RECORDOID || att->atttypmod >= 0)
1566 continue;
1567
1568 /*
1569 * If we can't identify the referenced table, do nothing. This'll
1570 * likely lead to failure later, but perhaps we can muddle through.
1571 */
1572 var = (Var *) list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
1573 i)->expr;
1574 if (!IsA(var, Var) || var->varattno != 0)
1575 continue;
1576 rte = list_nth(estate->es_range_table, var->varno - 1);
1577 if (rte->rtekind != RTE_RELATION)
1578 continue;
1579 reltype = get_rel_type_id(rte->relid);
1580 if (!OidIsValid(reltype))
1581 continue;
1582 att->atttypid = reltype;
1583 /* shouldn't need to change anything else */
1584 }
1585 return tupdesc;
1586}
#define OidIsValid(objectId)
Definition c.h:917
Oid get_rel_type_id(Oid relid)
Definition lsyscache.c:2293
@ RTE_RELATION
List * es_range_table
Definition execnodes.h:699
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242

References CreateTupleDescCopy(), EState::es_range_table, fb(), get_rel_type_id(), i, IsA, list_nth(), list_nth_node, TupleDescData::natts, OidIsValid, PlanState::plan, ScanState::ps, RTE_RELATION, 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 907 of file postgres_fdw.c.

908{
910 ListCell *lc;
911 Relids relids;
912
913 /*
914 * First, consider whether any active EC is potentially useful for a merge
915 * join against this relation.
916 */
917 if (rel->has_eclass_joins)
918 {
919 foreach(lc, root->eq_classes)
920 {
922
925 }
926 }
927
928 /*
929 * Next, consider whether there are any non-EC derivable join clauses that
930 * are merge-joinable. If the joininfo list is empty, we can exit
931 * quickly.
932 */
933 if (rel->joininfo == NIL)
934 return useful_eclass_list;
935
936 /* If this is a child rel, we must use the topmost parent rel to search. */
937 if (IS_OTHER_REL(rel))
938 {
940 relids = rel->top_parent_relids;
941 }
942 else
943 relids = rel->relids;
944
945 /* Check each join clause in turn. */
946 foreach(lc, rel->joininfo)
947 {
949
950 /* Consider only mergejoinable clauses */
951 if (restrictinfo->mergeopfamilies == NIL)
952 continue;
953
954 /* Make sure we've got canonical ECs. */
956
957 /*
958 * restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
959 * that left_ec and right_ec will be initialized, per comments in
960 * distribute_qual_to_rels.
961 *
962 * We want to identify which side of this merge-joinable clause
963 * contains columns from the relation produced by this RelOptInfo. We
964 * test for overlap, not containment, because there could be extra
965 * relations on either side. For example, suppose we've got something
966 * like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
967 * A.y = D.y. The input rel might be the joinrel between A and B, and
968 * we'll consider the join clause A.y = D.y. relids contains a
969 * relation not involved in the join class (B) and the equivalence
970 * class for the left-hand side of the clause contains a relation not
971 * involved in the input rel (C). Despite the fact that we have only
972 * overlap and not containment in either direction, A.y is potentially
973 * useful as a sort column.
974 *
975 * Note that it's even possible that relids overlaps neither side of
976 * the join clause. For example, consider A LEFT JOIN B ON A.x = B.x
977 * AND A.x = 1. The clause A.x = 1 will appear in B's joininfo list,
978 * but overlaps neither side of B. In that case, we just skip this
979 * join clause, since it doesn't suggest a useful sort order for this
980 * relation.
981 */
982 if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
984 restrictinfo->right_ec);
985 else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
987 restrictinfo->left_ec);
988 }
989
990 return useful_eclass_list;
991}
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:710
bool eclass_useful_for_merging(PlannerInfo *root, EquivalenceClass *eclass, RelOptInfo *rel)
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:1148
bool has_eclass_joins
Definition pathnodes.h:1150

References Assert, bms_is_empty, bms_overlap(), eclass_useful_for_merging(), fb(), 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 1003 of file postgres_fdw.c.

1004{
1007 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
1009 ListCell *lc;
1010
1011 /*
1012 * Pushing the query_pathkeys to the remote server is always worth
1013 * considering, because it might let us avoid a local sort.
1014 */
1015 fpinfo->qp_is_pushdown_safe = false;
1016 if (root->query_pathkeys)
1017 {
1018 bool query_pathkeys_ok = true;
1019
1020 foreach(lc, root->query_pathkeys)
1021 {
1023
1024 /*
1025 * The planner and executor don't have any clever strategy for
1026 * taking data sorted by a prefix of the query's pathkeys and
1027 * getting it to be sorted by all of those pathkeys. We'll just
1028 * end up resorting the entire data set. So, unless we can push
1029 * down all of the query pathkeys, forget it.
1030 */
1031 if (!is_foreign_pathkey(root, rel, pathkey))
1032 {
1033 query_pathkeys_ok = false;
1034 break;
1035 }
1036 }
1037
1039 {
1040 useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
1041 fpinfo->qp_is_pushdown_safe = true;
1042 }
1043 }
1044
1045 /*
1046 * Even if we're not using remote estimates, having the remote side do the
1047 * sort generally won't be any worse than doing it locally, and it might
1048 * be much better if the remote side can generate data in the right order
1049 * without needing a sort at all. However, what we're going to do next is
1050 * try to generate pathkeys that seem promising for possible merge joins,
1051 * and that's more speculative. A wrong choice might hurt quite a bit, so
1052 * bail out if we can't use remote estimates.
1053 */
1054 if (!fpinfo->use_remote_estimate)
1055 return useful_pathkeys_list;
1056
1057 /* Get the list of interesting EquivalenceClasses. */
1059
1060 /* Extract unique EC for query, if any, so we don't consider it again. */
1061 if (list_length(root->query_pathkeys) == 1)
1062 {
1063 PathKey *query_pathkey = linitial(root->query_pathkeys);
1064
1065 query_ec = query_pathkey->pk_eclass;
1066 }
1067
1068 /*
1069 * As a heuristic, the only pathkeys we consider here are those of length
1070 * one. It's surely possible to consider more, but since each one we
1071 * choose to consider will generate a round-trip to the remote side, we
1072 * need to be a bit cautious here. It would sure be nice to have a local
1073 * cache of information about remote index definitions...
1074 */
1075 foreach(lc, useful_eclass_list)
1076 {
1079
1080 /* If redundant with what we did above, skip it. */
1081 if (cur_ec == query_ec)
1082 continue;
1083
1084 /* Can't push down the sort if the EC's opfamily is not shippable. */
1085 if (!is_shippable(linitial_oid(cur_ec->ec_opfamilies),
1087 continue;
1088
1089 /* If no pushable expression for this rel, skip it. */
1090 if (find_em_for_rel(root, cur_ec, rel) == NULL)
1091 continue;
1092
1093 /* Looks like we can generate a pathkey, so let's do it. */
1095 linitial_oid(cur_ec->ec_opfamilies),
1096 COMPARE_LT,
1097 false);
1100 }
1101
1102 return useful_pathkeys_list;
1103}
@ COMPARE_LT
Definition cmptype.h:34
bool is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel, PathKey *pathkey)
Definition deparse.c:1176
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)

References COMPARE_LT, fb(), find_em_for_rel(), get_useful_ecs_for_relation(), is_foreign_pathkey(), is_shippable(), lappend(), lfirst, linitial, linitial_oid, list_copy(), list_length(), list_make1, make_canonical_pathkey(), NIL, and root.

Referenced by add_paths_with_pathkeys_for_rel().

◆ import_fetched_statistics()

static bool import_fetched_statistics ( Relation  relation,
const char schemaname,
const char relname,
int  attrcnt,
const RemoteAttributeMapping remattrmap,
RemoteStatsResults remstats 
)
static

Definition at line 6021 of file postgres_fdw.c.

6027{
6028 PGresult *res;
6030
6031 /* Set the 'version' parameter, which is common to both statistics. */
6032 args[0].value = Int32GetDatum(remstats->version);
6033 args[0].isnull = false;
6034
6035 /*
6036 * We import attribute statistics first, if any, because those are more
6037 * prone to errors. This avoids making a modification of pg_class that
6038 * will just get rolled back by a failed attribute import.
6039 */
6040 res = remstats->att;
6041 if (res != NULL)
6042 {
6044 Assert(PQntuples(res) >= 1);
6045
6046 for (int mapidx = 0; mapidx < attrcnt; mapidx++)
6047 {
6048 int row = remattrmap[mapidx].res_index;
6049 AttrNumber attnum = remattrmap[mapidx].local_attnum;
6050
6051 /* All mappings should have been assigned a result set row. */
6052 Assert(row >= 0);
6053
6054 /* Check for user-requested abort. */
6056
6057 /* Clear existing attribute statistics. */
6058 delete_attribute_statistics(relation, attnum, false);
6059
6060 /* Set the remaining parameters. */
6061 set_float_arg(&args[1],
6063 set_int32_arg(&args[2],
6065 set_float_arg(&args[3],
6067 set_text_arg(&args[4],
6069 set_floatarr_arg(&args[5],
6071 set_text_arg(&args[6],
6073 set_float_arg(&args[7],
6075 set_text_arg(&args[8],
6077 set_floatarr_arg(&args[9],
6079 set_floatarr_arg(&args[10],
6081 set_text_arg(&args[11],
6083 set_float_arg(&args[12],
6085 set_text_arg(&args[13],
6087
6088 /* Try to import the statistics. */
6089 if (!import_attribute_statistics(relation, attnum, false,
6090 &args[0], &args[1], &args[2],
6091 &args[3], &args[4], &args[5],
6092 &args[6], &args[7], &args[8],
6093 &args[9], &args[10], &args[11],
6094 &args[12], &args[13]))
6095 {
6097 errmsg("could not import statistics for foreign table \"%s.%s\" --- attribute statistics import failed for column \"%s\" of this foreign table",
6098 schemaname, relname,
6099 remattrmap[mapidx].local_attname));
6100 return false;
6101 }
6102 }
6103 }
6104
6105 /*
6106 * Import relation statistics.
6107 */
6108 res = remstats->rel;
6109 Assert(res != NULL);
6111 Assert(PQntuples(res) == 1);
6112
6113 /* Set the remaining parameters. */
6115 Assert(!args[1].isnull);
6117 Assert(!args[2].isnull);
6118 args[3].value = (Datum) 0;
6119 args[3].isnull = true;
6120 args[4].value = (Datum) 0;
6121 args[4].isnull = true;
6122
6123 /* Try to import the statistics. */
6124 if (!import_relation_statistics(relation, &args[0], &args[1],
6125 &args[2], &args[3], &args[4]))
6126 {
6128 errmsg("could not import statistics for foreign table \"%s.%s\" --- relation statistics import failed for this foreign table",
6129 schemaname, relname));
6130 return false;
6131 }
6132
6133 return true;
6134}
bool delete_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited)
bool import_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited, const NullableDatum *version, const NullableDatum *null_frac, const NullableDatum *avg_width, const NullableDatum *n_distinct, const NullableDatum *most_common_vals, const NullableDatum *most_common_freqs, const NullableDatum *histogram_bounds, const NullableDatum *correlation, const NullableDatum *most_common_elems, const NullableDatum *most_common_elem_freqs, const NullableDatum *elem_count_histogram, const NullableDatum *range_length_histogram, const NullableDatum *range_empty_frac, const NullableDatum *range_bounds_histogram)
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
static char * get_opt_value(PGresult *res, int row, int col)
static void set_text_arg(NullableDatum *arg, const char *s)
static void set_float_arg(NullableDatum *arg, const char *s)
static void set_int32_arg(NullableDatum *arg, const char *s)
static void set_floatarr_arg(NullableDatum *arg, const char *s)
static void set_uint32_arg(NullableDatum *arg, const char *s)
bool import_relation_statistics(Relation rel, const NullableDatum *version, const NullableDatum *relpages, const NullableDatum *reltuples, const NullableDatum *relallvisible, const NullableDatum *relallfrozen)

References Assert, attnum, ATTSTATS_AVG_WIDTH, ATTSTATS_CORRELATION, ATTSTATS_ELEM_COUNT_HISTOGRAM, ATTSTATS_HISTOGRAM_BOUNDS, ATTSTATS_MOST_COMMON_ELEM_FREQS, ATTSTATS_MOST_COMMON_ELEMS, ATTSTATS_MOST_COMMON_FREQS, ATTSTATS_MOST_COMMON_VALS, ATTSTATS_N_DISTINCT, ATTSTATS_NULL_FRAC, ATTSTATS_NUM_FIELDS, ATTSTATS_RANGE_BOUNDS_HISTOGRAM, ATTSTATS_RANGE_EMPTY_FRAC, ATTSTATS_RANGE_LENGTH_HISTOGRAM, CHECK_FOR_INTERRUPTS, delete_attribute_statistics(), ereport, errmsg, fb(), get_opt_value(), import_attribute_statistics(), import_relation_statistics(), Int32GetDatum(), PQnfields, PQntuples, relname, RELSTATS_NUM_FIELDS, RELSTATS_RELPAGES, RELSTATS_RELTUPLES, set_float_arg(), set_floatarr_arg(), set_int32_arg(), set_text_arg(), set_uint32_arg(), and WARNING.

Referenced by postgresImportForeignStatistics().

◆ init_returning_filter()

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

Definition at line 4726 of file postgres_fdw.c.

4729{
4731 ListCell *lc;
4732 int i;
4733
4734 /*
4735 * Calculate the mapping between the fdw_scan_tlist's entries and the
4736 * result tuple's attributes.
4737 *
4738 * The "map" is an array of indexes of the result tuple's attributes in
4739 * fdw_scan_tlist, i.e., one entry for every attribute of the result
4740 * tuple. We store zero for any attributes that don't have the
4741 * corresponding entries in that list, marking that a NULL is needed in
4742 * the result tuple.
4743 *
4744 * Also get the indexes of the entries for ctid and oid if any.
4745 */
4746 dmstate->attnoMap = (AttrNumber *)
4747 palloc0(resultTupType->natts * sizeof(AttrNumber));
4748
4749 dmstate->ctidAttno = dmstate->oidAttno = 0;
4750
4751 i = 1;
4752 dmstate->hasSystemCols = false;
4753 foreach(lc, fdw_scan_tlist)
4754 {
4756 Var *var = (Var *) tle->expr;
4757
4758 Assert(IsA(var, Var));
4759
4760 /*
4761 * If the Var is a column of the target relation to be retrieved from
4762 * the foreign server, get the index of the entry.
4763 */
4764 if (var->varno == rtindex &&
4765 list_member_int(dmstate->retrieved_attrs, i))
4766 {
4767 int attrno = var->varattno;
4768
4769 if (attrno < 0)
4770 {
4771 /*
4772 * We don't retrieve system columns other than ctid and oid.
4773 */
4775 dmstate->ctidAttno = i;
4776 else
4777 Assert(false);
4778 dmstate->hasSystemCols = true;
4779 }
4780 else
4781 {
4782 /*
4783 * We don't retrieve whole-row references to the target
4784 * relation either.
4785 */
4786 Assert(attrno > 0);
4787
4788 dmstate->attnoMap[attrno - 1] = i;
4789 }
4790 }
4791 i++;
4792 }
4793}
bool list_member_int(const List *list, int datum)
Definition list.c:702

References Assert, fb(), i, IsA, lfirst, list_member_int(), palloc0(), RelationGetDescr, 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 8371 of file postgres_fdw.c.

8378{
8379 HeapTuple tuple;
8380 TupleDesc tupdesc;
8381 Datum *values;
8382 bool *nulls;
8383 ItemPointer ctid = NULL;
8385 ErrorContextCallback errcallback;
8386 MemoryContext oldcontext;
8387 ListCell *lc;
8388 int j;
8389
8390 Assert(row < PQntuples(res));
8391
8392 /*
8393 * Do the following work in a temp context that we reset after each tuple.
8394 * This cleans up not only the data we have direct access to, but any
8395 * cruft the I/O functions might leak.
8396 */
8397 oldcontext = MemoryContextSwitchTo(temp_context);
8398
8399 /*
8400 * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
8401 * provided, otherwise look to the scan node's ScanTupleSlot.
8402 */
8403 if (rel)
8404 tupdesc = RelationGetDescr(rel);
8405 else
8406 {
8407 Assert(fsstate);
8408 tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
8409 }
8410
8411 values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
8412 nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
8413 /* Initialize to nulls for any columns not present in result */
8414 memset(nulls, true, tupdesc->natts * sizeof(bool));
8415
8416 /*
8417 * Set up and install callback to report where conversion error occurs.
8418 */
8419 errpos.cur_attno = 0;
8420 errpos.rel = rel;
8421 errpos.fsstate = fsstate;
8422 errcallback.callback = conversion_error_callback;
8423 errcallback.arg = &errpos;
8424 errcallback.previous = error_context_stack;
8425 error_context_stack = &errcallback;
8426
8427 /*
8428 * i indexes columns in the relation, j indexes columns in the PGresult.
8429 */
8430 j = 0;
8431 foreach(lc, retrieved_attrs)
8432 {
8433 int i = lfirst_int(lc);
8434 char *valstr;
8435
8436 /* fetch next column's textual value */
8437 if (PQgetisnull(res, row, j))
8438 valstr = NULL;
8439 else
8440 valstr = PQgetvalue(res, row, j);
8441
8442 /*
8443 * convert value to internal representation
8444 *
8445 * Note: we ignore system columns other than ctid and oid in result
8446 */
8447 errpos.cur_attno = i;
8448 if (i > 0)
8449 {
8450 /* ordinary column */
8451 Assert(i <= tupdesc->natts);
8452 nulls[i - 1] = (valstr == NULL);
8453 /* Apply the input function even to nulls, to support domains */
8454 values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
8455 valstr,
8456 attinmeta->attioparams[i - 1],
8457 attinmeta->atttypmods[i - 1]);
8458 }
8460 {
8461 /* ctid */
8462 if (valstr != NULL)
8463 {
8464 Datum datum;
8465
8467 ctid = (ItemPointer) DatumGetPointer(datum);
8468 }
8469 }
8470 errpos.cur_attno = 0;
8471
8472 j++;
8473 }
8474
8475 /* Uninstall error context callback. */
8476 error_context_stack = errcallback.previous;
8477
8478 /*
8479 * Check we got the expected number of columns. Note: j == 0 and
8480 * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
8481 */
8482 if (j > 0 && j != PQnfields(res))
8483 elog(ERROR, "remote query result does not match the foreign table");
8484
8485 /*
8486 * Build the result tuple in caller's memory context.
8487 */
8488 MemoryContextSwitchTo(oldcontext);
8489
8490 tuple = heap_form_tuple(tupdesc, values, nulls);
8491
8492 /*
8493 * If we have a CTID to return, install it in both t_self and t_ctid.
8494 * t_self is the normal place, but if the tuple is converted to a
8495 * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
8496 * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
8497 */
8498 if (ctid)
8499 tuple->t_self = tuple->t_data->t_ctid = *ctid;
8500
8501 /*
8502 * Stomp on the xmin, xmax, and cmin fields from the tuple created by
8503 * heap_form_tuple. heap_form_tuple actually creates the tuple with
8504 * DatumTupleFields, not HeapTupleFields, but the executor expects
8505 * HeapTupleFields and will happily extract system columns on that
8506 * assumption. If we don't do this then, for example, the tuple length
8507 * ends up in the xmin field, which isn't what we want.
8508 */
8512
8513 /* Clean up */
8515
8516 return tuple;
8517}
ErrorContextCallback * error_context_stack
Definition elog.c:100
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition fmgr.c:1532
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:688
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
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:299
void(* callback)(void *arg)
Definition elog.h:300
ItemPointerData t_self
Definition htup.h:65
HeapTupleHeader t_data
Definition htup.h:68
ItemPointerData t_ctid
Datum tidin(PG_FUNCTION_ARGS)
Definition tid.c:51

References ErrorContextCallback::arg, Assert, AttInMetadata::attinfuncs, AttInMetadata::attioparams, AttInMetadata::atttypmods, ErrorContextCallback::callback, conversion_error_callback(), CStringGetDatum(), DatumGetPointer(), DirectFunctionCall1, elog, ERROR, error_context_stack, fb(), heap_form_tuple(), HeapTupleHeaderSetCmin(), HeapTupleHeaderSetXmax(), HeapTupleHeaderSetXmin(), i, InputFunctionCall(), InvalidTransactionId, j, lfirst_int, MemoryContextReset(), MemoryContextSwitchTo(), TupleDescData::natts, palloc(), palloc0(), PQgetisnull, PQgetvalue, PQnfields, PQntuples, ErrorContextCallback::previous, 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().

◆ match_attrmap()

static bool match_attrmap ( PGresult res,
const char local_schemaname,
const char local_relname,
const char remote_schemaname,
const char remote_relname,
int  attrcnt,
RemoteAttributeMapping remattrmap 
)
static

Definition at line 5931 of file postgres_fdw.c.

5938{
5939 int numrows = PQntuples(res);
5940 int row = -1;
5941
5942 /* No work if there are no stats rows. */
5943 if (numrows == 0)
5944 {
5946 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" has no attribute statistics to import",
5949 return false;
5950 }
5951
5952 /* Scan all entries in the RemoteAttributeMapping. */
5953 for (int mapidx = 0; mapidx < attrcnt; mapidx++)
5954 {
5955 /*
5956 * First, check whether the entry matches the current stats row, if it
5957 * is set.
5958 */
5959 if (row >= 0 &&
5960 strcmp(remattrmap[mapidx].remote_attname,
5961 PQgetvalue(res, row, ATTSTATS_ATTNAME)) == 0)
5962 {
5963 remattrmap[mapidx].res_index = row;
5964 continue;
5965 }
5966
5967 /*
5968 * If we've exhausted all stats rows, it means the stats for the entry
5969 * are missing.
5970 */
5971 if (row >= numrows - 1)
5972 {
5974 errmsg("could not import statistics for foreign table \"%s.%s\" --- no attribute statistics found for column \"%s\" of remote table \"%s.%s\"",
5976 remattrmap[mapidx].remote_attname,
5978 return false;
5979 }
5980
5981 /* Advance to the next stats row. */
5982 row += 1;
5983
5984 /*
5985 * If the attname in the entry is less than that in the next stats
5986 * row, it means the stats for the entry are missing.
5987 */
5988 if (strcmp(remattrmap[mapidx].remote_attname,
5989 PQgetvalue(res, row, ATTSTATS_ATTNAME)) < 0)
5990 {
5992 errmsg("could not import statistics for foreign table \"%s.%s\" --- no attribute statistics found for column \"%s\" of remote table \"%s.%s\"",
5994 remattrmap[mapidx].remote_attname,
5996 return false;
5997 }
5998
5999 /* We should not have got a stats row we didn't expect. */
6000 if (strcmp(remattrmap[mapidx].remote_attname,
6001 PQgetvalue(res, row, ATTSTATS_ATTNAME)) > 0)
6002 elog(ERROR, "unexpected result from fetch_attstats query");
6003
6004 /* We found a match. */
6005 Assert(strcmp(remattrmap[mapidx].remote_attname,
6006 PQgetvalue(res, row, ATTSTATS_ATTNAME)) == 0);
6007 remattrmap[mapidx].res_index = row;
6008 }
6009
6010 /* We should have exhausted all stats rows. */
6011 if (row < numrows - 1)
6012 elog(ERROR, "unexpected result from fetch_attstats query");
6013
6014 return true;
6015}

References Assert, ATTSTATS_ATTNAME, elog, ereport, errmsg, ERROR, fb(), PQgetvalue, PQntuples, and WARNING.

Referenced by fetch_remote_statistics().

◆ merge_fdw_options()

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

Definition at line 7082 of file postgres_fdw.c.

7085{
7086 /* We must always have fpinfo_o. */
7088
7089 /* fpinfo_i may be NULL, but if present the servers must both match. */
7090 Assert(!fpinfo_i ||
7091 fpinfo_i->server->serverid == fpinfo_o->server->serverid);
7092
7093 /*
7094 * Copy the server specific FDW options. (For a join, both relations come
7095 * from the same server, so the server options should have the same value
7096 * for both relations.)
7097 */
7098 fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
7099 fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
7100 fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
7101 fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
7102 fpinfo->fetch_size = fpinfo_o->fetch_size;
7103 fpinfo->async_capable = fpinfo_o->async_capable;
7104
7105 /* Merge the table level options from either side of the join. */
7106 if (fpinfo_i)
7107 {
7108 /*
7109 * We'll prefer to use remote estimates for this join if any table
7110 * from either side of the join is using remote estimates. This is
7111 * most likely going to be preferred since they're already willing to
7112 * pay the price of a round trip to get the remote EXPLAIN. In any
7113 * case it's not entirely clear how we might otherwise handle this
7114 * best.
7115 */
7116 fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
7117 fpinfo_i->use_remote_estimate;
7118
7119 /*
7120 * Set fetch size to maximum of the joining sides, since we are
7121 * expecting the rows returned by the join to be proportional to the
7122 * relation sizes.
7123 */
7124 fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
7125
7126 /*
7127 * We'll prefer to consider this join async-capable if any table from
7128 * either side of the join is considered async-capable. This would be
7129 * reasonable because in that case the foreign server would have its
7130 * own resources to scan that table asynchronously, and the join could
7131 * also be computed asynchronously using the resources.
7132 */
7133 fpinfo->async_capable = fpinfo_o->async_capable ||
7134 fpinfo_i->async_capable;
7135 }
7136}
#define Max(x, y)
Definition c.h:1125

References Assert, fb(), and Max.

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

649{
650 FdwRoutine *routine = makeNode(FdwRoutine);
651
652 /* Functions for scanning foreign tables */
660
661 /* Functions for updating foreign tables */
678
679 /* Function for EvalPlanQual rechecks */
681 /* Support functions for EXPLAIN */
685
686 /* Support function for TRUNCATE */
688
689 /* Support functions for ANALYZE */
692
693 /* Support functions for IMPORT FOREIGN SCHEMA */
695
696 /* Support functions for join push-down */
698
699 /* Support functions for upper relation push-down */
701
702 /* Support functions for asynchronous execution */
707
708 PG_RETURN_POINTER(routine);
709}
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define makeNode(_type_)
Definition nodes.h:159
static int postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
static bool postgresImportForeignStatistics(Relation relation, List *va_cols, int elevel)
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)
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:243
ReScanForeignScan_function ReScanForeignScan
Definition fdwapi.h:218
BeginForeignInsert_function BeginForeignInsert
Definition fdwapi.h:242
RecheckForeignScan_function RecheckForeignScan
Definition fdwapi.h:253
AddForeignUpdateTargets_function AddForeignUpdateTargets
Definition fdwapi.h:233
BeginForeignModify_function BeginForeignModify
Definition fdwapi.h:235
EndForeignModify_function EndForeignModify
Definition fdwapi.h:241
BeginDirectModify_function BeginDirectModify
Definition fdwapi.h:246
PlanForeignModify_function PlanForeignModify
Definition fdwapi.h:234
PlanDirectModify_function PlanDirectModify
Definition fdwapi.h:245
ExecForeignInsert_function ExecForeignInsert
Definition fdwapi.h:236
BeginForeignScan_function BeginForeignScan
Definition fdwapi.h:216
ForeignAsyncRequest_function ForeignAsyncRequest
Definition fdwapi.h:283
IterateDirectModify_function IterateDirectModify
Definition fdwapi.h:247
ExecForeignUpdate_function ExecForeignUpdate
Definition fdwapi.h:239
GetForeignJoinPaths_function GetForeignJoinPaths
Definition fdwapi.h:227
ImportForeignStatistics_function ImportForeignStatistics
Definition fdwapi.h:262
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition fdwapi.h:237
GetForeignPaths_function GetForeignPaths
Definition fdwapi.h:214
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition fdwapi.h:238
GetForeignRelSize_function GetForeignRelSize
Definition fdwapi.h:213
ExplainForeignScan_function ExplainForeignScan
Definition fdwapi.h:256
EndForeignScan_function EndForeignScan
Definition fdwapi.h:219
AnalyzeForeignTable_function AnalyzeForeignTable
Definition fdwapi.h:261
EndDirectModify_function EndDirectModify
Definition fdwapi.h:248
ExplainForeignModify_function ExplainForeignModify
Definition fdwapi.h:257
IsForeignPathAsyncCapable_function IsForeignPathAsyncCapable
Definition fdwapi.h:282
IterateForeignScan_function IterateForeignScan
Definition fdwapi.h:217
ForeignAsyncNotify_function ForeignAsyncNotify
Definition fdwapi.h:285
ImportForeignSchema_function ImportForeignSchema
Definition fdwapi.h:265
GetForeignPlan_function GetForeignPlan
Definition fdwapi.h:215
ExecForeignDelete_function ExecForeignDelete
Definition fdwapi.h:240
ExecForeignTruncate_function ExecForeignTruncate
Definition fdwapi.h:268
ExplainDirectModify_function ExplainDirectModify
Definition fdwapi.h:258
IsForeignRelUpdatable_function IsForeignRelUpdatable
Definition fdwapi.h:244
GetForeignUpperPaths_function GetForeignUpperPaths
Definition fdwapi.h:230
ForeignAsyncConfigureWait_function ForeignAsyncConfigureWait
Definition fdwapi.h:284

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::ImportForeignStatistics, 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(), postgresImportForeignStatistics(), 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 5103 of file postgres_fdw.c.

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

1846{
1847 Var *var;
1848
1849 /*
1850 * In postgres_fdw, what we need is the ctid, same as for a regular table.
1851 */
1852
1853 /* Make a Var representing the desired value */
1854 var = makeVar(rtindex,
1856 TIDOID,
1857 -1,
1858 InvalidOid,
1859 0);
1860
1861 /* Register it as a row-identity column needed by this target rel */
1862 add_row_identity_var(root, var, rtindex, "ctid");
1863}
void add_row_identity_var(PlannerInfo *root, Var *orig_var, Index rtindex, const char *rowid_name)
Definition appendinfo.c:864
#define InvalidOid

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

Referenced by postgres_fdw_handler().

◆ postgresAnalyzeForeignTable()

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

Definition at line 4984 of file postgres_fdw.c.

4987{
4990 PGconn *conn;
4991 StringInfoData sql;
4992 PGresult *res;
4993
4994 /* Return the row-analysis function pointer */
4996
4997 /*
4998 * Now we have to get the number of pages. It's annoying that the ANALYZE
4999 * API requires us to return that now, because it forces some duplication
5000 * of effort between this routine and postgresAcquireSampleRowsFunc. But
5001 * it's probably not worth redefining that API at this point.
5002 */
5003
5004 /*
5005 * Get the connection to use. We do the remote access as the table's
5006 * owner, even if the ANALYZE was started by some other user.
5007 */
5009 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5010 conn = GetConnection(user, false, NULL);
5011
5012 /*
5013 * Construct command to get page count for relation.
5014 */
5015 initStringInfo(&sql);
5016 deparseAnalyzeSizeSql(&sql, relation);
5017
5018 res = pgfdw_exec_query(conn, sql.data, NULL);
5019 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5020 pgfdw_report_error(res, conn, sql.data);
5021
5022 if (PQntuples(res) != 1 || PQnfields(res) != 1)
5023 elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
5024 *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
5025 PQclear(res);
5026
5028
5029 return true;
5030}
void deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
Definition deparse.c:2550
static int postgresAcquireSampleRowsFunc(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)

References conn, StringInfoData::data, deparseAnalyzeSizeSql(), elog, ERROR, fb(), 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 2740 of file postgres_fdw.c.

2741{
2742 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
2743 EState *estate = node->ss.ps.state;
2745 Index rtindex;
2746 Oid userid;
2749 int numParams;
2750
2751 /*
2752 * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
2753 */
2754 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2755 return;
2756
2757 /*
2758 * We'll save private state in node->fdw_state.
2759 */
2761 node->fdw_state = dmstate;
2762
2763 /*
2764 * Identify which user to do the remote access as. This should match what
2765 * ExecCheckPermissions() does.
2766 */
2767 userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
2768
2769 /* Get info about foreign table. */
2770 rtindex = node->resultRelInfo->ri_RangeTableIndex;
2771 if (fsplan->scan.scanrelid == 0)
2772 dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags);
2773 else
2774 dmstate->rel = node->ss.ss_currentRelation;
2776 user = GetUserMapping(userid, table->serverid);
2777
2778 /*
2779 * Get connection to the foreign server. Connection manager will
2780 * establish new connection if necessary.
2781 */
2782 dmstate->conn = GetConnection(user, false, &dmstate->conn_state);
2783
2784 /* Update the foreign-join-related fields. */
2785 if (fsplan->scan.scanrelid == 0)
2786 {
2787 /* Save info about foreign table. */
2788 dmstate->resultRel = dmstate->rel;
2789
2790 /*
2791 * Set dmstate->rel to NULL to teach get_returning_data() and
2792 * make_tuple_from_result_row() that columns fetched from the remote
2793 * server are described by fdw_scan_tlist of the foreign-scan plan
2794 * node, not the tuple descriptor for the target relation.
2795 */
2796 dmstate->rel = NULL;
2797 }
2798
2799 /* Initialize state variable */
2800 dmstate->num_tuples = -1; /* -1 means not set yet */
2801
2802 /* Get private info created by planner functions. */
2803 dmstate->query = strVal(list_nth(fsplan->fdw_private,
2805 dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
2807 dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
2809 dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
2811
2812 /* Create context for per-tuple temp workspace. */
2813 dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
2814 "postgres_fdw temporary data",
2816
2817 /* Prepare for input conversion of RETURNING results. */
2818 if (dmstate->has_returning)
2819 {
2820 TupleDesc tupdesc;
2821
2822 if (fsplan->scan.scanrelid == 0)
2823 tupdesc = get_tupdesc_for_join_scan_tuples(node);
2824 else
2825 tupdesc = RelationGetDescr(dmstate->rel);
2826
2827 dmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
2828
2829 /*
2830 * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2831 * initialize a filter to extract an updated/deleted tuple from a scan
2832 * tuple.
2833 */
2834 if (fsplan->scan.scanrelid == 0)
2835 init_returning_filter(dmstate, fsplan->fdw_scan_tlist, rtindex);
2836 }
2837
2838 /*
2839 * Prepare for processing of parameters used in remote query, if any.
2840 */
2841 numParams = list_length(fsplan->fdw_exprs);
2842 dmstate->numParams = numParams;
2843 if (numParams > 0)
2845 fsplan->fdw_exprs,
2846 numParams,
2847 &dmstate->param_flinfo,
2848 &dmstate->param_exprs,
2849 &dmstate->param_values);
2850}
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition execUtils.c:768
#define EXEC_FLAG_EXPLAIN_ONLY
Definition executor.h:67
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)
Index ri_RangeTableIndex
Definition execnodes.h:511
Relation ss_currentRelation
Definition execnodes.h:1663
#define boolVal(v)
Definition value.h:81

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, boolVal, EState::es_query_cxt, EXEC_FLAG_EXPLAIN_ONLY, ExecOpenScanRelation(), fb(), ForeignScanState::fdw_state, FdwDirectModifyPrivateHasReturning, FdwDirectModifyPrivateRetrievedAttrs, FdwDirectModifyPrivateSetProcessed, FdwDirectModifyPrivateUpdateSql, get_tupdesc_for_join_scan_tuples(), GetConnection(), GetForeignTable(), GetUserId(), GetUserMapping(), init_returning_filter(), list_length(), list_nth(), OidIsValid, palloc0_object, PlanState::plan, prepare_query_params(), ScanState::ps, RelationGetDescr, RelationGetRelid, ForeignScanState::resultRelInfo, ResultRelInfo::ri_RangeTableIndex, 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 2251 of file postgres_fdw.c.

2253{
2256 EState *estate = mtstate->ps.state;
2257 Index resultRelation;
2258 Relation rel = resultRelInfo->ri_RelationDesc;
2260 TupleDesc tupdesc = RelationGetDescr(rel);
2261 int attnum;
2262 int values_end_len;
2263 StringInfoData sql;
2264 List *targetAttrs = NIL;
2265 List *retrieved_attrs = NIL;
2266 bool doNothing = false;
2267
2268 /*
2269 * If the foreign table we are about to insert routed rows into is also an
2270 * UPDATE subplan result rel that will be updated later, proceeding with
2271 * the INSERT will result in the later UPDATE incorrectly modifying those
2272 * routed rows, so prevent the INSERT --- it would be nice if we could
2273 * handle this case; but for now, throw an error for safety.
2274 */
2275 if (plan && plan->operation == CMD_UPDATE &&
2276 (resultRelInfo->ri_usesFdwDirectModify ||
2277 resultRelInfo->ri_FdwState))
2278 ereport(ERROR,
2280 errmsg("cannot route tuples into foreign table to be updated \"%s\"",
2282
2283 initStringInfo(&sql);
2284
2285 /* We transmit all columns that are defined in the foreign table. */
2286 for (attnum = 1; attnum <= tupdesc->natts; attnum++)
2287 {
2288 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
2289
2290 if (!attr->attisdropped)
2292 }
2293
2294 /* Check if we add the ON CONFLICT clause to the remote query. */
2295 if (plan)
2296 {
2297 OnConflictAction onConflictAction = plan->onConflictAction;
2298
2299 /* We only support DO NOTHING without an inference specification. */
2300 if (onConflictAction == ONCONFLICT_NOTHING)
2301 doNothing = true;
2302 else if (onConflictAction != ONCONFLICT_NONE)
2303 elog(ERROR, "unexpected ON CONFLICT specification: %d",
2304 (int) onConflictAction);
2305 }
2306
2307 /*
2308 * If the foreign table is a partition that doesn't have a corresponding
2309 * RTE entry, we need to create a new RTE describing the foreign table for
2310 * use by deparseInsertSql and create_foreign_modify() below, after first
2311 * copying the parent's RTE and modifying some fields to describe the
2312 * foreign partition to work on. However, if this is invoked by UPDATE,
2313 * the existing RTE may already correspond to this partition if it is one
2314 * of the UPDATE subplan target rels; in that case, we can just use the
2315 * existing RTE as-is.
2316 */
2317 if (resultRelInfo->ri_RangeTableIndex == 0)
2318 {
2319 ResultRelInfo *rootResultRelInfo = resultRelInfo->ri_RootResultRelInfo;
2320
2321 rte = exec_rt_fetch(rootResultRelInfo->ri_RangeTableIndex, estate);
2322 rte = copyObject(rte);
2323 rte->relid = RelationGetRelid(rel);
2324 rte->relkind = RELKIND_FOREIGN_TABLE;
2325
2326 /*
2327 * For UPDATE, we must use the RT index of the first subplan target
2328 * rel's RTE, because the core code would have built expressions for
2329 * the partition, such as RETURNING, using that RT index as varno of
2330 * Vars contained in those expressions.
2331 */
2332 if (plan && plan->operation == CMD_UPDATE &&
2333 rootResultRelInfo->ri_RangeTableIndex == plan->rootRelation)
2334 resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
2335 else
2336 resultRelation = rootResultRelInfo->ri_RangeTableIndex;
2337 }
2338 else
2339 {
2340 resultRelation = resultRelInfo->ri_RangeTableIndex;
2341 rte = exec_rt_fetch(resultRelation, estate);
2342 }
2343
2344 /* Construct the SQL command string. */
2345 deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing,
2346 resultRelInfo->ri_WithCheckOptions,
2347 resultRelInfo->ri_returningList,
2348 &retrieved_attrs, &values_end_len);
2349
2350 /* Construct an execution state. */
2352 rte,
2353 resultRelInfo,
2354 CMD_INSERT,
2355 NULL,
2356 sql.data,
2359 retrieved_attrs != NIL,
2360 retrieved_attrs);
2361
2362 /*
2363 * If the given resultRelInfo already has PgFdwModifyState set, it means
2364 * the foreign table is an UPDATE subplan result rel; in which case, store
2365 * the resulting state into the aux_fmstate of the PgFdwModifyState.
2366 */
2367 if (resultRelInfo->ri_FdwState)
2368 {
2369 Assert(plan && plan->operation == CMD_UPDATE);
2370 Assert(resultRelInfo->ri_usesFdwDirectModify == false);
2371 ((PgFdwModifyState *) resultRelInfo->ri_FdwState)->aux_fmstate = fmstate;
2372 }
2373 else
2374 resultRelInfo->ri_FdwState = fmstate;
2375}
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:2134
List * lappend_int(List *list, int datum)
Definition list.c:357
#define copyObject(obj)
Definition nodes.h:230
OnConflictAction
Definition nodes.h:425
@ ONCONFLICT_NONE
Definition nodes.h:426
@ ONCONFLICT_NOTHING
Definition nodes.h:427
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:78
ResultRelInfo * resultRelInfo
Definition execnodes.h:1447
struct ResultRelInfo * ri_RootResultRelInfo
Definition execnodes.h:655
List * ri_WithCheckOptions
Definition execnodes.h:583
List * ri_returningList
Definition execnodes.h:608
bool ri_usesFdwDirectModify
Definition execnodes.h:573

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

2014{
2016 char *query;
2017 List *target_attrs;
2018 bool has_returning;
2019 int values_end_len;
2020 List *retrieved_attrs;
2022
2023 /*
2024 * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
2025 * stays NULL.
2026 */
2027 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2028 return;
2029
2030 /* Deconstruct fdw_private data. */
2031 query = strVal(list_nth(fdw_private,
2033 target_attrs = (List *) list_nth(fdw_private,
2035 values_end_len = intVal(list_nth(fdw_private,
2037 has_returning = boolVal(list_nth(fdw_private,
2039 retrieved_attrs = (List *) list_nth(fdw_private,
2041
2042 /* Find RTE. */
2043 rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex,
2044 mtstate->ps.state);
2045
2046 /* Construct an execution state. */
2048 rte,
2049 resultRelInfo,
2050 mtstate->operation,
2051 outerPlanState(mtstate)->plan,
2052 query,
2053 target_attrs,
2055 has_returning,
2056 retrieved_attrs);
2057
2058 resultRelInfo->ri_FdwState = fmstate;
2059}
#define outerPlanState(node)
Definition execnodes.h:1300
#define intVal(v)
Definition value.h:79

References boolVal, create_foreign_modify(), EXEC_FLAG_EXPLAIN_ONLY, exec_rt_fetch(), fb(), 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 1593 of file postgres_fdw.c.

1594{
1595 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1596 EState *estate = node->ss.ps.state;
1597 PgFdwScanState *fsstate;
1599 Oid userid;
1602 int rtindex;
1603 int numParams;
1604
1605 /*
1606 * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
1607 */
1608 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1609 return;
1610
1611 /*
1612 * We'll save private state in node->fdw_state.
1613 */
1614 fsstate = palloc0_object(PgFdwScanState);
1615 node->fdw_state = fsstate;
1616
1617 /*
1618 * Identify which user to do the remote access as. This should match what
1619 * ExecCheckPermissions() does.
1620 */
1621 userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
1622 if (fsplan->scan.scanrelid > 0)
1623 rtindex = fsplan->scan.scanrelid;
1624 else
1625 rtindex = bms_next_member(fsplan->fs_base_relids, -1);
1626 rte = exec_rt_fetch(rtindex, estate);
1627
1628 /* Get info about foreign table. */
1629 table = GetForeignTable(rte->relid);
1630 user = GetUserMapping(userid, table->serverid);
1631
1632 /*
1633 * Get connection to the foreign server. Connection manager will
1634 * establish new connection if necessary.
1635 */
1636 fsstate->conn = GetConnection(user, false, &fsstate->conn_state);
1637
1638 /* Assign a unique ID for my cursor */
1639 fsstate->cursor_number = GetCursorNumber(fsstate->conn);
1640 fsstate->cursor_exists = false;
1641
1642 /* Get private info created by planner functions. */
1643 fsstate->query = strVal(list_nth(fsplan->fdw_private,
1645 fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
1647 fsstate->fetch_size = intVal(list_nth(fsplan->fdw_private,
1649
1650 /* Create contexts for batches of tuples and per-tuple temp workspace. */
1651 fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
1652 "postgres_fdw tuple data",
1654 fsstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
1655 "postgres_fdw temporary data",
1657
1658 /*
1659 * Get info we'll need for converting data fetched from the foreign server
1660 * into local representation and error reporting during that process.
1661 */
1662 if (fsplan->scan.scanrelid > 0)
1663 {
1664 fsstate->rel = node->ss.ss_currentRelation;
1665 fsstate->tupdesc = RelationGetDescr(fsstate->rel);
1666 }
1667 else
1668 {
1669 fsstate->rel = NULL;
1671 }
1672
1673 fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
1674
1675 /*
1676 * Prepare for processing of parameters used in remote query, if any.
1677 */
1678 numParams = list_length(fsplan->fdw_exprs);
1679 fsstate->numParams = numParams;
1680 if (numParams > 0)
1682 fsplan->fdw_exprs,
1683 numParams,
1684 &fsstate->param_flinfo,
1685 &fsstate->param_exprs,
1686 &fsstate->param_values);
1687
1688 /* Set the async-capable flag */
1689 fsstate->async_capable = node->ss.ps.async_capable;
1690}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1425
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
List * retrieved_attrs
FmgrInfo * param_flinfo
const char ** param_values
AttInMetadata * attinmeta
MemoryContext batch_cxt
MemoryContext temp_cxt
TupleDesc tupdesc
bool async_capable
Definition execnodes.h:1246

References ALLOCSET_DEFAULT_SIZES, ALLOCSET_SMALL_SIZES, AllocSetContextCreate, PgFdwScanState::async_capable, PlanState::async_capable, PgFdwScanState::attinmeta, PgFdwScanState::batch_cxt, bms_next_member(), PgFdwScanState::conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, EState::es_query_cxt, EXEC_FLAG_EXPLAIN_ONLY, exec_rt_fetch(), fb(), ForeignScanState::fdw_state, FdwScanPrivateFetchSize, FdwScanPrivateRetrievedAttrs, FdwScanPrivateSelectSql, PgFdwScanState::fetch_size, get_tupdesc_for_join_scan_tuples(), GetConnection(), GetCursorNumber(), GetForeignTable(), GetUserId(), GetUserMapping(), intVal, list_length(), list_nth(), PgFdwScanState::numParams, OidIsValid, palloc0_object, PgFdwScanState::param_exprs, PgFdwScanState::param_flinfo, PgFdwScanState::param_values, PlanState::plan, prepare_query_params(), ScanState::ps, PgFdwScanState::query, PgFdwScanState::rel, RelationGetDescr, PgFdwScanState::retrieved_attrs, ForeignScanState::ss, ScanState::ss_currentRelation, PlanState::state, strVal, table, PgFdwScanState::temp_cxt, PgFdwScanState::tupdesc, TupleDescGetAttInMetadata(), and user.

Referenced by postgres_fdw_handler().

◆ postgresEndDirectModify()

static void postgresEndDirectModify ( ForeignScanState node)
static

Definition at line 2901 of file postgres_fdw.c.

2902{
2904
2905 /* if dmstate is NULL, we are in EXPLAIN; nothing to do */
2906 if (dmstate == NULL)
2907 return;
2908
2909 /* Release PGresult */
2910 PQclear(dmstate->result);
2911
2912 /* Release remote connection */
2914 dmstate->conn = NULL;
2915
2916 /* MemoryContext will be deleted automatically. */
2917}

References fb(), ForeignScanState::fdw_state, PQclear, and ReleaseConnection().

Referenced by postgres_fdw_handler().

◆ postgresEndForeignInsert()

static void postgresEndForeignInsert ( EState estate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2382 of file postgres_fdw.c.

2384{
2386
2387 Assert(fmstate != NULL);
2388
2389 /*
2390 * If the fmstate has aux_fmstate set, get the aux_fmstate (see
2391 * postgresBeginForeignInsert())
2392 */
2393 if (fmstate->aux_fmstate)
2394 fmstate = fmstate->aux_fmstate;
2395
2396 /* Destroy the execution state */
2398}
static void finish_foreign_modify(PgFdwModifyState *fmstate)

References Assert, fb(), finish_foreign_modify(), and ResultRelInfo::ri_FdwState.

Referenced by postgres_fdw_handler().

◆ postgresEndForeignModify()

static void postgresEndForeignModify ( EState estate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 2233 of file postgres_fdw.c.

2235{
2237
2238 /* If fmstate is NULL, we are in EXPLAIN; nothing to do */
2239 if (fmstate == NULL)
2240 return;
2241
2242 /* Destroy the execution state */
2244}

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

Referenced by postgres_fdw_handler().

◆ postgresEndForeignScan()

static void postgresEndForeignScan ( ForeignScanState node)
static

Definition at line 1817 of file postgres_fdw.c.

1818{
1819 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1820
1821 /* if fsstate is NULL, we are in EXPLAIN; nothing to do */
1822 if (fsstate == NULL)
1823 return;
1824
1825 /* Close the cursor if open, to prevent accumulation of cursors */
1826 if (fsstate->cursor_exists)
1827 close_cursor(fsstate->conn, fsstate->cursor_number,
1828 fsstate->conn_state);
1829
1830 /* Release remote connection */
1831 ReleaseConnection(fsstate->conn);
1832 fsstate->conn = NULL;
1833
1834 /* MemoryContexts will be deleted automatically. */
1835}

References close_cursor(), PgFdwScanState::conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, fb(), ForeignScanState::fdw_state, 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 2095 of file postgres_fdw.c.

2100{
2103
2104 /*
2105 * If the fmstate has aux_fmstate set, use the aux_fmstate (see
2106 * postgresBeginForeignInsert())
2107 */
2108 if (fmstate->aux_fmstate)
2109 resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2110 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2111 slots, planSlots, numSlots);
2112 /* Revert that change */
2113 if (fmstate->aux_fmstate)
2114 resultRelInfo->ri_FdwState = fmstate;
2115
2116 return rslot;
2117}
static TupleTableSlot ** execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)

References CMD_INSERT, execute_foreign_modify(), fb(), 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 2214 of file postgres_fdw.c.

2218{
2220 int numSlots = 1;
2221
2222 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_DELETE,
2223 &slot, &planSlot, &numSlots);
2224
2225 return rslot ? rslot[0] : NULL;
2226}

References CMD_DELETE, execute_foreign_modify(), and fb().

Referenced by postgres_fdw_handler().

◆ postgresExecForeignInsert()

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

Definition at line 2066 of file postgres_fdw.c.

2070{
2073 int numSlots = 1;
2074
2075 /*
2076 * If the fmstate has aux_fmstate set, use the aux_fmstate (see
2077 * postgresBeginForeignInsert())
2078 */
2079 if (fmstate->aux_fmstate)
2080 resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2081 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2082 &slot, &planSlot, &numSlots);
2083 /* Revert that change */
2084 if (fmstate->aux_fmstate)
2085 resultRelInfo->ri_FdwState = fmstate;
2086
2087 return rslot ? *rslot : NULL;
2088}

References CMD_INSERT, execute_foreign_modify(), fb(), and ResultRelInfo::ri_FdwState.

Referenced by postgres_fdw_handler().

◆ postgresExecForeignTruncate()

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

Definition at line 3077 of file postgres_fdw.c.

3080{
3081 Oid serverid = InvalidOid;
3083 PGconn *conn = NULL;
3084 StringInfoData sql;
3085 ListCell *lc;
3086 bool server_truncatable = true;
3087
3088 /*
3089 * By default, all postgres_fdw foreign tables are assumed truncatable.
3090 * This can be overridden by a per-server setting, which in turn can be
3091 * overridden by a per-table setting.
3092 */
3093 foreach(lc, rels)
3094 {
3095 ForeignServer *server = NULL;
3096 Relation rel = lfirst(lc);
3098 ListCell *cell;
3099 bool truncatable;
3100
3101 /*
3102 * First time through, determine whether the foreign server allows
3103 * truncates. Since all specified foreign tables are assumed to belong
3104 * to the same foreign server, this result can be used for other
3105 * foreign tables.
3106 */
3107 if (!OidIsValid(serverid))
3108 {
3109 serverid = table->serverid;
3110 server = GetForeignServer(serverid);
3111
3112 foreach(cell, server->options)
3113 {
3114 DefElem *defel = (DefElem *) lfirst(cell);
3115
3116 if (strcmp(defel->defname, "truncatable") == 0)
3117 {
3119 break;
3120 }
3121 }
3122 }
3123
3124 /*
3125 * Confirm that all specified foreign tables belong to the same
3126 * foreign server.
3127 */
3128 Assert(table->serverid == serverid);
3129
3130 /* Determine whether this foreign table allows truncations */
3132 foreach(cell, table->options)
3133 {
3134 DefElem *defel = (DefElem *) lfirst(cell);
3135
3136 if (strcmp(defel->defname, "truncatable") == 0)
3137 {
3139 break;
3140 }
3141 }
3142
3143 if (!truncatable)
3144 ereport(ERROR,
3146 errmsg("foreign table \"%s\" does not allow truncates",
3148 }
3149 Assert(OidIsValid(serverid));
3150
3151 /*
3152 * Get connection to the foreign server. Connection manager will
3153 * establish new connection if necessary.
3154 */
3155 user = GetUserMapping(GetUserId(), serverid);
3156 conn = GetConnection(user, false, NULL);
3157
3158 /* Construct the TRUNCATE command string */
3159 initStringInfo(&sql);
3160 deparseTruncateSql(&sql, rels, behavior, restart_seqs);
3161
3162 /* Issue the TRUNCATE command to remote server */
3163 do_sql_command(conn, sql.data);
3164
3165 pfree(sql.data);
3166}
void do_sql_command(PGconn *conn, const char *sql)
Definition connection.c:849
void deparseTruncateSql(StringInfo buf, List *rels, DropBehavior behavior, bool restart_seqs)
Definition deparse.c:2697

References Assert, conn, StringInfoData::data, defGetBoolean(), deparseTruncateSql(), do_sql_command(), ereport, errcode(), errmsg, ERROR, fb(), 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 2195 of file postgres_fdw.c.

2199{
2201 int numSlots = 1;
2202
2203 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_UPDATE,
2204 &slot, &planSlot, &numSlots);
2205
2206 return rslot ? rslot[0] : NULL;
2207}

References CMD_UPDATE, execute_foreign_modify(), and fb().

Referenced by postgres_fdw_handler().

◆ postgresExplainDirectModify()

static void postgresExplainDirectModify ( ForeignScanState node,
ExplainState es 
)
static

Definition at line 3059 of file postgres_fdw.c.

3060{
3061 List *fdw_private;
3062 char *sql;
3063
3064 if (es->verbose)
3065 {
3066 fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
3067 sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql));
3068 ExplainPropertyText("Remote SQL", sql, es);
3069 }
3070}
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 3031 of file postgres_fdw.c.

3036{
3037 if (es->verbose)
3038 {
3039 char *sql = strVal(list_nth(fdw_private,
3041
3042 ExplainPropertyText("Remote SQL", sql, es);
3043
3044 /*
3045 * For INSERT we should always have batch size >= 1, but UPDATE and
3046 * DELETE don't support batching so don't show the property.
3047 */
3048 if (rinfo->ri_BatchSize > 0)
3049 ExplainPropertyInteger("Batch Size", NULL, rinfo->ri_BatchSize, es);
3050 }
3051}
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)

References ExplainPropertyInteger(), ExplainPropertyText(), fb(), 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 2924 of file postgres_fdw.c.

2925{
2927 List *fdw_private = plan->fdw_private;
2928
2929 /*
2930 * Identify foreign scans that are really joins or upper relations. The
2931 * input looks something like "(1) LEFT JOIN (2)", and we must replace the
2932 * digit string(s), which are RT indexes, with the correct relation names.
2933 * We do that here, not when the plan is created, because we can't know
2934 * what aliases ruleutils.c will assign at plan creation time.
2935 */
2936 if (list_length(fdw_private) > FdwScanPrivateRelations)
2937 {
2938 StringInfoData relations;
2939 char *rawrelations;
2940 char *ptr;
2941 int minrti,
2942 rtoffset;
2943
2945
2946 /*
2947 * A difficulty with using a string representation of RT indexes is
2948 * that setrefs.c won't update the string when flattening the
2949 * rangetable. To find out what rtoffset was applied, identify the
2950 * minimum RT index appearing in the string and compare it to the
2951 * minimum member of plan->fs_base_relids. (We expect all the relids
2952 * in the join will have been offset by the same amount; the Asserts
2953 * below should catch it if that ever changes.)
2954 */
2955 minrti = INT_MAX;
2956 ptr = rawrelations;
2957 while (*ptr)
2958 {
2959 if (isdigit((unsigned char) *ptr))
2960 {
2961 int rti = strtol(ptr, &ptr, 10);
2962
2963 if (rti < minrti)
2964 minrti = rti;
2965 }
2966 else
2967 ptr++;
2968 }
2969 rtoffset = bms_next_member(plan->fs_base_relids, -1) - minrti;
2970
2971 /* Now we can translate the string */
2972 initStringInfo(&relations);
2973 ptr = rawrelations;
2974 while (*ptr)
2975 {
2976 if (isdigit((unsigned char) *ptr))
2977 {
2978 int rti = strtol(ptr, &ptr, 10);
2980 char *relname;
2981 char *refname;
2982
2983 rti += rtoffset;
2984 Assert(bms_is_member(rti, plan->fs_base_relids));
2985 rte = rt_fetch(rti, es->rtable);
2986 Assert(rte->rtekind == RTE_RELATION);
2987 /* This logic should agree with explain.c's ExplainTargetRel */
2988 relname = get_rel_name(rte->relid);
2989 if (es->verbose)
2990 {
2991 char *namespace;
2992
2994 appendStringInfo(&relations, "%s.%s",
2995 quote_identifier(namespace),
2997 }
2998 else
2999 appendStringInfoString(&relations,
3001 refname = (char *) list_nth(es->rtable_names, rti - 1);
3002 if (refname == NULL)
3003 refname = rte->eref->aliasname;
3004 if (strcmp(refname, relname) != 0)
3005 appendStringInfo(&relations, " %s",
3006 quote_identifier(refname));
3007 }
3008 else
3009 appendStringInfoChar(&relations, *ptr++);
3010 }
3011 ExplainPropertyText("Relations", relations.data, es);
3012 }
3013
3014 /*
3015 * Add remote query, when VERBOSE option is specified.
3016 */
3017 if (es->verbose)
3018 {
3019 char *sql;
3020
3021 sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
3022 ExplainPropertyText("Remote SQL", sql, es);
3023 }
3024}
char * get_rel_name(Oid relid)
Definition lsyscache.c:2242
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2266
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3706
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
const char * quote_identifier(const char *ident)
List * rtable_names

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, bms_is_member(), bms_next_member(), castNode, StringInfoData::data, ExplainPropertyText(), fb(), 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, ForeignScanState::ss, strVal, and ExplainState::verbose.

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncConfigureWait()

static void postgresForeignAsyncConfigureWait ( AsyncRequest areq)
static

Definition at line 8091 of file postgres_fdw.c.

8092{
8093 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8094 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8095 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
8096 AppendState *requestor = (AppendState *) areq->requestor;
8097 WaitEventSet *set = requestor->as_eventset;
8098
8099 /* This should not be called unless callback_pending */
8100 Assert(areq->callback_pending);
8101
8102 /*
8103 * If process_pending_request() has been invoked on the given request
8104 * before we get here, we might have some tuples already; in which case
8105 * complete the request
8106 */
8107 if (fsstate->next_tuple < fsstate->num_tuples)
8108 {
8110 if (areq->request_complete)
8111 return;
8112 Assert(areq->callback_pending);
8113 }
8114
8115 /* We must have run out of tuples */
8116 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8117
8118 /* The core code would have registered postmaster death event */
8120
8121 /* Begin an asynchronous data fetch if not already done */
8122 if (!pendingAreq)
8124 else if (pendingAreq->requestor != areq->requestor)
8125 {
8126 /*
8127 * This is the case when the in-process request was made by another
8128 * Append. Note that it might be useless to process the request made
8129 * by that Append, because the query might not need tuples from that
8130 * Append anymore; so we avoid processing it to begin a fetch for the
8131 * given request if possible. If there are any child subplans of the
8132 * same parent that are ready for new requests, skip the given
8133 * request. Likewise, if there are any configured events other than
8134 * the postmaster death event, skip it. Otherwise, process the
8135 * in-process request, then begin a fetch to configure the event
8136 * below, because we might otherwise end up with no configured events
8137 * other than the postmaster death event.
8138 */
8139 if (!bms_is_empty(requestor->as_needrequest))
8140 return;
8141 if (GetNumRegisteredWaitEvents(set) > 1)
8142 return;
8143 process_pending_request(pendingAreq);
8145 }
8146 else if (pendingAreq->requestee != areq->requestee)
8147 {
8148 /*
8149 * This is the case when the in-process request was made by the same
8150 * parent but for a different child. Since we configure only the
8151 * event for the request made for that child, skip the given request.
8152 */
8153 return;
8154 }
8155 else
8156 Assert(pendingAreq == areq);
8157
8159 NULL, areq);
8160}
int PQsocket(const PGconn *conn)
static void fetch_more_data_begin(AsyncRequest *areq)
static void complete_pending_request(AsyncRequest *areq)
Bitmapset * as_needrequest
Definition execnodes.h:1550
struct WaitEventSet * as_eventset
Definition execnodes.h:1551
PlanState * requestor
Definition execnodes.h:676
PlanState * requestee
Definition execnodes.h:677
int GetNumRegisteredWaitEvents(WaitEventSet *set)
int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch, void *user_data)
#define WL_SOCKET_READABLE

References AddWaitEventToSet(), AppendState::as_eventset, AppendState::as_needrequest, Assert, bms_is_empty, complete_pending_request(), PgFdwScanState::conn, PgFdwScanState::conn_state, fb(), ForeignScanState::fdw_state, fetch_more_data_begin(), GetNumRegisteredWaitEvents(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, PgFdwConnState::pendingAreq, PQsocket(), process_pending_request(), AsyncRequest::requestee, AsyncRequest::requestor, and WL_SOCKET_READABLE.

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncNotify()

static void postgresForeignAsyncNotify ( AsyncRequest areq)
static

Definition at line 8168 of file postgres_fdw.c.

8169{
8170 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8171 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8172
8173 /* The core code would have initialized the callback_pending flag */
8174 Assert(!areq->callback_pending);
8175
8176 /*
8177 * If process_pending_request() has been invoked on the given request
8178 * before we get here, we might have some tuples already; in which case
8179 * produce the next tuple
8180 */
8181 if (fsstate->next_tuple < fsstate->num_tuples)
8182 {
8184 return;
8185 }
8186
8187 /* We must have run out of tuples */
8188 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8189
8190 /* The request should be currently in-process */
8191 Assert(fsstate->conn_state->pendingAreq == areq);
8192
8193 /* On error, report the original query, not the FETCH. */
8194 if (!PQconsumeInput(fsstate->conn))
8195 pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
8196
8197 fetch_more_data(node);
8198
8200}
int PQconsumeInput(PGconn *conn)
Definition fe-exec.c:2001
static void fetch_more_data(ForeignScanState *node)

References Assert, PgFdwScanState::conn, PgFdwScanState::conn_state, fb(), ForeignScanState::fdw_state, fetch_more_data(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, PgFdwConnState::pendingAreq, pgfdw_report_error(), PQconsumeInput(), produce_tuple_asynchronously(), and PgFdwScanState::query.

Referenced by postgres_fdw_handler().

◆ postgresForeignAsyncRequest()

static void postgresForeignAsyncRequest ( AsyncRequest areq)
static

Definition at line 8081 of file postgres_fdw.c.

8082{
8084}

References fb(), and produce_tuple_asynchronously().

Referenced by postgres_fdw_handler().

◆ postgresGetAnalyzeInfoForForeignTable()

static double postgresGetAnalyzeInfoForForeignTable ( Relation  relation,
bool can_tablesample 
)
static

Definition at line 5040 of file postgres_fdw.c.

5041{
5044 PGconn *conn;
5045 StringInfoData sql;
5046 PGresult *res;
5047 double reltuples;
5048 char relkind;
5049
5050 /* assume the remote relation does not support TABLESAMPLE */
5051 *can_tablesample = false;
5052
5053 /*
5054 * Get the connection to use. We do the remote access as the table's
5055 * owner, even if the ANALYZE was started by some other user.
5056 */
5058 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5059 conn = GetConnection(user, false, NULL);
5060
5061 /*
5062 * Construct command to get page count for relation.
5063 */
5064 initStringInfo(&sql);
5065 deparseAnalyzeInfoSql(&sql, relation);
5066
5067 res = pgfdw_exec_query(conn, sql.data, NULL);
5068 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5069 pgfdw_report_error(res, conn, sql.data);
5070
5071 if (PQntuples(res) != 1 || PQnfields(res) != RELSTATS_NUM_FIELDS)
5072 elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
5073 /* We don't use relpages here */
5074 reltuples = strtod(PQgetvalue(res, 0, RELSTATS_RELTUPLES), NULL);
5075 relkind = *(PQgetvalue(res, 0, RELSTATS_RELKIND));
5076 PQclear(res);
5077
5079
5080 /* TABLESAMPLE is supported only for regular tables and matviews */
5081 *can_tablesample = (relkind == RELKIND_RELATION ||
5082 relkind == RELKIND_MATVIEW ||
5083 relkind == RELKIND_PARTITIONED_TABLE);
5084
5085 return reltuples;
5086}

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

7149{
7152 double rows;
7153 int width;
7154 int disabled_nodes;
7155 Cost startup_cost;
7156 Cost total_cost;
7157 Path *epq_path; /* Path to create plan to be executed when
7158 * EvalPlanQual gets triggered. */
7159
7160 /*
7161 * Skip if this join combination has been considered already.
7162 */
7163 if (joinrel->fdw_private)
7164 return;
7165
7166 /*
7167 * This code does not work for joins with lateral references, since those
7168 * must have parameterized paths, which we don't generate yet.
7169 */
7170 if (!bms_is_empty(joinrel->lateral_relids))
7171 return;
7172
7173 /*
7174 * Create unfinished PgFdwRelationInfo entry which is used to indicate
7175 * that the join relation is already considered, so that we won't waste
7176 * time in judging safety of join pushdown and adding the same paths again
7177 * if found safe. Once we know that this join can be pushed down, we fill
7178 * the entry.
7179 */
7181 fpinfo->pushdown_safe = false;
7182 joinrel->fdw_private = fpinfo;
7183 /* attrs_used is only for base relations. */
7184 fpinfo->attrs_used = NULL;
7185
7186 /*
7187 * If there is a possibility that EvalPlanQual will be executed, we need
7188 * to be able to reconstruct the row using scans of the base relations.
7189 * GetExistingLocalJoinPath will find a suitable path for this purpose in
7190 * the path list of the joinrel, if one exists. We must be careful to
7191 * call it before adding any ForeignPath, since the ForeignPath might
7192 * dominate the only suitable local path available. We also do it before
7193 * calling foreign_join_ok(), since that function updates fpinfo and marks
7194 * it as pushable if the join is found to be pushable.
7195 */
7196 if (root->parse->commandType == CMD_DELETE ||
7197 root->parse->commandType == CMD_UPDATE ||
7198 root->rowMarks)
7199 {
7201 if (!epq_path)
7202 {
7203 elog(DEBUG3, "could not push down foreign join because a local path suitable for EPQ checks was not found");
7204 return;
7205 }
7206 }
7207 else
7208 epq_path = NULL;
7209
7210 if (!foreign_join_ok(root, joinrel, jointype, outerrel, innerrel, extra))
7211 {
7212 /* Free path required for EPQ if we copied one; we don't need it now */
7213 if (epq_path)
7214 pfree(epq_path);
7215 return;
7216 }
7217
7218 /*
7219 * Compute the selectivity and cost of the local_conds, so we don't have
7220 * to do it over again for each path. The best we can do for these
7221 * conditions is to estimate selectivity on the basis of local statistics.
7222 * The local conditions are applied after the join has been computed on
7223 * the remote side like quals in WHERE clause, so pass jointype as
7224 * JOIN_INNER.
7225 */
7226 fpinfo->local_conds_sel = clauselist_selectivity(root,
7227 fpinfo->local_conds,
7228 0,
7229 JOIN_INNER,
7230 NULL);
7231 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
7232
7233 /*
7234 * If we are going to estimate costs locally, estimate the join clause
7235 * selectivity here while we have special join info.
7236 */
7237 if (!fpinfo->use_remote_estimate)
7238 fpinfo->joinclause_sel = clauselist_selectivity(root, fpinfo->joinclauses,
7239 0, fpinfo->jointype,
7240 extra->sjinfo);
7241
7242 /* Estimate costs for bare join relation */
7244 &rows, &width, &disabled_nodes,
7245 &startup_cost, &total_cost);
7246 /* Now update this information in the joinrel */
7247 joinrel->rows = rows;
7248 joinrel->reltarget->width = width;
7249 fpinfo->rows = rows;
7250 fpinfo->width = width;
7251 fpinfo->disabled_nodes = disabled_nodes;
7252 fpinfo->startup_cost = startup_cost;
7253 fpinfo->total_cost = total_cost;
7254
7255 /*
7256 * Create a new join path and add it to the joinrel which represents a
7257 * join between foreign tables.
7258 */
7260 joinrel,
7261 NULL, /* default pathtarget */
7262 rows,
7263 disabled_nodes,
7264 startup_cost,
7265 total_cost,
7266 NIL, /* no pathkeys */
7267 joinrel->lateral_relids,
7268 epq_path,
7269 extra->restrictlist,
7270 NIL); /* no fdw_private */
7271
7272 /* Add generated path into joinrel by add_path(). */
7273 add_path(joinrel, (Path *) joinpath);
7274
7275 /* Consider pathkeys for the join relation */
7277 extra->restrictlist);
7278
7279 /* XXX Consider parameterized paths for the join relation */
7280}
#define DEBUG3
Definition elog.h:29
Path * GetExistingLocalJoinPath(RelOptInfo *joinrel)
Definition foreign.c:773
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:3623

References add_path(), add_paths_with_pathkeys_for_rel(), bms_is_empty, clauselist_selectivity(), CMD_DELETE, CMD_UPDATE, cost_qual_eval(), create_foreign_join_path(), DEBUG3, elog, estimate_path_cost_size(), fb(), foreign_join_ok(), GetExistingLocalJoinPath(), JOIN_INNER, RelOptInfo::lateral_relids, NIL, palloc0_object, pfree(), RelOptInfo::reltarget, JoinPathExtraData::restrictlist, root, RelOptInfo::rows, JoinPathExtraData::sjinfo, and PathTarget::width.

Referenced by postgres_fdw_handler().

◆ postgresGetForeignModifyBatchSize()

static int postgresGetForeignModifyBatchSize ( ResultRelInfo resultRelInfo)
static

Definition at line 2128 of file postgres_fdw.c.

2129{
2130 int batch_size;
2132
2133 /* should be called only once */
2134 Assert(resultRelInfo->ri_BatchSize == 0);
2135
2136 /*
2137 * Should never get called when the insert is being performed on a table
2138 * that is also among the target relations of an UPDATE operation, because
2139 * postgresBeginForeignInsert() currently rejects such insert attempts.
2140 */
2141 Assert(fmstate == NULL || fmstate->aux_fmstate == NULL);
2142
2143 /*
2144 * In EXPLAIN without ANALYZE, ri_FdwState is NULL, so we have to lookup
2145 * the option directly in server/table options. Otherwise just use the
2146 * value we determined earlier.
2147 */
2148 if (fmstate)
2149 batch_size = fmstate->batch_size;
2150 else
2151 batch_size = get_batch_size_option(resultRelInfo->ri_RelationDesc);
2152
2153 /*
2154 * Disable batching when we have to use RETURNING, there are any
2155 * BEFORE/AFTER ROW INSERT triggers on the foreign table, or there are any
2156 * WITH CHECK OPTION constraints from parent views.
2157 *
2158 * When there are any BEFORE ROW INSERT triggers on the table, we can't
2159 * support it, because such triggers might query the table we're inserting
2160 * into and act differently if the tuples that have already been processed
2161 * and prepared for insertion are not there.
2162 */
2163 if (resultRelInfo->ri_projectReturning != NULL ||
2164 resultRelInfo->ri_WithCheckOptions != NIL ||
2165 (resultRelInfo->ri_TrigDesc &&
2166 (resultRelInfo->ri_TrigDesc->trig_insert_before_row ||
2167 resultRelInfo->ri_TrigDesc->trig_insert_after_row)))
2168 return 1;
2169
2170 /*
2171 * If the foreign table has no columns, disable batching as the INSERT
2172 * syntax doesn't allow batching multiple empty rows into a zero-column
2173 * table in a single statement. This is needed for COPY FROM, in which
2174 * case fmstate must be non-NULL.
2175 */
2176 if (fmstate && list_length(fmstate->target_attrs) == 0)
2177 return 1;
2178
2179 /*
2180 * Otherwise use the batch size specified for server/table. The number of
2181 * parameters in a batch is limited to 65535 (uint16), so make sure we
2182 * don't exceed this limit by using the maximum batch_size possible.
2183 */
2184 if (fmstate && fmstate->p_nums > 0)
2185 batch_size = Min(batch_size, PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);
2186
2187 return batch_size;
2188}
#define PQ_QUERY_PARAM_MAX_LIMIT
Definition libpq-fe.h:524
TriggerDesc * ri_TrigDesc
Definition execnodes.h:549
bool trig_insert_after_row
Definition reltrigger.h:57
bool trig_insert_before_row
Definition reltrigger.h:56

References Assert, fb(), get_batch_size_option(), list_length(), Min, NIL, PQ_QUERY_PARAM_MAX_LIMIT, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwState, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, 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 1110 of file postgres_fdw.c.

1113{
1115 ForeignPath *path;
1116 List *ppi_list;
1117 ListCell *lc;
1118
1119 /*
1120 * Create simplest ForeignScan path node and add it to baserel. This path
1121 * corresponds to SeqScan path of regular tables (though depending on what
1122 * baserestrict conditions we were able to send to remote, there might
1123 * actually be an indexscan happening there). We already did all the work
1124 * to estimate cost and size of this path.
1125 *
1126 * Although this path uses no join clauses, it could still have required
1127 * parameterization due to LATERAL refs in its tlist.
1128 */
1130 NULL, /* default pathtarget */
1131 fpinfo->rows,
1132 fpinfo->disabled_nodes,
1133 fpinfo->startup_cost,
1134 fpinfo->total_cost,
1135 NIL, /* no pathkeys */
1136 baserel->lateral_relids,
1137 NULL, /* no extra plan */
1138 NIL, /* no fdw_restrictinfo list */
1139 NIL); /* no fdw_private list */
1140 add_path(baserel, (Path *) path);
1141
1142 /* Add paths with pathkeys */
1144
1145 /*
1146 * If we're not using remote estimates, stop here. We have no way to
1147 * estimate whether any join clauses would be worth sending across, so
1148 * don't bother building parameterized paths.
1149 */
1150 if (!fpinfo->use_remote_estimate)
1151 return;
1152
1153 /*
1154 * Thumb through all join clauses for the rel to identify which outer
1155 * relations could supply one or more safe-to-send-to-remote join clauses.
1156 * We'll build a parameterized path for each such outer relation.
1157 *
1158 * It's convenient to manage this by representing each candidate outer
1159 * relation by the ParamPathInfo node for it. We can then use the
1160 * ppi_clauses list in the ParamPathInfo node directly as a list of the
1161 * interesting join clauses for that rel. This takes care of the
1162 * possibility that there are multiple safe join clauses for such a rel,
1163 * and also ensures that we account for unsafe join clauses that we'll
1164 * still have to enforce locally (since the parameterized-path machinery
1165 * insists that we handle all movable clauses).
1166 */
1167 ppi_list = NIL;
1168 foreach(lc, baserel->joininfo)
1169 {
1170 RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
1173
1174 /* Check if clause can be moved to this rel */
1176 continue;
1177
1178 /* See if it is safe to send to remote */
1179 if (!is_foreign_expr(root, baserel, rinfo->clause))
1180 continue;
1181
1182 /* Calculate required outer rels for the resulting path */
1183 required_outer = bms_union(rinfo->clause_relids,
1184 baserel->lateral_relids);
1185 /* We do not want the foreign rel itself listed in required_outer */
1187
1188 /*
1189 * required_outer probably can't be empty here, but if it were, we
1190 * couldn't make a parameterized path.
1191 */
1193 continue;
1194
1195 /* Get the ParamPathInfo */
1199
1200 /*
1201 * Add it to list unless we already have it. Testing pointer equality
1202 * is OK since get_baserel_parampathinfo won't make duplicates.
1203 */
1205 }
1206
1207 /*
1208 * The above scan examined only "generic" join clauses, not those that
1209 * were absorbed into EquivalenceClauses. See if we can make anything out
1210 * of EquivalenceClauses.
1211 */
1212 if (baserel->has_eclass_joins)
1213 {
1214 /*
1215 * We repeatedly scan the eclass list looking for column references
1216 * (or expressions) belonging to the foreign rel. Each time we find
1217 * one, we generate a list of equivalence joinclauses for it, and then
1218 * see if any are safe to send to the remote. Repeat till there are
1219 * no more candidate EC members.
1220 */
1222
1224 for (;;)
1225 {
1226 List *clauses;
1227
1228 /* Make clauses, skipping any that join to lateral_referencers */
1229 arg.current = NULL;
1231 baserel,
1233 &arg,
1234 baserel->lateral_referencers);
1235
1236 /* Done if there are no more expressions in the foreign rel */
1237 if (arg.current == NULL)
1238 {
1239 Assert(clauses == NIL);
1240 break;
1241 }
1242
1243 /* Scan the extracted join clauses */
1244 foreach(lc, clauses)
1245 {
1246 RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
1249
1250 /* Check if clause can be moved to this rel */
1252 continue;
1253
1254 /* See if it is safe to send to remote */
1255 if (!is_foreign_expr(root, baserel, rinfo->clause))
1256 continue;
1257
1258 /* Calculate required outer rels for the resulting path */
1259 required_outer = bms_union(rinfo->clause_relids,
1260 baserel->lateral_relids);
1263 continue;
1264
1265 /* Get the ParamPathInfo */
1269
1270 /* Add it to list unless we already have it */
1272 }
1273
1274 /* Try again, now ignoring the expression we found this time */
1275 arg.already_used = lappend(arg.already_used, arg.current);
1276 }
1277 }
1278
1279 /*
1280 * Now build a path for each useful outer relation.
1281 */
1282 foreach(lc, ppi_list)
1283 {
1285 double rows;
1286 int width;
1287 int disabled_nodes;
1288 Cost startup_cost;
1289 Cost total_cost;
1290
1291 /* Get a cost estimate from the remote */
1293 param_info->ppi_clauses, NIL, NULL,
1294 &rows, &width, &disabled_nodes,
1295 &startup_cost, &total_cost);
1296
1297 /*
1298 * ppi_rows currently won't get looked at by anything, but still we
1299 * may as well ensure that it matches our idea of the rowcount.
1300 */
1301 param_info->ppi_rows = rows;
1302
1303 /* Make the path */
1305 NULL, /* default pathtarget */
1306 rows,
1307 disabled_nodes,
1308 startup_cost,
1309 total_cost,
1310 NIL, /* no pathkeys */
1311 param_info->ppi_req_outer,
1312 NULL,
1313 NIL, /* no fdw_restrictinfo list */
1314 NIL); /* no fdw_private list */
1315 add_path(baserel, (Path *) path);
1316 }
1317}
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition bitmapset.c:987
List * generate_implied_equalities_for_column(PlannerInfo *root, RelOptInfo *rel, ec_matches_callback_type callback, void *callback_arg, Relids prohibited_rels)
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:1704
bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel)

References add_path(), add_paths_with_pathkeys_for_rel(), ec_member_foreign_arg::already_used, arg, Assert, bms_del_member(), bms_is_empty, bms_union(), RestrictInfo::clause, create_foreignscan_path(), ec_member_matches_foreign(), estimate_path_cost_size(), fb(), generate_implied_equalities_for_column(), get_baserel_parampathinfo(), is_foreign_expr(), join_clause_is_movable_to(), lappend(), lfirst, list_append_unique_ptr(), NIL, and root.

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

1331{
1332 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
1334 List *fdw_private;
1336 List *local_exprs = NIL;
1337 List *params_list = NIL;
1338 List *fdw_scan_tlist = NIL;
1339 List *fdw_recheck_quals = NIL;
1340 List *retrieved_attrs;
1341 StringInfoData sql;
1342 bool has_final_sort = false;
1343 bool has_limit = false;
1344 ListCell *lc;
1345
1346 /*
1347 * Get FDW private data created by postgresGetForeignUpperPaths(), if any.
1348 */
1349 if (best_path->fdw_private)
1350 {
1351 has_final_sort = boolVal(list_nth(best_path->fdw_private,
1353 has_limit = boolVal(list_nth(best_path->fdw_private,
1355 }
1356
1357 if (IS_SIMPLE_REL(foreignrel))
1358 {
1359 /*
1360 * For base relations, set scan_relid as the relid of the relation.
1361 */
1362 scan_relid = foreignrel->relid;
1363
1364 /*
1365 * In a base-relation scan, we must apply the given scan_clauses.
1366 *
1367 * Separate the scan_clauses into those that can be executed remotely
1368 * and those that can't. baserestrictinfo clauses that were
1369 * previously determined to be safe or unsafe by classifyConditions
1370 * are found in fpinfo->remote_conds and fpinfo->local_conds. Anything
1371 * else in the scan_clauses list will be a join clause, which we have
1372 * to check for remote-safety.
1373 *
1374 * Note: the join clauses we see here should be the exact same ones
1375 * previously examined by postgresGetForeignPaths. Possibly it'd be
1376 * worth passing forward the classification work done then, rather
1377 * than repeating it here.
1378 *
1379 * This code must match "extract_actual_clauses(scan_clauses, false)"
1380 * except for the additional decision about remote versus local
1381 * execution.
1382 */
1383 foreach(lc, scan_clauses)
1384 {
1386
1387 /* Ignore any pseudoconstants, they're dealt with elsewhere */
1388 if (rinfo->pseudoconstant)
1389 continue;
1390
1391 if (list_member_ptr(fpinfo->remote_conds, rinfo))
1393 else if (list_member_ptr(fpinfo->local_conds, rinfo))
1395 else if (is_foreign_expr(root, foreignrel, rinfo->clause))
1397 else
1399 }
1400
1401 /*
1402 * For a base-relation scan, we have to support EPQ recheck, which
1403 * should recheck all the remote quals.
1404 */
1405 fdw_recheck_quals = remote_exprs;
1406 }
1407 else
1408 {
1409 /*
1410 * Join relation or upper relation - set scan_relid to 0.
1411 */
1412 scan_relid = 0;
1413
1414 /*
1415 * For a join rel, baserestrictinfo is NIL and we are not considering
1416 * parameterization right now, so there should be no scan_clauses for
1417 * a joinrel or an upper rel either.
1418 */
1420
1421 /*
1422 * Instead we get the conditions to apply from the fdw_private
1423 * structure.
1424 */
1425 remote_exprs = extract_actual_clauses(fpinfo->remote_conds, false);
1426 local_exprs = extract_actual_clauses(fpinfo->local_conds, false);
1427
1428 /*
1429 * We leave fdw_recheck_quals empty in this case, since we never need
1430 * to apply EPQ recheck clauses. In the case of a joinrel, EPQ
1431 * recheck is handled elsewhere --- see postgresGetForeignJoinPaths().
1432 * If we're planning an upperrel (ie, remote grouping or aggregation)
1433 * then there's no EPQ to do because SELECT FOR UPDATE wouldn't be
1434 * allowed, and indeed we *can't* put the remote clauses into
1435 * fdw_recheck_quals because the unaggregated Vars won't be available
1436 * locally.
1437 */
1438
1439 /* Build the list of columns to be fetched from the foreign server. */
1440 fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
1441
1442 /*
1443 * Ensure that the outer plan produces a tuple whose descriptor
1444 * matches our scan tuple slot. Also, remove the local conditions
1445 * from outer plan's quals, lest they be evaluated twice, once by the
1446 * local plan and once by the scan.
1447 */
1448 if (outer_plan)
1449 {
1450 /*
1451 * Right now, we only consider grouping and aggregation beyond
1452 * joins. Queries involving aggregates or grouping do not require
1453 * EPQ mechanism, hence should not have an outer plan here.
1454 */
1455 Assert(!IS_UPPER_REL(foreignrel));
1456
1457 /*
1458 * First, update the plan's qual list if possible. In some cases
1459 * the quals might be enforced below the topmost plan level, in
1460 * which case we'll fail to remove them; it's not worth working
1461 * harder than this.
1462 */
1463 foreach(lc, local_exprs)
1464 {
1465 Node *qual = lfirst(lc);
1466
1467 outer_plan->qual = list_delete(outer_plan->qual, qual);
1468
1469 /*
1470 * For an inner join the local conditions of foreign scan plan
1471 * can be part of the joinquals as well. (They might also be
1472 * in the mergequals or hashquals, but we can't touch those
1473 * without breaking the plan.)
1474 */
1475 if (IsA(outer_plan, NestLoop) ||
1476 IsA(outer_plan, MergeJoin) ||
1477 IsA(outer_plan, HashJoin))
1478 {
1479 Join *join_plan = (Join *) outer_plan;
1480
1481 if (join_plan->jointype == JOIN_INNER)
1483 qual);
1484 }
1485 }
1486
1487 /*
1488 * Now fix the subplan's tlist --- this might result in inserting
1489 * a Result node atop the plan tree.
1490 */
1491 outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist,
1492 best_path->path.parallel_safe);
1493 }
1494 }
1495
1496 /*
1497 * Build the query string to be sent for execution, and identify
1498 * expressions to be sent as parameters.
1499 */
1500 initStringInfo(&sql);
1501 deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
1502 remote_exprs, best_path->path.pathkeys,
1503 has_final_sort, has_limit, false,
1504 &retrieved_attrs, &params_list);
1505
1506 /* Remember remote_exprs for possible use by postgresPlanDirectModify */
1507 fpinfo->final_remote_exprs = remote_exprs;
1508
1509 /*
1510 * Build the fdw_private list that will be available to the executor.
1511 * Items in the list must match order in enum FdwScanPrivateIndex.
1512 */
1513 fdw_private = list_make3(makeString(sql.data),
1514 retrieved_attrs,
1515 makeInteger(fpinfo->fetch_size));
1516 if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
1517 fdw_private = lappend(fdw_private,
1518 makeString(fpinfo->relation_name));
1519
1520 /*
1521 * Create the ForeignScan node for the given relation.
1522 *
1523 * Note that the remote parameter expressions are stored in the fdw_exprs
1524 * field of the finished plan node; we can't keep them in private state
1525 * because then they wouldn't be subject to later planner processing.
1526 */
1527 return make_foreignscan(tlist,
1529 scan_relid,
1530 params_list,
1531 fdw_private,
1532 fdw_scan_tlist,
1533 fdw_recheck_quals,
1534 outer_plan);
1535}
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)
Plan * change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe)
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:248
List * extract_actual_clauses(List *restrictinfo_list, bool pseudoconstant)
List * joinqual
Definition plannodes.h:993
List * qual
Definition plannodes.h:237
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, StringInfoData::data, deparseSelectStmtForRel(), extract_actual_clauses(), fb(), FdwPathPrivateHasFinalSort, FdwPathPrivateHasLimit, initStringInfo(), is_foreign_expr(), IS_JOIN_REL, IS_SIMPLE_REL, IS_UPPER_REL, IsA, JOIN_INNER, lappend(), lfirst, lfirst_node, list_delete(), list_make3, list_member_ptr(), list_nth(), make_foreignscan(), makeInteger(), makeString(), NIL, Plan::qual, RelOptInfo::relid, and root.

Referenced by postgres_fdw_handler().

◆ postgresGetForeignRelSize()

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

Definition at line 719 of file postgres_fdw.c.

722{
724 ListCell *lc;
725
726 /*
727 * We use PgFdwRelationInfo to pass various information to subsequent
728 * functions.
729 */
731 baserel->fdw_private = fpinfo;
732
733 /* Base foreign tables need to be pushed down always. */
734 fpinfo->pushdown_safe = true;
735
736 /* Look up foreign-table catalog info. */
738 fpinfo->server = GetForeignServer(fpinfo->table->serverid);
739
740 /*
741 * Extract user-settable option values. Note that per-table settings of
742 * use_remote_estimate, fetch_size and async_capable override per-server
743 * settings of them, respectively.
744 */
745 fpinfo->use_remote_estimate = false;
746 fpinfo->fdw_startup_cost = DEFAULT_FDW_STARTUP_COST;
747 fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
748 fpinfo->shippable_extensions = NIL;
749 fpinfo->fetch_size = 100;
750 fpinfo->async_capable = false;
751
754
755 /*
756 * If the table or the server is configured to use remote estimates,
757 * identify which user to do remote access as during planning. This
758 * should match what ExecCheckPermissions() does. If we fail due to lack
759 * of permissions, the query would have failed at runtime anyway.
760 */
761 if (fpinfo->use_remote_estimate)
762 {
763 Oid userid;
764
765 userid = OidIsValid(baserel->userid) ? baserel->userid : GetUserId();
766 fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid);
767 }
768 else
769 fpinfo->user = NULL;
770
771 /*
772 * Identify which baserestrictinfo clauses can be sent to the remote
773 * server and which can't.
774 */
775 classifyConditions(root, baserel, baserel->baserestrictinfo,
776 &fpinfo->remote_conds, &fpinfo->local_conds);
777
778 /*
779 * Identify which attributes will need to be retrieved from the remote
780 * server. These include all attrs needed for joins or final output, plus
781 * all attrs used in the local_conds. (Note: if we end up using a
782 * parameterized scan, it's possible that some of the join clauses will be
783 * sent to the remote and thus we wouldn't really need to retrieve the
784 * columns used in them. Doesn't seem worth detecting that case though.)
785 */
786 fpinfo->attrs_used = NULL;
787 pull_varattnos((Node *) baserel->reltarget->exprs, baserel->relid,
788 &fpinfo->attrs_used);
789 foreach(lc, fpinfo->local_conds)
790 {
792
793 pull_varattnos((Node *) rinfo->clause, baserel->relid,
794 &fpinfo->attrs_used);
795 }
796
797 /*
798 * Compute the selectivity and cost of the local_conds, so we don't have
799 * to do it over again for each path. The best we can do for these
800 * conditions is to estimate selectivity on the basis of local statistics.
801 */
802 fpinfo->local_conds_sel = clauselist_selectivity(root,
803 fpinfo->local_conds,
804 baserel->relid,
806 NULL);
807
808 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
809
810 /*
811 * Set # of retrieved rows and cached relation costs to some negative
812 * value, so that we can detect when they are set to some sensible values,
813 * during one (usually the first) of the calls to estimate_path_cost_size.
814 */
815 fpinfo->retrieved_rows = -1;
816 fpinfo->rel_startup_cost = -1;
817 fpinfo->rel_total_cost = -1;
818
819 /*
820 * If the table or the server is configured to use remote estimates,
821 * connect to the foreign server and execute EXPLAIN to estimate the
822 * number of rows selected by the restriction clauses, as well as the
823 * average row width. Otherwise, estimate using whatever statistics we
824 * have locally, in a way similar to ordinary tables.
825 */
826 if (fpinfo->use_remote_estimate)
827 {
828 /*
829 * Get cost/size estimates with help of remote server. Save the
830 * values in fpinfo so we don't need to do it again to generate the
831 * basic foreign path.
832 */
834 &fpinfo->rows, &fpinfo->width,
835 &fpinfo->disabled_nodes,
836 &fpinfo->startup_cost, &fpinfo->total_cost);
837
838 /* Report estimated baserel size to planner. */
839 baserel->rows = fpinfo->rows;
840 baserel->reltarget->width = fpinfo->width;
841 }
842 else
843 {
844 /*
845 * If the foreign table has never been ANALYZEd, it will have
846 * reltuples < 0, meaning "unknown". We can't do much if we're not
847 * allowed to consult the remote server, but we can use a hack similar
848 * to plancat.c's treatment of empty relations: use a minimum size
849 * estimate of 10 pages, and divide by the column-datatype-based width
850 * estimate to get the corresponding number of tuples.
851 */
852 if (baserel->tuples < 0)
853 {
854 baserel->pages = 10;
855 baserel->tuples =
856 (10 * BLCKSZ) / (baserel->reltarget->width +
858 }
859
860 /* Estimate baserel size as best we can with local statistics. */
862
863 /* Fill in basically-bogus cost estimates for use later. */
865 &fpinfo->rows, &fpinfo->width,
866 &fpinfo->disabled_nodes,
867 &fpinfo->startup_cost, &fpinfo->total_cost);
868 }
869
870 /*
871 * fpinfo->relation_name gets the numeric rangetable index of the foreign
872 * table RTE. (If this query gets EXPLAIN'd, we'll convert that to a
873 * human-readable string at that time.)
874 */
875 fpinfo->relation_name = psprintf("%u", baserel->relid);
876
877 /* No outer and inner relations. */
878 fpinfo->make_outerrel_subquery = false;
879 fpinfo->make_innerrel_subquery = false;
880 fpinfo->lower_subquery_rels = NULL;
881 fpinfo->hidden_subquery_rels = NULL;
882 /* Set the relation index. */
883 fpinfo->relation_index = baserel->relid;
884}
#define MAXALIGN(LEN)
Definition c.h:955
void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition costsize.c:5516
#define SizeofHeapTupleHeader
static void apply_server_options(PgFdwRelationInfo *fpinfo)
#define DEFAULT_FDW_TUPLE_COST
static void apply_table_options(PgFdwRelationInfo *fpinfo)
#define DEFAULT_FDW_STARTUP_COST
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition var.c:296

References apply_server_options(), apply_table_options(), classifyConditions(), RestrictInfo::clause, clauselist_selectivity(), cost_qual_eval(), DEFAULT_FDW_STARTUP_COST, DEFAULT_FDW_TUPLE_COST, estimate_path_cost_size(), fb(), GetForeignServer(), GetForeignTable(), GetUserId(), GetUserMapping(), JOIN_INNER, lfirst_node, MAXALIGN, NIL, OidIsValid, palloc0_object, psprintf(), pull_varattnos(), root, set_baserel_size_estimates(), and SizeofHeapTupleHeader.

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

7531{
7533
7534 /*
7535 * If input rel is not safe to pushdown, then simply return as we cannot
7536 * perform any post-join operations on the foreign server.
7537 */
7538 if (!input_rel->fdw_private ||
7539 !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
7540 return;
7541
7542 /* Ignore stages we don't support; and skip any duplicate calls. */
7543 if ((stage != UPPERREL_GROUP_AGG &&
7544 stage != UPPERREL_ORDERED &&
7545 stage != UPPERREL_FINAL) ||
7546 output_rel->fdw_private)
7547 return;
7548
7550 fpinfo->pushdown_safe = false;
7551 fpinfo->stage = stage;
7552 output_rel->fdw_private = fpinfo;
7553
7554 switch (stage)
7555 {
7556 case UPPERREL_GROUP_AGG:
7558 (GroupPathExtraData *) extra);
7559 break;
7560 case UPPERREL_ORDERED:
7562 break;
7563 case UPPERREL_FINAL:
7565 (FinalPathExtraData *) extra);
7566 break;
7567 default:
7568 elog(ERROR, "unexpected upper relation: %d", (int) stage);
7569 break;
7570 }
7571}
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, fb(), palloc0_object, root, 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 6253 of file postgres_fdw.c.

6254{
6255 List *commands = NIL;
6256 bool import_collate = true;
6257 bool import_default = false;
6258 bool import_generated = true;
6259 bool import_not_null = true;
6260 ForeignServer *server;
6262 PGconn *conn;
6264 PGresult *res;
6265 int numrows,
6266 i;
6267 ListCell *lc;
6268
6269 /* Parse statement options */
6270 foreach(lc, stmt->options)
6271 {
6272 DefElem *def = (DefElem *) lfirst(lc);
6273
6274 if (strcmp(def->defname, "import_collate") == 0)
6276 else if (strcmp(def->defname, "import_default") == 0)
6278 else if (strcmp(def->defname, "import_generated") == 0)
6280 else if (strcmp(def->defname, "import_not_null") == 0)
6282 else
6283 ereport(ERROR,
6285 errmsg("invalid option \"%s\"", def->defname)));
6286 }
6287
6288 /*
6289 * Get connection to the foreign server. Connection manager will
6290 * establish new connection if necessary.
6291 */
6292 server = GetForeignServer(serverOid);
6294 conn = GetConnection(mapping, false, NULL);
6295
6296 /* Don't attempt to import collation if remote server hasn't got it */
6297 if (PQserverVersion(conn) < 90100)
6298 import_collate = false;
6299
6300 /* Create workspace for strings */
6302
6303 /* Check that the schema really exists */
6304 appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
6305 deparseStringLiteral(&buf, stmt->remote_schema);
6306
6307 res = pgfdw_exec_query(conn, buf.data, NULL);
6308 if (PQresultStatus(res) != PGRES_TUPLES_OK)
6309 pgfdw_report_error(res, conn, buf.data);
6310
6311 if (PQntuples(res) != 1)
6312 ereport(ERROR,
6314 errmsg("schema \"%s\" is not present on foreign server \"%s\"",
6315 stmt->remote_schema, server->servername)));
6316
6317 PQclear(res);
6319
6320 /*
6321 * Fetch all table data from this schema, possibly restricted by EXCEPT or
6322 * LIMIT TO. (We don't actually need to pay any attention to EXCEPT/LIMIT
6323 * TO here, because the core code will filter the statements we return
6324 * according to those lists anyway. But it should save a few cycles to
6325 * not process excluded tables in the first place.)
6326 *
6327 * Import table data for partitions only when they are explicitly
6328 * specified in LIMIT TO clause. Otherwise ignore them and only include
6329 * the definitions of the root partitioned tables to allow access to the
6330 * complete remote data set locally in the schema imported.
6331 *
6332 * Note: because we run the connection with search_path restricted to
6333 * pg_catalog, the format_type() and pg_get_expr() outputs will always
6334 * include a schema name for types/functions in other schemas, which is
6335 * what we want.
6336 */
6338 "SELECT relname, "
6339 " attname, "
6340 " format_type(atttypid, atttypmod), "
6341 " attnotnull, "
6342 " pg_get_expr(adbin, adrelid), ");
6343
6344 /* Generated columns are supported since Postgres 12 */
6345 if (PQserverVersion(conn) >= 120000)
6347 " attgenerated, ");
6348 else
6350 " NULL, ");
6351
6352 if (import_collate)
6354 " collname, "
6355 " collnsp.nspname ");
6356 else
6358 " NULL, NULL ");
6359
6361 "FROM pg_class c "
6362 " JOIN pg_namespace n ON "
6363 " relnamespace = n.oid "
6364 " LEFT JOIN pg_attribute a ON "
6365 " attrelid = c.oid AND attnum > 0 "
6366 " AND NOT attisdropped "
6367 " LEFT JOIN pg_attrdef ad ON "
6368 " adrelid = c.oid AND adnum = attnum ");
6369
6370 if (import_collate)
6372 " LEFT JOIN pg_collation coll ON "
6373 " coll.oid = attcollation "
6374 " LEFT JOIN pg_namespace collnsp ON "
6375 " collnsp.oid = collnamespace ");
6376
6378 "WHERE c.relkind IN ("
6384 " AND n.nspname = ");
6385 deparseStringLiteral(&buf, stmt->remote_schema);
6386
6387 /* Partitions are supported since Postgres 10 */
6388 if (PQserverVersion(conn) >= 100000 &&
6389 stmt->list_type != FDW_IMPORT_SCHEMA_LIMIT_TO)
6390 appendStringInfoString(&buf, " AND NOT c.relispartition ");
6391
6392 /* Apply restrictions for LIMIT TO and EXCEPT */
6393 if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
6394 stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
6395 {
6396 bool first_item = true;
6397
6398 appendStringInfoString(&buf, " AND c.relname ");
6399 if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
6400 appendStringInfoString(&buf, "NOT ");
6401 appendStringInfoString(&buf, "IN (");
6402
6403 /* Append list of table names within IN clause */
6404 foreach(lc, stmt->table_list)
6405 {
6406 RangeVar *rv = (RangeVar *) lfirst(lc);
6407
6408 if (first_item)
6409 first_item = false;
6410 else
6413 }
6415 }
6416
6417 /* Append ORDER BY at the end of query to ensure output ordering */
6418 appendStringInfoString(&buf, " ORDER BY c.relname, a.attnum");
6419
6420 /* Fetch the data */
6421 res = pgfdw_exec_query(conn, buf.data, NULL);
6422 if (PQresultStatus(res) != PGRES_TUPLES_OK)
6423 pgfdw_report_error(res, conn, buf.data);
6424
6425 /* Process results */
6426 numrows = PQntuples(res);
6427 /* note: incrementation of i happens in inner loop's while() test */
6428 for (i = 0; i < numrows;)
6429 {
6430 char *tablename = PQgetvalue(res, i, 0);
6431 bool first_item = true;
6432
6434 appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
6435 quote_identifier(tablename));
6436
6437 /* Scan all rows for this table */
6438 do
6439 {
6440 char *attname;
6441 char *typename;
6442 char *attnotnull;
6443 char *attgenerated;
6444 char *attdefault;
6445 char *collname;
6446 char *collnamespace;
6447
6448 /* If table has no columns, we'll see nulls here */
6449 if (PQgetisnull(res, i, 1))
6450 continue;
6451
6452 attname = PQgetvalue(res, i, 1);
6453 typename = PQgetvalue(res, i, 2);
6454 attnotnull = PQgetvalue(res, i, 3);
6455 attdefault = PQgetisnull(res, i, 4) ? NULL :
6456 PQgetvalue(res, i, 4);
6457 attgenerated = PQgetisnull(res, i, 5) ? NULL :
6458 PQgetvalue(res, i, 5);
6459 collname = PQgetisnull(res, i, 6) ? NULL :
6460 PQgetvalue(res, i, 6);
6461 collnamespace = PQgetisnull(res, i, 7) ? NULL :
6462 PQgetvalue(res, i, 7);
6463
6464 if (first_item)
6465 first_item = false;
6466 else
6467 appendStringInfoString(&buf, ",\n");
6468
6469 /* Print column name and type */
6470 appendStringInfo(&buf, " %s %s",
6472 typename);
6473
6474 /*
6475 * Add column_name option so that renaming the foreign table's
6476 * column doesn't break the association to the underlying column.
6477 */
6478 appendStringInfoString(&buf, " OPTIONS (column_name ");
6481
6482 /* Add COLLATE if needed */
6483 if (import_collate && collname != NULL && collnamespace != NULL)
6484 appendStringInfo(&buf, " COLLATE %s.%s",
6486 quote_identifier(collname));
6487
6488 /* Add DEFAULT if needed */
6489 if (import_default && attdefault != NULL &&
6490 (!attgenerated || !attgenerated[0]))
6491 appendStringInfo(&buf, " DEFAULT %s", attdefault);
6492
6493 /* Add GENERATED if needed */
6494 if (import_generated && attgenerated != NULL &&
6495 attgenerated[0] == ATTRIBUTE_GENERATED_STORED)
6496 {
6499 " GENERATED ALWAYS AS (%s) STORED",
6500 attdefault);
6501 }
6502
6503 /* Add NOT NULL if needed */
6504 if (import_not_null && attnotnull[0] == 't')
6505 appendStringInfoString(&buf, " NOT NULL");
6506 }
6507 while (++i < numrows &&
6508 strcmp(PQgetvalue(res, i, 0), tablename) == 0);
6509
6510 /*
6511 * Add server name and table-level options. We specify remote schema
6512 * and table name as options (the latter to ensure that renaming the
6513 * foreign table doesn't break the association).
6514 */
6515 appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
6516 quote_identifier(server->servername));
6517
6518 appendStringInfoString(&buf, "schema_name ");
6519 deparseStringLiteral(&buf, stmt->remote_schema);
6520 appendStringInfoString(&buf, ", table_name ");
6521 deparseStringLiteral(&buf, tablename);
6522
6524
6525 commands = lappend(commands, pstrdup(buf.data));
6526 }
6527 PQclear(res);
6528
6530
6531 return commands;
6532}
#define CppAsString2(x)
Definition c.h:565
#define stmt
@ FDW_IMPORT_SCHEMA_LIMIT_TO
@ FDW_IMPORT_SCHEMA_EXCEPT
bool attnotnull
void resetStringInfo(StringInfo str)
Definition stringinfo.c:126
char * servername
Definition foreign.h:40
char * relname
Definition primnodes.h:84

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, attname, attnotnull, buf, conn, CppAsString2, defGetBoolean(), DefElem::defname, deparseStringLiteral(), ereport, errcode(), errmsg, ERROR, fb(), 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().

◆ postgresImportForeignStatistics()

static bool postgresImportForeignStatistics ( Relation  relation,
List va_cols,
int  elevel 
)
static

Definition at line 5469 of file postgres_fdw.c.

5470{
5471 const char *schemaname = NULL;
5472 const char *relname = NULL;
5474 ForeignServer *server;
5475 RemoteStatsResults remstats = {.rel = NULL, .att = NULL};
5477 int attrcnt = 0;
5478 TimestampTz starttime = 0;
5479 bool restore_stats = false;
5480 bool ok = false;
5481 ListCell *lc;
5482
5483 schemaname = get_namespace_name(RelationGetNamespace(relation));
5484 relname = RelationGetRelationName(relation);
5486 server = GetForeignServer(table->serverid);
5487
5488 /*
5489 * Check whether the restore_stats option is enabled on the foreign table.
5490 * If not, silently ignore the foreign table.
5491 *
5492 * Server-level options can be overridden by table-level options, so check
5493 * server-level first.
5494 */
5495 foreach(lc, server->options)
5496 {
5497 DefElem *def = (DefElem *) lfirst(lc);
5498
5499 if (strcmp(def->defname, "restore_stats") == 0)
5500 {
5502 break;
5503 }
5504 }
5505 foreach(lc, table->options)
5506 {
5507 DefElem *def = (DefElem *) lfirst(lc);
5508
5509 if (strcmp(def->defname, "restore_stats") == 0)
5510 {
5512 break;
5513 }
5514 }
5515 if (!restore_stats)
5516 return false;
5517
5518 /*
5519 * We don't currently support statistics import for foreign tables with
5520 * extended statistics objects.
5521 */
5522 if (HasRelationExtStatistics(relation))
5523 {
5526 errmsg("cannot import statistics for foreign table \"%s.%s\" --- this foreign table has extended statistics objects",
5527 schemaname, relname));
5528 return false;
5529 }
5530
5531 /*
5532 * OK, let's do it.
5533 */
5534 ereport(elevel,
5535 (errmsg("importing statistics for foreign table \"%s.%s\"",
5536 schemaname, relname)));
5537
5538 starttime = GetCurrentTimestamp();
5539
5540 ok = fetch_remote_statistics(relation, va_cols,
5541 table, schemaname, relname,
5543
5544 if (ok)
5545 ok = import_fetched_statistics(relation, schemaname, relname,
5547
5548 if (ok)
5549 {
5550 pgstat_report_analyze(relation,
5551 remstats.livetuples, remstats.deadtuples,
5552 (va_cols == NIL), starttime);
5553
5554 ereport(elevel,
5555 (errmsg("finished importing statistics for foreign table \"%s.%s\"",
5556 schemaname, relname)));
5557 }
5558
5559 PQclear(remstats.rel);
5560 PQclear(remstats.att);
5562
5563 return ok;
5564}
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1649
int64 TimestampTz
Definition timestamp.h:39
bool HasRelationExtStatistics(Relation onerel)
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
void pgstat_report_analyze(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, bool resetcounter, TimestampTz starttime)
static bool import_fetched_statistics(Relation relation, const char *schemaname, const char *relname, int attrcnt, const RemoteAttributeMapping *remattrmap, RemoteStatsResults *remstats)
static void free_remattrmap(RemoteAttributeMapping *map, int len)
static bool fetch_remote_statistics(Relation relation, List *va_cols, ForeignTable *table, const char *local_schemaname, const char *local_relname, int *p_attrcnt, RemoteAttributeMapping **p_remattrmap, RemoteStatsResults *remstats)
#define RelationGetNamespace(relation)
Definition rel.h:557

References defGetBoolean(), DefElem::defname, ereport, errcode(), errmsg, fb(), fetch_remote_statistics(), free_remattrmap(), get_namespace_name(), GetCurrentTimestamp(), GetForeignServer(), GetForeignTable(), HasRelationExtStatistics(), import_fetched_statistics(), lfirst, NIL, ForeignServer::options, pgstat_report_analyze(), PQclear, RemoteStatsResults::rel, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, relname, table, and WARNING.

Referenced by postgres_fdw_handler().

◆ postgresIsForeignPathAsyncCapable()

static bool postgresIsForeignPathAsyncCapable ( ForeignPath path)
static

Definition at line 8068 of file postgres_fdw.c.

8069{
8070 RelOptInfo *rel = ((Path *) path)->parent;
8071 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
8072
8073 return fpinfo->async_capable;
8074}

References fb().

Referenced by postgres_fdw_handler().

◆ postgresIsForeignRelUpdatable()

static int postgresIsForeignRelUpdatable ( Relation  rel)
static

Definition at line 2406 of file postgres_fdw.c.

2407{
2408 bool updatable;
2410 ForeignServer *server;
2411 ListCell *lc;
2412
2413 /*
2414 * By default, all postgres_fdw foreign tables are assumed updatable. This
2415 * can be overridden by a per-server setting, which in turn can be
2416 * overridden by a per-table setting.
2417 */
2418 updatable = true;
2419
2421 server = GetForeignServer(table->serverid);
2422
2423 foreach(lc, server->options)
2424 {
2425 DefElem *def = (DefElem *) lfirst(lc);
2426
2427 if (strcmp(def->defname, "updatable") == 0)
2428 updatable = defGetBoolean(def);
2429 }
2430 foreach(lc, table->options)
2431 {
2432 DefElem *def = (DefElem *) lfirst(lc);
2433
2434 if (strcmp(def->defname, "updatable") == 0)
2435 updatable = defGetBoolean(def);
2436 }
2437
2438 /*
2439 * Currently "updatable" means support for INSERT, UPDATE and DELETE.
2440 */
2441 return updatable ?
2442 (1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE) : 0;
2443}

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

Referenced by postgres_fdw_handler().

◆ postgresIterateDirectModify()

static TupleTableSlot * postgresIterateDirectModify ( ForeignScanState node)
static

Definition at line 2857 of file postgres_fdw.c.

2858{
2860 EState *estate = node->ss.ps.state;
2861 ResultRelInfo *resultRelInfo = node->resultRelInfo;
2862
2863 /*
2864 * If this is the first call after Begin, execute the statement.
2865 */
2866 if (dmstate->num_tuples == -1)
2867 execute_dml_stmt(node);
2868
2869 /*
2870 * If the local query doesn't specify RETURNING, just clear tuple slot.
2871 */
2872 if (!resultRelInfo->ri_projectReturning)
2873 {
2874 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
2875 NodeInstrumentation *instr = node->ss.ps.instrument;
2876
2877 Assert(!dmstate->has_returning);
2878
2879 /* Increment the command es_processed count if necessary. */
2880 if (dmstate->set_processed)
2881 estate->es_processed += dmstate->num_tuples;
2882
2883 /* Increment the tuple count for EXPLAIN ANALYZE if necessary. */
2884 if (instr)
2885 instr->tuplecount += dmstate->num_tuples;
2886
2887 return ExecClearTuple(slot);
2888 }
2889
2890 /*
2891 * Get the next RETURNING tuple.
2892 */
2893 return get_returning_data(node);
2894}
static void execute_dml_stmt(ForeignScanState *node)
static TupleTableSlot * get_returning_data(ForeignScanState *node)
NodeInstrumentation * instrument
Definition execnodes.h:1212

References Assert, EState::es_processed, ExecClearTuple(), execute_dml_stmt(), fb(), ForeignScanState::fdw_state, get_returning_data(), PlanState::instrument, ScanState::ps, ForeignScanState::resultRelInfo, ResultRelInfo::ri_projectReturning, ForeignScanState::ss, ScanState::ss_ScanTupleSlot, PlanState::state, and NodeInstrumentation::tuplecount.

Referenced by postgres_fdw_handler().

◆ postgresIterateForeignScan()

static TupleTableSlot * postgresIterateForeignScan ( ForeignScanState node)
static

Definition at line 1698 of file postgres_fdw.c.

1699{
1700 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1701 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
1702
1703 /*
1704 * In sync mode, if this is the first call after Begin or ReScan, we need
1705 * to create the cursor on the remote side. In async mode, we would have
1706 * already created the cursor before we get here, even if this is the
1707 * first call after Begin or ReScan.
1708 */
1709 if (!fsstate->cursor_exists)
1710 create_cursor(node);
1711
1712 /*
1713 * Get some more tuples, if we've run out.
1714 */
1715 if (fsstate->next_tuple >= fsstate->num_tuples)
1716 {
1717 /* In async mode, just clear tuple slot. */
1718 if (fsstate->async_capable)
1719 return ExecClearTuple(slot);
1720 /* No point in another fetch if we already detected EOF, though. */
1721 if (!fsstate->eof_reached)
1722 fetch_more_data(node);
1723 /* If we didn't get any tuples, must be end of data. */
1724 if (fsstate->next_tuple >= fsstate->num_tuples)
1725 return ExecClearTuple(slot);
1726 }
1727
1728 /*
1729 * Return the next tuple.
1730 */
1731 ExecStoreHeapTuple(fsstate->tuples[fsstate->next_tuple++],
1732 slot,
1733 false);
1734
1735 return slot;
1736}

References PgFdwScanState::async_capable, create_cursor(), PgFdwScanState::cursor_exists, PgFdwScanState::eof_reached, ExecClearTuple(), ExecStoreHeapTuple(), ForeignScanState::fdw_state, fetch_more_data(), 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 2542 of file postgres_fdw.c.

2546{
2547 CmdType operation = plan->operation;
2548 RelOptInfo *foreignrel;
2551 Relation rel;
2552 StringInfoData sql;
2554 List *processed_tlist = NIL;
2555 List *targetAttrs = NIL;
2557 List *params_list = NIL;
2558 List *returningList = NIL;
2559 List *retrieved_attrs = NIL;
2560
2561 /*
2562 * Decide whether it is safe to modify a foreign table directly.
2563 */
2564
2565 /*
2566 * The table modification must be an UPDATE or DELETE.
2567 */
2569 return false;
2570
2571 /*
2572 * Try to locate the ForeignScan subplan that's scanning resultRelation.
2573 */
2575 if (!fscan)
2576 return false;
2577
2578 /*
2579 * It's unsafe to modify a foreign table directly if there are any quals
2580 * that should be evaluated locally.
2581 */
2582 if (fscan->scan.plan.qual != NIL)
2583 return false;
2584
2585 /* Safe to fetch data about the target foreign rel */
2586 if (fscan->scan.scanrelid == 0)
2587 {
2588 foreignrel = find_join_rel(root, fscan->fs_relids);
2589 /* We should have a rel for this foreign join. */
2590 Assert(foreignrel);
2591 }
2592 else
2593 foreignrel = root->simple_rel_array[resultRelation];
2594 rte = root->simple_rte_array[resultRelation];
2595 fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2596
2597 /*
2598 * It's unsafe to update a foreign table directly, if any expressions to
2599 * assign to the target columns are unsafe to evaluate remotely.
2600 */
2601 if (operation == CMD_UPDATE)
2602 {
2603 ListCell *lc,
2604 *lc2;
2605
2606 /*
2607 * The expressions of concern are the first N columns of the processed
2608 * targetlist, where N is the length of the rel's update_colnos.
2609 */
2610 get_translated_update_targetlist(root, resultRelation,
2611 &processed_tlist, &targetAttrs);
2612 forboth(lc, processed_tlist, lc2, targetAttrs)
2613 {
2615 AttrNumber attno = lfirst_int(lc2);
2616
2617 /* update's new-value expressions shouldn't be resjunk */
2618 Assert(!tle->resjunk);
2619
2620 if (attno <= InvalidAttrNumber) /* shouldn't happen */
2621 elog(ERROR, "system-column update is not supported");
2622
2623 if (!is_foreign_expr(root, foreignrel, (Expr *) tle->expr))
2624 return false;
2625 }
2626 }
2627
2628 /*
2629 * Ok, rewrite subplan so as to modify the foreign table directly.
2630 */
2631 initStringInfo(&sql);
2632
2633 /*
2634 * Core code already has some lock on each rel being planned, so we can
2635 * use NoLock here.
2636 */
2637 rel = table_open(rte->relid, NoLock);
2638
2639 /*
2640 * Recall the qual clauses that must be evaluated remotely. (These are
2641 * bare clauses not RestrictInfos, but deparse.c's appendConditions()
2642 * doesn't care.)
2643 */
2644 remote_exprs = fpinfo->final_remote_exprs;
2645
2646 /*
2647 * Extract the relevant RETURNING list if any.
2648 */
2649 if (plan->returningLists)
2650 {
2651 returningList = (List *) list_nth(plan->returningLists, subplan_index);
2652
2653 /*
2654 * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2655 * we fetch from the foreign server any Vars specified in RETURNING
2656 * that refer not only to the target relation but to non-target
2657 * relations. So we'll deparse them into the RETURNING clause of the
2658 * remote query; use a targetlist consisting of them instead, which
2659 * will be adjusted to be new fdw_scan_tlist of the foreign-scan plan
2660 * node below.
2661 */
2662 if (fscan->scan.scanrelid == 0)
2663 returningList = build_remote_returning(resultRelation, rel,
2664 returningList);
2665 }
2666
2667 /*
2668 * Construct the SQL command string.
2669 */
2670 switch (operation)
2671 {
2672 case CMD_UPDATE:
2673 deparseDirectUpdateSql(&sql, root, resultRelation, rel,
2674 foreignrel,
2675 processed_tlist,
2677 remote_exprs, &params_list,
2678 returningList, &retrieved_attrs);
2679 break;
2680 case CMD_DELETE:
2681 deparseDirectDeleteSql(&sql, root, resultRelation, rel,
2682 foreignrel,
2683 remote_exprs, &params_list,
2684 returningList, &retrieved_attrs);
2685 break;
2686 default:
2687 elog(ERROR, "unexpected operation: %d", (int) operation);
2688 break;
2689 }
2690
2691 /*
2692 * Update the operation and target relation info.
2693 */
2694 fscan->operation = operation;
2695 fscan->resultRelation = resultRelation;
2696
2697 /*
2698 * Update the fdw_exprs list that will be available to the executor.
2699 */
2700 fscan->fdw_exprs = params_list;
2701
2702 /*
2703 * Update the fdw_private list that will be available to the executor.
2704 * Items in the list must match enum FdwDirectModifyPrivateIndex, above.
2705 */
2706 fscan->fdw_private = list_make4(makeString(sql.data),
2707 makeBoolean((retrieved_attrs != NIL)),
2708 retrieved_attrs,
2709 makeBoolean(plan->canSetTag));
2710
2711 /*
2712 * Update the foreign-join-related fields.
2713 */
2714 if (fscan->scan.scanrelid == 0)
2715 {
2716 /* No need for the outer subplan. */
2717 fscan->scan.plan.lefttree = NULL;
2718
2719 /* Build new fdw_scan_tlist if UPDATE/DELETE .. RETURNING. */
2720 if (returningList)
2721 rebuild_fdw_scan_tlist(fscan, returningList);
2722 }
2723
2724 /*
2725 * Finally, unset the async-capable flag if it is set, as we currently
2726 * don't support asynchronous execution of direct modifications.
2727 */
2728 if (fscan->scan.plan.async_capable)
2729 fscan->scan.plan.async_capable = false;
2730
2731 table_close(rel, NoLock);
2732 return true;
2733}
void get_translated_update_targetlist(PlannerInfo *root, Index relid, List **processed_tlist, List **update_colnos)
Definition appendinfo.c:766
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:2442
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:2327
#define NoLock
Definition lockdefs.h:34
CmdType
Definition nodes.h:271
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
#define list_make4(x1, x2, x3, x4)
Definition pg_list.h:251
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:657
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, fb(), find_join_rel(), find_modifytable_subplan(), forboth, get_translated_update_targetlist(), initStringInfo(), InvalidAttrNumber, is_foreign_expr(), lfirst_int, lfirst_node, list_make4, list_nth(), makeBoolean(), makeString(), NIL, NoLock, operation, plan, rebuild_fdw_scan_tlist(), root, 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 1870 of file postgres_fdw.c.

1874{
1875 CmdType operation = plan->operation;
1876 RangeTblEntry *rte = planner_rt_fetch(resultRelation, root);
1877 Relation rel;
1878 StringInfoData sql;
1879 List *targetAttrs = NIL;
1881 List *returningList = NIL;
1882 List *retrieved_attrs = NIL;
1883 bool doNothing = false;
1884 int values_end_len = -1;
1885
1886 initStringInfo(&sql);
1887
1888 /*
1889 * Core code already has some lock on each rel being planned, so we can
1890 * use NoLock here.
1891 */
1892 rel = table_open(rte->relid, NoLock);
1893
1894 /*
1895 * In an INSERT, we transmit all columns that are defined in the foreign
1896 * table. In an UPDATE, if there are BEFORE ROW UPDATE triggers on the
1897 * foreign table, we transmit all columns like INSERT; else we transmit
1898 * only columns that were explicitly targets of the UPDATE, so as to avoid
1899 * unnecessary data transmission. (We can't do that for INSERT since we
1900 * would miss sending default values for columns not listed in the source
1901 * statement, and for UPDATE if there are BEFORE ROW UPDATE triggers since
1902 * those triggers might change values for non-target columns, in which
1903 * case we would miss sending changed values for those columns.)
1904 */
1905 if (operation == CMD_INSERT ||
1906 (operation == CMD_UPDATE &&
1907 rel->trigdesc &&
1909 {
1910 TupleDesc tupdesc = RelationGetDescr(rel);
1911 int attnum;
1912
1913 for (attnum = 1; attnum <= tupdesc->natts; attnum++)
1914 {
1915 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
1916
1917 if (!attr->attisdropped)
1919 }
1920 }
1921 else if (operation == CMD_UPDATE)
1922 {
1923 int col;
1924 RelOptInfo *rel = find_base_rel(root, resultRelation);
1926
1927 col = -1;
1928 while ((col = bms_next_member(allUpdatedCols, col)) >= 0)
1929 {
1930 /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
1932
1933 if (attno <= InvalidAttrNumber) /* shouldn't happen */
1934 elog(ERROR, "system-column update is not supported");
1936 }
1937 }
1938
1939 /*
1940 * Extract the relevant WITH CHECK OPTION list if any.
1941 */
1942 if (plan->withCheckOptionLists)
1943 withCheckOptionList = (List *) list_nth(plan->withCheckOptionLists,
1945
1946 /*
1947 * Extract the relevant RETURNING list if any.
1948 */
1949 if (plan->returningLists)
1950 returningList = (List *) list_nth(plan->returningLists, subplan_index);
1951
1952 /*
1953 * ON CONFLICT DO NOTHING/SELECT/UPDATE with inference specification
1954 * should have already been rejected in the optimizer, as presently there
1955 * is no way to recognize an arbiter index on a foreign table. Only DO
1956 * NOTHING is supported without an inference specification.
1957 */
1958 if (plan->onConflictAction == ONCONFLICT_NOTHING)
1959 doNothing = true;
1960 else if (plan->onConflictAction != ONCONFLICT_NONE)
1961 elog(ERROR, "unexpected ON CONFLICT specification: %d",
1962 (int) plan->onConflictAction);
1963
1964 /*
1965 * Construct the SQL command string.
1966 */
1967 switch (operation)
1968 {
1969 case CMD_INSERT:
1970 deparseInsertSql(&sql, rte, resultRelation, rel,
1972 withCheckOptionList, returningList,
1973 &retrieved_attrs, &values_end_len);
1974 break;
1975 case CMD_UPDATE:
1976 deparseUpdateSql(&sql, rte, resultRelation, rel,
1978 withCheckOptionList, returningList,
1979 &retrieved_attrs);
1980 break;
1981 case CMD_DELETE:
1982 deparseDeleteSql(&sql, rte, resultRelation, rel,
1983 returningList,
1984 &retrieved_attrs);
1985 break;
1986 default:
1987 elog(ERROR, "unexpected operation: %d", (int) operation);
1988 break;
1989 }
1990
1991 table_close(rel, NoLock);
1992
1993 /*
1994 * Build the fdw_private list that will be available to the executor.
1995 * Items in the list must match enum FdwModifyPrivateIndex, above.
1996 */
1997 return list_make5(makeString(sql.data),
2000 makeBoolean((retrieved_attrs != NIL)),
2001 retrieved_attrs);
2002}
void deparseUpdateSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, List *withCheckOptionList, List *returningList, List **retrieved_attrs)
Definition deparse.c:2267
void deparseDeleteSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *returningList, List **retrieved_attrs)
Definition deparse.c:2413
Bitmapset * get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel)
Definition inherit.c:654
#define planner_rt_fetch(rti, root)
Definition pathnodes.h:704
#define list_make5(x1, x2, x3, x4, x5)
Definition pg_list.h:254
RelOptInfo * find_base_rel(PlannerInfo *root, int relid)
Definition relnode.c:544
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, fb(), 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, operation, 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 2450 of file postgres_fdw.c.

2451{
2452 Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
2455
2456 /* For base foreign relations, it suffices to set fdw_recheck_quals */
2457 if (scanrelid > 0)
2458 return true;
2459
2460 Assert(outerPlan != NULL);
2461
2462 /* Execute a local join execution plan */
2464 if (TupIsNull(result))
2465 return false;
2466
2467 /* Store result in the given slot */
2468 ExecCopySlot(slot, result);
2469
2470 return true;
2471}
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:322
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:544

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

Referenced by postgres_fdw_handler().

◆ postgresReScanForeignScan()

static void postgresReScanForeignScan ( ForeignScanState node)
static

Definition at line 1743 of file postgres_fdw.c.

1744{
1745 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1746 char sql[64];
1747 PGresult *res;
1748
1749 /* If we haven't created the cursor yet, nothing to do. */
1750 if (!fsstate->cursor_exists)
1751 return;
1752
1753 /*
1754 * If the node is async-capable, and an asynchronous fetch for it has
1755 * begun, the asynchronous fetch might not have yet completed. Check if
1756 * the node is async-capable, and an asynchronous fetch for it is still in
1757 * progress; if so, complete the asynchronous fetch before restarting the
1758 * scan.
1759 */
1760 if (fsstate->async_capable &&
1761 fsstate->conn_state->pendingAreq &&
1762 fsstate->conn_state->pendingAreq->requestee == (PlanState *) node)
1763 fetch_more_data(node);
1764
1765 /*
1766 * If any internal parameters affecting this node have changed, we'd
1767 * better destroy and recreate the cursor. Otherwise, if the remote
1768 * server is v14 or older, rewinding it should be good enough; if not,
1769 * rewind is only allowed for scrollable cursors, but we don't have a way
1770 * to check the scrollability of it, so destroy and recreate it in any
1771 * case. If we've only fetched zero or one batch, we needn't even rewind
1772 * the cursor, just rescan what we have.
1773 */
1774 if (node->ss.ps.chgParam != NULL)
1775 {
1776 fsstate->cursor_exists = false;
1777 snprintf(sql, sizeof(sql), "CLOSE c%u",
1778 fsstate->cursor_number);
1779 }
1780 else if (fsstate->fetch_ct_2 > 1)
1781 {
1782 if (PQserverVersion(fsstate->conn) < 150000)
1783 snprintf(sql, sizeof(sql), "MOVE BACKWARD ALL IN c%u",
1784 fsstate->cursor_number);
1785 else
1786 {
1787 fsstate->cursor_exists = false;
1788 snprintf(sql, sizeof(sql), "CLOSE c%u",
1789 fsstate->cursor_number);
1790 }
1791 }
1792 else
1793 {
1794 /* Easy: just rescan what we already have in memory, if anything */
1795 fsstate->next_tuple = 0;
1796 return;
1797 }
1798
1799 res = pgfdw_exec_query(fsstate->conn, sql, fsstate->conn_state);
1800 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1801 pgfdw_report_error(res, fsstate->conn, sql);
1802 PQclear(res);
1803
1804 /* Now force a fresh FETCH. */
1805 fsstate->tuples = NULL;
1806 fsstate->num_tuples = 0;
1807 fsstate->next_tuple = 0;
1808 fsstate->fetch_ct_2 = 0;
1809 fsstate->eof_reached = false;
1810}
Bitmapset * chgParam
Definition execnodes.h:1236

References PgFdwScanState::async_capable, PlanState::chgParam, PgFdwScanState::conn, PgFdwScanState::conn_state, PgFdwScanState::cursor_exists, PgFdwScanState::cursor_number, PgFdwScanState::eof_reached, fb(), ForeignScanState::fdw_state, PgFdwScanState::fetch_ct_2, fetch_more_data(), 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 4288 of file postgres_fdw.c.

4289{
4290 char prep_name[NAMEDATALEN];
4291 char *p_name;
4292 PGresult *res;
4293
4294 /*
4295 * The caller would already have processed a pending asynchronous request
4296 * if any, so no need to do it here.
4297 */
4298
4299 /* Construct name we'll use for the prepared statement. */
4300 snprintf(prep_name, sizeof(prep_name), "pgsql_fdw_prep_%u",
4301 GetPrepStmtNumber(fmstate->conn));
4302 p_name = pstrdup(prep_name);
4303
4304 /*
4305 * We intentionally do not specify parameter types here, but leave the
4306 * remote server to derive them by default. This avoids possible problems
4307 * with the remote server using different type OIDs than we do. All of
4308 * the prepared statements we use in this module are simple enough that
4309 * the remote server will make the right choices.
4310 */
4311 if (!PQsendPrepare(fmstate->conn,
4312 p_name,
4313 fmstate->query,
4314 0,
4315 NULL))
4316 pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
4317
4318 /*
4319 * Get the result, and check for success.
4320 */
4321 res = pgfdw_get_result(fmstate->conn);
4322 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4323 pgfdw_report_error(res, fmstate->conn, fmstate->query);
4324 PQclear(res);
4325
4326 /* This action shows that the prepare has been done. */
4327 fmstate->p_name = p_name;
4328}
unsigned int GetPrepStmtNumber(PGconn *conn)
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
Definition fe-exec.c:1553
#define NAMEDATALEN

References fb(), GetPrepStmtNumber(), NAMEDATALEN, pgfdw_get_result(), pgfdw_report_error(), PGRES_COMMAND_OK, PQclear, PQresultStatus, PQsendPrepare(), pstrdup(), 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 4898 of file postgres_fdw.c.

4904{
4905 int i;
4906 ListCell *lc;
4907
4908 Assert(numParams > 0);
4909
4910 /* Prepare for output conversion of parameters used in remote query. */
4911 *param_flinfo = palloc0_array(FmgrInfo, numParams);
4912
4913 i = 0;
4914 foreach(lc, fdw_exprs)
4915 {
4916 Node *param_expr = (Node *) lfirst(lc);
4917 Oid typefnoid;
4918 bool isvarlena;
4919
4921 fmgr_info(typefnoid, &(*param_flinfo)[i]);
4922 i++;
4923 }
4924
4925 /*
4926 * Prepare remote-parameter expressions for evaluation. (Note: in
4927 * practice, we expect that all these expressions will be just Params, so
4928 * we could possibly do something more efficient than using the full
4929 * expression-eval machinery for this. But probably there would be little
4930 * benefit, and it'd require postgres_fdw to know more than is desirable
4931 * about Param evaluation.)
4932 */
4933 *param_exprs = ExecInitExprList(fdw_exprs, node);
4934
4935 /* Allocate buffer for text form of query parameters. */
4936 *param_values = (const char **) palloc0(numParams * sizeof(char *));
4937}
List * ExecInitExprList(List *nodes, PlanState *parent)
Definition execExpr.c:335
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42

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

Referenced by postgresBeginDirectModify(), and postgresBeginForeignScan().

◆ process_pending_request()

void process_pending_request ( AsyncRequest areq)

Definition at line 8304 of file postgres_fdw.c.

8305{
8306 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8307 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8308
8309 /* The request would have been pending for a callback */
8310 Assert(areq->callback_pending);
8311
8312 /* The request should be currently in-process */
8313 Assert(fsstate->conn_state->pendingAreq == areq);
8314
8315 fetch_more_data(node);
8316
8317 /*
8318 * If we didn't get any tuples, must be end of data; complete the request
8319 * now. Otherwise, we postpone completing the request until we are called
8320 * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
8321 */
8322 if (fsstate->next_tuple >= fsstate->num_tuples)
8323 {
8324 /* Unlike AsyncNotify, we unset callback_pending ourselves */
8325 areq->callback_pending = false;
8326 /* Mark the request as complete */
8328 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
8330 }
8331}
void ExecAsyncRequestDone(AsyncRequest *areq, TupleTableSlot *result)
Definition execAsync.c:138

References Assert, PgFdwScanState::conn_state, ExecAsyncRequestDone(), ExecAsyncResponse(), fb(), ForeignScanState::fdw_state, fetch_more_data(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, and PgFdwConnState::pendingAreq.

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

4947{
4948 int nestlevel;
4949 int i;
4950 ListCell *lc;
4951
4953
4954 i = 0;
4955 foreach(lc, param_exprs)
4956 {
4959 bool isNull;
4960
4961 /* Evaluate the parameter expression */
4962 expr_value = ExecEvalExpr(expr_state, econtext, &isNull);
4963
4964 /*
4965 * Get string representation of each parameter value by invoking
4966 * type-specific output function, unless the value is null.
4967 */
4968 if (isNull)
4969 param_values[i] = NULL;
4970 else
4971 param_values[i] = OutputFunctionCall(&param_flinfo[i], expr_value);
4972
4973 i++;
4974 }
4975
4977}
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:401

References ExecEvalExpr(), fb(), 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 8206 of file postgres_fdw.c.

8207{
8208 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8209 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8210 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
8212
8213 /* This should not be called if the request is currently in-process */
8214 Assert(areq != pendingAreq);
8215
8216 /* Fetch some more tuples, if we've run out */
8217 if (fsstate->next_tuple >= fsstate->num_tuples)
8218 {
8219 /* No point in another fetch if we already detected EOF, though */
8220 if (!fsstate->eof_reached)
8221 {
8222 /* Mark the request as pending for a callback */
8224 /* Begin another fetch if requested and if no pending request */
8225 if (fetch && !pendingAreq)
8227 }
8228 else
8229 {
8230 /* There's nothing more to do; just return a NULL pointer */
8231 result = NULL;
8232 /* Mark the request as complete */
8234 }
8235 return;
8236 }
8237
8238 /* Get a tuple from the ForeignScan node */
8239 result = areq->requestee->ExecProcNodeReal(areq->requestee);
8240 if (!TupIsNull(result))
8241 {
8242 /* Mark the request as complete */
8244 return;
8245 }
8246
8247 /* We must have run out of tuples */
8248 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8249
8250 /* Fetch some more tuples, if we've not detected EOF yet */
8251 if (!fsstate->eof_reached)
8252 {
8253 /* Mark the request as pending for a callback */
8255 /* Begin another fetch if requested and if no pending request */
8256 if (fetch && !pendingAreq)
8258 }
8259 else
8260 {
8261 /* There's nothing more to do; just return a NULL pointer */
8262 result = NULL;
8263 /* Mark the request as complete */
8265 }
8266}
void ExecAsyncRequestPending(AsyncRequest *areq)
Definition execAsync.c:150

References Assert, PgFdwScanState::conn_state, PgFdwScanState::eof_reached, ExecAsyncRequestDone(), ExecAsyncRequestPending(), fb(), ForeignScanState::fdw_state, fetch_more_data_begin(), PgFdwScanState::next_tuple, PgFdwScanState::num_tuples, PgFdwConnState::pendingAreq, result, 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 4583 of file postgres_fdw.c.

4584{
4585 List *new_tlist = tlist;
4586 List *old_tlist = fscan->fdw_scan_tlist;
4587 ListCell *lc;
4588
4589 foreach(lc, old_tlist)
4590 {
4592
4593 if (tlist_member(tle->expr, new_tlist))
4594 continue; /* already got it */
4595
4597 makeTargetEntry(tle->expr,
4599 NULL,
4600 false));
4601 }
4602 fscan->fdw_scan_tlist = new_tlist;
4603}

References fb(), lappend(), lfirst, list_length(), makeTargetEntry(), and tlist_member().

Referenced by postgresPlanDirectModify().

◆ remattrmap_cmp()

static int remattrmap_cmp ( const void v1,
const void v2 
)
static

Definition at line 5912 of file postgres_fdw.c.

5913{
5914 const RemoteAttributeMapping *r1 = v1;
5915 const RemoteAttributeMapping *r2 = v2;
5916
5917 return strcmp(r1->remote_attname, r2->remote_attname);
5918}

References fb().

Referenced by build_remattrmap().

◆ reset_transmission_modes()

void reset_transmission_modes ( int  nestlevel)

Definition at line 4023 of file postgres_fdw.c.

4024{
4025 AtEOXact_GUC(true, nestlevel);
4026}
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169

References AtEOXact_GUC(), and fb().

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

6541{
6542 List *vars;
6543 ListCell *lc;
6544 bool ok = true;
6545
6546 Assert(joinrel->reltarget);
6547
6549
6550 foreach(lc, vars)
6551 {
6552 Var *var = (Var *) lfirst(lc);
6553
6554 if (!IsA(var, Var))
6555 continue;
6556
6557 if (bms_is_member(var->varno, innerrel->relids))
6558 {
6559 /*
6560 * The planner can create semi-join, which refers to inner rel
6561 * vars in its target list. However, we deparse semi-join as an
6562 * exists() subquery, so can't handle references to inner rel in
6563 * the target list.
6564 */
6565 Assert(!bms_is_member(var->varno, outerrel->relids));
6566 ok = false;
6567 break;
6568 }
6569 }
6570 return ok;
6571}

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

Referenced by foreign_join_ok().

◆ set_float_arg()

static void set_float_arg ( NullableDatum arg,
const char s 
)
static

Definition at line 6209 of file postgres_fdw.c.

6210{
6211 if (s)
6212 {
6213 float4 val = float4in_internal((char *) s, NULL, "float", s, NULL);
6214
6215 arg->value = Float4GetDatum(val);
6216 arg->isnull = false;
6217 }
6218 else
6219 {
6220 arg->value = (Datum) 0;
6221 arg->isnull = true;
6222 }
6223}
float float4
Definition c.h:772
float4 float4in_internal(char *num, char **endptr_p, const char *type_name, const char *orig_string, struct Node *escontext)
Definition float.c:224
long val
Definition informix.c:689
static Datum Float4GetDatum(float4 X)
Definition postgres.h:481

References arg, fb(), Float4GetDatum(), float4in_internal(), and val.

Referenced by import_fetched_statistics().

◆ set_floatarr_arg()

static void set_floatarr_arg ( NullableDatum arg,
const char s 
)
static

Definition at line 6229 of file postgres_fdw.c.

6230{
6231 if (s)
6232 {
6233 FmgrInfo flinfo;
6234 Datum val;
6235
6236 fmgr_info(F_ARRAY_IN, &flinfo);
6237 val = InputFunctionCall(&flinfo, (char *) s, FLOAT4OID, -1);
6238
6239 arg->value = val;
6240 arg->isnull = false;
6241 }
6242 else
6243 {
6244 arg->value = (Datum) 0;
6245 arg->isnull = true;
6246 }
6247}

References arg, fb(), fmgr_info(), InputFunctionCall(), and val.

Referenced by import_fetched_statistics().

◆ set_int32_arg()

static void set_int32_arg ( NullableDatum arg,
const char s 
)
static

Definition at line 6169 of file postgres_fdw.c.

6170{
6171 if (s)
6172 {
6173 int32 val = pg_strtoint32(s);
6174
6175 arg->value = Int32GetDatum(val);
6176 arg->isnull = false;
6177 }
6178 else
6179 {
6180 arg->value = (Datum) 0;
6181 arg->isnull = true;
6182 }
6183}
int32_t int32
Definition c.h:679
int32 pg_strtoint32(const char *s)
Definition numutils.c:382

References arg, Int32GetDatum(), pg_strtoint32(), and val.

Referenced by import_fetched_statistics().

◆ set_text_arg()

static void set_text_arg ( NullableDatum arg,
const char s 
)
static

Definition at line 6151 of file postgres_fdw.c.

6152{
6153 if (s)
6154 {
6155 arg->value = CStringGetTextDatum(s);
6156 arg->isnull = false;
6157 }
6158 else
6159 {
6160 arg->value = (Datum) 0;
6161 arg->isnull = true;
6162 }
6163}
#define CStringGetTextDatum(s)
Definition builtins.h:98

References arg, and CStringGetTextDatum.

Referenced by import_fetched_statistics().

◆ set_transmission_modes()

int set_transmission_modes ( void  )

Definition at line 3987 of file postgres_fdw.c.

3988{
3989 int nestlevel = NewGUCNestLevel();
3990
3991 /*
3992 * The values set here should match what pg_dump does. See also
3993 * configure_remote_session in connection.c.
3994 */
3995 if (DateStyle != USE_ISO_DATES)
3996 (void) set_config_option("datestyle", "ISO",
3998 GUC_ACTION_SAVE, true, 0, false);
4000 (void) set_config_option("intervalstyle", "postgres",
4002 GUC_ACTION_SAVE, true, 0, false);
4003 if (extra_float_digits < 3)
4004 (void) set_config_option("extra_float_digits", "3",
4006 GUC_ACTION_SAVE, true, 0, false);
4007
4008 /*
4009 * In addition force restrictive search_path, in case there are any
4010 * regproc or similar constants to be printed.
4011 */
4012 (void) set_config_option("search_path", "pg_catalog",
4014 GUC_ACTION_SAVE, true, 0, false);
4015
4016 return nestlevel;
4017}
int extra_float_digits
Definition float.c:57
int DateStyle
Definition globals.c:127
int IntervalStyle
Definition globals.c:129
int NewGUCNestLevel(void)
Definition guc.c:2142
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:3248
@ 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:240
#define INTSTYLE_POSTGRES
Definition miscadmin.h:260

References DateStyle, extra_float_digits, fb(), 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().

◆ set_uint32_arg()

static void set_uint32_arg ( NullableDatum arg,
const char s 
)
static

Definition at line 6189 of file postgres_fdw.c.

6190{
6191 if (s)
6192 {
6193 uint32 val = uint32in_subr(s, NULL, "uint32", NULL);
6194
6195 arg->value = UInt32GetDatum(val);
6196 arg->isnull = false;
6197 }
6198 else
6199 {
6200 arg->value = (Datum) 0;
6201 arg->isnull = true;
6202 }
6203}
uint32_t uint32
Definition c.h:683
uint32 uint32in_subr(const char *s, char **endloc, const char *typname, Node *escontext)
Definition numutils.c:897
static Datum UInt32GetDatum(uint32 X)
Definition postgres.h:232

References arg, fb(), UInt32GetDatum(), uint32in_subr(), and val.

Referenced by import_fetched_statistics().

◆ store_returning_result()

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

Definition at line 4416 of file postgres_fdw.c.

4418{
4420
4422 fmstate->rel,
4423 fmstate->attinmeta,
4424 fmstate->retrieved_attrs,
4425 NULL,
4426 fmstate->temp_cxt);
4427
4428 /*
4429 * The returning slot will not necessarily be suitable to store heaptuples
4430 * directly, so allow for conversion.
4431 */
4432 ExecForceStoreHeapTuple(newtup, slot, true);
4433}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)

References ExecForceStoreHeapTuple(), fb(), and make_tuple_from_result_row().

Referenced by execute_foreign_modify().