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