From: Shigeru Hanada Date: Fri, 1 Oct 2010 10:53:31 +0000 (+0900) Subject: Add ANALYZE support to foreign-data wrapper handler. X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=5e132ed218eb973286e36235ecfbe5918fce3cb4;p=users%2Fhanada%2Fpostgres.git Add ANALYZE support to foreign-data wrapper handler. --- diff --git a/contrib/postgresql_fdw/postgresql_fdw.c b/contrib/postgresql_fdw/postgresql_fdw.c index fce9bcb463..a287177d8c 100644 --- a/contrib/postgresql_fdw/postgresql_fdw.c +++ b/contrib/postgresql_fdw/postgresql_fdw.c @@ -51,6 +51,8 @@ static void pgOpen(ForeignScanState *scanstate); 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); /* deparse SQL from the request */ static bool is_immutable_func(Oid funcid); @@ -81,7 +83,8 @@ FdwRoutine postgresql_fdw_routine = pgOpen, pgIterate, pgClose, - pgReOpen + pgReOpen, + pgAnalyze, }; /* @@ -735,3 +738,15 @@ storeResult(Tuplestorestate *tupstore, tuplestore_donestoring(tupstore); 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"))); +} diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml index d5f2528eb9..ff80cfbb9e 100644 --- a/doc/src/sgml/ref/analyze.sgml +++ b/doc/src/sgml/ref/analyze.sgml @@ -43,6 +43,13 @@ ANALYZE [ VERBOSE ] [ table [ ( + + + If the table was a foreign table, ANALYZE 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. + diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 14b66dd35b..d8a48f7b80 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -182,12 +182,13 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, * 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) + if (onerel->rd_rel->relkind != RELKIND_RELATION && + onerel->rd_rel->relkind != RELKIND_FOREIGN_TABLE) { /* 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, foreign tables or special system tables", + (errmsg("skipping \"%s\" --- cannot analyze indexes, views or special system tables", RelationGetRelationName(onerel)))); relation_close(onerel, ShareUpdateExclusiveLock); return; @@ -224,7 +225,26 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, /* * Do the normal non-recursive ANALYZE. */ - do_analyze_rel(onerel, vacstmt, update_reltuples, false); + 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); /* * If there are child tables, do recursive ANALYZE. diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 51bd3e1ae7..5492d0f50e 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -329,30 +329,38 @@ get_rel_oids(Oid relid, const RangeVar *vacrel, const char *stmttype) } else { - /* Process all plain relations listed in pg_class */ + /* Process all plain relations and foreign tables listed in pg_class */ Relation pgclass; HeapScanDesc scan; HeapTuple tuple; - ScanKeyData key; + ScanKeyData key[2]; + int i; - ScanKeyInit(&key, + ScanKeyInit(&key[0], Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_RELATION)); - pgclass = heap_open(RelationRelationId, AccessShareLock); + ScanKeyInit(&key[1], + Anum_pg_class_relkind, + BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(RELKIND_FOREIGN_TABLE)); - scan = heap_beginscan(pgclass, SnapshotNow, 1, &key); + pgclass = heap_open(RelationRelationId, AccessShareLock); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + for (i = 0; i < 2; i++) { - /* Make a relation list entry for this guy */ - oldcontext = MemoryContextSwitchTo(vac_context); - oid_list = lappend_oid(oid_list, HeapTupleGetOid(tuple)); - MemoryContextSwitchTo(oldcontext); + 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); } - heap_endscan(scan); heap_close(pgclass, AccessShareLock); } diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 14f2369a0f..01e47a2939 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -348,6 +348,21 @@ static const SchemaQuery Query_for_list_of_tsvf = { 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", @@ -2544,7 +2559,7 @@ psql_completion(char *text, int start, int end) 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_tables, + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tf, " UNION SELECT 'VERBOSE'"); else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 && pg_strcasecmp(prev_wd, "VERBOSE") == 0 && @@ -2558,13 +2573,13 @@ psql_completion(char *text, int start, int end) " 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_tables, + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tf, " 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_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tf, NULL); /* WITH [RECURSIVE] */ else if (pg_strcasecmp(prev_wd, "WITH") == 0) @@ -2573,7 +2588,7 @@ psql_completion(char *text, int start, int end) /* 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_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tf, NULL); /* WHERE */ /* Simple case of the word before the where being the table name */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 5b899c0501..e7254f7ac4 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -20,6 +20,7 @@ #include "foreign/foreign.h" #include "nodes/params.h" #include "nodes/plannodes.h" +#include "nodes/relation.h" #include "nodes/tidbitmap.h" #include "utils/hsearch.h" #include "utils/rel.h" @@ -1415,6 +1416,16 @@ struct FdwRoutine * 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); }; /* ----------------------------------------------------------------