Skip to content

Commit cff440d

Browse files
committed
pg_stat_statements: Widen query IDs from 32 bits to 64 bits.
This takes advantage of the infrastructure introduced by commit 81c5e46 to greatly reduce the likelihood that two different queries will end up with the same query ID. It's still possible, of course, but whereas before it the chances of a collision reached 25% around 50,000 queries, it will now take more than 3 billion queries. Backward incompatibility: Because the type exposed at the SQL level is int8, users may now see negative query IDs in the pg_stat_statements view (and also, query IDs more than 4 billion, which was the old limit). Patch by me, reviewed by Michael Paquier and Peter Geoghegan. Discussion: http://postgr.es/m/CA+TgmobG_Kp4cBKFmsznUAaM1GWW6hhRNiZC0KjRMOOeYnz5Yw@mail.gmail.com
1 parent f2dec34 commit cff440d

File tree

7 files changed

+42
-60
lines changed

7 files changed

+42
-60
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* as the collations of Vars and, most notably, the values of constants.
2222
*
2323
* This jumble is acquired at the end of parse analysis of each query, and
24-
* a 32-bit hash of it is stored into the query's Query.queryId field.
24+
* a 64-bit hash of it is stored into the query's Query.queryId field.
2525
* The server then copies this value around, making it available in plan
2626
* tree(s) generated from the query. The executor can then use this value
2727
* to blame query costs on the proper queryId.
@@ -95,7 +95,7 @@ PG_MODULE_MAGIC;
9595
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
9696

9797
/* Magic number identifying the stats file format */
98-
static const uint32 PGSS_FILE_HEADER = 0x20140125;
98+
static const uint32 PGSS_FILE_HEADER = 0x20171004;
9999

100100
/* PostgreSQL major version number, changes in which invalidate all entries */
101101
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -130,7 +130,7 @@ typedef struct pgssHashKey
130130
{
131131
Oid userid; /* user OID */
132132
Oid dbid; /* database OID */
133-
uint32 queryid; /* query identifier */
133+
uint64 queryid; /* query identifier */
134134
} pgssHashKey;
135135

136136
/*
@@ -301,10 +301,8 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
301301
ProcessUtilityContext context, ParamListInfo params,
302302
QueryEnvironment *queryEnv,
303303
DestReceiver *dest, char *completionTag);
304-
static uint32 pgss_hash_fn(const void *key, Size keysize);
305-
static int pgss_match_fn(const void *key1, const void *key2, Size keysize);
306-
static uint32 pgss_hash_string(const char *str, int len);
307-
static void pgss_store(const char *query, uint32 queryId,
304+
static uint64 pgss_hash_string(const char *str, int len);
305+
static void pgss_store(const char *query, uint64 queryId,
308306
int query_location, int query_len,
309307
double total_time, uint64 rows,
310308
const BufferUsage *bufusage,
@@ -500,12 +498,10 @@ pgss_shmem_startup(void)
500498
memset(&info, 0, sizeof(info));
501499
info.keysize = sizeof(pgssHashKey);
502500
info.entrysize = sizeof(pgssEntry);
503-
info.hash = pgss_hash_fn;
504-
info.match = pgss_match_fn;
505501
pgss_hash = ShmemInitHash("pg_stat_statements hash",
506502
pgss_max, pgss_max,
507503
&info,
508-
HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
504+
HASH_ELEM | HASH_BLOBS);
509505

510506
LWLockRelease(AddinShmemInitLock);
511507

@@ -781,7 +777,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
781777
prev_post_parse_analyze_hook(pstate, query);
782778

783779
/* Assert we didn't do this already */
784-
Assert(query->queryId == 0);
780+
Assert(query->queryId == UINT64CONST(0));
785781

786782
/* Safety check... */
787783
if (!pgss || !pgss_hash)
@@ -797,7 +793,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
797793
*/
798794
if (query->utilityStmt)
799795
{
800-
query->queryId = 0;
796+
query->queryId = UINT64CONST(0);
801797
return;
802798
}
803799

@@ -812,14 +808,15 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
812808

813809
/* Compute query ID and mark the Query node with it */
814810
JumbleQuery(&jstate, query);
815-
query->queryId = hash_any(jstate.jumble, jstate.jumble_len);
811+
query->queryId =
812+
DatumGetUInt64(hash_any_extended(jstate.jumble, jstate.jumble_len, 0));
816813

