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