2626#include "nodes/makefuncs.h"
2727#include "optimizer/cost.h"
2828#include "optimizer/pathnode.h"
29+ #include "optimizer/planmain.h"
30+ #include "optimizer/restrictinfo.h"
2931#include "utils/rel.h"
3032
3133PG_MODULE_MAGIC ;
@@ -48,7 +50,7 @@ struct FileFdwOption
4850 * Note: If you are adding new option for user mapping, you need to modify
4951 * fileGetOptions(), which currently doesn't bother to look at user mappings.
5052 */
51- static struct FileFdwOption valid_options []= {
53+ static const struct FileFdwOption valid_options []= {
5254/* File options */
5355{"filename" ,ForeignTableRelationId },
5456
@@ -71,6 +73,17 @@ static struct FileFdwOption valid_options[] = {
7173{NULL ,InvalidOid }
7274};
7375
76+ /*
77+ * FDW-specific information for RelOptInfo.fdw_private.
78+ */
79+ typedef struct FileFdwPlanState
80+ {
81+ char * filename ;/* file to read */
82+ List * options ;/* merged COPY options, excluding filename */
83+ BlockNumber pages ;/* estimate of file's physical size */
84+ double ntuples ;/* estimate of number of rows in file */
85+ }FileFdwPlanState ;
86+
7487/*
7588 * FDW-specific information for ForeignScanState.fdw_state.
7689 */
@@ -93,9 +106,18 @@ PG_FUNCTION_INFO_V1(file_fdw_validator);
93106/*
94107 * FDW callback routines
95108 */
96- static void filePlanForeignScan (Oid foreigntableid ,
97- PlannerInfo * root ,
98- RelOptInfo * baserel );
109+ static void fileGetForeignRelSize (PlannerInfo * root ,
110+ RelOptInfo * baserel ,
111+ Oid foreigntableid );
112+ static void fileGetForeignPaths (PlannerInfo * root ,
113+ RelOptInfo * baserel ,
114+ Oid foreigntableid );
115+ static ForeignScan * fileGetForeignPlan (PlannerInfo * root ,
116+ RelOptInfo * baserel ,
117+ Oid foreigntableid ,
118+ ForeignPath * best_path ,
119+ List * tlist ,
120+ List * scan_clauses );
99121static void fileExplainForeignScan (ForeignScanState * node ,ExplainState * es );
100122static void fileBeginForeignScan (ForeignScanState * node ,int eflags );
101123static TupleTableSlot * fileIterateForeignScan (ForeignScanState * node );
@@ -109,8 +131,10 @@ static bool is_valid_option(const char *option, Oid context);
109131static void fileGetOptions (Oid foreigntableid ,
110132char * * filename ,List * * other_options );
111133static List * get_file_fdw_attribute_options (Oid relid );
134+ static void estimate_size (PlannerInfo * root ,RelOptInfo * baserel ,
135+ FileFdwPlanState * fdw_private );
112136static void estimate_costs (PlannerInfo * root ,RelOptInfo * baserel ,
113- const char * filename ,
137+ FileFdwPlanState * fdw_private ,
114138Cost * startup_cost ,Cost * total_cost );
115139
116140
@@ -123,7 +147,9 @@ file_fdw_handler(PG_FUNCTION_ARGS)
123147{
124148FdwRoutine * fdwroutine = makeNode (FdwRoutine );
125149
126- fdwroutine -> PlanForeignScan = filePlanForeignScan ;
150+ fdwroutine -> GetForeignRelSize = fileGetForeignRelSize ;
151+ fdwroutine -> GetForeignPaths = fileGetForeignPaths ;
152+ fdwroutine -> GetForeignPlan = fileGetForeignPlan ;
127153fdwroutine -> ExplainForeignScan = fileExplainForeignScan ;
128154fdwroutine -> BeginForeignScan = fileBeginForeignScan ;
129155fdwroutine -> IterateForeignScan = fileIterateForeignScan ;
@@ -177,7 +203,7 @@ file_fdw_validator(PG_FUNCTION_ARGS)
177203
178204if (!is_valid_option (def -> defname ,catalog ))
179205{
180- struct FileFdwOption * opt ;
206+ const struct FileFdwOption * opt ;
181207StringInfoData buf ;
182208
183209/*
@@ -249,7 +275,7 @@ file_fdw_validator(PG_FUNCTION_ARGS)
249275static bool
250276is_valid_option (const char * option ,Oid context )
251277{
252- struct FileFdwOption * opt ;
278+ const struct FileFdwOption * opt ;
253279
254280for (opt = valid_options ;opt -> optname ;opt ++ )
255281{
@@ -381,28 +407,48 @@ get_file_fdw_attribute_options(Oid relid)
381407}
382408
383409/*
384- * filePlanForeignScan
410+ * fileGetForeignRelSize
411+ *Obtain relation size estimates for a foreign table
412+ */
413+ static void
414+ fileGetForeignRelSize (PlannerInfo * root ,
415+ RelOptInfo * baserel ,
416+ Oid foreigntableid )
417+ {
418+ FileFdwPlanState * fdw_private ;
419+
420+ /*
421+ * Fetch options. We only need filename at this point, but we might
422+ * as well get everything and not need to re-fetch it later in planning.
423+ */
424+ fdw_private = (FileFdwPlanState * )palloc (sizeof (FileFdwPlanState ));
425+ fileGetOptions (foreigntableid ,
426+ & fdw_private -> filename ,& fdw_private -> options );
427+ baserel -> fdw_private = (void * )fdw_private ;
428+
429+ /* Estimate relation size */
430+ estimate_size (root ,baserel ,fdw_private );
431+ }
432+
433+ /*
434+ * fileGetForeignPaths
385435 *Create possible access paths for a scan on the foreign table
386436 *
387437 *Currently we don't support any push-down feature, so there is only one
388438 *possible access path, which simply returns all records in the order in
389439 *the data file.
390440 */
391441static void
392- filePlanForeignScan ( Oid foreigntableid ,
393- PlannerInfo * root ,
394- RelOptInfo * baserel )
442+ fileGetForeignPaths ( PlannerInfo * root ,
443+ RelOptInfo * baserel ,
444+ Oid foreigntableid )
395445{
396- char * filename ;
397- List * options ;
446+ FileFdwPlanState * fdw_private = (FileFdwPlanState * )baserel -> fdw_private ;
398447Cost startup_cost ;
399448Cost total_cost ;
400449
401- /* Fetch options --- we only need filename at this point */
402- fileGetOptions (foreigntableid ,& filename ,& options );
403-
404- /* Estimate costs and update baserel->rows */
405- estimate_costs (root ,baserel ,filename ,
450+ /* Estimate costs */
451+ estimate_costs (root ,baserel ,fdw_private ,
406452& startup_cost ,& total_cost );
407453
408454/* Create a ForeignPath node and add it as only possible path */
@@ -422,6 +468,37 @@ filePlanForeignScan(Oid foreigntableid,
422468 */
423469}
424470
471+ /*
472+ * fileGetForeignPlan
473+ *Create a ForeignScan plan node for scanning the foreign table
474+ */
475+ static ForeignScan *
476+ fileGetForeignPlan (PlannerInfo * root ,
477+ RelOptInfo * baserel ,
478+ Oid foreigntableid ,
479+ ForeignPath * best_path ,
480+ List * tlist ,
481+ List * scan_clauses )
482+ {
483+ Index scan_relid = baserel -> relid ;
484+
485+ /*
486+ * We have no native ability to evaluate restriction clauses, so we just
487+ * put all the scan_clauses into the plan node's qual list for the
488+ * executor to check. So all we have to do here is strip RestrictInfo
489+ * nodes from the clauses and ignore pseudoconstants (which will be
490+ * handled elsewhere).
491+ */
492+ scan_clauses = extract_actual_clauses (scan_clauses , false);
493+
494+ /* Create the ForeignScan node */
495+ return make_foreignscan (tlist ,
496+ scan_clauses ,
497+ scan_relid ,
498+ NIL ,/* no expressions to evaluate */
499+ NIL );/* no private state either */
500+ }
501+
425502/*
426503 * fileExplainForeignScan
427504 *Produce extra output for EXPLAIN
@@ -568,38 +645,38 @@ fileReScanForeignScan(ForeignScanState *node)
568645}
569646
570647/*
571- * Estimatecosts of scanning a foreign table.
648+ * Estimatesize of a foreign table.
572649 *
573- * In addition to setting *startup_cost and *total_cost, this should
574- * update baserel->rows.
650+ * The main result is returned in baserel->rows. We also set
651+ * fdw_private->pages and fdw_private->ntuples for later use in the cost
652+ * calculation.
575653 */
576654static void
577- estimate_costs (PlannerInfo * root ,RelOptInfo * baserel ,
578- const char * filename ,
579- Cost * startup_cost ,Cost * total_cost )
655+ estimate_size (PlannerInfo * root ,RelOptInfo * baserel ,
656+ FileFdwPlanState * fdw_private )
580657{
581658struct stat stat_buf ;
582659BlockNumber pages ;
583660int tuple_width ;
584661double ntuples ;
585662double nrows ;
586- Cost run_cost = 0 ;
587- Cost cpu_per_tuple ;
588663
589664/*
590665 * Get size of the file. It might not be there at plan time, though, in
591666 * which case we have to use a default estimate.
592667 */
593- if (stat (filename ,& stat_buf )< 0 )
668+ if (stat (fdw_private -> filename ,& stat_buf )< 0 )
594669stat_buf .st_size = 10 * BLCKSZ ;
595670
596671/*
597- * Convert size to pages for use in I/O cost estimatebelow .
672+ * Convert size to pages for use in I/O cost estimatelater .
598673 */
599674pages = (stat_buf .st_size + (BLCKSZ - 1 )) /BLCKSZ ;
600675if (pages < 1 )
601676pages = 1 ;
602677
678+ fdw_private -> pages = pages ;
679+
603680/*
604681 * Estimate the number of tuples in the file. We back into this estimate
605682 * using the planner's idea of the relation width; which is bogus if not
@@ -611,6 +688,8 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
611688
612689ntuples = clamp_row_est ((double )stat_buf .st_size / (double )tuple_width );
613690
691+ fdw_private -> ntuples = ntuples ;
692+
614693/*
615694 * Now estimate the number of rows returned by the scan after applying the
616695 * baserestrictinfo quals.This is pretty bogus too, since the planner
@@ -627,12 +706,28 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
627706
628707/* Save the output-rows estimate for the planner */
629708baserel -> rows = nrows ;
709+ }
710+
711+ /*
712+ * Estimate costs of scanning a foreign table.
713+ *
714+ * Results are returned in *startup_cost and *total_cost.
715+ */
716+ static void
717+ estimate_costs (PlannerInfo * root ,RelOptInfo * baserel ,
718+ FileFdwPlanState * fdw_private ,
719+ Cost * startup_cost ,Cost * total_cost )
720+ {
721+ BlockNumber pages = fdw_private -> pages ;
722+ double ntuples = fdw_private -> ntuples ;
723+ Cost run_cost = 0 ;
724+ Cost cpu_per_tuple ;
630725
631726/*
632- *Now estimate costs. We estimate costs almost the same way as
633- *cost_seqscan(), thus assuming that I/O costs are equivalent to a
634- *regular table file of the same size. However, we take per-tuple CPU
635- *costs as 10x of a seqscan, to account for the cost of parsing records.
727+ * We estimate costs almost the same way as cost_seqscan(), thus assuming
728+ * that I/O costs are equivalent to a regular table file of the same size.
729+ * However, we take per-tuple CPU costs as 10x of a seqscan, to account
730+ * for the cost of parsing records.
636731 */
637732run_cost += seq_page_cost * pages ;
638733