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 "executor/spi.h"
#include "foreign/fdwapi.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/cost.h"
#include "optimizer/inherit.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "postgres_fdw.h"
#include "statistics/statistics.h"
#include "storage/latch.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
#include "utils/selfuncs.h"
Include dependency graph for postgres_fdw.c:

Go to the source code of this file.

Data Structures

struct  PgFdwScanState
 
struct  PgFdwModifyState
 
struct  PgFdwDirectModifyState
 
struct  PgFdwAnalyzeState
 
struct  PgFdwPathExtraData
 
struct  ConversionLocation
 
struct  ec_member_foreign_arg
 
struct  RemoteAttributeMapping
 
struct  RemoteStatsResults
 
struct  AttrResultArgMap
 

Macros

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

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
}
 
enum  RelImportSqlArgs {
  RELIMPORT_SQL_VERSION = 0 , RELIMPORT_SQL_SCHEMANAME , RELIMPORT_SQL_RELNAME , RELIMPORT_SQL_RELPAGES ,
  RELIMPORT_SQL_RELTUPLES , RELIMPORT_SQL_NUM_FIELDS
}
 
enum  AttImportSqlArgs {
  ATTIMPORT_SQL_VERSION = 0 , ATTIMPORT_SQL_SCHEMANAME , ATTIMPORT_SQL_RELNAME , ATTIMPORT_SQL_ATTNUM ,
  ATTIMPORT_SQL_NULL_FRAC , ATTIMPORT_SQL_AVG_WIDTH , ATTIMPORT_SQL_N_DISTINCT , ATTIMPORT_SQL_MOST_COMMON_VALS ,
  ATTIMPORT_SQL_MOST_COMMON_FREQS , ATTIMPORT_SQL_HISTOGRAM_BOUNDS , ATTIMPORT_SQL_CORRELATION , ATTIMPORT_SQL_MOST_COMMON_ELEMS ,
  ATTIMPORT_SQL_MOST_COMMON_ELEM_FREQS , ATTIMPORT_SQL_ELEM_COUNT_HISTOGRAM , ATTIMPORT_SQL_RANGE_LENGTH_HISTOGRAM , ATTIMPORT_SQL_RANGE_EMPTY_FRAC ,
  ATTIMPORT_SQL_RANGE_BOUNDS_HISTOGRAM , ATTIMPORT_SQL_NUM_FIELDS
}
 
enum  AttClearSqlArgs { ATTCLEAR_SQL_SCHEMANAME = 0 , ATTCLEAR_SQL_RELNAME , ATTCLEAR_SQL_ATTNAME , ATTCLEAR_SQL_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 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 (const char *schemaname, const char *relname, int attrcnt, const RemoteAttributeMapping *remattrmap, RemoteStatsResults *remstats)
 
static void map_field_to_arg (PGresult *res, int row, int field, int arg, Datum *values, char *nulls)
 
static bool import_spi_query_ok (void)
 
static void produce_tuple_asynchronously (AsyncRequest *areq, bool fetch)
 
static void fetch_more_data_begin (AsyncRequest *areq)
 
static void complete_pending_request (AsyncRequest *areq)
 
static HeapTuple make_tuple_from_result_row (PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
 
static void conversion_error_callback (void *arg)
 
static bool foreign_join_ok (PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra)
 
static bool foreign_grouping_ok (PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
 
static Listget_useful_pathkeys_for_relation (PlannerInfo *root, RelOptInfo *rel)
 
static Listget_useful_ecs_for_relation (PlannerInfo *root, RelOptInfo *rel)
 
static void add_paths_with_pathkeys_for_rel (PlannerInfo *root, RelOptInfo *rel, Path *epq_path, List *restrictlist)
 
static void add_foreign_grouping_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra)
 
static void add_foreign_ordered_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel)
 
static void add_foreign_final_paths (PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *final_rel, FinalPathExtraData *extra)
 
static void apply_server_options (PgFdwRelationInfo *fpinfo)
 
static void apply_table_options (PgFdwRelationInfo *fpinfo)
 
static void merge_fdw_options (PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
 
static int get_batch_size_option (Relation rel)
 
Datum postgres_fdw_handler (PG_FUNCTION_ARGS)
 
static TupleDesc get_tupdesc_for_join_scan_tuples (ForeignScanState *node)
 
static ForeignScanfind_modifytable_subplan (PlannerInfo *root, ModifyTable *plan, Index rtindex, int subplan_index)
 
int set_transmission_modes (void)
 
void reset_transmission_modes (int nestlevel)
 
static double postgresGetAnalyzeInfoForForeignTable (Relation relation, bool *can_tablesample)
 
static bool semijoin_target_ok (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel)
 
void process_pending_request (AsyncRequest *areq)
 
EquivalenceMemberfind_em_for_rel (PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 
EquivalenceMemberfind_em_for_rel_target (PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
 

Variables

static const charrelimport_sql
 
static const Oid relimport_argtypes [RELIMPORT_SQL_NUM_FIELDS]
 
static const charattimport_sql
 
static const Oid attimport_argtypes [ATTIMPORT_SQL_NUM_FIELDS]
 
static const AttrResultArgMap attr_result_arg_map [NUM_MAPPED_ATTIMPORT_ARGS]
 
static const charattclear_sql
 
static const Oid attclear_argtypes [ATTCLEAR_SQL_NUM_FIELDS]
 

Macro Definition Documentation

◆ DEFAULT_FDW_SORT_MULTIPLIER

#define DEFAULT_FDW_SORT_MULTIPLIER   1.2

Definition at line 68 of file postgres_fdw.c.

◆ DEFAULT_FDW_STARTUP_COST

#define DEFAULT_FDW_STARTUP_COST   100.0

Definition at line 62 of file postgres_fdw.c.

◆ DEFAULT_FDW_TUPLE_COST

#define DEFAULT_FDW_TUPLE_COST   0.2

Definition at line 65 of file postgres_fdw.c.

◆ NUM_MAPPED_ATTIMPORT_ARGS

#define NUM_MAPPED_ATTIMPORT_ARGS   13

Definition at line 462 of file postgres_fdw.c.

Typedef Documentation

◆ ConversionLocation

◆ PgFdwAnalyzeState

◆ PgFdwDirectModifyState

◆ PgFdwModifyState

◆ PgFdwScanState

Enumeration Type Documentation

◆ AttClearSqlArgs

Enumerator
ATTCLEAR_SQL_SCHEMANAME 
ATTCLEAR_SQL_RELNAME 
ATTCLEAR_SQL_ATTNAME 
ATTCLEAR_SQL_NUM_FIELDS 

Definition at line 486 of file postgres_fdw.c.

487{
492};
@ ATTCLEAR_SQL_ATTNAME
@ ATTCLEAR_SQL_SCHEMANAME
@ ATTCLEAR_SQL_RELNAME
@ ATTCLEAR_SQL_NUM_FIELDS

◆ AttImportSqlArgs

Enumerator
ATTIMPORT_SQL_VERSION 
ATTIMPORT_SQL_SCHEMANAME 
ATTIMPORT_SQL_RELNAME 
ATTIMPORT_SQL_ATTNUM 
ATTIMPORT_SQL_NULL_FRAC 
ATTIMPORT_SQL_AVG_WIDTH 
ATTIMPORT_SQL_N_DISTINCT 
ATTIMPORT_SQL_MOST_COMMON_VALS 
ATTIMPORT_SQL_MOST_COMMON_FREQS 
ATTIMPORT_SQL_HISTOGRAM_BOUNDS 
ATTIMPORT_SQL_CORRELATION 
ATTIMPORT_SQL_MOST_COMMON_ELEMS 
ATTIMPORT_SQL_MOST_COMMON_ELEM_FREQS 
ATTIMPORT_SQL_ELEM_COUNT_HISTOGRAM 
ATTIMPORT_SQL_RANGE_LENGTH_HISTOGRAM 
ATTIMPORT_SQL_RANGE_EMPTY_FRAC 
ATTIMPORT_SQL_RANGE_BOUNDS_HISTOGRAM 
ATTIMPORT_SQL_NUM_FIELDS 

Definition at line 420 of file postgres_fdw.c.

421{
440};
@ ATTIMPORT_SQL_VERSION
@ ATTIMPORT_SQL_MOST_COMMON_VALS
@ ATTIMPORT_SQL_RANGE_BOUNDS_HISTOGRAM
@ ATTIMPORT_SQL_MOST_COMMON_ELEM_FREQS
@ ATTIMPORT_SQL_SCHEMANAME
@ ATTIMPORT_SQL_ELEM_COUNT_HISTOGRAM
@ ATTIMPORT_SQL_ATTNUM
@ ATTIMPORT_SQL_MOST_COMMON_ELEMS
@ ATTIMPORT_SQL_N_DISTINCT
@ ATTIMPORT_SQL_HISTOGRAM_BOUNDS
@ ATTIMPORT_SQL_RANGE_EMPTY_FRAC
@ ATTIMPORT_SQL_RANGE_LENGTH_HISTOGRAM
@ ATTIMPORT_SQL_NULL_FRAC
@ ATTIMPORT_SQL_AVG_WIDTH
@ ATTIMPORT_SQL_NUM_FIELDS
@ ATTIMPORT_SQL_MOST_COMMON_FREQS
@ ATTIMPORT_SQL_CORRELATION
@ ATTIMPORT_SQL_RELNAME

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

352{
368};
@ 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 128 of file postgres_fdw.c.

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

◆ FdwModifyPrivateIndex

Enumerator
FdwModifyPrivateUpdateSql 
FdwModifyPrivateTargetAttnums 
FdwModifyPrivateLen 
FdwModifyPrivateHasReturning 
FdwModifyPrivateRetrievedAttrs 

Definition at line 105 of file postgres_fdw.c.

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

◆ FdwPathPrivateIndex

Enumerator
FdwPathPrivateHasFinalSort 
FdwPathPrivateHasLimit 

Definition at line 288 of file postgres_fdw.c.

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

◆ FdwScanPrivateIndex

Enumerator
FdwScanPrivateSelectSql 
FdwScanPrivateRetrievedAttrs 
FdwScanPrivateFetchSize 
FdwScanPrivateRelations 

Definition at line 77 of file postgres_fdw.c.

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

◆ RelImportSqlArgs

Enumerator
RELIMPORT_SQL_VERSION 
RELIMPORT_SQL_SCHEMANAME 
RELIMPORT_SQL_RELNAME 
RELIMPORT_SQL_RELPAGES 
RELIMPORT_SQL_RELTUPLES 
RELIMPORT_SQL_NUM_FIELDS 

Definition at line 380 of file postgres_fdw.c.

381{
388};
@ RELIMPORT_SQL_SCHEMANAME
@ RELIMPORT_SQL_RELTUPLES
@ RELIMPORT_SQL_RELPAGES
@ RELIMPORT_SQL_VERSION
@ RELIMPORT_SQL_RELNAME
@ RELIMPORT_SQL_NUM_FIELDS

◆ RelStatsColumns

Enumerator
RELSTATS_RELPAGES 
RELSTATS_RELTUPLES 
RELSTATS_RELKIND 
RELSTATS_NUM_FIELDS 

Definition at line 342 of file postgres_fdw.c.

343{
348};
@ 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 7864 of file postgres_fdw.c.

7867{
7868 Query *parse = root->parse;
7871 bool has_final_sort = false;
7872 List *pathkeys = NIL;
7874 bool save_use_remote_estimate = false;
7875 double rows;
7876 int width;
7877 int disabled_nodes;
7878 Cost startup_cost;
7879 Cost total_cost;
7880 List *fdw_private;
7882
7883 /*
7884 * Currently, we only support this for SELECT commands
7885 */
7886 if (parse->commandType != CMD_SELECT)
7887 return;
7888
7889 /*
7890 * No work if there is no FOR UPDATE/SHARE clause and if there is no need
7891 * to add a LIMIT node
7892 */
7893 if (!parse->rowMarks && !extra->limit_needed)
7894 return;
7895
7896 /* We don't support cases where there are any SRFs in the targetlist */
7897 if (parse->hasTargetSRFs)
7898 return;
7899
7900 /* Save the input_rel as outerrel in fpinfo */
7901 fpinfo->outerrel = input_rel;
7902
7903 /*
7904 * Copy foreign table, foreign server, user mapping, FDW options etc.
7905 * details from the input relation's fpinfo.
7906 */
7907 fpinfo->table = ifpinfo->table;
7908 fpinfo->server = ifpinfo->server;
7909 fpinfo->user = ifpinfo->user;
7911
7912 /*
7913 * If there is no need to add a LIMIT node, there might be a ForeignPath
7914 * in the input_rel's pathlist that implements all behavior of the query.
7915 * Note: we would already have accounted for the query's FOR UPDATE/SHARE
7916 * (if any) before we get here.
7917 */
7918 if (!extra->limit_needed)
7919 {
7920 ListCell *lc;
7921
7922 Assert(parse->rowMarks);
7923
7924 /*
7925 * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
7926 * so the input_rel should be a base, join, or ordered relation; and
7927 * if it's an ordered relation, its input relation should be a base or
7928 * join relation.
7929 */
7930 Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7931 input_rel->reloptkind == RELOPT_JOINREL ||
7932 (input_rel->reloptkind == RELOPT_UPPER_REL &&
7933 ifpinfo->stage == UPPERREL_ORDERED &&
7934 (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
7935 ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
7936
7937 foreach(lc, input_rel->pathlist)
7938 {
7939 Path *path = (Path *) lfirst(lc);
7940
7941 /*
7942 * apply_scanjoin_target_to_paths() uses create_projection_path()
7943 * to adjust each of its input paths if needed, whereas
7944 * create_ordered_paths() uses apply_projection_to_path() to do
7945 * that. So the former might have put a ProjectionPath on top of
7946 * the ForeignPath; look through ProjectionPath and see if the
7947 * path underneath it is ForeignPath.
7948 */
7949 if (IsA(path, ForeignPath) ||
7950 (IsA(path, ProjectionPath) &&
7951 IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
7952 {
7953 /*
7954 * Create foreign final path; this gets rid of a
7955 * no-longer-needed outer plan (if any), which makes the
7956 * EXPLAIN output look cleaner
7957 */
7959 path->parent,
7960 path->pathtarget,
7961 path->rows,
7962 path->disabled_nodes,
7963 path->startup_cost,
7964 path->total_cost,
7965 path->pathkeys,
7966 NULL, /* no extra plan */
7967 NIL, /* no fdw_restrictinfo
7968 * list */
7969 NIL); /* no fdw_private */
7970
7971 /* and add it to the final_rel */
7973
7974 /* Safe to push down */
7975 fpinfo->pushdown_safe = true;
7976
7977 return;
7978 }
7979 }
7980
7981 /*
7982 * If we get here it means no ForeignPaths; since we would already
7983 * have considered pushing down all operations for the query to the
7984 * remote server, give up on it.
7985 */
7986 return;
7987 }
7988
7989 Assert(extra->limit_needed);
7990
7991 /*
7992 * If the input_rel is an ordered relation, replace the input_rel with its
7993 * input relation
7994 */
7995 if (input_rel->reloptkind == RELOPT_UPPER_REL &&
7996 ifpinfo->stage == UPPERREL_ORDERED)
7997 {
7998 input_rel = ifpinfo->outerrel;
7999 ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
8000 has_final_sort = true;
8001 pathkeys = root->sort_pathkeys;
8002 }
8003
8004 /* The input_rel should be a base, join, or grouping relation */
8005 Assert(input_rel->reloptkind == RELOPT_BASEREL ||
8006 input_rel->reloptkind == RELOPT_JOINREL ||
8007 (input_rel->reloptkind == RELOPT_UPPER_REL &&
8008 ifpinfo->stage == UPPERREL_GROUP_AGG));
8009
8010 /*
8011 * We try to create a path below by extending a simple foreign path for
8012 * the underlying base, join, or grouping relation to perform the final
8013 * sort (if has_final_sort) and the LIMIT restriction remotely, which is
8014 * stored into the fdw_private list of the resulting path. (We
8015 * re-estimate the costs of sorting the underlying relation, if
8016 * has_final_sort.)
8017 */
8018
8019 /*
8020 * Assess if it is safe to push down the LIMIT and OFFSET to the remote
8021 * server
8022 */
8023
8024 /*
8025 * If the underlying relation has any local conditions, the LIMIT/OFFSET
8026 * cannot be pushed down.
8027 */
8028 if (ifpinfo->local_conds)
8029 return;
8030
8031 /*
8032 * If the query has FETCH FIRST .. WITH TIES, 1) it must have ORDER BY as
8033 * well, which is used to determine which additional rows tie for the last
8034 * place in the result set, and 2) ORDER BY must already have been
8035 * determined to be safe to push down before we get here. So in that case
8036 * the FETCH clause is safe to push down with ORDER BY if the remote
8037 * server is v13 or later, but if not, the remote query will fail entirely
8038 * for lack of support for it. Since we do not currently have a way to do
8039 * a remote-version check (without accessing the remote server), disable
8040 * pushing the FETCH clause for now.
8041 */
8042 if (parse->limitOption == LIMIT_OPTION_WITH_TIES)
8043 return;
8044
8045 /*
8046 * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
8047 * not safe to remote.
8048 */
8049 if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
8050 !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
8051 return;
8052
8053 /* Safe to push down */
8054 fpinfo->pushdown_safe = true;
8055
8056 /* Construct PgFdwPathExtraData */
8058 fpextra->target = root->upper_targets[UPPERREL_FINAL];
8059 fpextra->has_final_sort = has_final_sort;
8060 fpextra->has_limit = extra->limit_needed;
8061 fpextra->limit_tuples = extra->limit_tuples;
8062 fpextra->count_est = extra->count_est;
8063 fpextra->offset_est = extra->offset_est;
8064
8065 /*
8066 * Estimate the costs of performing the final sort and the LIMIT
8067 * restriction remotely. If has_final_sort is false, we wouldn't need to
8068 * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
8069 * roughly estimated using the costs we already have for the underlying
8070 * relation, in the same way as when use_remote_estimate is false. Since
8071 * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
8072 * false in that case.
8073 */
8074 if (!fpextra->has_final_sort)
8075 {
8076 save_use_remote_estimate = ifpinfo->use_remote_estimate;
8077 ifpinfo->use_remote_estimate = false;
8078 }
8080 &rows, &width, &disabled_nodes,
8081 &startup_cost, &total_cost);
8082 if (!fpextra->has_final_sort)
8083 ifpinfo->use_remote_estimate = save_use_remote_estimate;
8084
8085 /*
8086 * Build the fdw_private list that will be used by postgresGetForeignPlan.
8087 * Items in the list must match order in enum FdwPathPrivateIndex.
8088 */
8089 fdw_private = list_make2(makeBoolean(has_final_sort),
8090 makeBoolean(extra->limit_needed));
8091
8092 /*
8093 * Create foreign final path; this gets rid of a no-longer-needed outer
8094 * plan (if any), which makes the EXPLAIN output look cleaner
8095 */
8097 input_rel,
8098 root->upper_targets[UPPERREL_FINAL],
8099 rows,
8100 disabled_nodes,
8101 startup_cost,
8102 total_cost,
8103 pathkeys,
8104 NULL, /* no extra plan */
8105 NIL, /* no fdw_restrictinfo list */
8106 fdw_private);
8107
8108 /* and add it to the final_rel */
8110}
#define Assert(condition)
Definition c.h:943
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition deparse.c:244
#define palloc0_object(type)
Definition fe_memutils.h:75
void parse(int)
Definition parse.c:49
Datum subpath(PG_FUNCTION_ARGS)
Definition ltree_op.c:311
#define IsA(nodeptr, _type_)
Definition nodes.h:164
double Cost
Definition nodes.h:261
@ CMD_SELECT
Definition nodes.h:275
@ LIMIT_OPTION_WITH_TIES
Definition nodes.h:443
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:3693
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 7630 of file postgres_fdw.c.

7633{
7634 Query *parse = root->parse;
7635 PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
7636 PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
7638 double rows;
7639 int width;
7640 int disabled_nodes;
7641 Cost startup_cost;
7642 Cost total_cost;
7643
7644 /* Nothing to be done, if there is no grouping or aggregation required. */
7645 if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
7646 !root->hasHavingQual)
7647 return;
7648
7651
7652 /* save the input_rel as outerrel in fpinfo */
7653 fpinfo->outerrel = input_rel;
7654
7655 /*
7656 * Copy foreign table, foreign server, user mapping, FDW options etc.
7657 * details from the input relation's fpinfo.
7658 */
7659 fpinfo->table = ifpinfo->table;
7660 fpinfo->server = ifpinfo->server;
7661 fpinfo->user = ifpinfo->user;
7663
7664 /*
7665 * Assess if it is safe to push down aggregation and grouping.
7666 *
7667 * Use HAVING qual from extra. In case of child partition, it will have
7668 * translated Vars.
7669 */
7670 if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
7671 return;
7672
7673 /*
7674 * Compute the selectivity and cost of the local_conds, so we don't have
7675 * to do it over again for each path. (Currently we create just a single
7676 * path here, but in future it would be possible that we build more paths
7677 * such as pre-sorted paths as in postgresGetForeignPaths and
7678 * postgresGetForeignJoinPaths.) The best we can do for these conditions
7679 * is to estimate selectivity on the basis of local statistics.
7680 */
7681 fpinfo->local_conds_sel = clauselist_selectivity(root,
7682 fpinfo->local_conds,
7683 0,
7684 JOIN_INNER,
7685 NULL);
7686
7687 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
7688
7689 /* Estimate the cost of push down */
7690 estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
7691 &rows, &width, &disabled_nodes,
7692 &startup_cost, &total_cost);
7693
7694 /* Now update this information in the fpinfo */
7695 fpinfo->rows = rows;
7696 fpinfo->width = width;
7697 fpinfo->disabled_nodes = disabled_nodes;
7698 fpinfo->startup_cost = startup_cost;
7699 fpinfo->total_cost = total_cost;
7700
7701 /* Create and add foreign path to the grouping relation. */
7703 grouped_rel,
7704 grouped_rel->reltarget,
7705 rows,
7706 disabled_nodes,
7707 startup_cost,
7708 total_cost,
7709 NIL, /* no pathkeys */
7710 NULL,
7711 NIL, /* no fdw_restrictinfo list */
7712 NIL); /* no fdw_private */
7713
7714 /* Add generated path into grouped_rel by add_path(). */
7715 add_path(grouped_rel, (Path *) grouppath);
7716}
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:4900
@ JOIN_INNER
Definition nodes.h:303
@ PARTITIONWISE_AGGREGATE_FULL
Definition pathnodes.h:3648
@ PARTITIONWISE_AGGREGATE_NONE
Definition pathnodes.h:3647
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
PartitionwiseAggregateType patype
Definition pathnodes.h:3677
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 7726 of file postgres_fdw.c.

7728{
7729 Query *parse = root->parse;
7730 PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
7731 PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
7733 double rows;
7734 int width;
7735 int disabled_nodes;
7736 Cost startup_cost;
7737 Cost total_cost;
7738 List *fdw_private;
7740 ListCell *lc;
7741
7742 /* Shouldn't get here unless the query has ORDER BY */
7743 Assert(parse->sortClause);
7744
7745 /* We don't support cases where there are any SRFs in the targetlist */
7746 if (parse->hasTargetSRFs)
7747 return;
7748
7749 /* Save the input_rel as outerrel in fpinfo */
7750 fpinfo->outerrel = input_rel;
7751
7752 /*
7753 * Copy foreign table, foreign server, user mapping, FDW options etc.
7754 * details from the input relation's fpinfo.
7755 */
7756 fpinfo->table = ifpinfo->table;
7757 fpinfo->server = ifpinfo->server;
7758 fpinfo->user = ifpinfo->user;
7760
7761 /*
7762 * If the input_rel is a base or join relation, we would already have
7763 * considered pushing down the final sort to the remote server when
7764 * creating pre-sorted foreign paths for that relation, because the
7765 * query_pathkeys is set to the root->sort_pathkeys in that case (see
7766 * standard_qp_callback()).
7767 */
7768 if (input_rel->reloptkind == RELOPT_BASEREL ||
7769 input_rel->reloptkind == RELOPT_JOINREL)
7770 {
7771 Assert(root->query_pathkeys == root->sort_pathkeys);
7772
7773 /* Safe to push down if the query_pathkeys is safe to push down */
7774 fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
7775
7776 return;
7777 }
7778
7779 /* The input_rel should be a grouping relation */
7780 Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
7781 ifpinfo->stage == UPPERREL_GROUP_AGG);
7782
7783 /*
7784 * We try to create a path below by extending a simple foreign path for
7785 * the underlying grouping relation to perform the final sort remotely,
7786 * which is stored into the fdw_private list of the resulting path.
7787 */
7788
7789 /* Assess if it is safe to push down the final sort */
7790 foreach(lc, root->sort_pathkeys)
7791 {
7793 EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
7794
7795 /*
7796 * is_foreign_expr would detect volatile expressions as well, but
7797 * checking ec_has_volatile here saves some cycles.
7798 */
7799 if (pathkey_ec->ec_has_volatile)
7800 return;
7801
7802 /*
7803 * Can't push down the sort if pathkey's opfamily is not shippable.
7804 */
7806 fpinfo))
7807 return;
7808
7809 /*
7810 * The EC must contain a shippable EM that is computed in input_rel's
7811 * reltarget, else we can't push down the sort.
7812 */
7814 pathkey_ec,
7815 input_rel) == NULL)
7816 return;
7817 }
7818
7819 /* Safe to push down */
7820 fpinfo->pushdown_safe = true;
7821
7822 /* Construct PgFdwPathExtraData */
7824 fpextra->target = root->upper_targets[UPPERREL_ORDERED];
7825 fpextra->has_final_sort = true;
7826
7827 /* Estimate the costs of performing the final sort remotely */
7829 &rows, &width, &disabled_nodes,
7830 &startup_cost, &total_cost);
7831
7832 /*
7833 * Build the fdw_private list that will be used by postgresGetForeignPlan.
7834 * Items in the list must match order in enum FdwPathPrivateIndex.
7835 */
7836 fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
7837
7838 /* Create foreign ordering path */
7840 input_rel,
7841 root->upper_targets[UPPERREL_ORDERED],
7842 rows,
7843 disabled_nodes,
7844 startup_cost,
7845 total_cost,
7846 root->sort_pathkeys,
7847 NULL, /* no extra plan */
7848 NIL, /* no fdw_restrictinfo
7849 * list */
7850 fdw_private);
7851
7852 /* and add it to the ordered_rel */
7854}
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 6949 of file postgres_fdw.c.

6951{
6952 List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6953 ListCell *lc;
6954
6956
6957 /*
6958 * Before creating sorted paths, arrange for the passed-in EPQ path, if
6959 * any, to return columns needed by the parent ForeignScan node so that
6960 * they will propagate up through Sort nodes injected below, if necessary.
6961 */
6963 {
6964 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6965 PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6966
6967 /* Include columns required for evaluating PHVs in the tlist. */
6969 pull_var_clause((Node *) target->exprs,
6971
6972 /* Include columns required for evaluating the local conditions. */
6973 foreach(lc, fpinfo->local_conds)
6974 {
6976
6978 pull_var_clause((Node *) rinfo->clause,
6980 }
6981
6982 /*
6983 * If we have added any new columns, adjust the tlist of the EPQ path.
6984 *
6985 * Note: the plan created using this path will only be used to execute
6986 * EPQ checks, where accuracy of the plan cost and width estimates
6987 * would not be important, so we do not do set_pathtarget_cost_width()
6988 * for the new pathtarget here. See also postgresGetForeignPlan().
6989 */
6990 if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6991 {
6992 /* The EPQ path is a join path, so it is projection-capable. */
6994
6995 /*
6996 * Use create_projection_path() here, so as to avoid modifying it
6997 * in place.
6998 */
7000 rel,
7001 epq_path,
7002 target);
7003 }
7004 }
7005
7006 /* Create one path for each set of pathkeys we found above. */
7007 foreach(lc, useful_pathkeys_list)
7008 {
7009 double rows;
7010 int width;
7011 int disabled_nodes;
7012 Cost startup_cost;
7013 Cost total_cost;
7016
7018 &rows, &width, &disabled_nodes,
7019 &startup_cost, &total_cost);
7020
7021 /*
7022 * The EPQ path must be at least as well sorted as the path itself, in
7023 * case it gets used as input to a mergejoin.
7024 */
7026 if (sorted_epq_path != NULL &&
7028 sorted_epq_path->pathkeys))
7029 sorted_epq_path = (Path *)
7031 rel,
7034 -1.0);
7035
7036 if (IS_SIMPLE_REL(rel))
7037 add_path(rel, (Path *)
7039 NULL,
7040 rows,
7041 disabled_nodes,
7042 startup_cost,
7043 total_cost,
7045 rel->lateral_relids,
7047 NIL, /* no fdw_restrictinfo
7048 * list */
7049 NIL));
7050 else
7051 add_path(rel, (Path *)
7053 NULL,
7054 rows,
7055 disabled_nodes,
7056 startup_cost,
7057 total_cost,
7059 rel->lateral_relids,
7061 restrictlist,
7062 NIL));
7063 }
7064}
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:135
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 3855 of file postgres_fdw.c.

