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