From e93de476f9e9c87284b466eaab70f4b92c9d4bb3 Mon Sep 17 00:00:00 2001 From: Julien Rouhaud Date: Sun, 15 Mar 2015 03:15:25 +0100 Subject: [PATCH] Add SRF to list hypothetical indexes --- pg_hypo--0.1.sql | 23 ++++++++++-- pg_hypo.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 4 deletions(-) diff --git a/pg_hypo--0.1.sql b/pg_hypo--0.1.sql index 4a8440a..cfc79b8 100644 --- a/pg_hypo--0.1.sql +++ b/pg_hypo--0.1.sql @@ -27,8 +27,13 @@ pg_hypo_add_index_internal(IN indexid oid, LANGUAGE c COST 1000 AS '$libdir/pg_hypo', 'pg_hypo_add_index_internal'; +CREATE FUNCTION pg_hypo(OUT indexname text, OUT relid Oid, OUT attnum int, OUT amid Oid) + RETURNS SETOF record + LANGUAGE c COST 1000 +AS '$libdir/pg_hypo', 'pg_hypo'; + CREATE FUNCTION -pg_hypo_add_index(IN _nspname name, IN _relname name, IN _attname name, IN _indtype text) +pg_hypo_add_index(IN _nspname name, IN _relname name, IN _attname name, IN _amname text) RETURNS bool AS $_$ @@ -55,7 +60,21 @@ $_$ n.nspname = _nspname AND c.relname = _relname AND a.attname = _attname - AND am.amname = _indtype + AND am.amname = _amname ) src; $_$ LANGUAGE sql; + +CREATE FUNCTION pg_hypo_list_indexes(OUT indexname text, OUT nspname name, OUT relname name, OUT attname name, OUT amname name) + RETURNS SETOF record +AS +$_$ + SELECT h.indexname, n.nspname, c.relname, a.attname, am.amname + FROM pg_hypo() h + JOIN pg_class c ON c.oid = h.relid + JOIN pg_namespace n ON n.oid = c.relnamespace + JOIN pg_attribute a on a.attrelid = c.oid + JOIN pg_am am ON am.oid = h.amid + WHERE a.attnum = h.attnum; +$_$ +LANGUAGE sql; diff --git a/pg_hypo.c b/pg_hypo.c index e8e2016..3d346f7 100644 --- a/pg_hypo.c +++ b/pg_hypo.c @@ -16,6 +16,7 @@ #include "catalog/pg_opfamily.h" #include "catalog/pg_type.h" #include "commands/explain.h" +#include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodes.h" @@ -35,7 +36,8 @@ PG_MODULE_MAGIC; static const uint32 PGHYPO_FILE_HEADER = 0x6879706f; -#define HYPO_MAX_COL 1 +#define HYPO_MAX_COLS 1 +#define HYPO_NB_COLS 4 #if PG_VERSION_NUM >= 90300 #define HYPO_DUMP_FILE "pg_stat/pg_hypo.stat" #else @@ -86,6 +88,7 @@ Datum pg_hypo_add_index_internal(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(pg_hypo_reset); PG_FUNCTION_INFO_V1(pg_hypo_add_index_internal); +PG_FUNCTION_INFO_V1(pg_hypo); static Size hypo_memsize(void); static void entry_reset(void); @@ -344,7 +347,7 @@ entry_store(Oid indexid, bool found = false; /* Make sure user didn't try to add too many columns */ - if (ncolumns > HYPO_MAX_COL) + if (ncolumns > HYPO_MAX_COLS) return false; entry = hypoEntries; @@ -664,9 +667,96 @@ pg_hypo_add_index_internal(PG_FUNCTION_ARGS) Oid opfamily = PG_GETARG_OID(7); Oid opcintype = PG_GETARG_OID(8); + if (!hypo) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("pg_hypo must be loaded via shared_preload_libraries"))); + return entry_store(indexid, relid, indexname, relam, ncolumns, indexkeys, indexcollations, opfamily, opcintype); } +/* + * List created hypothetical indexes + */ +Datum +pg_hypo(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + hypoEntry *entry; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + int i = 0; + + + if (!hypo) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("pg_hypo must be loaded via shared_preload_libraries"))); + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + LWLockAcquire(hypo->lock, LW_SHARED); + + entry = hypoEntries; + while (i < hypo_max_indexes) + { + Datum values[HYPO_NB_COLS]; + bool nulls[HYPO_NB_COLS]; + int j = 0; + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + if (entry->indexid == InvalidOid) + break; + SpinLockAcquire(&entry->mutex); + + values[j++] = CStringGetTextDatum(entry->indexname); + values[j++] = ObjectIdGetDatum(entry->relid); + values[j++] = Int32GetDatum(entry->indexkeys); + values[j++] = ObjectIdGetDatum(entry->relam); + + SpinLockRelease(&entry->mutex); + + Assert(j == PG_STAT_PLAN_COLS); + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + entry++; + i++; + } + + LWLockRelease(hypo->lock); + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} + // stolen from backend/optimisze/util/plancat.c, no export of this function :( static List * build_index_tlist(PlannerInfo *root, IndexOptInfo *index,