3863{
3864 /*
3865 * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3866 * side is unlikely to generate properly-sorted output, so it would need
3867 * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3868 * if the GROUP BY clause is sort-able but isn't a superset of the given
3869 * pathkeys, adjust the costs with that function. Otherwise, adjust the
3870 * costs by applying the same heuristic as for the scan or join case.
3871 */
3872 if (!grouping_is_sortable(root->processed_groupClause) ||
3873 !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3874 {
3875 Path sort_path; /* dummy for result of cost_sort */
3876
3878 root,
3879 pathkeys,
3880 0,
3882 retrieved_rows,
3883 width,
3884 0.0,
3885 work_mem,
3886 limit_tuples);
3887
3888 *p_startup_cost = sort_path.startup_cost;
3889 *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3890 }
3891 else
3892 {
3893 /*
3894 * The default extra cost seems too large for foreign-grouping cases;
3895 * add 1/4th of that default.
3896 */
3898 - 1.0) * 0.25;
3899
3902 }
3903}
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:2201
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 5522 of file postgres_fdw.c.

5523{
5524 int targrows = astate->targrows;
5525 int pos; /* array index to store tuple in */
5526 MemoryContext oldcontext;
5527
5528 /* Always increment sample row counter. */
5529 astate->samplerows += 1;
5530
5531 /*
5532 * Determine the slot where this sample row should be stored. Set pos to
5533 * negative value to indicate the row should be skipped.
5534 */
5535 if (astate->numrows < targrows)
5536 {
5537 /* First targrows rows are always included into the sample */
5538 pos = astate->numrows++;
5539 }
5540 else
5541 {
5542 /*
5543 * Now we start replacing tuples in the sample until we reach the end
5544 * of the relation. Same algorithm as in acquire_sample_rows in
5545 * analyze.c; see Jeff Vitter's paper.
5546 */
5547 if (astate->rowstoskip < 0)
5548 astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5549
5550 if (astate->rowstoskip <= 0)
5551 {
5552 /* Choose a random reservoir element to replace. */
5553 pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5554 Assert(pos >= 0 && pos < targrows);
5555 heap_freetuple(astate->rows[pos]);
5556 }
5557 else
5558 {
5559 /* Skip this tuple. */
5560 pos = -1;
5561 }
5562
5563 astate->rowstoskip -= 1;
5564 }
5565
5566 if (pos >= 0)
5567 {
5568 /*
5569 * Create sample tuple from current result row, and store it in the
5570 * position determined above. The tuple has to be created in anl_cxt.
5571 */
5572 oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5573
5574 astate->rows[pos] = make_tuple_from_result_row(res, row,
5575 astate->rel,
5576 astate->attinmeta,
5577 astate->retrieved_attrs,
5578 NULL,
5579 astate->temp_cxt);
5580
5581 MemoryContextSwitchTo(oldcontext);
5582 }
5583}
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel, AttInMetadata *attinmeta, List *retrieved_attrs, ForeignScanState *fsstate, MemoryContext temp_context)
double sampler_random_fract(pg_prng_state *randstate)
Definition sampling.c:241
double reservoir_get_next_S(ReservoirState rs, double t, int n)
Definition sampling.c:147
ReservoirStateData rstate
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 4920 of file postgres_fdw.c.

4924{
4927 Datum *values;
4928 bool *isnull;
4930 bool *old_isnull;
4931 int i;
4932
4933 /*
4934 * Use the return tuple slot as a place to store the result tuple.
4935 */
4936 resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4937
4938 /*
4939 * Extract all the values of the scan tuple.
4940 */
4941 slot_getallattrs(slot);
4942 old_values = slot->tts_values;
4943 old_isnull = slot->tts_isnull;
4944
4945 /*
4946 * Prepare to build the result tuple.
4947 */
4949 values = resultSlot->tts_values;
4950 isnull = resultSlot->tts_isnull;
4951
4952 /*
4953 * Transpose data into proper fields of the result tuple.
4954 */
4955 for (i = 0; i < resultTupType->natts; i++)
4956 {
4957 int j = dmstate->attnoMap[i];
4958
4959 if (j == 0)
4960 {
4961 values[i] = (Datum) 0;
4962 isnull[i] = true;
4963 }
4964 else
4965 {
4966 values[i] = old_values[j - 1];
4967 isnull[i] = old_isnull[j - 1];
4968 }
4969 }
4970
4971 /*
4972 * Build the virtual tuple.
4973 */
4975
4976 /*
4977 * If we have any system columns to return, materialize a heap tuple in
4978 * the slot from column values set above and install system columns in
4979 * that tuple.
4980 */
4981 if (dmstate->hasSystemCols)
4982 {
4984
4985 /* ctid */
4986 if (dmstate->ctidAttno)
4987 {
4988 ItemPointer ctid = NULL;
4989
4990 ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4991 resultTup->t_self = *ctid;
4992 }
4993
4994 /*
4995 * And remaining columns
4996 *
4997 * Note: since we currently don't allow the target relation to appear
4998 * on the nullable side of an outer join, any system columns wouldn't
4999 * go to NULL.
5000 *
5001 * Note: no need to care about tableoid here because it will be
5002 * initialized in ExecProcessReturning().
5003 */
5007 }
5008
5009 /*
5010 * And return the result tuple.
5011 */
5012 return resultSlot;
5013}
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 7072 of file postgres_fdw.c.

7073{
7074 ListCell *lc;
7075
7076 foreach(lc, fpinfo->server->options)
7077 {
7078 DefElem *def = (DefElem *) lfirst(lc);
7079
7080 if (strcmp(def->defname, "use_remote_estimate") == 0)
7081 fpinfo->use_remote_estimate = defGetBoolean(def);
7082 else if (strcmp(def->defname, "fdw_startup_cost") == 0)
7083 (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
7084 NULL);
7085 else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
7086 (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
7087 NULL);
7088 else if (strcmp(def->defname, "extensions") == 0)
7089 fpinfo->shippable_extensions =
7091 else if (strcmp(def->defname, "fetch_size") == 0)
7092 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
7093 else if (strcmp(def->defname, "async_capable") == 0)
7094 fpinfo->async_capable = defGetBoolean(def);
7095 }
7096}
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition option.c:449
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:860

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

7105{
7106 ListCell *lc;
7107
7108 foreach(lc, fpinfo->table->options)
7109 {
7110 DefElem *def = (DefElem *) lfirst(lc);
7111
7112 if (strcmp(def->defname, "use_remote_estimate") == 0)
7113 fpinfo->use_remote_estimate = defGetBoolean(def);
7114 else if (strcmp(def->defname, "fetch_size") == 0)
7115 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
7116 else if (strcmp(def->defname, "async_capable") == 0)
7117 fpinfo->async_capable = defGetBoolean(def);
7118 }
7119}

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

5982{
5983 ListCell *lc;
5984
5985 if (va_cols == NIL)
5986 return true;
5987
5988 foreach(lc, va_cols)
5989 {
5990 char *col = strVal(lfirst(lc));
5991
5992 if (strcmp(attname, col) == 0)
5993 return true;
5994 }
5995 return false;
5996}
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 5915 of file postgres_fdw.c.

5917{
5918 TupleDesc tupdesc = RelationGetDescr(relation);
5920 int attrcnt = 0;
5921
5925 for (int i = 0; i < tupdesc->natts; i++)
5926 {
5927 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
5928 char *attname = NameStr(attr->attname);
5929 AttrNumber attnum = attr->attnum;
5930 char *remote_attname;
5932 ListCell *lc;
5933
5934 /* If a list is specified, exclude any attnames not in it. */
5935 if (!attname_in_list(attname, va_cols))
5936 continue;
5937
5938 if (!attribute_is_analyzable(relation, attnum, attr, NULL))
5939 continue;
5940
5941 /* If the column_name option is not specified, go with attname. */
5942 remote_attname = attname;
5944 foreach(lc, fc_options)
5945 {
5946 DefElem *def = (DefElem *) lfirst(lc);
5947
5948 if (strcmp(def->defname, "column_name") == 0)
5949 {
5950 remote_attname = defGetString(def);
5951 break;
5952 }
5953 }
5954
5955 if (attrcnt > 0)
5958
5959 remattrmap[attrcnt].local_attnum = attnum;
5960 strncpy(remattrmap[attrcnt].local_attname, attname, NAMEDATALEN);
5961 strncpy(remattrmap[attrcnt].remote_attname, remote_attname, NAMEDATALEN);
5962 remattrmap[attrcnt].res_index = -1;
5963 attrcnt++;
5964 }
5966
5967 /* Sort mapping by remote attribute name if needed. */
5968 if (attrcnt > 1)
5970
5971 *p_attrcnt = attrcnt;
5972 return remattrmap;
5973}
int16 AttrNumber
Definition attnum.h:21
#define NameStr(name)
Definition c.h:835
bool attribute_is_analyzable(Relation onerel, int attnum, Form_pg_attribute attr, int *p_attstattarget)
Definition analyze.c:1176
#define palloc_array(type, count)
Definition fe_memutils.h:76
List * GetForeignColumnOptions(Oid relid, AttrNumber attnum)
Definition foreign.c:324
int16 attnum
FormData_pg_attribute * Form_pg_attribute
#define NAMEDATALEN
#define qsort(a, b, c, d)
Definition port.h:495
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
const char * quote_identifier(const char *ident)
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, fb(), GetForeignColumnOptions(), i, initStringInfo(), lfirst, NAMEDATALEN, NameStr, TupleDescData::natts, palloc_array, qsort, quote_identifier(), 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 4603 of file postgres_fdw.c.

4604{
4605 bool have_wholerow = false;
4606 List *tlist = NIL;
4607 List *vars;
4608 ListCell *lc;
4609
4610 Assert(returningList);
4611
4612 vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4613
4614 /*
4615 * If there's a whole-row reference to the target relation, then we'll
4616 * need all the columns of the relation.
4617 */
4618 foreach(lc, vars)
4619 {
4620 Var *var = (Var *) lfirst(lc);
4621
4622 if (IsA(var, Var) &&
4623 var->varno == rtindex &&
4625 {
4626 have_wholerow = true;
4627 break;
4628 }
4629 }
4630
4631 if (have_wholerow)
4632 {
4633 TupleDesc tupdesc = RelationGetDescr(rel);
4634 int i;
4635
4636 for (i = 1; i <= tupdesc->natts; i++)
4637 {
4638 Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4639 Var *var;
4640
4641 /* Ignore dropped attributes. */
4642 if (attr->attisdropped)
4643 continue;
4644
4645 var = makeVar(rtindex,
4646 i,
4647 attr->atttypid,
4648 attr->atttypmod,
4649 attr->attcollation,
4650 0);
4651
4652 tlist = lappend(tlist,
4653 makeTargetEntry((Expr *) var,
4654 list_length(tlist) + 1,
4655 NULL,
4656 false));
4657 }
4658 }
4659
4660 /* Now add any remaining columns to tlist. */
4661 foreach(lc, vars)
4662 {
4663 Var *var = (Var *) lfirst(lc);
4664
4665 /*
4666 * No need for whole-row references to the target relation. We don't
4667 * need system columns other than ctid and oid either, since those are
4668 * set locally.
4669 */
4670 if (IsA(var, Var) &&
4671 var->varno == rtindex &&
4672 var->varattno <= InvalidAttrNumber &&
4674 continue; /* don't need it */
4675
4676 if (tlist_member((Expr *) var, tlist))
4677 continue; /* already got it */
4678
4679 tlist = lappend(tlist,
4680 makeTargetEntry((Expr *) var,
4681 list_length(tlist) + 1,
4682 NULL,
4683 false));
4684 }
4685
4686 list_free(vars);
4687
4688 return tlist;
4689}
#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 4153 of file postgres_fdw.c.

4155{
4156 char sql[64];
4157 PGresult *res;
4158
4159 snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
4160 res = pgfdw_exec_query(conn, sql, conn_state);
4161 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4162 pgfdw_report_error(res, conn, sql);
4163 PQclear(res);
4164}
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:260
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 8386 of file postgres_fdw.c.

8387{
8388 /* The request would have been pending for a callback */
8389 Assert(areq->callback_pending);
8390
8391 /* Unlike AsyncNotify, we unset callback_pending ourselves */
8392 areq->callback_pending = false;
8393
8394 /* We begin a fetch afterwards if necessary; don't fetch */
8396
8397 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
8399
8400 /* Also, we do instrumentation ourselves, if required */
8401 if (areq->requestee->instrument)
8402 InstrUpdateTupleCount(areq->requestee->instrument,
8403 TupIsNull(areq->result) ? 0.0 : 1.0);
8404}
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 8577 of file postgres_fdw.c.

8578{
8580 Relation rel = errpos->rel;
8581 ForeignScanState *fsstate = errpos->fsstate;
8582 const char *attname = NULL;
8583 const char *relname = NULL;
8584 bool is_wholerow = false;
8585
8586 /*
8587 * If we're in a scan node, always use aliases from the rangetable, for
8588 * consistency between the simple-relation and remote-join cases. Look at
8589 * the relation's tupdesc only if we're not in a scan node.
8590 */
8591 if (fsstate)
8592 {
8593 /* ForeignScan case */
8595 int varno = 0;
8596 AttrNumber colno = 0;
8597
8598 if (fsplan->scan.scanrelid > 0)
8599 {
8600 /* error occurred in a scan against a foreign table */
8601 varno = fsplan->scan.scanrelid;
8602 colno = errpos->cur_attno;
8603 }
8604 else
8605 {
8606 /* error occurred in a scan against a foreign join */
8608
8609 tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
8610 errpos->cur_attno - 1);
8611
8612 /*
8613 * Target list can have Vars and expressions. For Vars, we can
8614 * get some information, however for expressions we can't. Thus
8615 * for expressions, just show generic context message.
8616 */
8617 if (IsA(tle->expr, Var))
8618 {
8619 Var *var = (Var *) tle->expr;
8620
8621 varno = var->varno;
8622 colno = var->varattno;
8623 }
8624 }
8625
8626 if (varno > 0)
8627 {
8628 EState *estate = fsstate->ss.ps.state;
8629 RangeTblEntry *rte = exec_rt_fetch(varno, estate);
8630
8631 relname = rte->eref->aliasname;
8632
8633 if (colno == 0)
8634 is_wholerow = true;
8635 else if (colno > 0 && colno <= list_length(rte->eref->colnames))
8636 attname = strVal(list_nth(rte->eref->colnames, colno - 1));
8637 else if (colno == SelfItemPointerAttributeNumber)
8638 attname = "ctid";
8639 }
8640 }
8641 else if (rel)
8642 {
8643 /* Non-ForeignScan case (we should always have a rel here) */
8644 TupleDesc tupdesc = RelationGetDescr(rel);
8645
8647 if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
8648 {
8649 Form_pg_attribute attr = TupleDescAttr(tupdesc,
8650 errpos->cur_attno - 1);
8651
8652 attname = NameStr(attr->attname);
8653 }
8654 else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
8655 attname = "ctid";
8656 }
8657
8658 if (relname && is_wholerow)
8659 errcontext("whole-row reference to foreign table \"%s\"", relname);
8660 else if (relname && attname)
8661 errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
8662 else
8663 errcontext("processing expression at position %d in select list",
8664 errpos->cur_attno);
8665}
Datum arg
Definition elog.c:1322
#define errcontext
Definition elog.h:200
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition executor.h:710
#define castNode(_type_, nodeptr)
Definition nodes.h:182
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:1201
EState * state
Definition execnodes.h:1203
PlanState ps
Definition execnodes.h:1659

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

4465{
4466 const char **p_values;
4467 int i;
4468 int j;
4469 int pindex = 0;
4470 MemoryContext oldcontext;
4471
4472 oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4473
4474 p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4475
4476 /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4477 Assert(!(tupleid != NULL && numSlots > 1));
4478
4479 /* 1st parameter should be ctid, if it's in use */
4480 if (tupleid != NULL)
4481 {
4482 Assert(numSlots == 1);
4483 /* don't need set_transmission_modes for TID output */
4486 pindex++;
4487 }
4488
4489 /* get following parameters from slots */
4490 if (slots != NULL && fmstate->target_attrs != NIL)
4491 {
4492 TupleDesc tupdesc = RelationGetDescr(fmstate->rel);
4493 int nestlevel;
4494 ListCell *lc;
4495
4497
4498 for (i = 0; i < numSlots; i++)
4499 {
4500 j = (tupleid != NULL) ? 1 : 0;
4501 foreach(lc, fmstate->target_attrs)
4502 {
4503 int attnum = lfirst_int(lc);
4504 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
4505 Datum value;
4506 bool isnull;
4507
4508 /* Ignore generated columns; they are set to DEFAULT */
4509 if (attr->attgenerated)
4510 continue;
4511 value = slot_getattr(slots[i], attnum, &isnull);
4512 if (isnull)
4513 p_values[pindex] = NULL;
4514 else
4516 value);
4517 pindex++;
4518 j++;
4519 }
4520 }
4521
4523 }
4524
4525 Assert(pindex == fmstate->p_nums * numSlots);
4526
4527 MemoryContextSwitchTo(oldcontext);
4528
4529 return p_values;
4530}
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition fmgr.c:1684
static struct @177 value
void * palloc(Size size)
Definition mcxt.c:1387
#define lfirst_int(lc)
Definition pg_list.h:173
static Datum PointerGetDatum(const void *X)
Definition postgres.h:342
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 3940 of file postgres_fdw.c.

3941{
3942 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3943 ExprContext *econtext = node->ss.ps.ps_ExprContext;
3944 int numParams = fsstate->numParams;
3945 const char **values = fsstate->param_values;
3946 PGconn *conn = fsstate->conn;
3948 PGresult *res;
3949
3950 /* First, process a pending asynchronous request, if any. */
3951 if (fsstate->conn_state->pendingAreq)
3952 process_pending_request(fsstate->conn_state->pendingAreq);
3953
3954 /*
3955 * Construct array of query parameter values in text format. We do the
3956 * conversions in the short-lived per-tuple context, so as not to cause a
3957 * memory leak over repeated scans.
3958 */
3959 if (numParams > 0)
3960 {
3961 MemoryContext oldcontext;
3962
3963 oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3964
3965 process_query_params(econtext,
3966 fsstate->param_flinfo,
3967 fsstate->param_exprs,
3968 values);
3969
3970 MemoryContextSwitchTo(oldcontext);
3971 }
3972
3973 /* Construct the DECLARE CURSOR command */
3975 appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3976 fsstate->cursor_number, fsstate->query);
3977
3978 /*
3979 * Notice that we pass NULL for paramTypes, thus forcing the remote server
3980 * to infer types for all parameters. Since we explicitly cast every
3981 * parameter (see deparse.c), the "inference" is trivial and will produce
3982 * the desired result. This allows us to avoid assuming that the remote
3983 * server has the same OIDs we do for the parameters' types.
3984 */
3985 if (!PQsendQueryParams(conn, buf.data, numParams,
3986 NULL, values, NULL, NULL, 0))
3988
3989 /*
3990 * Get the result, and check for success.
3991 */
3992 res = pgfdw_get_result(conn);
3993 if (PQresultStatus(res) != PGRES_COMMAND_OK)
3994 pgfdw_report_error(res, conn, fsstate->query);
3995 PQclear(res);
3996
3997 /* Mark the cursor as created, and show no tuples have been retrieved */
3998 fsstate->cursor_exists = true;
3999 fsstate->tuples = NULL;
4000 fsstate->num_tuples = 0;
4001 fsstate->next_tuple = 0;
4002 fsstate->fetch_ct_2 = 0;
4003 fsstate->eof_reached = false;
4004
4005 /* Clean up */
4006 pfree(buf.data);
4007}
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:1616
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:1242

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

4182{
4184 Relation rel = resultRelInfo->ri_RelationDesc;
4185 TupleDesc tupdesc = RelationGetDescr(rel);
4186 Oid userid;
4190 Oid typefnoid;
4191 bool isvarlena;
4192 ListCell *lc;
4193
4194 /* Begin constructing PgFdwModifyState. */
4196 fmstate->rel = rel;
4197
4198 /* Identify which user to do the remote access as. */
4199 userid = ExecGetResultRelCheckAsUser(resultRelInfo, estate);
4200
4201 /* Get info about foreign table. */
4203 user = GetUserMapping(userid, table->serverid);
4204
4205 /* Open connection; report that we'll create a prepared statement. */
4206 fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
4207 fmstate->p_name = NULL; /* prepared statement not made yet */
4208
4209 /* Set up remote query information. */
4210 fmstate->query = query;
4211 if (operation == CMD_INSERT)
4212 {
4213 fmstate->query = pstrdup(fmstate->query);
4214 fmstate->orig_query = pstrdup(fmstate->query);
4215 }
4216 fmstate->target_attrs = target_attrs;
4217 fmstate->values_end = values_end;
4218 fmstate->has_returning = has_returning;
4219 fmstate->retrieved_attrs = retrieved_attrs;
4220
4221 /* Create context for per-tuple temp workspace. */
4222 fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
4223 "postgres_fdw temporary data",
4225
4226 /* Prepare for input conversion of RETURNING results. */
4227 if (fmstate->has_returning)
4228 fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
4229
4230 /* Prepare for output conversion of parameters used in prepared stmt. */
4231 n_params = list_length(fmstate->target_attrs) + 1;
4232 fmstate->p_flinfo = palloc0_array(FmgrInfo, n_params);
4233 fmstate->p_nums = 0;
4234
4236 {
4237 Assert(subplan != NULL);
4238
4239 /* Find the ctid resjunk column in the subplan's result */
4240 fmstate->ctidAttno = ExecFindJunkAttributeInTlist(subplan->targetlist,
4241 "ctid");
4242 if (!AttributeNumberIsValid(fmstate->ctidAttno))
4243 elog(ERROR, "could not find junk ctid column");
4244
4245 /* First transmittable parameter will be ctid */
4247 fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4248 fmstate->p_nums++;
4249 }
4250
4252 {
4253 /* Set up for remaining transmittable parameters */
4254 foreach(lc, fmstate->target_attrs)
4255 {
4256 int attnum = lfirst_int(lc);
4257 Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4258
4259 Assert(!attr->attisdropped);
4260
4261 /* Ignore generated columns; they are set to DEFAULT */
4262 if (attr->attgenerated)
4263 continue;
4264 getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
4265 fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4266 fmstate->p_nums++;
4267 }
4268 }
4269
4270 Assert(fmstate->p_nums <= n_params);
4271
4272 /* Set batch_size from foreign server/table options. */
4273 if (operation == CMD_INSERT)
4274 fmstate->batch_size = get_batch_size_option(rel);
4275
4276 fmstate->num_slots = 1;
4277
4278 /* Initialize auxiliary state */
4279 fmstate->aux_fmstate = NULL;
4280
4281 return fmstate;
4282}
#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:77
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:3129
char * pstrdup(const char *in)
Definition mcxt.c:1781
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
@ CMD_INSERT
Definition nodes.h:277
@ CMD_DELETE
Definition nodes.h:278
@ CMD_UPDATE
Definition nodes.h:276
static const struct lconv_member_info table[]
static char * user
Definition pg_regress.c: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:746
List * targetlist
Definition plannodes.h:235
Relation ri_RelationDesc
Definition execnodes.h:513

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

4580{
4581 char sql[64];
4582 PGresult *res;
4583
4584 /* do nothing if the query is not allocated */
4585 if (!fmstate->p_name)
4586 return;
4587
4588 snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4589 res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4590 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4591 pgfdw_report_error(res, fmstate->conn, sql);
4592 PQclear(res);
4593 pfree(fmstate->p_name);
4594 fmstate->p_name = NULL;
4595}

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

3914{
3916 Expr *expr = em->em_expr;
3917
3918 /*
3919 * If we've identified what we're processing in the current scan, we only
3920 * want to match that expression.
3921 */
3922 if (state->current != NULL)
3923 return equal(expr, state->current);
3924
3925 /*
3926 * Otherwise, ignore anything we've already processed.
3927 */
3928 if (list_member(state->already_used, expr))
3929 return false;
3930
3931 /* This is the new target to process. */
3932 state->current = expr;
3933 return true;
3934}
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 3304 of file postgres_fdw.c.