817814
/*
818815
* If we are unlucky enough to get a hash of zero, use 1 instead, to
819816
* prevent confusion with the utility-statement case.
820817
*/
821-
if (query->queryId == 0)
822-
query->queryId = 1;
818+
if (query->queryId == UINT64CONST(0))
819+
query->queryId = UINT64CONST(1);
823820

824821
/*
825822
* If we were able to identify any ignorable constants, we immediately
@@ -855,7 +852,7 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
855852
* counting of optimizable statements that are directly contained in
856853
* utility statements.
857854
*/
858-
if (pgss_enabled() && queryDesc->plannedstmt->queryId != 0)
855+
if (pgss_enabled() && queryDesc->plannedstmt->queryId != UINT64CONST(0))
859856
{
860857
/*
861858
* Set up to track total elapsed time in ExecutorRun. Make sure the
@@ -926,9 +923,9 @@ pgss_ExecutorFinish(QueryDesc *queryDesc)
926923
static void
927924
pgss_ExecutorEnd(QueryDesc *queryDesc)
928925
{
929-
uint32 queryId = queryDesc->plannedstmt->queryId;
926+
uint64 queryId = queryDesc->plannedstmt->queryId;
930927

931-
if (queryId != 0 && queryDesc->totaltime && pgss_enabled())
928+
if (queryId != UINT64CONST(0) && queryDesc->totaltime && pgss_enabled())
932929
{
933930
/*
934931
* Make sure stats accumulation is done. (Note: it's okay if several
@@ -1069,45 +1066,16 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
10691066
}
10701067
}
10711068

1072-
/*
1073-
* Calculate hash value for a key
1074-
*/
1075-
static uint32
1076-
pgss_hash_fn(const void *key, Size keysize)
1077-
{
1078-
const pgssHashKey *k = (const pgssHashKey *) key;
1079-
1080-
return hash_uint32((uint32) k->userid) ^
1081-
hash_uint32((uint32) k->dbid) ^
1082-
hash_uint32((uint32) k->queryid);
1083-
}
1084-
1085-
/*
1086-
* Compare two keys - zero means match
1087-
*/
1088-
static int
1089-
pgss_match_fn(const void *key1, const void *key2, Size keysize)
1090-
{
1091-
const pgssHashKey *k1 = (const pgssHashKey *) key1;
1092-
const pgssHashKey *k2 = (const pgssHashKey *) key2;
1093-
1094-
if (k1->userid == k2->userid &&
1095-
k1->dbid == k2->dbid &&
1096-
k1->queryid == k2->queryid)
1097-
return 0;
1098-
else
1099-
return 1;
1100-
}
1101-
11021069
/*
11031070
* Given an arbitrarily long query string, produce a hash for the purposes of
11041071
* identifying the query, without normalizing constants. Used when hashing
11051072
* utility statements.
11061073
*/
1107-
static uint32
1074+
static uint64
11081075
pgss_hash_string(const char *str, int len)
11091076
{
1110-
return hash_any((const unsigned char *) str, len);
1077+
return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
1078+
len, 0));
11111079
}
11121080

