diff options
author | Tom Lane | 2007-05-25 17:54:25 +0000 |
---|---|---|
committer | Tom Lane | 2007-05-25 17:54:25 +0000 |
commit | b74d8cc49642a5e8177b5d172fc7773327f40650 (patch) | |
tree | 5ca9dcaca65db412e13c30697b9ef02029f14e62 | |
parent | 3950b585a3a54bc84829e6b04e7872a8ddc0ead8 (diff) |
Create hooks to let a loadable plugin monitor (or even replace) the planner
and/or create plans for hypothetical situations; in particular, investigate
plans that would be generated using hypothetical indexes. This is a
heavily-rewritten version of the hooks proposed by Gurjeet Singh for his
Index Advisor project. In this formulation, the index advisor can be
entirely a loadable module instead of requiring a significant part to be
in the core backend, and plans can be generated for hypothetical indexes
without requiring the creation and rolling-back of system catalog entries.
The index advisor patch as-submitted is not compatible with these hooks,
but it needs significant work anyway due to other 8.2-to-8.3 planner
changes. With these hooks in the core backend, development of the advisor
can proceed as a pgfoundry project.
-rw-r--r-- | src/backend/commands/explain.c | 94 | ||||
-rw-r--r-- | src/backend/commands/prepare.c | 19 | ||||
-rw-r--r-- | src/backend/executor/nodeBitmapIndexscan.c | 16 | ||||
-rw-r--r-- | src/backend/executor/nodeIndexscan.c | 16 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 24 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 11 | ||||
-rw-r--r-- | src/include/commands/explain.h | 16 | ||||
-rw-r--r-- | src/include/optimizer/plancat.h | 7 | ||||
-rw-r--r-- | src/include/optimizer/planner.h | 9 |
9 files changed, 159 insertions, 53 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index cb95fea771..078b1bbc9e 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -33,6 +33,12 @@ #include "utils/tuplesort.h" +/* Hook for plugins to get control in ExplainOneQuery() */ +ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL; +/* Hook for plugins to get control in explain_get_index_name() */ +explain_get_index_name_hook_type explain_get_index_name_hook = NULL; + + typedef struct ExplainState { /* options */ @@ -61,6 +67,8 @@ static void show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols, StringInfo str, int indent, ExplainState *es); static void show_sort_info(SortState *sortstate, StringInfo str, int indent, ExplainState *es); +static const char *explain_get_index_name(Oid indexId); + /* * ExplainQuery - @@ -140,9 +148,6 @@ static void ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString, ParamListInfo params, TupOutputState *tstate) { - PlannedStmt *plan; - QueryDesc *queryDesc; - /* planner will not cope with utility statements */ if (query->commandType == CMD_UTILITY) { @@ -151,25 +156,19 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, const char *queryString, return; } - /* plan the query */ - plan = planner(query, 0, params); - - /* - * Update snapshot command ID to ensure this query sees results of any - * previously executed queries. (It's a bit cheesy to modify - * ActiveSnapshot without making a copy, but for the limited ways in which - * EXPLAIN can be invoked, I think it's OK, because the active snapshot - * shouldn't be shared with anything else anyway.) - */ - ActiveSnapshot->curcid = GetCurrentCommandId(); + /* if an advisor plugin is present, let it manage things */ + if (ExplainOneQuery_hook) + (*ExplainOneQuery_hook) (query, stmt, queryString, params, tstate); + else + { + PlannedStmt *plan; - /* Create a QueryDesc requesting no output */ - queryDesc = CreateQueryDesc(plan, - ActiveSnapshot, InvalidSnapshot, - None_Receiver, params, - stmt->analyze); + /* plan the query */ + plan = planner(query, 0, params); - ExplainOnePlan(queryDesc, stmt, tstate); + /* run it (if needed) and produce output */ + ExplainOnePlan(plan, params, stmt, tstate); + } } /* @@ -210,20 +209,35 @@ ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt, * not running the query. No cursor will be created, however. * * This is exported because it's called back from prepare.c in the - * EXPLAIN EXECUTE case - * - * Note: the passed-in QueryDesc is freed when we're done with it + * EXPLAIN EXECUTE case, and because an index advisor plugin would need + * to call it. */ void -ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt, - TupOutputState *tstate) +ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params, + ExplainStmt *stmt, TupOutputState *tstate) { + QueryDesc *queryDesc; instr_time starttime; double totaltime = 0; ExplainState *es; StringInfoData buf; int eflags; + /* + * Update snapshot command ID to ensure this query sees results of any + * previously executed queries. (It's a bit cheesy to modify + * ActiveSnapshot without making a copy, but for the limited ways in which + * EXPLAIN can be invoked, I think it's OK, because the active snapshot + * shouldn't be shared with anything else anyway.) + */ + ActiveSnapshot->curcid = GetCurrentCommandId(); + + /* Create a QueryDesc requesting no output */ + queryDesc = CreateQueryDesc(plannedstmt, + ActiveSnapshot, InvalidSnapshot, + None_Receiver, params, + stmt->analyze); + INSTR_TIME_SET_CURRENT(starttime); /* If analyzing, we need to cope with queued triggers */ @@ -592,7 +606,7 @@ explain_outNode(StringInfo str, if (ScanDirectionIsBackward(((IndexScan *) plan)->indexorderdir)) appendStringInfoString(str, " Backward"); appendStringInfo(str, " using %s", - quote_identifier(get_rel_name(((IndexScan *) plan)->indexid))); + explain_get_index_name(((IndexScan *) plan)->indexid)); /* FALL THRU */ case T_SeqScan: case T_BitmapHeapScan: @@ -618,7 +632,7 @@ explain_outNode(StringInfo str, break; case T_BitmapIndexScan: appendStringInfo(str, " on %s", - quote_identifier(get_rel_name(((BitmapIndexScan *) plan)->indexid))); + explain_get_index_name(((BitmapIndexScan *) plan)->indexid)); break; case T_SubqueryScan: if (((Scan *) plan)->scanrelid > 0) @@ -1150,3 +1164,29 @@ show_sort_info(SortState *sortstate, pfree(sortinfo); } } + +/* + * Fetch the name of an index in an EXPLAIN + * + * We allow plugins to get control here so that plans involving hypothetical + * indexes can be explained. + */ +static const char * +explain_get_index_name(Oid indexId) +{ + const char *result; + + if (explain_get_index_name_hook) + result = (*explain_get_index_name_hook) (indexId); + else + result = NULL; + if (result == NULL) + { + /* default behavior: look in the catalogs and quote it */ + result = get_rel_name(indexId); + if (result == NULL) + elog(ERROR, "cache lookup failed for index %u", indexId); + result = quote_identifier(result); + } + return result; +} diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index f29e1a1859..aad8dc05c9 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -678,8 +678,6 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt, if (IsA(pstmt, PlannedStmt)) { - QueryDesc *qdesc; - if (execstmt->into) { if (pstmt->commandType != CMD_SELECT || @@ -694,22 +692,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt, pstmt->intoClause = execstmt->into; } - /* - * Update snapshot command ID to ensure this query sees results of - * any previously executed queries. (It's a bit cheesy to modify - * ActiveSnapshot without making a copy, but for the limited ways - * in which EXPLAIN can be invoked, I think it's OK, because the - * active snapshot shouldn't be shared with anything else anyway.) - */ - ActiveSnapshot->curcid = GetCurrentCommandId(); - - /* Create a QueryDesc requesting no output */ - qdesc = CreateQueryDesc(pstmt, - ActiveSnapshot, InvalidSnapshot, - None_Receiver, - paramLI, stmt->analyze); - - ExplainOnePlan(qdesc, stmt, tstate); + ExplainOnePlan(pstmt, paramLI, stmt, tstate); } else { diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c index d0ac90e819..87667f72fc 100644 --- a/src/backend/executor/nodeBitmapIndexscan.c +++ b/src/backend/executor/nodeBitmapIndexscan.c @@ -198,10 +198,12 @@ ExecEndBitmapIndexScan(BitmapIndexScanState *node) #endif /* - * close the index relation + * close the index relation (no-op if we didn't open it) */ - index_endscan(indexScanDesc); - index_close(indexRelationDesc, NoLock); + if (indexScanDesc) + index_endscan(indexScanDesc); + if (indexRelationDesc) + index_close(indexRelationDesc, NoLock); } /* ---------------------------------------------------------------- @@ -257,6 +259,14 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) indexstate->ss.ss_currentScanDesc = NULL; /* + * If we are just doing EXPLAIN (ie, aren't going to run the plan), + * stop here. This allows an index-advisor plugin to EXPLAIN a plan + * containing references to nonexistent indexes. + */ + if (eflags & EXEC_FLAG_EXPLAIN_ONLY) + return indexstate; + + /* * Open the index relation. * * If the parent table is one of the target relations of the query, then diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index d4d9aef793..e6412a8fec 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -415,10 +415,12 @@ ExecEndIndexScan(IndexScanState *node) ExecClearTuple(node->ss.ss_ScanTupleSlot); /* - * close the index relation + * close the index relation (no-op if we didn't open it) */ - index_endscan(indexScanDesc); - index_close(indexRelationDesc, NoLock); + if (indexScanDesc) + index_endscan(indexScanDesc); + if (indexRelationDesc) + index_close(indexRelationDesc, NoLock); /* * close the heap relation. @@ -521,6 +523,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation)); /* + * If we are just doing EXPLAIN (ie, aren't going to run the plan), + * stop here. This allows an index-advisor plugin to EXPLAIN a plan + * containing references to nonexistent indexes. + */ + if (eflags & EXEC_FLAG_EXPLAIN_ONLY) + return indexstate; + + /* * Open the index relation. * * If the parent table is one of the target relations of the query, then diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index bb9f468f2b..987c113e77 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -42,6 +42,10 @@ #include "utils/syscache.h" +/* Hook for plugins to get control in planner() */ +planner_hook_type planner_hook = NULL; + + /* Expression kind codes for preprocess_expression */ #define EXPRKIND_QUAL 0 #define EXPRKIND_TARGET 1 @@ -79,11 +83,31 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist); * * Query optimizer entry point * + * To support loadable plugins that monitor or modify planner behavior, + * we provide a hook variable that lets a plugin get control before and + * after the standard planning process. The plugin would normally call + * standard_planner(). + * + * Note to plugin authors: standard_planner() scribbles on its Query input, + * so you'd better copy that data structure if you want to plan more than once. + * *****************************************************************************/ PlannedStmt * planner(Query *parse, int cursorOptions, ParamListInfo boundParams) { PlannedStmt *result; + + if (planner_hook) + result = (*planner_hook) (parse, cursorOptions, boundParams); + else + result = standard_planner(parse, cursorOptions, boundParams); + return result; +} + +PlannedStmt * +standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) +{ + PlannedStmt *result; PlannerGlobal *glob; double tuple_fraction; PlannerInfo *root; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index fd29e53720..a15b9393f9 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -40,6 +40,9 @@ /* GUC parameter */ bool constraint_exclusion = false; +/* Hook for plugins to get control in get_relation_info() */ +get_relation_info_hook_type get_relation_info_hook = NULL; + static void estimate_rel_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples); @@ -279,6 +282,14 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, rel->indexlist = indexinfos; heap_close(relation, NoLock); + + /* + * Allow a plugin to editorialize on the info we obtained from the + * catalogs. Actions might include altering the assumed relation size, + * removing an index, or adding a hypothetical index to the indexlist. + */ + if (get_relation_info_hook) + (*get_relation_info_hook) (root, relationObjectId, inhparent, rel); } /* diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index 7d882e5b14..5565380e9b 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -15,6 +15,18 @@ #include "executor/executor.h" +/* Hook for plugins to get control in ExplainOneQuery() */ +typedef void (*ExplainOneQuery_hook_type) (Query *query, + ExplainStmt *stmt, + const char *queryString, + ParamListInfo params, + TupOutputState *tstate); +extern DLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook; + +/* Hook for plugins to get control in explain_get_index_name() */ +typedef const char * (*explain_get_index_name_hook_type) (Oid indexId); +extern DLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook; + extern void ExplainQuery(ExplainStmt *stmt, const char *queryString, ParamListInfo params, DestReceiver *dest); @@ -26,7 +38,7 @@ extern void ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt, ParamListInfo params, TupOutputState *tstate); -extern void ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt, - TupOutputState *tstate); +extern void ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params, + ExplainStmt *stmt, TupOutputState *tstate); #endif /* EXPLAIN_H */ diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index da3a4cd0ca..261f7f5799 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -16,6 +16,13 @@ #include "nodes/relation.h" +/* Hook for plugins to get control in get_relation_info() */ +typedef void (*get_relation_info_hook_type) (PlannerInfo *root, + Oid relationObjectId, + bool inhparent, + RelOptInfo *rel); +extern DLLIMPORT get_relation_info_hook_type get_relation_info_hook; + extern void get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, RelOptInfo *rel); diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index c48c596f1b..7cfca18a65 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -18,8 +18,17 @@ #include "nodes/relation.h" +/* Hook for plugins to get control in planner() */ +typedef PlannedStmt * (*planner_hook_type) (Query *parse, + int cursorOptions, + ParamListInfo boundParams); +extern DLLIMPORT planner_hook_type planner_hook; + + extern PlannedStmt *planner(Query *parse, int cursorOptions, ParamListInfo boundParams); +extern PlannedStmt *standard_planner(Query *parse, int cursorOptions, + ParamListInfo boundParams); extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse, Index level, double tuple_fraction, PlannerInfo **subroot); |