3312{
3313 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
3314 double rows;
3315 double retrieved_rows;
3316 int width;
3317 int disabled_nodes = 0;
3318 Cost startup_cost;
3319 Cost total_cost;
3320
3321 /* Make sure the core code has set up the relation's reltarget */
3322 Assert(foreignrel->reltarget);
3323
3324 /*
3325 * If the table or the server is configured to use remote estimates,
3326 * connect to the foreign server and execute EXPLAIN to estimate the
3327 * number of rows selected by the restriction+join clauses. Otherwise,
3328 * estimate rows using whatever statistics we have locally, in a way
3329 * similar to ordinary tables.
3330 */
3331 if (fpinfo->use_remote_estimate)
3332 {
3335 StringInfoData sql;
3336 PGconn *conn;
3339 List *fdw_scan_tlist = NIL;
3340 List *remote_conds;
3341
3342 /* Required only to be passed to deparseSelectStmtForRel */
3343 List *retrieved_attrs;
3344
3345 /*
3346 * param_join_conds might contain both clauses that are safe to send
3347 * across, and clauses that aren't.
3348 */
3351
3352 /* Build the list of columns to be fetched from the foreign server. */
3353 if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
3354 fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
3355 else
3356 fdw_scan_tlist = NIL;
3357
3358 /*
3359 * The complete list of remote conditions includes everything from
3360 * baserestrictinfo plus any extra join_conds relevant to this
3361 * particular path.
3362 */
3363 remote_conds = list_concat(remote_param_join_conds,
3364 fpinfo->remote_conds);
3365
3366 /*
3367 * Construct EXPLAIN query including the desired SELECT, FROM, and
3368 * WHERE clauses. Params and other-relation Vars are replaced by dummy
3369 * values, so don't request params_list.
3370 */
3371 initStringInfo(&sql);
3372 appendStringInfoString(&sql, "EXPLAIN ");
3373 deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
3374 remote_conds, pathkeys,
3375 fpextra ? fpextra->has_final_sort : false,
3376 fpextra ? fpextra->has_limit : false,
3377 false, &retrieved_attrs, NULL);
3378
3379 /* Get the remote estimate */
3380 conn = GetConnection(fpinfo->user, false, NULL);
3381 get_remote_estimate(sql.data, conn, &rows, &width,
3382 &startup_cost, &total_cost);
3384
3385 retrieved_rows = rows;
3386
3387 /* Factor in the selectivity of the locally-checked quals */
3390 foreignrel->relid,
3391 JOIN_INNER,
3392 NULL);
3393 local_sel *= fpinfo->local_conds_sel;
3394
3395 rows = clamp_row_est(rows * local_sel);
3396
3397 /* Add in the eval cost of the locally-checked quals */
3398 startup_cost += fpinfo->local_conds_cost.startup;
3399 total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3401 startup_cost += local_cost.startup;
3402 total_cost += local_cost.per_tuple * retrieved_rows;
3403
3404 /*
3405 * Add in tlist eval cost for each output row. In case of an
3406 * aggregate, some of the tlist expressions such as grouping
3407 * expressions will be evaluated remotely, so adjust the costs.
3408 */
3409 startup_cost += foreignrel->reltarget->cost.startup;
3410 total_cost += foreignrel->reltarget->cost.startup;
3411 total_cost += foreignrel->reltarget->cost.per_tuple * rows;
3412 if (IS_UPPER_REL(foreignrel))
3413 {
3415
3416 cost_qual_eval(&tlist_cost, fdw_scan_tlist, root);
3417 startup_cost -= tlist_cost.startup;
3418 total_cost -= tlist_cost.startup;
3419 total_cost -= tlist_cost.per_tuple * rows;
3420 }
3421 }
3422 else
3423 {
3424 Cost run_cost = 0;
3425
3426 /*
3427 * We don't support join conditions in this mode (hence, no
3428 * parameterized paths can be made).
3429 */
3431
3432 /*
3433 * We will come here again and again with different set of pathkeys or
3434 * additional post-scan/join-processing steps that caller wants to
3435 * cost. We don't need to calculate the cost/size estimates for the
3436 * underlying scan, join, or grouping each time. Instead, use those
3437 * estimates if we have cached them already.
3438 */
3439 if (fpinfo->rel_startup_cost >= 0 && fpinfo->rel_total_cost >= 0)
3440 {
3441 Assert(fpinfo->retrieved_rows >= 0);
3442
3443 rows = fpinfo->rows;
3444 retrieved_rows = fpinfo->retrieved_rows;
3445 width = fpinfo->width;
3446 startup_cost = fpinfo->rel_startup_cost;
3447 run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
3448
3449 /*
3450 * If we estimate the costs of a foreign scan or a foreign join
3451 * with additional post-scan/join-processing steps, the scan or
3452 * join costs obtained from the cache wouldn't yet contain the
3453 * eval costs for the final scan/join target, which would've been
3454 * updated by apply_scanjoin_target_to_paths(); add the eval costs
3455 * now.
3456 */
3457 if (fpextra && !IS_UPPER_REL(foreignrel))
3458 {
3459 /* Shouldn't get here unless we have LIMIT */
3460 Assert(fpextra->has_limit);
3461 Assert(foreignrel->reloptkind == RELOPT_BASEREL ||
3462 foreignrel->reloptkind == RELOPT_JOINREL);
3463 startup_cost += foreignrel->reltarget->cost.startup;
3464 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3465 }
3466 }
3467 else if (IS_JOIN_REL(foreignrel))
3468 {
3473 double nrows;
3474
3475 /* Use rows/width estimates made by the core code. */
3476 rows = foreignrel->rows;
3477 width = foreignrel->reltarget->width;
3478
3479 /* For join we expect inner and outer relations set */
3480 Assert(fpinfo->innerrel && fpinfo->outerrel);
3481
3482 fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
3483 fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
3484
3485 /* Estimate of number of rows in cross product */
3486 nrows = fpinfo_i->rows * fpinfo_o->rows;
3487
3488 /*
3489 * Back into an estimate of the number of retrieved rows. Just in
3490 * case this is nuts, clamp to at most nrows.
3491 */
3492 retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3493 retrieved_rows = Min(retrieved_rows, nrows);
3494
3495 /*
3496 * The cost of foreign join is estimated as cost of generating
3497 * rows for the joining relations + cost for applying quals on the
3498 * rows.
3499 */
3500
3501 /*
3502 * Calculate the cost of clauses pushed down to the foreign server
3503 */
3504 cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
3505 /* Calculate the cost of applying join clauses */
3506 cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
3507
3508 /*
3509 * Startup cost includes startup cost of joining relations and the
3510 * startup cost for join and other clauses. We do not include the
3511 * startup cost specific to join strategy (e.g. setting up hash
3512 * tables) since we do not know what strategy the foreign server
3513 * is going to use.
3514 */
3515 startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
3516 startup_cost += join_cost.startup;
3517 startup_cost += remote_conds_cost.startup;
3518 startup_cost += fpinfo->local_conds_cost.startup;
3519
3520 /*
3521 * Run time cost includes:
3522 *
3523 * 1. Run time cost (total_cost - startup_cost) of relations being
3524 * joined
3525 *
3526 * 2. Run time cost of applying join clauses on the cross product
3527 * of the joining relations.
3528 *
3529 * 3. Run time cost of applying pushed down other clauses on the
3530 * result of join
3531 *
3532 * 4. Run time cost of applying nonpushable other clauses locally
3533 * on the result fetched from the foreign server.
3534 */
3535 run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
3536 run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
3537 run_cost += nrows * join_cost.per_tuple;
3538 nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
3539 run_cost += nrows * remote_conds_cost.per_tuple;
3540 run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3541
3542 /* Add in tlist eval cost for each output row */
3543 startup_cost += foreignrel->reltarget->cost.startup;
3544 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3545 }
3546 else if (IS_UPPER_REL(foreignrel))
3547 {
3548 RelOptInfo *outerrel = fpinfo->outerrel;
3551 double input_rows;
3552 int numGroupCols;
3553 double numGroups = 1;
3554
3555 /* The upper relation should have its outer relation set */
3556 Assert(outerrel);
3557 /* and that outer relation should have its reltarget set */
3558 Assert(outerrel->reltarget);
3559
3560 /*
3561 * This cost model is mixture of costing done for sorted and
3562 * hashed aggregates in cost_agg(). We are not sure which
3563 * strategy will be considered at remote side, thus for
3564 * simplicity, we put all startup related costs in startup_cost
3565 * and all finalization and run cost are added in total_cost.
3566 */
3567
3568 ofpinfo = (PgFdwRelationInfo *) outerrel->fdw_private;
3569
3570 /* Get rows from input rel */
3571 input_rows = ofpinfo->rows;
3572
3573 /* Collect statistics about aggregates for estimating costs. */
3574 if (root->parse->hasAggs)
3575 {
3577 }
3578
3579 /* Get number of grouping columns and possible number of groups */
3580 numGroupCols = list_length(root->processed_groupClause);
3581 numGroups = estimate_num_groups(root,
3582 get_sortgrouplist_exprs(root->processed_groupClause,
3583 fpinfo->grouped_tlist),
3584 input_rows, NULL, NULL);
3585
3586 /*
3587 * Get the retrieved_rows and rows estimates. If there are HAVING
3588 * quals, account for their selectivity.
3589 */
3590 if (root->hasHavingQual)
3591 {
3592 /* Factor in the selectivity of the remotely-checked quals */
3593 retrieved_rows =
3594 clamp_row_est(numGroups *
3596 fpinfo->remote_conds,
3597 0,
3598 JOIN_INNER,
3599 NULL));
3600 /* Factor in the selectivity of the locally-checked quals */
3601 rows = clamp_row_est(retrieved_rows * fpinfo->local_conds_sel);
3602 }
3603 else
3604 {
3605 rows = retrieved_rows = numGroups;
3606 }
3607
3608 /* Use width estimate made by the core code. */
3609 width = foreignrel->reltarget->width;
3610
3611 /*-----
3612 * Startup cost includes:
3613 * 1. Startup cost for underneath input relation, adjusted for
3614 * tlist replacement by apply_scanjoin_target_to_paths()
3615 * 2. Cost of performing aggregation, per cost_agg()
3616 *-----
3617 */
3618 startup_cost = ofpinfo->rel_startup_cost;
3619 startup_cost += outerrel->reltarget->cost.startup;
3620 startup_cost += aggcosts.transCost.startup;
3621 startup_cost += aggcosts.transCost.per_tuple * input_rows;
3622 startup_cost += aggcosts.finalCost.startup;
3623 startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
3624
3625 /*-----
3626 * Run time cost includes:
3627 * 1. Run time cost of underneath input relation, adjusted for
3628 * tlist replacement by apply_scanjoin_target_to_paths()
3629 * 2. Run time cost of performing aggregation, per cost_agg()
3630 *-----
3631 */
3632 run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
3633 run_cost += outerrel->reltarget->cost.per_tuple * input_rows;
3634 run_cost += aggcosts.finalCost.per_tuple * numGroups;
3635 run_cost += cpu_tuple_cost * numGroups;
3636
3637 /* Account for the eval cost of HAVING quals, if any */
3638 if (root->hasHavingQual)
3639 {
3641
3642 /* Add in the eval cost of the remotely-checked quals */
3643 cost_qual_eval(&remote_cost, fpinfo->remote_conds, root);
3644 startup_cost += remote_cost.startup;
3645 run_cost += remote_cost.per_tuple * numGroups;
3646 /* Add in the eval cost of the locally-checked quals */
3647 startup_cost += fpinfo->local_conds_cost.startup;
3648 run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3649 }
3650
3651 /* Add in tlist eval cost for each output row */
3652 startup_cost += foreignrel->reltarget->cost.startup;
3653 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3654 }
3655 else
3656 {
3658
3659 /* Use rows/width estimates made by set_baserel_size_estimates. */
3660 rows = foreignrel->rows;
3661 width = foreignrel->reltarget->width;
3662
3663 /*
3664 * Back into an estimate of the number of retrieved rows. Just in
3665 * case this is nuts, clamp to at most foreignrel->tuples.
3666 */
3667 retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3668 retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
3669
3670 /*
3671 * Cost as though this were a seqscan, which is pessimistic. We
3672 * effectively imagine the local_conds are being evaluated
3673 * remotely, too.
3674 */
3675 startup_cost = 0;
3676 run_cost = 0;
3677 run_cost += seq_page_cost * foreignrel->pages;
3678
3679 startup_cost += foreignrel->baserestrictcost.startup;
3681 run_cost += cpu_per_tuple * foreignrel->tuples;
3682
3683 /* Add in tlist eval cost for each output row */
3684 startup_cost += foreignrel->reltarget->cost.startup;
3685 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3686 }
3687
3688 /*
3689 * Without remote estimates, we have no real way to estimate the cost
3690 * of generating sorted output. It could be free if the query plan
3691 * the remote side would have chosen generates properly-sorted output
3692 * anyway, but in most cases it will cost something. Estimate a value
3693 * high enough that we won't pick the sorted path when the ordering
3694 * isn't locally useful, but low enough that we'll err on the side of
3695 * pushing down the ORDER BY clause when it's useful to do so.
3696 */
3697 if (pathkeys != NIL)
3698 {
3699 if (IS_UPPER_REL(foreignrel))
3700 {
3701 Assert(foreignrel->reloptkind == RELOPT_UPPER_REL &&
3702 fpinfo->stage == UPPERREL_GROUP_AGG);
3703
3704 /*
3705 * We can only get here when this function is called from
3706 * add_foreign_ordered_paths() or add_foreign_final_paths();
3707 * in which cases, the passed-in fpextra should not be NULL.
3708 */
3709 Assert(fpextra);
3711 retrieved_rows, width,
3712 fpextra->limit_tuples,
3713 &disabled_nodes,
3714 &startup_cost, &run_cost);
3715 }
3716 else
3717 {
3718 startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3719 run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3720 }
3721 }
3722
3723 total_cost = startup_cost + run_cost;
3724
3725 /* Adjust the cost estimates if we have LIMIT */
3726 if (fpextra && fpextra->has_limit)
3727 {
3728 adjust_limit_rows_costs(&rows, &startup_cost, &total_cost,
3729 fpextra->offset_est, fpextra->count_est);
3730 retrieved_rows = rows;
3731 }
3732 }
3733
3734 /*
3735 * If this includes the final sort step, the given target, which will be
3736 * applied to the resulting path, might have different expressions from
3737 * the foreignrel's reltarget (see make_sort_input_target()); adjust tlist
3738 * eval costs.
3739 */
3740 if (fpextra && fpextra->has_final_sort &&
3741 fpextra->target != foreignrel->reltarget)
3742 {
3743 QualCost oldcost = foreignrel->reltarget->cost;
3744 QualCost newcost = fpextra->target->cost;
3745
3746 startup_cost += newcost.startup - oldcost.startup;
3747 total_cost += newcost.startup - oldcost.startup;
3748 total_cost += (newcost.per_tuple - oldcost.per_tuple) * rows;
3749 }
3750
3751 /*
3752 * Cache the retrieved rows and cost estimates for scans, joins, or
3753 * groupings without any parameterization, pathkeys, or additional
3754 * post-scan/join-processing steps, before adding the costs for
3755 * transferring data from the foreign server. These estimates are useful
3756 * for costing remote joins involving this relation or costing other
3757 * remote operations on this relation such as remote sorts and remote
3758 * LIMIT restrictions, when the costs can not be obtained from the foreign
3759 * server. This function will be called at least once for every foreign
3760 * relation without any parameterization, pathkeys, or additional
3761 * post-scan/join-processing steps.
3762 */
3763 if (pathkeys == NIL && param_join_conds == NIL && fpextra == NULL)
3764 {
3765 fpinfo->retrieved_rows = retrieved_rows;
3766 fpinfo->rel_startup_cost = startup_cost;
3767 fpinfo->rel_total_cost = total_cost;
3768 }
3769
3770 /*
3771 * Add some additional cost factors to account for connection overhead
3772 * (fdw_startup_cost), transferring data across the network
3773 * (fdw_tuple_cost per retrieved row), and local manipulation of the data
3774 * (cpu_tuple_cost per retrieved row).
3775 */
3776 startup_cost += fpinfo->fdw_startup_cost;
3777 total_cost += fpinfo->fdw_startup_cost;
3778 total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
3779 total_cost += cpu_tuple_cost * retrieved_rows;
3780
3781 /*
3782 * If we have LIMIT, we should prefer performing the restriction remotely
3783 * rather than locally, as the former avoids extra row fetches from the
3784 * remote that the latter might cause. But since the core code doesn't
3785 * account for such fetches when estimating the costs of the local
3786 * restriction (see create_limit_path()), there would be no difference
3787 * between the costs of the local restriction and the costs of the remote
3788 * restriction estimated above if we don't use remote estimates (except
3789 * for the case where the foreignrel is a grouping relation, the given
3790 * pathkeys is not NIL, and the effects of a bounded sort for that rel is
3791 * accounted for in costing the remote restriction). Tweak the costs of
3792 * the remote restriction to ensure we'll prefer it if LIMIT is a useful
3793 * one.
3794 */
3795 if (!fpinfo->use_remote_estimate &&
3796 fpextra && fpextra->has_limit &&
3797 fpextra->limit_tuples > 0 &&
3798 fpextra->limit_tuples < fpinfo->rows)
3799 {
3800 Assert(fpinfo->rows > 0);
3801 total_cost -= (total_cost - startup_cost) * 0.05 *
3802 (fpinfo->rows - fpextra->limit_tuples) / fpinfo->rows;
3803 }
3804
3805 /* Return results. */
3806 *p_rows = rows;
3807 *p_width = width;
3808 *p_disabled_nodes = disabled_nodes;
3809 *p_startup_cost = startup_cost;
3810 *p_total_cost = total_cost;
3811}
#define Min(x, y)
Definition c.h:1091
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:214
void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, bool has_final_sort, bool has_limit, bool is_subquery, List **retrieved_attrs, List **params_list)
Definition deparse.c:1266
void classifyConditions(PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds, List **local_conds)
Definition deparse.c:218
List * build_tlist_to_deparse(RelOptInfo *foreignrel)
Definition deparse.c:1209
#define false
List * list_concat(List *list1, const List *list2)
Definition list.c:561
double Selectivity
Definition nodes.h:260
@ AGGSPLIT_SIMPLE
Definition nodes.h:387
void adjust_limit_rows_costs(double *rows, Cost *startup_cost, Cost *total_cost, int64 offset_est, int64 count_est)
Definition pathnode.c:3849
#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:3800
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 4730 of file postgres_fdw.c.

4731{
4733 ExprContext *econtext = node->ss.ps.ps_ExprContext;
4734 int numParams = dmstate->numParams;
4735 const char **values = dmstate->param_values;
4736
4737 /* First, process a pending asynchronous request, if any. */
4738 if (dmstate->conn_state->pendingAreq)
4739 process_pending_request(dmstate->conn_state->pendingAreq);
4740
4741 /*
4742 * Construct array of query parameter values in text format.
4743 */
4744 if (numParams > 0)
4745 process_query_params(econtext,
4746 dmstate->param_flinfo,
4747 dmstate->param_exprs,
4748 values);
4749
4750 /*
4751 * Notice that we pass NULL for paramTypes, thus forcing the remote server
4752 * to infer types for all parameters. Since we explicitly cast every
4753 * parameter (see deparse.c), the "inference" is trivial and will produce
4754 * the desired result. This allows us to avoid assuming that the remote
4755 * server has the same OIDs we do for the parameters' types.
4756 */
4757 if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4758 NULL, values, NULL, NULL, 0))
4759 pgfdw_report_error(NULL, dmstate->conn, dmstate->query);
4760
4761 /*
4762 * Get the result, and check for success.
4763 */
4764 dmstate->result = pgfdw_get_result(dmstate->conn);
4765 if (PQresultStatus(dmstate->result) !=
4766 (dmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
4768 dmstate->query);
4769
4770 /*
4771 * The result potentially needs to survive across multiple executor row
4772 * cycles, so move it to the context where the dmstate is.
4773 */
4774 dmstate->result = libpqsrv_PGresultSetParent(dmstate->result,
4776
4777 /* Get the number of rows affected. */
4778 if (dmstate->has_returning)
4779 dmstate->num_tuples = PQntuples(dmstate->result);
4780 else
4781 dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4782}
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:756

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

4298{
4300 ItemPointer ctid = NULL;
4301 const char **p_values;
4302 PGresult *res;
4303 int n_rows;
4304 StringInfoData sql;
4305
4306 /* The operation should be INSERT, UPDATE, or DELETE */
4308 operation == CMD_UPDATE ||
4310
4311 /* First, process a pending asynchronous request, if any. */
4312 if (fmstate->conn_state->pendingAreq)
4313 process_pending_request(fmstate->conn_state->pendingAreq);
4314
4315 /*
4316 * If the existing query was deparsed and prepared for a different number
4317 * of rows, rebuild it for the proper number.
4318 */
4319 if (operation == CMD_INSERT && fmstate->num_slots != *numSlots)
4320 {
4321 /* Destroy the prepared statement created previously */
4322 if (fmstate->p_name)
4324
4325 /* Build INSERT string with numSlots records in its VALUES clause. */
4326 initStringInfo(&sql);
4327 rebuildInsertSql(&sql, fmstate->rel,
4328 fmstate->orig_query, fmstate->target_attrs,
4329 fmstate->values_end, fmstate->p_nums,
4330 *numSlots - 1);
4331 pfree(fmstate->query);
4332 fmstate->query = sql.data;
4333 fmstate->num_slots = *numSlots;
4334 }
4335
4336 /* Set up the prepared statement on the remote server, if we didn't yet */
4337 if (!fmstate->p_name)
4339
4340 /*
4341 * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
4342 */
4344 {
4345 Datum datum;
4346 bool isNull;
4347
4349 fmstate->ctidAttno,
4350 &isNull);
4351 /* shouldn't ever get a null result... */
4352 if (isNull)
4353 elog(ERROR, "ctid is NULL");
4354 ctid = (ItemPointer) DatumGetPointer(datum);
4355 }
4356
4357 /* Convert parameters needed by prepared statement to text form */
4359
4360 /*
4361 * Execute the prepared statement.
4362 */
4363 if (!PQsendQueryPrepared(fmstate->conn,
4364 fmstate->p_name,
4365 fmstate->p_nums * (*numSlots),
4366 p_values,
4367 NULL,
4368 NULL,
4369 0))
4370 pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
4371
4372 /*
4373 * Get the result, and check for success.
4374 */
4375 res = pgfdw_get_result(fmstate->conn);
4376 if (PQresultStatus(res) !=
4377 (fmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
4378 pgfdw_report_error(res, fmstate->conn, fmstate->query);
4379
4380 /* Check number of rows affected, and fetch RETURNING tuple if any */
4381 if (fmstate->has_returning)
4382 {
4383 Assert(*numSlots == 1);
4384 n_rows = PQntuples(res);
4385 if (n_rows > 0)
4386 store_returning_result(fmstate, slots[0], res);
4387 }
4388 else
4389 n_rows = atoi(PQcmdTuples(res));
4390
4391 /* And clean up */
4392 PQclear(res);
4393
4394 MemoryContextReset(fmstate->temp_cxt);
4395
4396 *numSlots = n_rows;
4397
4398 /*
4399 * Return NULL if nothing was inserted/updated/deleted on the remote end
4400 */
4401 return (n_rows > 0) ? slots : NULL;
4402}
void rebuildInsertSql(StringInfo buf, Relation rel, char *orig_query, List *target_attrs, int values_end_len, int num_params, int num_rows)
Definition deparse.c:2187
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition executor.h: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:403
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:569

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

5846{
5847 StringInfoData sql;
5848 PGresult *res;
5849
5850 initStringInfo(&sql);
5852 "SELECT DISTINCT ON (attname COLLATE \"C\") attname,"
5853 " null_frac,"
5854 " avg_width,"
5855 " n_distinct,"
5856 " most_common_vals,"
5857 " most_common_freqs,"
5858 " histogram_bounds,"
5859 " correlation,");
5860
5861 /* Elements stats are supported since Postgres 9.2 */
5862 if (server_version_num >= 92000)
5864 " most_common_elems,"
5865 " most_common_elem_freqs,"
5866 " elem_count_histogram,");
5867 else
5869 " NULL, NULL, NULL,");
5870
5871 /* Range stats are supported since Postgres 17 */
5872 if (server_version_num >= 170000)
5874 " range_length_histogram,"
5875 " range_empty_frac,"
5876 " range_bounds_histogram");
5877 else
5879 " NULL, NULL, NULL,");
5880
5882 " FROM pg_catalog.pg_stats"
5883 " WHERE schemaname = ");
5886 " AND tablename = ");
5888 appendStringInfo(&sql,
5889 " AND attname = ANY('%s'::text[])",
5890 column_list);
5891
5892 /* inherited is supported since Postgres 9.0 */
5893 if (server_version_num >= 90000)
5895 " ORDER BY attname COLLATE \"C\", inherited DESC");
5896 else
5898 " ORDER BY attname COLLATE \"C\"");
5899
5900 res = pgfdw_exec_query(conn, sql.data, NULL);
5901 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5902 pgfdw_report_error(res, conn, sql.data);
5903
5904 if (PQnfields(res) != ATTSTATS_NUM_FIELDS)
5905 elog(ERROR, "unexpected result from fetch_attstats query");
5906
5907 return res;
5908}
void deparseStringLiteral(StringInfo buf, const char *val)
Definition deparse.c:2880
static int server_version_num
Definition guc_tables.c:623
#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 4013 of file postgres_fdw.c.

4014{
4015 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
4016 PGconn *conn = fsstate->conn;
4017 PGresult *res;
4018 int numrows;
4019 int i;
4020 MemoryContext oldcontext;
4021
4022 /*
4023 * We'll store the tuples in the batch_cxt. First, flush the previous
4024 * batch.
4025 */
4026 fsstate->tuples = NULL;
4027 MemoryContextReset(fsstate->batch_cxt);
4028 oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
4029
4030 if (fsstate->async_capable)
4031 {
4032 Assert(fsstate->conn_state->pendingAreq);
4033
4034 /*
4035 * The query was already sent by an earlier call to
4036 * fetch_more_data_begin. So now we just fetch the result.
4037 */
4038 res = pgfdw_get_result(conn);
4039 /* On error, report the original query, not the FETCH. */
4040 if (PQresultStatus(res) != PGRES_TUPLES_OK)
4041 pgfdw_report_error(res, conn, fsstate->query);
4042
4043 /* Reset per-connection state */
4044 fsstate->conn_state->pendingAreq = NULL;
4045 }
4046 else
4047 {
4048 char sql[64];
4049
4050 /* This is a regular synchronous fetch. */
4051 snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
4052 fsstate->fetch_size, fsstate->cursor_number);
4053
4054 res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
4055 /* On error, report the original query, not the FETCH. */
4056 if (PQresultStatus(res) != PGRES_TUPLES_OK)
4057 pgfdw_report_error(res, conn, fsstate->query);
4058 }
4059
4060 /* Convert the data into HeapTuples */
4061 numrows = PQntuples(res);
4062 fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
4063 fsstate->num_tuples = numrows;
4064 fsstate->next_tuple = 0;
4065
4066 for (i = 0; i < numrows; i++)
4067 {
4068 Assert(IsA(node->ss.ps.plan, ForeignScan));
4069
4070 fsstate->tuples[i] =
4072 fsstate->rel,
4073 fsstate->attinmeta,
4074 fsstate->retrieved_attrs,
4075 node,
4076 fsstate->temp_cxt);
4077 }
4078
4079 /* Update fetch_ct_2 */
4080 if (fsstate->fetch_ct_2 < 2)
4081 fsstate->fetch_ct_2++;
4082
4083 /* Must be EOF if we didn't get as many tuples as we asked for. */
4084 fsstate->eof_reached = (numrows < fsstate->fetch_size);
4085
4086 PQclear(res);
4087
4088 MemoryContextSwitchTo(oldcontext);
4089}
void * palloc0(Size size)
Definition mcxt.c:1417

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

8327{
8328 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8329 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8330 char sql[64];
8331
8332 Assert(!fsstate->conn_state->pendingAreq);
8333
8334 /* Create the cursor synchronously. */
8335 if (!fsstate->cursor_exists)
8336 create_cursor(node);
8337
8338 /* We will send this query, but not wait for the response. */
8339 snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
8340 fsstate->fetch_size, fsstate->cursor_number);
8341
8342 if (!PQsendQuery(fsstate->conn, sql))
8343 pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
8344
8345 /* Remember that the request is in process */
8346 fsstate->conn_state->pendingAreq = areq;
8347}
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 5821 of file postgres_fdw.c.

5822{
5823 StringInfoData sql;
5824 PGresult *res;
5825
5826 initStringInfo(&sql);
5827 deparseAnalyzeInfoSql(&sql, relation);
5828
5829 res = pgfdw_exec_query(conn, sql.data, NULL);
5830 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5831 pgfdw_report_error(res, conn, sql.data);
5832
5833 if (PQntuples(res) != 1 || PQnfields(res) != RELSTATS_NUM_FIELDS)
5834 elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
5835
5836 return res;
5837}
void deparseAnalyzeInfoSql(StringInfo buf, Relation rel)
Definition deparse.c:2552

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

5691{
5692 const char *remote_schemaname = NULL;
5693 const char *remote_relname = NULL;
5695 PGconn *conn;
5700 int attrcnt = 0;
5701 char relkind;
5702 double reltuples;
5703 bool ok = false;
5704 ListCell *lc;
5705
5706 /*
5707 * Assume the remote schema/relation names are the same as the local name
5708 * unless the foreign table's options tell us otherwise.
5709 */
5712 foreach(lc, table->options)
5713 {
5714 DefElem *def = (DefElem *) lfirst(lc);
5715
5716 if (strcmp(def->defname, "schema_name") == 0)
5718 else if (strcmp(def->defname, "table_name") == 0)
5720 }
5721
5722 /*
5723 * Get connection to the foreign server. Connection manager will
5724 * establish new connection if necessary.
5725 */
5726 user = GetUserMapping(GetUserId(), table->serverid);
5727 conn = GetConnection(user, false, NULL);
5728 remstats->server_version_num = server_version_num = PQserverVersion(conn);
5729
5730 /* Fetch relation stats. */
5731 remstats->rel = relstats = fetch_relstats(conn, relation);
5732
5733 /*
5734 * Verify that the remote table is the sort that can have meaningful stats
5735 * in pg_stats.
5736 *
5737 * Note that while relations of kinds RELKIND_INDEX and
5738 * RELKIND_PARTITIONED_INDEX can have rows in pg_stats, they obviously
5739 * can't support a foreign table.
5740 */
5741 relkind = *PQgetvalue(relstats, 0, RELSTATS_RELKIND);
5742 switch (relkind)
5743 {
5744 case RELKIND_RELATION:
5746 case RELKIND_MATVIEW:
5748 break;
5749 default:
5751 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" is of relkind \"%c\" which cannot have statistics",
5754 goto fetch_cleanup;
5755 }
5756
5757 /*
5758 * If the reltuples value > 0, then then we can expect to find attribute
5759 * stats for the remote table.
5760 *
5761 * In v14 or latter, if a reltuples value is -1, it means the table has
5762 * never been analyzed, so we wouldn't expect to find the stats for the
5763 * table; fallback to sampling in that case. If the value is 0, it means
5764 * it was empty; in which case skip the stats and import relation stats
5765 * only.
5766 *
5767 * In versions prior to v14, a value of 0 was ambiguous; it could mean
5768 * that the table had never been analyzed, or that it was empty. Either
5769 * way, we wouldn't expect to find the stats for the table, so we fallback
5770 * to sampling.
5771 */
5773 if (((server_version_num < 140000) && (reltuples == 0)) ||
5774 ((server_version_num >= 140000) && (reltuples == -1)))
5775 {
5777 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" has no relation statistics to import",
5780 goto fetch_cleanup;
5781 }
5782
5783
5784 if (reltuples > 0)
5785 {
5787
5788 *p_remattrmap = remattrmap = build_remattrmap(relation, va_cols,
5789 &attrcnt, &column_list);
5790 *p_attrcnt = attrcnt;
5791
5792 if (attrcnt > 0)
5793 {
5794 /* Fetch attribute stats. */
5799 column_list.data);
5800
5801 /* If any attribute statsare missing, fallback to sampling. */
5806 goto fetch_cleanup;
5807 }
5808 }
5809
5810 ok = true;
5811
5814 return ok;
5815}
#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 8680 of file postgres_fdw.c.

