PG-286: Several improvements.

This commit introduces serveral improvements:

1. Removal of pgss_store_query and pgss_store_utility functions: To
   store a query, we just use pgss_store(), this makes the code more
   uniform.

2. Always pass the query length to the pgss_store function using parse
   state from PostgreSQL to avoid calculating query length again.

3. Always clean the query (extra spaces, update query location) in
   pgss_store.

4. Normalize queries right before adding them to the query buffer, but
   only if user asked for query normalization.

5. Correctly handle utility queries among different PostgreSQL versions:
   - A word about how utility functions are handled on PG 13 and later
     versions:
      - On PostgreSQL <= 13, we have to compute a query ID, on later
        versions we can call EnableQueryId() to inform Postmaster we
	want to enable query ID computation.

      - On PostgreSQL <= 13, post_parse hook is called after process
        utility hook, on PostgreSQL >= 14, post_parse hook is called
        before process utility functions.

   - Based on that information, on PostgreSQL <= 13 / process utility,
     we pass 0 as queryid to the pgss_store function, then we calculate a
     queryid after cleaning the query (CleanQueryText) using
     pgss_hash_string.

   - On PostgreSQL 14 onward, post_parse() is called before
     pgss_ProcessUtility, we Clear queryId for prepared statements
     related utility, on process utility hook, we save the query ID for
     passing it to the pgss_store function, but mark the query ID with
     zero to avoid instrumenting it again on executor hooks.
pull/184/head
Diego Fronza 2021-12-04 11:05:24 -03:00 committed by Hamid Akhtar
parent 258fa7faa4
commit 4ed0b7cf3e
1 changed files with 300 additions and 252 deletions

View File

