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