8681{
8682 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
8685
8687 while ((em = eclass_member_iterator_next(&it)) != NULL)
8688 {
8689 /*
8690 * Note we require !bms_is_empty, else we'd accept constant
8691 * expressions which are not suitable for the purpose.
8692 */
8693 if (bms_is_subset(em->em_relids, rel->relids) &&
8694 !bms_is_empty(em->em_relids) &&
8695 bms_is_empty(bms_intersect(em->em_relids, fpinfo->hidden_subquery_rels)) &&
8696 is_foreign_expr(root, rel, em->em_expr))
8697 return em;
8698 }
8699
8700 return NULL;
8701}
Bitmapset * bms_intersect(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:292
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:412
#define bms_is_empty(a)
Definition bitmapset.h:118
void setup_eclass_member_iterator(EquivalenceMemberIterator *it, EquivalenceClass *ec, Relids child_relids)
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 8715 of file postgres_fdw.c.

8717{
8718 PathTarget *target = rel->reltarget;
8719 ListCell *lc1;
8720 int i;
8721
8722 i = 0;
8723 foreach(lc1, target->exprs)
8724 {
8725 Expr *expr = (Expr *) lfirst(lc1);
8727 ListCell *lc2;
8728
8729 /* Ignore non-sort expressions */
8730 if (sgref == 0 ||
8732 root->parse->sortClause) == NULL)
8733 {
8734 i++;
8735 continue;
8736 }
8737
8738 /* We ignore binary-compatible relabeling on both ends */
8739 while (expr && IsA(expr, RelabelType))
8740 expr = ((RelabelType *) expr)->arg;
8741
8742 /*
8743 * Locate an EquivalenceClass member matching this expr, if any.
8744 * Ignore child members.
8745 */
8746 foreach(lc2, ec->ec_members)
8747 {
8749 Expr *em_expr;
8750
8751 /* Don't match constants */
8752 if (em->em_is_const)
8753 continue;
8754
8755 /* Child members should not exist in ec_members */
8756 Assert(!em->em_is_child);
8757
8758 /* Match if same expression (after stripping relabel) */
8759 em_expr = em->em_expr;
8760 while (em_expr && IsA(em_expr, RelabelType))
8761 em_expr = ((RelabelType *) em_expr)->arg;
8762
8763 if (!equal(em_expr, expr))
8764 continue;
8765
8766 /* Check that expression (including relabels!) is shippable */
8767 if (is_foreign_expr(root, rel, em->em_expr))
8768 return em;
8769 }
8770
8771 i++;
8772 }
8773
8774 return NULL;
8775}
unsigned int Index
Definition c.h:698
#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 2603 of file postgres_fdw.c.

2607{
2608 Plan *subplan = outerPlan(plan);
2609
2610 /*
2611 * The cases we support are (1) the desired ForeignScan is the immediate
2612 * child of ModifyTable, or (2) it is the subplan_index'th child of an
2613 * Append node that is the immediate child of ModifyTable. There is no
2614 * point in looking further down, as that would mean that local joins are
2615 * involved, so we can't do the update directly.
2616 *
2617 * There could be a Result atop the Append too, acting to compute the
2618 * UPDATE targetlist values. We ignore that here; the tlist will be
2619 * checked by our caller.
2620 *
2621 * In principle we could examine all the children of the Append, but it's
2622 * currently unlikely that the core planner would generate such a plan
2623 * with the children out-of-order. Moreover, such a search risks costing
2624 * O(N^2) time when there are a lot of children.
2625 */
2626 if (IsA(subplan, Append))
2627 {
2628 Append *appendplan = (Append *) subplan;
2629
2630 if (subplan_index < list_length(appendplan->appendplans))
2631 subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2632 }
2633 else if (IsA(subplan, Result) &&
2634 outerPlan(subplan) != NULL &&
2635 IsA(outerPlan(subplan), Append))
2636 {
2637 Append *appendplan = (Append *) outerPlan(subplan);
2638
2639 if (subplan_index < list_length(appendplan->appendplans))
2640 subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2641 }
2642
2643 /* Now, have we got a ForeignScan on the desired rel? */
2644 if (IsA(subplan, ForeignScan))
2645 {
2646 ForeignScan *fscan = (ForeignScan *) subplan;
2647
2648 if (bms_is_member(rtindex, fscan->fs_base_relids))
2649 return fscan;
2650 }
2651
2652 return NULL;
2653}
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
#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 4561 of file postgres_fdw.c.

4562{
4563 Assert(fmstate != NULL);
4564
4565 /* If we created a prepared statement, destroy it */
4567
4568 /* Release remote connection */
4570 fmstate->conn = NULL;
4571}

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

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

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

◆ get_batch_size_option()

static int get_batch_size_option ( Relation  rel)
static

Definition at line 8782 of file postgres_fdw.c.

8783{
8786 ForeignServer *server;
8787 List *options;
8788 ListCell *lc;
8789
8790 /* we use 1 by default, which means "no batching" */
8791 int batch_size = 1;
8792
8793 /*
8794 * Load options for table and server. We append server options after table
8795 * options, because table options take precedence.
8796 */
8798 server = GetForeignServer(table->serverid);
8799
8800 options = NIL;
8801 options = list_concat(options, table->options);
8802 options = list_concat(options, server->options);
8803
8804 /* See if either table or server specifies batch_size. */
8805 foreach(lc, options)
8806 {
8807 DefElem *def = (DefElem *) lfirst(lc);
8808
8809 if (strcmp(def->defname, "batch_size") == 0)
8810 {
8811 (void) parse_int(defGetString(def), &batch_size, 0, NULL);
8812 break;
8813 }
8814 }
8815
8816 return batch_size;
8817}
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_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 3818 of file postgres_fdw.c.

3821{
3822 PGresult *res;
3823 char *line;
3824 char *p;
3825 int n;
3826
3827 /*
3828 * Execute EXPLAIN remotely.
3829 */
3830 res = pgfdw_exec_query(conn, sql, NULL);
3831 if (PQresultStatus(res) != PGRES_TUPLES_OK)
3832 pgfdw_report_error(res, conn, sql);
3833
3834 /*
3835 * Extract cost numbers for topmost plan node. Note we search for a left
3836 * paren from the end of the line to avoid being confused by other uses of
3837 * parentheses.
3838 */
3839 line = PQgetvalue(res, 0, 0);
3840 p = strrchr(line, '(');
3841 if (p == NULL)
3842 elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3843 n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3844 startup_cost, total_cost, rows, width);
3845 if (n != 4)
3846 elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3847 PQclear(res);
3848}

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

4789{
4791 EState *estate = node->ss.ps.state;
4792 ResultRelInfo *resultRelInfo = node->resultRelInfo;
4793 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4795
4796 Assert(resultRelInfo->ri_projectReturning);
4797
4798 /* If we didn't get any tuples, must be end of data. */
4799 if (dmstate->next_tuple >= dmstate->num_tuples)
4800 return ExecClearTuple(slot);
4801
4802 /* Increment the command es_processed count if necessary. */
4803 if (dmstate->set_processed)
4804 estate->es_processed += 1;
4805
4806 /*
4807 * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4808 * tuple. (has_returning is false when the local query is of the form
4809 * "UPDATE/DELETE .. RETURNING 1" for example.)
4810 */
4811 if (!dmstate->has_returning)
4812 {
4814 resultSlot = slot;
4815 }
4816 else
4817 {
4819
4821 dmstate->next_tuple,
4822 dmstate->rel,
4823 dmstate->attinmeta,
4824 dmstate->retrieved_attrs,
4825 node,
4826 dmstate->temp_cxt);
4827 ExecStoreHeapTuple(newtup, slot, false);
4828 /* Get the updated/deleted tuple. */
4829 if (dmstate->rel)
4830 resultSlot = slot;
4831 else
4832 resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4833 }
4834 dmstate->next_tuple++;
4835
4836 /* Make slot available for evaluation of the local query RETURNING list. */
4838 resultSlot;
4839
4840 return slot;
4841}
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:750
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
ResultRelInfo * resultRelInfo
Definition execnodes.h:2098
ExprContext * pi_exprContext
Definition execnodes.h:402
ProjectionInfo * ri_projectReturning
Definition execnodes.h:610
TupleTableSlot * ss_ScanTupleSlot
Definition execnodes.h:1662

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

1663{
1664 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1665 EState *estate = node->ss.ps.state;
1666 TupleDesc tupdesc;
1667
1668 /*
1669 * The core code has already set up a scan tuple slot based on
1670 * fsplan->fdw_scan_tlist, and this slot's tupdesc is mostly good enough,
1671 * but there's one case where it isn't. If we have any whole-row row
1672 * identifier Vars, they may have vartype RECORD, and we need to replace
1673 * that with the associated table's actual composite type. This ensures
1674 * that when we read those ROW() expression values from the remote server,
1675 * we can convert them to a composite type the local server knows.
1676 */
1678 for (int i = 0; i < tupdesc->natts; i++)
1679 {
1680 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
1681 Var *var;
1683 Oid reltype;
1684
1685 /* Nothing to do if it's not a generic RECORD attribute */
1686 if (att->atttypid != RECORDOID || att->atttypmod >= 0)
1687 continue;
1688
1689 /*
1690 * If we can't identify the referenced table, do nothing. This'll
1691 * likely lead to failure later, but perhaps we can muddle through.
1692 */
1693 var = (Var *) list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
1694 i)->expr;
1695 if (!IsA(var, Var) || var->varattno != 0)
1696 continue;
1697 rte = list_nth(estate->es_range_table, var->varno - 1);
1698 if (rte->rtekind != RTE_RELATION)
1699 continue;
1700 reltype = get_rel_type_id(rte->relid);
1701 if (!OidIsValid(reltype))
1702 continue;
1703 att->atttypid = reltype;
1704 /* shouldn't need to change anything else */
1705 }
1706 return tupdesc;
1707}
#define OidIsValid(objectId)
Definition c.h:858
Oid get_rel_type_id(Oid relid)
Definition lsyscache.c:2199
@ RTE_RELATION
List * es_range_table
Definition execnodes.h:698
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 1028 of file postgres_fdw.c.

1029{
1031 ListCell *lc;
1032 Relids relids;
1033
1034 /*
1035 * First, consider whether any active EC is potentially useful for a merge
1036 * join against this relation.
1037 */
1038 if (rel->has_eclass_joins)
1039 {
1040 foreach(lc, root->eq_classes)
1041 {
1043
1046 }
1047 }
1048
1049 /*
1050 * Next, consider whether there are any non-EC derivable join clauses that
1051 * are merge-joinable. If the joininfo list is empty, we can exit
1052 * quickly.
1053 */
1054 if (rel->joininfo == NIL)
1055 return useful_eclass_list;
1056
1057 /* If this is a child rel, we must use the topmost parent rel to search. */
1058 if (IS_OTHER_REL(rel))
1059 {
1061 relids = rel->top_parent_relids;
1062 }
1063 else
1064 relids = rel->relids;
1065
1066 /* Check each join clause in turn. */
1067 foreach(lc, rel->joininfo)
1068 {
1070
1071 /* Consider only mergejoinable clauses */
1072 if (restrictinfo->mergeopfamilies == NIL)
1073 continue;
1074
1075 /* Make sure we've got canonical ECs. */
1077
1078 /*
1079 * restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
1080 * that left_ec and right_ec will be initialized, per comments in
1081 * distribute_qual_to_rels.
1082 *
1083 * We want to identify which side of this merge-joinable clause
1084 * contains columns from the relation produced by this RelOptInfo. We
1085 * test for overlap, not containment, because there could be extra
1086 * relations on either side. For example, suppose we've got something
1087 * like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
1088 * A.y = D.y. The input rel might be the joinrel between A and B, and
1089 * we'll consider the join clause A.y = D.y. relids contains a
1090 * relation not involved in the join class (B) and the equivalence
1091 * class for the left-hand side of the clause contains a relation not
1092 * involved in the input rel (C). Despite the fact that we have only
1093 * overlap and not containment in either direction, A.y is potentially
1094 * useful as a sort column.
1095 *
1096 * Note that it's even possible that relids overlaps neither side of
1097 * the join clause. For example, consider A LEFT JOIN B ON A.x = B.x
1098 * AND A.x = 1. The clause A.x = 1 will appear in B's joininfo list,
1099 * but overlaps neither side of B. In that case, we just skip this
1100 * join clause, since it doesn't suggest a useful sort order for this
1101 * relation.
1102 */
1103 if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
1105 restrictinfo->right_ec);
1106 else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
1108 restrictinfo->left_ec);
1109 }
1110
1111 return useful_eclass_list;
1112}
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:575
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 1124 of file postgres_fdw.c.

1125{
1128 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
1130 ListCell *lc;
1131
1132 /*
1133 * Pushing the query_pathkeys to the remote server is always worth
1134 * considering, because it might let us avoid a local sort.
1135 */
1136 fpinfo->qp_is_pushdown_safe = false;
1137 if (root->query_pathkeys)
1138 {
1139 bool query_pathkeys_ok = true;
1140
1141 foreach(lc, root->query_pathkeys)
1142 {
1144
1145 /*
1146 * The planner and executor don't have any clever strategy for
1147 * taking data sorted by a prefix of the query's pathkeys and
1148 * getting it to be sorted by all of those pathkeys. We'll just
1149 * end up resorting the entire data set. So, unless we can push
1150 * down all of the query pathkeys, forget it.
1151 */
1152 if (!is_foreign_pathkey(root, rel, pathkey))
1153 {
1154 query_pathkeys_ok = false;
1155 break;
1156 }
1157 }
1158
1160 {
1161 useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
1162 fpinfo->qp_is_pushdown_safe = true;
1163 }
1164 }
1165
1166 /*
1167 * Even if we're not using remote estimates, having the remote side do the
1168 * sort generally won't be any worse than doing it locally, and it might
1169 * be much better if the remote side can generate data in the right order
1170 * without needing a sort at all. However, what we're going to do next is
1171 * try to generate pathkeys that seem promising for possible merge joins,
1172 * and that's more speculative. A wrong choice might hurt quite a bit, so
1173 * bail out if we can't use remote estimates.
1174 */
1175 if (!fpinfo->use_remote_estimate)
1176 return useful_pathkeys_list;
1177
1178 /* Get the list of interesting EquivalenceClasses. */
1180
1181 /* Extract unique EC for query, if any, so we don't consider it again. */
1182 if (list_length(root->query_pathkeys) == 1)
1183 {
1184 PathKey *query_pathkey = linitial(root->query_pathkeys);
1185
1186 query_ec = query_pathkey->pk_eclass;
1187 }
1188
1189 /*
1190 * As a heuristic, the only pathkeys we consider here are those of length
1191 * one. It's surely possible to consider more, but since each one we
1192 * choose to consider will generate a round-trip to the remote side, we
1193 * need to be a bit cautious here. It would sure be nice to have a local
1194 * cache of information about remote index definitions...
1195 */
1196 foreach(lc, useful_eclass_list)
1197 {
1200
1201 /* If redundant with what we did above, skip it. */
1202 if (cur_ec == query_ec)
1203 continue;
1204
1205 /* Can't push down the sort if the EC's opfamily is not shippable. */
1206 if (!is_shippable(linitial_oid(cur_ec->ec_opfamilies),
1208 continue;
1209
1210 /* If no pushable expression for this rel, skip it. */
1211 if (find_em_for_rel(root, cur_ec, rel) == NULL)
1212 continue;
1213
1214 /* Looks like we can generate a pathkey, so let's do it. */
1216 linitial_oid(cur_ec->ec_opfamilies),
1217 COMPARE_LT,
1218 false);
1221 }
1222
1223 return useful_pathkeys_list;
1224}
@ COMPARE_LT
Definition cmptype.h:34
bool is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel, PathKey *pathkey)
Definition deparse.c:1156
PathKey * make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, CompareType cmptype, bool nulls_first)
Definition pathkeys.c:56
#define linitial(l)
Definition pg_list.h:178
#define linitial_oid(l)
Definition pg_list.h:180
EquivalenceMember * find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
static List * get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel)

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 ( const char schemaname,
const char relname,
int  attrcnt,
const RemoteAttributeMapping remattrmap,
RemoteStatsResults remstats 
)
static

Definition at line 6111 of file postgres_fdw.c.

6116{
6120 char nulls[ATTIMPORT_SQL_NUM_FIELDS];
6121 int spirc;
6122 bool ok = false;
6123
6124 /* Assign all the invariant parameters common to relation/attribute stats */
6125 values[ATTIMPORT_SQL_VERSION] = Int32GetDatum(remstats->server_version_num);
6126 nulls[ATTIMPORT_SQL_VERSION] = ' ';
6127
6129 nulls[ATTIMPORT_SQL_SCHEMANAME] = ' ';
6130
6132 nulls[ATTIMPORT_SQL_RELNAME] = ' ';
6133
6134 SPI_connect();
6135
6136 /*
6137 * We import attribute statistics first, if any, because those are more
6138 * prone to errors. This avoids making a modification of pg_class that
6139 * will just get rolled back by a failed attribute import.
6140 */
6141 if (remstats->att != NULL)
6142 {
6144 Assert(PQntuples(remstats->att) >= 1);
6145
6148 if (attimport_plan == NULL)
6149 elog(ERROR, "failed to prepare attimport_sql query");
6150
6153 if (attclear_plan == NULL)
6154 elog(ERROR, "failed to prepare attclear_sql query");
6155
6156 nulls[ATTIMPORT_SQL_ATTNUM] = ' ';
6157
6158 for (int mapidx = 0; mapidx < attrcnt; mapidx++)
6159 {
6160 int row = remattrmap[mapidx].res_index;
6161 Datum *values2 = values + 1;
6162 char *nulls2 = nulls + 1;
6163
6164 /* All mappings should have been assigned a result set row. */
6165 Assert(row >= 0);
6166
6167 /*
6168 * Check for user-requested abort.
6169 */
6171
6172 /*
6173 * First, clear existing attribute stats.
6174 *
6175 * We can re-use the values/nulls because the number of parameters
6176 * is less and the first two params are the same as the second and
6177 * third ones in attimport_sql.
6178 */
6180 CStringGetTextDatum(remattrmap[mapidx].local_attname);
6181
6183 if (spirc != SPI_OK_SELECT)
6184 elog(ERROR, "failed to execute attclear_sql query for column \"%s\" of foreign table \"%s.%s\"",
6185 remattrmap[mapidx].local_attname, schemaname, relname);
6186
6188 Int16GetDatum(remattrmap[mapidx].local_attnum);
6189
6190 /* Loop through all mappable columns to set remaining arguments */
6191 for (int i = 0; i < NUM_MAPPED_ATTIMPORT_ARGS; i++)
6192 map_field_to_arg(remstats->att, row,
6195 values, nulls);
6196
6197 spirc = SPI_execute_plan(attimport_plan, values, nulls, false, 1);
6198 if (spirc != SPI_OK_SELECT)
6199 elog(ERROR, "failed to execute attimport_sql query for column \"%s\" of foreign table \"%s.%s\"",
6200 remattrmap[mapidx].local_attname, schemaname, relname);
6201
6202 if (!import_spi_query_ok())
6203 {
6205 errmsg("could not import statistics for foreign table \"%s.%s\" --- attribute statistics import failed for column \"%s\" of this foreign table",
6206 schemaname, relname,
6207 remattrmap[mapidx].local_attname));
6208 goto import_cleanup;
6209 }
6210 }
6211 }
6212
6213 /*
6214 * Import relation stats. We only perform this once, so there is no point
6215 * in preparing the statement.
6216 *
6217 * We can re-use the values/nulls because the number of parameters is less
6218 * and the first three params are the same as attimport_sql.
6219 */
6220 Assert(remstats->rel != NULL);
6222 Assert(PQntuples(remstats->rel) == 1);
6227
6231 values, nulls, false, 1);
6232 if (spirc != SPI_OK_SELECT)
6233 elog(ERROR, "failed to execute relimport_sql query for foreign table \"%s.%s\"",
6234 schemaname, relname);
6235
6236 if (!import_spi_query_ok())
6237 {
6239 errmsg("could not import statistics for foreign table \"%s.%s\" --- relation statistics import failed for this foreign table",
6240 schemaname, relname));
6241 goto import_cleanup;
6242 }
6243
6244 ok = true;
6245
6247 if (attimport_plan)
6249 if (attclear_plan)
6251 SPI_finish();
6252 return ok;
6253}
#define CStringGetTextDatum(s)
Definition builtins.h:98
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
static Datum Int16GetDatum(int16 X)
Definition postgres.h:172
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
static const char * attimport_sql
static const Oid relimport_argtypes[RELIMPORT_SQL_NUM_FIELDS]
static bool import_spi_query_ok(void)
static const AttrResultArgMap attr_result_arg_map[NUM_MAPPED_ATTIMPORT_ARGS]
static void map_field_to_arg(PGresult *res, int row, int field, int arg, Datum *values, char *nulls)
static const Oid attimport_argtypes[ATTIMPORT_SQL_NUM_FIELDS]
#define NUM_MAPPED_ATTIMPORT_ARGS
static const char * relimport_sql
static const char * attclear_sql
static const Oid attclear_argtypes[ATTCLEAR_SQL_NUM_FIELDS]
int SPI_freeplan(SPIPlanPtr plan)
Definition spi.c:1026
int SPI_execute_plan(SPIPlanPtr plan, const Datum *Values, const char *Nulls, bool read_only, long tcount)
Definition spi.c:673
int SPI_connect(void)
Definition spi.c:95
int SPI_finish(void)
Definition spi.c:183
int SPI_execute_with_args(const char *src, int nargs, Oid *argtypes, const Datum *Values, const char *Nulls, bool read_only, long tcount)
Definition spi.c:813
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition spi.c:861
#define SPI_OK_SELECT
Definition spi.h:86
enum AttImportSqlArgs arg_num
enum AttStatsColumns res_field

References AttrResultArgMap::arg_num, Assert, attclear_argtypes, attclear_sql, ATTCLEAR_SQL_ATTNAME, ATTCLEAR_SQL_NUM_FIELDS, attimport_argtypes, attimport_sql, ATTIMPORT_SQL_ATTNUM, ATTIMPORT_SQL_NUM_FIELDS, ATTIMPORT_SQL_RELNAME, ATTIMPORT_SQL_SCHEMANAME, ATTIMPORT_SQL_VERSION, attr_result_arg_map, ATTSTATS_NUM_FIELDS, CHECK_FOR_INTERRUPTS, CStringGetTextDatum, elog, ereport, errmsg, ERROR, fb(), i, import_spi_query_ok(), Int16GetDatum(), Int32GetDatum(), map_field_to_arg(), NUM_MAPPED_ATTIMPORT_ARGS, PQnfields, PQntuples, relimport_argtypes, relimport_sql, RELIMPORT_SQL_NUM_FIELDS, RELIMPORT_SQL_RELPAGES, RELIMPORT_SQL_RELTUPLES, relname, RELSTATS_NUM_FIELDS, RELSTATS_RELPAGES, RELSTATS_RELTUPLES, AttrResultArgMap::res_field, SPI_connect(), SPI_execute_plan(), SPI_execute_with_args(), SPI_finish(), SPI_freeplan(), SPI_OK_SELECT, SPI_prepare(), values, and WARNING.

Referenced by postgresImportForeignStatistics().

◆ import_spi_query_ok()

static bool import_spi_query_ok ( void  )
static

Definition at line 6280 of file postgres_fdw.c.

6281{
6282 TupleDesc tupdesc;
6283 Datum dat;
6284 bool isnull;
6285
6287 Assert(SPI_processed == 1);
6288
6289 tupdesc = SPI_tuptable->tupdesc;
6290 Assert(tupdesc->natts == 1);
6291 Assert(TupleDescAttr(tupdesc, 0)->atttypid == BOOLOID);
6292 dat = SPI_getbinval(SPI_tuptable->vals[0], tupdesc, 1, &isnull);
6293 Assert(!isnull);
6294
6295 return DatumGetBool(dat);
6296}
static bool DatumGetBool(Datum X)
Definition postgres.h:100
uint64 SPI_processed
Definition spi.c:45
SPITupleTable * SPI_tuptable
Definition spi.c:46
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
Definition spi.c:1253
TupleDesc tupdesc
Definition spi.h:25
HeapTuple * vals
Definition spi.h:26

References Assert, DatumGetBool(), fb(), TupleDescData::natts, SPI_getbinval(), SPI_processed, SPI_tuptable, SPITupleTable::tupdesc, TupleDescAttr(), and SPITupleTable::vals.

Referenced by import_fetched_statistics().

◆ init_returning_filter()

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

Definition at line 4847 of file postgres_fdw.c.

4850{
4852 ListCell *lc;
4853 int i;
4854
4855 /*
4856 * Calculate the mapping between the fdw_scan_tlist's entries and the
4857 * result tuple's attributes.
4858 *
4859 * The "map" is an array of indexes of the result tuple's attributes in
4860 * fdw_scan_tlist, i.e., one entry for every attribute of the result
4861 * tuple. We store zero for any attributes that don't have the
4862 * corresponding entries in that list, marking that a NULL is needed in
4863 * the result tuple.
4864 *
4865 * Also get the indexes of the entries for ctid and oid if any.
4866 */
4867 dmstate->attnoMap = (AttrNumber *)
4868 palloc0(resultTupType->natts * sizeof(AttrNumber));
4869
4870 dmstate->ctidAttno = dmstate->oidAttno = 0;
4871
4872 i = 1;
4873 dmstate->hasSystemCols = false;
4874 foreach(lc, fdw_scan_tlist)
4875 {
4877 Var *var = (Var *) tle->expr;
4878
4879 Assert(IsA(var, Var));
4880
4881 /*
4882 * If the Var is a column of the target relation to be retrieved from
4883 * the foreign server, get the index of the entry.
4884 */
4885 if (var->varno == rtindex &&
4886 list_member_int(dmstate->retrieved_attrs, i))
4887 {
4888 int attrno = var->varattno;
4889
4890 if (attrno < 0)
4891 {
4892 /*
4893 * We don't retrieve system columns other than ctid and oid.
4894 */
4896 dmstate->ctidAttno = i;
4897 else
4898 Assert(false);
4899 dmstate->hasSystemCols = true;
4900 }
4901 else
4902 {
4903 /*
4904 * We don't retrieve whole-row references to the target
4905 * relation either.
4906 */
4907 Assert(attrno > 0);
4908
4909 dmstate->attnoMap[attrno - 1] = i;
4910 }
4911 }
4912 i++;
4913 }
4914}
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 8420 of file postgres_fdw.c.

8427{
8428 HeapTuple tuple;
8429 TupleDesc tupdesc;
8430 Datum *values;
8431 bool *nulls;
8432 ItemPointer ctid = NULL;
8434 ErrorContextCallback errcallback;
8435 MemoryContext oldcontext;
8436 ListCell *lc;
8437 int j;
8438
8439 Assert(row < PQntuples(res));
8440
8441 /*
8442 * Do the following work in a temp context that we reset after each tuple.
8443 * This cleans up not only the data we have direct access to, but any
8444 * cruft the I/O functions might leak.
8445 */
8446 oldcontext = MemoryContextSwitchTo(temp_context);
8447
8448 /*
8449 * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
8450 * provided, otherwise look to the scan node's ScanTupleSlot.
8451 */
8452 if (rel)
8453 tupdesc = RelationGetDescr(rel);
8454 else
8455 {
8456 Assert(fsstate);
8457 tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
8458 }
8459
8460 values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
8461 nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
8462 /* Initialize to nulls for any columns not present in result */
8463 memset(nulls, true, tupdesc->natts * sizeof(bool));
8464
8465 /*
8466 * Set up and install callback to report where conversion error occurs.
8467 */
8468 errpos.cur_attno = 0;
8469 errpos.rel = rel;
8470 errpos.fsstate = fsstate;
8471 errcallback.callback = conversion_error_callback;
8472 errcallback.arg = &errpos;
8473 errcallback.previous = error_context_stack;
8474 error_context_stack = &errcallback;
8475
8476 /*
8477 * i indexes columns in the relation, j indexes columns in the PGresult.
8478 */
8479 j = 0;
8480 foreach(lc, retrieved_attrs)
8481 {
8482 int i = lfirst_int(lc);
8483 char *valstr;
8484
8485 /* fetch next column's textual value */
8486 if (PQgetisnull(res, row, j))
8487 valstr = NULL;
8488 else
8489 valstr = PQgetvalue(res, row, j);
8490
8491 /*
8492 * convert value to internal representation
8493 *
8494 * Note: we ignore system columns other than ctid and oid in result
8495 */
8496 errpos.cur_attno = i;
8497 if (i > 0)
8498 {
8499 /* ordinary column */
8500 Assert(i <= tupdesc->natts);
8501 nulls[i - 1] = (valstr == NULL);
8502 /* Apply the input function even to nulls, to support domains */
8503 values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
8504 valstr,
8505 attinmeta->attioparams[i - 1],
8506 attinmeta->atttypmods[i - 1]);
8507 }
8509 {
8510 /* ctid */
8511 if (valstr != NULL)
8512 {
8513 Datum datum;
8514
8516 ctid = (ItemPointer) DatumGetPointer(datum);
8517 }
8518 }
8519 errpos.cur_attno = 0;
8520
8521 j++;
8522 }
8523
8524 /* Uninstall error context callback. */
8525 error_context_stack = errcallback.previous;
8526
8527 /*
8528 * Check we got the expected number of columns. Note: j == 0 and
8529 * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
8530 */
8531 if (j > 0 && j != PQnfields(res))
8532 elog(ERROR, "remote query result does not match the foreign table");
8533
8534 /*
8535 * Build the result tuple in caller's memory context.
8536 */
8537 MemoryContextSwitchTo(oldcontext);
8538
8539 tuple = heap_form_tuple(tupdesc, values, nulls);
8540
8541 /*
8542 * If we have a CTID to return, install it in both t_self and t_ctid.
8543 * t_self is the normal place, but if the tuple is converted to a
8544 * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
8545 * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
8546 */
8547 if (ctid)
8548 tuple->t_self = tuple->t_data->t_ctid = *ctid;
8549
8550 /*
8551 * Stomp on the xmin, xmax, and cmin fields from the tuple created by
8552 * heap_form_tuple. heap_form_tuple actually creates the tuple with
8553 * DatumTupleFields, not HeapTupleFields, but the executor expects
8554 * HeapTupleFields and will happily extract system columns on that
8555 * assumption. If we don't do this then, for example, the tuple length
8556 * ends up in the xmin field, which isn't what we want.
8557 */
8561
8562 /* Clean up */
8564
8565 return tuple;
8566}
ErrorContextCallback * error_context_stack
Definition elog.c:99
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition fmgr.c:1532
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:684
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
#define PQgetisnull
static Datum CStringGetDatum(const char *X)
Definition postgres.h:370
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().

