static void pgIterate(ForeignScanState *scanstate);
static void pgClose(ForeignScanState *scanstate);
static void pgReOpen(ForeignScanState *scanstate);
-static void pgGetStatistics(Path *path, PlannerInfo *root, RelOptInfo *baserel);
+static void pgGetStatistics(ForeignPath *path, PlannerInfo *root, RelOptInfo *baserel);
/* deparse SQL from the request */
static bool is_immutable_func(Oid funcid);
* baserel->baserestrictinfo can be used to examine quals on the relation.
*/
static void
-pgGetStatistics(Path *path, PlannerInfo *root, RelOptInfo *baserel)
+pgGetStatistics(ForeignPath *path, PlannerInfo *root, RelOptInfo *baserel)
{
RangeTblEntry *rte;
double connection_cost = 0.0;
/*
* Estimate as same as sequencial scan on local table as approximate value.
*/
- cost_seqscan(path, root, baserel);
+ cost_seqscan(&path.path, root, baserel);
/* XXX override estimated cost */
- path->startup_cost = 1000;
- path->total_cost = 1000;
+ path->path.startup_cost = 1000;
+ path->path.total_cost = 1000;
/* Get cost factor from catalog and correct costs with them. */
rte = planner_rt_fetch(baserel->relid, root);
get_server_costs(rte->relid, &connection_cost, &transfer_cost);
- path->startup_cost += connection_cost;
- path->total_cost += connection_cost;
- path->total_cost += transfer_cost *
- path->parent->width * path->parent->rows;
+ path->path.startup_cost += connection_cost;
+ path->path.total_cost += connection_cost;
+ path->path.total_cost += transfer_cost *
+ path->path.parent->width * path->path.parent->rows;
}
#include "foreign/foreign.h"
#include "funcapi.h"
#include "miscadmin.h"
+#include "optimizer/cost.h"
+#include "parser/parsetree.h"
#include "storage/fd.h"
#include "utils/builtins.h"
static void Iterate(ForeignScanState *scanstate);
static void Close(ForeignScanState *scanstate);
static void ReOpen(ForeignScanState *scanstate);
-static void GetStatistics(Path *path, PlannerInfo *root, RelOptInfo *baserel);
+static void GetStatistics(ForeignPath *path, PlannerInfo *root, RelOptInfo *baserel);
static HeapTuple get_next_tuple(ForeignScanState *scanstate);
static void
Open(ForeignScanState *scanstate)
{
- elog(DEBUG1, "%s called", __func__);
Relation rel = scanstate->ss.ss_currentRelation;
CsvFdwReply *reply;
int i;
const char **keywords;
const char **values;
+ elog(DEBUG1, "%s called", __func__);
+
/* create CsvFdwReply and set default settings */
reply = (CsvFdwReply *) palloc(sizeof(*reply));
reply->filename = NULL;
{
struct stat st;
+ elog(DEBUG1, "opening CSV file \"%s\"", reply->filename);
+
reply->fp = AllocateFile(reply->filename, PG_BINARY_R);
if (reply->fp == NULL)
ereport(ERROR,
scanstate->reply = NULL;
- FreeFile(reply->fp);
+ if (reply->fp)
+ FreeFile(reply->fp);
pfree(reply);
}
* Estimate costs of scanning on a foreign table.
*/
static void
-GetStatistics(Path *path, PlannerInfo *root, RelOptInfo *baserel)
+GetStatistics(ForeignPath *path, PlannerInfo *root, RelOptInfo *baserel)
{
+ RangeTblEntry *rte;
+ ForeignTable *table;
+ int n;
+ const char **keywords;
+ const char **values;
+ int i;
+ char *filename = NULL;
+ struct stat stat;
+ BlockNumber pages;
+ double run_cost = 0;
+ double startup_cost = 0;
+ double cpu_per_tuple;
+
elog(DEBUG1, "%s called", __func__);
+
+ /* get filename from generic option of the foreign table */
+ rte = planner_rt_fetch(baserel->relid, root);
+ table = GetForeignTable(rte->relid);
+ keywords = palloc(sizeof(char *) * list_length(table->options));
+ values = palloc(sizeof(char *) * list_length(table->options));
+ n = flatten_generic_options(table->options, keywords, values);
+
+ for (i = 0; i < n; i++)
+ {
+ if (strcmp(keywords[i], "filename") == 0)
+ {
+ filename = pstrdup(values[i]);
+ break;
+ }
+ }
+
+ pfree(keywords);
+ pfree(values);
+
+ /* at least filename must be specified */
+ if (filename == NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_REPLY),
+ errmsg("generic option \"filename\" is required")));
+ }
+
+ /* get size of the CSV file */
+ if (lstat(filename, &stat) == -1)
+ {
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+ }
+ pfree(filename);
+
+ /*
+ * The way to estimate costs is almost same as cost_seqscan(), but there
+ * are some differences:
+ * - DISK costs are estimated based on CSV file size.
+ * - CPU costs are 2x of seq scan, incremented are overhead to parse CSV
+ */
+ pages = stat.st_size / BLCKSZ;
+ run_cost += seq_page_cost * pages;
+
+ startup_cost += baserel->baserestrictcost.startup;
+ cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
+ cpu_per_tuple *= 2;
+ run_cost += cpu_per_tuple * baserel->tuples;
+
+ path->path.startup_cost = startup_cost;
+ path->path.total_cost = startup_cost + run_cost;
}
/*
WRITE_NODE_FIELD(tidquals);
}
+static void
+_outForeignPath(StringInfo str, ForeignPath *node)
+{
+ WRITE_NODE_TYPE("FOREIGNPATH");
+
+ _outPathInfo(str, (Path *) node);
+}
+
static void
_outAppendPath(StringInfo str, AppendPath *node)
{
case T_TidPath:
_outTidPath(str, obj);
break;
+ case T_ForeignPath:
+ _outForeignPath(str, obj);
+ break;
case T_AppendPath:
_outAppendPath(str, obj);
break;
case T_TidPath:
ptype = "TidScan";
break;
+ case T_ForeignPath:
+ ptype = "ForeignScan";
+ break;
case T_AppendPath:
ptype = "Append";
break;
* Determines and returns the cost of scanning a foreign table sequentially.
*/
void
-cost_foreignscan(Path *path, PlannerInfo *root,
+cost_foreignscan(ForeignPath *path, PlannerInfo *root,
RelOptInfo *baserel)
{
RangeTblEntry *rte;
Path *
create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel)
{
- Path *pathnode = makeNode(Path);
+ ForeignPath *pathnode = makeNode(ForeignPath);
- pathnode->pathtype = T_ForeignScan;
- pathnode->parent = rel;
- pathnode->pathkeys = NIL; /* result is always unordered */
+ pathnode->path.pathtype = T_ForeignScan;
+ pathnode->path.parent = rel;
+ pathnode->path.pathkeys = NIL; /* result is always unordered */
cost_foreignscan(pathnode, root, rel);
* Estimate costs of a foreign path.
* FDW should update startup_cost and total_cost in the Path.
*/
- void (*GetStatistics)(Path *path, PlannerInfo *root, RelOptInfo *baserel);
+ void (*GetStatistics)(ForeignPath *path, PlannerInfo *root,
+ RelOptInfo *baserel);
/*
* Deparse query request and open a cursor for the foreign scan.
T_MergePath,
T_HashPath,
T_TidPath,
+ T_ForeignPath,
T_AppendPath,
T_ResultPath,
T_MaterialPath,
List *tidquals; /* qual(s) involving CTID = something */
} TidPath;
+/*
+ * ForeignPath represents a scan on a foreign table
+ */
+typedef struct ForeignPath
+{
+ Path path;
+} ForeignPath;
+
/*
* AppendPath represents an Append plan, ie, successive execution of
* several member plans.
extern void cost_valuesscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel);
extern void cost_ctescan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
-extern void cost_foreignscan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
+extern void cost_foreignscan(ForeignPath *path, PlannerInfo *root, RelOptInfo *baserel);
extern void cost_recursive_union(Plan *runion, Plan *nrterm, Plan *rterm);
extern void cost_sort(Path *path, PlannerInfo *root,
List *pathkeys, Cost input_cost, double tuples, int width,