11131081
/*
@@ -1121,7 +1089,7 @@ pgss_hash_string(const char *str, int len)
11211089
* query string. total_time, rows, bufusage are ignored in this case.
11221090
*/
11231091
static void
1124-
pgss_store(const char *query, uint32 queryId,
1092+
pgss_store(const char *query, uint64 queryId,
11251093
int query_location, int query_len,
11261094
double total_time, uint64 rows,
11271095
const BufferUsage *bufusage,
@@ -1173,7 +1141,7 @@ pgss_store(const char *query, uint32 queryId,
11731141
/*
11741142
* For utility statements, we just hash the query string to get an ID.
11751143
*/
1176-
if (queryId == 0)
1144+
if (queryId == UINT64CONST(0))
11771145
queryId = pgss_hash_string(query, query_len);
11781146

11791147
/* Set up key for hashtable search */
@@ -2324,8 +2292,10 @@ AppendJumble(pgssJumbleState *jstate, const unsigned char *item, Size size)
23242292

23252293
if (jumble_len >= JUMBLE_SIZE)
23262294
{
2327-
uint32 start_hash = hash_any(jumble, JUMBLE_SIZE);
2295+
uint64 start_hash;
23282296

2297+
start_hash = DatumGetUInt64(hash_any_extended(jumble,
2298+
JUMBLE_SIZE, 0));
23292299
memcpy(jumble, &start_hash, sizeof(start_hash));
23302300
jumble_len = sizeof(start_hash);
23312301
}

src/backend/executor/execParallel.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
162162
*/
163163
pstmt = makeNode(PlannedStmt);
164164
pstmt->commandType = CMD_SELECT;
165-
pstmt->queryId = 0;
165+
pstmt->queryId = UINT64CONST(0);
166166
pstmt->hasReturning = false;
167167
pstmt->hasModifyingCTE = false;
168168
pstmt->canSetTag = true;

src/backend/nodes/outfuncs.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ static void outChar(StringInfo str, char c);
5454
#define WRITE_UINT_FIELD(fldname) \
5555
appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname)
5656

57+
/* Write an unsigned integer field (anything written with UINT64_FORMAT) */
58+
#define WRITE_UINT64_FIELD(fldname) \
59+
appendStringInfo(str, " :" CppAsString(fldname) " " UINT64_FORMAT, \
60+
node->fldname)
61+
5762
/* Write an OID field (don't hard-wire assumption that OID is same as uint) */
5863
#define WRITE_OID_FIELD(fldname) \
5964
appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname)
@@ -260,7 +265,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
260265
WRITE_NODE_TYPE("PLANNEDSTMT");
261266

262267
WRITE_ENUM_FIELD(commandType, CmdType);
263-
WRITE_UINT_FIELD(queryId);
268+
WRITE_UINT64_FIELD(queryId);
264269
WRITE_BOOL_FIELD(hasReturning);
265270
WRITE_BOOL_FIELD(hasModifyingCTE);
266271
WRITE_BOOL_FIELD(canSetTag);

src/backend/nodes/readfuncs.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "nodes/parsenodes.h"
3434
#include "nodes/plannodes.h"
3535
#include "nodes/readfuncs.h"
36+
#include "utils/builtins.h"
3637

3738

3839
/*
@@ -70,6 +71,12 @@
7071
token = pg_strtok(&length); /* get field value */ \
7172
local_node->fldname = atoui(token)
7273

74+
/* Read an unsigned integer field (anything written using UINT64_FORMAT) */
75+
#define READ_UINT64_FIELD(fldname) \
76+
token = pg_strtok(&length); /* skip :fldname */ \
77+
token = pg_strtok(&length); /* get field value */ \
78+
local_node->fldname = pg_strtouint64(token, NULL, 10)
79+
7380
/* Read an long integer field (anything written as ":fldname %ld") */
7481
#define READ_LONG_FIELD(fldname) \
7582
token = pg_strtok(&length); /* skip :fldname */ \
@@ -231,7 +238,7 @@ _readQuery(void)
231238

232239
READ_ENUM_FIELD(commandType, CmdType);
233240
READ_ENUM_FIELD(querySource, QuerySource);
234-
local_node->queryId = 0; /* not saved in output format */
241+
local_node->queryId = UINT64CONST(0); /* not saved in output format */
235242
READ_BOOL_FIELD(canSetTag);
236243
READ_NODE_FIELD(utilityStmt);
237244
READ_INT_FIELD(resultRelation);
@@ -1456,7 +1463,7 @@ _readPlannedStmt(void)
14561463
READ_LOCALS(PlannedStmt);
14571464

14581465
READ_ENUM_FIELD(commandType, CmdType);
1459-
READ_UINT_FIELD(queryId);
1466+
READ_UINT64_FIELD(queryId);
14601467
READ_BOOL_FIELD(hasReturning);
14611468
READ_BOOL_FIELD(hasModifyingCTE);
14621469
READ_BOOL_FIELD(canSetTag);

src/backend/rewrite/rewriteHandler.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3575,7 +3575,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
35753575
List *
35763576
QueryRewrite(Query *parsetree)
35773577
{
3578-
uint32 input_query_id = parsetree->queryId;
3578+
uint64 input_query_id = parsetree->queryId;
35793579
List *querylist;
35803580
List *results;
35813581
ListCell *l;

src/include/nodes/parsenodes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ typedef struct Query
111111

112112
QuerySource querySource; /* where did I come from? */
113113

114-
uint32 queryId; /* query identifier (can be set by plugins) */
114+
uint64 queryId; /* query identifier (can be set by plugins) */
115115

116116
bool canSetTag; /* do I set the command result tag? */
117117

src/include/nodes/plannodes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ typedef struct PlannedStmt
4444

4545
CmdType commandType; /* select|insert|update|delete|utility */
4646

47-
uint32 queryId; /* query identifier (copied from Query) */
47+
uint64 queryId; /* query identifier (copied from Query) */
4848

4949
bool hasReturning; /* is it insert|update|delete RETURNING? */
5050

0 commit comments

Comments
 (0)