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