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