@ -136,6 +136,7 @@ DECLARE_HOOK(void pgss_ProcessUtility, PlannedStmt *pstmt, const char *queryStri
ParamListInfo params, QueryEnvironment *queryEnv, ParamListInfo params, QueryEnvironment *queryEnv,
DestReceiver *dest, DestReceiver *dest,
QueryCompletion *qc); QueryCompletion *qc);
static uint64 pgss_hash_string(const char *str, int len);
#else #else
static void BufferUsageAccumDiff(BufferUsage* bufusage, BufferUsage* pgBufferUsage, BufferUsage* bufusage_start); static void BufferUsageAccumDiff(BufferUsage* bufusage, BufferUsage* pgBufferUsage, BufferUsage* bufusage_start);
DECLARE_HOOK(void pgss_ProcessUtility, PlannedStmt *pstmt, const char *queryString, DECLARE_HOOK(void pgss_ProcessUtility, PlannedStmt *pstmt, const char *queryString,
@ -144,20 +145,18 @@ DECLARE_HOOK(void pgss_ProcessUtility, PlannedStmt *pstmt, const char *queryStri
DestReceiver *dest, DestReceiver *dest,
char *completionTag); char *completionTag);
#endif #endif
static uint64 pgss_hash_string(const char *str, int len);
char *unpack_sql_state(int sql_state); char *unpack_sql_state(int sql_state);
static void pgss_store_error(uint64 queryid, const char * query, ErrorData *edata); #define PGSM_HANDLED_UTILITY(n) (!IsA(n, ExecuteStmt) && \
!IsA(n, PrepareStmt) && \
!IsA(n, DeallocateStmt))
static void pgss_store_utility(const char *query, static void pgss_store_error(uint64 queryid, const char *query, ErrorData *edata);
double total_time,
uint64 rows,
BufferUsage *bufusage,
WalUsage *walusage);
static void pgss_store(uint64 queryid, static void pgss_store(uint64 queryid,
const char *query, const char *query,
int query_location,
int query_len,
PlanInfo *plan_info, PlanInfo *plan_info,
CmdType cmd_type, CmdType cmd_type,
SysInfo *sys_info, SysInfo *sys_info,
@ -179,6 +178,12 @@ static void JumbleQuery(JumbleState *jstate, Query *query);
static void JumbleRangeTable(JumbleState *jstate, List *rtable); static void JumbleRangeTable(JumbleState *jstate, List *rtable);
static void JumbleExpr(JumbleState *jstate, Node *node); static void JumbleExpr(JumbleState *jstate, Node *node);
static void RecordConstLocation(JumbleState *jstate, int location); static void RecordConstLocation(JumbleState *jstate, int location);
/*
* Given a possibly multi-statement source string, confine our attention to the
* relevant part of the string.
*/
static const char *
CleanQuerytext(const char *query, int *location, int *len);
#endif #endif
static char *generate_normalized_query(JumbleState *jstate, const char *query, static char *generate_normalized_query(JumbleState *jstate, const char *query,
@ -188,19 +193,6 @@ static int comp_location(const void *a, const void *b);
static uint64 get_next_wbucket(pgssSharedState *pgss); static uint64 get_next_wbucket(pgssSharedState *pgss);
static void
pgss_store_query(uint64 queryid,
const char * query,
CmdType cmd_type,
int query_location,
int query_len,
#if PG_VERSION_NUM > 130000
JumbleState *jstate,
#else
JumbleState *jstate,
#endif
pgssStoreKind kind);
#if PG_VERSION_NUM < 140000 #if PG_VERSION_NUM < 140000
static uint64 get_query_id(JumbleState *jstate, Query *query); static uint64 get_query_id(JumbleState *jstate, Query *query);
#endif #endif
@ -357,8 +349,6 @@ pgss_post_parse_analyze_benchmark(ParseState *pstate, Query *query, JumbleState
static void static void
pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate) pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
{ {
pgssStoreKind kind = PGSS_PARSE;
if (prev_post_parse_analyze_hook) if (prev_post_parse_analyze_hook)
prev_post_parse_analyze_hook(pstate, query, jstate); prev_post_parse_analyze_hook(pstate, query, jstate);
@ -376,6 +366,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
*/ */
if (query->utilityStmt) if (query->utilityStmt)
{ {
if (PGSM_TRACK_UTILITY && !PGSM_HANDLED_UTILITY(query->utilityStmt))
query->queryId = UINT64CONST(0); query->queryId = UINT64CONST(0);
return; return;
} }
@ -387,15 +378,21 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
* constants, the normalized string would be the same as the query text * constants, the normalized string would be the same as the query text
* anyway, so there's no need for an early entry. * anyway, so there's no need for an early entry.
*/ */
if (jstate == NULL || jstate->clocations_count <= 0) if (jstate && jstate->clocations_count > 0)
return; pgss_store(query->queryId, /* query id */
pgss_store_query(query->queryId, /* queryid */
pstate->p_sourcetext, /* query */ pstate->p_sourcetext, /* query */
query->stmt_location, /* query location */
query->stmt_len, /* query length */
NULL, /* PlanInfo */
query->commandType, /* CmdType */ query->commandType, /* CmdType */
query->stmt_location, /* Query Location */ NULL, /* SysInfo */
query->stmt_len, /* Query Len */ NULL, /* ErrorInfo */
0, /* totaltime */
0, /* rows */
NULL, /* bufusage */
NULL, /* walusage */
jstate, /* JumbleState */ jstate, /* JumbleState */
kind); /*pgssStoreKind */ PGSS_PARSE); /* pgssStoreKind */
} }
#else #else
@ -416,7 +413,6 @@ static void
pgss_post_parse_analyze(ParseState *pstate, Query *query) pgss_post_parse_analyze(ParseState *pstate, Query *query)
{ {
JumbleState jstate; JumbleState jstate;
pgssStoreKind kind = PGSS_PARSE;
if (prev_post_parse_analyze_hook) if (prev_post_parse_analyze_hook)
prev_post_parse_analyze_hook(pstate, query); prev_post_parse_analyze_hook(pstate, query);
@ -451,16 +447,21 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
if (query->queryId == UINT64CONST(0)) if (query->queryId == UINT64CONST(0))
query->queryId = UINT64CONST(1); query->queryId = UINT64CONST(1);
if (jstate.clocations_count <= 0) if (jstate.clocations_count > 0)
return; pgss_store(query->queryId, /* query id */
pgss_store_query(query->queryId, /* queryid */
pstate->p_sourcetext, /* query */ pstate->p_sourcetext, /* query */
query->stmt_location, /* query location */
query->stmt_len, /* query length */
NULL, /* PlanInfo */
query->commandType, /* CmdType */ query->commandType, /* CmdType */
query->stmt_location, /* Query Location */ NULL, /* SysInfo */
query->stmt_len, /* Query Len */ NULL, /* ErrorInfo */
0, /* totaltime */
0, /* rows */
NULL, /* bufusage */
NULL, /* walusage */
&jstate, /* JumbleState */ &jstate, /* JumbleState */
kind); /*pgssStoreKind */ PGSS_PARSE); /* pgssStoreKind */
} }
#endif #endif
@ -480,10 +481,8 @@ pgss_ExecutorStart_benchmark(QueryDesc *queryDesc, int eflags)
static void static void
pgss_ExecutorStart(QueryDesc *queryDesc, int eflags) pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
{ {
uint64 queryId = queryDesc->plannedstmt->queryId; if (getrusage(RUSAGE_SELF, &rusage_start) != 0)
pgsm_log_error("pgss_ExecutorStart: failed to execute getrusage");
if(getrusage(RUSAGE_SELF, &rusage_start) != 0)
elog(DEBUG1, "pg_stat_monitor: failed to execute getrusage");
if (prev_ExecutorStart) if (prev_ExecutorStart)
prev_ExecutorStart(queryDesc, eflags); prev_ExecutorStart(queryDesc, eflags);
@ -517,8 +516,10 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
#endif #endif
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
} }
pgss_store(queryId, /* query id */ pgss_store(queryDesc->plannedstmt->queryId, /* query id */
queryDesc->sourceText, /* query text */ queryDesc->sourceText, /* query text */
queryDesc->plannedstmt->stmt_location, /* query location */
queryDesc->plannedstmt->stmt_len, /* query length */
NULL, /* PlanInfo */ NULL, /* PlanInfo */
queryDesc->operation, /* CmdType */ queryDesc->operation, /* CmdType */
NULL, /* SysInfo */ NULL, /* SysInfo */
@ -526,12 +527,8 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
0, /* totaltime */ 0, /* totaltime */
0, /* rows */ 0, /* rows */
NULL, /* bufusage */ NULL, /* bufusage */
#if PG_VERSION_NUM >= 130000
NULL, /* walusage */ NULL, /* walusage */
#else NULL, /* JumbleState */
NULL,
#endif
NULL,
PGSS_EXEC); /* pgssStoreKind */ PGSS_EXEC); /* pgssStoreKind */
} }
} }
@ -659,7 +656,7 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
{ {
MemoryContext mct = MemoryContextSwitchTo(TopMemoryContext); MemoryContext mct = MemoryContextSwitchTo(TopMemoryContext);
plan_info.plan_len = snprintf(plan_info.plan_text, PLAN_TEXT_LEN, "%s", pgss_explain(queryDesc)); plan_info.plan_len = snprintf(plan_info.plan_text, PLAN_TEXT_LEN, "%s", pgss_explain(queryDesc));
plan_info.planid = DatumGetUInt64(hash_any_extended((const unsigned char*)plan_info.plan_text, plan_info.plan_len, 0)); plan_info.planid = DatumGetUInt64(hash_any_extended((const unsigned char *)plan_info.plan_text, plan_info.plan_len, 0));
plan_ptr = &plan_info; plan_ptr = &plan_info;
MemoryContextSwitchTo(mct); MemoryContextSwitchTo(mct);
} }
@ -679,6 +676,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
pgss_store(queryId, /* query id */ pgss_store(queryId, /* query id */
queryDesc->sourceText, /* query text */ queryDesc->sourceText, /* query text */
queryDesc->plannedstmt->stmt_location, /* query location */
queryDesc->plannedstmt->stmt_len, /* query length */
plan_ptr, /* PlanInfo */ plan_ptr, /* PlanInfo */
queryDesc->operation, /* CmdType */ queryDesc->operation, /* CmdType */
&sys_info, /* SysInfo */ &sys_info, /* SysInfo */
@ -831,6 +830,8 @@ pgss_planner_hook(Query *parse, const char *query_string, int cursorOptions, Par
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
pgss_store(parse->queryId, /* query id */ pgss_store(parse->queryId, /* query id */
query_string, /* query */ query_string, /* query */
parse->stmt_location, /* query location */
parse->stmt_len, /* query length */
NULL, /* PlanInfo */ NULL, /* PlanInfo */
parse->commandType, /* CmdType */ parse->commandType, /* CmdType */
NULL, /* SysInfo */ NULL, /* SysInfo */
@ -931,6 +932,23 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
#endif #endif
{ {
Node *parsetree = pstmt->utilityStmt; Node *parsetree = pstmt->utilityStmt;
uint64 queryId = 0;
#if PG_VERSION_NUM >= 140000
queryId = pstmt->queryId;
/*
* Force utility statements to get queryId zero. We do this even in cases
* where the statement contains an optimizable statement for which a
* queryId could be derived (such as EXPLAIN or DECLARE CURSOR). For such
* cases, runtime control will first go through ProcessUtility and then
* the executor, and we don't want the executor hooks to do anything,
* since we are already measuring the statement's costs at the utility
* level.
*/
if (PGSM_TRACK_UTILITY && !IsParallelWorker())
pstmt->queryId = UINT64CONST(0);
#endif
/* /*
* If it's an EXECUTE statement, we don't track it and don't increment the * If it's an EXECUTE statement, we don't track it and don't increment the
@ -946,10 +964,8 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
* *
* Likewise, we don't track execution of DEALLOCATE. * Likewise, we don't track execution of DEALLOCATE.
*/ */
if (PGSM_TRACK_UTILITY && if (PGSM_TRACK_UTILITY && PGSM_HANDLED_UTILITY(parsetree) &&
!IsA(parsetree, ExecuteStmt) && !IsParallelWorker())
!IsA(parsetree, PrepareStmt) &&
!IsA(parsetree, DeallocateStmt) && !IsParallelWorker())
{ {
instr_time start; instr_time start;
instr_time duration; instr_time duration;
@ -1012,7 +1028,16 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
INSTR_TIME_SUBTRACT(duration, start); INSTR_TIME_SUBTRACT(duration, start);
#if PG_VERSION_NUM >= 130000 #if PG_VERSION_NUM >= 130000
#if PG_VERSION_NUM >= 140000
rows = (qc && (qc->commandTag == CMDTAG_COPY ||
qc->commandTag == CMDTAG_FETCH ||
qc->commandTag == CMDTAG_SELECT ||
qc->commandTag == CMDTAG_REFRESH_MATERIALIZED_VIEW))
? qc->nprocessed
: 0;
#else
rows = (qc && qc->commandTag == CMDTAG_COPY) ? qc->nprocessed : 0; rows = (qc && qc->commandTag == CMDTAG_COPY) ? qc->nprocessed : 0;
#endif
/* calc differences of WAL counters. */ /* calc differences of WAL counters. */
memset(&walusage, 0, sizeof(WalUsage)); memset(&walusage, 0, sizeof(WalUsage));
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
@ -1027,11 +1052,21 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
/* calc differences of buffer counters. */ /* calc differences of buffer counters. */
memset(&bufusage, 0, sizeof(BufferUsage)); memset(&bufusage, 0, sizeof(BufferUsage));
BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start); BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
pgss_store_utility(queryString, /* query text */ pgss_store(
INSTR_TIME_GET_MILLISEC(duration), /* totaltime */ queryId, /* query ID */
queryString, /* query text */
pstmt->stmt_location, /* query location */
pstmt->stmt_len, /* query length */
NULL, /* PlanInfo */
0, /* CmdType */
NULL, /* SysInfo */
NULL, /* ErrorInfo */
INSTR_TIME_GET_MILLISEC(duration), /* total_time */
rows, /* rows */ rows, /* rows */
&bufusage, /* bufusage */ &bufusage, /* bufusage */
&walusage); /* walusage */ &walusage, /* walusage */
NULL, /* JumbleState */
PGSS_FINISHED); /* pgssStoreKind */
} }
else else
{ {
@ -1095,6 +1130,8 @@ BufferUsageAccumDiff(BufferUsage* bufusage, BufferUsage* pgBufferUsage, BufferUs
INSTR_TIME_SUBTRACT(bufusage->blk_write_time, bufusage_start->blk_write_time); INSTR_TIME_SUBTRACT(bufusage->blk_write_time, bufusage_start->blk_write_time);
} }
#endif #endif
#if PG_VERSION_NUM < 140000
/* /*
* Given an arbitrarily long query string, produce a hash for the purposes of * Given an arbitrarily long query string, produce a hash for the purposes of
* identifying the query, without normalizing constants. Used when hashing * identifying the query, without normalizing constants. Used when hashing
@ -1106,6 +1143,7 @@ pgss_hash_string(const char *str, int len)
return DatumGetUInt64(hash_any_extended((const unsigned char *) str, return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
len, 0)); len, 0));
} }
#endif
static PgBackendStatus* static PgBackendStatus*
pg_get_backend_status(void) pg_get_backend_status(void)
@ -1320,72 +1358,6 @@ pgss_update_entry(pgssEntry *entry,
} }
} }
static void
pgss_store_query(uint64 queryid,
const char * query,
CmdType cmd_type,
int query_location,
int query_len,
#if PG_VERSION_NUM > 130000
JumbleState *jstate,
#else
JumbleState *jstate,
#endif
pgssStoreKind kind)
{
char *norm_query = NULL;
if (query_location >= 0)
{
Assert(query_location <= strlen(query));
query += query_location;
/* Length of 0 (or -1) means "rest of string" */
if (query_len <= 0)
query_len = strlen(query);
else
Assert(query_len <= strlen(query));
}
else
{
/* If query location is unknown, distrust query_len as well */
query_location = 0;
query_len = strlen(query);
}
/*
* Discard leading and trailing whitespace, too. Use scanner_isspace()
* not libc's isspace(), because we want to match the lexer's behavior.
*/
while (query_len > 0 && scanner_isspace(query[0]))
query++, query_location++, query_len--;
while (query_len > 0 && scanner_isspace(query[query_len - 1]))
query_len--;
if (jstate)
norm_query = generate_normalized_query(jstate, query,
query_location,
&query_len,
GetDatabaseEncoding());
/*
* For utility statements, we just hash the query string to get an ID.
*/
if (queryid == UINT64CONST(0))
queryid = pgss_hash_string(query, query_len);
pgss_store(queryid, /* query id */
PGSM_NORMALIZED_QUERY ? (norm_query ? norm_query : query) : query, /* query */
NULL, /* PlanInfo */
cmd_type, /* CmdType */
NULL, /* SysInfo */
NULL, /* ErrorInfo */
0, /* totaltime */
0, /* rows */
NULL, /* bufusage */
NULL, /* walusage */
jstate, /* JumbleState */
kind); /* pgssStoreKind */
}
static void static void
pgss_store_error(uint64 queryid, pgss_store_error(uint64 queryid,
const char * query, const char * query,
@ -1399,6 +1371,8 @@ pgss_store_error(uint64 queryid,
pgss_store(queryid, /* query id */ pgss_store(queryid, /* query id */
query, /* query text */ query, /* query text */
0, /* query location */
strlen(query), /* query length */
NULL, /* PlanInfo */ NULL, /* PlanInfo */
0, /* CmdType */ 0, /* CmdType */
NULL, /* SysInfo */ NULL, /* SysInfo */
@ -1411,29 +1385,6 @@ pgss_store_error(uint64 queryid,
PGSS_ERROR); /* pgssStoreKind */ PGSS_ERROR); /* pgssStoreKind */
} }
static void
pgss_store_utility(const char *query,
double total_time,
uint64 rows,
BufferUsage *bufusage,
WalUsage *walusage)
{
uint64 queryid = pgss_hash_string(query, strlen(query));
pgss_store(queryid, /* query id */
query, /* query text */
NULL, /* PlanInfo */
0, /* CmdType */
NULL, /* SysInfo */
NULL, /* ErrorInfo */
total_time, /* total_time */
rows, /* rows */
bufusage, /* bufusage */
walusage, /* walusage */
NULL, /* JumbleState */
PGSS_FINISHED); /* pgssStoreKind */
}
/* /*
* Store some statistics for a statement. * Store some statistics for a statement.
* *
@ -1447,6 +1398,8 @@ pgss_store_utility(const char *query,
static void static void
pgss_store(uint64 queryid, pgss_store(uint64 queryid,
const char *query, const char *query,
int query_location,
int query_len,
PlanInfo *plan_info, PlanInfo *plan_info,
CmdType cmd_type, CmdType cmd_type,
SysInfo *sys_info, SysInfo *sys_info,
@ -1471,6 +1424,7 @@ pgss_store(uint64 queryid,
uint64 planid; uint64 planid;
uint64 appid; uint64 appid;
char comments[512] = ""; char comments[512] = "";
char *norm_query = NULL;
static bool found_app_name = false; static bool found_app_name = false;
static bool found_client_addr = false; static bool found_client_addr = false;
static uint client_addr = 0; static uint client_addr = 0;
@ -1483,6 +1437,34 @@ pgss_store(uint64 queryid,
if (!IsSystemInitialized()) if (!IsSystemInitialized())
return; return;
#if PG_VERSION_NUM >= 140000
/*
* Nothing to do if compute_query_id isn't enabled and no other module
* computed a query identifier.
*/
if (queryid == UINT64CONST(0))
return;
#endif
query = CleanQuerytext(query, &query_location, &query_len);
#if PG_VERSION_NUM < 140000
/*
* For utility statements, we just hash the query string to get an ID.
*/
if (queryid == UINT64CONST(0))
{
queryid = pgss_hash_string(query, query_len);
/*
* If we are unlucky enough to get a hash of zero(invalid), use
* queryID as 2 instead, queryID 1 is already in use for normal
* statements.
*/
if (queryid == UINT64CONST(0))
queryid = UINT64CONST(2);
}
#endif
Assert(query != NULL); Assert(query != NULL);
if (kind == PGSS_ERROR) if (kind == PGSS_ERROR)
{ {
@ -1529,24 +1511,41 @@ pgss_store(uint64 queryid,
if (!entry) if (!entry)
{ {
pgssQueryEntry *query_entry; pgssQueryEntry *query_entry;
size_t query_len = 0;
bool query_found = false; bool query_found = false;
uint64 prev_qbuf_len = 0; uint64 prev_qbuf_len = 0;
HTAB *pgss_query_hash; HTAB *pgss_query_hash;
pgss_query_hash = pgsm_get_query_hash(); pgss_query_hash = pgsm_get_query_hash();
/*
* Create a new, normalized query string if caller asked. We don't
* need to hold the lock while doing this work. (Note: in any case,
* it's possible that someone else creates a duplicate hashtable entry
* in the interval where we don't hold the lock below. That case is
* handled by entry_alloc.
*/
if (jstate && PGSM_NORMALIZED_QUERY)
{
LWLockRelease(pgss->lock);
norm_query = generate_normalized_query(jstate, query,
query_location,
&query_len,
GetDatabaseEncoding());
LWLockAcquire(pgss->lock, LW_SHARED);
}
query_entry = hash_search(pgss_query_hash, &queryid, HASH_ENTER_NULL, &query_found); query_entry = hash_search(pgss_query_hash, &queryid, HASH_ENTER_NULL, &query_found);
if (query_entry == NULL) if (query_entry == NULL)
{ {
LWLockRelease(pgss->lock); LWLockRelease(pgss->lock);
if (norm_query)
pfree(norm_query);
pgsm_log_error("pgss_store: out of memory (pgss_query_hash)."); pgsm_log_error("pgss_store: out of memory (pgss_query_hash).");
return; return;
} }
else if (!query_found) else if (!query_found)
{ {
/* New query, must add it to the buffer, calculate its length. */ /* New query, truncate length if necessary. */
query_len = strlen(query);
if (query_len > PGSM_QUERY_MAX_LEN) if (query_len > PGSM_QUERY_MAX_LEN)
query_len = PGSM_QUERY_MAX_LEN; query_len = PGSM_QUERY_MAX_LEN;
} }
@ -1557,9 +1556,16 @@ pgss_store(uint64 queryid,
if (!query_found) if (!query_found)
{ {
if (!SaveQueryText(bucketid, queryid, pgss_qbuf, query, query_len, &query_entry->query_pos)) if (!SaveQueryText(bucketid,
queryid,
pgss_qbuf,
norm_query ? norm_query : query,
query_len,
&query_entry->query_pos))
{ {
LWLockRelease(pgss->lock); LWLockRelease(pgss->lock);
if (norm_query)
pfree(norm_query);
pgsm_log_error("pgss_store: insufficient shared space for query."); pgsm_log_error("pgss_store: insufficient shared space for query.");
return; return;
} }
@ -1581,7 +1587,8 @@ pgss_store(uint64 queryid,
memcpy(pgss_qbuf, &prev_qbuf_len, sizeof(prev_qbuf_len)); memcpy(pgss_qbuf, &prev_qbuf_len, sizeof(prev_qbuf_len));
} }
LWLockRelease(pgss->lock); LWLockRelease(pgss->lock);
elog(DEBUG1, "pg_stat_monitor: out of memory"); if (norm_query)
pfree(norm_query);
return; return;
} }
entry->query_pos = query_entry->query_pos; entry->query_pos = query_entry->query_pos;
@ -1607,6 +1614,8 @@ pgss_store(uint64 queryid,
application_name_len); application_name_len);
LWLockRelease(pgss->lock); LWLockRelease(pgss->lock);
if (norm_query)
pfree(norm_query);
} }
/* /*
* Reset all statement statistics. * Reset all statement statistics.
@ -2781,6 +2790,45 @@ RecordConstLocation(JumbleState *jstate, int location)
jstate->clocations_count++; jstate->clocations_count++;
} }
} }
static const char *
CleanQuerytext(const char *query, int *location, int *len)
{
int query_location = *location;
int query_len = *len;
/* First apply starting offset, unless it's -1 (unknown). */
if (query_location >= 0)
{
Assert(query_location <= strlen(query));
query += query_location;
/* Length of 0 (or -1) means "rest of string" */
if (query_len <= 0)
query_len = strlen(query);
else
Assert(query_len <= strlen(query));
}
else
{
/* If query location is unknown, distrust query_len as well */
query_location = 0;
query_len = strlen(query);
}
/*
* Discard leading and trailing whitespace, too. Use scanner_isspace()
* not libc's isspace(), because we want to match the lexer's behavior.
*/
while (query_len > 0 && scanner_isspace(query[0]))
query++, query_location++, query_len--;
while (query_len > 0 && scanner_isspace(query[query_len - 1]))
query_len--;
*location = query_location;
*len = query_len;
return query;
}
#endif #endif
/* /*