PostgreSQL Source Code git master
Loading...
Searching...
No Matches
postgres_fdw.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * postgres_fdw.c
4 * Foreign-data wrapper for remote PostgreSQL servers
5 *
6 * Portions Copyright (c) 2012-2026, PostgreSQL Global Development Group
7 *
8 * IDENTIFICATION
9 * contrib/postgres_fdw/postgres_fdw.c
10 *
11 *-------------------------------------------------------------------------
12 */
13#include "postgres.h"
14
15#include <limits.h>
16
17#include "access/htup_details.h"
18#include "access/sysattr.h"
19#include "access/table.h"
20#include "catalog/pg_opfamily.h"
21#include "commands/defrem.h"
24#include "commands/vacuum.h"
25#include "executor/execAsync.h"
26#include "executor/instrument.h"
27#include "foreign/fdwapi.h"
28#include "funcapi.h"
29#include "miscadmin.h"
30#include "nodes/makefuncs.h"
31#include "nodes/nodeFuncs.h"
33#include "optimizer/cost.h"
34#include "optimizer/inherit.h"
35#include "optimizer/optimizer.h"
36#include "optimizer/pathnode.h"
37#include "optimizer/paths.h"
38#include "optimizer/planmain.h"
39#include "optimizer/prep.h"
41#include "optimizer/tlist.h"
42#include "parser/parsetree.h"
43#include "pgstat.h"
44#include "postgres_fdw.h"
46#include "storage/latch.h"
47#include "utils/builtins.h"
48#include "utils/float.h"
49#include "utils/fmgroids.h"
50#include "utils/guc.h"
51#include "utils/lsyscache.h"
52#include "utils/memutils.h"
53#include "utils/rel.h"
54#include "utils/sampling.h"
55#include "utils/selfuncs.h"
56#include "utils/timestamp.h"
57
59 .name = "postgres_fdw",
60 .version = PG_VERSION
61);
62
63/* Default CPU cost to start up a foreign query. */
64#define DEFAULT_FDW_STARTUP_COST 100.0
65
66/* Default CPU cost to process 1 row (above and beyond cpu_tuple_cost). */
67#define DEFAULT_FDW_TUPLE_COST 0.2
68
69/* If no remote estimates, assume a sort costs 20% extra */
70#define DEFAULT_FDW_SORT_MULTIPLIER 1.2
71
72/*
73 * Indexes of FDW-private information stored in fdw_private lists.
74 *
75 * These items are indexed with the enum FdwScanPrivateIndex, so an item
76 * can be fetched with list_nth(). For example, to get the SELECT statement:
77 * sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
78 */
80{
81 /* SQL statement to execute remotely (as a String node) */
83 /* Integer list of attribute numbers retrieved by the SELECT */
85 /* Integer representing the desired fetch_size */
87
88 /*
89 * String describing join i.e. names of relations being joined and types
90 * of join, added when the scan is join
91 */
93};
94
95/*
96 * Similarly, this enum describes what's kept in the fdw_private list for
97 * a ModifyTable node referencing a postgres_fdw foreign table. We store:
98 *
99 * 1) INSERT/UPDATE/DELETE statement text to be sent to the remote server
100 * 2) Integer list of target attribute numbers for INSERT/UPDATE
101 * (NIL for a DELETE)
102 * 3) Length till the end of VALUES clause for INSERT
103 * (-1 for a DELETE/UPDATE)
104 * 4) Boolean flag showing if the remote query has a RETURNING clause
105 * 5) Integer list of attribute numbers retrieved by RETURNING, if any
106 */
108{
109 /* SQL statement to execute remotely (as a String node) */
111 /* Integer list of target attribute numbers for INSERT/UPDATE */
113 /* Length till the end of VALUES clause (as an Integer node) */
115 /* has-returning flag (as a Boolean node) */
117 /* Integer list of attribute numbers retrieved by RETURNING */
119};
120
121/*
122 * Similarly, this enum describes what's kept in the fdw_private list for
123 * a ForeignScan node that modifies a foreign table directly. We store:
124 *
125 * 1) UPDATE/DELETE statement text to be sent to the remote server
126 * 2) Boolean flag showing if the remote query has a RETURNING clause
127 * 3) Integer list of attribute numbers retrieved by RETURNING, if any
128 * 4) Boolean flag showing if we set the command es_processed
129 */
131{
132 /* SQL statement to execute remotely (as a String node) */
134 /* has-returning flag (as a Boolean node) */
136 /* Integer list of attribute numbers retrieved by RETURNING */
138 /* set-processed flag (as a Boolean node) */
140};
141
142/*
143 * Execution state of a foreign scan using postgres_fdw.
144 */
145typedef struct PgFdwScanState
146{
147 Relation rel; /* relcache entry for the foreign table. NULL
148 * for a foreign join scan. */
149 TupleDesc tupdesc; /* tuple descriptor of scan */
150 AttInMetadata *attinmeta; /* attribute datatype conversion metadata */
151
152 /* extracted fdw_private data */
153 char *query; /* text of SELECT command */
154 List *retrieved_attrs; /* list of retrieved attribute numbers */
155
156 /* for remote query execution */
157 PGconn *conn; /* connection for the scan */
158 PgFdwConnState *conn_state; /* extra per-connection state */
159 unsigned int cursor_number; /* quasi-unique ID for my cursor */
160 bool cursor_exists; /* have we created the cursor? */
161 int numParams; /* number of parameters passed to query */
162 FmgrInfo *param_flinfo; /* output conversion functions for them */
163 List *param_exprs; /* executable expressions for param values */
164 const char **param_values; /* textual values of query parameters */
165
166 /* for storing result tuples */
167 HeapTuple *tuples; /* array of currently-retrieved tuples */
168 int num_tuples; /* # of tuples in array */
169 int next_tuple; /* index of next one to return */
170
171 /* batch-level state, for optimizing rewinds and avoiding useless fetch */
172 int fetch_ct_2; /* Min(# of fetches done, 2) */
173 bool eof_reached; /* true if last fetch reached EOF */
174
175 /* for asynchronous execution */
176 bool async_capable; /* engage asynchronous-capable logic? */
177
178 /* working memory contexts */
179 MemoryContext batch_cxt; /* context holding current batch of tuples */
180 MemoryContext temp_cxt; /* context for per-tuple temporary data */
181
182 int fetch_size; /* number of tuples per fetch */
184
185/*
186 * Execution state of a foreign insert/update/delete operation.
187 */
188typedef struct PgFdwModifyState
189{
190 Relation rel; /* relcache entry for the foreign table */
191 AttInMetadata *attinmeta; /* attribute datatype conversion metadata */
192
193 /* for remote query execution */
194 PGconn *conn; /* connection for the scan */
195 PgFdwConnState *conn_state; /* extra per-connection state */
196 char *p_name; /* name of prepared statement, if created */
197
198 /* extracted fdw_private data */
199 char *query; /* text of INSERT/UPDATE/DELETE command */
200 char *orig_query; /* original text of INSERT command */
201 List *target_attrs; /* list of target attribute numbers */
202 int values_end; /* length up to the end of VALUES */
203 int batch_size; /* value of FDW option "batch_size" */
204 bool has_returning; /* is there a RETURNING clause? */
205 List *retrieved_attrs; /* attr numbers retrieved by RETURNING */
206
207 /* info about parameters for prepared statement */
208 AttrNumber ctidAttno; /* attnum of input resjunk ctid column */
209 int p_nums; /* number of parameters to transmit */
210 FmgrInfo *p_flinfo; /* output conversion functions for them */
211
212 /* batch operation stuff */
213 int num_slots; /* number of slots to insert */
214
215 /* working memory context */
216 MemoryContext temp_cxt; /* context for per-tuple temporary data */
217
218 /* for update row movement if subplan result rel */
219 struct PgFdwModifyState *aux_fmstate; /* foreign-insert state, if
220 * created */
222
223/*
224 * Execution state of a foreign scan that modifies a foreign table directly.
225 */
227{
228 Relation rel; /* relcache entry for the foreign table */
229 AttInMetadata *attinmeta; /* attribute datatype conversion metadata */
230
231 /* extracted fdw_private data */
232 char *query; /* text of UPDATE/DELETE command */
233 bool has_returning; /* is there a RETURNING clause? */
234 List *retrieved_attrs; /* attr numbers retrieved by RETURNING */
235 bool set_processed; /* do we set the command es_processed? */
236
237 /* for remote query execution */
238 PGconn *conn; /* connection for the update */
239 PgFdwConnState *conn_state; /* extra per-connection state */
240 int numParams; /* number of parameters passed to query */
241 FmgrInfo *param_flinfo; /* output conversion functions for them */
242 List *param_exprs; /* executable expressions for param values */
243 const char **param_values; /* textual values of query parameters */
244
245 /* for storing result tuples */
246 PGresult *result; /* result for query */
247 int num_tuples; /* # of result tuples */
248 int next_tuple; /* index of next one to return */
249 Relation resultRel; /* relcache entry for the target relation */
250 AttrNumber *attnoMap; /* array of attnums of input user columns */
251 AttrNumber ctidAttno; /* attnum of input ctid column */
252 AttrNumber oidAttno; /* attnum of input oid column */
253 bool hasSystemCols; /* are there system columns of resultRel? */
254
255 /* working memory context */
256 MemoryContext temp_cxt; /* context for per-tuple temporary data */
258
259/*
260 * Workspace for analyzing a foreign table.
261 */
262typedef struct PgFdwAnalyzeState
263{
264 Relation rel; /* relcache entry for the foreign table */
265 AttInMetadata *attinmeta; /* attribute datatype conversion metadata */
266 List *retrieved_attrs; /* attr numbers retrieved by query */
267
268 /* collected sample rows */
269 HeapTuple *rows; /* array of size targrows */
270 int targrows; /* target # of sample rows */
271 int numrows; /* # of sample rows collected */
272
273 /* for random sampling */
274 double samplerows; /* # of rows fetched */
275 double rowstoskip; /* # of rows to skip before next sample */
276 ReservoirStateData rstate; /* state for reservoir sampling */
277
278 /* working memory contexts */
279 MemoryContext anl_cxt; /* context for per-analyze lifespan data */
280 MemoryContext temp_cxt; /* context for per-tuple temporary data */
282
283/*
284 * This enum describes what's kept in the fdw_private list for a ForeignPath.
285 * We store:
286 *
287 * 1) Boolean flag showing if the remote query has the final sort
288 * 2) Boolean flag showing if the remote query has the LIMIT clause
289 */
291{
292 /* has-final-sort flag (as a Boolean node) */
294 /* has-limit flag (as a Boolean node) */
296};
297
298/* Struct for extra information passed to estimate_path_cost_size() */
308
309/*
310 * Identify the attribute where data conversion fails.
311 */
312typedef struct ConversionLocation
313{
314 AttrNumber cur_attno; /* attribute number being processed, or 0 */
315 Relation rel; /* foreign table being processed, or NULL */
316 ForeignScanState *fsstate; /* plan node being processed, or NULL */
318
319/* Callback argument for ec_member_matches_foreign */
320typedef struct
321{
322 Expr *current; /* current expr, or NULL if not yet found */
323 List *already_used; /* expressions already dealt with */
325
326/* Pairs of remote columns with local columns */
334
335/* Result sets that are returned from a foreign statistics scan */
344
345/* Column order in relation stats query */
353
354/* Column order in attribute stats query */
373
374/*
375 * SQL functions
376 */
378
379/*
380 * FDW callback routines
381 */
389 RelOptInfo *foreignrel,
392 List *tlist,
394 Plan *outer_plan);
395static void postgresBeginForeignScan(ForeignScanState *node, int eflags);
400 Index rtindex,
401 RangeTblEntry *target_rte,
405 Index resultRelation,
406 int subplan_index);
408 ResultRelInfo *resultRelInfo,
409 List *fdw_private,
410 int subplan_index,
411 int eflags);
413 ResultRelInfo *resultRelInfo,
414 TupleTableSlot *slot,
415 TupleTableSlot *planSlot);
417 ResultRelInfo *resultRelInfo,
418 TupleTableSlot **slots,
420 int *numSlots);
421static int postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo);
423 ResultRelInfo *resultRelInfo,
424 TupleTableSlot *slot,
425 TupleTableSlot *planSlot);
427 ResultRelInfo *resultRelInfo,
428 TupleTableSlot *slot,
429 TupleTableSlot *planSlot);
430static void postgresEndForeignModify(EState *estate,
431 ResultRelInfo *resultRelInfo);
433 ResultRelInfo *resultRelInfo);
434static void postgresEndForeignInsert(EState *estate,
435 ResultRelInfo *resultRelInfo);
439 Index resultRelation,
440 int subplan_index);
441static void postgresBeginDirectModify(ForeignScanState *node, int eflags);
445 ExplainState *es);
447 ResultRelInfo *rinfo,
448 List *fdw_private,
449 int subplan_index,
450 ExplainState *es);
452 ExplainState *es);
453static void postgresExecForeignTruncate(List *rels,
454 DropBehavior behavior,
455 bool restart_seqs);
456static bool postgresAnalyzeForeignTable(Relation relation,
459static bool postgresImportForeignStatistics(Relation relation,
460 List *va_cols,
461 int elevel);
463 Oid serverOid);
465 RelOptInfo *joinrel,
466 RelOptInfo *outerrel,
467 RelOptInfo *innerrel,
468 JoinType jointype,
469 JoinPathExtraData *extra);
471 TupleTableSlot *slot);
473 UpperRelationKind stage,
476 void *extra);
481
482/*
483 * Helper functions
484 */
486 RelOptInfo *foreignrel,
488 List *pathkeys,
490 double *p_rows, int *p_width,
491 int *p_disabled_nodes,
493static void get_remote_estimate(const char *sql,
494 PGconn *conn,
495 double *rows,
496 int *width,
497 Cost *startup_cost,
498 Cost *total_cost);
500 List *pathkeys,
501 double retrieved_rows,
502 double width,
503 double limit_tuples,
504 int *p_disabled_nodes,
509 void *arg);
510static void create_cursor(ForeignScanState *node);
511static void fetch_more_data(ForeignScanState *node);
512static void close_cursor(PGconn *conn, unsigned int cursor_number,
513 PgFdwConnState *conn_state);
516 ResultRelInfo *resultRelInfo,
518 Plan *subplan,
519 char *query,
520 List *target_attrs,
521 int values_end,
522 bool has_returning,
523 List *retrieved_attrs);
525 ResultRelInfo *resultRelInfo,
527 TupleTableSlot **slots,
529 int *numSlots);
533 TupleTableSlot **slots,
534 int numSlots);
536 TupleTableSlot *slot, PGresult *res);
539static List *build_remote_returning(Index rtindex, Relation rel,
540 List *returningList);
541static void rebuild_fdw_scan_tlist(ForeignScan *fscan, List *tlist);
542static void execute_dml_stmt(ForeignScanState *node);
545 List *fdw_scan_tlist,
546 Index rtindex);
548 ResultRelInfo *resultRelInfo,
549 TupleTableSlot *slot,
550 EState *estate);
551static void prepare_query_params(PlanState *node,
552 List *fdw_exprs,
553 int numParams,
554 FmgrInfo **param_flinfo,
555 List **param_exprs,
556 const char ***param_values);
557static void process_query_params(ExprContext *econtext,
558 FmgrInfo *param_flinfo,
559 List *param_exprs,
560 const char **param_values);
561static int postgresAcquireSampleRowsFunc(Relation relation, int elevel,
562 HeapTuple *rows, int targrows,
563 double *totalrows,
564 double *totaldeadrows);
565static void analyze_row_processor(PGresult *res, int row,
566 PgFdwAnalyzeState *astate);
567static bool fetch_remote_statistics(Relation relation,
568 List *va_cols,
570 const char *local_schemaname,
571 const char *local_relname,
572 int *p_attrcnt,
575static PGresult *fetch_relstats(PGconn *conn, Relation relation);
577 const char *remote_schemaname, const char *remote_relname,
578 const char *column_list);
579static RemoteAttributeMapping *build_remattrmap(Relation relation, List *va_cols,
581static void free_remattrmap(RemoteAttributeMapping *map, int len);
582static bool attname_in_list(const char *attname, List *va_cols);
583static int remattrmap_cmp(const void *v1, const void *v2);
584static bool match_attrmap(PGresult *res,
585 const char *local_schemaname,
586 const char *local_relname,
587 const char *remote_schemaname,
588 const char *remote_relname,
589 int attrcnt,
591static bool import_fetched_statistics(Relation relation,
592 const char *schemaname,
593 const char *relname,
594 int attrcnt,
597static char *get_opt_value(PGresult *res, int row, int col);
598static void set_text_arg(NullableDatum *arg, const char *s);
599static void set_int32_arg(NullableDatum *arg, const char *s);
600static void set_uint32_arg(NullableDatum *arg, const char *s);
601static void set_float_arg(NullableDatum *arg, const char *s);
602static void set_floatarr_arg(NullableDatum *arg, const char *s);
603static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch);
607 int row,
608 Relation rel,
609 AttInMetadata *attinmeta,
610 List *retrieved_attrs,
611 ForeignScanState *fsstate,
613static void conversion_error_callback(void *arg);
614static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel,
615 JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel,
616 JoinPathExtraData *extra);
617static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
618 Node *havingQual);
620 RelOptInfo *rel);
623 Path *epq_path, List *restrictlist);
626 RelOptInfo *grouped_rel,
627 GroupPathExtraData *extra);
634 FinalPathExtraData *extra);
640static int get_batch_size_option(Relation rel);
641
642
643/*
644 * Foreign-data wrapper handler function: return a struct with pointers
645 * to my callback routines.
646 */
647Datum
649{
650 FdwRoutine *routine = makeNode(FdwRoutine);
651
652 /* Functions for scanning foreign tables */
660
661 /* Functions for updating foreign tables */
678
679 /* Function for EvalPlanQual rechecks */
681 /* Support functions for EXPLAIN */
685
686 /* Support function for TRUNCATE */
688
689 /* Support functions for ANALYZE */
692
693 /* Support functions for IMPORT FOREIGN SCHEMA */
695
696 /* Support functions for join push-down */
698
699 /* Support functions for upper relation push-down */
701
702 /* Support functions for asynchronous execution */
707
708 PG_RETURN_POINTER(routine);
709}
710
711/*
712 * postgresGetForeignRelSize
713 * Estimate # of rows and width of the result of the scan
714 *
715 * We should consider the effect of all baserestrictinfo clauses here, but
716 * not any join clauses.
717 */
718static void
722{
724 ListCell *lc;
725
726 /*
727 * We use PgFdwRelationInfo to pass various information to subsequent
728 * functions.
729 */
731 baserel->fdw_private = fpinfo;
732
733 /* Base foreign tables need to be pushed down always. */
734 fpinfo->pushdown_safe = true;
735
736 /* Look up foreign-table catalog info. */
738 fpinfo->server = GetForeignServer(fpinfo->table->serverid);
739
740 /*
741 * Extract user-settable option values. Note that per-table settings of
742 * use_remote_estimate, fetch_size and async_capable override per-server
743 * settings of them, respectively.
744 */
745 fpinfo->use_remote_estimate = false;
746 fpinfo->fdw_startup_cost = DEFAULT_FDW_STARTUP_COST;
747 fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
748 fpinfo->shippable_extensions = NIL;
749 fpinfo->fetch_size = 100;
750 fpinfo->async_capable = false;
751
754
755 /*
756 * If the table or the server is configured to use remote estimates,
757 * identify which user to do remote access as during planning. This
758 * should match what ExecCheckPermissions() does. If we fail due to lack
759 * of permissions, the query would have failed at runtime anyway.
760 */
761 if (fpinfo->use_remote_estimate)
762 {
763 Oid userid;
764
765 userid = OidIsValid(baserel->userid) ? baserel->userid : GetUserId();
766 fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid);
767 }
768 else
769 fpinfo->user = NULL;
770
771 /*
772 * Identify which baserestrictinfo clauses can be sent to the remote
773 * server and which can't.
774 */
775 classifyConditions(root, baserel, baserel->baserestrictinfo,
776 &fpinfo->remote_conds, &fpinfo->local_conds);
777
778 /*
779 * Identify which attributes will need to be retrieved from the remote
780 * server. These include all attrs needed for joins or final output, plus
781 * all attrs used in the local_conds. (Note: if we end up using a
782 * parameterized scan, it's possible that some of the join clauses will be
783 * sent to the remote and thus we wouldn't really need to retrieve the
784 * columns used in them. Doesn't seem worth detecting that case though.)
785 */
786 fpinfo->attrs_used = NULL;
787 pull_varattnos((Node *) baserel->reltarget->exprs, baserel->relid,
788 &fpinfo->attrs_used);
789 foreach(lc, fpinfo->local_conds)
790 {
792
793 pull_varattnos((Node *) rinfo->clause, baserel->relid,
794 &fpinfo->attrs_used);
795 }
796
797 /*
798 * Compute the selectivity and cost of the local_conds, so we don't have
799 * to do it over again for each path. The best we can do for these
800 * conditions is to estimate selectivity on the basis of local statistics.
801 */
802 fpinfo->local_conds_sel = clauselist_selectivity(root,
803 fpinfo->local_conds,
804 baserel->relid,
806 NULL);
807
808 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
809
810 /*
811 * Set # of retrieved rows and cached relation costs to some negative
812 * value, so that we can detect when they are set to some sensible values,
813 * during one (usually the first) of the calls to estimate_path_cost_size.
814 */
815 fpinfo->retrieved_rows = -1;
816 fpinfo->rel_startup_cost = -1;
817 fpinfo->rel_total_cost = -1;
818
819 /*
820 * If the table or the server is configured to use remote estimates,
821 * connect to the foreign server and execute EXPLAIN to estimate the
822 * number of rows selected by the restriction clauses, as well as the
823 * average row width. Otherwise, estimate using whatever statistics we
824 * have locally, in a way similar to ordinary tables.
825 */
826 if (fpinfo->use_remote_estimate)
827 {
828 /*
829 * Get cost/size estimates with help of remote server. Save the
830 * values in fpinfo so we don't need to do it again to generate the
831 * basic foreign path.
832 */
834 &fpinfo->rows, &fpinfo->width,
835 &fpinfo->disabled_nodes,
836 &fpinfo->startup_cost, &fpinfo->total_cost);
837
838 /* Report estimated baserel size to planner. */
839 baserel->rows = fpinfo->rows;
840 baserel->reltarget->width = fpinfo->width;
841 }
842 else
843 {
844 /*
845 * If the foreign table has never been ANALYZEd, it will have
846 * reltuples < 0, meaning "unknown". We can't do much if we're not
847 * allowed to consult the remote server, but we can use a hack similar
848 * to plancat.c's treatment of empty relations: use a minimum size
849 * estimate of 10 pages, and divide by the column-datatype-based width
850 * estimate to get the corresponding number of tuples.
851 */
852 if (baserel->tuples < 0)
853 {
854 baserel->pages = 10;
855 baserel->tuples =
856 (10 * BLCKSZ) / (baserel->reltarget->width +
858 }
859
860 /* Estimate baserel size as best we can with local statistics. */
862
863 /* Fill in basically-bogus cost estimates for use later. */
865 &fpinfo->rows, &fpinfo->width,
866 &fpinfo->disabled_nodes,
867 &fpinfo->startup_cost, &fpinfo->total_cost);
868 }
869
870 /*
871 * fpinfo->relation_name gets the numeric rangetable index of the foreign
872 * table RTE. (If this query gets EXPLAIN'd, we'll convert that to a
873 * human-readable string at that time.)
874 */
875 fpinfo->relation_name = psprintf("%u", baserel->relid);
876
877 /* No outer and inner relations. */
878 fpinfo->make_outerrel_subquery = false;
879 fpinfo->make_innerrel_subquery = false;
880 fpinfo->lower_subquery_rels = NULL;
881 fpinfo->hidden_subquery_rels = NULL;
882 /* Set the relation index. */
883 fpinfo->relation_index = baserel->relid;
884}
885
886/*
887 * get_useful_ecs_for_relation
888 * Determine which EquivalenceClasses might be involved in useful
889 * orderings of this relation.
890 *
891 * This function is in some respects a mirror image of the core function
892 * pathkeys_useful_for_merging: for a regular table, we know what indexes
893 * we have and want to test whether any of them are useful. For a foreign
894 * table, we don't know what indexes are present on the remote side but
895 * want to speculate about which ones we'd like to use if they existed.
896 *
897 * This function returns a list of potentially-useful equivalence classes,
898 * but it does not guarantee that an EquivalenceMember exists which contains
899 * Vars only from the given relation. For example, given ft1 JOIN t1 ON
900 * ft1.x + t1.x = 0, this function will say that the equivalence class
901 * containing ft1.x + t1.x is potentially useful. Supposing ft1 is remote and
902 * t1 is local (or on a different server), it will turn out that no useful
903 * ORDER BY clause can be generated. It's not our job to figure that out
904 * here; we're only interested in identifying relevant ECs.
905 */
906static List *
908{
910 ListCell *lc;
911 Relids relids;
912
913 /*
914 * First, consider whether any active EC is potentially useful for a merge
915 * join against this relation.
916 */
917 if (rel->has_eclass_joins)
918 {
919 foreach(lc, root->eq_classes)
920 {
922
925 }
926 }
927
928 /*
929 * Next, consider whether there are any non-EC derivable join clauses that
930 * are merge-joinable. If the joininfo list is empty, we can exit
931 * quickly.
932 */
933 if (rel->joininfo == NIL)
934 return useful_eclass_list;
935
936 /* If this is a child rel, we must use the topmost parent rel to search. */
937 if (IS_OTHER_REL(rel))
938 {
940 relids = rel->top_parent_relids;
941 }
942 else
943 relids = rel->relids;
944
945 /* Check each join clause in turn. */
946 foreach(lc, rel->joininfo)
947 {
949
950 /* Consider only mergejoinable clauses */
951 if (restrictinfo->mergeopfamilies == NIL)
952 continue;
953
954 /* Make sure we've got canonical ECs. */
956
957 /*
958 * restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
959 * that left_ec and right_ec will be initialized, per comments in
960 * distribute_qual_to_rels.
961 *
962 * We want to identify which side of this merge-joinable clause
963 * contains columns from the relation produced by this RelOptInfo. We
964 * test for overlap, not containment, because there could be extra
965 * relations on either side. For example, suppose we've got something
966 * like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
967 * A.y = D.y. The input rel might be the joinrel between A and B, and
968 * we'll consider the join clause A.y = D.y. relids contains a
969 * relation not involved in the join class (B) and the equivalence
970 * class for the left-hand side of the clause contains a relation not
971 * involved in the input rel (C). Despite the fact that we have only
972 * overlap and not containment in either direction, A.y is potentially
973 * useful as a sort column.
974 *
975 * Note that it's even possible that relids overlaps neither side of
976 * the join clause. For example, consider A LEFT JOIN B ON A.x = B.x
977 * AND A.x = 1. The clause A.x = 1 will appear in B's joininfo list,
978 * but overlaps neither side of B. In that case, we just skip this
979 * join clause, since it doesn't suggest a useful sort order for this
980 * relation.
981 */
982 if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
984 restrictinfo->right_ec);
985 else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
987 restrictinfo->left_ec);
988 }
989
990 return useful_eclass_list;
991}
992
993/*
994 * get_useful_pathkeys_for_relation
995 * Determine which orderings of a relation might be useful.
996 *
997 * Getting data in sorted order can be useful either because the requested
998 * order matches the final output ordering for the overall query we're
999 * planning, or because it enables an efficient merge join. Here, we try
1000 * to figure out which pathkeys to consider.
1001 */
1002static List *
1004{
1007 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
1009 ListCell *lc;
1010
1011 /*
1012 * Pushing the query_pathkeys to the remote server is always worth
1013 * considering, because it might let us avoid a local sort.
1014 */
1015 fpinfo->qp_is_pushdown_safe = false;
1016 if (root->query_pathkeys)
1017 {
1018 bool query_pathkeys_ok = true;
1019
1020 foreach(lc, root->query_pathkeys)
1021 {
1023
1024 /*
1025 * The planner and executor don't have any clever strategy for
1026 * taking data sorted by a prefix of the query's pathkeys and
1027 * getting it to be sorted by all of those pathkeys. We'll just
1028 * end up resorting the entire data set. So, unless we can push
1029 * down all of the query pathkeys, forget it.
1030 */
1031 if (!is_foreign_pathkey(root, rel, pathkey))
1032 {
1033 query_pathkeys_ok = false;
1034 break;
1035 }
1036 }
1037
1039 {
1040 useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
1041 fpinfo->qp_is_pushdown_safe = true;
1042 }
1043 }
1044
1045 /*
1046 * Even if we're not using remote estimates, having the remote side do the
1047 * sort generally won't be any worse than doing it locally, and it might
1048 * be much better if the remote side can generate data in the right order
1049 * without needing a sort at all. However, what we're going to do next is
1050 * try to generate pathkeys that seem promising for possible merge joins,
1051 * and that's more speculative. A wrong choice might hurt quite a bit, so
1052 * bail out if we can't use remote estimates.
1053 */
1054 if (!fpinfo->use_remote_estimate)
1055 return useful_pathkeys_list;
1056
1057 /* Get the list of interesting EquivalenceClasses. */
1059
1060 /* Extract unique EC for query, if any, so we don't consider it again. */
1061 if (list_length(root->query_pathkeys) == 1)
1062 {
1063 PathKey *query_pathkey = linitial(root->query_pathkeys);
1064
1065 query_ec = query_pathkey->pk_eclass;
1066 }
1067
1068 /*
1069 * As a heuristic, the only pathkeys we consider here are those of length
1070 * one. It's surely possible to consider more, but since each one we
1071 * choose to consider will generate a round-trip to the remote side, we
1072 * need to be a bit cautious here. It would sure be nice to have a local
1073 * cache of information about remote index definitions...
1074 */
1075 foreach(lc, useful_eclass_list)
1076 {
1079
1080 /* If redundant with what we did above, skip it. */
1081 if (cur_ec == query_ec)
1082 continue;
1083
1084 /* Can't push down the sort if the EC's opfamily is not shippable. */
1085 if (!is_shippable(linitial_oid(cur_ec->ec_opfamilies),
1087 continue;
1088
1089 /* If no pushable expression for this rel, skip it. */
1090 if (find_em_for_rel(root, cur_ec, rel) == NULL)
1091 continue;
1092
1093 /* Looks like we can generate a pathkey, so let's do it. */
1095 linitial_oid(cur_ec->ec_opfamilies),
1096 COMPARE_LT,
1097 false);
1100 }
1101
1102 return useful_pathkeys_list;
1103}
1104
1105/*
1106 * postgresGetForeignPaths
1107 * Create possible scan paths for a scan on the foreign table
1108 */
1109static void
1113{
1115 ForeignPath *path;
1116 List *ppi_list;
1117 ListCell *lc;
1118
1119 /*
1120 * Create simplest ForeignScan path node and add it to baserel. This path
1121 * corresponds to SeqScan path of regular tables (though depending on what
1122 * baserestrict conditions we were able to send to remote, there might
1123 * actually be an indexscan happening there). We already did all the work
1124 * to estimate cost and size of this path.
1125 *
1126 * Although this path uses no join clauses, it could still have required
1127 * parameterization due to LATERAL refs in its tlist.
1128 */
1130 NULL, /* default pathtarget */
1131 fpinfo->rows,
1132 fpinfo->disabled_nodes,
1133 fpinfo->startup_cost,
1134 fpinfo->total_cost,
1135 NIL, /* no pathkeys */
1136 baserel->lateral_relids,
1137 NULL, /* no extra plan */
1138 NIL, /* no fdw_restrictinfo list */
1139 NIL); /* no fdw_private list */
1140 add_path(baserel, (Path *) path);
1141
1142 /* Add paths with pathkeys */
1144
1145 /*
1146 * If we're not using remote estimates, stop here. We have no way to
1147 * estimate whether any join clauses would be worth sending across, so
1148 * don't bother building parameterized paths.
1149 */
1150 if (!fpinfo->use_remote_estimate)
1151 return;
1152
1153 /*
1154 * Thumb through all join clauses for the rel to identify which outer
1155 * relations could supply one or more safe-to-send-to-remote join clauses.
1156 * We'll build a parameterized path for each such outer relation.
1157 *
1158 * It's convenient to manage this by representing each candidate outer
1159 * relation by the ParamPathInfo node for it. We can then use the
1160 * ppi_clauses list in the ParamPathInfo node directly as a list of the
1161 * interesting join clauses for that rel. This takes care of the
1162 * possibility that there are multiple safe join clauses for such a rel,
1163 * and also ensures that we account for unsafe join clauses that we'll
1164 * still have to enforce locally (since the parameterized-path machinery
1165 * insists that we handle all movable clauses).
1166 */
1167 ppi_list = NIL;
1168 foreach(lc, baserel->joininfo)
1169 {
1170 RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
1173
1174 /* Check if clause can be moved to this rel */
1176 continue;
1177
1178 /* See if it is safe to send to remote */
1179 if (!is_foreign_expr(root, baserel, rinfo->clause))
1180 continue;
1181
1182 /* Calculate required outer rels for the resulting path */
1183 required_outer = bms_union(rinfo->clause_relids,
1184 baserel->lateral_relids);
1185 /* We do not want the foreign rel itself listed in required_outer */
1187
1188 /*
1189 * required_outer probably can't be empty here, but if it were, we
1190 * couldn't make a parameterized path.
1191 */
1193 continue;
1194
1195 /* Get the ParamPathInfo */
1199
1200 /*
1201 * Add it to list unless we already have it. Testing pointer equality
1202 * is OK since get_baserel_parampathinfo won't make duplicates.
1203 */
1205 }
1206
1207 /*
1208 * The above scan examined only "generic" join clauses, not those that
1209 * were absorbed into EquivalenceClauses. See if we can make anything out
1210 * of EquivalenceClauses.
1211 */
1212 if (baserel->has_eclass_joins)
1213 {
1214 /*
1215 * We repeatedly scan the eclass list looking for column references
1216 * (or expressions) belonging to the foreign rel. Each time we find
1217 * one, we generate a list of equivalence joinclauses for it, and then
1218 * see if any are safe to send to the remote. Repeat till there are
1219 * no more candidate EC members.
1220 */
1222
1224 for (;;)
1225 {
1226 List *clauses;
1227
1228 /* Make clauses, skipping any that join to lateral_referencers */
1229 arg.current = NULL;
1231 baserel,
1233 &arg,
1234 baserel->lateral_referencers);
1235
1236 /* Done if there are no more expressions in the foreign rel */
1237 if (arg.current == NULL)
1238 {
1239 Assert(clauses == NIL);
1240 break;
1241 }
1242
1243 /* Scan the extracted join clauses */
1244 foreach(lc, clauses)
1245 {
1246 RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
1249
1250 /* Check if clause can be moved to this rel */
1252 continue;
1253
1254 /* See if it is safe to send to remote */
1255 if (!is_foreign_expr(root, baserel, rinfo->clause))
1256 continue;
1257
1258 /* Calculate required outer rels for the resulting path */
1259 required_outer = bms_union(rinfo->clause_relids,
1260 baserel->lateral_relids);
1263 continue;
1264
1265 /* Get the ParamPathInfo */
1269
1270 /* Add it to list unless we already have it */
1272 }
1273
1274 /* Try again, now ignoring the expression we found this time */
1275 arg.already_used = lappend(arg.already_used, arg.current);
1276 }
1277 }
1278
1279 /*
1280 * Now build a path for each useful outer relation.
1281 */
1282 foreach(lc, ppi_list)
1283 {
1285 double rows;
1286 int width;
1287 int disabled_nodes;
1288 Cost startup_cost;
1289 Cost total_cost;
1290
1291 /* Get a cost estimate from the remote */
1293 param_info->ppi_clauses, NIL, NULL,
1294 &rows, &width, &disabled_nodes,
1295 &startup_cost, &total_cost);
1296
1297 /*
1298 * ppi_rows currently won't get looked at by anything, but still we
1299 * may as well ensure that it matches our idea of the rowcount.
1300 */
1301 param_info->ppi_rows = rows;
1302
1303 /* Make the path */
1305 NULL, /* default pathtarget */
1306 rows,
1307 disabled_nodes,
1308 startup_cost,
1309 total_cost,
1310 NIL, /* no pathkeys */
1311 param_info->ppi_req_outer,
1312 NULL,
1313 NIL, /* no fdw_restrictinfo list */
1314 NIL); /* no fdw_private list */
1315 add_path(baserel, (Path *) path);
1316 }
1317}
1318
1319/*
1320 * postgresGetForeignPlan
1321 * Create ForeignScan plan node which implements selected best path
1322 */
1323static ForeignScan *
1325 RelOptInfo *foreignrel,
1328 List *tlist,
1330 Plan *outer_plan)
1331{
1332 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
1334 List *fdw_private;
1336 List *local_exprs = NIL;
1337 List *params_list = NIL;
1338 List *fdw_scan_tlist = NIL;
1339 List *fdw_recheck_quals = NIL;
1340 List *retrieved_attrs;
1341 StringInfoData sql;
1342 bool has_final_sort = false;
1343 bool has_limit = false;
1344 ListCell *lc;
1345
1346 /*
1347 * Get FDW private data created by postgresGetForeignUpperPaths(), if any.
1348 */
1349 if (best_path->fdw_private)
1350 {
1351 has_final_sort = boolVal(list_nth(best_path->fdw_private,
1353 has_limit = boolVal(list_nth(best_path->fdw_private,
1355 }
1356
1357 if (IS_SIMPLE_REL(foreignrel))
1358 {
1359 /*
1360 * For base relations, set scan_relid as the relid of the relation.
1361 */
1362 scan_relid = foreignrel->relid;
1363
1364 /*
1365 * In a base-relation scan, we must apply the given scan_clauses.
1366 *
1367 * Separate the scan_clauses into those that can be executed remotely
1368 * and those that can't. baserestrictinfo clauses that were
1369 * previously determined to be safe or unsafe by classifyConditions
1370 * are found in fpinfo->remote_conds and fpinfo->local_conds. Anything
1371 * else in the scan_clauses list will be a join clause, which we have
1372 * to check for remote-safety.
1373 *
1374 * Note: the join clauses we see here should be the exact same ones
1375 * previously examined by postgresGetForeignPaths. Possibly it'd be
1376 * worth passing forward the classification work done then, rather
1377 * than repeating it here.
1378 *
1379 * This code must match "extract_actual_clauses(scan_clauses, false)"
1380 * except for the additional decision about remote versus local
1381 * execution.
1382 */
1383 foreach(lc, scan_clauses)
1384 {
1386
1387 /* Ignore any pseudoconstants, they're dealt with elsewhere */
1388 if (rinfo->pseudoconstant)
1389 continue;
1390
1391 if (list_member_ptr(fpinfo->remote_conds, rinfo))
1393 else if (list_member_ptr(fpinfo->local_conds, rinfo))
1395 else if (is_foreign_expr(root, foreignrel, rinfo->clause))
1397 else
1399 }
1400
1401 /*
1402 * For a base-relation scan, we have to support EPQ recheck, which
1403 * should recheck all the remote quals.
1404 */
1405 fdw_recheck_quals = remote_exprs;
1406 }
1407 else
1408 {
1409 /*
1410 * Join relation or upper relation - set scan_relid to 0.
1411 */
1412 scan_relid = 0;
1413
1414 /*
1415 * For a join rel, baserestrictinfo is NIL and we are not considering
1416 * parameterization right now, so there should be no scan_clauses for
1417 * a joinrel or an upper rel either.
1418 */
1420
1421 /*
1422 * Instead we get the conditions to apply from the fdw_private
1423 * structure.
1424 */
1425 remote_exprs = extract_actual_clauses(fpinfo->remote_conds, false);
1426 local_exprs = extract_actual_clauses(fpinfo->local_conds, false);
1427
1428 /*
1429 * We leave fdw_recheck_quals empty in this case, since we never need
1430 * to apply EPQ recheck clauses. In the case of a joinrel, EPQ
1431 * recheck is handled elsewhere --- see postgresGetForeignJoinPaths().
1432 * If we're planning an upperrel (ie, remote grouping or aggregation)
1433 * then there's no EPQ to do because SELECT FOR UPDATE wouldn't be
1434 * allowed, and indeed we *can't* put the remote clauses into
1435 * fdw_recheck_quals because the unaggregated Vars won't be available
1436 * locally.
1437 */
1438
1439 /* Build the list of columns to be fetched from the foreign server. */
1440 fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
1441
1442 /*
1443 * Ensure that the outer plan produces a tuple whose descriptor
1444 * matches our scan tuple slot. Also, remove the local conditions
1445 * from outer plan's quals, lest they be evaluated twice, once by the
1446 * local plan and once by the scan.
1447 */
1448 if (outer_plan)
1449 {
1450 /*
1451 * Right now, we only consider grouping and aggregation beyond
1452 * joins. Queries involving aggregates or grouping do not require
1453 * EPQ mechanism, hence should not have an outer plan here.
1454 */
1455 Assert(!IS_UPPER_REL(foreignrel));
1456
1457 /*
1458 * First, update the plan's qual list if possible. In some cases
1459 * the quals might be enforced below the topmost plan level, in
1460 * which case we'll fail to remove them; it's not worth working
1461 * harder than this.
1462 */
1463 foreach(lc, local_exprs)
1464 {
1465 Node *qual = lfirst(lc);
1466
1467 outer_plan->qual = list_delete(outer_plan->qual, qual);
1468
1469 /*
1470 * For an inner join the local conditions of foreign scan plan
1471 * can be part of the joinquals as well. (They might also be
1472 * in the mergequals or hashquals, but we can't touch those
1473 * without breaking the plan.)
1474 */
1475 if (IsA(outer_plan, NestLoop) ||
1476 IsA(outer_plan, MergeJoin) ||
1477 IsA(outer_plan, HashJoin))
1478 {
1479 Join *join_plan = (Join *) outer_plan;
1480
1481 if (join_plan->jointype == JOIN_INNER)
1482 join_plan->joinqual = list_delete(join_plan->joinqual,
1483 qual);
1484 }
1485 }
1486
1487 /*
1488 * Now fix the subplan's tlist --- this might result in inserting
1489 * a Result node atop the plan tree.
1490 */
1491 outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist,
1492 best_path->path.parallel_safe);
1493 }
1494 }
1495
1496 /*
1497 * Build the query string to be sent for execution, and identify
1498 * expressions to be sent as parameters.
1499 */
1500 initStringInfo(&sql);
1501 deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
1502 remote_exprs, best_path->path.pathkeys,
1503 has_final_sort, has_limit, false,
1504 &retrieved_attrs, &params_list);
1505
1506 /* Remember remote_exprs for possible use by postgresPlanDirectModify */
1507 fpinfo->final_remote_exprs = remote_exprs;
1508
1509 /*
1510 * Build the fdw_private list that will be available to the executor.
1511 * Items in the list must match order in enum FdwScanPrivateIndex.
1512 */
1513 fdw_private = list_make3(makeString(sql.data),
1514 retrieved_attrs,
1515 makeInteger(fpinfo->fetch_size));
1516 if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
1517 fdw_private = lappend(fdw_private,
1518 makeString(fpinfo->relation_name));
1519
1520 /*
1521 * Create the ForeignScan node for the given relation.
1522 *
1523 * Note that the remote parameter expressions are stored in the fdw_exprs
1524 * field of the finished plan node; we can't keep them in private state
1525 * because then they wouldn't be subject to later planner processing.
1526 */
1527 return make_foreignscan(tlist,
1529 scan_relid,
1530 params_list,
1531 fdw_private,
1532 fdw_scan_tlist,
1533 fdw_recheck_quals,
1534 outer_plan);
1535}
1536
1537/*
1538 * Construct a tuple descriptor for the scan tuples handled by a foreign join.
1539 */
1540static TupleDesc
1542{
1543 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1544 EState *estate = node->ss.ps.state;
1545 TupleDesc tupdesc;
1546
1547 /*
1548 * The core code has already set up a scan tuple slot based on
1549 * fsplan->fdw_scan_tlist, and this slot's tupdesc is mostly good enough,
1550 * but there's one case where it isn't. If we have any whole-row row
1551 * identifier Vars, they may have vartype RECORD, and we need to replace
1552 * that with the associated table's actual composite type. This ensures
1553 * that when we read those ROW() expression values from the remote server,
1554 * we can convert them to a composite type the local server knows.
1555 */
1557 for (int i = 0; i < tupdesc->natts; i++)
1558 {
1559 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
1560 Var *var;
1562 Oid reltype;
1563
1564 /* Nothing to do if it's not a generic RECORD attribute */
1565 if (att->atttypid != RECORDOID || att->atttypmod >= 0)
1566 continue;
1567
1568 /*
1569 * If we can't identify the referenced table, do nothing. This'll
1570 * likely lead to failure later, but perhaps we can muddle through.
1571 */
1572 var = (Var *) list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
1573 i)->expr;
1574 if (!IsA(var, Var) || var->varattno != 0)
1575 continue;
1576 rte = list_nth(estate->es_range_table, var->varno - 1);
1577 if (rte->rtekind != RTE_RELATION)
1578 continue;
1579 reltype = get_rel_type_id(rte->relid);
1580 if (!OidIsValid(reltype))
1581 continue;
1582 att->atttypid = reltype;
1583 /* shouldn't need to change anything else */
1584 }
1585 return tupdesc;
1586}
1587
1588/*
1589 * postgresBeginForeignScan
1590 * Initiate an executor scan of a foreign PostgreSQL table.
1591 */
1592static void
1594{
1595 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
1596 EState *estate = node->ss.ps.state;
1597 PgFdwScanState *fsstate;
1599 Oid userid;
1602 int rtindex;
1603 int numParams;
1604
1605 /*
1606 * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
1607 */
1608 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1609 return;
1610
1611 /*
1612 * We'll save private state in node->fdw_state.
1613 */
1614 fsstate = palloc0_object(PgFdwScanState);
1615 node->fdw_state = fsstate;
1616
1617 /*
1618 * Identify which user to do the remote access as. This should match what
1619 * ExecCheckPermissions() does.
1620 */
1621 userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
1622 if (fsplan->scan.scanrelid > 0)
1623 rtindex = fsplan->scan.scanrelid;
1624 else
1625 rtindex = bms_next_member(fsplan->fs_base_relids, -1);
1626 rte = exec_rt_fetch(rtindex, estate);
1627
1628 /* Get info about foreign table. */
1629 table = GetForeignTable(rte->relid);
1630 user = GetUserMapping(userid, table->serverid);
1631
1632 /*
1633 * Get connection to the foreign server. Connection manager will
1634 * establish new connection if necessary.
1635 */
1636 fsstate->conn = GetConnection(user, false, &fsstate->conn_state);
1637
1638 /* Assign a unique ID for my cursor */
1639 fsstate->cursor_number = GetCursorNumber(fsstate->conn);
1640 fsstate->cursor_exists = false;
1641
1642 /* Get private info created by planner functions. */
1643 fsstate->query = strVal(list_nth(fsplan->fdw_private,
1645 fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
1647 fsstate->fetch_size = intVal(list_nth(fsplan->fdw_private,
1649
1650 /* Create contexts for batches of tuples and per-tuple temp workspace. */
1651 fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
1652 "postgres_fdw tuple data",
1654 fsstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
1655 "postgres_fdw temporary data",
1657
1658 /*
1659 * Get info we'll need for converting data fetched from the foreign server
1660 * into local representation and error reporting during that process.
1661 */
1662 if (fsplan->scan.scanrelid > 0)
1663 {
1664 fsstate->rel = node->ss.ss_currentRelation;
1665 fsstate->tupdesc = RelationGetDescr(fsstate->rel);
1666 }
1667 else
1668 {
1669 fsstate->rel = NULL;
1671 }
1672
1673 fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
1674
1675 /*
1676 * Prepare for processing of parameters used in remote query, if any.
1677 */
1678 numParams = list_length(fsplan->fdw_exprs);
1679 fsstate->numParams = numParams;
1680 if (numParams > 0)
1682 fsplan->fdw_exprs,
1683 numParams,
1684 &fsstate->param_flinfo,
1685 &fsstate->param_exprs,
1686 &fsstate->param_values);
1687
1688 /* Set the async-capable flag */
1689 fsstate->async_capable = node->ss.ps.async_capable;
1690}
1691
1692/*
1693 * postgresIterateForeignScan
1694 * Retrieve next row from the result set, or clear tuple slot to indicate
1695 * EOF.
1696 */
1697static TupleTableSlot *
1699{
1700 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1701 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
1702
1703 /*
1704 * In sync mode, if this is the first call after Begin or ReScan, we need
1705 * to create the cursor on the remote side. In async mode, we would have
1706 * already created the cursor before we get here, even if this is the
1707 * first call after Begin or ReScan.
1708 */
1709 if (!fsstate->cursor_exists)
1710 create_cursor(node);
1711
1712 /*
1713 * Get some more tuples, if we've run out.
1714 */
1715 if (fsstate->next_tuple >= fsstate->num_tuples)
1716 {
1717 /* In async mode, just clear tuple slot. */
1718 if (fsstate->async_capable)
1719 return ExecClearTuple(slot);
1720 /* No point in another fetch if we already detected EOF, though. */
1721 if (!fsstate->eof_reached)
1722 fetch_more_data(node);
1723 /* If we didn't get any tuples, must be end of data. */
1724 if (fsstate->next_tuple >= fsstate->num_tuples)
1725 return ExecClearTuple(slot);
1726 }
1727
1728 /*
1729 * Return the next tuple.
1730 */
1731 ExecStoreHeapTuple(fsstate->tuples[fsstate->next_tuple++],
1732 slot,
1733 false);
1734
1735 return slot;
1736}
1737
1738/*
1739 * postgresReScanForeignScan
1740 * Restart the scan.
1741 */
1742static void
1744{
1745 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1746 char sql[64];
1747 PGresult *res;
1748
1749 /* If we haven't created the cursor yet, nothing to do. */
1750 if (!fsstate->cursor_exists)
1751 return;
1752
1753 /*
1754 * If the node is async-capable, and an asynchronous fetch for it has
1755 * begun, the asynchronous fetch might not have yet completed. Check if
1756 * the node is async-capable, and an asynchronous fetch for it is still in
1757 * progress; if so, complete the asynchronous fetch before restarting the
1758 * scan.
1759 */
1760 if (fsstate->async_capable &&
1761 fsstate->conn_state->pendingAreq &&
1762 fsstate->conn_state->pendingAreq->requestee == (PlanState *) node)
1763 fetch_more_data(node);
1764
1765 /*
1766 * If any internal parameters affecting this node have changed, we'd
1767 * better destroy and recreate the cursor. Otherwise, if the remote
1768 * server is v14 or older, rewinding it should be good enough; if not,
1769 * rewind is only allowed for scrollable cursors, but we don't have a way
1770 * to check the scrollability of it, so destroy and recreate it in any
1771 * case. If we've only fetched zero or one batch, we needn't even rewind
1772 * the cursor, just rescan what we have.
1773 */
1774 if (node->ss.ps.chgParam != NULL)
1775 {
1776 fsstate->cursor_exists = false;
1777 snprintf(sql, sizeof(sql), "CLOSE c%u",
1778 fsstate->cursor_number);
1779 }
1780 else if (fsstate->fetch_ct_2 > 1)
1781 {
1782 if (PQserverVersion(fsstate->conn) < 150000)
1783 snprintf(sql, sizeof(sql), "MOVE BACKWARD ALL IN c%u",
1784 fsstate->cursor_number);
1785 else
1786 {
1787 fsstate->cursor_exists = false;
1788 snprintf(sql, sizeof(sql), "CLOSE c%u",
1789 fsstate->cursor_number);
1790 }
1791 }
1792 else
1793 {
1794 /* Easy: just rescan what we already have in memory, if anything */
1795 fsstate->next_tuple = 0;
1796 return;
1797 }
1798
1799 res = pgfdw_exec_query(fsstate->conn, sql, fsstate->conn_state);
1800 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1801 pgfdw_report_error(res, fsstate->conn, sql);
1802 PQclear(res);
1803
1804 /* Now force a fresh FETCH. */
1805 fsstate->tuples = NULL;
1806 fsstate->num_tuples = 0;
1807 fsstate->next_tuple = 0;
1808 fsstate->fetch_ct_2 = 0;
1809 fsstate->eof_reached = false;
1810}
1811
1812/*
1813 * postgresEndForeignScan
1814 * Finish scanning foreign table and dispose objects used for this scan
1815 */
1816static void
1818{
1819 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
1820
1821 /* if fsstate is NULL, we are in EXPLAIN; nothing to do */
1822 if (fsstate == NULL)
1823 return;
1824
1825 /* Close the cursor if open, to prevent accumulation of cursors */
1826 if (fsstate->cursor_exists)
1827 close_cursor(fsstate->conn, fsstate->cursor_number,
1828 fsstate->conn_state);
1829
1830 /* Release remote connection */
1831 ReleaseConnection(fsstate->conn);
1832 fsstate->conn = NULL;
1833
1834 /* MemoryContexts will be deleted automatically. */
1835}
1836
1837/*
1838 * postgresAddForeignUpdateTargets
1839 * Add resjunk column(s) needed for update/delete on a foreign table
1840 */
1841static void
1843 Index rtindex,
1844 RangeTblEntry *target_rte,
1846{
1847 Var *var;
1848
1849 /*
1850 * In postgres_fdw, what we need is the ctid, same as for a regular table.
1851 */
1852
1853 /* Make a Var representing the desired value */
1854 var = makeVar(rtindex,
1856 TIDOID,
1857 -1,
1858 InvalidOid,
1859 0);
1860
1861 /* Register it as a row-identity column needed by this target rel */
1862 add_row_identity_var(root, var, rtindex, "ctid");
1863}
1864
1865/*
1866 * postgresPlanForeignModify
1867 * Plan an insert/update/delete operation on a foreign table
1868 */
1869static List *
1872 Index resultRelation,
1873 int subplan_index)
1874{
1875 CmdType operation = plan->operation;
1876 RangeTblEntry *rte = planner_rt_fetch(resultRelation, root);
1877 Relation rel;
1878 StringInfoData sql;
1879 List *targetAttrs = NIL;
1881 List *returningList = NIL;
1882 List *retrieved_attrs = NIL;
1883 bool doNothing = false;
1884 int values_end_len = -1;
1885
1886 initStringInfo(&sql);
1887
1888 /*
1889 * Core code already has some lock on each rel being planned, so we can
1890 * use NoLock here.
1891 */
1892 rel = table_open(rte->relid, NoLock);
1893
1894 /*
1895 * In an INSERT, we transmit all columns that are defined in the foreign
1896 * table. In an UPDATE, if there are BEFORE ROW UPDATE triggers on the
1897 * foreign table, we transmit all columns like INSERT; else we transmit
1898 * only columns that were explicitly targets of the UPDATE, so as to avoid
1899 * unnecessary data transmission. (We can't do that for INSERT since we
1900 * would miss sending default values for columns not listed in the source
1901 * statement, and for UPDATE if there are BEFORE ROW UPDATE triggers since
1902 * those triggers might change values for non-target columns, in which
1903 * case we would miss sending changed values for those columns.)
1904 */
1905 if (operation == CMD_INSERT ||
1906 (operation == CMD_UPDATE &&
1907 rel->trigdesc &&
1909 {
1910 TupleDesc tupdesc = RelationGetDescr(rel);
1911 int attnum;
1912
1913 for (attnum = 1; attnum <= tupdesc->natts; attnum++)
1914 {
1915 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
1916
1917 if (!attr->attisdropped)
1919 }
1920 }
1921 else if (operation == CMD_UPDATE)
1922 {
1923 int col;
1924 RelOptInfo *rel = find_base_rel(root, resultRelation);
1926
1927 col = -1;
1928 while ((col = bms_next_member(allUpdatedCols, col)) >= 0)
1929 {
1930 /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
1932
1933 if (attno <= InvalidAttrNumber) /* shouldn't happen */
1934 elog(ERROR, "system-column update is not supported");
1936 }
1937 }
1938
1939 /*
1940 * Extract the relevant WITH CHECK OPTION list if any.
1941 */
1942 if (plan->withCheckOptionLists)
1943 withCheckOptionList = (List *) list_nth(plan->withCheckOptionLists,
1945
1946 /*
1947 * Extract the relevant RETURNING list if any.
1948 */
1949 if (plan->returningLists)
1950 returningList = (List *) list_nth(plan->returningLists, subplan_index);
1951
1952 /*
1953 * ON CONFLICT DO NOTHING/SELECT/UPDATE with inference specification
1954 * should have already been rejected in the optimizer, as presently there
1955 * is no way to recognize an arbiter index on a foreign table. Only DO
1956 * NOTHING is supported without an inference specification.
1957 */
1958 if (plan->onConflictAction == ONCONFLICT_NOTHING)
1959 doNothing = true;
1960 else if (plan->onConflictAction != ONCONFLICT_NONE)
1961 elog(ERROR, "unexpected ON CONFLICT specification: %d",
1962 (int) plan->onConflictAction);
1963
1964 /*
1965 * Construct the SQL command string.
1966 */
1967 switch (operation)
1968 {
1969 case CMD_INSERT:
1970 deparseInsertSql(&sql, rte, resultRelation, rel,
1972 withCheckOptionList, returningList,
1973 &retrieved_attrs, &values_end_len);
1974 break;
1975 case CMD_UPDATE:
1976 deparseUpdateSql(&sql, rte, resultRelation, rel,
1978 withCheckOptionList, returningList,
1979 &retrieved_attrs);
1980 break;
1981 case CMD_DELETE:
1982 deparseDeleteSql(&sql, rte, resultRelation, rel,
1983 returningList,
1984 &retrieved_attrs);
1985 break;
1986 default:
1987 elog(ERROR, "unexpected operation: %d", (int) operation);
1988 break;
1989 }
1990
1991 table_close(rel, NoLock);
1992
1993 /*
1994 * Build the fdw_private list that will be available to the executor.
1995 * Items in the list must match enum FdwModifyPrivateIndex, above.
1996 */
1997 return list_make5(makeString(sql.data),
2000 makeBoolean((retrieved_attrs != NIL)),
2001 retrieved_attrs);
2002}
2003
2004/*
2005 * postgresBeginForeignModify
2006 * Begin an insert/update/delete operation on a foreign table
2007 */
2008static void
2010 ResultRelInfo *resultRelInfo,
2011 List *fdw_private,
2012 int subplan_index,
2013 int eflags)
2014{
2016 char *query;
2017 List *target_attrs;
2018 bool has_returning;
2019 int values_end_len;
2020 List *retrieved_attrs;
2022
2023 /*
2024 * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
2025 * stays NULL.
2026 */
2027 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2028 return;
2029
2030 /* Deconstruct fdw_private data. */
2031 query = strVal(list_nth(fdw_private,
2033 target_attrs = (List *) list_nth(fdw_private,
2035 values_end_len = intVal(list_nth(fdw_private,
2037 has_returning = boolVal(list_nth(fdw_private,
2039 retrieved_attrs = (List *) list_nth(fdw_private,
2041
2042 /* Find RTE. */
2043 rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex,
2044 mtstate->ps.state);
2045
2046 /* Construct an execution state. */
2048 rte,
2049 resultRelInfo,
2050 mtstate->operation,
2051 outerPlanState(mtstate)->plan,
2052 query,
2053 target_attrs,
2055 has_returning,
2056 retrieved_attrs);
2057
2058 resultRelInfo->ri_FdwState = fmstate;
2059}
2060
2061/*
2062 * postgresExecForeignInsert
2063 * Insert one row into a foreign table
2064 */
2065static TupleTableSlot *
2067 ResultRelInfo *resultRelInfo,
2068 TupleTableSlot *slot,
2069 TupleTableSlot *planSlot)
2070{
2073 int numSlots = 1;
2074
2075 /*
2076 * If the fmstate has aux_fmstate set, use the aux_fmstate (see
2077 * postgresBeginForeignInsert())
2078 */
2079 if (fmstate->aux_fmstate)
2080 resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2081 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2082 &slot, &planSlot, &numSlots);
2083 /* Revert that change */
2084 if (fmstate->aux_fmstate)
2085 resultRelInfo->ri_FdwState = fmstate;
2086
2087 return rslot ? *rslot : NULL;
2088}
2089
2090/*
2091 * postgresExecForeignBatchInsert
2092 * Insert multiple rows into a foreign table
2093 */
2094static TupleTableSlot **
2096 ResultRelInfo *resultRelInfo,
2097 TupleTableSlot **slots,
2099 int *numSlots)
2100{
2103
2104 /*
2105 * If the fmstate has aux_fmstate set, use the aux_fmstate (see
2106 * postgresBeginForeignInsert())
2107 */
2108 if (fmstate->aux_fmstate)
2109 resultRelInfo->ri_FdwState = fmstate->aux_fmstate;
2110 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_INSERT,
2111 slots, planSlots, numSlots);
2112 /* Revert that change */
2113 if (fmstate->aux_fmstate)
2114 resultRelInfo->ri_FdwState = fmstate;
2115
2116 return rslot;
2117}
2118
2119/*
2120 * postgresGetForeignModifyBatchSize
2121 * Determine the maximum number of tuples that can be inserted in bulk
2122 *
2123 * Returns the batch size specified for server or table. When batching is not
2124 * allowed (e.g. for tables with BEFORE/AFTER ROW triggers or with RETURNING
2125 * clause), returns 1.
2126 */
2127static int
2129{
2130 int batch_size;
2132
2133 /* should be called only once */
2134 Assert(resultRelInfo->ri_BatchSize == 0);
2135
2136 /*
2137 * Should never get called when the insert is being performed on a table
2138 * that is also among the target relations of an UPDATE operation, because
2139 * postgresBeginForeignInsert() currently rejects such insert attempts.
2140 */
2141 Assert(fmstate == NULL || fmstate->aux_fmstate == NULL);
2142
2143 /*
2144 * In EXPLAIN without ANALYZE, ri_FdwState is NULL, so we have to lookup
2145 * the option directly in server/table options. Otherwise just use the
2146 * value we determined earlier.
2147 */
2148 if (fmstate)
2149 batch_size = fmstate->batch_size;
2150 else
2151 batch_size = get_batch_size_option(resultRelInfo->ri_RelationDesc);
2152
2153 /*
2154 * Disable batching when we have to use RETURNING, there are any
2155 * BEFORE/AFTER ROW INSERT triggers on the foreign table, or there are any
2156 * WITH CHECK OPTION constraints from parent views.
2157 *
2158 * When there are any BEFORE ROW INSERT triggers on the table, we can't
2159 * support it, because such triggers might query the table we're inserting
2160 * into and act differently if the tuples that have already been processed
2161 * and prepared for insertion are not there.
2162 */
2163 if (resultRelInfo->ri_projectReturning != NULL ||
2164 resultRelInfo->ri_WithCheckOptions != NIL ||
2165 (resultRelInfo->ri_TrigDesc &&
2166 (resultRelInfo->ri_TrigDesc->trig_insert_before_row ||
2167 resultRelInfo->ri_TrigDesc->trig_insert_after_row)))
2168 return 1;
2169
2170 /*
2171 * If the foreign table has no columns, disable batching as the INSERT
2172 * syntax doesn't allow batching multiple empty rows into a zero-column
2173 * table in a single statement. This is needed for COPY FROM, in which
2174 * case fmstate must be non-NULL.
2175 */
2176 if (fmstate && list_length(fmstate->target_attrs) == 0)
2177 return 1;
2178
2179 /*
2180 * Otherwise use the batch size specified for server/table. The number of
2181 * parameters in a batch is limited to 65535 (uint16), so make sure we
2182 * don't exceed this limit by using the maximum batch_size possible.
2183 */
2184 if (fmstate && fmstate->p_nums > 0)
2185 batch_size = Min(batch_size, PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);
2186
2187 return batch_size;
2188}
2189
2190/*
2191 * postgresExecForeignUpdate
2192 * Update one row in a foreign table
2193 */
2194static TupleTableSlot *
2196 ResultRelInfo *resultRelInfo,
2197 TupleTableSlot *slot,
2198 TupleTableSlot *planSlot)
2199{
2201 int numSlots = 1;
2202
2203 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_UPDATE,
2204 &slot, &planSlot, &numSlots);
2205
2206 return rslot ? rslot[0] : NULL;
2207}
2208
2209/*
2210 * postgresExecForeignDelete
2211 * Delete one row from a foreign table
2212 */
2213static TupleTableSlot *
2215 ResultRelInfo *resultRelInfo,
2216 TupleTableSlot *slot,
2217 TupleTableSlot *planSlot)
2218{
2220 int numSlots = 1;
2221
2222 rslot = execute_foreign_modify(estate, resultRelInfo, CMD_DELETE,
2223 &slot, &planSlot, &numSlots);
2224
2225 return rslot ? rslot[0] : NULL;
2226}
2227
2228/*
2229 * postgresEndForeignModify
2230 * Finish an insert/update/delete operation on a foreign table
2231 */
2232static void
2234 ResultRelInfo *resultRelInfo)
2235{
2237
2238 /* If fmstate is NULL, we are in EXPLAIN; nothing to do */
2239 if (fmstate == NULL)
2240 return;
2241
2242 /* Destroy the execution state */
2244}
2245
2246/*
2247 * postgresBeginForeignInsert
2248 * Begin an insert operation on a foreign table
2249 */
2250static void
2252 ResultRelInfo *resultRelInfo)
2253{
2256 EState *estate = mtstate->ps.state;
2257 Index resultRelation;
2258 Relation rel = resultRelInfo->ri_RelationDesc;
2260 TupleDesc tupdesc = RelationGetDescr(rel);
2261 int attnum;
2262 int values_end_len;
2263 StringInfoData sql;
2264 List *targetAttrs = NIL;
2265 List *retrieved_attrs = NIL;
2266 bool doNothing = false;
2267
2268 /*
2269 * If the foreign table we are about to insert routed rows into is also an
2270 * UPDATE subplan result rel that will be updated later, proceeding with
2271 * the INSERT will result in the later UPDATE incorrectly modifying those
2272 * routed rows, so prevent the INSERT --- it would be nice if we could
2273 * handle this case; but for now, throw an error for safety.
2274 */
2275 if (plan && plan->operation == CMD_UPDATE &&
2276 (resultRelInfo->ri_usesFdwDirectModify ||
2277 resultRelInfo->ri_FdwState))
2278 ereport(ERROR,
2280 errmsg("cannot route tuples into foreign table to be updated \"%s\"",
2282
2283 initStringInfo(&sql);
2284
2285 /* We transmit all columns that are defined in the foreign table. */
2286 for (attnum = 1; attnum <= tupdesc->natts; attnum++)
2287 {
2288 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
2289
2290 if (!attr->attisdropped)
2292 }
2293
2294 /* Check if we add the ON CONFLICT clause to the remote query. */
2295 if (plan)
2296 {
2297 OnConflictAction onConflictAction = plan->onConflictAction;
2298
2299 /* We only support DO NOTHING without an inference specification. */
2300 if (onConflictAction == ONCONFLICT_NOTHING)
2301 doNothing = true;
2302 else if (onConflictAction != ONCONFLICT_NONE)
2303 elog(ERROR, "unexpected ON CONFLICT specification: %d",
2304 (int) onConflictAction);
2305 }
2306
2307 /*
2308 * If the foreign table is a partition that doesn't have a corresponding
2309 * RTE entry, we need to create a new RTE describing the foreign table for
2310 * use by deparseInsertSql and create_foreign_modify() below, after first
2311 * copying the parent's RTE and modifying some fields to describe the
2312 * foreign partition to work on. However, if this is invoked by UPDATE,
2313 * the existing RTE may already correspond to this partition if it is one
2314 * of the UPDATE subplan target rels; in that case, we can just use the
2315 * existing RTE as-is.
2316 */
2317 if (resultRelInfo->ri_RangeTableIndex == 0)
2318 {
2319 ResultRelInfo *rootResultRelInfo = resultRelInfo->ri_RootResultRelInfo;
2320
2321 rte = exec_rt_fetch(rootResultRelInfo->ri_RangeTableIndex, estate);
2322 rte = copyObject(rte);
2323 rte->relid = RelationGetRelid(rel);
2324 rte->relkind = RELKIND_FOREIGN_TABLE;
2325
2326 /*
2327 * For UPDATE, we must use the RT index of the first subplan target
2328 * rel's RTE, because the core code would have built expressions for
2329 * the partition, such as RETURNING, using that RT index as varno of
2330 * Vars contained in those expressions.
2331 */
2332 if (plan && plan->operation == CMD_UPDATE &&
2333 rootResultRelInfo->ri_RangeTableIndex == plan->rootRelation)
2334 resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
2335 else
2336 resultRelation = rootResultRelInfo->ri_RangeTableIndex;
2337 }
2338 else
2339 {
2340 resultRelation = resultRelInfo->ri_RangeTableIndex;
2341 rte = exec_rt_fetch(resultRelation, estate);
2342 }
2343
2344 /* Construct the SQL command string. */
2345 deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing,
2346 resultRelInfo->ri_WithCheckOptions,
2347 resultRelInfo->ri_returningList,
2348 &retrieved_attrs, &values_end_len);
2349
2350 /* Construct an execution state. */
2352 rte,
2353 resultRelInfo,
2354 CMD_INSERT,
2355 NULL,
2356 sql.data,
2359 retrieved_attrs != NIL,
2360 retrieved_attrs);
2361
2362 /*
2363 * If the given resultRelInfo already has PgFdwModifyState set, it means
2364 * the foreign table is an UPDATE subplan result rel; in which case, store
2365 * the resulting state into the aux_fmstate of the PgFdwModifyState.
2366 */
2367 if (resultRelInfo->ri_FdwState)
2368 {
2369 Assert(plan && plan->operation == CMD_UPDATE);
2370 Assert(resultRelInfo->ri_usesFdwDirectModify == false);
2371 ((PgFdwModifyState *) resultRelInfo->ri_FdwState)->aux_fmstate = fmstate;
2372 }
2373 else
2374 resultRelInfo->ri_FdwState = fmstate;
2375}
2376
2377/*
2378 * postgresEndForeignInsert
2379 * Finish an insert operation on a foreign table
2380 */
2381static void
2383 ResultRelInfo *resultRelInfo)
2384{
2386
2387 Assert(fmstate != NULL);
2388
2389 /*
2390 * If the fmstate has aux_fmstate set, get the aux_fmstate (see
2391 * postgresBeginForeignInsert())
2392 */
2393 if (fmstate->aux_fmstate)
2394 fmstate = fmstate->aux_fmstate;
2395
2396 /* Destroy the execution state */
2398}
2399
2400/*
2401 * postgresIsForeignRelUpdatable
2402 * Determine whether a foreign table supports INSERT, UPDATE and/or
2403 * DELETE.
2404 */
2405static int
2407{
2408 bool updatable;
2410 ForeignServer *server;
2411 ListCell *lc;
2412
2413 /*
2414 * By default, all postgres_fdw foreign tables are assumed updatable. This
2415 * can be overridden by a per-server setting, which in turn can be
2416 * overridden by a per-table setting.
2417 */
2418 updatable = true;
2419
2421 server = GetForeignServer(table->serverid);
2422
2423 foreach(lc, server->options)
2424 {
2425 DefElem *def = (DefElem *) lfirst(lc);
2426
2427 if (strcmp(def->defname, "updatable") == 0)
2428 updatable = defGetBoolean(def);
2429 }
2430 foreach(lc, table->options)
2431 {
2432 DefElem *def = (DefElem *) lfirst(lc);
2433
2434 if (strcmp(def->defname, "updatable") == 0)
2435 updatable = defGetBoolean(def);
2436 }
2437
2438 /*
2439 * Currently "updatable" means support for INSERT, UPDATE and DELETE.
2440 */
2441 return updatable ?
2442 (1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE) : 0;
2443}
2444
2445/*
2446 * postgresRecheckForeignScan
2447 * Execute a local join execution plan for a foreign join
2448 */
2449static bool
2451{
2452 Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
2455
2456 /* For base foreign relations, it suffices to set fdw_recheck_quals */
2457 if (scanrelid > 0)
2458 return true;
2459
2460 Assert(outerPlan != NULL);
2461
2462 /* Execute a local join execution plan */
2464 if (TupIsNull(result))
2465 return false;
2466
2467 /* Store result in the given slot */
2468 ExecCopySlot(slot, result);
2469
2470 return true;
2471}
2472
2473/*
2474 * find_modifytable_subplan
2475 * Helper routine for postgresPlanDirectModify to find the
2476 * ModifyTable subplan node that scans the specified RTI.
2477 *
2478 * Returns NULL if the subplan couldn't be identified. That's not a fatal
2479 * error condition, we just abandon trying to do the update directly.
2480 */
2481static ForeignScan *
2484 Index rtindex,
2485 int subplan_index)
2486{
2487 Plan *subplan = outerPlan(plan);
2488
2489 /*
2490 * The cases we support are (1) the desired ForeignScan is the immediate
2491 * child of ModifyTable, or (2) it is the subplan_index'th child of an
2492 * Append node that is the immediate child of ModifyTable. There is no
2493 * point in looking further down, as that would mean that local joins are
2494 * involved, so we can't do the update directly.
2495 *
2496 * There could be a Result atop the Append too, acting to compute the
2497 * UPDATE targetlist values. We ignore that here; the tlist will be
2498 * checked by our caller.
2499 *
2500 * In principle we could examine all the children of the Append, but it's
2501 * currently unlikely that the core planner would generate such a plan
2502 * with the children out-of-order. Moreover, such a search risks costing
2503 * O(N^2) time when there are a lot of children.
2504 */
2505 if (IsA(subplan, Append))
2506 {
2507 Append *appendplan = (Append *) subplan;
2508
2509 if (subplan_index < list_length(appendplan->appendplans))
2510 subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2511 }
2512 else if (IsA(subplan, Result) &&
2513 outerPlan(subplan) != NULL &&
2514 IsA(outerPlan(subplan), Append))
2515 {
2516 Append *appendplan = (Append *) outerPlan(subplan);
2517
2518 if (subplan_index < list_length(appendplan->appendplans))
2519 subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index);
2520 }
2521
2522 /* Now, have we got a ForeignScan on the desired rel? */
2523 if (IsA(subplan, ForeignScan))
2524 {
2525 ForeignScan *fscan = (ForeignScan *) subplan;
2526
2527 if (bms_is_member(rtindex, fscan->fs_base_relids))
2528 return fscan;
2529 }
2530
2531 return NULL;
2532}
2533
2534/*
2535 * postgresPlanDirectModify
2536 * Consider a direct foreign table modification
2537 *
2538 * Decide whether it is safe to modify a foreign table directly, and if so,
2539 * rewrite subplan accordingly.
2540 */
2541static bool
2544 Index resultRelation,
2545 int subplan_index)
2546{
2547 CmdType operation = plan->operation;
2548 RelOptInfo *foreignrel;
2551 Relation rel;
2552 StringInfoData sql;
2554 List *processed_tlist = NIL;
2555 List *targetAttrs = NIL;
2557 List *params_list = NIL;
2558 List *returningList = NIL;
2559 List *retrieved_attrs = NIL;
2560
2561 /*
2562 * Decide whether it is safe to modify a foreign table directly.
2563 */
2564
2565 /*
2566 * The table modification must be an UPDATE or DELETE.
2567 */
2569 return false;
2570
2571 /*
2572 * Try to locate the ForeignScan subplan that's scanning resultRelation.
2573 */
2575 if (!fscan)
2576 return false;
2577
2578 /*
2579 * It's unsafe to modify a foreign table directly if there are any quals
2580 * that should be evaluated locally.
2581 */
2582 if (fscan->scan.plan.qual != NIL)
2583 return false;
2584
2585 /* Safe to fetch data about the target foreign rel */
2586 if (fscan->scan.scanrelid == 0)
2587 {
2588 foreignrel = find_join_rel(root, fscan->fs_relids);
2589 /* We should have a rel for this foreign join. */
2590 Assert(foreignrel);
2591 }
2592 else
2593 foreignrel = root->simple_rel_array[resultRelation];
2594 rte = root->simple_rte_array[resultRelation];
2595 fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2596
2597 /*
2598 * It's unsafe to update a foreign table directly, if any expressions to
2599 * assign to the target columns are unsafe to evaluate remotely.
2600 */
2601 if (operation == CMD_UPDATE)
2602 {
2603 ListCell *lc,
2604 *lc2;
2605
2606 /*
2607 * The expressions of concern are the first N columns of the processed
2608 * targetlist, where N is the length of the rel's update_colnos.
2609 */
2610 get_translated_update_targetlist(root, resultRelation,
2611 &processed_tlist, &targetAttrs);
2612 forboth(lc, processed_tlist, lc2, targetAttrs)
2613 {
2615 AttrNumber attno = lfirst_int(lc2);
2616
2617 /* update's new-value expressions shouldn't be resjunk */
2618 Assert(!tle->resjunk);
2619
2620 if (attno <= InvalidAttrNumber) /* shouldn't happen */
2621 elog(ERROR, "system-column update is not supported");
2622
2623 if (!is_foreign_expr(root, foreignrel, (Expr *) tle->expr))
2624 return false;
2625 }
2626 }
2627
2628 /*
2629 * Ok, rewrite subplan so as to modify the foreign table directly.
2630 */
2631 initStringInfo(&sql);
2632
2633 /*
2634 * Core code already has some lock on each rel being planned, so we can
2635 * use NoLock here.
2636 */
2637 rel = table_open(rte->relid, NoLock);
2638
2639 /*
2640 * Recall the qual clauses that must be evaluated remotely. (These are
2641 * bare clauses not RestrictInfos, but deparse.c's appendConditions()
2642 * doesn't care.)
2643 */
2644 remote_exprs = fpinfo->final_remote_exprs;
2645
2646 /*
2647 * Extract the relevant RETURNING list if any.
2648 */
2649 if (plan->returningLists)
2650 {
2651 returningList = (List *) list_nth(plan->returningLists, subplan_index);
2652
2653 /*
2654 * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2655 * we fetch from the foreign server any Vars specified in RETURNING
2656 * that refer not only to the target relation but to non-target
2657 * relations. So we'll deparse them into the RETURNING clause of the
2658 * remote query; use a targetlist consisting of them instead, which
2659 * will be adjusted to be new fdw_scan_tlist of the foreign-scan plan
2660 * node below.
2661 */
2662 if (fscan->scan.scanrelid == 0)
2663 returningList = build_remote_returning(resultRelation, rel,
2664 returningList);
2665 }
2666
2667 /*
2668 * Construct the SQL command string.
2669 */
2670 switch (operation)
2671 {
2672 case CMD_UPDATE:
2673 deparseDirectUpdateSql(&sql, root, resultRelation, rel,
2674 foreignrel,
2675 processed_tlist,
2677 remote_exprs, &params_list,
2678 returningList, &retrieved_attrs);
2679 break;
2680 case CMD_DELETE:
2681 deparseDirectDeleteSql(&sql, root, resultRelation, rel,
2682 foreignrel,
2683 remote_exprs, &params_list,
2684 returningList, &retrieved_attrs);
2685 break;
2686 default:
2687 elog(ERROR, "unexpected operation: %d", (int) operation);
2688 break;
2689 }
2690
2691 /*
2692 * Update the operation and target relation info.
2693 */
2694 fscan->operation = operation;
2695 fscan->resultRelation = resultRelation;
2696
2697 /*
2698 * Update the fdw_exprs list that will be available to the executor.
2699 */
2700 fscan->fdw_exprs = params_list;
2701
2702 /*
2703 * Update the fdw_private list that will be available to the executor.
2704 * Items in the list must match enum FdwDirectModifyPrivateIndex, above.
2705 */
2706 fscan->fdw_private = list_make4(makeString(sql.data),
2707 makeBoolean((retrieved_attrs != NIL)),
2708 retrieved_attrs,
2709 makeBoolean(plan->canSetTag));
2710
2711 /*
2712 * Update the foreign-join-related fields.
2713 */
2714 if (fscan->scan.scanrelid == 0)
2715 {
2716 /* No need for the outer subplan. */
2717 fscan->scan.plan.lefttree = NULL;
2718
2719 /* Build new fdw_scan_tlist if UPDATE/DELETE .. RETURNING. */
2720 if (returningList)
2721 rebuild_fdw_scan_tlist(fscan, returningList);
2722 }
2723
2724 /*
2725 * Finally, unset the async-capable flag if it is set, as we currently
2726 * don't support asynchronous execution of direct modifications.
2727 */
2728 if (fscan->scan.plan.async_capable)
2729 fscan->scan.plan.async_capable = false;
2730
2731 table_close(rel, NoLock);
2732 return true;
2733}
2734
2735/*
2736 * postgresBeginDirectModify
2737 * Prepare a direct foreign table modification
2738 */
2739static void
2741{
2742 ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
2743 EState *estate = node->ss.ps.state;
2745 Index rtindex;
2746 Oid userid;
2749 int numParams;
2750
2751 /*
2752 * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
2753 */
2754 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
2755 return;
2756
2757 /*
2758 * We'll save private state in node->fdw_state.
2759 */
2761 node->fdw_state = dmstate;
2762
2763 /*
2764 * Identify which user to do the remote access as. This should match what
2765 * ExecCheckPermissions() does.
2766 */
2767 userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
2768
2769 /* Get info about foreign table. */
2770 rtindex = node->resultRelInfo->ri_RangeTableIndex;
2771 if (fsplan->scan.scanrelid == 0)
2772 dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags);
2773 else
2774 dmstate->rel = node->ss.ss_currentRelation;
2776 user = GetUserMapping(userid, table->serverid);
2777
2778 /*
2779 * Get connection to the foreign server. Connection manager will
2780 * establish new connection if necessary.
2781 */
2782 dmstate->conn = GetConnection(user, false, &dmstate->conn_state);
2783
2784 /* Update the foreign-join-related fields. */
2785 if (fsplan->scan.scanrelid == 0)
2786 {
2787 /* Save info about foreign table. */
2788 dmstate->resultRel = dmstate->rel;
2789
2790 /*
2791 * Set dmstate->rel to NULL to teach get_returning_data() and
2792 * make_tuple_from_result_row() that columns fetched from the remote
2793 * server are described by fdw_scan_tlist of the foreign-scan plan
2794 * node, not the tuple descriptor for the target relation.
2795 */
2796 dmstate->rel = NULL;
2797 }
2798
2799 /* Initialize state variable */
2800 dmstate->num_tuples = -1; /* -1 means not set yet */
2801
2802 /* Get private info created by planner functions. */
2803 dmstate->query = strVal(list_nth(fsplan->fdw_private,
2805 dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
2807 dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
2809 dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
2811
2812 /* Create context for per-tuple temp workspace. */
2813 dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
2814 "postgres_fdw temporary data",
2816
2817 /* Prepare for input conversion of RETURNING results. */
2818 if (dmstate->has_returning)
2819 {
2820 TupleDesc tupdesc;
2821
2822 if (fsplan->scan.scanrelid == 0)
2823 tupdesc = get_tupdesc_for_join_scan_tuples(node);
2824 else
2825 tupdesc = RelationGetDescr(dmstate->rel);
2826
2827 dmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
2828
2829 /*
2830 * When performing an UPDATE/DELETE .. RETURNING on a join directly,
2831 * initialize a filter to extract an updated/deleted tuple from a scan
2832 * tuple.
2833 */
2834 if (fsplan->scan.scanrelid == 0)
2835 init_returning_filter(dmstate, fsplan->fdw_scan_tlist, rtindex);
2836 }
2837
2838 /*
2839 * Prepare for processing of parameters used in remote query, if any.
2840 */
2841 numParams = list_length(fsplan->fdw_exprs);
2842 dmstate->numParams = numParams;
2843 if (numParams > 0)
2845 fsplan->fdw_exprs,
2846 numParams,
2847 &dmstate->param_flinfo,
2848 &dmstate->param_exprs,
2849 &dmstate->param_values);
2850}
2851
2852/*
2853 * postgresIterateDirectModify
2854 * Execute a direct foreign table modification
2855 */
2856static TupleTableSlot *
2858{
2860 EState *estate = node->ss.ps.state;
2861 ResultRelInfo *resultRelInfo = node->resultRelInfo;
2862
2863 /*
2864 * If this is the first call after Begin, execute the statement.
2865 */
2866 if (dmstate->num_tuples == -1)
2867 execute_dml_stmt(node);
2868
2869 /*
2870 * If the local query doesn't specify RETURNING, just clear tuple slot.
2871 */
2872 if (!resultRelInfo->ri_projectReturning)
2873 {
2874 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
2875 NodeInstrumentation *instr = node->ss.ps.instrument;
2876
2877 Assert(!dmstate->has_returning);
2878
2879 /* Increment the command es_processed count if necessary. */
2880 if (dmstate->set_processed)
2881 estate->es_processed += dmstate->num_tuples;
2882
2883 /* Increment the tuple count for EXPLAIN ANALYZE if necessary. */
2884 if (instr)
2885 instr->tuplecount += dmstate->num_tuples;
2886
2887 return ExecClearTuple(slot);
2888 }
2889
2890 /*
2891 * Get the next RETURNING tuple.
2892 */
2893 return get_returning_data(node);
2894}
2895
2896/*
2897 * postgresEndDirectModify
2898 * Finish a direct foreign table modification
2899 */
2900static void
2902{
2904
2905 /* if dmstate is NULL, we are in EXPLAIN; nothing to do */
2906 if (dmstate == NULL)
2907 return;
2908
2909 /* Release PGresult */
2910 PQclear(dmstate->result);
2911
2912 /* Release remote connection */
2914 dmstate->conn = NULL;
2915
2916 /* MemoryContext will be deleted automatically. */
2917}
2918
2919/*
2920 * postgresExplainForeignScan
2921 * Produce extra output for EXPLAIN of a ForeignScan on a foreign table
2922 */
2923static void
2925{
2927 List *fdw_private = plan->fdw_private;
2928
2929 /*
2930 * Identify foreign scans that are really joins or upper relations. The
2931 * input looks something like "(1) LEFT JOIN (2)", and we must replace the
2932 * digit string(s), which are RT indexes, with the correct relation names.
2933 * We do that here, not when the plan is created, because we can't know
2934 * what aliases ruleutils.c will assign at plan creation time.
2935 */
2936 if (list_length(fdw_private) > FdwScanPrivateRelations)
2937 {
2938 StringInfoData relations;
2939 char *rawrelations;
2940 char *ptr;
2941 int minrti,
2942 rtoffset;
2943
2945
2946 /*
2947 * A difficulty with using a string representation of RT indexes is
2948 * that setrefs.c won't update the string when flattening the
2949 * rangetable. To find out what rtoffset was applied, identify the
2950 * minimum RT index appearing in the string and compare it to the
2951 * minimum member of plan->fs_base_relids. (We expect all the relids
2952 * in the join will have been offset by the same amount; the Asserts
2953 * below should catch it if that ever changes.)
2954 */
2955 minrti = INT_MAX;
2956 ptr = rawrelations;
2957 while (*ptr)
2958 {
2959 if (isdigit((unsigned char) *ptr))
2960 {
2961 int rti = strtol(ptr, &ptr, 10);
2962
2963 if (rti < minrti)
2964 minrti = rti;
2965 }
2966 else
2967 ptr++;
2968 }
2969 rtoffset = bms_next_member(plan->fs_base_relids, -1) - minrti;
2970
2971 /* Now we can translate the string */
2972 initStringInfo(&relations);
2973 ptr = rawrelations;
2974 while (*ptr)
2975 {
2976 if (isdigit((unsigned char) *ptr))
2977 {
2978 int rti = strtol(ptr, &ptr, 10);
2980 char *relname;
2981 char *refname;
2982
2983 rti += rtoffset;
2984 Assert(bms_is_member(rti, plan->fs_base_relids));
2985 rte = rt_fetch(rti, es->rtable);
2986 Assert(rte->rtekind == RTE_RELATION);
2987 /* This logic should agree with explain.c's ExplainTargetRel */
2988 relname = get_rel_name(rte->relid);
2989 if (es->verbose)
2990 {
2991 char *namespace;
2992
2994 appendStringInfo(&relations, "%s.%s",
2995 quote_identifier(namespace),
2997 }
2998 else
2999 appendStringInfoString(&relations,
3001 refname = (char *) list_nth(es->rtable_names, rti - 1);
3002 if (refname == NULL)
3003 refname = rte->eref->aliasname;
3004 if (strcmp(refname, relname) != 0)
3005 appendStringInfo(&relations, " %s",
3006 quote_identifier(refname));
3007 }
3008 else
3009 appendStringInfoChar(&relations, *ptr++);
3010 }
3011 ExplainPropertyText("Relations", relations.data, es);
3012 }
3013
3014 /*
3015 * Add remote query, when VERBOSE option is specified.
3016 */
3017 if (es->verbose)
3018 {
3019 char *sql;
3020
3021 sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
3022 ExplainPropertyText("Remote SQL", sql, es);
3023 }
3024}
3025
3026/*
3027 * postgresExplainForeignModify
3028 * Produce extra output for EXPLAIN of a ModifyTable on a foreign table
3029 */
3030static void
3032 ResultRelInfo *rinfo,
3033 List *fdw_private,
3034 int subplan_index,
3035 ExplainState *es)
3036{
3037 if (es->verbose)
3038 {
3039 char *sql = strVal(list_nth(fdw_private,
3041
3042 ExplainPropertyText("Remote SQL", sql, es);
3043
3044 /*
3045 * For INSERT we should always have batch size >= 1, but UPDATE and
3046 * DELETE don't support batching so don't show the property.
3047 */
3048 if (rinfo->ri_BatchSize > 0)
3049 ExplainPropertyInteger("Batch Size", NULL, rinfo->ri_BatchSize, es);
3050 }
3051}
3052
3053/*
3054 * postgresExplainDirectModify
3055 * Produce extra output for EXPLAIN of a ForeignScan that modifies a
3056 * foreign table directly
3057 */
3058static void
3060{
3061 List *fdw_private;
3062 char *sql;
3063
3064 if (es->verbose)
3065 {
3066 fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
3067 sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql));
3068 ExplainPropertyText("Remote SQL", sql, es);
3069 }
3070}
3071
3072/*
3073 * postgresExecForeignTruncate
3074 * Truncate one or more foreign tables
3075 */
3076static void
3078 DropBehavior behavior,
3079 bool restart_seqs)
3080{
3081 Oid serverid = InvalidOid;
3083 PGconn *conn = NULL;
3084 StringInfoData sql;
3085 ListCell *lc;
3086 bool server_truncatable = true;
3087
3088 /*
3089 * By default, all postgres_fdw foreign tables are assumed truncatable.
3090 * This can be overridden by a per-server setting, which in turn can be
3091 * overridden by a per-table setting.
3092 */
3093 foreach(lc, rels)
3094 {
3095 ForeignServer *server = NULL;
3096 Relation rel = lfirst(lc);
3098 ListCell *cell;
3099 bool truncatable;
3100
3101 /*
3102 * First time through, determine whether the foreign server allows
3103 * truncates. Since all specified foreign tables are assumed to belong
3104 * to the same foreign server, this result can be used for other
3105 * foreign tables.
3106 */
3107 if (!OidIsValid(serverid))
3108 {
3109 serverid = table->serverid;
3110 server = GetForeignServer(serverid);
3111
3112 foreach(cell, server->options)
3113 {
3114 DefElem *defel = (DefElem *) lfirst(cell);
3115
3116 if (strcmp(defel->defname, "truncatable") == 0)
3117 {
3119 break;
3120 }
3121 }
3122 }
3123
3124 /*
3125 * Confirm that all specified foreign tables belong to the same
3126 * foreign server.
3127 */
3128 Assert(table->serverid == serverid);
3129
3130 /* Determine whether this foreign table allows truncations */
3132 foreach(cell, table->options)
3133 {
3134 DefElem *defel = (DefElem *) lfirst(cell);
3135
3136 if (strcmp(defel->defname, "truncatable") == 0)
3137 {
3139 break;
3140 }
3141 }
3142
3143 if (!truncatable)
3144 ereport(ERROR,
3146 errmsg("foreign table \"%s\" does not allow truncates",
3148 }
3149 Assert(OidIsValid(serverid));
3150
3151 /*
3152 * Get connection to the foreign server. Connection manager will
3153 * establish new connection if necessary.
3154 */
3155 user = GetUserMapping(GetUserId(), serverid);
3156 conn = GetConnection(user, false, NULL);
3157
3158 /* Construct the TRUNCATE command string */
3159 initStringInfo(&sql);
3160 deparseTruncateSql(&sql, rels, behavior, restart_seqs);
3161
3162 /* Issue the TRUNCATE command to remote server */
3163 do_sql_command(conn, sql.data);
3164
3165 pfree(sql.data);
3166}
3167
3168/*
3169 * estimate_path_cost_size
3170 * Get cost and size estimates for a foreign scan on given foreign relation
3171 * either a base relation or a join between foreign relations or an upper
3172 * relation containing foreign relations.
3173 *
3174 * param_join_conds are the parameterization clauses with outer relations.
3175 * pathkeys specify the expected sort order if any for given path being costed.
3176 * fpextra specifies additional post-scan/join-processing steps such as the
3177 * final sort and the LIMIT restriction.
3178 *
3179 * The function returns the cost and size estimates in p_rows, p_width,
3180 * p_disabled_nodes, p_startup_cost and p_total_cost variables.
3181 */
3182static void
3184 RelOptInfo *foreignrel,
3186 List *pathkeys,
3188 double *p_rows, int *p_width,
3189 int *p_disabled_nodes,
3191{
3192 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
3193 double rows;
3194 double retrieved_rows;
3195 int width;
3196 int disabled_nodes = 0;
3197 Cost startup_cost;
3198 Cost total_cost;
3199
3200 /* Make sure the core code has set up the relation's reltarget */
3201 Assert(foreignrel->reltarget);
3202
3203 /*
3204 * If the table or the server is configured to use remote estimates,
3205 * connect to the foreign server and execute EXPLAIN to estimate the
3206 * number of rows selected by the restriction+join clauses. Otherwise,
3207 * estimate rows using whatever statistics we have locally, in a way
3208 * similar to ordinary tables.
3209 */
3210 if (fpinfo->use_remote_estimate)
3211 {
3214 StringInfoData sql;
3215 PGconn *conn;
3218 List *fdw_scan_tlist = NIL;
3219 List *remote_conds;
3220
3221 /* Required only to be passed to deparseSelectStmtForRel */
3222 List *retrieved_attrs;
3223
3224 /*
3225 * param_join_conds might contain both clauses that are safe to send
3226 * across, and clauses that aren't.
3227 */
3230
3231 /* Build the list of columns to be fetched from the foreign server. */
3232 if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
3233 fdw_scan_tlist = build_tlist_to_deparse(foreignrel);
3234 else
3235 fdw_scan_tlist = NIL;
3236
3237 /*
3238 * The complete list of remote conditions includes everything from
3239 * baserestrictinfo plus any extra join_conds relevant to this
3240 * particular path.
3241 */
3242 remote_conds = list_concat(remote_param_join_conds,
3243 fpinfo->remote_conds);
3244
3245 /*
3246 * Construct EXPLAIN query including the desired SELECT, FROM, and
3247 * WHERE clauses. Params and other-relation Vars are replaced by dummy
3248 * values, so don't request params_list.
3249 */
3250 initStringInfo(&sql);
3251 appendStringInfoString(&sql, "EXPLAIN ");
3252 deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist,
3253 remote_conds, pathkeys,
3254 fpextra ? fpextra->has_final_sort : false,
3255 fpextra ? fpextra->has_limit : false,
3256 false, &retrieved_attrs, NULL);
3257
3258 /* Get the remote estimate */
3259 conn = GetConnection(fpinfo->user, false, NULL);
3260 get_remote_estimate(sql.data, conn, &rows, &width,
3261 &startup_cost, &total_cost);
3263
3264 retrieved_rows = rows;
3265
3266 /* Factor in the selectivity of the locally-checked quals */
3269 foreignrel->relid,
3270 JOIN_INNER,
3271 NULL);
3272 local_sel *= fpinfo->local_conds_sel;
3273
3274 rows = clamp_row_est(rows * local_sel);
3275
3276 /* Add in the eval cost of the locally-checked quals */
3277 startup_cost += fpinfo->local_conds_cost.startup;
3278 total_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3280 startup_cost += local_cost.startup;
3281 total_cost += local_cost.per_tuple * retrieved_rows;
3282
3283 /*
3284 * Add in tlist eval cost for each output row. In case of an
3285 * aggregate, some of the tlist expressions such as grouping
3286 * expressions will be evaluated remotely, so adjust the costs.
3287 */
3288 startup_cost += foreignrel->reltarget->cost.startup;
3289 total_cost += foreignrel->reltarget->cost.startup;
3290 total_cost += foreignrel->reltarget->cost.per_tuple * rows;
3291 if (IS_UPPER_REL(foreignrel))
3292 {
3294
3295 cost_qual_eval(&tlist_cost, fdw_scan_tlist, root);
3296 startup_cost -= tlist_cost.startup;
3297 total_cost -= tlist_cost.startup;
3298 total_cost -= tlist_cost.per_tuple * rows;
3299 }
3300 }
3301 else
3302 {
3303 Cost run_cost = 0;
3304
3305 /*
3306 * We don't support join conditions in this mode (hence, no
3307 * parameterized paths can be made).
3308 */
3310
3311 /*
3312 * We will come here again and again with different set of pathkeys or
3313 * additional post-scan/join-processing steps that caller wants to
3314 * cost. We don't need to calculate the cost/size estimates for the
3315 * underlying scan, join, or grouping each time. Instead, use those
3316 * estimates if we have cached them already.
3317 */
3318 if (fpinfo->rel_startup_cost >= 0 && fpinfo->rel_total_cost >= 0)
3319 {
3320 Assert(fpinfo->retrieved_rows >= 0);
3321
3322 rows = fpinfo->rows;
3323 retrieved_rows = fpinfo->retrieved_rows;
3324 width = fpinfo->width;
3325 startup_cost = fpinfo->rel_startup_cost;
3326 run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
3327
3328 /*
3329 * If we estimate the costs of a foreign scan or a foreign join
3330 * with additional post-scan/join-processing steps, the scan or
3331 * join costs obtained from the cache wouldn't yet contain the
3332 * eval costs for the final scan/join target, which would've been
3333 * updated by apply_scanjoin_target_to_paths(); add the eval costs
3334 * now.
3335 */
3336 if (fpextra && !IS_UPPER_REL(foreignrel))
3337 {
3338 /* Shouldn't get here unless we have LIMIT */
3339 Assert(fpextra->has_limit);
3340 Assert(foreignrel->reloptkind == RELOPT_BASEREL ||
3341 foreignrel->reloptkind == RELOPT_JOINREL);
3342 startup_cost += foreignrel->reltarget->cost.startup;
3343 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3344 }
3345 }
3346 else if (IS_JOIN_REL(foreignrel))
3347 {
3352 double nrows;
3353
3354 /* Use rows/width estimates made by the core code. */
3355 rows = foreignrel->rows;
3356 width = foreignrel->reltarget->width;
3357
3358 /* For join we expect inner and outer relations set */
3359 Assert(fpinfo->innerrel && fpinfo->outerrel);
3360
3361 fpinfo_i = (PgFdwRelationInfo *) fpinfo->innerrel->fdw_private;
3362 fpinfo_o = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
3363
3364 /* Estimate of number of rows in cross product */
3365 nrows = fpinfo_i->rows * fpinfo_o->rows;
3366
3367 /*
3368 * Back into an estimate of the number of retrieved rows. Just in
3369 * case this is nuts, clamp to at most nrows.
3370 */
3371 retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3372 retrieved_rows = Min(retrieved_rows, nrows);
3373
3374 /*
3375 * The cost of foreign join is estimated as cost of generating
3376 * rows for the joining relations + cost for applying quals on the
3377 * rows.
3378 */
3379
3380 /*
3381 * Calculate the cost of clauses pushed down to the foreign server
3382 */
3383 cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root);
3384 /* Calculate the cost of applying join clauses */
3385 cost_qual_eval(&join_cost, fpinfo->joinclauses, root);
3386
3387 /*
3388 * Startup cost includes startup cost of joining relations and the
3389 * startup cost for join and other clauses. We do not include the
3390 * startup cost specific to join strategy (e.g. setting up hash
3391 * tables) since we do not know what strategy the foreign server
3392 * is going to use.
3393 */
3394 startup_cost = fpinfo_i->rel_startup_cost + fpinfo_o->rel_startup_cost;
3395 startup_cost += join_cost.startup;
3396 startup_cost += remote_conds_cost.startup;
3397 startup_cost += fpinfo->local_conds_cost.startup;
3398
3399 /*
3400 * Run time cost includes:
3401 *
3402 * 1. Run time cost (total_cost - startup_cost) of relations being
3403 * joined
3404 *
3405 * 2. Run time cost of applying join clauses on the cross product
3406 * of the joining relations.
3407 *
3408 * 3. Run time cost of applying pushed down other clauses on the
3409 * result of join
3410 *
3411 * 4. Run time cost of applying nonpushable other clauses locally
3412 * on the result fetched from the foreign server.
3413 */
3414 run_cost = fpinfo_i->rel_total_cost - fpinfo_i->rel_startup_cost;
3415 run_cost += fpinfo_o->rel_total_cost - fpinfo_o->rel_startup_cost;
3416 run_cost += nrows * join_cost.per_tuple;
3417 nrows = clamp_row_est(nrows * fpinfo->joinclause_sel);
3418 run_cost += nrows * remote_conds_cost.per_tuple;
3419 run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3420
3421 /* Add in tlist eval cost for each output row */
3422 startup_cost += foreignrel->reltarget->cost.startup;
3423 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3424 }
3425 else if (IS_UPPER_REL(foreignrel))
3426 {
3427 RelOptInfo *outerrel = fpinfo->outerrel;
3430 double input_rows;
3431 int numGroupCols;
3432 double numGroups = 1;
3433
3434 /* The upper relation should have its outer relation set */
3435 Assert(outerrel);
3436 /* and that outer relation should have its reltarget set */
3437 Assert(outerrel->reltarget);
3438
3439 /*
3440 * This cost model is mixture of costing done for sorted and
3441 * hashed aggregates in cost_agg(). We are not sure which
3442 * strategy will be considered at remote side, thus for
3443 * simplicity, we put all startup related costs in startup_cost
3444 * and all finalization and run cost are added in total_cost.
3445 */
3446
3447 ofpinfo = (PgFdwRelationInfo *) outerrel->fdw_private;
3448
3449 /* Get rows from input rel */
3450 input_rows = ofpinfo->rows;
3451
3452 /* Collect statistics about aggregates for estimating costs. */
3453 if (root->parse->hasAggs)
3454 {
3456 }
3457
3458 /* Get number of grouping columns and possible number of groups */
3459 numGroupCols = list_length(root->processed_groupClause);
3460 numGroups = estimate_num_groups(root,
3461 get_sortgrouplist_exprs(root->processed_groupClause,
3462 fpinfo->grouped_tlist),
3463 input_rows, NULL, NULL);
3464
3465 /*
3466 * Get the retrieved_rows and rows estimates. If there are HAVING
3467 * quals, account for their selectivity.
3468 */
3469 if (root->hasHavingQual)
3470 {
3471 /* Factor in the selectivity of the remotely-checked quals */
3472 retrieved_rows =
3473 clamp_row_est(numGroups *
3475 fpinfo->remote_conds,
3476 0,
3477 JOIN_INNER,
3478 NULL));
3479 /* Factor in the selectivity of the locally-checked quals */
3480 rows = clamp_row_est(retrieved_rows * fpinfo->local_conds_sel);
3481 }
3482 else
3483 {
3484 rows = retrieved_rows = numGroups;
3485 }
3486
3487 /* Use width estimate made by the core code. */
3488 width = foreignrel->reltarget->width;
3489
3490 /*-----
3491 * Startup cost includes:
3492 * 1. Startup cost for underneath input relation, adjusted for
3493 * tlist replacement by apply_scanjoin_target_to_paths()
3494 * 2. Cost of performing aggregation, per cost_agg()
3495 *-----
3496 */
3497 startup_cost = ofpinfo->rel_startup_cost;
3498 startup_cost += outerrel->reltarget->cost.startup;
3499 startup_cost += aggcosts.transCost.startup;
3500 startup_cost += aggcosts.transCost.per_tuple * input_rows;
3501 startup_cost += aggcosts.finalCost.startup;
3502 startup_cost += (cpu_operator_cost * numGroupCols) * input_rows;
3503
3504 /*-----
3505 * Run time cost includes:
3506 * 1. Run time cost of underneath input relation, adjusted for
3507 * tlist replacement by apply_scanjoin_target_to_paths()
3508 * 2. Run time cost of performing aggregation, per cost_agg()
3509 *-----
3510 */
3511 run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost;
3512 run_cost += outerrel->reltarget->cost.per_tuple * input_rows;
3513 run_cost += aggcosts.finalCost.per_tuple * numGroups;
3514 run_cost += cpu_tuple_cost * numGroups;
3515
3516 /* Account for the eval cost of HAVING quals, if any */
3517 if (root->hasHavingQual)
3518 {
3520
3521 /* Add in the eval cost of the remotely-checked quals */
3522 cost_qual_eval(&remote_cost, fpinfo->remote_conds, root);
3523 startup_cost += remote_cost.startup;
3524 run_cost += remote_cost.per_tuple * numGroups;
3525 /* Add in the eval cost of the locally-checked quals */
3526 startup_cost += fpinfo->local_conds_cost.startup;
3527 run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows;
3528 }
3529
3530 /* Add in tlist eval cost for each output row */
3531 startup_cost += foreignrel->reltarget->cost.startup;
3532 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3533 }
3534 else
3535 {
3537
3538 /* Use rows/width estimates made by set_baserel_size_estimates. */
3539 rows = foreignrel->rows;
3540 width = foreignrel->reltarget->width;
3541
3542 /*
3543 * Back into an estimate of the number of retrieved rows. Just in
3544 * case this is nuts, clamp to at most foreignrel->tuples.
3545 */
3546 retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
3547 retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
3548
3549 /*
3550 * Cost as though this were a seqscan, which is pessimistic. We
3551 * effectively imagine the local_conds are being evaluated
3552 * remotely, too.
3553 */
3554 startup_cost = 0;
3555 run_cost = 0;
3556 run_cost += seq_page_cost * foreignrel->pages;
3557
3558 startup_cost += foreignrel->baserestrictcost.startup;
3560 run_cost += cpu_per_tuple * foreignrel->tuples;
3561
3562 /* Add in tlist eval cost for each output row */
3563 startup_cost += foreignrel->reltarget->cost.startup;
3564 run_cost += foreignrel->reltarget->cost.per_tuple * rows;
3565 }
3566
3567 /*
3568 * Without remote estimates, we have no real way to estimate the cost
3569 * of generating sorted output. It could be free if the query plan
3570 * the remote side would have chosen generates properly-sorted output
3571 * anyway, but in most cases it will cost something. Estimate a value
3572 * high enough that we won't pick the sorted path when the ordering
3573 * isn't locally useful, but low enough that we'll err on the side of
3574 * pushing down the ORDER BY clause when it's useful to do so.
3575 */
3576 if (pathkeys != NIL)
3577 {
3578 if (IS_UPPER_REL(foreignrel))
3579 {
3580 Assert(foreignrel->reloptkind == RELOPT_UPPER_REL &&
3581 fpinfo->stage == UPPERREL_GROUP_AGG);
3582
3583 /*
3584 * We can only get here when this function is called from
3585 * add_foreign_ordered_paths() or add_foreign_final_paths();
3586 * in which cases, the passed-in fpextra should not be NULL.
3587 */
3588 Assert(fpextra);
3590 retrieved_rows, width,
3591 fpextra->limit_tuples,
3592 &disabled_nodes,
3593 &startup_cost, &run_cost);
3594 }
3595 else
3596 {
3597 startup_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3598 run_cost *= DEFAULT_FDW_SORT_MULTIPLIER;
3599 }
3600 }
3601
3602 total_cost = startup_cost + run_cost;
3603
3604 /* Adjust the cost estimates if we have LIMIT */
3605 if (fpextra && fpextra->has_limit)
3606 {
3607 adjust_limit_rows_costs(&rows, &startup_cost, &total_cost,
3608 fpextra->offset_est, fpextra->count_est);
3609 retrieved_rows = rows;
3610 }
3611 }
3612
3613 /*
3614 * If this includes the final sort step, the given target, which will be
3615 * applied to the resulting path, might have different expressions from
3616 * the foreignrel's reltarget (see make_sort_input_target()); adjust tlist
3617 * eval costs.
3618 */
3619 if (fpextra && fpextra->has_final_sort &&
3620 fpextra->target != foreignrel->reltarget)
3621 {
3622 QualCost oldcost = foreignrel->reltarget->cost;
3623 QualCost newcost = fpextra->target->cost;
3624
3625 startup_cost += newcost.startup - oldcost.startup;
3626 total_cost += newcost.startup - oldcost.startup;
3627 total_cost += (newcost.per_tuple - oldcost.per_tuple) * rows;
3628 }
3629
3630 /*
3631 * Cache the retrieved rows and cost estimates for scans, joins, or
3632 * groupings without any parameterization, pathkeys, or additional
3633 * post-scan/join-processing steps, before adding the costs for
3634 * transferring data from the foreign server. These estimates are useful
3635 * for costing remote joins involving this relation or costing other
3636 * remote operations on this relation such as remote sorts and remote
3637 * LIMIT restrictions, when the costs can not be obtained from the foreign
3638 * server. This function will be called at least once for every foreign
3639 * relation without any parameterization, pathkeys, or additional
3640 * post-scan/join-processing steps.
3641 */
3642 if (pathkeys == NIL && param_join_conds == NIL && fpextra == NULL)
3643 {
3644 fpinfo->retrieved_rows = retrieved_rows;
3645 fpinfo->rel_startup_cost = startup_cost;
3646 fpinfo->rel_total_cost = total_cost;
3647 }
3648
3649 /*
3650 * Add some additional cost factors to account for connection overhead
3651 * (fdw_startup_cost), transferring data across the network
3652 * (fdw_tuple_cost per retrieved row), and local manipulation of the data
3653 * (cpu_tuple_cost per retrieved row).
3654 */
3655 startup_cost += fpinfo->fdw_startup_cost;
3656 total_cost += fpinfo->fdw_startup_cost;
3657 total_cost += fpinfo->fdw_tuple_cost * retrieved_rows;
3658 total_cost += cpu_tuple_cost * retrieved_rows;
3659
3660 /*
3661 * If we have LIMIT, we should prefer performing the restriction remotely
3662 * rather than locally, as the former avoids extra row fetches from the
3663 * remote that the latter might cause. But since the core code doesn't
3664 * account for such fetches when estimating the costs of the local
3665 * restriction (see create_limit_path()), there would be no difference
3666 * between the costs of the local restriction and the costs of the remote
3667 * restriction estimated above if we don't use remote estimates (except
3668 * for the case where the foreignrel is a grouping relation, the given
3669 * pathkeys is not NIL, and the effects of a bounded sort for that rel is
3670 * accounted for in costing the remote restriction). Tweak the costs of
3671 * the remote restriction to ensure we'll prefer it if LIMIT is a useful
3672 * one.
3673 */
3674 if (!fpinfo->use_remote_estimate &&
3675 fpextra && fpextra->has_limit &&
3676 fpextra->limit_tuples > 0 &&
3677 fpextra->limit_tuples < fpinfo->rows)
3678 {
3679 Assert(fpinfo->rows > 0);
3680 total_cost -= (total_cost - startup_cost) * 0.05 *
3681 (fpinfo->rows - fpextra->limit_tuples) / fpinfo->rows;
3682 }
3683
3684 /* Return results. */
3685 *p_rows = rows;
3686 *p_width = width;
3687 *p_disabled_nodes = disabled_nodes;
3688 *p_startup_cost = startup_cost;
3689 *p_total_cost = total_cost;
3690}
3691
3692/*
3693 * Estimate costs of executing a SQL statement remotely.
3694 * The given "sql" must be an EXPLAIN command.
3695 */
3696static void
3698 double *rows, int *width,
3699 Cost *startup_cost, Cost *total_cost)
3700{
3701 PGresult *res;
3702 char *line;
3703 char *p;
3704 int n;
3705
3706 /*
3707 * Execute EXPLAIN remotely.
3708 */
3709 res = pgfdw_exec_query(conn, sql, NULL);
3710 if (PQresultStatus(res) != PGRES_TUPLES_OK)
3711 pgfdw_report_error(res, conn, sql);
3712
3713 /*
3714 * Extract cost numbers for topmost plan node. Note we search for a left
3715 * paren from the end of the line to avoid being confused by other uses of
3716 * parentheses.
3717 */
3718 line = PQgetvalue(res, 0, 0);
3719 p = strrchr(line, '(');
3720 if (p == NULL)
3721 elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3722 n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
3723 startup_cost, total_cost, rows, width);
3724 if (n != 4)
3725 elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
3726 PQclear(res);
3727}
3728
3729/*
3730 * Adjust the cost estimates of a foreign grouping path to include the cost of
3731 * generating properly-sorted output.
3732 */
3733static void
3735 List *pathkeys,
3736 double retrieved_rows,
3737 double width,
3738 double limit_tuples,
3739 int *p_disabled_nodes,
3742{
3743 /*
3744 * If the GROUP BY clause isn't sort-able, the plan chosen by the remote
3745 * side is unlikely to generate properly-sorted output, so it would need
3746 * an explicit sort; adjust the given costs with cost_sort(). Likewise,
3747 * if the GROUP BY clause is sort-able but isn't a superset of the given
3748 * pathkeys, adjust the costs with that function. Otherwise, adjust the
3749 * costs by applying the same heuristic as for the scan or join case.
3750 */
3751 if (!grouping_is_sortable(root->processed_groupClause) ||
3752 !pathkeys_contained_in(pathkeys, root->group_pathkeys))
3753 {
3754 Path sort_path; /* dummy for result of cost_sort */
3755
3757 root,
3758 pathkeys,
3759 0,
3761 retrieved_rows,
3762 width,
3763 0.0,
3764 work_mem,
3765 limit_tuples);
3766
3767 *p_startup_cost = sort_path.startup_cost;
3768 *p_run_cost = sort_path.total_cost - sort_path.startup_cost;
3769 }
3770 else
3771 {
3772 /*
3773 * The default extra cost seems too large for foreign-grouping cases;
3774 * add 1/4th of that default.
3775 */
3777 - 1.0) * 0.25;
3778
3781 }
3782}
3783
3784/*
3785 * Detect whether we want to process an EquivalenceClass member.
3786 *
3787 * This is a callback for use by generate_implied_equalities_for_column.
3788 */
3789static bool
3792 void *arg)
3793{
3795 Expr *expr = em->em_expr;
3796
3797 /*
3798 * If we've identified what we're processing in the current scan, we only
3799 * want to match that expression.
3800 */
3801 if (state->current != NULL)
3802 return equal(expr, state->current);
3803
3804 /*
3805 * Otherwise, ignore anything we've already processed.
3806 */
3807 if (list_member(state->already_used, expr))
3808 return false;
3809
3810 /* This is the new target to process. */
3811 state->current = expr;
3812 return true;
3813}
3814
3815/*
3816 * Create cursor for node's query with current parameter values.
3817 */
3818static void
3820{
3821 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3822 ExprContext *econtext = node->ss.ps.ps_ExprContext;
3823 int numParams = fsstate->numParams;
3824 const char **values = fsstate->param_values;
3825 PGconn *conn = fsstate->conn;
3827 PGresult *res;
3828
3829 /* First, process a pending asynchronous request, if any. */
3830 if (fsstate->conn_state->pendingAreq)
3832
3833 /*
3834 * Construct array of query parameter values in text format. We do the
3835 * conversions in the short-lived per-tuple context, so as not to cause a
3836 * memory leak over repeated scans.
3837 */
3838 if (numParams > 0)
3839 {
3840 MemoryContext oldcontext;
3841
3842 oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
3843
3844 process_query_params(econtext,
3845 fsstate->param_flinfo,
3846 fsstate->param_exprs,
3847 values);
3848
3849 MemoryContextSwitchTo(oldcontext);
3850 }
3851
3852 /* Construct the DECLARE CURSOR command */
3854 appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
3855 fsstate->cursor_number, fsstate->query);
3856
3857 /*
3858 * Notice that we pass NULL for paramTypes, thus forcing the remote server
3859 * to infer types for all parameters. Since we explicitly cast every
3860 * parameter (see deparse.c), the "inference" is trivial and will produce
3861 * the desired result. This allows us to avoid assuming that the remote
3862 * server has the same OIDs we do for the parameters' types.
3863 */
3864 if (!PQsendQueryParams(conn, buf.data, numParams,
3865 NULL, values, NULL, NULL, 0))
3867
3868 /*
3869 * Get the result, and check for success.
3870 */
3871 res = pgfdw_get_result(conn);
3872 if (PQresultStatus(res) != PGRES_COMMAND_OK)
3873 pgfdw_report_error(res, conn, fsstate->query);
3874 PQclear(res);
3875
3876 /* Mark the cursor as created, and show no tuples have been retrieved */
3877 fsstate->cursor_exists = true;
3878 fsstate->tuples = NULL;
3879 fsstate->num_tuples = 0;
3880 fsstate->next_tuple = 0;
3881 fsstate->fetch_ct_2 = 0;
3882 fsstate->eof_reached = false;
3883
3884 /* Clean up */
3885 pfree(buf.data);
3886}
3887
3888/*
3889 * Fetch some more rows from the node's cursor.
3890 */
3891static void
3893{
3894 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
3895 PGconn *conn = fsstate->conn;
3896 PGresult *res;
3897 int numrows;
3898 int i;
3899 MemoryContext oldcontext;
3900
3901 /*
3902 * We'll store the tuples in the batch_cxt. First, flush the previous
3903 * batch.
3904 */
3905 fsstate->tuples = NULL;
3906 MemoryContextReset(fsstate->batch_cxt);
3907 oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
3908
3909 if (fsstate->async_capable)
3910 {
3911 Assert(fsstate->conn_state->pendingAreq);
3912
3913 /*
3914 * The query was already sent by an earlier call to
3915 * fetch_more_data_begin. So now we just fetch the result.
3916 */
3917 res = pgfdw_get_result(conn);
3918 /* On error, report the original query, not the FETCH. */
3919 if (PQresultStatus(res) != PGRES_TUPLES_OK)
3920 pgfdw_report_error(res, conn, fsstate->query);
3921
3922 /* Reset per-connection state */
3923 fsstate->conn_state->pendingAreq = NULL;
3924 }
3925 else
3926 {
3927 char sql[64];
3928
3929 /* This is a regular synchronous fetch. */
3930 snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
3931 fsstate->fetch_size, fsstate->cursor_number);
3932
3933 res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
3934 /* On error, report the original query, not the FETCH. */
3935 if (PQresultStatus(res) != PGRES_TUPLES_OK)
3936 pgfdw_report_error(res, conn, fsstate->query);
3937 }
3938
3939 /* Convert the data into HeapTuples */
3940 numrows = PQntuples(res);
3941 fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
3942 fsstate->num_tuples = numrows;
3943 fsstate->next_tuple = 0;
3944
3945 for (i = 0; i < numrows; i++)
3946 {
3947 Assert(IsA(node->ss.ps.plan, ForeignScan));
3948
3949 fsstate->tuples[i] =
3951 fsstate->rel,
3952 fsstate->attinmeta,
3953 fsstate->retrieved_attrs,
3954 node,
3955 fsstate->temp_cxt);
3956 }
3957
3958 /* Update fetch_ct_2 */
3959 if (fsstate->fetch_ct_2 < 2)
3960 fsstate->fetch_ct_2++;
3961
3962 /* Must be EOF if we didn't get as many tuples as we asked for. */
3963 fsstate->eof_reached = (numrows < fsstate->fetch_size);
3964
3965 PQclear(res);
3966
3967 MemoryContextSwitchTo(oldcontext);
3968}
3969
3970/*
3971 * Force assorted GUC parameters to settings that ensure that we'll output
3972 * data values in a form that is unambiguous to the remote server.
3973 *
3974 * This is rather expensive and annoying to do once per row, but there's
3975 * little choice if we want to be sure values are transmitted accurately;
3976 * we can't leave the settings in place between rows for fear of affecting
3977 * user-visible computations.
3978 *
3979 * We use the equivalent of a function SET option to allow the settings to
3980 * persist only until the caller calls reset_transmission_modes(). If an
3981 * error is thrown in between, guc.c will take care of undoing the settings.
3982 *
3983 * The return value is the nestlevel that must be passed to
3984 * reset_transmission_modes() to undo things.
3985 */
3986int
3988{
3989 int nestlevel = NewGUCNestLevel();
3990
3991 /*
3992 * The values set here should match what pg_dump does. See also
3993 * configure_remote_session in connection.c.
3994 */
3995 if (DateStyle != USE_ISO_DATES)
3996 (void) set_config_option("datestyle", "ISO",
3998 GUC_ACTION_SAVE, true, 0, false);
4000 (void) set_config_option("intervalstyle", "postgres",
4002 GUC_ACTION_SAVE, true, 0, false);
4003 if (extra_float_digits < 3)
4004 (void) set_config_option("extra_float_digits", "3",
4006 GUC_ACTION_SAVE, true, 0, false);
4007
4008 /*
4009 * In addition force restrictive search_path, in case there are any
4010 * regproc or similar constants to be printed.
4011 */
4012 (void) set_config_option("search_path", "pg_catalog",
4014 GUC_ACTION_SAVE, true, 0, false);
4015
4016 return nestlevel;
4017}
4018
4019/*
4020 * Undo the effects of set_transmission_modes().
4021 */
4022void
4027
4028/*
4029 * Utility routine to close a cursor.
4030 */
4031static void
4033 PgFdwConnState *conn_state)
4034{
4035 char sql[64];
4036 PGresult *res;
4037
4038 snprintf(sql, sizeof(sql), "CLOSE c%u", cursor_number);
4039 res = pgfdw_exec_query(conn, sql, conn_state);
4040 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4041 pgfdw_report_error(res, conn, sql);
4042 PQclear(res);
4043}
4044
4045/*
4046 * create_foreign_modify
4047 * Construct an execution state of a foreign insert/update/delete
4048 * operation
4049 */
4050static PgFdwModifyState *
4053 ResultRelInfo *resultRelInfo,
4055 Plan *subplan,
4056 char *query,
4057 List *target_attrs,
4058 int values_end,
4059 bool has_returning,
4060 List *retrieved_attrs)
4061{
4063 Relation rel = resultRelInfo->ri_RelationDesc;
4064 TupleDesc tupdesc = RelationGetDescr(rel);
4065 Oid userid;
4069 Oid typefnoid;
4070 bool isvarlena;
4071 ListCell *lc;
4072
4073 /* Begin constructing PgFdwModifyState. */
4075 fmstate->rel = rel;
4076
4077 /* Identify which user to do the remote access as. */
4078 userid = ExecGetResultRelCheckAsUser(resultRelInfo, estate);
4079
4080 /* Get info about foreign table. */
4082 user = GetUserMapping(userid, table->serverid);
4083
4084 /* Open connection; report that we'll create a prepared statement. */
4085 fmstate->conn = GetConnection(user, true, &fmstate->conn_state);
4086 fmstate->p_name = NULL; /* prepared statement not made yet */
4087
4088 /* Set up remote query information. */
4089 fmstate->query = query;
4090 if (operation == CMD_INSERT)
4091 {
4092 fmstate->query = pstrdup(fmstate->query);
4093 fmstate->orig_query = pstrdup(fmstate->query);
4094 }
4095 fmstate->target_attrs = target_attrs;
4096 fmstate->values_end = values_end;
4097 fmstate->has_returning = has_returning;
4098 fmstate->retrieved_attrs = retrieved_attrs;
4099
4100 /* Create context for per-tuple temp workspace. */
4101 fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
4102 "postgres_fdw temporary data",
4104
4105 /* Prepare for input conversion of RETURNING results. */
4106 if (fmstate->has_returning)
4107 fmstate->attinmeta = TupleDescGetAttInMetadata(tupdesc);
4108
4109 /* Prepare for output conversion of parameters used in prepared stmt. */
4110 n_params = list_length(fmstate->target_attrs) + 1;
4111 fmstate->p_flinfo = palloc0_array(FmgrInfo, n_params);
4112 fmstate->p_nums = 0;
4113
4115 {
4116 Assert(subplan != NULL);
4117
4118 /* Find the ctid resjunk column in the subplan's result */
4119 fmstate->ctidAttno = ExecFindJunkAttributeInTlist(subplan->targetlist,
4120 "ctid");
4121 if (!AttributeNumberIsValid(fmstate->ctidAttno))
4122 elog(ERROR, "could not find junk ctid column");
4123
4124 /* First transmittable parameter will be ctid */
4126 fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4127 fmstate->p_nums++;
4128 }
4129
4131 {
4132 /* Set up for remaining transmittable parameters */
4133 foreach(lc, fmstate->target_attrs)
4134 {
4135 int attnum = lfirst_int(lc);
4136 Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4137
4138 Assert(!attr->attisdropped);
4139
4140 /* Ignore generated columns; they are set to DEFAULT */
4141 if (attr->attgenerated)
4142 continue;
4143 getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
4144 fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
4145 fmstate->p_nums++;
4146 }
4147 }
4148
4149 Assert(fmstate->p_nums <= n_params);
4150
4151 /* Set batch_size from foreign server/table options. */
4152 if (operation == CMD_INSERT)
4153 fmstate->batch_size = get_batch_size_option(rel);
4154
4155 fmstate->num_slots = 1;
4156
4157 /* Initialize auxiliary state */
4158 fmstate->aux_fmstate = NULL;
4159
4160 return fmstate;
4161}
4162
4163/*
4164 * execute_foreign_modify
4165 * Perform foreign-table modification as required, and fetch RETURNING
4166 * result if any. (This is the shared guts of postgresExecForeignInsert,
4167 * postgresExecForeignBatchInsert, postgresExecForeignUpdate, and
4168 * postgresExecForeignDelete.)
4169 */
4170static TupleTableSlot **
4172 ResultRelInfo *resultRelInfo,
4174 TupleTableSlot **slots,
4176 int *numSlots)
4177{
4179 ItemPointer ctid = NULL;
4180 const char **p_values;
4181 PGresult *res;
4182 int n_rows;
4183 StringInfoData sql;
4184
4185 /* The operation should be INSERT, UPDATE, or DELETE */
4187 operation == CMD_UPDATE ||
4189
4190 /* First, process a pending asynchronous request, if any. */
4191 if (fmstate->conn_state->pendingAreq)
4192 process_pending_request(fmstate->conn_state->pendingAreq);
4193
4194 /*
4195 * If the existing query was deparsed and prepared for a different number
4196 * of rows, rebuild it for the proper number.
4197 */
4198 if (operation == CMD_INSERT && fmstate->num_slots != *numSlots)
4199 {
4200 /* Destroy the prepared statement created previously */
4201 if (fmstate->p_name)
4203
4204 /* Build INSERT string with numSlots records in its VALUES clause. */
4205 initStringInfo(&sql);
4206 rebuildInsertSql(&sql, fmstate->rel,
4207 fmstate->orig_query, fmstate->target_attrs,
4208 fmstate->values_end, fmstate->p_nums,
4209 *numSlots - 1);
4210 pfree(fmstate->query);
4211 fmstate->query = sql.data;
4212 fmstate->num_slots = *numSlots;
4213 }
4214
4215 /* Set up the prepared statement on the remote server, if we didn't yet */
4216 if (!fmstate->p_name)
4218
4219 /*
4220 * For UPDATE/DELETE, get the ctid that was passed up as a resjunk column
4221 */
4223 {
4224 Datum datum;
4225 bool isNull;
4226
4228 fmstate->ctidAttno,
4229 &isNull);
4230 /* shouldn't ever get a null result... */
4231 if (isNull)
4232 elog(ERROR, "ctid is NULL");
4233 ctid = (ItemPointer) DatumGetPointer(datum);
4234 }
4235
4236 /* Convert parameters needed by prepared statement to text form */
4238
4239 /*
4240 * Execute the prepared statement.
4241 */
4242 if (!PQsendQueryPrepared(fmstate->conn,
4243 fmstate->p_name,
4244 fmstate->p_nums * (*numSlots),
4245 p_values,
4246 NULL,
4247 NULL,
4248 0))
4249 pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
4250
4251 /*
4252 * Get the result, and check for success.
4253 */
4254 res = pgfdw_get_result(fmstate->conn);
4255 if (PQresultStatus(res) !=
4256 (fmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
4257 pgfdw_report_error(res, fmstate->conn, fmstate->query);
4258
4259 /* Check number of rows affected, and fetch RETURNING tuple if any */
4260 if (fmstate->has_returning)
4261 {
4262 Assert(*numSlots == 1);
4263 n_rows = PQntuples(res);
4264 if (n_rows > 0)
4265 store_returning_result(fmstate, slots[0], res);
4266 }
4267 else
4268 n_rows = atoi(PQcmdTuples(res));
4269
4270 /* And clean up */
4271 PQclear(res);
4272
4273 MemoryContextReset(fmstate->temp_cxt);
4274
4275 *numSlots = n_rows;
4276
4277 /*
4278 * Return NULL if nothing was inserted/updated/deleted on the remote end
4279 */
4280 return (n_rows > 0) ? slots : NULL;
4281}
4282
4283/*
4284 * prepare_foreign_modify
4285 * Establish a prepared statement for execution of INSERT/UPDATE/DELETE
4286 */
4287static void
4289{
4290 char prep_name[NAMEDATALEN];
4291 char *p_name;
4292 PGresult *res;
4293
4294 /*
4295 * The caller would already have processed a pending asynchronous request
4296 * if any, so no need to do it here.
4297 */
4298
4299 /* Construct name we'll use for the prepared statement. */
4300 snprintf(prep_name, sizeof(prep_name), "pgsql_fdw_prep_%u",
4301 GetPrepStmtNumber(fmstate->conn));
4302 p_name = pstrdup(prep_name);
4303
4304 /*
4305 * We intentionally do not specify parameter types here, but leave the
4306 * remote server to derive them by default. This avoids possible problems
4307 * with the remote server using different type OIDs than we do. All of
4308 * the prepared statements we use in this module are simple enough that
4309 * the remote server will make the right choices.
4310 */
4311 if (!PQsendPrepare(fmstate->conn,
4312 p_name,
4313 fmstate->query,
4314 0,
4315 NULL))
4316 pgfdw_report_error(NULL, fmstate->conn, fmstate->query);
4317
4318 /*
4319 * Get the result, and check for success.
4320 */
4321 res = pgfdw_get_result(fmstate->conn);
4322 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4323 pgfdw_report_error(res, fmstate->conn, fmstate->query);
4324 PQclear(res);
4325
4326 /* This action shows that the prepare has been done. */
4327 fmstate->p_name = p_name;
4328}
4329
4330/*
4331 * convert_prep_stmt_params
4332 * Create array of text strings representing parameter values
4333 *
4334 * tupleid is ctid to send, or NULL if none
4335 * slot is slot to get remaining parameters from, or NULL if none
4336 *
4337 * Data is constructed in temp_cxt; caller should reset that after use.
4338 */
4339static const char **
4342 TupleTableSlot **slots,
4343 int numSlots)
4344{
4345 const char **p_values;
4346 int i;
4347 int j;
4348 int pindex = 0;
4349 MemoryContext oldcontext;
4350
4351 oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt);
4352
4353 p_values = (const char **) palloc(sizeof(char *) * fmstate->p_nums * numSlots);
4354
4355 /* ctid is provided only for UPDATE/DELETE, which don't allow batching */
4356 Assert(!(tupleid != NULL && numSlots > 1));
4357
4358 /* 1st parameter should be ctid, if it's in use */
4359 if (tupleid != NULL)
4360 {
4361 Assert(numSlots == 1);
4362 /* don't need set_transmission_modes for TID output */
4365 pindex++;
4366 }
4367
4368 /* get following parameters from slots */
4369 if (slots != NULL && fmstate->target_attrs != NIL)
4370 {
4371 TupleDesc tupdesc = RelationGetDescr(fmstate->rel);
4372 int nestlevel;
4373 ListCell *lc;
4374
4376
4377 for (i = 0; i < numSlots; i++)
4378 {
4379 j = (tupleid != NULL) ? 1 : 0;
4380 foreach(lc, fmstate->target_attrs)
4381 {
4382 int attnum = lfirst_int(lc);
4383 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
4384 Datum value;
4385 bool isnull;
4386
4387 /* Ignore generated columns; they are set to DEFAULT */
4388 if (attr->attgenerated)
4389 continue;
4390 value = slot_getattr(slots[i], attnum, &isnull);
4391 if (isnull)
4392 p_values[pindex] = NULL;
4393 else
4395 value);
4396 pindex++;
4397 j++;
4398 }
4399 }
4400
4402 }
4403
4404 Assert(pindex == fmstate->p_nums * numSlots);
4405
4406 MemoryContextSwitchTo(oldcontext);
4407
4408 return p_values;
4409}
4410
4411/*
4412 * store_returning_result
4413 * Store the result of a RETURNING clause
4414 */
4415static void
4417 TupleTableSlot *slot, PGresult *res)
4418{
4420
4422 fmstate->rel,
4423 fmstate->attinmeta,
4424 fmstate->retrieved_attrs,
4425 NULL,
4426 fmstate->temp_cxt);
4427
4428 /*
4429 * The returning slot will not necessarily be suitable to store heaptuples
4430 * directly, so allow for conversion.
4431 */
4432 ExecForceStoreHeapTuple(newtup, slot, true);
4433}
4434
4435/*
4436 * finish_foreign_modify
4437 * Release resources for a foreign insert/update/delete operation
4438 */
4439static void
4441{
4442 Assert(fmstate != NULL);
4443
4444 /* If we created a prepared statement, destroy it */
4446
4447 /* Release remote connection */
4449 fmstate->conn = NULL;
4450}
4451
4452/*
4453 * deallocate_query
4454 * Deallocate a prepared statement for a foreign insert/update/delete
4455 * operation
4456 */
4457static void
4459{
4460 char sql[64];
4461 PGresult *res;
4462
4463 /* do nothing if the query is not allocated */
4464 if (!fmstate->p_name)
4465 return;
4466
4467 snprintf(sql, sizeof(sql), "DEALLOCATE %s", fmstate->p_name);
4468 res = pgfdw_exec_query(fmstate->conn, sql, fmstate->conn_state);
4469 if (PQresultStatus(res) != PGRES_COMMAND_OK)
4470 pgfdw_report_error(res, fmstate->conn, sql);
4471 PQclear(res);
4472 pfree(fmstate->p_name);
4473 fmstate->p_name = NULL;
4474}
4475
4476/*
4477 * build_remote_returning
4478 * Build a RETURNING targetlist of a remote query for performing an
4479 * UPDATE/DELETE .. RETURNING on a join directly
4480 */
4481static List *
4482build_remote_returning(Index rtindex, Relation rel, List *returningList)
4483{
4484 bool have_wholerow = false;
4485 List *tlist = NIL;
4486 List *vars;
4487 ListCell *lc;
4488
4489 Assert(returningList);
4490
4491 vars = pull_var_clause((Node *) returningList, PVC_INCLUDE_PLACEHOLDERS);
4492
4493 /*
4494 * If there's a whole-row reference to the target relation, then we'll
4495 * need all the columns of the relation.
4496 */
4497 foreach(lc, vars)
4498 {
4499 Var *var = (Var *) lfirst(lc);
4500
4501 if (IsA(var, Var) &&
4502 var->varno == rtindex &&
4504 {
4505 have_wholerow = true;
4506 break;
4507 }
4508 }
4509
4510 if (have_wholerow)
4511 {
4512 TupleDesc tupdesc = RelationGetDescr(rel);
4513 int i;
4514
4515 for (i = 1; i <= tupdesc->natts; i++)
4516 {
4517 Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
4518 Var *var;
4519
4520 /* Ignore dropped attributes. */
4521 if (attr->attisdropped)
4522 continue;
4523
4524 var = makeVar(rtindex,
4525 i,
4526 attr->atttypid,
4527 attr->atttypmod,
4528 attr->attcollation,
4529 0);
4530
4531 tlist = lappend(tlist,
4532 makeTargetEntry((Expr *) var,
4533 list_length(tlist) + 1,
4534 NULL,
4535 false));
4536 }
4537 }
4538
4539 /* Now add any remaining columns to tlist. */
4540 foreach(lc, vars)
4541 {
4542 Var *var = (Var *) lfirst(lc);
4543
4544 /*
4545 * No need for whole-row references to the target relation. We don't
4546 * need system columns other than ctid and oid either, since those are
4547 * set locally.
4548 */
4549 if (IsA(var, Var) &&
4550 var->varno == rtindex &&
4551 var->varattno <= InvalidAttrNumber &&
4553 continue; /* don't need it */
4554
4555 if (tlist_member((Expr *) var, tlist))
4556 continue; /* already got it */
4557
4558 tlist = lappend(tlist,
4559 makeTargetEntry((Expr *) var,
4560 list_length(tlist) + 1,
4561 NULL,
4562 false));
4563 }
4564
4565 list_free(vars);
4566
4567 return tlist;
4568}
4569
4570/*
4571 * rebuild_fdw_scan_tlist
4572 * Build new fdw_scan_tlist of given foreign-scan plan node from given
4573 * tlist
4574 *
4575 * There might be columns that the fdw_scan_tlist of the given foreign-scan
4576 * plan node contains that the given tlist doesn't. The fdw_scan_tlist would
4577 * have contained resjunk columns such as 'ctid' of the target relation and
4578 * 'wholerow' of non-target relations, but the tlist might not contain them,
4579 * for example. So, adjust the tlist so it contains all the columns specified
4580 * in the fdw_scan_tlist; else setrefs.c will get confused.
4581 */
4582static void
4584{
4585 List *new_tlist = tlist;
4586 List *old_tlist = fscan->fdw_scan_tlist;
4587 ListCell *lc;
4588
4589 foreach(lc, old_tlist)
4590 {
4592
4593 if (tlist_member(tle->expr, new_tlist))
4594 continue; /* already got it */
4595
4597 makeTargetEntry(tle->expr,
4599 NULL,
4600 false));
4601 }
4602 fscan->fdw_scan_tlist = new_tlist;
4603}
4604
4605/*
4606 * Execute a direct UPDATE/DELETE statement.
4607 */
4608static void
4610{
4612 ExprContext *econtext = node->ss.ps.ps_ExprContext;
4613 int numParams = dmstate->numParams;
4614 const char **values = dmstate->param_values;
4615
4616 /* First, process a pending asynchronous request, if any. */
4617 if (dmstate->conn_state->pendingAreq)
4618 process_pending_request(dmstate->conn_state->pendingAreq);
4619
4620 /*
4621 * Construct array of query parameter values in text format.
4622 */
4623 if (numParams > 0)
4624 process_query_params(econtext,
4625 dmstate->param_flinfo,
4626 dmstate->param_exprs,
4627 values);
4628
4629 /*
4630 * Notice that we pass NULL for paramTypes, thus forcing the remote server
4631 * to infer types for all parameters. Since we explicitly cast every
4632 * parameter (see deparse.c), the "inference" is trivial and will produce
4633 * the desired result. This allows us to avoid assuming that the remote
4634 * server has the same OIDs we do for the parameters' types.
4635 */
4636 if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
4637 NULL, values, NULL, NULL, 0))
4638 pgfdw_report_error(NULL, dmstate->conn, dmstate->query);
4639
4640 /*
4641 * Get the result, and check for success.
4642 */
4643 dmstate->result = pgfdw_get_result(dmstate->conn);
4644 if (PQresultStatus(dmstate->result) !=
4645 (dmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
4646 pgfdw_report_error(dmstate->result, dmstate->conn,
4647 dmstate->query);
4648
4649 /*
4650 * The result potentially needs to survive across multiple executor row
4651 * cycles, so move it to the context where the dmstate is.
4652 */
4653 dmstate->result = libpqsrv_PGresultSetParent(dmstate->result,
4655
4656 /* Get the number of rows affected. */
4657 if (dmstate->has_returning)
4658 dmstate->num_tuples = PQntuples(dmstate->result);
4659 else
4660 dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
4661}
4662
4663/*
4664 * Get the result of a RETURNING clause.
4665 */
4666static TupleTableSlot *
4668{
4670 EState *estate = node->ss.ps.state;
4671 ResultRelInfo *resultRelInfo = node->resultRelInfo;
4672 TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
4674
4675 Assert(resultRelInfo->ri_projectReturning);
4676
4677 /* If we didn't get any tuples, must be end of data. */
4678 if (dmstate->next_tuple >= dmstate->num_tuples)
4679 return ExecClearTuple(slot);
4680
4681 /* Increment the command es_processed count if necessary. */
4682 if (dmstate->set_processed)
4683 estate->es_processed += 1;
4684
4685 /*
4686 * Store a RETURNING tuple. If has_returning is false, just emit a dummy
4687 * tuple. (has_returning is false when the local query is of the form
4688 * "UPDATE/DELETE .. RETURNING 1" for example.)
4689 */
4690 if (!dmstate->has_returning)
4691 {
4693 resultSlot = slot;
4694 }
4695 else
4696 {
4698
4700 dmstate->next_tuple,
4701 dmstate->rel,
4702 dmstate->attinmeta,
4703 dmstate->retrieved_attrs,
4704 node,
4705 dmstate->temp_cxt);
4706 ExecStoreHeapTuple(newtup, slot, false);
4707 /* Get the updated/deleted tuple. */
4708 if (dmstate->rel)
4709 resultSlot = slot;
4710 else
4711 resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
4712 }
4713 dmstate->next_tuple++;
4714
4715 /* Make slot available for evaluation of the local query RETURNING list. */
4717 resultSlot;
4718
4719 return slot;
4720}
4721
4722/*
4723 * Initialize a filter to extract an updated/deleted tuple from a scan tuple.
4724 */
4725static void
4727 List *fdw_scan_tlist,
4728 Index rtindex)
4729{
4731 ListCell *lc;
4732 int i;
4733
4734 /*
4735 * Calculate the mapping between the fdw_scan_tlist's entries and the
4736 * result tuple's attributes.
4737 *
4738 * The "map" is an array of indexes of the result tuple's attributes in
4739 * fdw_scan_tlist, i.e., one entry for every attribute of the result
4740 * tuple. We store zero for any attributes that don't have the
4741 * corresponding entries in that list, marking that a NULL is needed in
4742 * the result tuple.
4743 *
4744 * Also get the indexes of the entries for ctid and oid if any.
4745 */
4746 dmstate->attnoMap = (AttrNumber *)
4747 palloc0(resultTupType->natts * sizeof(AttrNumber));
4748
4749 dmstate->ctidAttno = dmstate->oidAttno = 0;
4750
4751 i = 1;
4752 dmstate->hasSystemCols = false;
4753 foreach(lc, fdw_scan_tlist)
4754 {
4756 Var *var = (Var *) tle->expr;
4757
4758 Assert(IsA(var, Var));
4759
4760 /*
4761 * If the Var is a column of the target relation to be retrieved from
4762 * the foreign server, get the index of the entry.
4763 */
4764 if (var->varno == rtindex &&
4765 list_member_int(dmstate->retrieved_attrs, i))
4766 {
4767 int attrno = var->varattno;
4768
4769 if (attrno < 0)
4770 {
4771 /*
4772 * We don't retrieve system columns other than ctid and oid.
4773 */
4775 dmstate->ctidAttno = i;
4776 else
4777 Assert(false);
4778 dmstate->hasSystemCols = true;
4779 }
4780 else
4781 {
4782 /*
4783 * We don't retrieve whole-row references to the target
4784 * relation either.
4785 */
4786 Assert(attrno > 0);
4787
4788 dmstate->attnoMap[attrno - 1] = i;
4789 }
4790 }
4791 i++;
4792 }
4793}
4794
4795/*
4796 * Extract and return an updated/deleted tuple from a scan tuple.
4797 */
4798static TupleTableSlot *
4800 ResultRelInfo *resultRelInfo,
4801 TupleTableSlot *slot,
4802 EState *estate)
4803{
4806 Datum *values;
4807 bool *isnull;
4809 bool *old_isnull;
4810 int i;
4811
4812 /*
4813 * Use the return tuple slot as a place to store the result tuple.
4814 */
4815 resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
4816
4817 /*
4818 * Extract all the values of the scan tuple.
4819 */
4820 slot_getallattrs(slot);
4821 old_values = slot->tts_values;
4822 old_isnull = slot->tts_isnull;
4823
4824 /*
4825 * Prepare to build the result tuple.
4826 */
4828 values = resultSlot->tts_values;
4829 isnull = resultSlot->tts_isnull;
4830
4831 /*
4832 * Transpose data into proper fields of the result tuple.
4833 */
4834 for (i = 0; i < resultTupType->natts; i++)
4835 {
4836 int j = dmstate->attnoMap[i];
4837
4838 if (j == 0)
4839 {
4840 values[i] = (Datum) 0;
4841 isnull[i] = true;
4842 }
4843 else
4844 {
4845 values[i] = old_values[j - 1];
4846 isnull[i] = old_isnull[j - 1];
4847 }
4848 }
4849
4850 /*
4851 * Build the virtual tuple.
4852 */
4854
4855 /*
4856 * If we have any system columns to return, materialize a heap tuple in
4857 * the slot from column values set above and install system columns in
4858 * that tuple.
4859 */
4860 if (dmstate->hasSystemCols)
4861 {
4863
4864 /* ctid */
4865 if (dmstate->ctidAttno)
4866 {
4867 ItemPointer ctid = NULL;
4868
4869 ctid = (ItemPointer) DatumGetPointer(old_values[dmstate->ctidAttno - 1]);
4870 resultTup->t_self = *ctid;
4871 }
4872
4873 /*
4874 * And remaining columns
4875 *
4876 * Note: since we currently don't allow the target relation to appear
4877 * on the nullable side of an outer join, any system columns wouldn't
4878 * go to NULL.
4879 *
4880 * Note: no need to care about tableoid here because it will be
4881 * initialized in ExecProcessReturning().
4882 */
4886 }
4887
4888 /*
4889 * And return the result tuple.
4890 */
4891 return resultSlot;
4892}
4893
4894/*
4895 * Prepare for processing of parameters used in remote query.
4896 */
4897static void
4899 List *fdw_exprs,
4900 int numParams,
4901 FmgrInfo **param_flinfo,
4902 List **param_exprs,
4903 const char ***param_values)
4904{
4905 int i;
4906 ListCell *lc;
4907
4908 Assert(numParams > 0);
4909
4910 /* Prepare for output conversion of parameters used in remote query. */
4911 *param_flinfo = palloc0_array(FmgrInfo, numParams);
4912
4913 i = 0;
4914 foreach(lc, fdw_exprs)
4915 {
4916 Node *param_expr = (Node *) lfirst(lc);
4917 Oid typefnoid;
4918 bool isvarlena;
4919
4921 fmgr_info(typefnoid, &(*param_flinfo)[i]);
4922 i++;
4923 }
4924
4925 /*
4926 * Prepare remote-parameter expressions for evaluation. (Note: in
4927 * practice, we expect that all these expressions will be just Params, so
4928 * we could possibly do something more efficient than using the full
4929 * expression-eval machinery for this. But probably there would be little
4930 * benefit, and it'd require postgres_fdw to know more than is desirable
4931 * about Param evaluation.)
4932 */
4933 *param_exprs = ExecInitExprList(fdw_exprs, node);
4934
4935 /* Allocate buffer for text form of query parameters. */
4936 *param_values = (const char **) palloc0(numParams * sizeof(char *));
4937}
4938
4939/*
4940 * Construct array of query parameter values in text format.
4941 */
4942static void
4944 FmgrInfo *param_flinfo,
4945 List *param_exprs,
4946 const char **param_values)
4947{
4948 int nestlevel;
4949 int i;
4950 ListCell *lc;
4951
4953
4954 i = 0;
4955 foreach(lc, param_exprs)
4956 {
4959 bool isNull;
4960
4961 /* Evaluate the parameter expression */
4962 expr_value = ExecEvalExpr(expr_state, econtext, &isNull);
4963
4964 /*
4965 * Get string representation of each parameter value by invoking
4966 * type-specific output function, unless the value is null.
4967 */
4968 if (isNull)
4969 param_values[i] = NULL;
4970 else
4971 param_values[i] = OutputFunctionCall(&param_flinfo[i], expr_value);
4972
4973 i++;
4974 }
4975
4977}
4978
4979/*
4980 * postgresAnalyzeForeignTable
4981 * Test whether analyzing this foreign table is supported
4982 */
4983static bool
4987{
4990 PGconn *conn;
4991 StringInfoData sql;
4992 PGresult *res;
4993
4994 /* Return the row-analysis function pointer */
4996
4997 /*
4998 * Now we have to get the number of pages. It's annoying that the ANALYZE
4999 * API requires us to return that now, because it forces some duplication
5000 * of effort between this routine and postgresAcquireSampleRowsFunc. But
5001 * it's probably not worth redefining that API at this point.
5002 */
5003
5004 /*
5005 * Get the connection to use. We do the remote access as the table's
5006 * owner, even if the ANALYZE was started by some other user.
5007 */
5009 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5010 conn = GetConnection(user, false, NULL);
5011
5012 /*
5013 * Construct command to get page count for relation.
5014 */
5015 initStringInfo(&sql);
5016 deparseAnalyzeSizeSql(&sql, relation);
5017
5018 res = pgfdw_exec_query(conn, sql.data, NULL);
5019 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5020 pgfdw_report_error(res, conn, sql.data);
5021
5022 if (PQntuples(res) != 1 || PQnfields(res) != 1)
5023 elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
5024 *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
5025 PQclear(res);
5026
5028
5029 return true;
5030}
5031
5032/*
5033 * postgresGetAnalyzeInfoForForeignTable
5034 * Count tuples in foreign table (just get pg_class.reltuples).
5035 *
5036 * can_tablesample determines if the remote relation supports acquiring the
5037 * sample using TABLESAMPLE.
5038 */
5039static double
5041{
5044 PGconn *conn;
5045 StringInfoData sql;
5046 PGresult *res;
5047 double reltuples;
5048 char relkind;
5049
5050 /* assume the remote relation does not support TABLESAMPLE */
5051 *can_tablesample = false;
5052
5053 /*
5054 * Get the connection to use. We do the remote access as the table's
5055 * owner, even if the ANALYZE was started by some other user.
5056 */
5058 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5059 conn = GetConnection(user, false, NULL);
5060
5061 /*
5062 * Construct command to get page count for relation.
5063 */
5064 initStringInfo(&sql);
5065 deparseAnalyzeInfoSql(&sql, relation);
5066
5067 res = pgfdw_exec_query(conn, sql.data, NULL);
5068 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5069 pgfdw_report_error(res, conn, sql.data);
5070
5071 if (PQntuples(res) != 1 || PQnfields(res) != RELSTATS_NUM_FIELDS)
5072 elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
5073 /* We don't use relpages here */
5074 reltuples = strtod(PQgetvalue(res, 0, RELSTATS_RELTUPLES), NULL);
5075 relkind = *(PQgetvalue(res, 0, RELSTATS_RELKIND));
5076 PQclear(res);
5077
5079
5080 /* TABLESAMPLE is supported only for regular tables and matviews */
5081 *can_tablesample = (relkind == RELKIND_RELATION ||
5082 relkind == RELKIND_MATVIEW ||
5083 relkind == RELKIND_PARTITIONED_TABLE);
5084
5085 return reltuples;
5086}
5087
5088/*
5089 * Acquire a random sample of rows from foreign table managed by postgres_fdw.
5090 *
5091 * Selected rows are returned in the caller-allocated array rows[],
5092 * which must have at least targrows entries.
5093 * The actual number of rows selected is returned as the function result.
5094 * We also count the total number of rows in the table and return it into
5095 * *totalrows. Note that *totaldeadrows is always set to 0.
5096 *
5097 * Note that the returned list of rows is not always in order by physical
5098 * position in the table. Therefore, correlation estimates derived later
5099 * may be meaningless, but it's OK because we don't use the estimates
5100 * currently (the planner only pays attention to correlation for indexscans).
5101 */
5102static int
5104 HeapTuple *rows, int targrows,
5105 double *totalrows,
5106 double *totaldeadrows)
5107{
5108 PgFdwAnalyzeState astate;
5110 ForeignServer *server;
5112 PGconn *conn;
5114 PgFdwSamplingMethod method = ANALYZE_SAMPLE_AUTO; /* auto is default */
5115 double sample_frac = -1.0;
5116 double reltuples = -1.0;
5117 unsigned int cursor_number;
5118 StringInfoData sql;
5119 PGresult *res;
5120 char fetch_sql[64];
5121 int fetch_size;
5122 ListCell *lc;
5123
5124 /* Initialize workspace state */
5125 astate.rel = relation;
5127
5128 astate.rows = rows;
5129 astate.targrows = targrows;
5130 astate.numrows = 0;
5131 astate.samplerows = 0;
5132 astate.rowstoskip = -1; /* -1 means not set yet */
5133 reservoir_init_selection_state(&astate.rstate, targrows);
5134
5135 /* Remember ANALYZE context, and create a per-tuple temp context */
5138 "postgres_fdw temporary data",
5140
5141 /*
5142 * Get the connection to use. We do the remote access as the table's
5143 * owner, even if the ANALYZE was started by some other user.
5144 */
5146 server = GetForeignServer(table->serverid);
5147 user = GetUserMapping(relation->rd_rel->relowner, table->serverid);
5148 conn = GetConnection(user, false, NULL);
5149
5150 /* We'll need server version, so fetch it now. */
5152
5153 /*
5154 * What sampling method should we use?
5155 */
5156 foreach(lc, server->options)
5157 {
5158 DefElem *def = (DefElem *) lfirst(lc);
5159
5160 if (strcmp(def->defname, "analyze_sampling") == 0)
5161 {
5162 char *value = defGetString(def);
5163
5164 if (strcmp(value, "off") == 0)
5165 method = ANALYZE_SAMPLE_OFF;
5166 else if (strcmp(value, "auto") == 0)
5167 method = ANALYZE_SAMPLE_AUTO;
5168 else if (strcmp(value, "random") == 0)
5169 method = ANALYZE_SAMPLE_RANDOM;
5170 else if (strcmp(value, "system") == 0)
5171 method = ANALYZE_SAMPLE_SYSTEM;
5172 else if (strcmp(value, "bernoulli") == 0)
5173 method = ANALYZE_SAMPLE_BERNOULLI;
5174
5175 break;
5176 }
5177 }
5178
5179 foreach(lc, table->options)
5180 {
5181 DefElem *def = (DefElem *) lfirst(lc);
5182
5183 if (strcmp(def->defname, "analyze_sampling") == 0)
5184 {
5185 char *value = defGetString(def);
5186
5187 if (strcmp(value, "off") == 0)
5188 method = ANALYZE_SAMPLE_OFF;
5189 else if (strcmp(value, "auto") == 0)
5190 method = ANALYZE_SAMPLE_AUTO;
5191 else if (strcmp(value, "random") == 0)
5192 method = ANALYZE_SAMPLE_RANDOM;
5193 else if (strcmp(value, "system") == 0)
5194 method = ANALYZE_SAMPLE_SYSTEM;
5195 else if (strcmp(value, "bernoulli") == 0)
5196 method = ANALYZE_SAMPLE_BERNOULLI;
5197
5198 break;
5199 }
5200 }
5201
5202 /*
5203 * Error-out if explicitly required one of the TABLESAMPLE methods, but
5204 * the server does not support it.
5205 */
5206 if ((server_version_num < 95000) &&
5207 (method == ANALYZE_SAMPLE_SYSTEM ||
5208 method == ANALYZE_SAMPLE_BERNOULLI))
5209 ereport(ERROR,
5211 errmsg("remote server does not support TABLESAMPLE feature")));
5212
5213 /*
5214 * If we've decided to do remote sampling, calculate the sampling rate. We
5215 * need to get the number of tuples from the remote server, but skip that
5216 * network round-trip if not needed.
5217 */
5218 if (method != ANALYZE_SAMPLE_OFF)
5219 {
5220 bool can_tablesample;
5221
5222 reltuples = postgresGetAnalyzeInfoForForeignTable(relation,
5224
5225 /*
5226 * Make sure we're not choosing TABLESAMPLE when the remote relation
5227 * does not support that. But only do this for "auto" - if the user
5228 * explicitly requested BERNOULLI/SYSTEM, it's better to fail.
5229 */
5230 if (!can_tablesample && (method == ANALYZE_SAMPLE_AUTO))
5231 method = ANALYZE_SAMPLE_RANDOM;
5232
5233 /*
5234 * Remote's reltuples could be 0 or -1 if the table has never been
5235 * vacuumed/analyzed. In that case, disable sampling after all.
5236 */
5237 if ((reltuples <= 0) || (targrows >= reltuples))
5238 method = ANALYZE_SAMPLE_OFF;
5239 else
5240 {
5241 /*
5242 * All supported sampling methods require sampling rate, not
5243 * target rows directly, so we calculate that using the remote
5244 * reltuples value. That's imperfect, because it might be off a
5245 * good deal, but that's not something we can (or should) address
5246 * here.
5247 *
5248 * If reltuples is too low (i.e. when table grew), we'll end up
5249 * sampling more rows - but then we'll apply the local sampling,
5250 * so we get the expected sample size. This is the same outcome as
5251 * without remote sampling.
5252 *
5253 * If reltuples is too high (e.g. after bulk DELETE), we will end
5254 * up sampling too few rows.
5255 *
5256 * We can't really do much better here - we could try sampling a
5257 * bit more rows, but we don't know how off the reltuples value is
5258 * so how much is "a bit more"?
5259 *
5260 * Furthermore, the targrows value for partitions is determined
5261 * based on table size (relpages), which can be off in different
5262 * ways too. Adjusting the sampling rate here might make the issue
5263 * worse.
5264 */
5265 sample_frac = targrows / reltuples;
5266
5267 /*
5268 * We should never get sampling rate outside the valid range
5269 * (between 0.0 and 1.0), because those cases should be covered by
5270 * the previous branch that sets ANALYZE_SAMPLE_OFF.
5271 */
5272 Assert(sample_frac >= 0.0 && sample_frac <= 1.0);
5273 }
5274 }
5275
5276 /*
5277 * For "auto" method, pick the one we believe is best. For servers with
5278 * TABLESAMPLE support we pick BERNOULLI, for old servers we fall-back to
5279 * random() to at least reduce network transfer.
5280 */
5281 if (method == ANALYZE_SAMPLE_AUTO)
5282 {
5283 if (server_version_num < 95000)
5284 method = ANALYZE_SAMPLE_RANDOM;
5285 else
5286 method = ANALYZE_SAMPLE_BERNOULLI;
5287 }
5288
5289 /*
5290 * Construct cursor that retrieves whole rows from remote.
5291 */
5293 initStringInfo(&sql);
5294 appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
5295
5296 deparseAnalyzeSql(&sql, relation, method, sample_frac, &astate.retrieved_attrs);
5297
5298 res = pgfdw_exec_query(conn, sql.data, NULL);
5299 if (PQresultStatus(res) != PGRES_COMMAND_OK)
5300 pgfdw_report_error(res, conn, sql.data);
5301 PQclear(res);
5302
5303 /*
5304 * Determine the fetch size. The default is arbitrary, but shouldn't be
5305 * enormous.
5306 */
5307 fetch_size = 100;
5308 foreach(lc, server->options)
5309 {
5310 DefElem *def = (DefElem *) lfirst(lc);
5311
5312 if (strcmp(def->defname, "fetch_size") == 0)
5313 {
5315 break;
5316 }
5317 }
5318 foreach(lc, table->options)
5319 {
5320 DefElem *def = (DefElem *) lfirst(lc);
5321
5322 if (strcmp(def->defname, "fetch_size") == 0)
5323 {
5325 break;
5326 }
5327 }
5328
5329 /* Construct command to fetch rows from remote. */
5330 snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
5332
5333 /* Retrieve and process rows a batch at a time. */
5334 for (;;)
5335 {
5336 int numrows;
5337 int i;
5338
5339 /* Allow users to cancel long query */
5341
5342 /*
5343 * XXX possible future improvement: if rowstoskip is large, we could
5344 * issue a MOVE rather than physically fetching the rows, then just
5345 * adjust rowstoskip and samplerows appropriately.
5346 */
5347
5348 /* Fetch some rows */
5350 /* On error, report the original query, not the FETCH. */
5351 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5352 pgfdw_report_error(res, conn, sql.data);
5353
5354 /* Process whatever we got. */
5355 numrows = PQntuples(res);
5356 for (i = 0; i < numrows; i++)
5357 analyze_row_processor(res, i, &astate);
5358
5359 PQclear(res);
5360
5361 /* Must be EOF if we didn't get all the rows requested. */
5362 if (numrows < fetch_size)
5363 break;
5364 }
5365
5366 /* Close the cursor, just to be tidy. */
5368
5370
5371 /* We assume that we have no dead tuple. */
5372 *totaldeadrows = 0.0;
5373
5374 /*
5375 * Without sampling, we've retrieved all living tuples from foreign
5376 * server, so report that as totalrows. Otherwise use the reltuples
5377 * estimate we got from the remote side.
5378 */
5379 if (method == ANALYZE_SAMPLE_OFF)
5380 *totalrows = astate.samplerows;
5381 else
5382 *totalrows = reltuples;
5383
5384 /*
5385 * Emit some interesting relation info
5386 */
5387 ereport(elevel,
5388 (errmsg("\"%s\": table contains %.0f rows, %d rows in sample",
5389 RelationGetRelationName(relation),
5390 *totalrows, astate.numrows)));
5391
5392 return astate.numrows;
5393}
5394
5395/*
5396 * Collect sample rows from the result of query.
5397 * - Use all tuples in sample until target # of samples are collected.
5398 * - Subsequently, replace already-sampled tuples randomly.
5399 */
5400static void
5402{
5403 int targrows = astate->targrows;
5404 int pos; /* array index to store tuple in */
5405 MemoryContext oldcontext;
5406
5407 /* Always increment sample row counter. */
5408 astate->samplerows += 1;
5409
5410 /*
5411 * Determine the slot where this sample row should be stored. Set pos to
5412 * negative value to indicate the row should be skipped.
5413 */
5414 if (astate->numrows < targrows)
5415 {
5416 /* First targrows rows are always included into the sample */
5417 pos = astate->numrows++;
5418 }
5419 else
5420 {
5421 /*
5422 * Now we start replacing tuples in the sample until we reach the end
5423 * of the relation. Same algorithm as in acquire_sample_rows in
5424 * analyze.c; see Jeff Vitter's paper.
5425 */
5426 if (astate->rowstoskip < 0)
5427 astate->rowstoskip = reservoir_get_next_S(&astate->rstate, astate->samplerows, targrows);
5428
5429 if (astate->rowstoskip <= 0)
5430 {
5431 /* Choose a random reservoir element to replace. */
5432 pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
5433 Assert(pos >= 0 && pos < targrows);
5434 heap_freetuple(astate->rows[pos]);
5435 }
5436 else
5437 {
5438 /* Skip this tuple. */
5439 pos = -1;
5440 }
5441
5442 astate->rowstoskip -= 1;
5443 }
5444
5445 if (pos >= 0)
5446 {
5447 /*
5448 * Create sample tuple from current result row, and store it in the
5449 * position determined above. The tuple has to be created in anl_cxt.
5450 */
5451 oldcontext = MemoryContextSwitchTo(astate->anl_cxt);
5452
5453 astate->rows[pos] = make_tuple_from_result_row(res, row,
5454 astate->rel,
5455 astate->attinmeta,
5456 astate->retrieved_attrs,
5457 NULL,
5458 astate->temp_cxt);
5459
5460 MemoryContextSwitchTo(oldcontext);
5461 }
5462}
5463
5464/*
5465 * postgresImportForeignStatistics
5466 * Attempt to fetch/restore remote statistics instead of sampling.
5467 */
5468static bool
5469postgresImportForeignStatistics(Relation relation, List *va_cols, int elevel)
5470{
5471 const char *schemaname = NULL;
5472 const char *relname = NULL;
5474 ForeignServer *server;
5475 RemoteStatsResults remstats = {.rel = NULL, .att = NULL};
5477 int attrcnt = 0;
5478 TimestampTz starttime = 0;
5479 bool restore_stats = false;
5480 bool ok = false;
5481 ListCell *lc;
5482
5483 schemaname = get_namespace_name(RelationGetNamespace(relation));
5484 relname = RelationGetRelationName(relation);
5486 server = GetForeignServer(table->serverid);
5487
5488 /*
5489 * Check whether the restore_stats option is enabled on the foreign table.
5490 * If not, silently ignore the foreign table.
5491 *
5492 * Server-level options can be overridden by table-level options, so check
5493 * server-level first.
5494 */
5495 foreach(lc, server->options)
5496 {
5497 DefElem *def = (DefElem *) lfirst(lc);
5498
5499 if (strcmp(def->defname, "restore_stats") == 0)
5500 {
5502 break;
5503 }
5504 }
5505 foreach(lc, table->options)
5506 {
5507 DefElem *def = (DefElem *) lfirst(lc);
5508
5509 if (strcmp(def->defname, "restore_stats") == 0)
5510 {
5512 break;
5513 }
5514 }
5515 if (!restore_stats)
5516 return false;
5517
5518 /*
5519 * We don't currently support statistics import for foreign tables with
5520 * extended statistics objects.
5521 */
5522 if (HasRelationExtStatistics(relation))
5523 {
5526 errmsg("cannot import statistics for foreign table \"%s.%s\" --- this foreign table has extended statistics objects",
5527 schemaname, relname));
5528 return false;
5529 }
5530
5531 /*
5532 * OK, let's do it.
5533 */
5534 ereport(elevel,
5535 (errmsg("importing statistics for foreign table \"%s.%s\"",
5536 schemaname, relname)));
5537
5538 starttime = GetCurrentTimestamp();
5539
5540 ok = fetch_remote_statistics(relation, va_cols,
5541 table, schemaname, relname,
5543
5544 if (ok)
5545 ok = import_fetched_statistics(relation, schemaname, relname,
5547
5548 if (ok)
5549 {
5550 pgstat_report_analyze(relation,
5551 remstats.livetuples, remstats.deadtuples,
5552 (va_cols == NIL), starttime);
5553
5554 ereport(elevel,
5555 (errmsg("finished importing statistics for foreign table \"%s.%s\"",
5556 schemaname, relname)));
5557 }
5558
5559 PQclear(remstats.rel);
5560 PQclear(remstats.att);
5562
5563 return ok;
5564}
5565
5566/*
5567 * Attempt to fetch statistics from a remote server.
5568 */
5569static bool
5571 List *va_cols,
5573 const char *local_schemaname,
5574 const char *local_relname,
5575 int *p_attrcnt,
5578{
5579 const char *remote_schemaname = NULL;
5580 const char *remote_relname = NULL;
5582 PGconn *conn;
5587 int attrcnt = 0;
5588 char relkind;
5589 double reltuples;
5590 bool ok = false;
5591 ListCell *lc;
5592
5593 /*
5594 * Assume the remote schema/relation names are the same as the local name
5595 * unless the foreign table's options tell us otherwise.
5596 */
5599 foreach(lc, table->options)
5600 {
5601 DefElem *def = (DefElem *) lfirst(lc);
5602
5603 if (strcmp(def->defname, "schema_name") == 0)
5605 else if (strcmp(def->defname, "table_name") == 0)
5607 }
5608
5609 /*
5610 * Get connection to the foreign server. Connection manager will
5611 * establish new connection if necessary.
5612 */
5613 user = GetUserMapping(GetUserId(), table->serverid);
5614 conn = GetConnection(user, false, NULL);
5616
5617 /* Fetch relation stats. */
5618 remstats->rel = relstats = fetch_relstats(conn, relation);
5619
5620 /*
5621 * Verify that the remote table is the sort that can have meaningful stats
5622 * in pg_stats.
5623 *
5624 * Note that while relations of kinds RELKIND_INDEX and
5625 * RELKIND_PARTITIONED_INDEX can have rows in pg_stats, they obviously
5626 * can't support a foreign table.
5627 */
5628 relkind = *PQgetvalue(relstats, 0, RELSTATS_RELKIND);
5629 switch (relkind)
5630 {
5631 case RELKIND_RELATION:
5633 case RELKIND_MATVIEW:
5635 break;
5636 default:
5638 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" is of relkind \"%c\" which cannot have statistics",
5641 goto fetch_cleanup;
5642 }
5643
5644 /*
5645 * If the reltuples value > 0, then then we can expect to find attribute
5646 * stats for the remote table.
5647 *
5648 * In v14 or latter, if a reltuples value is -1, it means the table has
5649 * never been analyzed, so we wouldn't expect to find the stats for the
5650 * table; fallback to sampling in that case. If the value is 0, it means
5651 * it was empty; in which case skip the stats and import relation stats
5652 * only.
5653 *
5654 * In versions prior to v14, a value of 0 was ambiguous; it could mean
5655 * that the table had never been analyzed, or that it was empty. Either
5656 * way, we wouldn't expect to find the stats for the table, so we fallback
5657 * to sampling.
5658 */
5660 if (((server_version_num < 140000) && (reltuples == 0)) ||
5661 ((server_version_num >= 140000) && (reltuples == -1)))
5662 {
5664 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" has no relation statistics to import",
5667 goto fetch_cleanup;
5668 }
5669
5670 if (reltuples > 0)
5671 {
5673
5674 *p_remattrmap = remattrmap = build_remattrmap(relation, va_cols,
5675 &attrcnt, &column_list);
5676 *p_attrcnt = attrcnt;
5677
5678 if (attrcnt > 0)
5679 {
5680 /* Fetch attribute stats. */
5685 column_list.data);
5686
5687 /* If any attribute stats are missing, fallback to sampling. */
5692 goto fetch_cleanup;
5693 }
5694 }
5695
5696 /* We assume that we have no dead tuple. */
5697 remstats->deadtuples = 0.0;
5698 remstats->livetuples = reltuples;
5699
5700 ok = true;
5701
5704 return ok;
5705}
5706
5707/*
5708 * Attempt to fetch remote relation stats.
5709 */
5710static PGresult *
5712{
5713 StringInfoData sql;
5714 PGresult *res;
5715
5716 initStringInfo(&sql);
5717 deparseAnalyzeInfoSql(&sql, relation);
5718
5719 res = pgfdw_exec_query(conn, sql.data, NULL);
5720 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5721 pgfdw_report_error(res, conn, sql.data);
5722
5723 if (PQntuples(res) != 1 || PQnfields(res) != RELSTATS_NUM_FIELDS)
5724 elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
5725
5726 return res;
5727}
5728
5729/*
5730 * Attempt to fetch remote attribute stats.
5731 */
5732static PGresult *
5734 const char *remote_schemaname, const char *remote_relname,
5735 const char *column_list)
5736{
5737 StringInfoData sql;
5738 PGresult *res;
5739
5740 initStringInfo(&sql);
5742 "SELECT DISTINCT ON (attname COLLATE \"C\") attname,"
5743 " null_frac,"
5744 " avg_width,"
5745 " n_distinct,"
5746 " most_common_vals,"
5747 " most_common_freqs,"
5748 " histogram_bounds,"
5749 " correlation,");
5750
5751 /* Elements stats are supported since Postgres 9.2 */
5752 if (server_version_num >= 92000)
5754 " most_common_elems,"
5755 " most_common_elem_freqs,"
5756 " elem_count_histogram,");
5757 else
5759 " NULL, NULL, NULL,");
5760
5761 /* Range stats are supported since Postgres 17 */
5762 if (server_version_num >= 170000)
5764 " range_length_histogram,"
5765 " range_empty_frac,"
5766 " range_bounds_histogram");
5767 else
5769 " NULL, NULL, NULL");
5770
5772 " FROM pg_catalog.pg_stats"
5773 " WHERE schemaname = ");
5776 " AND tablename = ");
5778 appendStringInfo(&sql,
5779 " AND attname = ANY(%s)",
5780 column_list);
5781
5782 /* inherited is supported since Postgres 9.0 */
5783 if (server_version_num >= 90000)
5785 " ORDER BY attname COLLATE \"C\", inherited DESC");
5786 else
5788 " ORDER BY attname COLLATE \"C\"");
5789
5790 res = pgfdw_exec_query(conn, sql.data, NULL);
5791 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5792 pgfdw_report_error(res, conn, sql.data);
5793
5794 if (PQnfields(res) != ATTSTATS_NUM_FIELDS)
5795 elog(ERROR, "unexpected result from fetch_attstats query");
5796
5797 return res;
5798}
5799
5800/*
5801 * Build the mapping of local columns to remote columns and create a column
5802 * list used for constructing the fetch_attstats query.
5803 */
5805build_remattrmap(Relation relation, List *va_cols,
5807{
5808 TupleDesc tupdesc = RelationGetDescr(relation);
5810 int attrcnt = 0;
5811
5815 for (int i = 0; i < tupdesc->natts; i++)
5816 {
5817 Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
5818 char *attname = NameStr(attr->attname);
5819 AttrNumber attnum = attr->attnum;
5820 char *remote_attname;
5822 ListCell *lc;
5823
5824 /* If a list is specified, exclude any attnames not in it. */
5825 if (!attname_in_list(attname, va_cols))
5826 continue;
5827
5828 if (!attribute_is_analyzable(relation, attnum, attr, NULL))
5829 continue;
5830
5831 /* If the column_name option is not specified, go with attname. */
5832 remote_attname = attname;
5834 foreach(lc, fc_options)
5835 {
5836 DefElem *def = (DefElem *) lfirst(lc);
5837
5838 if (strcmp(def->defname, "column_name") == 0)
5839 {
5840 remote_attname = defGetString(def);
5841 break;
5842 }
5843 }
5844
5845 if (attrcnt > 0)
5847 deparseStringLiteral(column_list, remote_attname);
5848
5849 remattrmap[attrcnt].local_attnum = attnum;
5850 remattrmap[attrcnt].local_attname = pstrdup(attname);
5851 remattrmap[attrcnt].remote_attname = pstrdup(remote_attname);
5852 remattrmap[attrcnt].res_index = -1;
5853 attrcnt++;
5854 }
5856
5857 /* Sort mapping by remote attribute name if needed. */
5858 if (attrcnt > 1)
5860
5861 *p_attrcnt = attrcnt;
5862 return remattrmap;
5863}
5864
5865/*
5866 * Free the structure created by build_remattrmap().
5867 */
5868static void
5870{
5871 if (!map)
5872 return;
5873
5874 for (int i = 0; i < len; i++)
5875 {
5876 Assert(map[i].local_attname);
5877 pfree(map[i].local_attname);
5878 Assert(map[i].remote_attname);
5879 pfree(map[i].remote_attname);
5880 }
5881
5882 pfree(map);
5883}
5884
5885/*
5886 * Test if an attribute name is in the list.
5887 *
5888 * An empty list means that all attribute names are in the list.
5889 */
5890static bool
5891attname_in_list(const char *attname, List *va_cols)
5892{
5893 ListCell *lc;
5894
5895 if (va_cols == NIL)
5896 return true;
5897
5898 foreach(lc, va_cols)
5899 {
5900 char *col = strVal(lfirst(lc));
5901
5902 if (strcmp(attname, col) == 0)
5903 return true;
5904 }
5905 return false;
5906}
5907
5908/*
5909 * Compare two RemoteAttributeMappings for sorting.
5910 */
5911static int
5912remattrmap_cmp(const void *v1, const void *v2)
5913{
5914 const RemoteAttributeMapping *r1 = v1;
5915 const RemoteAttributeMapping *r2 = v2;
5916
5917 return strcmp(r1->remote_attname, r2->remote_attname);
5918}
5919
5920/*
5921 * Match local columns to result set rows.
5922 *
5923 * As the result set consists of the attribute stats for some/all of distinct
5924 * mapped remote columns in the RemoteAttributeMapping, every entry in it
5925 * should have at most one match in the result set; which is also ordered by
5926 * attname, so we find such pairs by doing a merge join.
5927 *
5928 * Returns true if every entry in it has a match, and false if not.
5929 */
5930static bool
5932 const char *local_schemaname,
5933 const char *local_relname,
5934 const char *remote_schemaname,
5935 const char *remote_relname,
5936 int attrcnt,
5938{
5939 int numrows = PQntuples(res);
5940 int row = -1;
5941
5942 /* No work if there are no stats rows. */
5943 if (numrows == 0)
5944 {
5946 errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" has no attribute statistics to import",
5949 return false;
5950 }
5951
5952 /* Scan all entries in the RemoteAttributeMapping. */
5953 for (int mapidx = 0; mapidx < attrcnt; mapidx++)
5954 {
5955 /*
5956 * First, check whether the entry matches the current stats row, if it
5957 * is set.
5958 */
5959 if (row >= 0 &&
5960 strcmp(remattrmap[mapidx].remote_attname,
5961 PQgetvalue(res, row, ATTSTATS_ATTNAME)) == 0)
5962 {
5963 remattrmap[mapidx].res_index = row;
5964 continue;
5965 }
5966
5967 /*
5968 * If we've exhausted all stats rows, it means the stats for the entry
5969 * are missing.
5970 */
5971 if (row >= numrows - 1)
5972 {
5974 errmsg("could not import statistics for foreign table \"%s.%s\" --- no attribute statistics found for column \"%s\" of remote table \"%s.%s\"",
5976 remattrmap[mapidx].remote_attname,
5978 return false;
5979 }
5980
5981 /* Advance to the next stats row. */
5982 row += 1;
5983
5984 /*
5985 * If the attname in the entry is less than that in the next stats
5986 * row, it means the stats for the entry are missing.
5987 */
5988 if (strcmp(remattrmap[mapidx].remote_attname,
5989 PQgetvalue(res, row, ATTSTATS_ATTNAME)) < 0)
5990 {
5992 errmsg("could not import statistics for foreign table \"%s.%s\" --- no attribute statistics found for column \"%s\" of remote table \"%s.%s\"",
5994 remattrmap[mapidx].remote_attname,
5996 return false;
5997 }
5998
5999 /* We should not have got a stats row we didn't expect. */
6000 if (strcmp(remattrmap[mapidx].remote_attname,
6001 PQgetvalue(res, row, ATTSTATS_ATTNAME)) > 0)
6002 elog(ERROR, "unexpected result from fetch_attstats query");
6003
6004 /* We found a match. */
6005 Assert(strcmp(remattrmap[mapidx].remote_attname,
6006 PQgetvalue(res, row, ATTSTATS_ATTNAME)) == 0);
6007 remattrmap[mapidx].res_index = row;
6008 }
6009
6010 /* We should have exhausted all stats rows. */
6011 if (row < numrows - 1)
6012 elog(ERROR, "unexpected result from fetch_attstats query");
6013
6014 return true;
6015}
6016
6017/*
6018 * Import fetched statistics into the local statistics tables.
6019 */
6020static bool
6022 const char *schemaname,
6023 const char *relname,
6024 int attrcnt,
6027{
6028 PGresult *res;
6030
6031 /* Set the 'version' parameter, which is common to both statistics. */
6032 args[0].value = Int32GetDatum(remstats->version);
6033 args[0].isnull = false;
6034
6035 /*
6036 * We import attribute statistics first, if any, because those are more
6037 * prone to errors. This avoids making a modification of pg_class that
6038 * will just get rolled back by a failed attribute import.
6039 */
6040 res = remstats->att;
6041 if (res != NULL)
6042 {
6044 Assert(PQntuples(res) >= 1);
6045
6046 for (int mapidx = 0; mapidx < attrcnt; mapidx++)
6047 {
6048 int row = remattrmap[mapidx].res_index;
6049 AttrNumber attnum = remattrmap[mapidx].local_attnum;
6050
6051 /* All mappings should have been assigned a result set row. */
6052 Assert(row >= 0);
6053
6054 /* Check for user-requested abort. */
6056
6057 /* Clear existing attribute statistics. */
6058 delete_attribute_statistics(relation, attnum, false);
6059
6060 /* Set the remaining parameters. */
6061 set_float_arg(&args[1],
6063 set_int32_arg(&args[2],
6065 set_float_arg(&args[3],
6067 set_text_arg(&args[4],
6069 set_floatarr_arg(&args[5],
6071 set_text_arg(&args[6],
6073 set_float_arg(&args[7],
6075 set_text_arg(&args[8],
6077 set_floatarr_arg(&args[9],
6079 set_floatarr_arg(&args[10],
6081 set_text_arg(&args[11],
6083 set_float_arg(&args[12],
6085 set_text_arg(&args[13],
6087
6088 /* Try to import the statistics. */
6089 if (!import_attribute_statistics(relation, attnum, false,
6090 &args[0], &args[1], &args[2],
6091 &args[3], &args[4], &args[5],
6092 &args[6], &args[7], &args[8],
6093 &args[9], &args[10], &args[11],
6094 &args[12], &args[13]))
6095 {
6097 errmsg("could not import statistics for foreign table \"%s.%s\" --- attribute statistics import failed for column \"%s\" of this foreign table",
6098 schemaname, relname,
6099 remattrmap[mapidx].local_attname));
6100 return false;
6101 }
6102 }
6103 }
6104
6105 /*
6106 * Import relation statistics.
6107 */
6108 res = remstats->rel;
6109 Assert(res != NULL);
6111 Assert(PQntuples(res) == 1);
6112
6113 /* Set the remaining parameters. */
6115 Assert(!args[1].isnull);
6117 Assert(!args[2].isnull);
6118 args[3].value = (Datum) 0;
6119 args[3].isnull = true;
6120 args[4].value = (Datum) 0;
6121 args[4].isnull = true;
6122
6123 /* Try to import the statistics. */
6124 if (!import_relation_statistics(relation, &args[0], &args[1],
6125 &args[2], &args[3], &args[4]))
6126 {
6128 errmsg("could not import statistics for foreign table \"%s.%s\" --- relation statistics import failed for this foreign table",
6129 schemaname, relname));
6130 return false;
6131 }
6132
6133 return true;
6134}
6135
6136/*
6137 * Convenience routine to fetch the value for the row/column of the PGresult
6138 */
6139static char *
6140get_opt_value(PGresult *res, int row, int col)
6141{
6142 if (PQgetisnull(res, row, col))
6143 return NULL;
6144 return PQgetvalue(res, row, col);
6145}
6146
6147/*
6148 * Convenience routine for setting optional text arguments
6149 */
6150static void
6152{
6153 if (s)
6154 {
6155 arg->value = CStringGetTextDatum(s);
6156 arg->isnull = false;
6157 }
6158 else
6159 {
6160 arg->value = (Datum) 0;
6161 arg->isnull = true;
6162 }
6163}
6164
6165/*
6166 * Convenience routine for setting optional int32 arguments
6167 */
6168static void
6170{
6171 if (s)
6172 {
6173 int32 val = pg_strtoint32(s);
6174
6175 arg->value = Int32GetDatum(val);
6176 arg->isnull = false;
6177 }
6178 else
6179 {
6180 arg->value = (Datum) 0;
6181 arg->isnull = true;
6182 }
6183}
6184
6185/*
6186 * Convenience routine for setting optional uint32 arguments
6187 */
6188static void
6190{
6191 if (s)
6192 {
6193 uint32 val = uint32in_subr(s, NULL, "uint32", NULL);
6194
6195 arg->value = UInt32GetDatum(val);
6196 arg->isnull = false;
6197 }
6198 else
6199 {
6200 arg->value = (Datum) 0;
6201 arg->isnull = true;
6202 }
6203}
6204
6205/*
6206 * Convenience routine for setting optional float arguments
6207 */
6208static void
6210{
6211 if (s)
6212 {
6213 float4 val = float4in_internal((char *) s, NULL, "float", s, NULL);
6214
6215 arg->value = Float4GetDatum(val);
6216 arg->isnull = false;
6217 }
6218 else
6219 {
6220 arg->value = (Datum) 0;
6221 arg->isnull = true;
6222 }
6223}
6224
6225/*
6226 * Convenience routine for setting optional float[] arguments
6227 */
6228static void
6230{
6231 if (s)
6232 {
6233 FmgrInfo flinfo;
6234 Datum val;
6235
6236 fmgr_info(F_ARRAY_IN, &flinfo);
6237 val = InputFunctionCall(&flinfo, (char *) s, FLOAT4OID, -1);
6238
6239 arg->value = val;
6240 arg->isnull = false;
6241 }
6242 else
6243 {
6244 arg->value = (Datum) 0;
6245 arg->isnull = true;
6246 }
6247}
6248
6249/*
6250 * Import a foreign schema
6251 */
6252static List *
6254{
6255 List *commands = NIL;
6256 bool import_collate = true;
6257 bool import_default = false;
6258 bool import_generated = true;
6259 bool import_not_null = true;
6260 ForeignServer *server;
6262 PGconn *conn;
6264 PGresult *res;
6265 int numrows,
6266 i;
6267 ListCell *lc;
6268
6269 /* Parse statement options */
6270 foreach(lc, stmt->options)
6271 {
6272 DefElem *def = (DefElem *) lfirst(lc);
6273
6274 if (strcmp(def->defname, "import_collate") == 0)
6276 else if (strcmp(def->defname, "import_default") == 0)
6278 else if (strcmp(def->defname, "import_generated") == 0)
6280 else if (strcmp(def->defname, "import_not_null") == 0)
6282 else
6283 ereport(ERROR,
6285 errmsg("invalid option \"%s\"", def->defname)));
6286 }
6287
6288 /*
6289 * Get connection to the foreign server. Connection manager will
6290 * establish new connection if necessary.
6291 */
6292 server = GetForeignServer(serverOid);
6294 conn = GetConnection(mapping, false, NULL);
6295
6296 /* Don't attempt to import collation if remote server hasn't got it */
6297 if (PQserverVersion(conn) < 90100)
6298 import_collate = false;
6299
6300 /* Create workspace for strings */
6302
6303 /* Check that the schema really exists */
6304 appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
6305 deparseStringLiteral(&buf, stmt->remote_schema);
6306
6307 res = pgfdw_exec_query(conn, buf.data, NULL);
6308 if (PQresultStatus(res) != PGRES_TUPLES_OK)
6309 pgfdw_report_error(res, conn, buf.data);
6310
6311 if (PQntuples(res) != 1)
6312 ereport(ERROR,
6314 errmsg("schema \"%s\" is not present on foreign server \"%s\"",
6315 stmt->remote_schema, server->servername)));
6316
6317 PQclear(res);
6319
6320 /*
6321 * Fetch all table data from this schema, possibly restricted by EXCEPT or
6322 * LIMIT TO. (We don't actually need to pay any attention to EXCEPT/LIMIT
6323 * TO here, because the core code will filter the statements we return
6324 * according to those lists anyway. But it should save a few cycles to
6325 * not process excluded tables in the first place.)
6326 *
6327 * Import table data for partitions only when they are explicitly
6328 * specified in LIMIT TO clause. Otherwise ignore them and only include
6329 * the definitions of the root partitioned tables to allow access to the
6330 * complete remote data set locally in the schema imported.
6331 *
6332 * Note: because we run the connection with search_path restricted to
6333 * pg_catalog, the format_type() and pg_get_expr() outputs will always
6334 * include a schema name for types/functions in other schemas, which is
6335 * what we want.
6336 */
6338 "SELECT relname, "
6339 " attname, "
6340 " format_type(atttypid, atttypmod), "
6341 " attnotnull, "
6342 " pg_get_expr(adbin, adrelid), ");
6343
6344 /* Generated columns are supported since Postgres 12 */
6345 if (PQserverVersion(conn) >= 120000)
6347 " attgenerated, ");
6348 else
6350 " NULL, ");
6351
6352 if (import_collate)
6354 " collname, "
6355 " collnsp.nspname ");
6356 else
6358 " NULL, NULL ");
6359
6361 "FROM pg_class c "
6362 " JOIN pg_namespace n ON "
6363 " relnamespace = n.oid "
6364 " LEFT JOIN pg_attribute a ON "
6365 " attrelid = c.oid AND attnum > 0 "
6366 " AND NOT attisdropped "
6367 " LEFT JOIN pg_attrdef ad ON "
6368 " adrelid = c.oid AND adnum = attnum ");
6369
6370 if (import_collate)
6372 " LEFT JOIN pg_collation coll ON "
6373 " coll.oid = attcollation "
6374 " LEFT JOIN pg_namespace collnsp ON "
6375 " collnsp.oid = collnamespace ");
6376
6378 "WHERE c.relkind IN ("
6384 " AND n.nspname = ");
6385 deparseStringLiteral(&buf, stmt->remote_schema);
6386
6387 /* Partitions are supported since Postgres 10 */
6388 if (PQserverVersion(conn) >= 100000 &&
6389 stmt->list_type != FDW_IMPORT_SCHEMA_LIMIT_TO)
6390 appendStringInfoString(&buf, " AND NOT c.relispartition ");
6391
6392 /* Apply restrictions for LIMIT TO and EXCEPT */
6393 if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
6394 stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
6395 {
6396 bool first_item = true;
6397
6398 appendStringInfoString(&buf, " AND c.relname ");
6399 if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
6400 appendStringInfoString(&buf, "NOT ");
6401 appendStringInfoString(&buf, "IN (");
6402
6403 /* Append list of table names within IN clause */
6404 foreach(lc, stmt->table_list)
6405 {
6406 RangeVar *rv = (RangeVar *) lfirst(lc);
6407
6408 if (first_item)
6409 first_item = false;
6410 else
6413 }
6415 }
6416
6417 /* Append ORDER BY at the end of query to ensure output ordering */
6418 appendStringInfoString(&buf, " ORDER BY c.relname, a.attnum");
6419
6420 /* Fetch the data */
6421 res = pgfdw_exec_query(conn, buf.data, NULL);
6422 if (PQresultStatus(res) != PGRES_TUPLES_OK)
6423 pgfdw_report_error(res, conn, buf.data);
6424
6425 /* Process results */
6426 numrows = PQntuples(res);
6427 /* note: incrementation of i happens in inner loop's while() test */
6428 for (i = 0; i < numrows;)
6429 {
6430 char *tablename = PQgetvalue(res, i, 0);
6431 bool first_item = true;
6432
6434 appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
6435 quote_identifier(tablename));
6436
6437 /* Scan all rows for this table */
6438 do
6439 {
6440 char *attname;
6441 char *typename;
6442 char *attnotnull;
6443 char *attgenerated;
6444 char *attdefault;
6445 char *collname;
6446 char *collnamespace;
6447
6448 /* If table has no columns, we'll see nulls here */
6449 if (PQgetisnull(res, i, 1))
6450 continue;
6451
6452 attname = PQgetvalue(res, i, 1);
6453 typename = PQgetvalue(res, i, 2);
6454 attnotnull = PQgetvalue(res, i, 3);
6455 attdefault = PQgetisnull(res, i, 4) ? NULL :
6456 PQgetvalue(res, i, 4);
6457 attgenerated = PQgetisnull(res, i, 5) ? NULL :
6458 PQgetvalue(res, i, 5);
6459 collname = PQgetisnull(res, i, 6) ? NULL :
6460 PQgetvalue(res, i, 6);
6461 collnamespace = PQgetisnull(res, i, 7) ? NULL :
6462 PQgetvalue(res, i, 7);
6463
6464 if (first_item)
6465 first_item = false;
6466 else
6467 appendStringInfoString(&buf, ",\n");
6468
6469 /* Print column name and type */
6470 appendStringInfo(&buf, " %s %s",
6472 typename);
6473
6474 /*
6475 * Add column_name option so that renaming the foreign table's
6476 * column doesn't break the association to the underlying column.
6477 */
6478 appendStringInfoString(&buf, " OPTIONS (column_name ");
6481
6482 /* Add COLLATE if needed */
6483 if (import_collate && collname != NULL && collnamespace != NULL)
6484 appendStringInfo(&buf, " COLLATE %s.%s",
6486 quote_identifier(collname));
6487
6488 /* Add DEFAULT if needed */
6489 if (import_default && attdefault != NULL &&
6490 (!attgenerated || !attgenerated[0]))
6491 appendStringInfo(&buf, " DEFAULT %s", attdefault);
6492
6493 /* Add GENERATED if needed */
6494 if (import_generated && attgenerated != NULL &&
6495 attgenerated[0] == ATTRIBUTE_GENERATED_STORED)
6496 {
6499 " GENERATED ALWAYS AS (%s) STORED",
6500 attdefault);
6501 }
6502
6503 /* Add NOT NULL if needed */
6504 if (import_not_null && attnotnull[0] == 't')
6505 appendStringInfoString(&buf, " NOT NULL");
6506 }
6507 while (++i < numrows &&
6508 strcmp(PQgetvalue(res, i, 0), tablename) == 0);
6509
6510 /*
6511 * Add server name and table-level options. We specify remote schema
6512 * and table name as options (the latter to ensure that renaming the
6513 * foreign table doesn't break the association).
6514 */
6515 appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
6516 quote_identifier(server->servername));
6517
6518 appendStringInfoString(&buf, "schema_name ");
6519 deparseStringLiteral(&buf, stmt->remote_schema);
6520 appendStringInfoString(&buf, ", table_name ");
6521 deparseStringLiteral(&buf, tablename);
6522
6524
6525 commands = lappend(commands, pstrdup(buf.data));
6526 }
6527 PQclear(res);
6528
6530
6531 return commands;
6532}
6533
6534/*
6535 * Check if reltarget is safe enough to push down semi-join. Reltarget is not
6536 * safe, if it contains references to inner rel relids, which do not belong to
6537 * outer rel.
6538 */
6539static bool
6541{
6542 List *vars;
6543 ListCell *lc;
6544 bool ok = true;
6545
6546 Assert(joinrel->reltarget);
6547
6549
6550 foreach(lc, vars)
6551 {
6552 Var *var = (Var *) lfirst(lc);
6553
6554 if (!IsA(var, Var))
6555 continue;
6556
6557 if (bms_is_member(var->varno, innerrel->relids))
6558 {
6559 /*
6560 * The planner can create semi-join, which refers to inner rel
6561 * vars in its target list. However, we deparse semi-join as an
6562 * exists() subquery, so can't handle references to inner rel in
6563 * the target list.
6564 */
6565 Assert(!bms_is_member(var->varno, outerrel->relids));
6566 ok = false;
6567 break;
6568 }
6569 }
6570 return ok;
6571}
6572
6573/*
6574 * Assess whether the join between inner and outer relations can be pushed down
6575 * to the foreign server. As a side effect, save information we obtain in this
6576 * function to PgFdwRelationInfo passed in.
6577 */
6578static bool
6580 RelOptInfo *outerrel, RelOptInfo *innerrel,
6581 JoinPathExtraData *extra)
6582{
6586 ListCell *lc;
6587 List *joinclauses;
6588
6589 /*
6590 * We support pushing down INNER, LEFT, RIGHT, FULL OUTER and SEMI joins.
6591 * Constructing queries representing ANTI joins is hard, hence not
6592 * considered right now.
6593 */
6594 if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
6595 jointype != JOIN_RIGHT && jointype != JOIN_FULL &&
6596 jointype != JOIN_SEMI)
6597 return false;
6598
6599 /*
6600 * We can't push down semi-join if its reltarget is not safe
6601 */
6602 if ((jointype == JOIN_SEMI) && !semijoin_target_ok(root, joinrel, outerrel, innerrel))
6603 return false;
6604
6605 /*
6606 * If either of the joining relations is marked as unsafe to pushdown, the
6607 * join can not be pushed down.
6608 */
6609 fpinfo = (PgFdwRelationInfo *) joinrel->fdw_private;
6610 fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private;
6611 fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private;
6612 if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
6613 !fpinfo_i || !fpinfo_i->pushdown_safe)
6614 return false;
6615
6616 /*
6617 * If joining relations have local conditions, those conditions are
6618 * required to be applied before joining the relations. Hence the join can
6619 * not be pushed down.
6620 */
6621 if (fpinfo_o->local_conds || fpinfo_i->local_conds)
6622 return false;
6623
6624 /*
6625 * Merge FDW options. We might be tempted to do this after we have deemed
6626 * the foreign join to be OK. But we must do this beforehand so that we
6627 * know which quals can be evaluated on the foreign server, which might
6628 * depend on shippable_extensions.
6629 */
6630 fpinfo->server = fpinfo_o->server;
6632
6633 /*
6634 * Separate restrict list into join quals and pushed-down (other) quals.
6635 *
6636 * Join quals belonging to an outer join must all be shippable, else we
6637 * cannot execute the join remotely. Add such quals to 'joinclauses'.
6638 *
6639 * Add other quals to fpinfo->remote_conds if they are shippable, else to
6640 * fpinfo->local_conds. In an inner join it's okay to execute conditions
6641 * either locally or remotely; the same is true for pushed-down conditions
6642 * at an outer join.
6643 *
6644 * Note we might return failure after having already scribbled on
6645 * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
6646 * won't consult those lists again if we deem the join unshippable.
6647 */
6648 joinclauses = NIL;
6649 foreach(lc, extra->restrictlist)
6650 {
6652 bool is_remote_clause = is_foreign_expr(root, joinrel,
6653 rinfo->clause);
6654
6655 if (IS_OUTER_JOIN(jointype) &&
6656 !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
6657 {
6658 if (!is_remote_clause)
6659 return false;
6660 joinclauses = lappend(joinclauses, rinfo);
6661 }
6662 else
6663 {
6664 if (is_remote_clause)
6665 fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
6666 else
6667 fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
6668 }
6669 }
6670
6671 /*
6672 * deparseExplicitTargetList() isn't smart enough to handle anything other
6673 * than a Var. In particular, if there's some PlaceHolderVar that would
6674 * need to be evaluated within this join tree (because there's an upper
6675 * reference to a quantity that may go to NULL as a result of an outer
6676 * join), then we can't try to push the join down because we'll fail when
6677 * we get to deparseExplicitTargetList(). However, a PlaceHolderVar that
6678 * needs to be evaluated *at the top* of this join tree is OK, because we
6679 * can do that locally after fetching the results from the remote side.
6680 */
6681 foreach(lc, root->placeholder_list)
6682 {
6684 Relids relids;
6685
6686 /* PlaceHolderInfo refers to parent relids, not child relids. */
6687 relids = IS_OTHER_REL(joinrel) ?
6688 joinrel->top_parent_relids : joinrel->relids;
6689
6690 if (bms_is_subset(phinfo->ph_eval_at, relids) &&
6691 bms_nonempty_difference(relids, phinfo->ph_eval_at))
6692 return false;
6693 }
6694
6695 /* Save the join clauses, for later use. */
6696 fpinfo->joinclauses = joinclauses;
6697
6698 fpinfo->outerrel = outerrel;
6699 fpinfo->innerrel = innerrel;
6700 fpinfo->jointype = jointype;
6701
6702 /*
6703 * By default, both the input relations are not required to be deparsed as
6704 * subqueries, but there might be some relations covered by the input
6705 * relations that are required to be deparsed as subqueries, so save the
6706 * relids of those relations for later use by the deparser.
6707 */
6708 fpinfo->make_outerrel_subquery = false;
6709 fpinfo->make_innerrel_subquery = false;
6710 Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids));
6711 Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids));
6712 fpinfo->lower_subquery_rels = bms_union(fpinfo_o->lower_subquery_rels,
6713 fpinfo_i->lower_subquery_rels);
6714 fpinfo->hidden_subquery_rels = bms_union(fpinfo_o->hidden_subquery_rels,
6715 fpinfo_i->hidden_subquery_rels);
6716
6717 /*
6718 * Pull the other remote conditions from the joining relations into join
6719 * clauses or other remote clauses (remote_conds) of this relation
6720 * wherever possible. This avoids building subqueries at every join step.
6721 *
6722 * For an inner join, clauses from both the relations are added to the
6723 * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from
6724 * the outer side are added to remote_conds since those can be evaluated
6725 * after the join is evaluated. The clauses from inner side are added to
6726 * the joinclauses, since they need to be evaluated while constructing the
6727 * join.
6728 *
6729 * For SEMI-JOIN clauses from inner relation can not be added to
6730 * remote_conds, but should be treated as join clauses (as they are
6731 * deparsed to EXISTS subquery, where inner relation can be referred). A
6732 * list of relation ids, which can't be referred to from higher levels, is
6733 * preserved as a hidden_subquery_rels list.
6734 *
6735 * For a FULL OUTER JOIN, the other clauses from either relation can not
6736 * be added to the joinclauses or remote_conds, since each relation acts
6737 * as an outer relation for the other.
6738 *
6739 * The joining sides can not have local conditions, thus no need to test
6740 * shippability of the clauses being pulled up.
6741 */
6742 switch (jointype)
6743 {
6744 case JOIN_INNER:
6745 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
6746 fpinfo_i->remote_conds);
6747 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
6748 fpinfo_o->remote_conds);
6749 break;
6750
6751 case JOIN_LEFT:
6752
6753 /*
6754 * When semi-join is involved in the inner or outer part of the
6755 * left join, it's deparsed as a subquery, and we can't refer to
6756 * its vars on the upper level.
6757 */
6758 if (bms_is_empty(fpinfo_i->hidden_subquery_rels))
6759 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
6760 fpinfo_i->remote_conds);
6761 if (bms_is_empty(fpinfo_o->hidden_subquery_rels))
6762 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
6763 fpinfo_o->remote_conds);
6764 break;
6765
6766 case JOIN_RIGHT:
6767
6768 /*
6769 * When semi-join is involved in the inner or outer part of the
6770 * right join, it's deparsed as a subquery, and we can't refer to
6771 * its vars on the upper level.
6772 */
6773 if (bms_is_empty(fpinfo_o->hidden_subquery_rels))
6774 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
6775 fpinfo_o->remote_conds);
6776 if (bms_is_empty(fpinfo_i->hidden_subquery_rels))
6777 fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
6778 fpinfo_i->remote_conds);
6779 break;
6780
6781 case JOIN_SEMI:
6782 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
6783 fpinfo_i->remote_conds);
6784 fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
6785 fpinfo->remote_conds);
6786 fpinfo->remote_conds = list_copy(fpinfo_o->remote_conds);
6787 fpinfo->hidden_subquery_rels = bms_union(fpinfo->hidden_subquery_rels,
6788 innerrel->relids);
6789 break;
6790
6791 case JOIN_FULL:
6792
6793 /*
6794 * In this case, if any of the input relations has conditions, we
6795 * need to deparse that relation as a subquery so that the
6796 * conditions can be evaluated before the join. Remember it in
6797 * the fpinfo of this relation so that the deparser can take
6798 * appropriate action. Also, save the relids of base relations
6799 * covered by that relation for later use by the deparser.
6800 */
6801 if (fpinfo_o->remote_conds)
6802 {
6803 fpinfo->make_outerrel_subquery = true;
6804 fpinfo->lower_subquery_rels =
6805 bms_add_members(fpinfo->lower_subquery_rels,
6806 outerrel->relids);
6807 }
6808 if (fpinfo_i->remote_conds)
6809 {
6810 fpinfo->make_innerrel_subquery = true;
6811 fpinfo->lower_subquery_rels =
6812 bms_add_members(fpinfo->lower_subquery_rels,
6813 innerrel->relids);
6814 }
6815 break;
6816
6817 default:
6818 /* Should not happen, we have just checked this above */
6819 elog(ERROR, "unsupported join type %d", jointype);
6820 }
6821
6822 /*
6823 * For an inner join, all restrictions can be treated alike. Treating the
6824 * pushed down conditions as join conditions allows a top level full outer
6825 * join to be deparsed without requiring subqueries.
6826 */
6827 if (jointype == JOIN_INNER)
6828 {
6829 Assert(!fpinfo->joinclauses);
6830 fpinfo->joinclauses = fpinfo->remote_conds;
6831 fpinfo->remote_conds = NIL;
6832 }
6833 else if (jointype == JOIN_LEFT || jointype == JOIN_RIGHT || jointype == JOIN_FULL)
6834 {
6835 /*
6836 * Conditions, generated from semi-joins, should be evaluated before
6837 * LEFT/RIGHT/FULL join.
6838 */
6839 if (!bms_is_empty(fpinfo_o->hidden_subquery_rels))
6840 {
6841 fpinfo->make_outerrel_subquery = true;
6842 fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, outerrel->relids);
6843 }
6844
6845 if (!bms_is_empty(fpinfo_i->hidden_subquery_rels))
6846 {
6847 fpinfo->make_innerrel_subquery = true;
6848 fpinfo->lower_subquery_rels = bms_add_members(fpinfo->lower_subquery_rels, innerrel->relids);
6849 }
6850 }
6851
6852 /* Mark that this join can be pushed down safely */
6853 fpinfo->pushdown_safe = true;
6854
6855 /* Get user mapping */
6856 if (fpinfo->use_remote_estimate)
6857 {
6858 if (fpinfo_o->use_remote_estimate)
6859 fpinfo->user = fpinfo_o->user;
6860 else
6861 fpinfo->user = fpinfo_i->user;
6862 }
6863 else
6864 fpinfo->user = NULL;
6865
6866 /*
6867 * Set # of retrieved rows and cached relation costs to some negative
6868 * value, so that we can detect when they are set to some sensible values,
6869 * during one (usually the first) of the calls to estimate_path_cost_size.
6870 */
6871 fpinfo->retrieved_rows = -1;
6872 fpinfo->rel_startup_cost = -1;
6873 fpinfo->rel_total_cost = -1;
6874
6875 /*
6876 * Set the string describing this join relation to be used in EXPLAIN
6877 * output of corresponding ForeignScan. Note that the decoration we add
6878 * to the base relation names mustn't include any digits, or it'll confuse
6879 * postgresExplainForeignScan.
6880 */
6881 fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)",
6882 fpinfo_o->relation_name,
6883 get_jointype_name(fpinfo->jointype),
6884 fpinfo_i->relation_name);
6885
6886 /*
6887 * Set the relation index. This is defined as the position of this
6888 * joinrel in the join_rel_list list plus the length of the rtable list.
6889 * Note that since this joinrel is at the end of the join_rel_list list
6890 * when we are called, we can get the position by list_length.
6891 */
6892 Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */
6893 fpinfo->relation_index =
6894 list_length(root->parse->rtable) + list_length(root->join_rel_list);
6895
6896 return true;
6897}
6898
6899static void
6901 Path *epq_path, List *restrictlist)
6902{
6903 List *useful_pathkeys_list = NIL; /* List of all pathkeys */
6904 ListCell *lc;
6905
6907
6908 /*
6909 * Before creating sorted paths, arrange for the passed-in EPQ path, if
6910 * any, to return columns needed by the parent ForeignScan node so that
6911 * they will propagate up through Sort nodes injected below, if necessary.
6912 */
6914 {
6915 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
6916 PathTarget *target = copy_pathtarget(epq_path->pathtarget);
6917
6918 /* Include columns required for evaluating PHVs in the tlist. */
6920 pull_var_clause((Node *) target->exprs,
6922
6923 /* Include columns required for evaluating the local conditions. */
6924 foreach(lc, fpinfo->local_conds)
6925 {
6927
6929 pull_var_clause((Node *) rinfo->clause,
6931 }
6932
6933 /*
6934 * If we have added any new columns, adjust the tlist of the EPQ path.
6935 *
6936 * Note: the plan created using this path will only be used to execute
6937 * EPQ checks, where accuracy of the plan cost and width estimates
6938 * would not be important, so we do not do set_pathtarget_cost_width()
6939 * for the new pathtarget here. See also postgresGetForeignPlan().
6940 */
6941 if (list_length(target->exprs) > list_length(epq_path->pathtarget->exprs))
6942 {
6943 /* The EPQ path is a join path, so it is projection-capable. */
6945
6946 /*
6947 * Use create_projection_path() here, so as to avoid modifying it
6948 * in place.
6949 */
6951 rel,
6952 epq_path,
6953 target);
6954 }
6955 }
6956
6957 /* Create one path for each set of pathkeys we found above. */
6958 foreach(lc, useful_pathkeys_list)
6959 {
6960 double rows;
6961 int width;
6962 int disabled_nodes;
6963 Cost startup_cost;
6964 Cost total_cost;
6967
6969 &rows, &width, &disabled_nodes,
6970 &startup_cost, &total_cost);
6971
6972 /*
6973 * The EPQ path must be at least as well sorted as the path itself, in
6974 * case it gets used as input to a mergejoin.
6975 */
6977 if (sorted_epq_path != NULL &&
6979 sorted_epq_path->pathkeys))
6980 sorted_epq_path = (Path *)
6982 rel,
6985 -1.0);
6986
6987 if (IS_SIMPLE_REL(rel))
6988 add_path(rel, (Path *)
6990 NULL,
6991 rows,
6992 disabled_nodes,
6993 startup_cost,
6994 total_cost,
6996 rel->lateral_relids,
6998 NIL, /* no fdw_restrictinfo
6999 * list */
7000 NIL));
7001 else
7002 add_path(rel, (Path *)
7004 NULL,
7005 rows,
7006 disabled_nodes,
7007 startup_cost,
7008 total_cost,
7010 rel->lateral_relids,
7012 restrictlist,
7013 NIL));
7014 }
7015}
7016
7017/*
7018 * Parse options from foreign server and apply them to fpinfo.
7019 *
7020 * New options might also require tweaking merge_fdw_options().
7021 */
7022static void
7024{
7025 ListCell *lc;
7026
7027 foreach(lc, fpinfo->server->options)
7028 {
7029 DefElem *def = (DefElem *) lfirst(lc);
7030
7031 if (strcmp(def->defname, "use_remote_estimate") == 0)
7032 fpinfo->use_remote_estimate = defGetBoolean(def);
7033 else if (strcmp(def->defname, "fdw_startup_cost") == 0)
7034 (void) parse_real(defGetString(def), &fpinfo->fdw_startup_cost, 0,
7035 NULL);
7036 else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
7037 (void) parse_real(defGetString(def), &fpinfo->fdw_tuple_cost, 0,
7038 NULL);
7039 else if (strcmp(def->defname, "extensions") == 0)
7040 fpinfo->shippable_extensions =
7042 else if (strcmp(def->defname, "fetch_size") == 0)
7043 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
7044 else if (strcmp(def->defname, "async_capable") == 0)
7045 fpinfo->async_capable = defGetBoolean(def);
7046 }
7047}
7048
7049/*
7050 * Parse options from foreign table and apply them to fpinfo.
7051 *
7052 * New options might also require tweaking merge_fdw_options().
7053 */
7054static void
7056{
7057 ListCell *lc;
7058
7059 foreach(lc, fpinfo->table->options)
7060 {
7061 DefElem *def = (DefElem *) lfirst(lc);
7062
7063 if (strcmp(def->defname, "use_remote_estimate") == 0)
7064 fpinfo->use_remote_estimate = defGetBoolean(def);
7065 else if (strcmp(def->defname, "fetch_size") == 0)
7066 (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
7067 else if (strcmp(def->defname, "async_capable") == 0)
7068 fpinfo->async_capable = defGetBoolean(def);
7069 }
7070}
7071
7072/*
7073 * Merge FDW options from input relations into a new set of options for a join
7074 * or an upper rel.
7075 *
7076 * For a join relation, FDW-specific information about the inner and outer
7077 * relations is provided using fpinfo_i and fpinfo_o. For an upper relation,
7078 * fpinfo_o provides the information for the input relation; fpinfo_i is
7079 * expected to NULL.
7080 */
7081static void
7085{
7086 /* We must always have fpinfo_o. */
7088
7089 /* fpinfo_i may be NULL, but if present the servers must both match. */
7090 Assert(!fpinfo_i ||
7091 fpinfo_i->server->serverid == fpinfo_o->server->serverid);
7092
7093 /*
7094 * Copy the server specific FDW options. (For a join, both relations come
7095 * from the same server, so the server options should have the same value
7096 * for both relations.)
7097 */
7098 fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
7099 fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
7100 fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
7101 fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
7102 fpinfo->fetch_size = fpinfo_o->fetch_size;
7103 fpinfo->async_capable = fpinfo_o->async_capable;
7104
7105 /* Merge the table level options from either side of the join. */
7106 if (fpinfo_i)
7107 {
7108 /*
7109 * We'll prefer to use remote estimates for this join if any table
7110 * from either side of the join is using remote estimates. This is
7111 * most likely going to be preferred since they're already willing to
7112 * pay the price of a round trip to get the remote EXPLAIN. In any
7113 * case it's not entirely clear how we might otherwise handle this
7114 * best.
7115 */
7116 fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
7117 fpinfo_i->use_remote_estimate;
7118
7119 /*
7120 * Set fetch size to maximum of the joining sides, since we are
7121 * expecting the rows returned by the join to be proportional to the
7122 * relation sizes.
7123 */
7124 fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
7125
7126 /*
7127 * We'll prefer to consider this join async-capable if any table from
7128 * either side of the join is considered async-capable. This would be
7129 * reasonable because in that case the foreign server would have its
7130 * own resources to scan that table asynchronously, and the join could
7131 * also be computed asynchronously using the resources.
7132 */
7133 fpinfo->async_capable = fpinfo_o->async_capable ||
7134 fpinfo_i->async_capable;
7135 }
7136}
7137
7138/*
7139 * postgresGetForeignJoinPaths
7140 * Add possible ForeignPath to joinrel, if join is safe to push down.
7141 */
7142static void
7144 RelOptInfo *joinrel,
7145 RelOptInfo *outerrel,
7146 RelOptInfo *innerrel,
7147 JoinType jointype,
7148 JoinPathExtraData *extra)
7149{
7152 double rows;
7153 int width;
7154 int disabled_nodes;
7155 Cost startup_cost;
7156 Cost total_cost;
7157 Path *epq_path; /* Path to create plan to be executed when
7158 * EvalPlanQual gets triggered. */
7159
7160 /*
7161 * Skip if this join combination has been considered already.
7162 */
7163 if (joinrel->fdw_private)
7164 return;
7165
7166 /*
7167 * This code does not work for joins with lateral references, since those
7168 * must have parameterized paths, which we don't generate yet.
7169 */
7170 if (!bms_is_empty(joinrel->lateral_relids))
7171 return;
7172
7173 /*
7174 * Create unfinished PgFdwRelationInfo entry which is used to indicate
7175 * that the join relation is already considered, so that we won't waste
7176 * time in judging safety of join pushdown and adding the same paths again
7177 * if found safe. Once we know that this join can be pushed down, we fill
7178 * the entry.
7179 */
7181 fpinfo->pushdown_safe = false;
7182 joinrel->fdw_private = fpinfo;
7183 /* attrs_used is only for base relations. */
7184 fpinfo->attrs_used = NULL;
7185
7186 /*
7187 * If there is a possibility that EvalPlanQual will be executed, we need
7188 * to be able to reconstruct the row using scans of the base relations.
7189 * GetExistingLocalJoinPath will find a suitable path for this purpose in
7190 * the path list of the joinrel, if one exists. We must be careful to
7191 * call it before adding any ForeignPath, since the ForeignPath might
7192 * dominate the only suitable local path available. We also do it before
7193 * calling foreign_join_ok(), since that function updates fpinfo and marks
7194 * it as pushable if the join is found to be pushable.
7195 */
7196 if (root->parse->commandType == CMD_DELETE ||
7197 root->parse->commandType == CMD_UPDATE ||
7198 root->rowMarks)
7199 {
7201 if (!epq_path)
7202 {
7203 elog(DEBUG3, "could not push down foreign join because a local path suitable for EPQ checks was not found");
7204 return;
7205 }
7206 }
7207 else
7208 epq_path = NULL;
7209
7210 if (!foreign_join_ok(root, joinrel, jointype, outerrel, innerrel, extra))
7211 {
7212 /* Free path required for EPQ if we copied one; we don't need it now */
7213 if (epq_path)
7214 pfree(epq_path);
7215 return;
7216 }
7217
7218 /*
7219 * Compute the selectivity and cost of the local_conds, so we don't have
7220 * to do it over again for each path. The best we can do for these
7221 * conditions is to estimate selectivity on the basis of local statistics.
7222 * The local conditions are applied after the join has been computed on
7223 * the remote side like quals in WHERE clause, so pass jointype as
7224 * JOIN_INNER.
7225 */
7226 fpinfo->local_conds_sel = clauselist_selectivity(root,
7227 fpinfo->local_conds,
7228 0,
7229 JOIN_INNER,
7230 NULL);
7231 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
7232
7233 /*
7234 * If we are going to estimate costs locally, estimate the join clause
7235 * selectivity here while we have special join info.
7236 */
7237 if (!fpinfo->use_remote_estimate)
7238 fpinfo->joinclause_sel = clauselist_selectivity(root, fpinfo->joinclauses,
7239 0, fpinfo->jointype,
7240 extra->sjinfo);
7241
7242 /* Estimate costs for bare join relation */
7244 &rows, &width, &disabled_nodes,
7245 &startup_cost, &total_cost);
7246 /* Now update this information in the joinrel */
7247 joinrel->rows = rows;
7248 joinrel->reltarget->width = width;
7249 fpinfo->rows = rows;
7250 fpinfo->width = width;
7251 fpinfo->disabled_nodes = disabled_nodes;
7252 fpinfo->startup_cost = startup_cost;
7253 fpinfo->total_cost = total_cost;
7254
7255 /*
7256 * Create a new join path and add it to the joinrel which represents a
7257 * join between foreign tables.
7258 */
7260 joinrel,
7261 NULL, /* default pathtarget */
7262 rows,
7263 disabled_nodes,
7264 startup_cost,
7265 total_cost,
7266 NIL, /* no pathkeys */
7267 joinrel->lateral_relids,
7268 epq_path,
7269 extra->restrictlist,
7270 NIL); /* no fdw_private */
7271
7272 /* Add generated path into joinrel by add_path(). */
7273 add_path(joinrel, (Path *) joinpath);
7274
7275 /* Consider pathkeys for the join relation */
7277 extra->restrictlist);
7278
7279 /* XXX Consider parameterized paths for the join relation */
7280}
7281
7282/*
7283 * Assess whether the aggregation, grouping and having operations can be pushed
7284 * down to the foreign server. As a side effect, save information we obtain in
7285 * this function to PgFdwRelationInfo of the input relation.
7286 */
7287static bool
7289 Node *havingQual)
7290{
7291 Query *query = root->parse;
7292 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
7293 PathTarget *grouping_target = grouped_rel->reltarget;
7295 ListCell *lc;
7296 int i;
7297 List *tlist = NIL;
7298
7299 /* We currently don't support pushing Grouping Sets. */
7300 if (query->groupingSets)
7301 return false;
7302
7303 /* Get the fpinfo of the underlying scan relation. */
7304 ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
7305
7306 /*
7307 * If underlying scan relation has any local conditions, those conditions
7308 * are required to be applied before performing aggregation. Hence the
7309 * aggregate cannot be pushed down.
7310 */
7311 if (ofpinfo->local_conds)
7312 return false;
7313
7314 /*
7315 * Examine grouping expressions, as well as other expressions we'd need to
7316 * compute, and check whether they are safe to push down to the foreign
7317 * server. All GROUP BY expressions will be part of the grouping target
7318 * and thus there is no need to search for them separately. Add grouping
7319 * expressions into target list which will be passed to foreign server.
7320 *
7321 * A tricky fine point is that we must not put any expression into the
7322 * target list that is just a foreign param (that is, something that
7323 * deparse.c would conclude has to be sent to the foreign server). If we
7324 * do, the expression will also appear in the fdw_exprs list of the plan
7325 * node, and setrefs.c will get confused and decide that the fdw_exprs
7326 * entry is actually a reference to the fdw_scan_tlist entry, resulting in
7327 * a broken plan. Somewhat oddly, it's OK if the expression contains such
7328 * a node, as long as it's not at top level; then no match is possible.
7329 */
7330 i = 0;
7331 foreach(lc, grouping_target->exprs)
7332 {
7333 Expr *expr = (Expr *) lfirst(lc);
7335 ListCell *l;
7336
7337 /*
7338 * Check whether this expression is part of GROUP BY clause. Note we
7339 * check the whole GROUP BY clause not just processed_groupClause,
7340 * because we will ship all of it, cf. appendGroupByClause.
7341 */
7343 {
7345
7346 /*
7347 * If any GROUP BY expression is not shippable, then we cannot
7348 * push down aggregation to the foreign server.
7349 */
7350 if (!is_foreign_expr(root, grouped_rel, expr))
7351 return false;
7352
7353 /*
7354 * If it would be a foreign param, we can't put it into the tlist,
7355 * so we have to fail.
7356 */
7357 if (is_foreign_param(root, grouped_rel, expr))
7358 return false;
7359
7360 /*
7361 * Pushable, so add to tlist. We need to create a TLE for this
7362 * expression and apply the sortgroupref to it. We cannot use
7363 * add_to_flat_tlist() here because that avoids making duplicate
7364 * entries in the tlist. If there are duplicate entries with
7365 * distinct sortgrouprefs, we have to duplicate that situation in
7366 * the output tlist.
7367 */
7368 tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
7369 tle->ressortgroupref = sgref;
7370 tlist = lappend(tlist, tle);
7371 }
7372 else
7373 {
7374 /*
7375 * Non-grouping expression we need to compute. Can we ship it
7376 * as-is to the foreign server?
7377 */
7378 if (is_foreign_expr(root, grouped_rel, expr) &&
7379 !is_foreign_param(root, grouped_rel, expr))
7380 {
7381 /* Yes, so add to tlist as-is; OK to suppress duplicates */
7382 tlist = add_to_flat_tlist(tlist, list_make1(expr));
7383 }
7384 else
7385 {
7386 /* Not pushable as a whole; extract its Vars and aggregates */
7387 List *aggvars;
7388
7389 aggvars = pull_var_clause((Node *) expr,
7391
7392 /*
7393 * If any aggregate expression is not shippable, then we
7394 * cannot push down aggregation to the foreign server. (We
7395 * don't have to check is_foreign_param, since that certainly
7396 * won't return true for any such expression.)
7397 */
7398 if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
7399 return false;
7400
7401 /*
7402 * Add aggregates, if any, into the targetlist. Plain Vars
7403 * outside an aggregate can be ignored, because they should be
7404 * either same as some GROUP BY column or part of some GROUP
7405 * BY expression. In either case, they are already part of
7406 * the targetlist and thus no need to add them again. In fact
7407 * including plain Vars in the tlist when they do not match a
7408 * GROUP BY column would cause the foreign server to complain
7409 * that the shipped query is invalid.
7410 */
7411 foreach(l, aggvars)
7412 {
7413 Expr *aggref = (Expr *) lfirst(l);
7414
7415 if (IsA(aggref, Aggref))
7416 tlist = add_to_flat_tlist(tlist, list_make1(aggref));
7417 }
7418 }
7419 }
7420
7421 i++;
7422 }
7423
7424 /*
7425 * Classify the pushable and non-pushable HAVING clauses and save them in
7426 * remote_conds and local_conds of the grouped rel's fpinfo.
7427 */
7428 if (havingQual)
7429 {
7430 foreach(lc, (List *) havingQual)
7431 {
7432 Expr *expr = (Expr *) lfirst(lc);
7433 RestrictInfo *rinfo;
7434
7435 /*
7436 * Currently, the core code doesn't wrap havingQuals in
7437 * RestrictInfos, so we must make our own.
7438 */
7439 Assert(!IsA(expr, RestrictInfo));
7440 rinfo = make_restrictinfo(root,
7441 expr,
7442 true,
7443 false,
7444 false,
7445 false,
7446 root->qual_security_level,
7447 grouped_rel->relids,
7448 NULL,
7449 NULL);
7450 if (is_foreign_expr(root, grouped_rel, expr))
7451 fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
7452 else
7453 fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
7454 }
7455 }
7456
7457 /*
7458 * If there are any local conditions, pull Vars and aggregates from it and
7459 * check whether they are safe to pushdown or not.
7460 */
7461 if (fpinfo->local_conds)
7462 {
7463 List *aggvars = NIL;
7464
7465 foreach(lc, fpinfo->local_conds)
7466 {
7468
7470 pull_var_clause((Node *) rinfo->clause,
7472 }
7473
7474 foreach(lc, aggvars)
7475 {
7476 Expr *expr = (Expr *) lfirst(lc);
7477
7478 /*
7479 * If aggregates within local conditions are not safe to push
7480 * down, then we cannot push down the query. Vars are already
7481 * part of GROUP BY clause which are checked above, so no need to
7482 * access them again here. Again, we need not check
7483 * is_foreign_param for a foreign aggregate.
7484 */
7485 if (IsA(expr, Aggref))
7486 {
7487 if (!is_foreign_expr(root, grouped_rel, expr))
7488 return false;
7489
7490 tlist = add_to_flat_tlist(tlist, list_make1(expr));
7491 }
7492 }
7493 }
7494
7495 /* Store generated targetlist */
7496 fpinfo->grouped_tlist = tlist;
7497
7498 /* Safe to pushdown */
7499 fpinfo->pushdown_safe = true;
7500
7501 /*
7502 * Set # of retrieved rows and cached relation costs to some negative
7503 * value, so that we can detect when they are set to some sensible values,
7504 * during one (usually the first) of the calls to estimate_path_cost_size.
7505 */
7506 fpinfo->retrieved_rows = -1;
7507 fpinfo->rel_startup_cost = -1;
7508 fpinfo->rel_total_cost = -1;
7509
7510 /*
7511 * Set the string describing this grouped relation to be used in EXPLAIN
7512 * output of corresponding ForeignScan. Note that the decoration we add
7513 * to the base relation name mustn't include any digits, or it'll confuse
7514 * postgresExplainForeignScan.
7515 */
7516 fpinfo->relation_name = psprintf("Aggregate on (%s)",
7517 ofpinfo->relation_name);
7518
7519 return true;
7520}
7521
7522/*
7523 * postgresGetForeignUpperPaths
7524 * Add paths for post-join operations like aggregation, grouping etc. if
7525 * corresponding operations are safe to push down.
7526 */
7527static void
7530 void *extra)
7531{
7533
7534 /*
7535 * If input rel is not safe to pushdown, then simply return as we cannot
7536 * perform any post-join operations on the foreign server.
7537 */
7538 if (!input_rel->fdw_private ||
7539 !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
7540 return;
7541
7542 /* Ignore stages we don't support; and skip any duplicate calls. */
7543 if ((stage != UPPERREL_GROUP_AGG &&
7544 stage != UPPERREL_ORDERED &&
7545 stage != UPPERREL_FINAL) ||
7546 output_rel->fdw_private)
7547 return;
7548
7550 fpinfo->pushdown_safe = false;
7551 fpinfo->stage = stage;
7552 output_rel->fdw_private = fpinfo;
7553
7554 switch (stage)
7555 {
7556 case UPPERREL_GROUP_AGG:
7558 (GroupPathExtraData *) extra);
7559 break;
7560 case UPPERREL_ORDERED:
7562 break;
7563 case UPPERREL_FINAL:
7565 (FinalPathExtraData *) extra);
7566 break;
7567 default:
7568 elog(ERROR, "unexpected upper relation: %d", (int) stage);
7569 break;
7570 }
7571}
7572
7573/*
7574 * add_foreign_grouping_paths
7575 * Add foreign path for grouping and/or aggregation.
7576 *
7577 * Given input_rel represents the underlying scan. The paths are added to the
7578 * given grouped_rel.
7579 */
7580static void
7582 RelOptInfo *grouped_rel,
7583 GroupPathExtraData *extra)
7584{
7585 Query *parse = root->parse;
7586 PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
7587 PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
7589 double rows;
7590 int width;
7591 int disabled_nodes;
7592 Cost startup_cost;
7593 Cost total_cost;
7594
7595 /* Nothing to be done, if there is no grouping or aggregation required. */
7596 if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
7597 !root->hasHavingQual)
7598 return;
7599
7602
7603 /* save the input_rel as outerrel in fpinfo */
7604 fpinfo->outerrel = input_rel;
7605
7606 /*
7607 * Copy foreign table, foreign server, user mapping, FDW options etc.
7608 * details from the input relation's fpinfo.
7609 */
7610 fpinfo->table = ifpinfo->table;
7611 fpinfo->server = ifpinfo->server;
7612 fpinfo->user = ifpinfo->user;
7614
7615 /*
7616 * Assess if it is safe to push down aggregation and grouping.
7617 *
7618 * Use HAVING qual from extra. In case of child partition, it will have
7619 * translated Vars.
7620 */
7621 if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
7622 return;
7623
7624 /*
7625 * Compute the selectivity and cost of the local_conds, so we don't have
7626 * to do it over again for each path. (Currently we create just a single
7627 * path here, but in future it would be possible that we build more paths
7628 * such as pre-sorted paths as in postgresGetForeignPaths and
7629 * postgresGetForeignJoinPaths.) The best we can do for these conditions
7630 * is to estimate selectivity on the basis of local statistics.
7631 */
7632 fpinfo->local_conds_sel = clauselist_selectivity(root,
7633 fpinfo->local_conds,
7634 0,
7635 JOIN_INNER,
7636 NULL);
7637
7638 cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
7639
7640 /* Estimate the cost of push down */
7641 estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
7642 &rows, &width, &disabled_nodes,
7643 &startup_cost, &total_cost);
7644
7645 /* Now update this information in the fpinfo */
7646 fpinfo->rows = rows;
7647 fpinfo->width = width;
7648 fpinfo->disabled_nodes = disabled_nodes;
7649 fpinfo->startup_cost = startup_cost;
7650 fpinfo->total_cost = total_cost;
7651
7652 /* Create and add foreign path to the grouping relation. */
7654 grouped_rel,
7655 grouped_rel->reltarget,
7656 rows,
7657 disabled_nodes,
7658 startup_cost,
7659 total_cost,
7660 NIL, /* no pathkeys */
7661 NULL,
7662 NIL, /* no fdw_restrictinfo list */
7663 NIL); /* no fdw_private */
7664
7665 /* Add generated path into grouped_rel by add_path(). */
7666 add_path(grouped_rel, (Path *) grouppath);
7667}
7668
7669/*
7670 * add_foreign_ordered_paths
7671 * Add foreign paths for performing the final sort remotely.
7672 *
7673 * Given input_rel contains the source-data Paths. The paths are added to the
7674 * given ordered_rel.
7675 */
7676static void
7679{
7680 Query *parse = root->parse;
7681 PgFdwRelationInfo *ifpinfo = input_rel->fdw_private;
7682 PgFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
7684 double rows;
7685 int width;
7686 int disabled_nodes;
7687 Cost startup_cost;
7688 Cost total_cost;
7689 List *fdw_private;
7691 ListCell *lc;
7692
7693 /* Shouldn't get here unless the query has ORDER BY */
7694 Assert(parse->sortClause);
7695
7696 /* We don't support cases where there are any SRFs in the targetlist */
7697 if (parse->hasTargetSRFs)
7698 return;
7699
7700 /* Save the input_rel as outerrel in fpinfo */
7701 fpinfo->outerrel = input_rel;
7702
7703 /*
7704 * Copy foreign table, foreign server, user mapping, FDW options etc.
7705 * details from the input relation's fpinfo.
7706 */
7707 fpinfo->table = ifpinfo->table;
7708 fpinfo->server = ifpinfo->server;
7709 fpinfo->user = ifpinfo->user;
7711
7712 /*
7713 * If the input_rel is a base or join relation, we would already have
7714 * considered pushing down the final sort to the remote server when
7715 * creating pre-sorted foreign paths for that relation, because the
7716 * query_pathkeys is set to the root->sort_pathkeys in that case (see
7717 * standard_qp_callback()).
7718 */
7719 if (input_rel->reloptkind == RELOPT_BASEREL ||
7720 input_rel->reloptkind == RELOPT_JOINREL)
7721 {
7722 Assert(root->query_pathkeys == root->sort_pathkeys);
7723
7724 /* Safe to push down if the query_pathkeys is safe to push down */
7725 fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
7726
7727 return;
7728 }
7729
7730 /* The input_rel should be a grouping relation */
7731 Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
7732 ifpinfo->stage == UPPERREL_GROUP_AGG);
7733
7734 /*
7735 * We try to create a path below by extending a simple foreign path for
7736 * the underlying grouping relation to perform the final sort remotely,
7737 * which is stored into the fdw_private list of the resulting path.
7738 */
7739
7740 /* Assess if it is safe to push down the final sort */
7741 foreach(lc, root->sort_pathkeys)
7742 {
7744 EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
7745
7746 /*
7747 * is_foreign_expr would detect volatile expressions as well, but
7748 * checking ec_has_volatile here saves some cycles.
7749 */
7750 if (pathkey_ec->ec_has_volatile)
7751 return;
7752
7753 /*
7754 * Can't push down the sort if pathkey's opfamily is not shippable.
7755 */
7757 fpinfo))
7758 return;
7759
7760 /*
7761 * The EC must contain a shippable EM that is computed in input_rel's
7762 * reltarget, else we can't push down the sort.
7763 */
7765 pathkey_ec,
7766 input_rel) == NULL)
7767 return;
7768 }
7769
7770 /* Safe to push down */
7771 fpinfo->pushdown_safe = true;
7772
7773 /* Construct PgFdwPathExtraData */
7775 fpextra->target = root->upper_targets[UPPERREL_ORDERED];
7776 fpextra->has_final_sort = true;
7777
7778 /* Estimate the costs of performing the final sort remotely */
7780 &rows, &width, &disabled_nodes,
7781 &startup_cost, &total_cost);
7782
7783 /*
7784 * Build the fdw_private list that will be used by postgresGetForeignPlan.
7785 * Items in the list must match order in enum FdwPathPrivateIndex.
7786 */
7787 fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
7788
7789 /* Create foreign ordering path */
7791 input_rel,
7792 root->upper_targets[UPPERREL_ORDERED],
7793 rows,
7794 disabled_nodes,
7795 startup_cost,
7796 total_cost,
7797 root->sort_pathkeys,
7798 NULL, /* no extra plan */
7799 NIL, /* no fdw_restrictinfo
7800 * list */
7801 fdw_private);
7802
7803 /* and add it to the ordered_rel */
7805}
7806
7807/*
7808 * add_foreign_final_paths
7809 * Add foreign paths for performing the final processing remotely.
7810 *
7811 * Given input_rel contains the source-data Paths. The paths are added to the
7812 * given final_rel.
7813 */
7814static void
7817 FinalPathExtraData *extra)
7818{
7819 Query *parse = root->parse;
7822 bool has_final_sort = false;
7823 List *pathkeys = NIL;
7825 bool save_use_remote_estimate = false;
7826 double rows;
7827 int width;
7828 int disabled_nodes;
7829 Cost startup_cost;
7830 Cost total_cost;
7831 List *fdw_private;
7833
7834 /*
7835 * Currently, we only support this for SELECT commands
7836 */
7837 if (parse->commandType != CMD_SELECT)
7838 return;
7839
7840 /*
7841 * No work if there is no FOR UPDATE/SHARE clause and if there is no need
7842 * to add a LIMIT node
7843 */
7844 if (!parse->rowMarks && !extra->limit_needed)
7845 return;
7846
7847 /* We don't support cases where there are any SRFs in the targetlist */
7848 if (parse->hasTargetSRFs)
7849 return;
7850
7851 /* Save the input_rel as outerrel in fpinfo */
7852 fpinfo->outerrel = input_rel;
7853
7854 /*
7855 * Copy foreign table, foreign server, user mapping, FDW options etc.
7856 * details from the input relation's fpinfo.
7857 */
7858 fpinfo->table = ifpinfo->table;
7859 fpinfo->server = ifpinfo->server;
7860 fpinfo->user = ifpinfo->user;
7862
7863 /*
7864 * If there is no need to add a LIMIT node, there might be a ForeignPath
7865 * in the input_rel's pathlist that implements all behavior of the query.
7866 * Note: we would already have accounted for the query's FOR UPDATE/SHARE
7867 * (if any) before we get here.
7868 */
7869 if (!extra->limit_needed)
7870 {
7871 ListCell *lc;
7872
7873 Assert(parse->rowMarks);
7874
7875 /*
7876 * Grouping and aggregation are not supported with FOR UPDATE/SHARE,
7877 * so the input_rel should be a base, join, or ordered relation; and
7878 * if it's an ordered relation, its input relation should be a base or
7879 * join relation.
7880 */
7881 Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7882 input_rel->reloptkind == RELOPT_JOINREL ||
7883 (input_rel->reloptkind == RELOPT_UPPER_REL &&
7884 ifpinfo->stage == UPPERREL_ORDERED &&
7885 (ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
7886 ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
7887
7888 foreach(lc, input_rel->pathlist)
7889 {
7890 Path *path = (Path *) lfirst(lc);
7891
7892 /*
7893 * apply_scanjoin_target_to_paths() uses create_projection_path()
7894 * to adjust each of its input paths if needed, whereas
7895 * create_ordered_paths() uses apply_projection_to_path() to do
7896 * that. So the former might have put a ProjectionPath on top of
7897 * the ForeignPath; look through ProjectionPath and see if the
7898 * path underneath it is ForeignPath.
7899 */
7900 if (IsA(path, ForeignPath) ||
7901 (IsA(path, ProjectionPath) &&
7902 IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
7903 {
7904 /*
7905 * Create foreign final path; this gets rid of a
7906 * no-longer-needed outer plan (if any), which makes the
7907 * EXPLAIN output look cleaner
7908 */
7910 path->parent,
7911 path->pathtarget,
7912 path->rows,
7913 path->disabled_nodes,
7914 path->startup_cost,
7915 path->total_cost,
7916 path->pathkeys,
7917 NULL, /* no extra plan */
7918 NIL, /* no fdw_restrictinfo
7919 * list */
7920 NIL); /* no fdw_private */
7921
7922 /* and add it to the final_rel */
7924
7925 /* Safe to push down */
7926 fpinfo->pushdown_safe = true;
7927
7928 return;
7929 }
7930 }
7931
7932 /*
7933 * If we get here it means no ForeignPaths; since we would already
7934 * have considered pushing down all operations for the query to the
7935 * remote server, give up on it.
7936 */
7937 return;
7938 }
7939
7940 Assert(extra->limit_needed);
7941
7942 /*
7943 * If the input_rel is an ordered relation, replace the input_rel with its
7944 * input relation
7945 */
7946 if (input_rel->reloptkind == RELOPT_UPPER_REL &&
7947 ifpinfo->stage == UPPERREL_ORDERED)
7948 {
7949 input_rel = ifpinfo->outerrel;
7950 ifpinfo = (PgFdwRelationInfo *) input_rel->fdw_private;
7951 has_final_sort = true;
7952 pathkeys = root->sort_pathkeys;
7953 }
7954
7955 /* The input_rel should be a base, join, or grouping relation */
7956 Assert(input_rel->reloptkind == RELOPT_BASEREL ||
7957 input_rel->reloptkind == RELOPT_JOINREL ||
7958 (input_rel->reloptkind == RELOPT_UPPER_REL &&
7959 ifpinfo->stage == UPPERREL_GROUP_AGG));
7960
7961 /*
7962 * We try to create a path below by extending a simple foreign path for
7963 * the underlying base, join, or grouping relation to perform the final
7964 * sort (if has_final_sort) and the LIMIT restriction remotely, which is
7965 * stored into the fdw_private list of the resulting path. (We
7966 * re-estimate the costs of sorting the underlying relation, if
7967 * has_final_sort.)
7968 */
7969
7970 /*
7971 * Assess if it is safe to push down the LIMIT and OFFSET to the remote
7972 * server
7973 */
7974
7975 /*
7976 * If the underlying relation has any local conditions, the LIMIT/OFFSET
7977 * cannot be pushed down.
7978 */
7979 if (ifpinfo->local_conds)
7980 return;
7981
7982 /*
7983 * If the query has FETCH FIRST .. WITH TIES, 1) it must have ORDER BY as
7984 * well, which is used to determine which additional rows tie for the last
7985 * place in the result set, and 2) ORDER BY must already have been
7986 * determined to be safe to push down before we get here. So in that case
7987 * the FETCH clause is safe to push down with ORDER BY if the remote
7988 * server is v13 or later, but if not, the remote query will fail entirely
7989 * for lack of support for it. Since we do not currently have a way to do
7990 * a remote-version check (without accessing the remote server), disable
7991 * pushing the FETCH clause for now.
7992 */
7993 if (parse->limitOption == LIMIT_OPTION_WITH_TIES)
7994 return;
7995
7996 /*
7997 * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
7998 * not safe to remote.
7999 */
8000 if (!is_foreign_expr(root, input_rel, (Expr *) parse->limitOffset) ||
8001 !is_foreign_expr(root, input_rel, (Expr *) parse->limitCount))
8002 return;
8003
8004 /* Safe to push down */
8005 fpinfo->pushdown_safe = true;
8006
8007 /* Construct PgFdwPathExtraData */
8009 fpextra->target = root->upper_targets[UPPERREL_FINAL];
8010 fpextra->has_final_sort = has_final_sort;
8011 fpextra->has_limit = extra->limit_needed;
8012 fpextra->limit_tuples = extra->limit_tuples;
8013 fpextra->count_est = extra->count_est;
8014 fpextra->offset_est = extra->offset_est;
8015
8016 /*
8017 * Estimate the costs of performing the final sort and the LIMIT
8018 * restriction remotely. If has_final_sort is false, we wouldn't need to
8019 * execute EXPLAIN anymore if use_remote_estimate, since the costs can be
8020 * roughly estimated using the costs we already have for the underlying
8021 * relation, in the same way as when use_remote_estimate is false. Since
8022 * it's pretty expensive to execute EXPLAIN, force use_remote_estimate to
8023 * false in that case.
8024 */
8025 if (!fpextra->has_final_sort)
8026 {
8027 save_use_remote_estimate = ifpinfo->use_remote_estimate;
8028 ifpinfo->use_remote_estimate = false;
8029 }
8031 &rows, &width, &disabled_nodes,
8032 &startup_cost, &total_cost);
8033 if (!fpextra->has_final_sort)
8034 ifpinfo->use_remote_estimate = save_use_remote_estimate;
8035
8036 /*
8037 * Build the fdw_private list that will be used by postgresGetForeignPlan.
8038 * Items in the list must match order in enum FdwPathPrivateIndex.
8039 */
8040 fdw_private = list_make2(makeBoolean(has_final_sort),
8041 makeBoolean(extra->limit_needed));
8042
8043 /*
8044 * Create foreign final path; this gets rid of a no-longer-needed outer
8045 * plan (if any), which makes the EXPLAIN output look cleaner
8046 */
8048 input_rel,
8049 root->upper_targets[UPPERREL_FINAL],
8050 rows,
8051 disabled_nodes,
8052 startup_cost,
8053 total_cost,
8054 pathkeys,
8055 NULL, /* no extra plan */
8056 NIL, /* no fdw_restrictinfo list */
8057 fdw_private);
8058
8059 /* and add it to the final_rel */
8061}
8062
8063/*
8064 * postgresIsForeignPathAsyncCapable
8065 * Check whether a given ForeignPath node is async-capable.
8066 */
8067static bool
8069{
8070 RelOptInfo *rel = ((Path *) path)->parent;
8071 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
8072
8073 return fpinfo->async_capable;
8074}
8075
8076/*
8077 * postgresForeignAsyncRequest
8078 * Asynchronously request next tuple from a foreign PostgreSQL table.
8079 */
8080static void
8085
8086/*
8087 * postgresForeignAsyncConfigureWait
8088 * Configure a file descriptor event for which we wish to wait.
8089 */
8090static void
8092{
8093 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8094 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8095 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
8096 AppendState *requestor = (AppendState *) areq->requestor;
8097 WaitEventSet *set = requestor->as_eventset;
8098
8099 /* This should not be called unless callback_pending */
8100 Assert(areq->callback_pending);
8101
8102 /*
8103 * If process_pending_request() has been invoked on the given request
8104 * before we get here, we might have some tuples already; in which case
8105 * complete the request
8106 */
8107 if (fsstate->next_tuple < fsstate->num_tuples)
8108 {
8110 if (areq->request_complete)
8111 return;
8112 Assert(areq->callback_pending);
8113 }
8114
8115 /* We must have run out of tuples */
8116 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8117
8118 /* The core code would have registered postmaster death event */
8120
8121 /* Begin an asynchronous data fetch if not already done */
8122 if (!pendingAreq)
8124 else if (pendingAreq->requestor != areq->requestor)
8125 {
8126 /*
8127 * This is the case when the in-process request was made by another
8128 * Append. Note that it might be useless to process the request made
8129 * by that Append, because the query might not need tuples from that
8130 * Append anymore; so we avoid processing it to begin a fetch for the
8131 * given request if possible. If there are any child subplans of the
8132 * same parent that are ready for new requests, skip the given
8133 * request. Likewise, if there are any configured events other than
8134 * the postmaster death event, skip it. Otherwise, process the
8135 * in-process request, then begin a fetch to configure the event
8136 * below, because we might otherwise end up with no configured events
8137 * other than the postmaster death event.
8138 */
8139 if (!bms_is_empty(requestor->as_needrequest))
8140 return;
8141 if (GetNumRegisteredWaitEvents(set) > 1)
8142 return;
8143 process_pending_request(pendingAreq);
8145 }
8146 else if (pendingAreq->requestee != areq->requestee)
8147 {
8148 /*
8149 * This is the case when the in-process request was made by the same
8150 * parent but for a different child. Since we configure only the
8151 * event for the request made for that child, skip the given request.
8152 */
8153 return;
8154 }
8155 else
8156 Assert(pendingAreq == areq);
8157
8159 NULL, areq);
8160}
8161
8162/*
8163 * postgresForeignAsyncNotify
8164 * Fetch some more tuples from a file descriptor that becomes ready,
8165 * requesting next tuple.
8166 */
8167static void
8169{
8170 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8171 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8172
8173 /* The core code would have initialized the callback_pending flag */
8174 Assert(!areq->callback_pending);
8175
8176 /*
8177 * If process_pending_request() has been invoked on the given request
8178 * before we get here, we might have some tuples already; in which case
8179 * produce the next tuple
8180 */
8181 if (fsstate->next_tuple < fsstate->num_tuples)
8182 {
8184 return;
8185 }
8186
8187 /* We must have run out of tuples */
8188 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8189
8190 /* The request should be currently in-process */
8191 Assert(fsstate->conn_state->pendingAreq == areq);
8192
8193 /* On error, report the original query, not the FETCH. */
8194 if (!PQconsumeInput(fsstate->conn))
8195 pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
8196
8197 fetch_more_data(node);
8198
8200}
8201
8202/*
8203 * Asynchronously produce next tuple from a foreign PostgreSQL table.
8204 */
8205static void
8207{
8208 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8209 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8210 AsyncRequest *pendingAreq = fsstate->conn_state->pendingAreq;
8212
8213 /* This should not be called if the request is currently in-process */
8214 Assert(areq != pendingAreq);
8215
8216 /* Fetch some more tuples, if we've run out */
8217 if (fsstate->next_tuple >= fsstate->num_tuples)
8218 {
8219 /* No point in another fetch if we already detected EOF, though */
8220 if (!fsstate->eof_reached)
8221 {
8222 /* Mark the request as pending for a callback */
8224 /* Begin another fetch if requested and if no pending request */
8225 if (fetch && !pendingAreq)
8227 }
8228 else
8229 {
8230 /* There's nothing more to do; just return a NULL pointer */
8231 result = NULL;
8232 /* Mark the request as complete */
8234 }
8235 return;
8236 }
8237
8238 /* Get a tuple from the ForeignScan node */
8239 result = areq->requestee->ExecProcNodeReal(areq->requestee);
8240 if (!TupIsNull(result))
8241 {
8242 /* Mark the request as complete */
8244 return;
8245 }
8246
8247 /* We must have run out of tuples */
8248 Assert(fsstate->next_tuple >= fsstate->num_tuples);
8249
8250 /* Fetch some more tuples, if we've not detected EOF yet */
8251 if (!fsstate->eof_reached)
8252 {
8253 /* Mark the request as pending for a callback */
8255 /* Begin another fetch if requested and if no pending request */
8256 if (fetch && !pendingAreq)
8258 }
8259 else
8260 {
8261 /* There's nothing more to do; just return a NULL pointer */
8262 result = NULL;
8263 /* Mark the request as complete */
8265 }
8266}
8267
8268/*
8269 * Begin an asynchronous data fetch.
8270 *
8271 * Note: this function assumes there is no currently-in-progress asynchronous
8272 * data fetch.
8273 *
8274 * Note: fetch_more_data must be called to fetch the result.
8275 */
8276static void
8278{
8279 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8280 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8281 char sql[64];
8282
8283 Assert(!fsstate->conn_state->pendingAreq);
8284
8285 /* Create the cursor synchronously. */
8286 if (!fsstate->cursor_exists)
8287 create_cursor(node);
8288
8289 /* We will send this query, but not wait for the response. */
8290 snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
8291 fsstate->fetch_size, fsstate->cursor_number);
8292
8293 if (!PQsendQuery(fsstate->conn, sql))
8294 pgfdw_report_error(NULL, fsstate->conn, fsstate->query);
8295
8296 /* Remember that the request is in process */
8297 fsstate->conn_state->pendingAreq = areq;
8298}
8299
8300/*
8301 * Process a pending asynchronous request.
8302 */
8303void
8305{
8306 ForeignScanState *node = (ForeignScanState *) areq->requestee;
8307 PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
8308
8309 /* The request would have been pending for a callback */
8310 Assert(areq->callback_pending);
8311
8312 /* The request should be currently in-process */
8313 Assert(fsstate->conn_state->pendingAreq == areq);
8314
8315 fetch_more_data(node);
8316
8317 /*
8318 * If we didn't get any tuples, must be end of data; complete the request
8319 * now. Otherwise, we postpone completing the request until we are called
8320 * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
8321 */
8322 if (fsstate->next_tuple >= fsstate->num_tuples)
8323 {
8324 /* Unlike AsyncNotify, we unset callback_pending ourselves */
8325 areq->callback_pending = false;
8326 /* Mark the request as complete */
8328 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
8330 }
8331}
8332
8333/*
8334 * Complete a pending asynchronous request.
8335 */
8336static void
8338{
8339 /* The request would have been pending for a callback */
8340 Assert(areq->callback_pending);
8341
8342 /* Unlike AsyncNotify, we unset callback_pending ourselves */
8343 areq->callback_pending = false;
8344
8345 /* We begin a fetch afterwards if necessary; don't fetch */
8347
8348 /* Unlike AsyncNotify, we call ExecAsyncResponse ourselves */
8350
8351 /* Also, we do instrumentation ourselves, if required */
8352 if (areq->requestee->instrument)
8353 InstrUpdateTupleCount(areq->requestee->instrument,
8354 TupIsNull(areq->result) ? 0.0 : 1.0);
8355}
8356
8357/*
8358 * Create a tuple from the specified row of the PGresult.
8359 *
8360 * rel is the local representation of the foreign table, attinmeta is
8361 * conversion data for the rel's tupdesc, and retrieved_attrs is an
8362 * integer list of the table column numbers present in the PGresult.
8363 * fsstate is the ForeignScan plan node's execution state.
8364 * temp_context is a working context that can be reset after each tuple.
8365 *
8366 * Note: either rel or fsstate, but not both, can be NULL. rel is NULL
8367 * if we're processing a remote join, while fsstate is NULL in a non-query
8368 * context such as ANALYZE, or if we're processing a non-scan query node.
8369 */
8370static HeapTuple
8372 int row,
8373 Relation rel,
8374 AttInMetadata *attinmeta,
8375 List *retrieved_attrs,
8376 ForeignScanState *fsstate,
8378{
8379 HeapTuple tuple;
8380 TupleDesc tupdesc;
8381 Datum *values;
8382 bool *nulls;
8383 ItemPointer ctid = NULL;
8385 ErrorContextCallback errcallback;
8386 MemoryContext oldcontext;
8387 ListCell *lc;
8388 int j;
8389
8390 Assert(row < PQntuples(res));
8391
8392 /*
8393 * Do the following work in a temp context that we reset after each tuple.
8394 * This cleans up not only the data we have direct access to, but any
8395 * cruft the I/O functions might leak.
8396 */
8397 oldcontext = MemoryContextSwitchTo(temp_context);
8398
8399 /*
8400 * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
8401 * provided, otherwise look to the scan node's ScanTupleSlot.
8402 */
8403 if (rel)
8404 tupdesc = RelationGetDescr(rel);
8405 else
8406 {
8407 Assert(fsstate);
8408 tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
8409 }
8410
8411 values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
8412 nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
8413 /* Initialize to nulls for any columns not present in result */
8414 memset(nulls, true, tupdesc->natts * sizeof(bool));
8415
8416 /*
8417 * Set up and install callback to report where conversion error occurs.
8418 */
8419 errpos.cur_attno = 0;
8420 errpos.rel = rel;
8421 errpos.fsstate = fsstate;
8422 errcallback.callback = conversion_error_callback;
8423 errcallback.arg = &errpos;
8424 errcallback.previous = error_context_stack;
8425 error_context_stack = &errcallback;
8426
8427 /*
8428 * i indexes columns in the relation, j indexes columns in the PGresult.
8429 */
8430 j = 0;
8431 foreach(lc, retrieved_attrs)
8432 {
8433 int i = lfirst_int(lc);
8434 char *valstr;
8435
8436 /* fetch next column's textual value */
8437 if (PQgetisnull(res, row, j))
8438 valstr = NULL;
8439 else
8440 valstr = PQgetvalue(res, row, j);
8441
8442 /*
8443 * convert value to internal representation
8444 *
8445 * Note: we ignore system columns other than ctid and oid in result
8446 */
8447 errpos.cur_attno = i;
8448 if (i > 0)
8449 {
8450 /* ordinary column */
8451 Assert(i <= tupdesc->natts);
8452 nulls[i - 1] = (valstr == NULL);
8453 /* Apply the input function even to nulls, to support domains */
8454 values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
8455 valstr,
8456 attinmeta->attioparams[i - 1],
8457 attinmeta->atttypmods[i - 1]);
8458 }
8460 {
8461 /* ctid */
8462 if (valstr != NULL)
8463 {
8464 Datum datum;
8465
8467 ctid = (ItemPointer) DatumGetPointer(datum);
8468 }
8469 }
8470 errpos.cur_attno = 0;
8471
8472 j++;
8473 }
8474
8475 /* Uninstall error context callback. */
8476 error_context_stack = errcallback.previous;
8477
8478 /*
8479 * Check we got the expected number of columns. Note: j == 0 and
8480 * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
8481 */
8482 if (j > 0 && j != PQnfields(res))
8483 elog(ERROR, "remote query result does not match the foreign table");
8484
8485 /*
8486 * Build the result tuple in caller's memory context.
8487 */
8488 MemoryContextSwitchTo(oldcontext);
8489
8490 tuple = heap_form_tuple(tupdesc, values, nulls);
8491
8492 /*
8493 * If we have a CTID to return, install it in both t_self and t_ctid.
8494 * t_self is the normal place, but if the tuple is converted to a
8495 * composite Datum, t_self will be lost; setting t_ctid allows CTID to be
8496 * preserved during EvalPlanQual re-evaluations (see ROW_MARK_COPY code).
8497 */
8498 if (ctid)
8499 tuple->t_self = tuple->t_data->t_ctid = *ctid;
8500
8501 /*
8502 * Stomp on the xmin, xmax, and cmin fields from the tuple created by
8503 * heap_form_tuple. heap_form_tuple actually creates the tuple with
8504 * DatumTupleFields, not HeapTupleFields, but the executor expects
8505 * HeapTupleFields and will happily extract system columns on that
8506 * assumption. If we don't do this then, for example, the tuple length
8507 * ends up in the xmin field, which isn't what we want.
8508 */
8512
8513 /* Clean up */
8515
8516 return tuple;
8517}
8518
8519/*
8520 * Callback function which is called when error occurs during column value
8521 * conversion. Print names of column and relation.
8522 *
8523 * Note that this function mustn't do any catalog lookups, since we are in
8524 * an already-failed transaction. Fortunately, we can get the needed info
8525 * from the relation or the query's rangetable instead.
8526 */
8527static void
8529{
8531 Relation rel = errpos->rel;
8532 ForeignScanState *fsstate = errpos->fsstate;
8533 const char *attname = NULL;
8534 const char *relname = NULL;
8535 bool is_wholerow = false;
8536
8537 /*
8538 * If we're in a scan node, always use aliases from the rangetable, for
8539 * consistency between the simple-relation and remote-join cases. Look at
8540 * the relation's tupdesc only if we're not in a scan node.
8541 */
8542 if (fsstate)
8543 {
8544 /* ForeignScan case */
8546 int varno = 0;
8547 AttrNumber colno = 0;
8548
8549 if (fsplan->scan.scanrelid > 0)
8550 {
8551 /* error occurred in a scan against a foreign table */
8552 varno = fsplan->scan.scanrelid;
8553 colno = errpos->cur_attno;
8554 }
8555 else
8556 {
8557 /* error occurred in a scan against a foreign join */
8559
8560 tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
8561 errpos->cur_attno - 1);
8562
8563 /*
8564 * Target list can have Vars and expressions. For Vars, we can
8565 * get some information, however for expressions we can't. Thus
8566 * for expressions, just show generic context message.
8567 */
8568 if (IsA(tle->expr, Var))
8569 {
8570 Var *var = (Var *) tle->expr;
8571
8572 varno = var->varno;
8573 colno = var->varattno;
8574 }
8575 }
8576
8577 if (varno > 0)
8578 {
8579 EState *estate = fsstate->ss.ps.state;
8580 RangeTblEntry *rte = exec_rt_fetch(varno, estate);
8581
8582 relname = rte->eref->aliasname;
8583
8584 if (colno == 0)
8585 is_wholerow = true;
8586 else if (colno > 0 && colno <= list_length(rte->eref->colnames))
8587 attname = strVal(list_nth(rte->eref->colnames, colno - 1));
8588 else if (colno == SelfItemPointerAttributeNumber)
8589 attname = "ctid";
8590 }
8591 }
8592 else if (rel)
8593 {
8594 /* Non-ForeignScan case (we should always have a rel here) */
8595 TupleDesc tupdesc = RelationGetDescr(rel);
8596
8598 if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
8599 {
8600 Form_pg_attribute attr = TupleDescAttr(tupdesc,
8601 errpos->cur_attno - 1);
8602
8603 attname = NameStr(attr->attname);
8604 }
8605 else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
8606 attname = "ctid";
8607 }
8608
8609 if (relname && is_wholerow)
8610 errcontext("whole-row reference to foreign table \"%s\"", relname);
8611 else if (relname && attname)
8612 errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
8613 else
8614 errcontext("processing expression at position %d in select list",
8615 errpos->cur_attno);
8616}
8617
8618/*
8619 * Given an EquivalenceClass and a foreign relation, find an EC member
8620 * that can be used to sort the relation remotely according to a pathkey
8621 * using this EC.
8622 *
8623 * If there is more than one suitable candidate, return an arbitrary
8624 * one of them. If there is none, return NULL.
8625 *
8626 * This checks that the EC member expression uses only Vars from the given
8627 * rel and is shippable. Caller must separately verify that the pathkey's
8628 * ordering operator is shippable.
8629 */
8632{
8633 PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
8636
8638 while ((em = eclass_member_iterator_next(&it)) != NULL)
8639 {
8640 /*
8641 * Note we require !bms_is_empty, else we'd accept constant
8642 * expressions which are not suitable for the purpose.
8643 */
8644 if (bms_is_subset(em->em_relids, rel->relids) &&
8645 !bms_is_empty(em->em_relids) &&
8646 bms_is_empty(bms_intersect(em->em_relids, fpinfo->hidden_subquery_rels)) &&
8647 is_foreign_expr(root, rel, em->em_expr))
8648 return em;
8649 }
8650
8651 return NULL;
8652}
8653
8654/*
8655 * Find an EquivalenceClass member that is to be computed as a sort column
8656 * in the given rel's reltarget, and is shippable.
8657 *
8658 * If there is more than one suitable candidate, return an arbitrary
8659 * one of them. If there is none, return NULL.
8660 *
8661 * This checks that the EC member expression uses only Vars from the given
8662 * rel and is shippable. Caller must separately verify that the pathkey's
8663 * ordering operator is shippable.
8664 */
8667 RelOptInfo *rel)
8668{
8669 PathTarget *target = rel->reltarget;
8670 ListCell *lc1;
8671 int i;
8672
8673 i = 0;
8674 foreach(lc1, target->exprs)
8675 {
8676 Expr *expr = (Expr *) lfirst(lc1);
8678 ListCell *lc2;
8679
8680 /* Ignore non-sort expressions */
8681 if (sgref == 0 ||
8683 root->parse->sortClause) == NULL)
8684 {
8685 i++;
8686 continue;
8687 }
8688
8689 /* We ignore binary-compatible relabeling on both ends */
8690 while (expr && IsA(expr, RelabelType))
8691 expr = ((RelabelType *) expr)->arg;
8692
8693 /*
8694 * Locate an EquivalenceClass member matching this expr, if any.
8695 * Ignore child members.
8696 */
8697 foreach(lc2, ec->ec_members)
8698 {
8700 Expr *em_expr;
8701
8702 /* Don't match constants */
8703 if (em->em_is_const)
8704 continue;
8705
8706 /* Child members should not exist in ec_members */
8707 Assert(!em->em_is_child);
8708
8709 /* Match if same expression (after stripping relabel) */
8710 em_expr = em->em_expr;
8711 while (em_expr && IsA(em_expr, RelabelType))
8712 em_expr = ((RelabelType *) em_expr)->arg;
8713
8714 if (!equal(em_expr, expr))
8715 continue;
8716
8717 /* Check that expression (including relabels!) is shippable */
8718 if (is_foreign_expr(root, rel, em->em_expr))
8719 return em;
8720 }
8721
8722 i++;
8723 }
8724
8725 return NULL;
8726}
8727
8728/*
8729 * Determine batch size for a given foreign table. The option specified for
8730 * a table has precedence.
8731 */
8732static int
8734{
8737 ForeignServer *server;
8738 List *options;
8739 ListCell *lc;
8740
8741 /* we use 1 by default, which means "no batching" */
8742 int batch_size = 1;
8743
8744 /*
8745 * Load options for table and server. We append server options after table
8746 * options, because table options take precedence.
8747 */
8749 server = GetForeignServer(table->serverid);
8750
8751 options = NIL;
8752 options = list_concat(options, table->options);
8753 options = list_concat(options, server->options);
8754
8755 /* See if either table or server specifies batch_size. */
8756 foreach(lc, options)
8757 {
8758 DefElem *def = (DefElem *) lfirst(lc);
8759
8760 if (strcmp(def->defname, "batch_size") == 0)
8761 {
8762 (void) parse_int(defGetString(def), &batch_size, 0, NULL);
8763 break;
8764 }
8765 }
8766
8767 return batch_size;
8768}
void get_translated_update_targetlist(PlannerInfo *root, Index relid, List **processed_tlist, List **update_colnos)
Definition appendinfo.c:766
void add_row_identity_var(PlannerInfo *root, Var *orig_var, Index rtindex, const char *rowid_name)
Definition appendinfo.c:864
int16 AttrNumber
Definition attnum.h:21
#define AttributeNumberIsValid(attributeNumber)
Definition attnum.h:34
#define InvalidAttrNumber
Definition attnum.h:23
bool delete_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited)
bool import_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited, const NullableDatum *version, const NullableDatum *null_frac, const NullableDatum *avg_width, const NullableDatum *n_distinct, const NullableDatum *most_common_vals, const NullableDatum *most_common_freqs, const NullableDatum *histogram_bounds, const NullableDatum *correlation, const NullableDatum *most_common_elems, const NullableDatum *most_common_elem_freqs, const NullableDatum *elem_count_histogram, const NullableDatum *range_length_histogram, const NullableDatum *range_empty_frac, const NullableDatum *range_bounds_histogram)
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1649
Bitmapset * bms_intersect(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:293
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1425
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition bitmapset.c:987
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:547
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:645
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:1036
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:252
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:710
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:769
#define bms_is_empty(a)
Definition bitmapset.h:119
uint32 BlockNumber
Definition block.h:31
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define CStringGetTextDatum(s)
Definition builtins.h:98
#define NameStr(name)
Definition c.h:894
#define Min(x, y)
Definition c.h:1131
#define MAXALIGN(LEN)
Definition c.h:955
#define Max(x, y)
Definition c.h:1125
#define Assert(condition)
Definition c.h:1002
int64_t int64
Definition c.h:680
#define CppAsString2(x)
Definition c.h:565
int32_t int32
Definition c.h:679
uint32_t uint32
Definition c.h:683
unsigned int Index
Definition c.h:757
float float4
Definition c.h:772
#define OidIsValid(objectId)
Definition c.h:917
uint32 result
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition clausesel.c:100
@ COMPARE_LT
Definition cmptype.h:34
bool attribute_is_analyzable(Relation onerel, int attnum, Form_pg_attribute attr, int *p_attstattarget)
Definition analyze.c:1176
unsigned int GetCursorNumber(PGconn *conn)
void do_sql_command(PGconn *conn, const char *sql)
Definition connection.c:849
PGresult * pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
void ReleaseConnection(PGconn *conn)
PGresult * pgfdw_get_result(PGconn *conn)
void pgfdw_report_error(PGresult *res, PGconn *conn, const char *sql)
static unsigned int cursor_number
Definition connection.c:84
unsigned int GetPrepStmtNumber(PGconn *conn)
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition option.c:450
double cpu_operator_cost
Definition costsize.c:135
void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
Definition costsize.c:5516
void cost_sort(Path *path, PlannerInfo *root, List *pathkeys, int input_disabled_nodes, Cost input_cost, double tuples, int width, Cost comparison_cost, int sort_mem, double limit_tuples)
Definition costsize.c:2202
double cpu_tuple_cost
Definition costsize.c:133
void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
Definition costsize.c:4923
double seq_page_cost
Definition costsize.c:131
double clamp_row_est(double nrows)
Definition costsize.c:215
bool is_projection_capable_path(Path *path)
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)
static DataChecksumsWorkerOperation operation
int64 TimestampTz
Definition timestamp.h:39
char * defGetString(DefElem *def)
Definition define.c:34
bool defGetBoolean(DefElem *def)
Definition define.c:93
void deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
Definition deparse.c:2550
const char * get_jointype_name(JoinType jointype)
Definition deparse.c:1692
void deparseAnalyzeInfoSql(StringInfo buf, Relation rel)
Definition deparse.c:2572
void deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, RelOptInfo *foreignrel, List *remote_conds, List **params_list, List *returningList, List **retrieved_attrs)
Definition deparse.c:2442
void deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, RelOptInfo *foreignrel, List *targetlist, List *targetAttrs, List *remote_conds, List **params_list, List *returningList, List **retrieved_attrs)
Definition deparse.c:2327
bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition deparse.c:1135
void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, bool has_final_sort, bool has_limit, bool is_subquery, List **retrieved_attrs, List **params_list)
Definition deparse.c:1286
void deparseStringLiteral(StringInfo buf, const char *val)
Definition deparse.c:2900
void rebuildInsertSql(StringInfo buf, Relation rel, char *orig_query, List *target_attrs, int values_end_len, int num_params, int num_rows)
Definition deparse.c:2207
void deparseInsertSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, bool doNothing, List *withCheckOptionList, List *returningList, List **retrieved_attrs, int *values_end_len)
Definition deparse.c:2134
void deparseUpdateSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, List *withCheckOptionList, List *returningList, List **retrieved_attrs)
Definition deparse.c:2267
void deparseDeleteSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *returningList, List **retrieved_attrs)
Definition deparse.c:2413
void deparseAnalyzeSql(StringInfo buf, Relation rel, PgFdwSamplingMethod sample_method, double sample_frac, List **retrieved_attrs)
Definition deparse.c:2612
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition deparse.c:244
void classifyConditions(PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds, List **local_conds)
Definition deparse.c:218
void deparseTruncateSql(StringInfo buf, List *rels, DropBehavior behavior, bool restart_seqs)
Definition deparse.c:2697
bool is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel, PathKey *pathkey)
Definition deparse.c:1176
List * build_tlist_to_deparse(RelOptInfo *foreignrel)
Definition deparse.c:1229
Datum arg
Definition elog.c:1323
ErrorContextCallback * error_context_stack
Definition elog.c:100
int errcode(int sqlerrcode)
Definition elog.c:875
#define errcontext
Definition elog.h:200
#define DEBUG3
Definition elog.h:29
#define WARNING
Definition elog.h:37
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
void setup_eclass_member_iterator(EquivalenceMemberIterator *it, EquivalenceClass *ec, Relids child_relids)
List * generate_implied_equalities_for_column(PlannerInfo *root, RelOptInfo *rel, ec_matches_callback_type callback, void *callback_arg, Relids prohibited_rels)
EquivalenceMember * eclass_member_iterator_next(EquivalenceMemberIterator *it)
bool eclass_useful_for_merging(PlannerInfo *root, EquivalenceClass *eclass, RelOptInfo *rel)
void ExecAsyncResponse(AsyncRequest *areq)
Definition execAsync.c:118
void ExecAsyncRequestPending(AsyncRequest *areq)
Definition execAsync.c:150
void ExecAsyncRequestDone(AsyncRequest *areq, TupleTableSlot *result)
Definition execAsync.c:138
List * ExecInitExprList(List *nodes, PlanState *parent)
Definition execExpr.c:335
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition execJunk.c:222
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1274
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition execUtils.c:768
Oid ExecGetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate)
Definition execUtils.c:1515
#define outerPlanState(node)
Definition execnodes.h:1300
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition executor.h:708
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:322
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:401
#define EXEC_FLAG_EXPLAIN_ONLY
Definition executor.h:67
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition executor.h:226
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
bool HasRelationExtStatistics(Relation onerel)
int(* AcquireSampleRowsFunc)(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
Definition fdwapi.h:151
int PQserverVersion(const PGconn *conn)
int PQsocket(const 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
int PQconsumeInput(PGconn *conn)
Definition fe-exec.c:2001
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
Definition fe-exec.c:1553
int PQsendQuery(PGconn *conn, const char *query)
Definition fe-exec.c:1433
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
#define palloc_array(type, count)
Definition fe_memutils.h:91
#define palloc0_array(type, count)
Definition fe_memutils.h:92
#define palloc0_object(type)
Definition fe_memutils.h:90
int extra_float_digits
Definition float.c:57
float4 float4in_internal(char *num, char **endptr_p, const char *type_name, const char *orig_string, struct Node *escontext)
Definition float.c:224
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition fmgr.c:1532
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition fmgr.c:129
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition fmgr.c:1684
#define PG_MODULE_MAGIC_EXT(...)
Definition fmgr.h:540
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:688
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
ForeignTable * GetForeignTable(Oid relid)
Definition foreign.c:286
Path * GetExistingLocalJoinPath(RelOptInfo *joinrel)
Definition foreign.c:773
UserMapping * GetUserMapping(Oid userid, Oid serverid)
Definition foreign.c:232
ForeignServer * GetForeignServer(Oid serverid)
Definition foreign.c:114
List * GetForeignColumnOptions(Oid relid, AttrNumber attnum)
Definition foreign.c:324
int DateStyle
Definition globals.c:127
int IntervalStyle
Definition globals.c:129
int work_mem
Definition globals.c:133
bool parse_int(const char *value, int *result, int flags, const char **hintmsg)
Definition guc.c:2775
int NewGUCNestLevel(void)
Definition guc.c:2142
bool parse_real(const char *value, double *result, int flags, const char **hintmsg)
Definition guc.c:2865
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
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
static int server_version_num
Definition guc_tables.c:624
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
#define SizeofHeapTupleHeader
static void HeapTupleHeaderSetCmin(HeapTupleHeaderData *tup, CommandId cid)
static void HeapTupleHeaderSetXmin(HeapTupleHeaderData *tup, TransactionId xid)
static void HeapTupleHeaderSetXmax(HeapTupleHeaderData *tup, TransactionId xid)
void parse(int)
Definition parse.c:49
#define stmt
long val
Definition informix.c:689
static struct @175 value
Bitmapset * get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel)
Definition inherit.c:654
void InstrUpdateTupleCount(NodeInstrumentation *instr, double nTuples)
Definition instrument.c:196
int j
Definition isn.c:78
int i
Definition isn.c:77
ItemPointerData * ItemPointer
Definition itemptr.h:49
#define PQgetvalue
#define PQclear
static libpqsrv_PGresult * libpqsrv_PGresultSetParent(libpqsrv_PGresult *bres, MemoryContext ctx)
#define PQcmdTuples
#define PQnfields
#define PQresultStatus
#define PQgetisnull
#define PQntuples
@ PGRES_COMMAND_OK
Definition libpq-fe.h:131
@ PGRES_TUPLES_OK
Definition libpq-fe.h:134
#define PQ_QUERY_PARAM_MAX_LIMIT
Definition libpq-fe.h:524
List * lappend(List *list, void *datum)
Definition list.c:339
List * list_delete(List *list, void *datum)
Definition list.c:853
List * list_concat(List *list1, const List *list2)
Definition list.c:561
List * list_copy(const List *oldlist)
Definition list.c:1573
List * lappend_int(List *list, int datum)
Definition list.c:357
bool list_member_ptr(const List *list, const void *datum)
Definition list.c:682
void list_free(List *list)
Definition list.c:1546
bool list_member_int(const List *list, int datum)
Definition list.c:702
bool list_member(const List *list, const void *datum)
Definition list.c:661
List * list_append_unique_ptr(List *list, void *datum)
Definition list.c:1356
#define NoLock
Definition lockdefs.h:34
char * get_rel_name(Oid relid)
Definition lsyscache.c:2242
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition lsyscache.c:3223
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2266
Oid get_rel_type_id(Oid relid)
Definition lsyscache.c:2293
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3706
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
Datum subpath(PG_FUNCTION_ARGS)
Definition ltree_op.c:348
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
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
char * pstrdup(const char *in)
Definition mcxt.c:1910
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc0(Size size)
Definition mcxt.c:1420
void * palloc(Size size)
Definition mcxt.c:1390
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
MemoryContext GetMemoryChunkContext(void *pointer)
Definition mcxt.c:759
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
#define USE_ISO_DATES
Definition miscadmin.h:240
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
#define INTSTYLE_POSTGRES
Definition miscadmin.h:260
Oid GetUserId(void)
Definition miscinit.c:470
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
#define IsA(nodeptr, _type_)
Definition nodes.h:162
#define copyObject(obj)
Definition nodes.h:230
double Cost
Definition nodes.h:259
#define IS_OUTER_JOIN(jointype)
Definition nodes.h:346
OnConflictAction
Definition nodes.h:425
@ ONCONFLICT_NONE
Definition nodes.h:426
@ ONCONFLICT_NOTHING
Definition nodes.h:427
CmdType
Definition nodes.h:271
@ CMD_INSERT
Definition nodes.h:275
@ CMD_DELETE
Definition nodes.h:276
@ CMD_UPDATE
Definition nodes.h:274
@ CMD_SELECT
Definition nodes.h:273
double Selectivity
Definition nodes.h:258
@ AGGSPLIT_SIMPLE
Definition nodes.h:385
@ LIMIT_OPTION_WITH_TIES
Definition nodes.h:441
#define makeNode(_type_)
Definition nodes.h:159
#define castNode(_type_, nodeptr)
Definition nodes.h:180
JoinType
Definition nodes.h:296
@ JOIN_SEMI
Definition nodes.h:315
@ JOIN_FULL
Definition nodes.h:303
@ JOIN_INNER
Definition nodes.h:301
@ JOIN_RIGHT
Definition nodes.h:304
@ JOIN_LEFT
Definition nodes.h:302
uint32 uint32in_subr(const char *s, char **endloc, const char *typname, Node *escontext)
Definition numutils.c:897
int32 pg_strtoint32(const char *s)
Definition numutils.c:382
static char * errmsg
#define PVC_RECURSE_PLACEHOLDERS
Definition optimizer.h:202
#define PVC_INCLUDE_PLACEHOLDERS
Definition optimizer.h:201
#define PVC_INCLUDE_AGGREGATES
Definition optimizer.h:197
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
@ FDW_IMPORT_SCHEMA_LIMIT_TO
@ FDW_IMPORT_SCHEMA_EXCEPT
@ RTE_RELATION
DropBehavior
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
PathKey * make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, CompareType cmptype, bool nulls_first)
Definition pathkeys.c:56
void update_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
Definition pathkeys.c:1510
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition pathkeys.c:343
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
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
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition pathnode.c:459
void adjust_limit_rows_costs(double *rows, Cost *startup_cost, Cost *total_cost, int64 offset_est, int64 count_est)
Definition pathnode.c:3869
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition pathnodes.h:3058
@ PARTITIONWISE_AGGREGATE_FULL
Definition pathnodes.h:3662
@ PARTITIONWISE_AGGREGATE_NONE
Definition pathnodes.h:3661
#define IS_SIMPLE_REL(rel)
Definition pathnodes.h:989
#define IS_JOIN_REL(rel)
Definition pathnodes.h:994
#define get_pathtarget_sortgroupref(target, colno)
Definition pathnodes.h:1894
#define planner_rt_fetch(rti, root)
Definition pathnodes.h:704
UpperRelationKind
Definition pathnodes.h:143
@ 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 IS_OTHER_REL(rel)
Definition pathnodes.h:1004
#define IS_UPPER_REL(rel)
Definition pathnodes.h:999
NameData attname
int16 attnum
FormData_pg_attribute * Form_pg_attribute
bool attnotnull
NameData relname
Definition pg_class.h:40
#define NAMEDATALEN
const void size_t len
#define lfirst(lc)
Definition pg_list.h:172
#define lfirst_node(type, lc)
Definition pg_list.h:176
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
#define lfirst_int(lc)
Definition pg_list.h:173
#define list_make5(x1, x2, x3, x4, x5)
Definition pg_list.h:254
#define list_make1(x1)
Definition pg_list.h:244
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
#define linitial(l)
Definition pg_list.h:178
#define list_make3(x1, x2, x3)
Definition pg_list.h:248
#define list_nth_node(type, list, n)
Definition pg_list.h:359
#define linitial_oid(l)
Definition pg_list.h:180
#define list_make2(x1, x2)
Definition pg_list.h:246
#define list_make4(x1, x2, x3, x4)
Definition pg_list.h:251
static const struct lconv_member_info table[]
#define plan(x)
Definition pg_regress.c:164
static char * user
Definition pg_regress.c:121
uint64 fetch_size
Definition pg_rewind.c:85
static char buf[DEFAULT_XLOG_SEG_SIZE]
void pgstat_report_analyze(Relation rel, PgStat_Counter livetuples, PgStat_Counter deadtuples, bool resetcounter, TimestampTz starttime)
#define outerPlan(node)
Definition plannodes.h:267
#define snprintf
Definition port.h:261
#define qsort(a, b, c, d)
Definition port.h:496
static Datum Float4GetDatum(float4 X)
Definition postgres.h:481
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
static Datum UInt32GetDatum(uint32 X)
Definition postgres.h:232
#define PointerGetDatum(X)
Definition postgres.h:354
#define InvalidOid
unsigned int Oid
static int postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
#define DEFAULT_FDW_SORT_MULTIPLIER
static char * get_opt_value(PGresult *res, int row, int col)
static const char ** convert_prep_stmt_params(PgFdwModifyState *fmstate, ItemPointer tupleid, TupleTableSlot **slots, int numSlots)
static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
static bool postgresImportForeignStatistics(Relation relation, List *va_cols, int elevel)
static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra)
static bool import_fetched_statistics(Relation relation, const char *schemaname, const char *relname, int attrcnt, const RemoteAttributeMapping *remattrmap, RemoteStatsResults *remstats)
static void postgresBeginForeignScan(ForeignScanState *node, int eflags)
static bool postgresIsForeignPathAsyncCapable(ForeignPath *path)
static void store_returning_result(PgFdwModifyState *fmstate, TupleTableSlot *slot, PGresult *res)
static void create_cursor(ForeignScanState *node)
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 void analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
static TupleTableSlot ** postgresExecForeignBatchInsert(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
static void deallocate_query(PgFdwModifyState *fmstate)
static void postgresGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
static void postgresExplainForeignScan(ForeignScanState *node, ExplainState *es)
static TupleDesc get_tupdesc_for_join_scan_tuples(ForeignScanState *node)
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 conversion_error_callback(void *arg)
static void postgresReScanForeignScan(ForeignScanState *node)
static void prepare_foreign_modify(PgFdwModifyState *fmstate)
static void postgresForeignAsyncRequest(AsyncRequest *areq)
static int postgresIsForeignRelUpdatable(Relation rel)
void reset_transmission_modes(int nestlevel)
int set_transmission_modes(void)
static double postgresGetAnalyzeInfoForForeignTable(Relation relation, bool *can_tablesample)
static RemoteAttributeMapping * build_remattrmap(Relation relation, List *va_cols, int *p_attrcnt, StringInfo column_list)
FdwDirectModifyPrivateIndex
@ FdwDirectModifyPrivateSetProcessed
@ FdwDirectModifyPrivateHasReturning
@ FdwDirectModifyPrivateRetrievedAttrs
@ FdwDirectModifyPrivateUpdateSql
static void rebuild_fdw_scan_tlist(ForeignScan *fscan, List *tlist)
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 free_remattrmap(RemoteAttributeMapping *map, int len)
static void postgresGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
RelStatsColumns
@ RELSTATS_RELTUPLES
@ RELSTATS_RELKIND
@ RELSTATS_NUM_FIELDS
@ RELSTATS_RELPAGES
static void add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra)
static List * postgresPlanForeignModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index)
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 fetch_more_data(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 postgresEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo)
static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch)
static ForeignScan * find_modifytable_subplan(PlannerInfo *root, ModifyTable *plan, Index rtindex, int subplan_index)
static void add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel)
static bool semijoin_target_ok(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel)
static bool attname_in_list(const char *attname, List *va_cols)
static void merge_fdw_options(PgFdwRelationInfo *fpinfo, const PgFdwRelationInfo *fpinfo_o, const PgFdwRelationInfo *fpinfo_i)
static void set_text_arg(NullableDatum *arg, const char *s)
static void postgresAddForeignUpdateTargets(PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation)
static void postgresGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
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 postgresEndDirectModify(ForeignScanState *node)
static void get_remote_estimate(const char *sql, PGconn *conn, double *rows, int *width, Cost *startup_cost, Cost *total_cost)
static void postgresForeignAsyncConfigureWait(AsyncRequest *areq)
static PGresult * fetch_attstats(PGconn *conn, int server_version_num, const char *remote_schemaname, const char *remote_relname, const char *column_list)
EquivalenceMember * find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
static void postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra)
static void apply_server_options(PgFdwRelationInfo *fpinfo)
static void close_cursor(PGconn *conn, unsigned int cursor_number, PgFdwConnState *conn_state)
FdwPathPrivateIndex
@ FdwPathPrivateHasLimit
@ FdwPathPrivateHasFinalSort
static int get_batch_size_option(Relation rel)
static void postgresForeignAsyncNotify(AsyncRequest *areq)
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 void add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *final_rel, FinalPathExtraData *extra)
static void fetch_more_data_begin(AsyncRequest *areq)
static void execute_dml_stmt(ForeignScanState *node)
static TupleTableSlot * postgresIterateForeignScan(ForeignScanState *node)
static TupleTableSlot * postgresExecForeignDelete(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
static int remattrmap_cmp(const void *v1, const void *v2)
static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual)
FdwScanPrivateIndex
@ FdwScanPrivateRetrievedAttrs
@ FdwScanPrivateSelectSql
@ FdwScanPrivateFetchSize
@ FdwScanPrivateRelations
static bool ec_member_matches_foreign(PlannerInfo *root, RelOptInfo *rel, EquivalenceClass *ec, EquivalenceMember *em, void *arg)
FdwModifyPrivateIndex
@ FdwModifyPrivateLen
@ FdwModifyPrivateUpdateSql
@ FdwModifyPrivateTargetAttnums
@ FdwModifyPrivateRetrievedAttrs
@ FdwModifyPrivateHasReturning
static void set_float_arg(NullableDatum *arg, const char *s)
static TupleTableSlot ** execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, TupleTableSlot **slots, TupleTableSlot **planSlots, int *numSlots)
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 void add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel, Path *epq_path, List *restrictlist)
static void set_int32_arg(NullableDatum *arg, const char *s)
static bool postgresRecheckForeignScan(ForeignScanState *node, TupleTableSlot *slot)
static List * postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
static void complete_pending_request(AsyncRequest *areq)
static PGresult * fetch_relstats(PGconn *conn, Relation relation)
static bool postgresAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
static List * build_remote_returning(Index rtindex, Relation rel, List *returningList)
static TupleTableSlot * get_returning_data(ForeignScanState *node)
AttStatsColumns
@ 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
void process_pending_request(AsyncRequest *areq)
#define DEFAULT_FDW_TUPLE_COST
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 void postgresBeginForeignModify(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags)
static void process_query_params(ExprContext *econtext, FmgrInfo *param_flinfo, List *param_exprs, const char **param_values)
static void postgresBeginForeignInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static void init_returning_filter(PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex)
static TupleTableSlot * postgresIterateDirectModify(ForeignScanState *node)
static void postgresBeginDirectModify(ForeignScanState *node, int eflags)
static List * get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
static void set_floatarr_arg(NullableDatum *arg, const char *s)
Datum postgres_fdw_handler(PG_FUNCTION_ARGS)
static TupleTableSlot * postgresExecForeignInsert(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot)
static void apply_table_options(PgFdwRelationInfo *fpinfo)
static int postgresAcquireSampleRowsFunc(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
static void postgresEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo)
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)
EquivalenceMember * find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
static void postgresExplainDirectModify(ForeignScanState *node, ExplainState *es)
#define DEFAULT_FDW_STARTUP_COST
static List * get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel)
static void finish_foreign_modify(PgFdwModifyState *fmstate)
static void set_uint32_arg(NullableDatum *arg, const char *s)
bool is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
Definition shippable.c:163
PgFdwSamplingMethod
@ ANALYZE_SAMPLE_AUTO
@ ANALYZE_SAMPLE_OFF
@ ANALYZE_SAMPLE_BERNOULLI
@ ANALYZE_SAMPLE_SYSTEM
@ ANALYZE_SAMPLE_RANDOM
void get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
Definition prepagg.c:559
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
tree ctl root
Definition radixtree.h:1857
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationGetDescr(relation)
Definition rel.h:542
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557
bool import_relation_statistics(Relation rel, const NullableDatum *version, const NullableDatum *relpages, const NullableDatum *reltuples, const NullableDatum *relallvisible, const NullableDatum *relallfrozen)
RelOptInfo * find_base_rel(PlannerInfo *root, int relid)
Definition relnode.c:544
RelOptInfo * find_join_rel(PlannerInfo *root, Relids relids)
Definition relnode.c:657
ParamPathInfo * get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel, Relids required_outer)
Definition relnode.c:1704
List * extract_actual_clauses(List *restrictinfo_list, bool pseudoconstant)
bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel)
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)
const char * quote_identifier(const char *ident)
void reservoir_init_selection_state(ReservoirState rs, int n)
Definition sampling.c:133
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
double estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, List **pgset, EstimationInfo *estinfo)
Definition selfuncs.c:3804
PGconn * GetConnection(void)
Definition streamutil.c:60
PGconn * conn
Definition streamutil.c:52
void resetStringInfo(StringInfo str)
Definition stringinfo.c:126
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
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
Bitmapset * as_needrequest
Definition execnodes.h:1550
struct WaitEventSet * as_eventset
Definition execnodes.h:1551
PlanState * requestor
Definition execnodes.h:676
PlanState * requestee
Definition execnodes.h:677
FmgrInfo * attinfuncs
Definition funcapi.h:41
Oid * attioparams
Definition funcapi.h:44
int32 * atttypmods
Definition funcapi.h:47
bool attgenerated
Definition tupdesc.h:79
bool attisdropped
Definition tupdesc.h:78
ForeignScanState * fsstate
char * defname
Definition parsenodes.h:862
uint64 es_processed
Definition execnodes.h:751
List * es_range_table
Definition execnodes.h:699
MemoryContext es_query_cxt
Definition execnodes.h:747
struct ErrorContextCallback * previous
Definition elog.h:299
void(* callback)(void *arg)
Definition elog.h:300
List * rtable_names
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:295
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
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
Cardinality limit_tuples
Definition pathnodes.h:3707
ResultRelInfo * resultRelInfo
Definition execnodes.h:2101
List * options
Definition foreign.h:43
char * servername
Definition foreign.h:40
PartitionwiseAggregateType patype
Definition pathnodes.h:3691
ItemPointerData t_self
Definition htup.h:65
HeapTupleHeader t_data
Definition htup.h:68
ItemPointerData t_ctid
SpecialJoinInfo * sjinfo
Definition pathnodes.h:3623
Definition pg_list.h:54
ResultRelInfo * resultRelInfo
Definition execnodes.h:1447
Definition nodes.h:133
List * exprs
Definition pathnodes.h:1878
QualCost cost
Definition pathnodes.h:1884
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
ReservoirStateData rstate
AttInMetadata * attinmeta
MemoryContext anl_cxt
MemoryContext temp_cxt
AsyncRequest * pendingAreq
PgFdwConnState * conn_state
const char ** param_values
AttInMetadata * attinmeta
MemoryContext temp_cxt
AttInMetadata * attinmeta
FmgrInfo * p_flinfo
PgFdwConnState * conn_state
AttrNumber ctidAttno
struct PgFdwModifyState * aux_fmstate
PathTarget * target
List * retrieved_attrs
FmgrInfo * param_flinfo
const char ** param_values
AttInMetadata * attinmeta
MemoryContext batch_cxt
unsigned int cursor_number
MemoryContext temp_cxt
TupleDesc tupdesc
PgFdwConnState * conn_state
HeapTuple * tuples
Plan * plan
Definition execnodes.h:1202
EState * state
Definition execnodes.h:1204
NodeInstrumentation * instrument
Definition execnodes.h:1212
Bitmapset * chgParam
Definition execnodes.h:1236
ExprContext * ps_ExprContext
Definition execnodes.h:1243
bool async_capable
Definition execnodes.h:1246
List * qual
Definition plannodes.h:237
List * targetlist
Definition plannodes.h:235
ExprContext * pi_exprContext
Definition execnodes.h:402
Cost per_tuple
Definition pathnodes.h:121
Cost startup
Definition pathnodes.h:120
List * groupClause
Definition parsenodes.h:221
List * groupingSets
Definition parsenodes.h:224
char * relname
Definition primnodes.h:84
List * joininfo
Definition pathnodes.h:1148
Relids relids
Definition pathnodes.h:1021
struct PathTarget * reltarget
Definition pathnodes.h:1045
Index relid
Definition pathnodes.h:1069
Cardinality tuples
Definition pathnodes.h:1096
Relids top_parent_relids
Definition pathnodes.h:1174
BlockNumber pages
Definition pathnodes.h:1095
Relids lateral_relids
Definition pathnodes.h:1064
RelOptKind reloptkind
Definition pathnodes.h:1015
QualCost baserestrictcost
Definition pathnodes.h:1144
bool has_eclass_joins
Definition pathnodes.h:1150
Cardinality rows
Definition pathnodes.h:1027
TriggerDesc * trigdesc
Definition rel.h:117
Form_pg_class rd_rel
Definition rel.h:111
pg_prng_state randstate
Definition sampling.h:49
Expr * clause
Definition pathnodes.h:2901
struct ResultRelInfo * ri_RootResultRelInfo
Definition execnodes.h:655
Relation ri_RelationDesc
Definition execnodes.h:514
List * ri_WithCheckOptions
Definition execnodes.h:583
TriggerDesc * ri_TrigDesc
Definition execnodes.h:549
Index ri_RangeTableIndex
Definition execnodes.h:511
void * ri_FdwState
Definition execnodes.h:570
ProjectionInfo * ri_projectReturning
Definition execnodes.h:611
List * ri_returningList
Definition execnodes.h:608
bool ri_usesFdwDirectModify
Definition execnodes.h:573
Relation ss_currentRelation
Definition execnodes.h:1663
TupleTableSlot * ss_ScanTupleSlot
Definition execnodes.h:1665
PlanState ps
Definition execnodes.h:1662
bool trig_insert_after_row
Definition reltrigger.h:57
bool trig_update_before_row
Definition reltrigger.h:61
bool trig_insert_before_row
Definition reltrigger.h:56
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
bool * tts_isnull
Definition tuptable.h:133
Datum * tts_values
Definition tuptable.h:131
AttrNumber varattno
Definition primnodes.h:275
int varno
Definition primnodes.h:270
#define FirstLowInvalidHeapAttributeNumber
Definition sysattr.h:27
#define SelfItemPointerAttributeNumber
Definition sysattr.h:21
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
Datum tidin(PG_FUNCTION_ARGS)
Definition tid.c:51
TargetEntry * tlist_member(Expr *node, List *targetlist)
Definition tlist.c:88
SortGroupClause * get_sortgroupref_clause_noerr(Index sortref, List *clauses)
Definition tlist.c:452
bool grouping_is_sortable(List *groupClause)
Definition tlist.c:549
PathTarget * copy_pathtarget(PathTarget *src)
Definition tlist.c:666
void add_new_columns_to_pathtarget(PathTarget *target, List *exprs)
Definition tlist.c:761
List * get_sortgrouplist_exprs(List *sgClauses, List *targetList)
Definition tlist.c:401
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition tlist.c:141
#define InvalidTransactionId
Definition transam.h:31
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
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
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
#define TupIsNull(slot)
Definition tuptable.h:325
static void slot_getallattrs(TupleTableSlot *slot)
Definition tuptable.h:390
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:544
Integer * makeInteger(int i)
Definition value.c:23
String * makeString(char *str)
Definition value.c:63
Boolean * makeBoolean(bool val)
Definition value.c:49
#define boolVal(v)
Definition value.h:81
#define intVal(v)
Definition value.h:79
#define strVal(v)
Definition value.h:82
List * pull_var_clause(Node *node, int flags)
Definition var.c:653
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition var.c:296
const char * name
int GetNumRegisteredWaitEvents(WaitEventSet *set)
int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch, void *user_data)
#define WL_SOCKET_READABLE