◆ map_field_to_arg()

static void map_field_to_arg ( PGresult res,
int  row,
int  field,
int  arg,
Datum values,
char nulls 
)
static

Definition at line 6259 of file postgres_fdw.c.

6261{
6262 if (PQgetisnull(res, row, field))
6263 {
6264 values[arg] = (Datum) 0;
6265 nulls[arg] = 'n';
6266 }
6267 else
6268 {
6269 const char *s = PQgetvalue(res, row, field);
6270
6272 nulls[arg] = ' ';
6273 }
6274}

References arg, CStringGetTextDatum, PQgetisnull, PQgetvalue, and values.

Referenced by import_fetched_statistics().

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

6028{
6029 int numrows = PQntuples(res);
6030 int row = -1;
6031
6032 /* No work if there are no stats rows. */
6033 if (numrows == 0)
6034 {
6036 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" has no attribute statistics to import",
6039 return false;
6040 }
6041
6042 /* Scan all entries in the RemoteAttributeMapping. */
6043 for (int mapidx = 0; mapidx < attrcnt; mapidx++)
6044 {
6045 /*
6046 * First, check whether the entry matches the current stats row, if it
6047 * is set.
6048 */
6049 if (row >= 0 &&
6050 strcmp(remattrmap[mapidx].remote_attname,
6051 PQgetvalue(res, row, ATTSTATS_ATTNAME)) == 0)
6052 {
6053 remattrmap[mapidx].res_index = row;
6054 continue;
6055 }
6056
6057 /*
6058 * If we've exhausted all stats rows, it means the stats for the entry
6059 * are missing.
6060 */
6061 if (row >= numrows - 1)
6062 {
6064 errmsg("could not import statistics for foreign table \"%s.%s\" --- no attribute statistics found for column \"%s\" of remote table \"%s.%s\"",
6066 remattrmap[mapidx].remote_attname,
6068 return false;
6069 }
6070
6071 /* Advance to the next stats row. */
6072 row += 1;
6073
6074 /*
6075 * If the attname in the entry is less than that in the next stats
6076 * row, it means the stats for the entry are missing.
6077 */
6078 if (strcmp(remattrmap[mapidx].remote_attname,
6079 PQgetvalue(res, row, ATTSTATS_ATTNAME)) < 0)
6080 {
6082 errmsg("could not import statistics for foreign table \"%s.%s\" --- no attribute statistics found for column \"%s\" of remote table \"%s.%s\"",
6084 remattrmap[mapidx].remote_attname,
6086 return false;
6087 }
6088
6089 /* We should not have got a stats row we didn't expect. */
6090 if (strcmp(remattrmap[mapidx].remote_attname,
6091 PQgetvalue(res, row, ATTSTATS_ATTNAME)) > 0)
6092 elog(ERROR, "unexpected result from fetch_attstats query");
6093
6094 /* We found a match. */
6095 Assert(strcmp(remattrmap[mapidx].remote_attname,
6096 PQgetvalue(res, row, ATTSTATS_ATTNAME)) == 0);
6097 remattrmap[mapidx].res_index = row;
6098 }
6099
6100 /* We should have exhausted all stats rows. */
6101 if (row < numrows - 1)
6102 elog(ERROR, "unexpected result from fetch_attstats query");
6103
6104 return true;
6105}

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

7134{
7135 /* We must always have fpinfo_o. */
7137
7138 /* fpinfo_i may be NULL, but if present the servers must both match. */
7139 Assert(!fpinfo_i ||
7140 fpinfo_i->server->serverid == fpinfo_o->server->serverid);
7141
7142 /*
7143 * Copy the server specific FDW options. (For a join, both relations come
7144 * from the same server, so the server options should have the same value
7145 * for both relations.)
7146 */
7147 fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
7148 fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
7149 fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
7150 fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
7151 fpinfo->fetch_size = fpinfo_o->fetch_size;
7152 fpinfo->async_capable = fpinfo_o->async_capable;
7153
7154 /* Merge the table level options from either side of the join. */
7155 if (fpinfo_i)
7156 {
7157 /*
7158 * We'll prefer to use remote estimates for this join if any table
7159 * from either side of the join is using remote estimates. This is
7160 * most likely going to be preferred since they're already willing to
7161 * pay the price of a round trip to get the remote EXPLAIN. In any
7162 * case it's not entirely clear how we might otherwise handle this
7163 * best.
7164 */
7165 fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
7166 fpinfo_i->use_remote_estimate;
7167
7168 /*
7169 * Set fetch size to maximum of the joining sides, since we are
7170 * expecting the rows returned by the join to be proportional to the
7171 * relation sizes.
7172 */
7173 fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
7174
7175 /*
7176 * We'll prefer to consider this join async-capable if any table from
7177 * either side of the join is considered async-capable. This would be
7178 * reasonable because in that case the foreign server would have its
7179 * own resources to scan that table asynchronously, and the join could
7180 * also be computed asynchronously using the resources.
7181 */
7182 fpinfo->async_capable = fpinfo_o->async_capable ||
7183 fpinfo_i->async_capable;
7184 }
7185}
#define Max(x, y)
Definition c.h:1085

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

770{
771 FdwRoutine *routine = makeNode(FdwRoutine);
772
773 /* Functions for scanning foreign tables */
781
782 /* Functions for updating foreign tables */
799
800 /* Function for EvalPlanQual rechecks */
802 /* Support functions for EXPLAIN */
806
807 /* Support function for TRUNCATE */
809
810 /* Support functions for ANALYZE */
813
814 /* Support functions for IMPORT FOREIGN SCHEMA */
816
817 /* Support functions for join push-down */
819
820 /* Support functions for upper relation push-down */
822
823 /* Support functions for asynchronous execution */
828
829 PG_RETURN_POINTER(routine);
830}
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define makeNode(_type_)
Definition nodes.h:161
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 5224 of file postgres_fdw.c.

5228{
5229 PgFdwAnalyzeState astate;
5231 ForeignServer *server;
5233 PGconn *conn;
5235 PgFdwSamplingMethod method = ANALYZE_SAMPLE_AUTO; /* auto is default */
5236 double sample_frac = -1.0;
5237 double reltuples = -1.0;
5238 unsigned int cursor_number;
5239 StringInfoData sql;
5240 PGresult *res;
5241 char fetch_sql[64];
5242 int fetch_size;
5243 ListCell *lc;
5244
5245 /* Initialize workspace state */
5246 astate.rel = relation;
5248
5249 astate.rows = rows;
5250 astate.targrows = targrows;
5251 astate.numrows = 0;
5252 astate.samplerows = 0;
5253 astate.rowstoskip = -1; /* -1 means not set yet */
5254 reservoir_init_selection_state(&astate.rstate, targrows);
5255
5256 /* Remember ANALYZE context, and create a per-tuple temp context */
5259 "postgres_fdw temporary data",
5261
5262 /*
5263 * Get the connection to use. We do the remote access as the table's
5264 * owner, even if the ANALYZE was started by some other user.
5265 */
5267 server = GetForeignServer(table->serverid);
5268 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5269 conn = GetConnection(user, false, NULL);
5270
5271 /* We'll need server version, so fetch it now. */
5273
5274 /*
5275 * What sampling method should we use?
5276 */
5277 foreach(lc, server->options)
5278 {
5279 DefElem *def = (DefElem *) lfirst(lc);
5280
5281 if (strcmp(def->defname, "analyze_sampling") == 0)
5282 {
5283 char *value = defGetString(def);
5284
5285 if (strcmp(value, "off") == 0)
5286 method = ANALYZE_SAMPLE_OFF;
5287 else if (strcmp(value, "auto") == 0)
5288 method = ANALYZE_SAMPLE_AUTO;
5289 else if (strcmp(value, "random") == 0)
5290 method = ANALYZE_SAMPLE_RANDOM;
5291 else if (strcmp(value, "system") == 0)
5292 method = ANALYZE_SAMPLE_SYSTEM;
5293 else if (strcmp(value, "bernoulli") == 0)
5294 method = ANALYZE_SAMPLE_BERNOULLI;
5295
5296 break;
5297 }
5298 }
5299
5300 foreach(lc, table->options)
5301 {
5302 DefElem *def = (DefElem *) lfirst(lc);
5303
5304 if (strcmp(def->defname, "analyze_sampling") == 0)
5305 {
5306 char *value = defGetString(def);
5307
5308 if (strcmp(value, "off") == 0)
5309 method = ANALYZE_SAMPLE_OFF;
5310 else if (strcmp(value, "auto") == 0)
5311 method = ANALYZE_SAMPLE_AUTO;
5312 else if (strcmp(value, "random") == 0)
5313 method = ANALYZE_SAMPLE_RANDOM;
5314 else if (strcmp(value, "system") == 0)
5315 method = ANALYZE_SAMPLE_SYSTEM;
5316 else if (strcmp(value, "bernoulli") == 0)
5317 method = ANALYZE_SAMPLE_BERNOULLI;
5318
5319 break;
5320 }
5321 }
5322
5323 /*
5324 * Error-out if explicitly required one of the TABLESAMPLE methods, but
5325 * the server does not support it.
5326 */
5327 if ((server_version_num < 95000) &&
5328 (method == ANALYZE_SAMPLE_SYSTEM ||
5329 method == ANALYZE_SAMPLE_BERNOULLI))
5330 ereport(ERROR,
5332 errmsg("remote server does not support TABLESAMPLE feature")));
5333
5334 /*
5335 * If we've decided to do remote sampling, calculate the sampling rate. We
5336 * need to get the number of tuples from the remote server, but skip that
5337 * network round-trip if not needed.
5338 */
5339 if (method != ANALYZE_SAMPLE_OFF)
5340 {
5341 bool can_tablesample;
5342
5343 reltuples = postgresGetAnalyzeInfoForForeignTable(relation,
5345
5346 /*
5347 * Make sure we're not choosing TABLESAMPLE when the remote relation
5348 * does not support that. But only do this for "auto" - if the user
5349 * explicitly requested BERNOULLI/SYSTEM, it's better to fail.
5350 */
5351 if (!can_tablesample && (method == ANALYZE_SAMPLE_AUTO))
5352 method = ANALYZE_SAMPLE_RANDOM;
5353
5354 /*
5355 * Remote's reltuples could be 0 or -1 if the table has never been
5356 * vacuumed/analyzed. In that case, disable sampling after all.
5357 */
5358 if ((reltuples <= 0) || (targrows >= reltuples))
5359 method = ANALYZE_SAMPLE_OFF;
5360 else
5361 {
5362 /*
5363 * All supported sampling methods require sampling rate, not
5364 * target rows directly, so we calculate that using the remote
5365 * reltuples value. That's imperfect, because it might be off a
5366 * good deal, but that's not something we can (or should) address
5367 * here.
5368 *
5369 * If reltuples is too low (i.e. when table grew), we'll end up
5370 * sampling more rows - but then we'll apply the local sampling,
5371 * so we get the expected sample size. This is the same outcome as
5372 * without remote sampling.
5373 *
5374 * If reltuples is too high (e.g. after bulk DELETE), we will end
5375 * up sampling too few rows.
5376 *
5377 * We can't really do much better here - we could try sampling a
5378 * bit more rows, but we don't know how off the reltuples value is
5379 * so how much is "a bit more"?
5380 *
5381 * Furthermore, the targrows value for partitions is determined
5382 * based on table size (relpages), which can be off in different
5383 * ways too. Adjusting the sampling rate here might make the issue
5384 * worse.
5385 */
5386 sample_frac = targrows / reltuples;
5387
5388 /*
5389 * We should never get sampling rate outside the valid range
5390 * (between 0.0 and 1.0), because those cases should be covered by
5391 * the previous branch that sets ANALYZE_SAMPLE_OFF.
5392 */
5393 Assert(sample_frac >= 0.0 && sample_frac <= 1.0);
5394 }
5395 }
5396
5397 /*
5398 * For "auto" method, pick the one we believe is best. For servers with
5399 * TABLESAMPLE support we pick BERNOULLI, for old servers we fall-back to
5400 * random() to at least reduce network transfer.
5401 */
5402 if (method == ANALYZE_SAMPLE_AUTO)
5403 {
5404 if (server_version_num < 95000)
5405 method = ANALYZE_SAMPLE_RANDOM;
5406 else
5407 method = ANALYZE_SAMPLE_BERNOULLI;
5408 }
5409
5410 /*
5411 * Construct cursor that retrieves whole rows from remote.
5412 */
5414 initStringInfo(&sql);
5415 appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
5416
5417 deparseAnalyzeSql(&sql, relation, method, sample_frac, &astate.retrieved_attrs);
5418
5419 res = pgfdw_exec_query(conn, sql.data, NULL);
5420 if (PQresultStatus(res) != PGRES_COMMAND_OK)
5421 pgfdw_report_error(res, conn, sql.data);
5422 PQclear(res);
5423
5424 /*
5425 * Determine the fetch size. The default is arbitrary, but shouldn't be
5426 * enormous.
5427 */
5428 fetch_size = 100;
5429 foreach(lc, server->options)
5430 {
5431 DefElem *def = (DefElem *) lfirst(lc);
5432
5433 if (strcmp(def->defname, "fetch_size") == 0)
5434 {
5436 break;
5437 }
5438 }
5439 foreach(lc, table->options)
5440 {
5441 DefElem *def = (DefElem *) lfirst(lc);
5442
5443 if (strcmp(def->defname, "fetch_size") == 0)
5444 {
5446 break;
5447 }
5448 }
5449
5450 /* Construct command to fetch rows from remote. */
5451 snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
5453
5454 /* Retrieve and process rows a batch at a time. */
5455 for (;;)
5456 {
5457 int numrows;
5458 int i;
5459
5460 /* Allow users to cancel long query */
5462
5463 /*
5464 * XXX possible future improvement: if rowstoskip is large, we could
5465 * issue a MOVE rather than physically fetching the rows, then just
5466 * adjust rowstoskip and samplerows appropriately.
5467 */
5468
5469 /* Fetch some rows */
5471 /* On error, report the original query, not the FETCH. */
5472 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5473 pgfdw_report_error(res, conn, sql.data);
5474
5475 /* Process whatever we got. */
5476 numrows = PQntuples(res);
5477 for (i = 0; i < numrows; i++)
5478 analyze_row_processor(res, i, &astate);
5479
5480 PQclear(res);
5481
5482 /* Must be EOF if we didn't get all the rows requested. */
5483 if (numrows < fetch_size)
5484 break;
5485 }
5486
5487 /* Close the cursor, just to be tidy. */
5489
5491
5492 /* We assume that we have no dead tuple. */
5493 *totaldeadrows = 0.0;
5494
5495 /*
5496 * Without sampling, we've retrieved all living tuples from foreign
5497 * server, so report that as totalrows. Otherwise use the reltuples
5498 * estimate we got from the remote side.
5499 */
5500 if (method == ANALYZE_SAMPLE_OFF)
5501 *totalrows = astate.samplerows;
5502 else
5503 *totalrows = reltuples;
5504
5505 /*
5506 * Emit some interesting relation info
5507 */
5508 ereport(elevel,
5509 (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
5510 RelationGetRelationName(relation),
5511 *totalrows, astate.numrows)));
5512
5513 return astate.numrows;
5514}
unsigned int GetCursorNumber(PGconn *conn)
void deparseAnalyzeSql(StringInfo buf, Relation rel, PgFdwSamplingMethod sample_method, double sample_frac, List **retrieved_attrs)
Definition deparse.c:2592
int errcode(int sqlerrcode)
Definition elog.c:874
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
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 1963 of file postgres_fdw.c.

1967{
1968 Var *var;
1969
1970 /*
1971 * In postgres_fdw, what we need is the ctid, same as for a regular table.
1972 */
1973
1974 /* Make a Var representing the desired value */
1975 var = makeVar(rtindex,
1977 TIDOID,
1978 -1,
1979 InvalidOid,
1980 0);
1981
1982 /* Register it as a row-identity column needed by this target rel */
1983 add_row_identity_var(root, var, rtindex, "ctid");
1984}
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 5105 of file postgres_fdw.c.

5108{
5111 PGconn *conn;
5112 StringInfoData sql;
5113 PGresult *res;
5114
5115 /* Return the row-analysis function pointer */
5117
5118 /*
5119 * Now we have to get the number of pages. It's annoying that the ANALYZE
5120 * API requires us to return that now, because it forces some duplication
5121 * of effort between this routine and postgresAcquireSampleRowsFunc. But
5122 * it's probably not worth redefining that API at this point.
5123 */
5124
5125 /*
5126 * Get the connection to use. We do the remote access as the table's
5127 * owner, even if the ANALYZE was started by some other user.
5128 */
5130 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5131 conn = GetConnection(user, false, NULL);
5132
5133 /*
5134 * Construct command to get page count for relation.
5135 */
5136 initStringInfo(&sql);
5137 deparseAnalyzeSizeSql(&sql, relation);
5138
5139 res = pgfdw_exec_query(conn, sql.data, NULL);
5140 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5141 pgfdw_report_error(res, conn, sql.data);
5142
5143 if (PQntuples(res) != 1 || PQnfields(res) != 1)
5144 elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
5145 *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
5146 PQclear(res);
5147
5149
5150 return true;
5151}
void deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
Definition deparse.c:2530
static int postgresAcquireSampleRowsFunc(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)

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

2862{
2863 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
2864 EState *estate = node->ss.ps.state;
2866 Index rtindex;
2867 Oid userid;
2870 int numParams;
2871
2872 /*
2873 * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
2874 */
2875 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2876 return;
2877
2878 /*
2879 * We'll save private state in node->fdw_state.
2880 */
2882 node->fdw_state = dmstate;
2883
2884 /*
2885 * Identify which user to do the remote access as. This should match what
2886 * ExecCheckPermissions() does.
2887 */
2888 userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
2889
2890 /* Get info about foreign table. */
2891 rtindex = node->resultRelInfo->ri_RangeTableIndex;
2892 if (fsplan->scan.scanrelid == 0)
2893 dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags);
2894 else
2895 dmstate->rel = node->ss.ss_currentRelation;
2897 user = GetUserMapping(userid, table->serverid);
2898
2899 /*
2900 * Get connection to the foreign server. Connection manager will
2901 * establish new connection if necessary.
2902 */
2903 dmstate->conn = GetConnection(user, false, &dmstate->conn_state);
2904
2905 /* Update the foreign-join-related fields. */
2906 if (fsplan->scan.scanrelid == 0)
2907 {
2908 /* Save info about foreign table. */
2909 dmstate->resultRel = dmstate->rel;
2910
2911 /*
2912 * Set dmstate->rel to NULL to teach get_returning_data() and
2913 * make_tuple_from_result_row() that columns fetched from the remote
2914 * server are described by fdw_scan_tlist of the foreign-scan plan
2915 * node, not the tuple descriptor for the target relation.
2916 */
2917 dmstate->rel = NULL;
2918 }
2919
2920 /* Initialize state variable */
2921 dmstate->num_tuples = -1; /* -1 means not set yet */
2922
2923 /* Get private info created by planner functions. */
2924 dmstate->query = strVal(list_nth(fsplan->fdw_private,
2926 dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
2928 dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
2930 dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
2932
2933 /* Create context for per-tuple temp workspace. */
2934 dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
2935 "postgres_fdw temporary data",
2937
2938 /* Prepare for input conversion of RETURNING results. */
2939 if (dmstate->has_returning)
2940 {
2941 TupleDesc tupdesc;
2942
2943 if (fsplan->scan.scanrelid == 0)
2944 tupdesc = get_tupdesc_for_join_scan_tuples(node);
2945 else
2946 tupdesc = RelationGetDescr(dmstate->rel);
2947
2948 dmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
2949
2950 /*
2951 * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2952 * initialize a filter to extract an updated/deleted tuple from a scan
2953 * tuple.
2954 */
2955 if (fsplan->scan.scanrelid == 0)
2956 init_returning_filter(dmstate, fsplan->fdw_scan_tlist, rtindex);
2957 }
2958
2959 /*
2960 * Prepare for processing of parameters used in remote query, if any.
2961 */
2962 numParams = list_length(fsplan->fdw_exprs);
2963 dmstate->numParams = numParams;
2964 if (numParams > 0)
2966 fsplan->fdw_exprs,
2967 numParams,
2968 &dmstate->param_flinfo,
2969 &dmstate->param_exprs,
2970 &dmstate->param_values);
2971}
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:510
Relation ss_currentRelation
Definition execnodes.h:1660
#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 2372 of file postgres_fdw.c.

2374{
2377 EState *estate = mtstate->ps.state;
2378 Index resultRelation;
2379 Relation rel = resultRelInfo->ri_RelationDesc;
2381 TupleDesc tupdesc = RelationGetDescr(rel);
2382 int attnum;
2383 int values_end_len;
2384 StringInfoData sql;
2385 List *targetAttrs = NIL;
2386 List *retrieved_attrs = NIL;
2387 bool doNothing = false;
2388
2389 /*
2390 * If the foreign table we are about to insert routed rows into is also an
2391 * UPDATE subplan result rel that will be updated later, proceeding with
2392 * the INSERT will result in the later UPDATE incorrectly modifying those
2393 * routed rows, so prevent the INSERT --- it would be nice if we could
2394 * handle this case; but for now, throw an error for safety.
2395 */
2396 if (plan && plan->operation == CMD_UPDATE &&
2397 (resultRelInfo->ri_usesFdwDirectModify ||
2398 resultRelInfo->ri_FdwState))
2399 ereport(ERROR,
2401 errmsg("cannot route tuples into foreign table to be updated \"%s\"",
2403
2404 initStringInfo(&sql);
2405
2406 /* We transmit all columns that are defined in the foreign table. */
2407 for (attnum = 1; attnum <= tupdesc->natts; attnum++)
2408 {
2409 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
2410
2411 if (!attr->attisdropped)
2413 }
2414
2415 /* Check if we add the ON CONFLICT clause to the remote query. */
2416 if (plan)
2417 {
2418 OnConflictAction onConflictAction = plan->onConflictAction;
2419
2420 /* We only support DO NOTHING without an inference specification. */
2421 if (onConflictAction == ONCONFLICT_NOTHING)
2422 doNothing = true;
2423 else if (onConflictAction != ONCONFLICT_NONE)
2424 elog(ERROR, "unexpected ON CONFLICT specification: %d",
2425 (int) onConflictAction);
2426 }
2427
2428 /*
2429 * If the foreign table is a partition that doesn't have a corresponding
2430 * RTE entry, we need to create a new RTE describing the foreign table for
2431 * use by deparseInsertSql and create_foreign_modify() below, after first
2432 * copying the parent's RTE and modifying some fields to describe the
2433 * foreign partition to work on. However, if this is invoked by UPDATE,
2434 * the existing RTE may already correspond to this partition if it is one
2435 * of the UPDATE subplan target rels; in that case, we can just use the
2436 * existing RTE as-is.
2437 */
2438 if (resultRelInfo->ri_RangeTableIndex == 0)
2439 {
2440 ResultRelInfo *rootResultRelInfo = resultRelInfo->ri_RootResultRelInfo;
2441
2442 rte = exec_rt_fetch(rootResultRelInfo->ri_RangeTableIndex, estate);
2443 rte = copyObject(rte);
2444 rte->relid = RelationGetRelid(rel);
2445 rte->relkind = RELKIND_FOREIGN_TABLE;
2446
2447 /*
2448 * For UPDATE, we must use the RT index of the first subplan target
2449 * rel's RTE, because the core code would have built expressions for
2450 * the partition, such as RETURNING, using that RT index as varno of
2451 * Vars contained in those expressions.
2452 */
2453 if (plan && plan->operation == CMD_UPDATE &&
2454 rootResultRelInfo->ri_RangeTableIndex == plan->rootRelation)
2455 resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
2456 else
2457 resultRelation = rootResultRelInfo->ri_RangeTableIndex;
2458 }
2459 else
2460 {
2461 resultRelation = resultRelInfo->ri_RangeTableIndex;
2462 rte = exec_rt_fetch(resultRelation, estate);
2463 }
2464
2465 /* Construct the SQL command string. */
2466 deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing,
2467 resultRelInfo->ri_WithCheckOptions,
2468 resultRelInfo->ri_returningList,
2469 &retrieved_attrs, &values_end_len);
2470
2471 /* Construct an execution state. */
2473 rte,
2474 resultRelInfo,
2475 CMD_INSERT,
2476 NULL,
2477 sql.data,
2480 retrieved_attrs != NIL,
2481 retrieved_attrs);
2482
2483 /*
2484 * If the given resultRelInfo already has PgFdwModifyState set, it means
2485 * the foreign table is an UPDATE subplan result rel; in which case, store
2486 * the resulting state into the aux_fmstate of the PgFdwModifyState.
2487 */
2488 if (resultRelInfo->ri_FdwState)
2489 {
2490 Assert(plan && plan->operation == CMD_UPDATE);
2491 Assert(resultRelInfo->ri_usesFdwDirectModify == false);
2492 ((PgFdwModifyState *) resultRelInfo->ri_FdwState)->aux_fmstate = fmstate;
2493 }
2494 else
2495 resultRelInfo->ri_FdwState = fmstate;
2496}
void deparseInsertSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, bool doNothing, List *withCheckOptionList, List *returningList, List **retrieved_attrs, int *values_end_len)
Definition deparse.c:2114
List * lappend_int(List *list, int datum)
Definition list.c:357
#define copyObject(obj)
Definition nodes.h:232
OnConflictAction
Definition nodes.h:427
@ ONCONFLICT_NONE
Definition nodes.h:428
@ ONCONFLICT_NOTHING
Definition nodes.h:429
static PgFdwModifyState * create_foreign_modify(EState *estate, RangeTblEntry *rte, ResultRelInfo *resultRelInfo, CmdType operation, Plan *subplan, char *query, List *target_attrs, int values_end, bool has_returning, List *retrieved_attrs)
bool attisdropped
Definition tupdesc.h:78
ResultRelInfo * resultRelInfo
Definition execnodes.h:1446
struct ResultRelInfo * ri_RootResultRelInfo
Definition execnodes.h:654
List * ri_WithCheckOptions
Definition execnodes.h:582
List * ri_returningList
Definition execnodes.h:607
bool ri_usesFdwDirectModify
Definition execnodes.h:572

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

