diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 80d6df8..36ff2f9 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -723,7 +723,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match the definition of the * pg_prepared_statements view in system_views.sql */ - tupdesc = CreateTemplateTupleDesc(5); + tupdesc = CreateTemplateTupleDesc(11); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", @@ -734,6 +734,18 @@ pg_prepared_statement(PG_FUNCTION_ARGS) REGTYPEARRAYOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "calls", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "custom_calls", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 8, "plan_generation", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 9, "generic_cost", + FLOAT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 10, "custom_cost", + FLOAT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 11, "last_plan", + TEXTOID, -1, 0); /* * We put all the tuples into a tuplestore in one scan of the hashtable. @@ -755,8 +767,8 @@ pg_prepared_statement(PG_FUNCTION_ARGS) hash_seq_init(&hash_seq, prepared_queries); while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL) { - Datum values[5]; - bool nulls[5]; + Datum values[11]; + bool nulls[11]; MemSet(nulls, 0, sizeof(nulls)); @@ -766,6 +778,27 @@ pg_prepared_statement(PG_FUNCTION_ARGS) values[3] = build_regtype_array(prep_stmt->plansource->param_types, prep_stmt->plansource->num_params); values[4] = BoolGetDatum(prep_stmt->from_sql); + values[5] = Int64GetDatumFast(prep_stmt->plansource->num_total_calls); + values[6] = Int64GetDatumFast(prep_stmt->plansource->num_custom_plans); + values[7] = Int64GetDatumFast(prep_stmt->plansource->generation); + if (prep_stmt->plansource->generic_cost >= 0.0) + values[8] = Float8GetDatum(prep_stmt->plansource->generic_cost); + else + nulls[8] = true; + + if (prep_stmt->plansource->num_custom_plans > 0) + values[9] = + Float8GetDatum(prep_stmt->plansource->total_custom_cost / + prep_stmt->plansource->num_custom_plans); + else + nulls[9] = true; + + if (prep_stmt->plansource->last_plan_type == PLAN_CACHE_TYPE_CUSTOM) + values[10] = CStringGetTextDatum("custom"); + else if (prep_stmt->plansource->last_plan_type == PLAN_CACHE_TYPE_GENERIC) + values[10] = CStringGetTextDatum("generic"); + else + nulls[10] = true; tuplestore_putvalues(tupstore, tupdesc, values, nulls); } diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 75b475c..6457fcc 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -219,6 +219,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree, plansource->generic_cost = -1; plansource->total_custom_cost = 0; plansource->num_custom_plans = 0; + plansource->last_plan_type = PLAN_CACHE_TYPE_NONE; MemoryContextSwitchTo(oldcxt); @@ -286,6 +287,8 @@ CreateOneShotCachedPlan(RawStmt *raw_parse_tree, plansource->generic_cost = -1; plansource->total_custom_cost = 0; plansource->num_custom_plans = 0; + plansource->num_total_calls = 0; + plansource->last_plan_type = PLAN_CACHE_TYPE_NONE; return plansource; } @@ -1213,13 +1216,15 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, { /* Build a custom plan */ plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv); - /* Accumulate total costs of custom plans, but 'ware overflow */ - if (plansource->num_custom_plans < INT_MAX) - { - plansource->total_custom_cost += cached_plan_cost(plan, true); - plansource->num_custom_plans++; - } + /* Accumulate total costs of custom plans */ + plansource->total_custom_cost += cached_plan_cost(plan, true); + plansource->num_custom_plans++; + plansource->last_plan_type = PLAN_CACHE_TYPE_CUSTOM; } + else + plansource->last_plan_type = PLAN_CACHE_TYPE_GENERIC; + + plansource->num_total_calls++; Assert(plan != NULL); @@ -1575,6 +1580,10 @@ CopyCachedPlan(CachedPlanSource *plansource) newsource->generic_cost = plansource->generic_cost; newsource->total_custom_cost = plansource->total_custom_cost; newsource->num_custom_plans = plansource->num_custom_plans; + newsource->num_total_calls = plansource->num_total_calls; + + /* XXX: Should inherit original plansource? */ + newsource->last_plan_type = PLAN_CACHE_TYPE_NONE; MemoryContextSwitchTo(oldcxt); diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 4bce3ad..52add8d 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7745,9 +7745,9 @@ { oid => '2510', descr => 'get the prepared statements for this session', proname => 'pg_prepared_statement', prorows => '1000', proretset => 't', provolatile => 's', proparallel => 'r', prorettype => 'record', - proargtypes => '', proallargtypes => '{text,text,timestamptz,_regtype,bool}', - proargmodes => '{o,o,o,o,o}', - proargnames => '{name,statement,prepare_time,parameter_types,from_sql}', + proargtypes => '', proallargtypes => '{text,text,timestamptz,_regtype,bool,int8,int8,int8,float8,float8,text}', + proargmodes => '{o,o,o,o,o,o,o,o,o,o,o}', + proargnames => '{name,statement,prepare_time,parameter_types,from_sql,calls,custom_calls,plan_generation,generic_cost,custom_cost,last_plan}', prosrc => 'pg_prepared_statement' }, { oid => '2511', descr => 'get the open cursors for this session', proname => 'pg_cursor', prorows => '1000', proretset => 't', diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h index 522020d..32c86ec 100644 --- a/src/include/utils/plancache.h +++ b/src/include/utils/plancache.h @@ -34,6 +34,13 @@ typedef enum PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN } PlanCacheMode; +typedef enum +{ + PLAN_CACHE_TYPE_NONE, + PLAN_CACHE_TYPE_CUSTOM, + PLAN_CACHE_TYPE_GENERIC +} PlanCacheType; + /* GUC parameter */ extern int plan_cache_mode; @@ -124,13 +131,15 @@ typedef struct CachedPlanSource bool is_complete; /* has CompleteCachedPlan been done? */ bool is_saved; /* has CachedPlanSource been "saved"? */ bool is_valid; /* is the query_list currently valid? */ - int generation; /* increments each time we create a plan */ + int64 generation; /* increments each time we create a plan */ /* If CachedPlanSource has been saved, it is a member of a global list */ dlist_node node; /* list link, if is_saved */ /* State kept to help decide whether to use custom or generic plans: */ double generic_cost; /* cost of generic plan, or -1 if not known */ double total_custom_cost; /* total cost of custom plans so far */ - int num_custom_plans; /* number of plans included in total */ + int64 num_custom_plans; /* # of custom plans included in total */ + int64 num_total_calls;/* total number of execution */ + PlanCacheType last_plan_type; /* type of last plan */ } CachedPlanSource; /* diff --git a/src/test/regress/expected/prepare.out b/src/test/regress/expected/prepare.out index 3306c69..e92ce6b 100644 --- a/src/test/regress/expected/prepare.out +++ b/src/test/regress/expected/prepare.out @@ -64,6 +64,49 @@ EXECUTE q2('postgres'); postgres | f | t (1 row) +EXECUTE q2('postgres'); + datname | datistemplate | datallowconn +----------+---------------+-------------- + postgres | f | t +(1 row) + +EXECUTE q2('postgres'); + datname | datistemplate | datallowconn +----------+---------------+-------------- + postgres | f | t +(1 row) + +EXECUTE q2('postgres'); + datname | datistemplate | datallowconn +----------+---------------+-------------- + postgres | f | t +(1 row) + +EXECUTE q2('postgres'); + datname | datistemplate | datallowconn +----------+---------------+-------------- + postgres | f | t +(1 row) + +EXECUTE q2('postgres'); + datname | datistemplate | datallowconn +----------+---------------+-------------- + postgres | f | t +(1 row) + +EXECUTE q2('postgres'); + datname | datistemplate | datallowconn +----------+---------------+-------------- + postgres | f | t +(1 row) + +-- the view should return the correct counts +SELECT name, calls, custom_calls, plan_generation, last_plan FROM pg_prepared_statements; + name | calls | custom_calls | plan_generation | last_plan +------+-------+--------------+-----------------+----------- + q2 | 7 | 5 | 6 | generic +(1 row) + PREPARE q3(text, int, float, boolean, smallint) AS SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR ten = $3::bigint OR true = $4 OR odd = $5::int) diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 8876025..adedd01 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1428,8 +1428,14 @@ pg_prepared_statements| SELECT p.name, p.statement, p.prepare_time, p.parameter_types, - p.from_sql - FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql); + p.from_sql, + p.calls, + p.custom_calls, + p.plan_generation, + p.generic_cost, + p.custom_cost, + p.last_plan + FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql, calls, custom_calls, plan_generation, generic_cost, custom_cost, last_plan); pg_prepared_xacts| SELECT p.transaction, p.gid, p.prepared, diff --git a/src/test/regress/sql/prepare.sql b/src/test/regress/sql/prepare.sql index 985d0f0..c886558 100644 --- a/src/test/regress/sql/prepare.sql +++ b/src/test/regress/sql/prepare.sql @@ -35,6 +35,15 @@ PREPARE q2(text) AS FROM pg_database WHERE datname = $1; EXECUTE q2('postgres'); +EXECUTE q2('postgres'); +EXECUTE q2('postgres'); +EXECUTE q2('postgres'); +EXECUTE q2('postgres'); +EXECUTE q2('postgres'); +EXECUTE q2('postgres'); + +-- the view should return the correct counts +SELECT name, calls, custom_calls, plan_generation, last_plan FROM pg_prepared_statements; PREPARE q3(text, int, float, boolean, smallint) AS SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR