diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 608ebf93ce..39e8073f55 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994-5, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.163 2007/05/04 21:29:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.164 2007/05/25 17:54:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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); + /* if an advisor plugin is present, let it manage things */ + if (ExplainOneQuery_hook) + (*ExplainOneQuery_hook) (query, stmt, queryString, params, tstate); + else + { + PlannedStmt *plan; - /* - * 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(); + /* plan the query */ + plan = planner(query, 0, params); - /* Create a QueryDesc requesting no output */ - queryDesc = CreateQueryDesc(plan, - ActiveSnapshot, InvalidSnapshot, - None_Receiver, params, - stmt->analyze); - - 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 997f66c818..de999a3637 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -10,7 +10,7 @@ * Copyright (c) 2002-2007, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.75 2007/04/27 22:05:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.76 2007/05/25 17:54:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 d3fbf34833..6c14b8a413 100644 --- a/src/backend/executor/nodeBitmapIndexscan.c +++ b/src/backend/executor/nodeBitmapIndexscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.22 2007/01/05 22:19:28 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.23 2007/05/25 17:54:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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); } /* ---------------------------------------------------------------- @@ -256,6 +258,14 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) indexstate->ss.ss_currentRelation = NULL; 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. * diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index d9136de003..8c22e3ade0 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.121 2007/04/06 22:33:42 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.122 2007/05/25 17:54:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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. @@ -520,6 +522,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. * diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 366c50f273..0f9776cdca 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.219 2007/05/04 01:13:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.220 2007/05/25 17:54:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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,9 +83,29 @@ 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; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 662fcbaaf8..605e7f64ee 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.134 2007/04/21 21:01:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.135 2007/05/25 17:54:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 42879ce5a4..73bf41e49c 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994-5, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/explain.h,v 1.30 2007/03/13 00:33:43 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/explain.h,v 1.31 2007/05/25 17:54:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 31391d29a9..a19ba780ee 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.43 2007/01/05 22:19:56 momjian Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.44 2007/05/25 17:54:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 b568a7dbe8..a08a83ae71 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.39 2007/04/16 01:14:58 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.40 2007/05/25 17:54:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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);