2135{
2137 char *query;
2138 List *target_attrs;
2139 bool has_returning;
2140 int values_end_len;
2141 List *retrieved_attrs;
2143
2144 /*
2145 * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
2146 * stays NULL.
2147 */
2148 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2149 return;
2150
2151 /* Deconstruct fdw_private data. */
2152 query = strVal(list_nth(fdw_private,
2154 target_attrs = (List *) list_nth(fdw_private,
2156 values_end_len = intVal(list_nth(fdw_private,
2158 has_returning = boolVal(list_nth(fdw_private,
2160 retrieved_attrs = (List *) list_nth(fdw_private,
2162
2163 /* Find RTE. */
2164 rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex,
2165 mtstate->ps.state);
2166
2167 /* Construct an execution state. */
2169 rte,
2170 resultRelInfo,
2171 mtstate->operation,
2172 outerPlanState(mtstate)->plan,
2173 query,
2174 target_attrs,
2176 has_returning,
2177 retrieved_attrs);
2178
2179 resultRelInfo->ri_FdwState = fmstate;
2180}
#define outerPlanState(node)
Definition execnodes.h:1299
#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 1714 of file postgres_fdw.c.

1715{
1716 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1717 EState *estate = node->ss.ps.state;
1718 PgFdwScanState *fsstate;
1720 Oid userid;
1723 int rtindex;
1724 int numParams;
1725
1726 /*
1727 * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
1728 */
1729 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1730 return;
1731
1732 /*
1733 * We'll save private state in node->fdw_state.
1734 */
1735 fsstate = palloc0_object(PgFdwScanState);
1736 node->fdw_state = fsstate;
1737
1738 /*
1739 * Identify which user to do the remote access as. This should match what
1740 * ExecCheckPermissions() does.
1741 */
1742 userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
1743 if (fsplan->scan.scanrelid > 0)
1744 rtindex = fsplan->scan.scanrelid;
1745 else
1746 rtindex = bms_next_member(fsplan->fs_base_relids, -1);
1747 rte = exec_rt_fetch(rtindex, estate);
1748
1749 /* Get info about foreign table. */
1750 table = GetForeignTable(rte->relid);
1751 user = GetUserMapping(userid, table->serverid);
1752
1753 /*
1754 * Get connection to the foreign server. Connection manager will
1755 * establish new connection if necessary.
1756 */
1757 fsstate->conn = GetConnection(user, false, &fsstate->conn_state);
1758
1759 /* Assign a unique ID for my cursor */
1760 fsstate->cursor_number = GetCursorNumber(fsstate->conn);
1761 fsstate->cursor_exists = false;
1762
1763 /* Get private info created by planner functions. */
1764 fsstate->query = strVal(list_nth(fsplan->fdw_private,
1766 fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
1768 fsstate->fetch_size = intVal(list_nth(fsplan->fdw_private,
1770
1771 /* Create contexts for batches of tuples and per-tuple temp workspace. */
1772 fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
1773 "postgres_fdw tuple data",
1775 fsstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
1776 "postgres_fdw temporary data",
1778
1779 /*
1780 * Get info we'll need for converting data fetched from the foreign server
1781 * into local representation and error reporting during that process.
1782 */
1783 if (fsplan->scan.scanrelid > 0)
1784 {
1785 fsstate->rel = node->ss.ss_currentRelation;
1786 fsstate->tupdesc = RelationGetDescr(fsstate->rel);
1787 }
1788 else
1789 {
1790 fsstate->rel = NULL;
1792 }
1793
1794 fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
1795
1796 /*
1797 * Prepare for processing of parameters used in remote query, if any.
1798 */
1799 numParams = list_length(fsplan->fdw_exprs);
1800 fsstate->numParams = numParams;
1801 if (numParams > 0)
1803 fsplan->fdw_exprs,
1804 numParams,
1805 &fsstate->param_flinfo,
1806 &fsstate->param_exprs,
1807 &fsstate->param_values);
1808
1809 /* Set the async-capable flag */
1810 fsstate->async_capable = node->ss.ps.async_capable;
1811}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
#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:1245

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

3023{
3025
3026 /* if dmstate is NULL, we are in EXPLAIN; nothing to do */
3027 if (dmstate == NULL)
3028 return;
3029
3030 /* Release PGresult */
3031 PQclear(dmstate->result);
3032
3033 /* Release remote connection */
3035 dmstate->conn = NULL;
3036
3037 /* MemoryContext will be deleted automatically. */
3038}

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

2505{
2507
2508 Assert(fmstate != NULL);
2509
2510 /*
2511 * If the fmstate has aux_fmstate set, get the aux_fmstate (see
2512 * postgresBeginForeignInsert())
2513 */
2514 if (fmstate->aux_fmstate)
2515 fmstate = fmstate->aux_fmstate;
2516
2517 /* Destroy the execution state */
2519}
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 2354 of file postgres_fdw.c.

2356{
2358
2359 /* If fmstate is NULL, we are in EXPLAIN; nothing to do */
2360 if (fmstate == NULL)
2361 return;
2362
2363 /* Destroy the execution state */
2365}

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

Referenced by postgres_fdw_handler().

◆ postgresEndForeignScan()

static void postgresEndForeignScan ( ForeignScanState node)
static

Definition at line 1938 of file postgres_fdw.c.

1939{
1940 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1941
1942 /* if fsstate is NULL, we are in EXPLAIN; nothing to do */
1943 if (fsstate == NULL)
1944 return;
1945
1946 /* Close the cursor if open, to prevent accumulation of cursors */
1947 if (fsstate->cursor_exists)
1948 close_cursor(fsstate->conn, fsstate->cursor_number,
1949 fsstate->conn_state);
1950
1951 /* Release remote connection */
1952 ReleaseConnection(fsstate->conn);
1953 fsstate->conn = NULL;
1954
1955 /* MemoryContexts will be deleted automatically. */
1956}

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

2221{
2224
2225 /*
2226 * If the fmstate has aux_fmstate set, use the aux_fmstate (see
2227 * postgresBeginForeignInsert())
2228 */
2229 if (fmstate->aux_fmstate)
2230 resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2231 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2232 slots, planSlots, numSlots);
2233 /* Revert that change */
2234 if (fmstate->aux_fmstate)
2235 resultRelInfo->ri_FdwState = fmstate;
2236
2237 return rslot;
2238}
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 2335 of file postgres_fdw.c.

2339{
2341 int numSlots = 1;
2342
2343 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_DELETE,
2344 &slot, &planSlot, &numSlots);
2345
2346 return rslot ? rslot[0] : NULL;
2347}

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

2191{
2194 int numSlots = 1;
2195
2196 /*
2197 * If the fmstate has aux_fmstate set, use the aux_fmstate (see
2198 * postgresBeginForeignInsert())
2199 */
2200 if (fmstate->aux_fmstate)
2201 resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2202 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2203 &slot, &planSlot, &numSlots);
2204 /* Revert that change */
2205 if (fmstate->aux_fmstate)
2206 resultRelInfo->ri_FdwState = fmstate;
2207
2208 return rslot ? *rslot : NULL;
2209}

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

3201{
3202 Oid serverid = InvalidOid;
3204 PGconn *conn = NULL;
3205 StringInfoData sql;
3206 ListCell *lc;
3207 bool server_truncatable = true;
3208
3209 /*
3210 * By default, all postgres_fdw foreign tables are assumed truncatable.
3211 * This can be overridden by a per-server setting, which in turn can be
3212 * overridden by a per-table setting.
3213 */
3214 foreach(lc, rels)
3215 {
3216 ForeignServer *server = NULL;
3217 Relation rel = lfirst(lc);
3219 ListCell *cell;
3220 bool truncatable;
3221
3222 /*
3223 * First time through, determine whether the foreign server allows
3224 * truncates. Since all specified foreign tables are assumed to belong
3225 * to the same foreign server, this result can be used for other
3226 * foreign tables.
3227 */
3228 if (!OidIsValid(serverid))
3229 {
3230 serverid = table->serverid;
3231 server = GetForeignServer(serverid);
3232
3233 foreach(cell, server->options)
3234 {
3235 DefElem *defel = (DefElem *) lfirst(cell);
3236
3237 if (strcmp(defel->defname, "truncatable") == 0)
3238 {
3240 break;
3241 }
3242 }
3243 }
3244
3245 /*
3246 * Confirm that all specified foreign tables belong to the same
3247 * foreign server.
3248 */
3249 Assert(table->serverid == serverid);
3250
3251 /* Determine whether this foreign table allows truncations */
3253 foreach(cell, table->options)
3254 {
3255 DefElem *defel = (DefElem *) lfirst(cell);
3256
3257 if (strcmp(defel->defname, "truncatable") == 0)
3258 {
3260 break;
3261 }
3262 }
3263
3264 if (!truncatable)
3265 ereport(ERROR,
3267 errmsg("foreign table \"%s\" does not allow truncates",
3269 }
3270 Assert(OidIsValid(serverid));
3271
3272 /*
3273 * Get connection to the foreign server. Connection manager will
3274 * establish new connection if necessary.
3275 */
3276 user = GetUserMapping(GetUserId(), serverid);
3277 conn = GetConnection(user, false, NULL);
3278
3279 /* Construct the TRUNCATE command string */
3280 initStringInfo(&sql);
3281 deparseTruncateSql(&sql, rels, behavior, restart_seqs);
3282
3283 /* Issue the TRUNCATE command to remote server */
3284 do_sql_command(conn, sql.data);
3285
3286 pfree(sql.data);
3287}
void do_sql_command(PGconn *conn, const char *sql)
Definition connection.c:841
void deparseTruncateSql(StringInfo buf, List *rels, DropBehavior behavior, bool restart_seqs)
Definition deparse.c:2677

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

2320{
2322 int numSlots = 1;
2323
2324 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_UPDATE,
2325 &slot, &planSlot, &numSlots);
2326
2327 return rslot ? rslot[0] : NULL;
2328}

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

3181{
3182 List *fdw_private;
3183 char *sql;
3184
3185 if (es->verbose)
3186 {
3187 fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
3188 sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql));
3189 ExplainPropertyText("Remote SQL", sql, es);
3190 }
3191}
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 3152 of file postgres_fdw.c.

3157{
3158 if (es->verbose)
3159 {
3160 char *sql = strVal(list_nth(fdw_private,
3162
3163 ExplainPropertyText("Remote SQL", sql, es);
3164
3165 /*
3166 * For INSERT we should always have batch size >= 1, but UPDATE and
3167 * DELETE don't support batching so don't show the property.
3168 */
3169 if (rinfo->ri_BatchSize > 0)
3170 ExplainPropertyInteger("Batch Size", NULL, rinfo->ri_BatchSize, es);
3171 }
3172}
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 3045 of file postgres_fdw.c.

3046{
3048 List *fdw_private = plan->fdw_private;
3049
3050 /*
3051 * Identify foreign scans that are really joins or upper relations. The
3052 * input looks something like "(1) LEFT JOIN (2)", and we must replace the
3053 * digit string(s), which are RT indexes, with the correct relation names.
3054 * We do that here, not when the plan is created, because we can't know
3055 * what aliases ruleutils.c will assign at plan creation time.
3056 */
3057 if (list_length(fdw_private) > FdwScanPrivateRelations)
3058 {
3059 StringInfoData relations;
3060 char *rawrelations;
3061 char *ptr;
3062 int minrti,
3063 rtoffset;
3064
3066
3067 /*
3068 * A difficulty with using a string representation of RT indexes is
3069 * that setrefs.c won't update the string when flattening the
3070 * rangetable. To find out what rtoffset was applied, identify the
3071 * minimum RT index appearing in the string and compare it to the
3072 * minimum member of plan->fs_base_relids. (We expect all the relids
3073 * in the join will have been offset by the same amount; the Asserts
3074 * below should catch it if that ever changes.)
3075 */
3076 minrti = INT_MAX;
3077 ptr = rawrelations;
3078 while (*ptr)
3079 {
3080 if (isdigit((unsigned char) *ptr))
3081 {
3082 int rti = strtol(ptr, &ptr, 10);
3083
3084 if (rti < minrti)
3085 minrti = rti;
3086 }
3087 else
3088 ptr++;
3089 }
3090 rtoffset = bms_next_member(plan->fs_base_relids, -1) - minrti;
3091
3092 /* Now we can translate the string */
3093 initStringInfo(&relations);
3094 ptr = rawrelations;
3095 while (*ptr)
3096 {
3097 if (isdigit((unsigned char) *ptr))
3098 {
3099 int rti = strtol(ptr, &ptr, 10);
3101 char *relname;
3102 char *refname;
3103
3104 rti += rtoffset;
3105 Assert(bms_is_member(rti, plan->fs_base_relids));
3106 rte = rt_fetch(rti, es->rtable);
3107 Assert(rte->rtekind == RTE_RELATION);
3108 /* This logic should agree with explain.c's ExplainTargetRel */
3109 relname = get_rel_name(rte->relid);
3110 if (es->verbose)
3111 {
3112 char *namespace;
3113
3115 appendStringInfo(&relations, "%s.%s",
3116 quote_identifier(namespace),
3118 }
3119 else
3120 appendStringInfoString(&relations,
3122 refname = (char *) list_nth(es->rtable_names, rti - 1);
3123 if (refname == NULL)
3124 refname = rte->eref->aliasname;
3125 if (strcmp(refname, relname) != 0)
3126 appendStringInfo(&relations, " %s",
3127 quote_identifier(refname));
3128 }
3129 else
3130 appendStringInfoChar(&relations, *ptr++);
3131 }
3132 ExplainPropertyText("Relations", relations.data, es);
3133 }
3134
3135 /*
3136 * Add remote query, when VERBOSE option is specified.
3137 */
3138 if (es->verbose)
3139 {
3140 char *sql;
3141
3142 sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
3143 ExplainPropertyText("Remote SQL", sql, es);
3144 }
3145}
char * get_rel_name(Oid relid)
Definition lsyscache.c:2148
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2172
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3612
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
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 8140 of file postgres_fdw.c.

8141{
8142 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8143 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8144 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
8145 AppendState *requestor = (AppendState *) areq->requestor;
8146 WaitEventSet *set = requestor->as_eventset;
8147
8148 /* This should not be called unless callback_pending */
8149 Assert(areq->callback_pending);
8150
8151 /*
8152 * If process_pending_request() has been invoked on the given request
8153 * before we get here, we might have some tuples already; in which case
8154 * complete the request
8155 */
8156 if (fsstate->next_tuple < fsstate->num_tuples)
8157 {
8159 if (areq->request_complete)
8160 return;
8161 Assert(areq->callback_pending);
8162 }
8163
8164 /* We must have run out of tuples */
8165 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8166
8167 /* The core code would have registered postmaster death event */
8169
8170 /* Begin an asynchronous data fetch if not already done */
8171 if (!pendingAreq)
8173 else if (pendingAreq->requestor != areq->requestor)
8174 {
8175 /*
8176 * This is the case when the in-process request was made by another
8177 * Append. Note that it might be useless to process the request made
8178 * by that Append, because the query might not need tuples from that
8179 * Append anymore; so we avoid processing it to begin a fetch for the
8180 * given request if possible. If there are any child subplans of the
8181 * same parent that are ready for new requests, skip the given
8182 * request. Likewise, if there are any configured events other than
8183 * the postmaster death event, skip it. Otherwise, process the
8184 * in-process request, then begin a fetch to configure the event
8185 * below, because we might otherwise end up with no configured events
8186 * other than the postmaster death event.
8187 */
8188 if (!bms_is_empty(requestor->as_needrequest))
8189 return;
8190 if (GetNumRegisteredWaitEvents(set) > 1)
8191 return;
8192 process_pending_request(pendingAreq);
8194 }
8195 else if (pendingAreq->requestee != areq->requestee)
8196 {
8197 /*
8198 * This is the case when the in-process request was made by the same
8199 * parent but for a different child. Since we configure only the
8200 * event for the request made for that child, skip the given request.
8201 */
8202 return;
8203 }
8204 else
8205 Assert(pendingAreq == areq);
8206
8208 NULL, areq);
8209}
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:1547
struct WaitEventSet * as_eventset
Definition execnodes.h:1548
PlanState * requestor
Definition execnodes.h:675
PlanState * requestee
Definition execnodes.h:676
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 8217 of file postgres_fdw.c.

8218{
8219 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8220 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8221
8222 /* The core code would have initialized the callback_pending flag */
8223 Assert(!areq->callback_pending);
8224
8225 /*
8226 * If process_pending_request() has been invoked on the given request
8227 * before we get here, we might have some tuples already; in which case
8228 * produce the next tuple
8229 */
8230 if (fsstate->next_tuple < fsstate->num_tuples)
8231 {
8233 return;
8234 }
8235
8236 /* We must have run out of tuples */
8237 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8238
8239 /* The request should be currently in-process */
8240 Assert(fsstate->conn_state->pendingAreq == areq);
8241
8242 /* On error, report the original query, not the FETCH. */
8243 if (!PQconsumeInput(fsstate->conn))
8244 pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
8245
8246 fetch_more_data(node);
8247
8249}
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 8130 of file postgres_fdw.c.

8131{
8133}

References fb(), and produce_tuple_asynchronously().

Referenced by postgres_fdw_handler().

◆ postgresGetAnalyzeInfoForForeignTable()

static double postgresGetAnalyzeInfoForForeignTable ( Relation  relation,
bool can_tablesample 
)
static

Definition at line 5161 of file postgres_fdw.c.

5162{
5165 PGconn *conn;
5166 StringInfoData sql;
5167 PGresult *res;
5168 double reltuples;
5169 char relkind;
5170
5171 /* assume the remote relation does not support TABLESAMPLE */
5172 *can_tablesample = false;
5173
5174 /*
5175 * Get the connection to use. We do the remote access as the table's
5176 * owner, even if the ANALYZE was started by some other user.
5177 */
5179 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5180 conn = GetConnection(user, false, NULL);
5181
5182 /*
5183 * Construct command to get page count for relation.
5184 */
5185 initStringInfo(&sql);
5186 deparseAnalyzeInfoSql(&sql, relation);
5187
5188 res = pgfdw_exec_query(conn, sql.data, NULL);
5189 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5190 pgfdw_report_error(res, conn, sql.data);
5191
5192 if (PQntuples(res) != 1 || PQnfields(res) != RELSTATS_NUM_FIELDS)
5193 elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
5194 /* We don't use relpages here */
5195 reltuples = strtod(PQgetvalue(res, 0, RELSTATS_RELTUPLES), NULL);
5196 relkind = *(PQgetvalue(res, 0, RELSTATS_RELKIND));
5197 PQclear(res);
5198
5200
5201 /* TABLESAMPLE is supported only for regular tables and matviews */
5202 *can_tablesample = (relkind == RELKIND_RELATION ||
5203 relkind == RELKIND_MATVIEW ||
5204 relkind == RELKIND_PARTITIONED_TABLE);
5205
5206 return reltuples;
5207}

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

7198{
7201 double rows;
7202 int width;
7203 int disabled_nodes;
7204 Cost startup_cost;
7205 Cost total_cost;
7206 Path *epq_path; /* Path to create plan to be executed when
7207 * EvalPlanQual gets triggered. */
7208
7209 /*
7210 * Skip if this join combination has been considered already.
7211 */
7212 if (joinrel->fdw_private)
7213 return;
7214
7215 /*
7216 * This code does not work for joins with lateral references, since those
7217 * must have parameterized paths, which we don't generate yet.
7218 */
7219 if (!bms_is_empty(joinrel->lateral_relids))
7220 return;
7221
7222 /*
7223 * Create unfinished PgFdwRelationInfo entry which is used to indicate
7224 * that the join relation is already considered, so that we won't waste
7225 * time in judging safety of join pushdown and adding the same paths again
7226 * if found safe. Once we know that this join can be pushed down, we fill
7227 * the entry.
7228 */
7230 fpinfo->pushdown_safe = false;
7231 joinrel->fdw_private = fpinfo;
7232 /* attrs_used is only for base relations. */
7233 fpinfo->attrs_used = NULL;
7234
7235 /*
7236 * If there is a possibility that EvalPlanQual will be executed, we need
7237 * to be able to reconstruct the row using scans of the base relations.
7238 * GetExistingLocalJoinPath will find a suitable path for this purpose in
7239 * the path list of the joinrel, if one exists. We must be careful to
7240 * call it before adding any ForeignPath, since the ForeignPath might
7241 * dominate the only suitable local path available. We also do it before
7242 * calling foreign_join_ok(), since that function updates fpinfo and marks
7243 * it as pushable if the join is found to be pushable.
7244 */
7245 if (root->parse->commandType == CMD_DELETE ||
7246 root->parse->commandType == CMD_UPDATE ||
7247 root->rowMarks)
7248 {
7250 if (!epq_path)
7251 {
7252 elog(DEBUG3, "could not push down foreign join because a local path suitable for EPQ checks was not found");
7253 return;
7254 }
7255 }
7256 else
7257 epq_path = NULL;
7258
7259 if (!foreign_join_ok(root, joinrel, jointype, outerrel, innerrel, extra))
7260 {
7261 /* Free path required for EPQ if we copied one; we don't need it now */
7262 if (epq_path)
7263 pfree(epq_path);
7264 return;
7265 }
7266
7267 /*
7268 * Compute the selectivity and cost of the local_conds, so we don't have
7269 * to do it over again for each path. The best we can do for these
7270 * conditions is to estimate selectivity on the basis of local statistics.
7271 * The local conditions are applied after the join has been computed on
7272 * the remote side like quals in WHERE clause, so pass jointype as
7273 * JOIN_INNER.
7274 */
7275 fpinfo->local_conds_sel = clauselist_selectivity(root,
7276 fpinfo->local_conds,
7277 0,
7278 JOIN_INNER,
7279 NULL);
7280 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
7281
7282 /*
7283 * If we are going to estimate costs locally, estimate the join clause
7284 * selectivity here while we have special join info.
7285 */
7286 if (!fpinfo->use_remote_estimate)
7287 fpinfo->joinclause_sel = clauselist_selectivity(root, fpinfo->joinclauses,
7288 0, fpinfo->jointype,
7289 extra->sjinfo);
7290
7291 /* Estimate costs for bare join relation */
7293 &rows, &width, &disabled_nodes,
7294 &startup_cost, &total_cost);
7295 /* Now update this information in the joinrel */
7296 joinrel->rows = rows;
7297 joinrel->reltarget->width = width;
7298 fpinfo->rows = rows;
7299 fpinfo->width = width;
7300 fpinfo->disabled_nodes = disabled_nodes;
7301 fpinfo->startup_cost = startup_cost;
7302 fpinfo->total_cost = total_cost;
7303
7304 /*
7305 * Create a new join path and add it to the joinrel which represents a
7306 * join between foreign tables.
7307 */
7309 joinrel,
7310 NULL, /* default pathtarget */
7311 rows,
7312 disabled_nodes,
7313 startup_cost,
7314 total_cost,
7315 NIL, /* no pathkeys */
7316 joinrel->lateral_relids,
7317 epq_path,
7318 extra->restrictlist,
7319 NIL); /* no fdw_private */
7320
7321 /* Add generated path into joinrel by add_path(). */
7322 add_path(joinrel, (Path *) joinpath);
7323
7324 /* Consider pathkeys for the join relation */
7326 extra->restrictlist);
7327
7328 /* XXX Consider parameterized paths for the join relation */
7329}
#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:3609

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

2250{
2251 int batch_size;
2253
2254 /* should be called only once */
2255 Assert(resultRelInfo->ri_BatchSize == 0);
2256
2257 /*
2258 * Should never get called when the insert is being performed on a table
2259 * that is also among the target relations of an UPDATE operation, because
2260 * postgresBeginForeignInsert() currently rejects such insert attempts.
2261 */
2262 Assert(fmstate == NULL || fmstate->aux_fmstate == NULL);
2263
2264 /*
2265 * In EXPLAIN without ANALYZE, ri_FdwState is NULL, so we have to lookup
2266 * the option directly in server/table options. Otherwise just use the
2267 * value we determined earlier.
2268 */
2269 if (fmstate)
2270 batch_size = fmstate->batch_size;
2271 else
2272 batch_size = get_batch_size_option(resultRelInfo->ri_RelationDesc);
2273
2274 /*
2275 * Disable batching when we have to use RETURNING, there are any
2276 * BEFORE/AFTER ROW INSERT triggers on the foreign table, or there are any
2277 * WITH CHECK OPTION constraints from parent views.
2278 *
2279 * When there are any BEFORE ROW INSERT triggers on the table, we can't
2280 * support it, because such triggers might query the table we're inserting
2281 * into and act differently if the tuples that have already been processed
2282 * and prepared for insertion are not there.
2283 */
2284 if (resultRelInfo->ri_projectReturning != NULL ||
2285 resultRelInfo->ri_WithCheckOptions != NIL ||
2286 (resultRelInfo->ri_TrigDesc &&
2287 (resultRelInfo->ri_TrigDesc->trig_insert_before_row ||
2288 resultRelInfo->ri_TrigDesc->trig_insert_after_row)))
2289 return 1;
2290
2291 /*
2292 * If the foreign table has no columns, disable batching as the INSERT
2293 * syntax doesn't allow batching multiple empty rows into a zero-column
2294 * table in a single statement. This is needed for COPY FROM, in which
2295 * case fmstate must be non-NULL.
2296 */
2297 if (fmstate && list_length(fmstate->target_attrs) == 0)
2298 return 1;
2299
2300 /*
2301 * Otherwise use the batch size specified for server/table. The number of
2302 * parameters in a batch is limited to 65535 (uint16), so make sure we
2303 * don't exceed this limit by using the maximum batch_size possible.
2304 */
2305 if (fmstate && fmstate->p_nums > 0)
2306 batch_size = Min(batch_size, PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);
2307
2308 return batch_size;
2309}
#define PQ_QUERY_PARAM_MAX_LIMIT
Definition libpq-fe.h:517
TriggerDesc * ri_TrigDesc
Definition execnodes.h:548
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 1231 of file postgres_fdw.c.

1234{
1236 ForeignPath *path;
1237 List *ppi_list;
1238 ListCell *lc;
1239
1240 /*
1241 * Create simplest ForeignScan path node and add it to baserel. This path
1242 * corresponds to SeqScan path of regular tables (though depending on what
1243 * baserestrict conditions we were able to send to remote, there might
1244 * actually be an indexscan happening there). We already did all the work
1245 * to estimate cost and size of this path.
1246 *
1247 * Although this path uses no join clauses, it could still have required
1248 * parameterization due to LATERAL refs in its tlist.
1249 */
1251 NULL, /* default pathtarget */
1252 fpinfo->rows,
1253 fpinfo->disabled_nodes,
1254 fpinfo->startup_cost,
1255 fpinfo->total_cost,
1256 NIL, /* no pathkeys */
1257 baserel->lateral_relids,
1258 NULL, /* no extra plan */
1259 NIL, /* no fdw_restrictinfo list */
1260 NIL); /* no fdw_private list */
1261 add_path(baserel, (Path *) path);
1262
1263 /* Add paths with pathkeys */
1265
1266 /*
1267 * If we're not using remote estimates, stop here. We have no way to
1268 * estimate whether any join clauses would be worth sending across, so
1269 * don't bother building parameterized paths.
1270 */
1271 if (!fpinfo->use_remote_estimate)
1272 return;
1273
1274 /*
1275 * Thumb through all join clauses for the rel to identify which outer
1276 * relations could supply one or more safe-to-send-to-remote join clauses.
1277 * We'll build a parameterized path for each such outer relation.
1278 *
1279 * It's convenient to manage this by representing each candidate outer
1280 * relation by the ParamPathInfo node for it. We can then use the
1281 * ppi_clauses list in the ParamPathInfo node directly as a list of the
1282 * interesting join clauses for that rel. This takes care of the
1283 * possibility that there are multiple safe join clauses for such a rel,
1284 * and also ensures that we account for unsafe join clauses that we'll
1285 * still have to enforce locally (since the parameterized-path machinery
1286 * insists that we handle all movable clauses).
1287 */
1288 ppi_list = NIL;
1289 foreach(lc, baserel->joininfo)
1290 {
1291 RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
1294
1295 /* Check if clause can be moved to this rel */
1297 continue;
1298
1299 /* See if it is safe to send to remote */
1300 if (!is_foreign_expr(root, baserel, rinfo->clause))
1301 continue;
1302
1303 /* Calculate required outer rels for the resulting path */
1304 required_outer = bms_union(rinfo->clause_relids,
1305 baserel->lateral_relids);
1306 /* We do not want the foreign rel itself listed in required_outer */
1308
1309 /*
1310 * required_outer probably can't be empty here, but if it were, we
1311 * couldn't make a parameterized path.
1312 */
1314 continue;
1315
1316 /* Get the ParamPathInfo */
1320
1321 /*
1322 * Add it to list unless we already have it. Testing pointer equality
1323 * is OK since get_baserel_parampathinfo won't make duplicates.
1324 */
1326 }
1327
1328 /*
1329 * The above scan examined only "generic" join clauses, not those that
1330 * were absorbed into EquivalenceClauses. See if we can make anything out
1331 * of EquivalenceClauses.
1332 */
1333 if (baserel->has_eclass_joins)
1334 {
1335 /*
1336 * We repeatedly scan the eclass list looking for column references
1337 * (or expressions) belonging to the foreign rel. Each time we find
1338 * one, we generate a list of equivalence joinclauses for it, and then
1339 * see if any are safe to send to the remote. Repeat till there are
1340 * no more candidate EC members.
1341 */
1343
1345 for (;;)
1346 {
1347 List *clauses;
1348
1349 /* Make clauses, skipping any that join to lateral_referencers */
1350 arg.current = NULL;
1352 baserel,
1354 &arg,
1355 baserel->lateral_referencers);
1356
1357 /* Done if there are no more expressions in the foreign rel */
1358 if (arg.current == NULL)
1359 {
1360 Assert(clauses == NIL);
1361 break;
1362 }
1363
1364 /* Scan the extracted join clauses */
1365 foreach(lc, clauses)
1366 {
1367 RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
1370
1371 /* Check if clause can be moved to this rel */
1373 continue;
1374
1375 /* See if it is safe to send to remote */
1376 if (!is_foreign_expr(root, baserel, rinfo->clause))
1377 continue;
1378
1379 /* Calculate required outer rels for the resulting path */
1380 required_outer = bms_union(rinfo->clause_relids,
1381 baserel->lateral_relids);
1384 continue;
1385
1386 /* Get the ParamPathInfo */
1390
1391 /* Add it to list unless we already have it */
1393 }
1394
1395 /* Try again, now ignoring the expression we found this time */
1396 arg.already_used = lappend(arg.already_used, arg.current);
1397 }
1398 }
1399
1400 /*
1401 * Now build a path for each useful outer relation.
1402 */
1403 foreach(lc, ppi_list)
1404 {
1406 double rows;
1407 int width;
1408 int disabled_nodes;
1409 Cost startup_cost;
1410 Cost total_cost;
1411
1412 /* Get a cost estimate from the remote */
1414 param_info->ppi_clauses, NIL, NULL,
1415 &rows, &width, &disabled_nodes,
1416 &startup_cost, &total_cost);
1417
1418 /*
1419 * ppi_rows currently won't get looked at by anything, but still we
1420 * may as well ensure that it matches our idea of the rowcount.
1421 */
1422 param_info->ppi_rows = rows;
1423
1424 /* Make the path */
1426 NULL, /* default pathtarget */
1427 rows,
1428 disabled_nodes,
1429 startup_cost,
1430 total_cost,
1431 NIL, /* no pathkeys */
1432 param_info->ppi_req_outer,
1433 NULL,
1434 NIL, /* no fdw_restrictinfo list */
1435 NIL); /* no fdw_private list */
1436 add_path(baserel, (Path *) path);
1437 }
1438}
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition bitmapset.c:852
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 1445 of file postgres_fdw.c.

1452{
1453 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
1455 List *fdw_private;
1457 List *local_exprs = NIL;
1458 List *params_list = NIL;
1459 List *fdw_scan_tlist = NIL;
1460 List *fdw_recheck_quals = NIL;
1461 List *retrieved_attrs;
1462 StringInfoData sql;
1463 bool has_final_sort = false;
1464 bool has_limit = false;
1465 ListCell *lc;
1466
1467 /*
1468 * Get FDW private data created by postgresGetForeignUpperPaths(), if any.
1469 */
1470 if (best_path->fdw_private)
1471 {
1472 has_final_sort = boolVal(list_nth(best_path->fdw_private,
1474 has_limit = boolVal(list_nth(best_path->fdw_private,
1476 }
1477
1478 if (IS_SIMPLE_REL(foreignrel))
1479 {
1480 /*
1481 * For base relations, set scan_relid as the relid of the relation.
1482 */
1483 scan_relid = foreignrel->relid;
1484
1485 /*
1486 * In a base-relation scan, we must apply the given scan_clauses.
1487 *
1488 * Separate the scan_clauses into those that can be executed remotely
1489 * and those that can't. baserestrictinfo clauses that were
1490 * previously determined to be safe or unsafe by classifyConditions
1491 * are found in fpinfo->remote_conds and fpinfo->local_conds. Anything
1492 * else in the scan_clauses list will be a join clause, which we have
1493 * to check for remote-safety.
1494 *
1495 * Note: the join clauses we see here should be the exact same ones
1496 * previously examined by postgresGetForeignPaths. Possibly it'd be
1497 * worth passing forward the classification work done then, rather
1498 * than repeating it here.
1499 *
1500 * This code must match "extract_actual_clauses(scan_clauses, false)"
1501 * except for the additional decision about remote versus local
1502 * execution.
1503 */
1504 foreach(lc, scan_clauses)
1505 {
1507
1508 /* Ignore any pseudoconstants, they're dealt with elsewhere */
1509 if (rinfo->pseudoconstant)
1510 continue;
1511
1512 if (list_member_ptr(fpinfo->remote_conds, rinfo))
1514 else if (list_member_ptr(fpinfo->local_conds, rinfo))
1516 else if (is_foreign_expr(root, foreignrel, rinfo->clause))
1518 else
1520 }
1521
1522 /*
1523 * For a base-relation scan, we have to support EPQ recheck, which
1524 * should recheck all the remote quals.
1525 */
1526 fdw_recheck_quals = remote_exprs;
1527 }
1528 else
1529 {
1530 /*
1531 * Join relation or upper relation - set scan_relid to 0.
1532 */
1533 scan_relid = 0;
1534
1535 /*
1536 * For a join rel, baserestrictinfo is NIL and we are not considering
1537 * parameterization right now, so there should be no scan_clauses for
1538 * a joinrel or an upper rel either.
1539 */
1541
1542 /*
1543 * Instead we get the conditions to apply from the fdw_private
1544 * structure.
1545 */
1546 remote_exprs = extract_actual_clauses(fpinfo->remote_conds, false);
1547 local_exprs = extract_actual_clauses(fpinfo->local_conds, false);
1548
1549 /*
1550 * We leave fdw_recheck_quals empty in this case, since we never need
1551 * to apply EPQ recheck clauses. In the case of a joinrel, EPQ
1552 * recheck is handled elsewhere --- see postgresGetForeignJoinPaths().
1553 * If we're planning an upperrel (ie, remote grouping or aggregation)
1554 * then there's no EPQ to do because SELECT FOR UPDATE wouldn't be
1555 * allowed, and indeed we *can't* put the remote clauses into
1556 * fdw_recheck_quals because the unaggregated Vars won't be available
1557 * locally.
1558 */
1559
1560 /* Build the list of columns to be fetched from the foreign server. */
1561 fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
1562
1563 /*
1564 * Ensure that the outer plan produces a tuple whose descriptor
1565 * matches our scan tuple slot. Also, remove the local conditions
1566 * from outer plan's quals, lest they be evaluated twice, once by the
1567 * local plan and once by the scan.
1568 */
1569 if (outer_plan)
1570 {
1571 /*
1572 * Right now, we only consider grouping and aggregation beyond
1573 * joins. Queries involving aggregates or grouping do not require
1574 * EPQ mechanism, hence should not have an outer plan here.
1575 */
1576 Assert(!IS_UPPER_REL(foreignrel));
1577
1578 /*
1579 * First, update the plan's qual list if possible. In some cases
1580 * the quals might be enforced below the topmost plan level, in
1581 * which case we'll fail to remove them; it's not worth working
1582 * harder than this.
1583 */
1584 foreach(lc, local_exprs)
1585 {
1586 Node *qual = lfirst(lc);
1587
1588 outer_plan->qual = list_delete(outer_plan->qual, qual);
1589
1590 /*
1591 * For an inner join the local conditions of foreign scan plan
1592 * can be part of the joinquals as well. (They might also be
1593 * in the mergequals or hashquals, but we can't touch those
1594 * without breaking the plan.)
1595 */
1596 if (IsA(outer_plan, NestLoop) ||
1597 IsA(outer_plan, MergeJoin) ||
1598 IsA(outer_plan, HashJoin))
1599 {
1600 Join *join_plan = (Join *) outer_plan;
1601
1602 if (join_plan->jointype == JOIN_INNER)
1604 qual);
1605 }
1606 }
1607
1608 /*
1609 * Now fix the subplan's tlist --- this might result in inserting
1610 * a Result node atop the plan tree.
1611 */
1612 outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist,
1613 best_path->path.parallel_safe);
1614 }
1615 }
1616
1617 /*
1618 * Build the query string to be sent for execution, and identify
1619 * expressions to be sent as parameters.
1620 */
1621 initStringInfo(&sql);
1622 deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
1623 remote_exprs, best_path->path.pathkeys,
1624 has_final_sort, has_limit, false,
1625 &retrieved_attrs, &params_list);
1626
1627 /* Remember remote_exprs for possible use by postgresPlanDirectModify */
1628 fpinfo->final_remote_exprs = remote_exprs;
1629
1630 /*
1631 * Build the fdw_private list that will be available to the executor.
1632 * Items in the list must match order in enum FdwScanPrivateIndex.
1633 */
1634 fdw_private = list_make3(makeString(sql.data),
1635 retrieved_attrs,
1636 makeInteger(fpinfo->fetch_size));
1637 if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
1638 fdw_private = lappend(fdw_private,
1639 makeString(fpinfo->relation_name));
1640
1641 /*
1642 * Create the ForeignScan node for the given relation.
1643 *
1644 * Note that the remote parameter expressions are stored in the fdw_exprs
1645 * field of the finished plan node; we can't keep them in private state
1646 * because then they wouldn't be subject to later planner processing.
1647 */
1648 return make_foreignscan(tlist,
1650 scan_relid,
1651 params_list,
1652 fdw_private,
1653 fdw_scan_tlist,
1654 fdw_recheck_quals,
1655 outer_plan);
1656}
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:992
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 840 of file postgres_fdw.c.

843{
845 ListCell *lc;
846
847 /*
848 * We use PgFdwRelationInfo to pass various information to subsequent
849 * functions.
850 */
852 baserel->fdw_private = fpinfo;
853
854 /* Base foreign tables need to be pushed down always. */
855 fpinfo->pushdown_safe = true;
856
857 /* Look up foreign-table catalog info. */
859 fpinfo->server = GetForeignServer(fpinfo->table->serverid);
860
861 /*
862 * Extract user-settable option values. Note that per-table settings of
863 * use_remote_estimate, fetch_size and async_capable override per-server
864 * settings of them, respectively.
865 */
866 fpinfo->use_remote_estimate = false;
867 fpinfo->fdw_startup_cost = DEFAULT_FDW_STARTUP_COST;
868 fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
869 fpinfo->shippable_extensions = NIL;
870 fpinfo->fetch_size = 100;
871 fpinfo->async_capable = false;
872
875
876 /*
877 * If the table or the server is configured to use remote estimates,
878 * identify which user to do remote access as during planning. This
879 * should match what ExecCheckPermissions() does. If we fail due to lack
880 * of permissions, the query would have failed at runtime anyway.
881 */
882 if (fpinfo->use_remote_estimate)
883 {
884 Oid userid;
885
886 userid = OidIsValid(baserel->userid) ? baserel->userid : GetUserId();
887 fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid);
888 }
889 else
890 fpinfo->user = NULL;
891
892 /*
893 * Identify which baserestrictinfo clauses can be sent to the remote
894 * server and which can't.
895 */
896 classifyConditions(root, baserel, baserel->baserestrictinfo,
897 &fpinfo->remote_conds, &fpinfo->local_conds);
898
899 /*
900 * Identify which attributes will need to be retrieved from the remote
901 * server. These include all attrs needed for joins or final output, plus
902 * all attrs used in the local_conds. (Note: if we end up using a
903 * parameterized scan, it's possible that some of the join clauses will be
904 * sent to the remote and thus we wouldn't really need to retrieve the
905 * columns used in them. Doesn't seem worth detecting that case though.)
906 */
907 fpinfo->attrs_used = NULL;
908 pull_varattnos((Node *) baserel->reltarget->exprs, baserel->relid,
909 &fpinfo->attrs_used);
910 foreach(lc, fpinfo->local_conds)
911 {
913
914 pull_varattnos((Node *) rinfo->clause, baserel->relid,
915 &fpinfo->attrs_used);
916 }
917
918 /*
919 * Compute the selectivity and cost of the local_conds, so we don't have
920 * to do it over again for each path. The best we can do for these
921 * conditions is to estimate selectivity on the basis of local statistics.
922 */
923 fpinfo->local_conds_sel = clauselist_selectivity(root,
924 fpinfo->local_conds,
925 baserel->relid,
927 NULL);
928
929 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
930
931 /*
932 * Set # of retrieved rows and cached relation costs to some negative
933 * value, so that we can detect when they are set to some sensible values,
934 * during one (usually the first) of the calls to estimate_path_cost_size.
935 */
936 fpinfo->retrieved_rows = -1;
937 fpinfo->rel_startup_cost = -1;
938 fpinfo->rel_total_cost = -1;
939
940 /*
941 * If the table or the server is configured to use remote estimates,
942 * connect to the foreign server and execute EXPLAIN to estimate the
943 * number of rows selected by the restriction clauses, as well as the
944 * average row width. Otherwise, estimate using whatever statistics we
945 * have locally, in a way similar to ordinary tables.
946 */
947 if (fpinfo->use_remote_estimate)
948 {
949 /*
950 * Get cost/size estimates with help of remote server. Save the
951 * values in fpinfo so we don't need to do it again to generate the
952 * basic foreign path.
953 */
955 &fpinfo->rows, &fpinfo->width,
956 &fpinfo->disabled_nodes,
957 &fpinfo->startup_cost, &fpinfo->total_cost);
958
959 /* Report estimated baserel size to planner. */
960 baserel->rows = fpinfo->rows;
961 baserel->reltarget->width = fpinfo->width;
962 }
963 else
964 {
965 /*
966 * If the foreign table has never been ANALYZEd, it will have
967 * reltuples < 0, meaning "unknown". We can't do much if we're not
968 * allowed to consult the remote server, but we can use a hack similar
969 * to plancat.c's treatment of empty relations: use a minimum size
970 * estimate of 10 pages, and divide by the column-datatype-based width
971 * estimate to get the corresponding number of tuples.
972 */
973 if (baserel->tuples < 0)
974 {
975 baserel->pages = 10;
976 baserel->tuples =
977 (10 * BLCKSZ) / (baserel->reltarget->width +
979 }
980
981 /* Estimate baserel size as best we can with local statistics. */
983
984 /* Fill in basically-bogus cost estimates for use later. */
986 &fpinfo->rows, &fpinfo->width,
987 &fpinfo->disabled_nodes,
988 &fpinfo->startup_cost, &fpinfo->total_cost);
989 }
990
991 /*
992 * fpinfo->relation_name gets the numeric rangetable index of the foreign
993 * table RTE. (If this query gets EXPLAIN'd, we'll convert that to a
994 * human-readable string at that time.)
995 */
996 fpinfo->relation_name = psprintf("%u", baserel->relid);
997
998 /* No outer and inner relations. */
999 fpinfo->make_outerrel_subquery = false;
1000 fpinfo->make_innerrel_subquery = false;
1001 fpinfo->lower_subquery_rels = NULL;
1002 fpinfo->hidden_subquery_rels = NULL;
1003 /* Set the relation index. */
1004 fpinfo->relation_index = baserel->relid;
1005}
#define MAXALIGN(LEN)
Definition c.h:896
void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition costsize.c:5493
#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 7577 of file postgres_fdw.c.

