mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Track identical top vs nested queries independently in pg_stat_statements
Changing pg_stat_statements.track between 'all' and 'top' would control if pg_stat_statements tracked just top level statements or also statements inside functions, but when tracking all it would not differentiate between the two. Being table to differentiate this is useful both to track where the actual query is coming from, and to see if there are differences in executions between the two. To do this, add a boolean to the hash key indicating if the statement was top level or not. Experience from the pg_stat_kcache module shows that in at least some "reasonable worloads" only <5% of the queries show up both top level and nested. Based on this, admittedly small, dataset, this patch does not try to de-duplicate those query *texts*, and will just store one copy for the top level and one for the nested. Author: Julien Rohaud Reviewed-By: Magnus Hagander, Masahiro Ikeda Discussion: https://postgr.es/m/20201202040516.GA43757@nol
This commit is contained in:
@ -87,7 +87,7 @@ PG_MODULE_MAGIC;
|
||||
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
|
||||
|
||||
/* Magic number identifying the stats file format */
|
||||
static const uint32 PGSS_FILE_HEADER = 0x20201218;
|
||||
static const uint32 PGSS_FILE_HEADER = 0x20201227;
|
||||
|
||||
/* PostgreSQL major version number, changes in which invalidate all entries */
|
||||
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
|
||||
@ -119,7 +119,8 @@ typedef enum pgssVersion
|
||||
PGSS_V1_1,
|
||||
PGSS_V1_2,
|
||||
PGSS_V1_3,
|
||||
PGSS_V1_8
|
||||
PGSS_V1_8,
|
||||
PGSS_V1_10
|
||||
} pgssVersion;
|
||||
|
||||
typedef enum pgssStoreKind
|
||||
@ -141,16 +142,17 @@ typedef enum pgssStoreKind
|
||||
* Hashtable key that defines the identity of a hashtable entry. We separate
|
||||
* queries by user and by database even if they are otherwise identical.
|
||||
*
|
||||
* Right now, this structure contains no padding. If you add any, make sure
|
||||
* to teach pgss_store() to zero the padding bytes. Otherwise, things will
|
||||
* break, because pgss_hash is created using HASH_BLOBS, and thus tag_hash
|
||||
* is used to hash this.
|
||||
* If you add a new key to this struct, make sure to teach pgss_store() to
|
||||
* zero the padding bytes. Otherwise, things will break, because pgss_hash is
|
||||
* created using HASH_BLOBS, and thus tag_hash is used to hash this.
|
||||
|
||||
*/
|
||||
typedef struct pgssHashKey
|
||||
{
|
||||
Oid userid; /* user OID */
|
||||
Oid dbid; /* database OID */
|
||||
uint64 queryid; /* query identifier */
|
||||
bool toplevel; /* query executed at top level */
|
||||
} pgssHashKey;
|
||||
|
||||
/*
|
||||
@ -297,6 +299,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_7);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_statements_1_2);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_statements_1_3);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_statements);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
|
||||
|
||||
@ -1224,9 +1227,14 @@ pgss_store(const char *query, uint64 queryId,
|
||||
query = CleanQuerytext(query, &query_location, &query_len);
|
||||
|
||||
/* Set up key for hashtable search */
|
||||
|
||||
/* memset() is required when pgssHashKey is without padding only */
|
||||
memset(&key, 0, sizeof(pgssHashKey));
|
||||
|
||||
key.userid = GetUserId();
|
||||
key.dbid = MyDatabaseId;
|
||||
key.queryid = queryId;
|
||||
key.toplevel = (exec_nested_level == 0);
|
||||
|
||||
/* Lookup the hash table entry with shared lock. */
|
||||
LWLockAcquire(pgss->lock, LW_SHARED);
|
||||
@ -1406,7 +1414,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
|
||||
#define PG_STAT_STATEMENTS_COLS_V1_2 19
|
||||
#define PG_STAT_STATEMENTS_COLS_V1_3 23
|
||||
#define PG_STAT_STATEMENTS_COLS_V1_8 32
|
||||
#define PG_STAT_STATEMENTS_COLS 32 /* maximum of above */
|
||||
#define PG_STAT_STATEMENTS_COLS_V1_10 33
|
||||
#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
|
||||
|
||||
/*
|
||||
* Retrieve statement statistics.
|
||||
@ -1418,6 +1427,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
|
||||
* expected API version is identified by embedding it in the C name of the
|
||||
* function. Unfortunately we weren't bright enough to do that for 1.1.
|
||||
*/
|
||||
Datum
|
||||
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bool showtext = PG_GETARG_BOOL(0);
|
||||
|
||||
pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
|
||||
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
Datum
|
||||
pg_stat_statements_1_8(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@ -1537,6 +1556,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
|
||||
if (api_version != PGSS_V1_8)
|
||||
elog(ERROR, "incorrect number of output arguments");
|
||||
break;
|
||||
case PG_STAT_STATEMENTS_COLS_V1_10:
|
||||
if (api_version != PGSS_V1_10)
|
||||
elog(ERROR, "incorrect number of output arguments");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "incorrect number of output arguments");
|
||||
}
|
||||
@ -1628,6 +1651,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
|
||||
|
||||
values[i++] = ObjectIdGetDatum(entry->key.userid);
|
||||
values[i++] = ObjectIdGetDatum(entry->key.dbid);
|
||||
if (api_version >= PGSS_V1_10)
|
||||
values[i++] = BoolGetDatum(entry->key.toplevel);
|
||||
|
||||
if (is_allowed_role || entry->key.userid == userid)
|
||||
{
|
||||
@ -1765,6 +1790,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
|
||||
api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
|
||||
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
|
||||
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
|
||||
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
|
||||
-1 /* fail if you forget to update this assert */ ));
|
||||
|
||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||
@ -2437,10 +2463,20 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
|
||||
if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
|
||||
{
|
||||
/* If all the parameters are available, use the fast path. */
|
||||
memset(&key, 0, sizeof(pgssHashKey));
|
||||
key.userid = userid;
|
||||
key.dbid = dbid;
|
||||
key.queryid = queryid;
|
||||
|
||||
/* Remove the key if it exists, starting with the top-level entry */
|
||||
key.toplevel = false;
|
||||
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
|
||||
if (entry) /* found */
|
||||
num_remove++;
|
||||
|
||||
/* Also remove entries for top level statements */
|
||||
key.toplevel = true;
|
||||
|
||||
/* Remove the key if exists */
|
||||
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
|
||||
if (entry) /* found */
|
||||
|
Reference in New Issue
Block a user