static void pgIterate(ForeignScanState *scanstate);
static void pgClose(ForeignScanState *scanstate);
static void pgReOpen(ForeignScanState *scanstate);
-static void pgAnalyze(Relation rel, VacuumStmt *vacstmt, bool update_reltuples,
- bool inh);
-static void pgEstimateCost(Path *path, PlannerInfo *root, RelOptInfo *baserel);
+static void pgGetStatistics(Path *path, PlannerInfo *root, RelOptInfo *baserel);
/* deparse SQL from the request */
static bool is_immutable_func(Oid funcid);
{
pgConnectServer,
pgFreeFSConnection,
+ pgGetStatistics,
pgOpen,
pgIterate,
pgClose,
pgReOpen,
- pgAnalyze,
- pgEstimateCost,
};
/*
pfree(values);
}
-/*
- * Collect statistics about contents of remote table and update pg_statistics.
- * If update_reltuples was true, update pg_class.reltuples/relpages too.
- */
-static void
-pgAnalyze(Relation rel, VacuumStmt *vacstmt, bool update_reltuples, bool inh)
-{
- ereport(WARNING,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("analyze of remote PostgreSQL table not implemented")));
-}
-
/*
* Estimate costs of scanning on a foreign table.
*
* baserel->baserestrictinfo can be used to examine quals on the relation.
*/
static void
-pgEstimateCost(Path *path, PlannerInfo *root, RelOptInfo *baserel)
+pgGetStatistics(Path *path, PlannerInfo *root, RelOptInfo *baserel)
{
RangeTblEntry *rte;
ForeignTable *table;
only that table. It is further possible to give a list of column names,
in which case only the statistics for those columns are collected.
</para>
-
- <para>
- If the table was a foreign table, <command>ANALYZE</command> transfers the
- responsibility to collect statistics to foreign-data wrapper's handler. If
- the handler of the foreign-data wrapper didn't support Analyze() API,
- analyze of the table is skipped.
- </para>
</refsect1>
<refsect1>
* Check that it's a plain table; we used to do this in get_rel_oids() but
* seems safer to check after we've locked the relation.
*/
- if (onerel->rd_rel->relkind != RELKIND_RELATION &&
- onerel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+ if (onerel->rd_rel->relkind != RELKIND_RELATION)
{
/* No need for a WARNING if we already complained during VACUUM */
if (!(vacstmt->options & VACOPT_VACUUM))
ereport(WARNING,
- (errmsg("skipping \"%s\" --- cannot analyze indexes, views or special system tables",
+ (errmsg("skipping \"%s\" --- cannot analyze indexes, views, foreign tables or special system tables",
RelationGetRelationName(onerel))));
relation_close(onerel, ShareUpdateExclusiveLock);
return;
/*
* Do the normal non-recursive ANALYZE.
*/
- if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
- {
- ForeignTable *table;
- ForeignServer *server;
- ForeignDataWrapper *wrapper;
- FdwRoutine *routine;
-
- table = GetForeignTable(onerel->rd_id);
- server = GetForeignServer(table->serverid);
- wrapper = GetForeignDataWrapper(server->fdwid);
- routine = GetFdwRoutine(wrapper->fdwhandler);
- if (routine->Analyze)
- routine->Analyze(onerel, vacstmt, update_reltuples, false);
- else
- ereport(WARNING,
- (errmsg("skipping \"%s\" --- fdwhandler doesn't support analyze",
- RelationGetRelationName(onerel))));
- }
- else
- do_analyze_rel(onerel, vacstmt, update_reltuples, false);
+ do_analyze_rel(onerel, vacstmt, update_reltuples, false);
/*
* If there are child tables, do recursive ANALYZE.
}
else
{
- /* Process all plain relations and foreign tables listed in pg_class */
+ /* Process all plain relations listed in pg_class */
Relation pgclass;
HeapScanDesc scan;
HeapTuple tuple;
- ScanKeyData key[2];
- int i;
+ ScanKeyData key;
- ScanKeyInit(&key[0],
+ ScanKeyInit(&key,
Anum_pg_class_relkind,
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- ScanKeyInit(&key[1],
- Anum_pg_class_relkind,
- BTEqualStrategyNumber, F_CHAREQ,
- CharGetDatum(RELKIND_FOREIGN_TABLE));
-
pgclass = heap_open(RelationRelationId, AccessShareLock);
- for (i = 0; i < 2; i++)
+ scan = heap_beginscan(pgclass, SnapshotNow, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- scan = heap_beginscan(pgclass, SnapshotNow, 1, &key[i]);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- /* Make a relation list entry for this guy */
- oldcontext = MemoryContextSwitchTo(vac_context);
- oid_list = lappend_oid(oid_list, HeapTupleGetOid(tuple));
- MemoryContextSwitchTo(oldcontext);
- }
- heap_endscan(scan);
+ /* Make a relation list entry for this guy */
+ oldcontext = MemoryContextSwitchTo(vac_context);
+ oid_list = lappend_oid(oid_list, HeapTupleGetOid(tuple));
+ MemoryContextSwitchTo(oldcontext);
}
+ heap_endscan(scan);
heap_close(pgclass, AccessShareLock);
}
Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_RELATION);
- /* Have the wrapper handler estimate the cost of this scan. */
+ /* Leave estimation of the costs to the wrapper handler */
rte = planner_rt_fetch(baserel->relid, root);
table = GetForeignTable(rte->relid);
server = GetForeignServer(table->serverid);
wrapper = GetForeignDataWrapper(server->fdwid);
routine = GetFdwRoutine(wrapper->fdwhandler);
- /* Use cost_seqscan() instead if the wrapper did't support this API. */
- if (routine->EstimateCost != NULL)
- routine->EstimateCost(path, root, baserel);
- else
- cost_seqscan(path, root, baserel);
+ routine->GetStatistics(path, root, baserel);
}
/*
NULL
};
-static const SchemaQuery Query_for_list_of_tf = {
- /* catname */
- "pg_catalog.pg_class c",
- /* selcondition */
- "c.relkind IN ('r', 'f')",
- /* viscondition */
- "pg_catalog.pg_table_is_visible(c.oid)",
- /* namespace */
- "c.relnamespace",
- /* result */
- "pg_catalog.quote_ident(c.relname)",
- /* qualresult */
- NULL
-};
-
static const SchemaQuery Query_for_list_of_views = {
/* catname */
"pg_catalog.pg_class c",
pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
(pg_strcasecmp(prev2_wd, "FULL") == 0 ||
pg_strcasecmp(prev2_wd, "FREEZE") == 0))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tf,
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
" UNION SELECT 'VERBOSE'");
else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
" UNION SELECT 'ANALYZE'");
else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
pg_strcasecmp(prev_wd, "ANALYZE") == 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tf,
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
" UNION SELECT 'VERBOSE'");
else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
pg_strcasecmp(prev2_wd, "VERBOSE") == 0) ||
(pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
pg_strcasecmp(prev2_wd, "ANALYZE") == 0))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tf, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
/* WITH [RECURSIVE] */
else if (pg_strcasecmp(prev_wd, "WITH") == 0)
/* ANALYZE */
/* If the previous word is ANALYZE, produce list of tables */
else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tf, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
/* WHERE */
/* Simple case of the word before the where being the table name */
*/
void (*FreeFSConnection)(FSConnection *conn);
+ /*
+ * 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);
+
/*
* Deparse query request and open a cursor for the foreign scan.
*/
* executed again.
*/
void (*ReOpen)(ForeignScanState *scanstate);
-
- /*
- * Emulate ANALYZE on a table and create/update the statistics for the
- * foreign table. Statistics to be updated are:
- * - all columns of pg_statistics
- * - reltuples, relpages of pg_class (only if update_reltuples was true)
- *
- * If this API was not implemented (== NULL), ANALYZE skips that relation.
- */
- void (*Analyze)(Relation rel, VacuumStmt *vacstmt, bool update_reltuples,
- bool inh);
-
- /*
- * Estimate costs of scanning on the foreign table.
- * FDW should update startup_cost and total_cost in the Path.
- *
- * If this API was not implemented (== NULL), cost_seqscan() will be used
- * instead.
- */
- void (*EstimateCost)(Path *path, PlannerInfo *root, RelOptInfo *baserel);
};
/* ----------------------------------------------------------------