7580{
7582
7583 /*
7584 * If input rel is not safe to pushdown, then simply return as we cannot
7585 * perform any post-join operations on the foreign server.
7586 */
7587 if (!input_rel->fdw_private ||
7588 !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
7589 return;
7590
7591 /* Ignore stages we don't support; and skip any duplicate calls. */
7592 if ((stage != UPPERREL_GROUP_AGG &&
7593 stage != UPPERREL_ORDERED &&
7594 stage != UPPERREL_FINAL) ||
7595 output_rel->fdw_private)
7596 return;
7597
7599 fpinfo->pushdown_safe = false;
7600 fpinfo->stage = stage;
7601 output_rel->fdw_private = fpinfo;
7602
7603 switch (stage)
7604 {
7605 case UPPERREL_GROUP_AGG:
7607 (GroupPathExtraData *) extra);
7608 break;
7609 case UPPERREL_ORDERED:
7611 break;
7612 case UPPERREL_FINAL:
7614 (FinalPathExtraData *) extra);
7615 break;
7616 default:
7617 elog(ERROR, "unexpected upper relation: %d", (int) stage);
7618 break;
7619 }
7620}
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 6302 of file postgres_fdw.c.

6303{
6304 List *commands = NIL;
6305 bool import_collate = true;
6306 bool import_default = false;
6307 bool import_generated = true;
6308 bool import_not_null = true;
6309 ForeignServer *server;
6311 PGconn *conn;
6313 PGresult *res;
6314 int numrows,
6315 i;
6316 ListCell *lc;
6317
6318 /* Parse statement options */
6319 foreach(lc, stmt->options)
6320 {
6321 DefElem *def = (DefElem *) lfirst(lc);
6322
6323 if (strcmp(def->defname, "import_collate") == 0)
6325 else if (strcmp(def->defname, "import_default") == 0)
6327 else if (strcmp(def->defname, "import_generated") == 0)
6329 else if (strcmp(def->defname, "import_not_null") == 0)
6331 else
6332 ereport(ERROR,
6334 errmsg("invalid option \"%s\"", def->defname)));
6335 }
6336
6337 /*
6338 * Get connection to the foreign server. Connection manager will
6339 * establish new connection if necessary.
6340 */
6341 server = GetForeignServer(serverOid);
6343 conn = GetConnection(mapping, false, NULL);
6344
6345 /* Don't attempt to import collation if remote server hasn't got it */
6346 if (PQserverVersion(conn) < 90100)
6347 import_collate = false;
6348
6349 /* Create workspace for strings */
6351
6352 /* Check that the schema really exists */
6353 appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
6354 deparseStringLiteral(&buf, stmt->remote_schema);
6355
6356 res = pgfdw_exec_query(conn, buf.data, NULL);
6357 if (PQresultStatus(res) != PGRES_TUPLES_OK)
6358 pgfdw_report_error(res, conn, buf.data);
6359
6360 if (PQntuples(res) != 1)
6361 ereport(ERROR,
6363 errmsg("schema \"%s\" is not present on foreign server \"%s\"",
6364 stmt->remote_schema, server->servername)));
6365
6366 PQclear(res);
6368
6369 /*
6370 * Fetch all table data from this schema, possibly restricted by EXCEPT or
6371 * LIMIT TO. (We don't actually need to pay any attention to EXCEPT/LIMIT
6372 * TO here, because the core code will filter the statements we return
6373 * according to those lists anyway. But it should save a few cycles to
6374 * not process excluded tables in the first place.)
6375 *
6376 * Import table data for partitions only when they are explicitly
6377 * specified in LIMIT TO clause. Otherwise ignore them and only include
6378 * the definitions of the root partitioned tables to allow access to the
6379 * complete remote data set locally in the schema imported.
6380 *
6381 * Note: because we run the connection with search_path restricted to
6382 * pg_catalog, the format_type() and pg_get_expr() outputs will always
6383 * include a schema name for types/functions in other schemas, which is
6384 * what we want.
6385 */
6387 "SELECT relname, "
6388 " attname, "
6389 " format_type(atttypid, atttypmod), "
6390 " attnotnull, "
6391 " pg_get_expr(adbin, adrelid), ");
6392
6393 /* Generated columns are supported since Postgres 12 */
6394 if (PQserverVersion(conn) >= 120000)
6396 " attgenerated, ");
6397 else
6399 " NULL, ");
6400
6401 if (import_collate)
6403 " collname, "
6404 " collnsp.nspname ");
6405 else
6407 " NULL, NULL ");
6408
6410 "FROM pg_class c "
6411 " JOIN pg_namespace n ON "
6412 " relnamespace = n.oid "
6413 " LEFT JOIN pg_attribute a ON "
6414 " attrelid = c.oid AND attnum > 0 "
6415 " AND NOT attisdropped "
6416 " LEFT JOIN pg_attrdef ad ON "
6417 " adrelid = c.oid AND adnum = attnum ");
6418
6419 if (import_collate)
6421 " LEFT JOIN pg_collation coll ON "
6422 " coll.oid = attcollation "
6423 " LEFT JOIN pg_namespace collnsp ON "
6424 " collnsp.oid = collnamespace ");
6425
6427 "WHERE c.relkind IN ("
6433 " AND n.nspname = ");
6434 deparseStringLiteral(&buf, stmt->remote_schema);
6435
6436 /* Partitions are supported since Postgres 10 */
6437 if (PQserverVersion(conn) >= 100000 &&
6438 stmt->list_type != FDW_IMPORT_SCHEMA_LIMIT_TO)
6439 appendStringInfoString(&buf, " AND NOT c.relispartition ");
6440
6441 /* Apply restrictions for LIMIT TO and EXCEPT */
6442 if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
6443 stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
6444 {
6445 bool first_item = true;
6446
6447 appendStringInfoString(&buf, " AND c.relname ");
6448 if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
6449 appendStringInfoString(&buf, "NOT ");
6450 appendStringInfoString(&buf, "IN (");
6451
6452 /* Append list of table names within IN clause */
6453 foreach(lc, stmt->table_list)
6454 {
6455 RangeVar *rv = (RangeVar *) lfirst(lc);
6456
6457 if (first_item)
6458 first_item = false;
6459 else
6462 }
6464 }
6465
6466 /* Append ORDER BY at the end of query to ensure output ordering */
6467 appendStringInfoString(&buf, " ORDER BY c.relname, a.attnum");
6468
6469 /* Fetch the data */
6470 res = pgfdw_exec_query(conn, buf.data, NULL);
6471 if (PQresultStatus(res) != PGRES_TUPLES_OK)
6472 pgfdw_report_error(res, conn, buf.data);
6473
6474 /* Process results */
6475 numrows = PQntuples(res);
6476 /* note: incrementation of i happens in inner loop's while() test */
6477 for (i = 0; i < numrows;)
6478 {
6479 char *tablename = PQgetvalue(res, i, 0);
6480 bool first_item = true;
6481
6483 appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
6484 quote_identifier(tablename));
6485
6486 /* Scan all rows for this table */
6487 do
6488 {
6489 char *attname;
6490 char *typename;
6491 char *attnotnull;
6492 char *attgenerated;
6493 char *attdefault;
6494 char *collname;
6495 char *collnamespace;
6496
6497 /* If table has no columns, we'll see nulls here */
6498 if (PQgetisnull(res, i, 1))
6499 continue;
6500
6501 attname = PQgetvalue(res, i, 1);
6502 typename = PQgetvalue(res, i, 2);
6503 attnotnull = PQgetvalue(res, i, 3);
6504 attdefault = PQgetisnull(res, i, 4) ? NULL :
6505 PQgetvalue(res, i, 4);
6506 attgenerated = PQgetisnull(res, i, 5) ? NULL :
6507 PQgetvalue(res, i, 5);
6508 collname = PQgetisnull(res, i, 6) ? NULL :
6509 PQgetvalue(res, i, 6);
6510 collnamespace = PQgetisnull(res, i, 7) ? NULL :
6511 PQgetvalue(res, i, 7);
6512
6513 if (first_item)
6514 first_item = false;
6515 else
6516 appendStringInfoString(&buf, ",\n");
6517
6518 /* Print column name and type */
6519 appendStringInfo(&buf, " %s %s",
6521 typename);
6522
6523 /*
6524 * Add column_name option so that renaming the foreign table's
6525 * column doesn't break the association to the underlying column.
6526 */
6527 appendStringInfoString(&buf, " OPTIONS (column_name ");
6530
6531 /* Add COLLATE if needed */
6532 if (import_collate && collname != NULL && collnamespace != NULL)
6533 appendStringInfo(&buf, " COLLATE %s.%s",
6535 quote_identifier(collname));
6536
6537 /* Add DEFAULT if needed */
6538 if (import_default && attdefault != NULL &&
6539 (!attgenerated || !attgenerated[0]))
6540 appendStringInfo(&buf, " DEFAULT %s", attdefault);
6541
6542 /* Add GENERATED if needed */
6543 if (import_generated && attgenerated != NULL &&
6544 attgenerated[0] == ATTRIBUTE_GENERATED_STORED)
6545 {
6548 " GENERATED ALWAYS AS (%s) STORED",
6549 attdefault);
6550 }
6551
6552 /* Add NOT NULL if needed */
6553 if (import_not_null && attnotnull[0] == 't')
6554 appendStringInfoString(&buf, " NOT NULL");
6555 }
6556 while (++i < numrows &&
6557 strcmp(PQgetvalue(res, i, 0), tablename) == 0);
6558
6559 /*
6560 * Add server name and table-level options. We specify remote schema
6561 * and table name as options (the latter to ensure that renaming the
6562 * foreign table doesn't break the association).
6563 */
6564 appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
6565 quote_identifier(server->servername));
6566
6567 appendStringInfoString(&buf, "schema_name ");
6568 deparseStringLiteral(&buf, stmt->remote_schema);
6569 appendStringInfoString(&buf, ", table_name ");
6570 deparseStringLiteral(&buf, tablename);
6571
6573
6574 commands = lappend(commands, pstrdup(buf.data));
6575 }
6576 PQclear(res);
6577
6579
6580 return commands;
6581}
#define CppAsString2(x)
Definition c.h:506
#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 5590 of file postgres_fdw.c.

5591{
5592 const char *schemaname = NULL;
5593 const char *relname = NULL;
5595 ForeignServer *server;
5596 RemoteStatsResults remstats = {.rel = NULL,.att = NULL};
5598 int attrcnt = 0;
5599 bool restore_stats = false;
5600 bool ok = false;
5601 ListCell *lc;
5602
5603 schemaname = get_namespace_name(RelationGetNamespace(relation));
5604 relname = RelationGetRelationName(relation);
5606 server = GetForeignServer(table->serverid);
5607
5608 /*
5609 * Check whether the restore_stats option is enabled on the foreign table.
5610 * If not, silently ignore the foreign table.
5611 *
5612 * Server-level options can be overridden by table-level options, so check
5613 * server-level first.
5614 */
5615 foreach(lc, server->options)
5616 {
5617 DefElem *def = (DefElem *) lfirst(lc);
5618
5619 if (strcmp(def->defname, "restore_stats") == 0)
5620 {
5622 break;
5623 }
5624 }
5625 foreach(lc, table->options)
5626 {
5627 DefElem *def = (DefElem *) lfirst(lc);
5628
5629 if (strcmp(def->defname, "restore_stats") == 0)
5630 {
5632 break;
5633 }
5634 }
5635 if (!restore_stats)
5636 return false;
5637
5638 /*
5639 * We don't currently support statistics import for foreign tables with
5640 * extended statistics objects.
5641 */
5642 if (HasRelationExtStatistics(relation))
5643 {
5646 errmsg("cannot import statistics for foreign table \"%s.%s\" --- this foreign table has extended statistics objects",
5647 schemaname, relname));
5648 return false;
5649 }
5650
5651 /*
5652 * OK, let's do it.
5653 */
5654 ereport(elevel,
5655 (errmsg("importing statistics for foreign table \"%s.%s\"",
5656 schemaname, relname)));
5657
5658 ok = fetch_remote_statistics(relation, va_cols,
5659 table, schemaname, relname,
5661
5662 if (ok)
5663 ok = import_fetched_statistics(schemaname, relname,
5665
5666 if (ok)
5667 ereport(elevel,
5668 (errmsg("finished importing statistics for foreign table \"%s.%s\"",
5669 schemaname, relname)));
5670
5671 PQclear(remstats.rel);
5672 PQclear(remstats.att);
5673 if (remattrmap)
5675
5676 return ok;
5677}
bool HasRelationExtStatistics(Relation onerel)
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3588
static bool import_fetched_statistics(const char *schemaname, const char *relname, int attrcnt, const RemoteAttributeMapping *remattrmap, RemoteStatsResults *remstats)
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(), get_namespace_name(), GetForeignServer(), GetForeignTable(), HasRelationExtStatistics(), import_fetched_statistics(), lfirst, ForeignServer::options, pfree(), 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 8117 of file postgres_fdw.c.

8118{
8119 RelOptInfo *rel = ((Path *) path)->parent;
8120 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
8121
8122 return fpinfo->async_capable;
8123}

References fb().

Referenced by postgres_fdw_handler().

◆ postgresIsForeignRelUpdatable()

static int postgresIsForeignRelUpdatable ( Relation  rel)
static

Definition at line 2527 of file postgres_fdw.c.

2528{
2529 bool updatable;
2531 ForeignServer *server;
2532 ListCell *lc;
2533
2534 /*
2535 * By default, all postgres_fdw foreign tables are assumed updatable. This
2536 * can be overridden by a per-server setting, which in turn can be
2537 * overridden by a per-table setting.
2538 */
2539 updatable = true;
2540
2542 server = GetForeignServer(table->serverid);
2543
2544 foreach(lc, server->options)
2545 {
2546 DefElem *def = (DefElem *) lfirst(lc);
2547
2548 if (strcmp(def->defname, "updatable") == 0)
2549 updatable = defGetBoolean(def);
2550 }
2551 foreach(lc, table->options)
2552 {
2553 DefElem *def = (DefElem *) lfirst(lc);
2554
2555 if (strcmp(def->defname, "updatable") == 0)
2556 updatable = defGetBoolean(def);
2557 }
2558
2559 /*
2560 * Currently "updatable" means support for INSERT, UPDATE and DELETE.
2561 */
2562 return updatable ?
2563 (1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE) : 0;
2564}

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

2979{
2981 EState *estate = node->ss.ps.state;
2982 ResultRelInfo *resultRelInfo = node->resultRelInfo;
2983
2984 /*
2985 * If this is the first call after Begin, execute the statement.
2986 */
2987 if (dmstate->num_tuples == -1)
2988 execute_dml_stmt(node);
2989
2990 /*
2991 * If the local query doesn't specify RETURNING, just clear tuple slot.
2992 */
2993 if (!resultRelInfo->ri_projectReturning)
2994 {
2995 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
2996 NodeInstrumentation *instr = node->ss.ps.instrument;
2997
2998 Assert(!dmstate->has_returning);
2999
3000 /* Increment the command es_processed count if necessary. */
3001 if (dmstate->set_processed)
3002 estate->es_processed += dmstate->num_tuples;
3003
3004 /* Increment the tuple count for EXPLAIN ANALYZE if necessary. */
3005 if (instr)
3006 instr->tuplecount += dmstate->num_tuples;
3007
3008 return ExecClearTuple(slot);
3009 }
3010
3011 /*
3012 * Get the next RETURNING tuple.
3013 */
3014 return get_returning_data(node);
3015}
static void execute_dml_stmt(ForeignScanState *node)
static TupleTableSlot * get_returning_data(ForeignScanState *node)
NodeInstrumentation * instrument
Definition execnodes.h:1211

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

1820{
1821 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1822 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
1823
1824 /*
1825 * In sync mode, if this is the first call after Begin or ReScan, we need
1826 * to create the cursor on the remote side. In async mode, we would have
1827 * already created the cursor before we get here, even if this is the
1828 * first call after Begin or ReScan.
1829 */
1830 if (!fsstate->cursor_exists)
1831 create_cursor(node);
1832
1833 /*
1834 * Get some more tuples, if we've run out.
1835 */
1836 if (fsstate->next_tuple >= fsstate->num_tuples)
1837 {
1838 /* In async mode, just clear tuple slot. */
1839 if (fsstate->async_capable)
1840 return ExecClearTuple(slot);
1841 /* No point in another fetch if we already detected EOF, though. */
1842 if (!fsstate->eof_reached)
1843 fetch_more_data(node);
1844 /* If we didn't get any tuples, must be end of data. */
1845 if (fsstate->next_tuple >= fsstate->num_tuples)
1846 return ExecClearTuple(slot);
1847 }
1848
1849 /*
1850 * Return the next tuple.
1851 */
1852 ExecStoreHeapTuple(fsstate->tuples[fsstate->next_tuple++],
1853 slot,
1854 false);
1855
1856 return slot;
1857}

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

2667{
2668 CmdType operation = plan->operation;
2669 RelOptInfo *foreignrel;
2672 Relation rel;
2673 StringInfoData sql;
2675 List *processed_tlist = NIL;
2676 List *targetAttrs = NIL;
2678 List *params_list = NIL;
2679 List *returningList = NIL;
2680 List *retrieved_attrs = NIL;
2681
2682 /*
2683 * Decide whether it is safe to modify a foreign table directly.
2684 */
2685
2686 /*
2687 * The table modification must be an UPDATE or DELETE.
2688 */
2690 return false;
2691
2692 /*
2693 * Try to locate the ForeignScan subplan that's scanning resultRelation.
2694 */
2696 if (!fscan)
2697 return false;
2698
2699 /*
2700 * It's unsafe to modify a foreign table directly if there are any quals
2701 * that should be evaluated locally.
2702 */
2703 if (fscan->scan.plan.qual != NIL)
2704 return false;
2705
2706 /* Safe to fetch data about the target foreign rel */
2707 if (fscan->scan.scanrelid == 0)
2708 {
2709 foreignrel = find_join_rel(root, fscan->fs_relids);
2710 /* We should have a rel for this foreign join. */
2711 Assert(foreignrel);
2712 }
2713 else
2714 foreignrel = root->simple_rel_array[resultRelation];
2715 rte = root->simple_rte_array[resultRelation];
2716 fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2717
2718 /*
2719 * It's unsafe to update a foreign table directly, if any expressions to
2720 * assign to the target columns are unsafe to evaluate remotely.
2721 */
2722 if (operation == CMD_UPDATE)
2723 {
2724 ListCell *lc,
2725 *lc2;
2726
2727 /*
2728 * The expressions of concern are the first N columns of the processed
2729 * targetlist, where N is the length of the rel's update_colnos.
2730 */
2731 get_translated_update_targetlist(root, resultRelation,
2732 &processed_tlist, &targetAttrs);
2733 forboth(lc, processed_tlist, lc2, targetAttrs)
2734 {
2736 AttrNumber attno = lfirst_int(lc2);
2737
2738 /* update's new-value expressions shouldn't be resjunk */
2739 Assert(!tle->resjunk);
2740
2741 if (attno <= InvalidAttrNumber) /* shouldn't happen */
2742 elog(ERROR, "system-column update is not supported");
2743
2744 if (!is_foreign_expr(root, foreignrel, (Expr *) tle->expr))
2745 return false;
2746 }
2747 }
2748
2749 /*
2750 * Ok, rewrite subplan so as to modify the foreign table directly.
2751 */
2752 initStringInfo(&sql);
2753
2754 /*
2755 * Core code already has some lock on each rel being planned, so we can
2756 * use NoLock here.
2757 */
2758 rel = table_open(rte->relid, NoLock);
2759
2760 /*
2761 * Recall the qual clauses that must be evaluated remotely. (These are
2762 * bare clauses not RestrictInfos, but deparse.c's appendConditions()
2763 * doesn't care.)
2764 */
2765 remote_exprs = fpinfo->final_remote_exprs;
2766
2767 /*
2768 * Extract the relevant RETURNING list if any.
2769 */
2770 if (plan->returningLists)
2771 {
2772 returningList = (List *) list_nth(plan->returningLists, subplan_index);
2773
2774 /*
2775 * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2776 * we fetch from the foreign server any Vars specified in RETURNING
2777 * that refer not only to the target relation but to non-target
2778 * relations. So we'll deparse them into the RETURNING clause of the
2779 * remote query; use a targetlist consisting of them instead, which
2780 * will be adjusted to be new fdw_scan_tlist of the foreign-scan plan
2781 * node below.
2782 */
2783 if (fscan->scan.scanrelid == 0)
2784 returningList = build_remote_returning(resultRelation, rel,
2785 returningList);
2786 }
2787
2788 /*
2789 * Construct the SQL command string.
2790 */
2791 switch (operation)
2792 {
2793 case CMD_UPDATE:
2794 deparseDirectUpdateSql(&sql, root, resultRelation, rel,
2795 foreignrel,
2796 processed_tlist,
2798 remote_exprs, &params_list,
2799 returningList, &retrieved_attrs);
2800 break;
2801 case CMD_DELETE:
2802 deparseDirectDeleteSql(&sql, root, resultRelation, rel,
2803 foreignrel,
2804 remote_exprs, &params_list,
2805 returningList, &retrieved_attrs);
2806 break;
2807 default:
2808 elog(ERROR, "unexpected operation: %d", (int) operation);
2809 break;
2810 }
2811
2812 /*
2813 * Update the operation and target relation info.
2814 */
2815 fscan->operation = operation;
2816 fscan->resultRelation = resultRelation;
2817
2818 /*
2819 * Update the fdw_exprs list that will be available to the executor.
2820 */
2821 fscan->fdw_exprs = params_list;
2822
2823 /*
2824 * Update the fdw_private list that will be available to the executor.
2825 * Items in the list must match enum FdwDirectModifyPrivateIndex, above.
2826 */
2827 fscan->fdw_private = list_make4(makeString(sql.data),
2828 makeBoolean((retrieved_attrs != NIL)),
2829 retrieved_attrs,
2830 makeBoolean(plan->canSetTag));
2831
2832 /*
2833 * Update the foreign-join-related fields.
2834 */
2835 if (fscan->scan.scanrelid == 0)
2836 {
2837 /* No need for the outer subplan. */
2838 fscan->scan.plan.lefttree = NULL;
2839
2840 /* Build new fdw_scan_tlist if UPDATE/DELETE .. RETURNING. */
2841 if (returningList)
2842 rebuild_fdw_scan_tlist(fscan, returningList);
2843 }
2844
2845 /*
2846 * Finally, unset the async-capable flag if it is set, as we currently
2847 * don't support asynchronous execution of direct modifications.
2848 */
2849 if (fscan->scan.plan.async_capable)
2850 fscan->scan.plan.async_capable = false;
2851
2852 table_close(rel, NoLock);
2853 return true;
2854}
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:2422
void deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, RelOptInfo *foreignrel, List *targetlist, List *targetAttrs, List *remote_conds, List **params_list, List *returningList, List **retrieved_attrs)
Definition deparse.c:2307
#define NoLock
Definition lockdefs.h:34
CmdType
Definition nodes.h:273
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h: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 1991 of file postgres_fdw.c.

1995{
1996 CmdType operation = plan->operation;
1997 RangeTblEntry *rte = planner_rt_fetch(resultRelation, root);
1998 Relation rel;
1999 StringInfoData sql;
2000 List *targetAttrs = NIL;
2002 List *returningList = NIL;
2003 List *retrieved_attrs = NIL;
2004 bool doNothing = false;
2005 int values_end_len = -1;
2006
2007 initStringInfo(&sql);
2008
2009 /*
2010 * Core code already has some lock on each rel being planned, so we can
2011 * use NoLock here.
2012 */
2013 rel = table_open(rte->relid, NoLock);
2014
2015 /*
2016 * In an INSERT, we transmit all columns that are defined in the foreign
2017 * table. In an UPDATE, if there are BEFORE ROW UPDATE triggers on the
2018 * foreign table, we transmit all columns like INSERT; else we transmit
2019 * only columns that were explicitly targets of the UPDATE, so as to avoid
2020 * unnecessary data transmission. (We can't do that for INSERT since we
2021 * would miss sending default values for columns not listed in the source
2022 * statement, and for UPDATE if there are BEFORE ROW UPDATE triggers since
2023 * those triggers might change values for non-target columns, in which
2024 * case we would miss sending changed values for those columns.)
2025 */
2026 if (operation == CMD_INSERT ||
2027 (operation == CMD_UPDATE &&
2028 rel->trigdesc &&
2030 {
2031 TupleDesc tupdesc = RelationGetDescr(rel);
2032 int attnum;
2033
2034 for (attnum = 1; attnum <= tupdesc->natts; attnum++)
2035 {
2036 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
2037
2038 if (!attr->attisdropped)
2040 }
2041 }
2042 else if (operation == CMD_UPDATE)
2043 {
2044 int col;
2045 RelOptInfo *rel = find_base_rel(root, resultRelation);
2047
2048 col = -1;
2049 while ((col = bms_next_member(allUpdatedCols, col)) >= 0)
2050 {
2051 /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
2053
2054 if (attno <= InvalidAttrNumber) /* shouldn't happen */
2055 elog(ERROR, "system-column update is not supported");
2057 }
2058 }
2059
2060 /*
2061 * Extract the relevant WITH CHECK OPTION list if any.
2062 */
2063 if (plan->withCheckOptionLists)
2064 withCheckOptionList = (List *) list_nth(plan->withCheckOptionLists,
2066
2067 /*
2068 * Extract the relevant RETURNING list if any.
2069 */
2070 if (plan->returningLists)
2071 returningList = (List *) list_nth(plan->returningLists, subplan_index);
2072
2073 /*
2074 * ON CONFLICT DO NOTHING/SELECT/UPDATE with inference specification
2075 * should have already been rejected in the optimizer, as presently there
2076 * is no way to recognize an arbiter index on a foreign table. Only DO
2077 * NOTHING is supported without an inference specification.
2078 */
2079 if (plan->onConflictAction == ONCONFLICT_NOTHING)
2080 doNothing = true;
2081 else if (plan->onConflictAction != ONCONFLICT_NONE)
2082 elog(ERROR, "unexpected ON CONFLICT specification: %d",
2083 (int) plan->onConflictAction);
2084
2085 /*
2086 * Construct the SQL command string.
2087 */
2088 switch (operation)
2089 {
2090 case CMD_INSERT:
2091 deparseInsertSql(&sql, rte, resultRelation, rel,
2093 withCheckOptionList, returningList,
2094 &retrieved_attrs, &values_end_len);
2095 break;
2096 case CMD_UPDATE:
2097 deparseUpdateSql(&sql, rte, resultRelation, rel,
2099 withCheckOptionList, returningList,
2100 &retrieved_attrs);
2101 break;
2102 case CMD_DELETE:
2103 deparseDeleteSql(&sql, rte, resultRelation, rel,
2104 returningList,
2105 &retrieved_attrs);
2106 break;
2107 default:
2108 elog(ERROR, "unexpected operation: %d", (int) operation);
2109 break;
2110 }
2111
2112 table_close(rel, NoLock);
2113
2114 /*
2115 * Build the fdw_private list that will be available to the executor.
2116 * Items in the list must match enum FdwModifyPrivateIndex, above.
2117 */
2118 return list_make5(makeString(sql.data),
2121 makeBoolean((retrieved_attrs != NIL)),
2122 retrieved_attrs);
2123}
void deparseUpdateSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, List *withCheckOptionList, List *returningList, List **retrieved_attrs)
Definition deparse.c:2247
void deparseDeleteSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *returningList, List **retrieved_attrs)
Definition deparse.c:2393
Bitmapset * get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel)
Definition inherit.c: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 2571 of file postgres_fdw.c.

2572{
2573 Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
2576
2577 /* For base foreign relations, it suffices to set fdw_recheck_quals */
2578 if (scanrelid > 0)
2579 return true;
2580
2581 Assert(outerPlan != NULL);
2582
2583 /* Execute a local join execution plan */
2585 if (TupIsNull(result))
2586 return false;
2587
2588 /* Store result in the given slot */
2589 ExecCopySlot(slot, result);
2590
2591 return true;
2592}
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:322
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:543

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

1865{
1866 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1867 char sql[64];
1868 PGresult *res;
1869
1870 /* If we haven't created the cursor yet, nothing to do. */
1871 if (!fsstate->cursor_exists)
1872 return;
1873
1874 /*
1875 * If the node is async-capable, and an asynchronous fetch for it has
1876 * begun, the asynchronous fetch might not have yet completed. Check if
1877 * the node is async-capable, and an asynchronous fetch for it is still in
1878 * progress; if so, complete the asynchronous fetch before restarting the
1879 * scan.
1880 */
1881 if (fsstate->async_capable &&
1882 fsstate->conn_state->pendingAreq &&
1883 fsstate->conn_state->pendingAreq->requestee == (PlanState *) node)
1884 fetch_more_data(node);
1885
1886 /*
1887 * If any internal parameters affecting this node have changed, we'd
1888 * better destroy and recreate the cursor. Otherwise, if the remote
1889 * server is v14 or older, rewinding it should be good enough; if not,
1890 * rewind is only allowed for scrollable cursors, but we don't have a way
1891 * to check the scrollability of it, so destroy and recreate it in any
1892 * case. If we've only fetched zero or one batch, we needn't even rewind
1893 * the cursor, just rescan what we have.
1894 */
1895 if (node->ss.ps.chgParam != NULL)
1896 {
1897 fsstate->cursor_exists = false;
1898 snprintf(sql, sizeof(sql), "CLOSE c%u",
1899 fsstate->cursor_number);
1900 }
1901 else if (fsstate->fetch_ct_2 > 1)
1902 {
1903 if (PQserverVersion(fsstate->conn) < 150000)
1904 snprintf(sql, sizeof(sql), "MOVE BACKWARD ALL IN c%u",
1905 fsstate->cursor_number);
1906 else
1907 {
1908 fsstate->cursor_exists = false;
1909 snprintf(sql, sizeof(sql), "CLOSE c%u",
1910 fsstate->cursor_number);
1911 }
1912 }
1913 else
1914 {
1915 /* Easy: just rescan what we already have in memory, if anything */
1916 fsstate->next_tuple = 0;
1917 return;
1918 }
1919
1920 res = pgfdw_exec_query(fsstate->conn, sql, fsstate->conn_state);
1921 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1922 pgfdw_report_error(res, fsstate->conn, sql);
1923 PQclear(res);
1924
1925 /* Now force a fresh FETCH. */
1926 fsstate->tuples = NULL;
1927 fsstate->num_tuples = 0;
1928 fsstate->next_tuple = 0;
1929 fsstate->fetch_ct_2 = 0;
1930 fsstate->eof_reached = false;
1931}
Bitmapset * chgParam
Definition execnodes.h:1235

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

4410{
4411 char prep_name[NAMEDATALEN];
4412 char *p_name;
4413 PGresult *res;
4414
4415 /*
4416 * The caller would already have processed a pending asynchronous request
4417 * if any, so no need to do it here.
4418 */
4419
4420 /* Construct name we'll use for the prepared statement. */
4421 snprintf(prep_name, sizeof(prep_name), "pgsql_fdw_prep_%u",
4422 GetPrepStmtNumber(fmstate->conn));
4423 p_name = pstrdup(prep_name);
4424
4425 /*
4426 * We intentionally do not specify parameter types here, but leave the
4427 * remote server to derive them by default. This avoids possible problems
4428 * with the remote server using different type OIDs than we do. All of
4429 * the prepared statements we use in this module are simple enough that
4430 * the remote server will make the right choices.
4431 */
4432 if (!PQsendPrepare(fmstate->conn,
4433 p_name,
4434 fmstate->query,
4435 0,
4436 NULL))
4437 pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
4438
4439 /*
4440 * Get the result, and check for success.
4441 */
4442 res = pgfdw_get_result(fmstate->conn);
4443 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4444 pgfdw_report_error(res, fmstate->conn, fmstate->query);
4445 PQclear(res);
4446
4447 /* This action shows that the prepare has been done. */
4448 fmstate->p_name = p_name;
4449}
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

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

5025{
5026 int i;
5027 ListCell *lc;
5028
5029 Assert(numParams > 0);
5030
5031 /* Prepare for output conversion of parameters used in remote query. */
5032 *param_flinfo = palloc0_array(FmgrInfo, numParams);
5033
5034 i = 0;
5035 foreach(lc, fdw_exprs)
5036 {
5037 Node *param_expr = (Node *) lfirst(lc);
5038 Oid typefnoid;
5039 bool isvarlena;
5040
5042 fmgr_info(typefnoid, &(*param_flinfo)[i]);
5043 i++;
5044 }
5045
5046 /*
5047 * Prepare remote-parameter expressions for evaluation. (Note: in
5048 * practice, we expect that all these expressions will be just Params, so
5049 * we could possibly do something more efficient than using the full
5050 * expression-eval machinery for this. But probably there would be little
5051 * benefit, and it'd require postgres_fdw to know more than is desirable
5052 * about Param evaluation.)
5053 */
5054 *param_exprs = ExecInitExprList(fdw_exprs, node);
5055
5056 /* Allocate buffer for text form of query parameters. */
5057 *param_values = (const char **) palloc0(numParams * sizeof(char *));
5058}
List * ExecInitExprList(List *nodes, PlanState *parent)
Definition execExpr.c:356
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 8353 of file postgres_fdw.c.

8354{
8355 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8356 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8357
8358 /* The request would have been pending for a callback */
8359 Assert(areq->callback_pending);
8360
8361 /* The request should be currently in-process */
8362 Assert(fsstate->conn_state->pendingAreq == areq);
8363
8364 fetch_more_data(node);
8365
8366 /*
8367 * If we didn't get any tuples, must be end of data; complete the request
8368 * now. Otherwise, we postpone completing the request until we are called
8369 * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
8370 */
8371 if (fsstate->next_tuple >= fsstate->num_tuples)
8372 {
8373 /* Unlike AsyncNotify, we unset callback_pending ourselves */
8374 areq->callback_pending = false;
8375 /* Mark the request as complete */
8377 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
8379 }
8380}
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 5064 of file postgres_fdw.c.

5068{
5069 int nestlevel;
5070 int i;
5071 ListCell *lc;
5072
5074
5075 i = 0;
5076 foreach(lc, param_exprs)
5077 {
5080 bool isNull;
5081
5082 /* Evaluate the parameter expression */
5083 expr_value = ExecEvalExpr(expr_state, econtext, &isNull);
5084
5085 /*
5086 * Get string representation of each parameter value by invoking
5087 * type-specific output function, unless the value is null.
5088 */
5089 if (isNull)
5090 param_values[i] = NULL;
5091 else
5092 param_values[i] = OutputFunctionCall(&param_flinfo[i], expr_value);
5093
5094 i++;
5095 }
5096
5098}
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:403

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

8256{
8257 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8258 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8259 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
8261
8262 /* This should not be called if the request is currently in-process */
8263 Assert(areq != pendingAreq);
8264
8265 /* Fetch some more tuples, if we've run out */
8266 if (fsstate->next_tuple >= fsstate->num_tuples)
8267 {
8268 /* No point in another fetch if we already detected EOF, though */
8269 if (!fsstate->eof_reached)
8270 {
8271 /* Mark the request as pending for a callback */
8273 /* Begin another fetch if requested and if no pending request */
8274 if (fetch && !pendingAreq)
8276 }
8277 else
8278 {
8279 /* There's nothing more to do; just return a NULL pointer */
8280 result = NULL;
8281 /* Mark the request as complete */
8283 }
8284 return;
8285 }
8286
8287 /* Get a tuple from the ForeignScan node */
8288 result = areq->requestee->ExecProcNodeReal(areq->requestee);
8289 if (!TupIsNull(result))
8290 {
8291 /* Mark the request as complete */
8293 return;
8294 }
8295
8296 /* We must have run out of tuples */
8297 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8298
8299 /* Fetch some more tuples, if we've not detected EOF yet */
8300 if (!fsstate->eof_reached)
8301 {
8302 /* Mark the request as pending for a callback */
8304 /* Begin another fetch if requested and if no pending request */
8305 if (fetch && !pendingAreq)
8307 }
8308 else
8309 {
8310 /* There's nothing more to do; just return a NULL pointer */
8311 result = NULL;
8312 /* Mark the request as complete */
8314 }
8315}
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 4704 of file postgres_fdw.c.

4705{
4706 List *new_tlist = tlist;
4707 List *old_tlist = fscan->fdw_scan_tlist;
4708 ListCell *lc;
4709
4710 foreach(lc, old_tlist)
4711 {
4713
4714 if (tlist_member(tle->expr, new_tlist))
4715 continue; /* already got it */
4716
4718 makeTargetEntry(tle->expr,
4720 NULL,
4721 false));
4722 }
4723 fscan->fdw_scan_tlist = new_tlist;
4724}

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

6003{
6004 const RemoteAttributeMapping *r1 = v1;
6005 const RemoteAttributeMapping *r2 = v2;
6006
6007 return strncmp(r1->remote_attname, r2->remote_attname, NAMEDATALEN);
6008}

References fb(), and NAMEDATALEN.

Referenced by build_remattrmap().

◆ reset_transmission_modes()

void reset_transmission_modes ( int  nestlevel)

Definition at line 4144 of file postgres_fdw.c.

4145{
4146 AtEOXact_GUC(true, nestlevel);
4147}
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 6589 of file postgres_fdw.c.

6590{
6591 List *vars;
6592 ListCell *lc;
6593 bool ok = true;
6594
6595 Assert(joinrel->reltarget);
6596
6598
6599 foreach(lc, vars)
6600 {
6601 Var *var = (Var *) lfirst(lc);
6602
6603 if (!IsA(var, Var))
6604 continue;
6605
6606 if (bms_is_member(var->varno, innerrel->relids))
6607 {
6608 /*
6609 * The planner can create semi-join, which refers to inner rel
6610 * vars in its target list. However, we deparse semi-join as an
6611 * exists() subquery, so can't handle references to inner rel in
6612 * the target list.
6613 */
6614 Assert(!bms_is_member(var->varno, outerrel->relids));
6615 ok = false;
6616 break;
6617 }
6618 }
6619 return ok;
6620}

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_transmission_modes()

int set_transmission_modes ( void  )

Definition at line 4108 of file postgres_fdw.c.

4109{
4110 int nestlevel = NewGUCNestLevel();
4111
4112 /*
4113 * The values set here should match what pg_dump does. See also
4114 * configure_remote_session in connection.c.
4115 */
4116 if (DateStyle != USE_ISO_DATES)
4117 (void) set_config_option("datestyle", "ISO",
4119 GUC_ACTION_SAVE, true, 0, false);
4121 (void) set_config_option("intervalstyle", "postgres",
4123 GUC_ACTION_SAVE, true, 0, false);
4124 if (extra_float_digits < 3)
4125 (void) set_config_option("extra_float_digits", "3",
4127 GUC_ACTION_SAVE, true, 0, false);
4128
4129 /*
4130 * In addition force restrictive search_path, in case there are any
4131 * regproc or similar constants to be printed.
4132 */
4133 (void) set_config_option("search_path", "pg_catalog",
4135 GUC_ACTION_SAVE, true, 0, false);
4136
4137 return nestlevel;
4138}
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().

◆ store_returning_result()

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

Definition at line 4537 of file postgres_fdw.c.

4539{
4541
4543 fmstate->rel,
4544 fmstate->attinmeta,
4545 fmstate->retrieved_attrs,
4546 NULL,
4547 fmstate->temp_cxt);
4548
4549 /*
4550 * The returning slot will not necessarily be suitable to store heaptuples
4551 * directly, so allow for conversion.
4552 */
4553 ExecForceStoreHeapTuple(newtup, slot, true);
4554}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)

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

Referenced by execute_foreign_modify().

Variable Documentation

◆ attclear_argtypes

const Oid attclear_argtypes[ATTCLEAR_SQL_NUM_FIELDS]
static
Initial value:

Definition at line 495 of file postgres_fdw.c.

496{
498};

Referenced by import_fetched_statistics().

◆ attclear_sql

const char* attclear_sql
static
Initial value:
=
"SELECT pg_catalog.pg_clear_attribute_stats($1, $2, $3, false)"

Definition at line 482 of file postgres_fdw.c.

Referenced by import_fetched_statistics().

◆ attimport_argtypes

◆ attimport_sql

const char* attimport_sql
static
Initial value:
=
"SELECT pg_catalog.pg_restore_attribute_stats(\n"
"\t'version', $1,\n"
"\t'schemaname', $2,\n"
"\t'relname', $3,\n"
"\t'attnum', $4,\n"
"\t'inherited', false::boolean,\n"
"\t'null_frac', $5::real,\n"
"\t'avg_width', $6::integer,\n"
"\t'n_distinct', $7::real,\n"
"\t'most_common_vals', $8,\n"
"\t'most_common_freqs', $9::real[],\n"
"\t'histogram_bounds', $10,\n"
"\t'correlation', $11::real,\n"
"\t'most_common_elems', $12,\n"
"\t'most_common_elem_freqs', $13::real[],\n"
"\t'elem_count_histogram', $14::real[],\n"
"\t'range_length_histogram', $15,\n"
"\t'range_empty_frac', $16::real,\n"
"\t'range_bounds_histogram', $17)"

Definition at line 398 of file postgres_fdw.c.

Referenced by import_fetched_statistics().

◆ attr_result_arg_map

const AttrResultArgMap attr_result_arg_map[NUM_MAPPED_ATTIMPORT_ARGS]
static
Initial value:

Definition at line 464 of file postgres_fdw.c.

Referenced by import_fetched_statistics().

◆ relimport_argtypes

const Oid relimport_argtypes[RELIMPORT_SQL_NUM_FIELDS]
static
Initial value:

Definition at line 391 of file postgres_fdw.c.

392{
394 TEXTOID,
395};

Referenced by import_fetched_statistics().

◆ relimport_sql

const char* relimport_sql
static
Initial value:
=
"SELECT pg_catalog.pg_restore_relation_stats(\n"
"\t'version', $1,\n"
"\t'schemaname', $2,\n"
"\t'relname', $3,\n"
"\t'relpages', $4::integer,\n"
"\t'reltuples', $5::real)"

Definition at line 371 of file postgres_fdw.c.

Referenced by import_fetched_statistics().