<A HREF="http://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-SHARED-PRELOAD-LIBRARIES">shared_preload_libraries</A> in
<TT CLASS="FILENAME">postgresql.conf</TT>, because it requires
additional shared memory. This means that a server restart is
- required to add or remove the module.
+ required to add or remove the
+ module. <TT CLASS="LITERAL">pg_store_plans</TT> requires the GUC
+ variable <TT CLASS="LITERAL">compute_query_id</TT> to be "on" or
+ "auto". If it is set to
+ "no", <TT CLASS="LITERAL">pg_store_plans</TT> is silently disabled.
</P>
<DIV CLASS="SECT2">
<H2 CLASS="SECT2">1. The <TT CLASS="STRUCTNAME">pg_store_plans</TT>
<TR><TD><TT CLASS="STRUCTFIELD">queryid</TT></TD>
<TD><TT CLASS="TYPE">bigint</TT></TD>
<TD> </TD>
- <TD>Internal hash code, computed from the statement's query string.</TD>
+ <TD>Core-generated query ID. If compute_query_id is set to "no", pg_store_plan is silently disabled. This is usable as the join key
+ with <TT CLASS="LITERAL">pg_stat_statements</TT>.</TD></TD>
</TR>
<TR><TD><TT CLASS="STRUCTFIELD">planid</TT></TD>
<TD><TT CLASS="TYPE">bigint</TT></TD>
<TD> </TD>
- <TD>Internal hash code, computed from the statement's plan
- representation.</TD>
- </TR>
-<TR><TD><TT CLASS="STRUCTFIELD">queryid_stat_statements</TT></TD>
- <TD><TT CLASS="TYPE">bigint</TT></TD>
- <TD> </TD>
- <TD>A copy of pg_stat_statements' query hash code. This is
- available only when pg_stat_statements is installed.</TD>
- </TR>
+ <TD>Plan hash code, computed from the normalized plan representation.
+</TR>
<TR><TD><TT CLASS="STRUCTFIELD">plan</TT></TD>
<TD><TT CLASS="TYPE">text</TT></TD>
<TD> </TD>
are seemingly equivalent except for the values of literal constants
or fluctuating values such like costs or measured time. </P>
-<P> For PostgreSQL 9.4 or later, you can find the corresponding query
+<P> For PostgreSQL 14 or later, you can find the corresponding query
for a <TT CLASS="structname">pg_store_plans</TT> entry
in <TT CLASS="structname">pg_stat_statements</TT> by joining using
-<TT CLASS="structname">queryid_stat_statements</TT>. Otherwise it is
-identified by using <TT CLASS="VARNAME">queryid</TT>
-and <CODE CLASS="FUNCTION">pg_store_plans_hash_query</CODE >, like
-following.
+<TT CLASS="structname">queryid</TT>, like the following.
</P>
<P>
-<PRE CLASS="SCREEN">SELECT s.query, p.plan FROM pg_store_plans p JOIN pg_stat_statements s ON (pg_store_plans_hash_query(s.query)) = p.queryid;</PRE>
+<PRE CLASS="SCREEN">SELECT s.query, p.plan FROM pg_store_plans p JOIN pg_stat_statements s USING (queryid);</PRE>
</P>
-<P> However plan id is calculated ignoring fluctuating values, the
-values for most recent execution are still displayed
-in <TT CLASS="STRUCTNAME">pg_store_plans.plan</TT>.
+<P> Plan ID is calculated excluding fluctuating properties of plans. On the other hand, the <TT CLASS="STRUCTNAME">pg_store_plans.plan</TT> view keeps showing the most recent values for those fluctuating properties.
</P>
-<P> In some cases, <TT CLASS="STRUCTNAME">pg_stat_statements</TT>
- merges semantically equivalent queries which are considered
- different by
-<TT CLASS="STRUCTNAME">pg_stat_statements</TT>. In the cases
-correspondent in <TT CLASS="STRUCTNAME">pg_stat_statements</TT> might
-not be found, but there is a small chance that this happenes. In
-contrast, there also is a small chance that some queries might be
-regarded as equivalent and merged into one entry
-in <TT CLASS="STRUCTNAME">pg_store_plans</TT> but differentiated
-in <TT CLASS="STRUCTNAME">pg_stat_statements</TT> mainly for utility
-statements.
-</P>
-
<P><TT CLASS="STRUCTNAME">pg_store_plans</TT>
and <TT CLASS="STRUCTNAME">pg_stat_statements</TT> maintain thier
entries individually so there is certain unavoidable chance
especially for entries with low execution frequency that no
correspondent is found.
</P>
-<P><TT CLASS="STRUCTFIELD">queryid_stat_statements</TT> has the same
- restriction to <TT CLASS="STRUCTNAME">pg_stat_statements</TT> in
- terms of stability. Although <TT CLASS="STRUCTFIELD">queryid</TT>
- and <TT CLASS="STRUCTFIELD">planid</TT>
- in <TT CLASS="STRUCTNAME">pg_store_plans</TT> doesn't have such a
- restriction, assuming long-term stability is also discouraged.
- </P>
</DIV>
<DIV CLASS="SECT2">
<H2 CLASS="SECT2">
bench=# SELECT pg_store_plans_reset();
$ pgbench -i bench
-$ pgbench -c10 -t3000 bench
+$ pgbench -c10 -t1000 bench
bench=# \x
bench=# SELECT s.query, p.plan,
p.calls as "plan calls", s.calls as "stmt calls",
p.total_time / p.calls as "time/call", p.first_call, p.last_call
FROM pg_stat_statements s
- JOIN pg_store_plans p ON
- (p.queryid = pg_store_plans_hash_query(s.query) and p.calls < s.calls)
+ JOIN pg_store_plans p USING (queryid) WHERE p.calls < s.calls
ORDER BY query ASC, "time/call" DESC;
--[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------------
-query | UPDATE pgbench_branches SET bbalance = bbalance + ? WHERE bid = ?;
-plan | Update on pgbench_branches (cost=0.00..8.01 rows=1 width=370) (actual rows=0 loops=1)
- | -> Seq Scan on pgbench_branches (cost=0.00..8.01 rows=1 width=370) (actual rows=1 loops=1)
- | Filter: (bid = 1)
-plan calls | 15583
-stmt calls | 30000
-time/call | 40.096513957518
-first_call | 2014-04-25 14:29:17.163924+09
-last_call | 2014-04-25 14:31:29.421635+09
--[ RECORD 2 ]----------------------------------------------------------------------------------------------------------------------------
-query | UPDATE pgbench_branches SET bbalance = bbalance + ? WHERE bid = ?;
-plan | Update on pgbench_branches (cost=0.12..8.14 rows=1 width=370) (actual rows=0 loops=1)
- | -> Index Scan using pgbench_branches_pkey on pgbench_branches (cost=0.12..8.14 rows=1 width=370) (actual rows=1 loops=1)
- | Index Cond: (bid = 1)
-plan calls | 14417
-stmt calls | 30000
-time/call | 39.1920771311645
-first_call | 2014-04-25 14:31:29.288913+09
-last_call | 2014-04-25 14:33:31.287061+09
--[ RECORD 3 ]----------------------------------------------------------------------------------------------------------------------------
-query | UPDATE pgbench_tellers SET tbalance = tbalance + ? WHERE tid = ?;
-plan | Update on pgbench_tellers (cost=0.14..8.16 rows=1 width=358) (actual rows=0 loops=1)
- | -> Index Scan using pgbench_tellers_pkey on pgbench_tellers (cost=0.14..8.16 rows=1 width=358) (actual rows=1 loops=1)
- | Index Cond: (tid = 7)
-plan calls | 4
-stmt calls | 30000
-time/call | 87.0435
-first_call | 2014-04-25 14:30:37.850293+09
-last_call | 2014-04-25 14:32:38.083977+09
--[ RECORD 4 ]----------------------------------------------------------------------------------------------------------------------------
-query | UPDATE pgbench_tellers SET tbalance = tbalance + ? WHERE tid = ?;
-plan | Update on pgbench_tellers (cost=4.14..8.16 rows=1 width=358) (actual rows=0 loops=1)
- | -> Bitmap Heap Scan on pgbench_tellers (cost=4.14..8.16 rows=1 width=358) (actual rows=1 loops=1)
- | Recheck Cond: (tid = 10)
- | -> Bitmap Index Scan using pgbench_tellers_pkey (cost=0.00..4.14 rows=1 width=0) (actual rows=1 loops=1)
- | Index Cond: (tid = 10)
-plan calls | 29996
-stmt calls | 30000
-time/call | 33.6455953793834
-first_call | 2014-04-25 14:29:17.162871+09
-last_call | 2014-04-25 14:33:31.28646+09</PRE>
+-[ RECORD 1 ]-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
+plan | Update on pgbench_tellers (cost=0.00..7.88 rows=0 width=0) +
+ | -> Seq Scan on pgbench_tellers (cost=0.00..7.88 rows=1 width=10) +
+ | Filter: (tid = 1)
+plan calls | 396
+stmt calls | 10000
+time/call | 16.15434492676767
+first_call | 2021-11-25 15:11:38.258838+09
+last_call | 2021-11-25 15:11:40.170291+09
+-[ RECORD 2 ]-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
+plan | Update on pgbench_tellers (cost=0.14..8.15 rows=0 width=0) +
+ | -> Index Scan using pgbench_tellers_pkey on pgbench_tellers (cost=0.14..8.15 rows=1 width=10) +
+ | Index Cond: (tid = 8) +
+plan calls | 9604
+stmt calls | 10000
+time/call | 10.287281695439345
+first_call | 2021-11-25 15:11:40.161556+09
+last_call | 2021-11-25 15:12:09.957773+09
+-[ RECORD 3 ]-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+query | select s.query, p.plan, p.calls as "plan calls", s.calls as "stmt calls", p.total_time / p.calls as "time/call", p.first_call, p.last_call from pg_stat_statements s join pg_store_plans p using (queryid) where p.calls < s.calls order by query asc, "time/call" desc
+plan | Sort (cost=309.71..313.88 rows=1667 width=104) +
+ | Sort Key: pg_stat_statements.query, ((pg_store_plans.total_time / (pg_store_plans.calls)::double precision)) DESC +
+ | -> Merge Join (cost=119.66..220.50 rows=1667 width=104) +
+ | Merge Cond: (pg_stat_statements.queryid = pg_store_plans.queryid) +
+ | Join Filter: (pg_store_plans.calls < pg_stat_statements.calls) +
+ | -> Sort (cost=59.83..62.33 rows=1000 width=48) +
+ | Sort Key: pg_stat_statements.queryid +
+ | -> Function Scan on pg_stat_statements (cost=0.00..10.00 rows=1000 width=48) +
+ | -> Sort (cost=59.83..62.33 rows=1000 width=72) +
+ | Sort Key: pg_store_plans.queryid +
+ | -> Function Scan on pg_store_plans (cost=0.00..10.00 rows=1000 width=72) +
+plan calls | 3
+stmt calls | 4
+time/call | 16.387161
+first_call | 2021-11-25 15:20:57.978082+09
+last_call | 2021-11-25 15:23:48.631993+09
+-[ RECORD 4 ]-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+query | select s.query, p.plan, p.calls as "plan calls", s.calls as "stmt calls", p.total_time / p.calls as "time/call", p.first_call, p.last_call from pg_stat_statements s join pg_store_plans p using (queryid) where p.calls < s.calls order by query asc, "time/call" desc
+plan | Sort (cost=309.71..313.88 rows=1667 width=104) +
+ | Sort Key: pg_stat_statements.query, ((pg_store_plans.total_time / (pg_store_plans.calls)::double precision)) DESC +
+ | Sort Method: quicksort Memory: 26kB +
+ | -> Merge Join (cost=119.66..220.50 rows=1667 width=104) +
+ | Merge Cond: (pg_stat_statements.queryid = pg_store_plans.queryid) +
+ | Join Filter: (pg_store_plans.calls < pg_stat_statements.calls) +
+ | Rows Removed by Join Filter: 7 +
+ | -> Sort (cost=59.83..62.33 rows=1000 width=48) +
+ | Sort Key: pg_stat_statements.queryid +
+ | Sort Method: quicksort Memory: 27kB +
+ | -> Function Scan on pg_stat_statements (cost=0.00..10.00 rows=1000 width=48) +
+ | -> Sort (cost=59.83..62.33 rows=1000 width=72) +
+ | Sort Key: pg_store_plans.queryid +
+ | Sort Method: quicksort Memory: 30kB +
+ | -> Function Scan on pg_store_plans (cost=0.00..10.00 rows=1000 width=72) +
+plan calls | 1
+stmt calls | 4
+time/call | 4.46928
+first_call | 2021-11-25 15:12:27.142535+09
+last_call | 2021-11-25 15:12:27.142536+09
+
+postgres=#
+</PRE>
</DIV>
</DIV>
<HR>
FOR r IN SELECT s.query as q, p.plan as p, p.calls as c, p.rows r
FROM pg_stat_statements s
- JOIN pg_store_plans p ON (s.queryid = p.queryid_stat_statements)
+ JOIN pg_store_plans p USING (queryid)
WHERE s.query = 'SELECT count(*) FROM (SELECT * FROM t1) AS x'
ORDER BY p.calls
LOOP
FOR r IN SELECT s.query as q, p.plan as p, p.calls as c, p.rows r
FROM pg_stat_statements s
- JOIN pg_store_plans p ON (s.queryid = p.queryid_stat_statements)
+ JOIN pg_store_plans p USING (queryid)
WHERE s.query = 'SELECT count(*) FROM (SELECT * FROM t1) AS x'
ORDER BY p.calls
LOOP
OUT dbid oid,
OUT queryid int8,
OUT planid int8,
- OUT queryid_stat_statements int8,
OUT plan text,
OUT calls int8,
OUT total_time float8,
OUT last_call timestamptz
)
RETURNS SETOF record
-AS 'MODULE_PATHNAME'
+AS 'MODULE_PATHNAME', 'pg_store_plans_1_6'
LANGUAGE C;
-- Register a view on the function for ease of use.
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#if PG_VERSION_NUM >= 140000
+#include "utils/queryjumble.h"
+#endif
#include "utils/timestamp.h"
#include "pgsp_json.h"
#define STICKY_DECREASE_FACTOR (0.50) /* factor for sticky entries */
#define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */
+/* In PostgreSQL 11, queryid becomes a uint64 internally.
+ */
+#if PG_VERSION_NUM >= 110000
+typedef uint64 queryid_t;
+#define PGSP_NO_QUERYID UINT64CONST(0)
+#else
+typedef uint32 queryid_t;
+#define PGSP_NO_QUERYID 0
+#endif
+
+/*
+ * Extension version number, for supporting older extension versions' objects
+ */
+typedef enum pgspVersion
+{
+ PGSP_V1_5 = 0,
+ PGSP_V1_6
+} pgspVersion;
+
/*
* Hashtable key that defines the identity of a hashtable entry. We separate
* queries by user and by database even if they are otherwise identical.
{
Oid userid; /* user OID */
Oid dbid; /* database OID */
- uint32 queryid; /* internal query identifier */
+ queryid_t queryid; /* query identifier */
uint32 planid; /* plan identifier */
} pgspHashKey;
} Counters;
/*
- * The type of queryId has been widen as of PG11. Define substitute type rather
- * than put #if here and there.
- */
-typedef uint64 queryid_t;
-
-/*
* Statistics per plan
*
* NB: see the file read/write code before changing field order here.
typedef struct pgspEntry
{
pgspHashKey key; /* hash key of entry - MUST BE FIRST */
- queryid_t queryid; /* query identifier from stat_statements*/
Counters counters; /* the statistics for this query */
int plan_len; /* # of valid bytes in query string */
int encoding; /* query encoding */
static int plan_format; /* Plan representation style in
* pg_store_plans.plan */
-#define pgsp_enabled() \
+#if PG_VERSION_NUM >= 140000
+/*
+ * For pg14 and later, we rely on core queryid calculation. If
+ * it's not available it means that the admin explicitly refused to
+ * compute it, for performance reason or other. In that case, we
+ * will also consider that this extension is disabled.
+ */
+#define pgsp_enabled(q) \
+ ((track_level == TRACK_LEVEL_ALL || \
+ (track_level == TRACK_LEVEL_TOP && nested_level == 0)) && \
+ (q != PGSP_NO_QUERYID))
+#else
+#define pgsp_enabled(q) \
(track_level == TRACK_LEVEL_ALL || \
(track_level == TRACK_LEVEL_TOP && nested_level == 0))
+#endif
/*---- Function declarations ----*/
PG_FUNCTION_INFO_V1(pg_store_plans_reset);
PG_FUNCTION_INFO_V1(pg_store_plans_hash_query);
PG_FUNCTION_INFO_V1(pg_store_plans);
+PG_FUNCTION_INFO_V1(pg_store_plans_1_6);
PG_FUNCTION_INFO_V1(pg_store_plans_shorten);
PG_FUNCTION_INFO_V1(pg_store_plans_normalize);
PG_FUNCTION_INFO_V1(pg_store_plans_jsonplan);
QueryEnvironment *queryEnv,
DestReceiver *dest, COMPTAG_TYPE *completionTag);
static uint32 hash_query(const char* query);
-static void pgsp_store(char *plan, uint32 queryId, queryid_t queryId_pgss,
+static void pgsp_store(char *plan, queryid_t queryId,
double total_time, uint64 rows,
const BufferUsage *bufusage);
+static void pg_store_plans_internal(FunctionCallInfo fcinfo,
+ pgspVersion api_version);
static Size shared_mem_size(void);
static pgspEntry *entry_alloc(pgspHashKey *key, const char *query,
int plan_len, bool sticky);
if (!process_shared_preload_libraries_in_progress)
return;
+#if PG_VERSION_NUM >= 140000
+ /*
+ * Inform the postmaster that we want to enable query_id calculation if
+ * compute_query_id is set to auto.
+ */
+ EnableQueryId();
+#endif
+
/*
* Define (or redefine) custom GUC variables.
*/
(log_timing ? 0: INSTRUMENT_ROWS)|
(log_buffers ? INSTRUMENT_BUFFERS : 0);
}
+
if (prev_ExecutorStart)
prev_ExecutorStart(queryDesc, eflags);
else
* Set up to track total elapsed time in ExecutorRun. Allocate in per-query
* context so as to be free at ExecutorEnd.
*/
- if (queryDesc->totaltime == NULL && pgsp_enabled())
+ if (queryDesc->totaltime == NULL &&
+ pgsp_enabled(queryDesc->plannedstmt->queryId))
{
MemoryContext oldcxt;
*/
InstrEndLoop(queryDesc->totaltime);
- if (pgsp_enabled() &&
+ if (pgsp_enabled(queryDesc->plannedstmt->queryId) &&
+ queryDesc->totaltime->total &&
queryDesc->totaltime->total >=
(double)min_duration / 1000.0)
{
- ExplainState *es = NewExplainState();
- StringInfo es_str = es->str;
+ queryid_t queryid;
+ ExplainState *es;
+ StringInfo es_str;
+
+ es = NewExplainState();
+ es_str = es->str;
es->analyze = queryDesc->instrument_options;
es->verbose = log_verbose;
es_str->data[0] = '{';
es_str->data[es_str->len - 1] = '}';
+ queryid = queryDesc->plannedstmt->queryId;
+#if PG_VERSION_NUM < 140000
+ /*
+ * For versions before pg14, a queryid is only available if
+ * pg_stat_statements extension (or similar) if configured. We
+ * don't want a hard requirement for such an extension so fallback
+ * to an internal queryid calculation in some case.
+ * For pg14 and above, core postgres can compute a queryid so we
+ * will rely on it.
+ */
+ if (queryid == PGSP_NO_QUERYID)
+ queryid = (queryid_t) hash_query(queryDesc->sourceText);
+#else
+ Assert(queryid != PGSP_NO_QUERYID);
+#endif
+
pgsp_store(es_str->data,
- hash_query(queryDesc->sourceText),
- queryDesc->plannedstmt->queryId,
+ queryid,
queryDesc->totaltime->total * 1000.0, /* convert to msec */
queryDesc->estate->es_processed,
&queryDesc->totaltime->bufusage);
queryid = hash_any((const unsigned char*)normquery, strlen(normquery));
pfree(normquery);
+ /* If we are unlucky enough to get a hash of zero, use 1 instead */
+ if (queryid == 0)
+ queryid = 1;
+
return queryid;
}
* value of the given plan, which is calculated in ths function.
*/
static void
-pgsp_store(char *plan, uint32 queryId, queryid_t queryId_pgss,
+pgsp_store(char *plan, queryid_t queryId,
double total_time, uint64 rows,
const BufferUsage *bufusage)
{
char *shorten_plan = NULL;
volatile pgspEntry *e;
- Assert(plan != NULL);
+ Assert(plan != NULL && queryId != PGSP_NO_QUERYID);
/* Safety check... */
if (!shared_state || !hash_table)
e = (volatile pgspEntry *) entry;
SpinLockAcquire(&e->mutex);
- e->queryid = queryId_pgss;
-
/* "Unstick" entry if it was previously sticky */
if (e->counters.calls == 0)
{
PG_RETURN_VOID();
}
-#define PG_STORE_PLANS_COLS 27
+/* Number of output arguments (columns) for various API versions */
+#define PG_STORE_PLANS_COLS_V1_5 27
+#define PG_STORE_PLANS_COLS_V1_6 26
+#define PG_STORE_PLANS_COLS 27 /* maximum of above */
/*
* Retrieve statement statistics.
*/
Datum
+pg_store_plans_1_6(PG_FUNCTION_ARGS)
+{
+ pg_store_plans_internal(fcinfo, PGSP_V1_6);
+
+ return (Datum) 0;
+}
+
+Datum
pg_store_plans(PG_FUNCTION_ARGS)
{
+ pg_store_plans_internal(fcinfo, PGSP_V1_5);
+
+ return (Datum) 0;
+}
+
+static void
+pg_store_plans_internal(FunctionCallInfo fcinfo,
+ pgspVersion api_version)
+{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
bool nulls[PG_STORE_PLANS_COLS];
int i = 0;
int64 queryid = entry->key.queryid;
- int64 queryid_stmt = entry->queryid;
int64 planid = entry->key.planid;
Counters tmp;
double stddev;
{
values[i++] = Int64GetDatumFast(queryid);
values[i++] = Int64GetDatumFast(planid);
- values[i++] = Int64GetDatumFast(queryid_stmt);
+ if (api_version == PGSP_V1_5)
+ values[i++] = ObjectIdGetDatum(queryid);
}
else
{
values[i++] = Int64GetDatumFast(0);
values[i++] = Int64GetDatumFast(0);
- values[i++] = Int64GetDatumFast(0);
+ if (api_version == PGSP_V1_5)
+ values[i++] = Int64GetDatumFast(0);
}
-
if (is_allowed_role || entry->key.userid == userid)
{
char *pstr = entry->plan;
values[i++] = Float8GetDatumFast(tmp.blk_write_time);
values[i++] = TimestampTzGetDatum(tmp.first_call);
values[i++] = TimestampTzGetDatum(tmp.last_call);
- Assert(i == PG_STORE_PLANS_COLS);
+
+ Assert(i == (api_version == PGSP_V1_5 ? PG_STORE_PLANS_COLS_V1_5 :
+ api_version == PGSP_V1_6 ? PG_STORE_PLANS_COLS_V1_6 :
+ -1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
-
- return (Datum) 0;
}
/*
FOR r IN SELECT s.query as q, p.plan as p, p.calls as c, p.rows r
FROM pg_stat_statements s
- JOIN pg_store_plans p ON (s.queryid = p.queryid_stat_statements)
+ JOIN pg_store_plans p USING (queryid)
WHERE s.query = 'SELECT count(*) FROM (SELECT * FROM t1) AS x'
ORDER BY p.calls
LOOP