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,
DestReceiver *dest,
QueryCompletion *qc);
static uint64 pgss_hash_string(const char *str, int len);
#else
static void BufferUsageAccumDiff(BufferUsage* bufusage, BufferUsage* pgBufferUsage, BufferUsage* bufusage_start);
DECLARE_HOOK(void pgss_ProcessUtility, PlannedStmt *pstmt, const char *queryString,
@ -144,30 +145,28 @@ DECLARE_HOOK(void pgss_ProcessUtility, PlannedStmt *pstmt, const char *queryStri
DestReceiver *dest,
char *completionTag);
#endif
static uint64 pgss_hash_string(const char *str, int len);
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,
double total_time,
uint64 rows,
BufferUsage *bufusage,
WalUsage *walusage);
static void pgss_store_error(uint64 queryid, const char *query, ErrorData *edata);
static void pgss_store(uint64 queryid,
const char *query,
PlanInfo *plan_info,
CmdType cmd_type,
SysInfo *sys_info,
ErrorInfo *error_info,
double total_time,
uint64 rows,
BufferUsage *bufusage,
WalUsage *walusage,
JumbleState *jstate,
pgssStoreKind kind);
const char *query,
int query_location,
int query_len,
PlanInfo *plan_info,
CmdType cmd_type,
SysInfo *sys_info,
ErrorInfo *error_info,
double total_time,
uint64 rows,
BufferUsage *bufusage,
WalUsage *walusage,
JumbleState *jstate,
pgssStoreKind kind);
static void pg_stat_monitor_internal(FunctionCallInfo fcinfo,
bool showtext);
@ -179,6 +178,12 @@ static void JumbleQuery(JumbleState *jstate, Query *query);
static void JumbleRangeTable(JumbleState *jstate, List *rtable);
static void JumbleExpr(JumbleState *jstate, Node *node);
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
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 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
static uint64 get_query_id(JumbleState *jstate, Query *query);
#endif
@ -357,8 +349,6 @@ pgss_post_parse_analyze_benchmark(ParseState *pstate, Query *query, JumbleState
static void
pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
{
pgssStoreKind kind = PGSS_PARSE;
if (prev_post_parse_analyze_hook)
prev_post_parse_analyze_hook(pstate, query, jstate);
@ -376,7 +366,8 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
*/
if (query->utilityStmt)
{
query->queryId = UINT64CONST(0);
if (PGSM_TRACK_UTILITY && !PGSM_HANDLED_UTILITY(query->utilityStmt))
query->queryId = UINT64CONST(0);
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
* anyway, so there's no need for an early entry.
*/
if (jstate == NULL || jstate->clocations_count <= 0)
return;
pgss_store_query(query->queryId, /* queryid */
pstate->p_sourcetext, /* query */
query->commandType, /* CmdType */
query->stmt_location, /* Query Location */
query->stmt_len, /* Query Len */
jstate, /* JumbleState */
kind); /*pgssStoreKind */
if (jstate && jstate->clocations_count > 0)
pgss_store(query->queryId, /* query id */
pstate->p_sourcetext, /* query */
query->stmt_location, /* query location */
query->stmt_len, /* query length */
NULL, /* PlanInfo */
query->commandType, /* CmdType */
NULL, /* SysInfo */
NULL, /* ErrorInfo */
0, /* totaltime */
0, /* rows */
NULL, /* bufusage */
NULL, /* walusage */
jstate, /* JumbleState */
PGSS_PARSE); /* pgssStoreKind */
}
#else
@ -416,7 +413,6 @@ static void
pgss_post_parse_analyze(ParseState *pstate, Query *query)
{
JumbleState jstate;
pgssStoreKind kind = PGSS_PARSE;
if (prev_post_parse_analyze_hook)
prev_post_parse_analyze_hook(pstate, query);
@ -451,16 +447,21 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
if (query->queryId == UINT64CONST(0))
query->queryId = UINT64CONST(1);
if (jstate.clocations_count <= 0)
return;
pgss_store_query(query->queryId, /* queryid */
pstate->p_sourcetext, /* query */
query->commandType, /* CmdType */
query->stmt_location, /* Query Location */
query->stmt_len, /* Query Len */
&jstate, /* JumbleState */
kind); /*pgssStoreKind */
if (jstate.clocations_count > 0)
pgss_store(query->queryId, /* query id */
pstate->p_sourcetext, /* query */
query->stmt_location, /* query location */
query->stmt_len, /* query length */
NULL, /* PlanInfo */
query->commandType, /* CmdType */
NULL, /* SysInfo */
NULL, /* ErrorInfo */
0, /* totaltime */
0, /* rows */
NULL, /* bufusage */
NULL, /* walusage */
&jstate, /* JumbleState */
PGSS_PARSE); /* pgssStoreKind */
}
#endif
@ -480,10 +481,8 @@ pgss_ExecutorStart_benchmark(QueryDesc *queryDesc, int eflags)
static void
pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
{
uint64 queryId = queryDesc->plannedstmt->queryId;
if(getrusage(RUSAGE_SELF, &rusage_start) != 0)
elog(DEBUG1, "pg_stat_monitor: failed to execute getrusage");
if (getrusage(RUSAGE_SELF, &rusage_start) != 0)
pgsm_log_error("pgss_ExecutorStart: failed to execute getrusage");
if (prev_ExecutorStart)
prev_ExecutorStart(queryDesc, eflags);
@ -517,22 +516,20 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
#endif
MemoryContextSwitchTo(oldcxt);
}
pgss_store(queryId, /* query id */
pgss_store(queryDesc->plannedstmt->queryId, /* query id */
queryDesc->sourceText, /* query text */
queryDesc->plannedstmt->stmt_location, /* query location */
queryDesc->plannedstmt->stmt_len, /* query length */
NULL, /* PlanInfo */
queryDesc->operation, /* CmdType */
NULL, /* SysInfo */
NULL, /* ErrorInfo */
0, /* totaltime */
0, /* rows */
NULL, /* bufusage */
#if PG_VERSION_NUM >= 130000
NULL, /* bufusage */
NULL, /* walusage */
#else
NULL,
#endif
NULL,
PGSS_EXEC); /* pgssStoreKind */
NULL, /* JumbleState */
PGSS_EXEC); /* pgssStoreKind */
}
}
@ -649,17 +646,17 @@ pgss_ExecutorEnd_benchmark(QueryDesc *queryDesc)
static void
pgss_ExecutorEnd(QueryDesc *queryDesc)
{
uint64 queryId = queryDesc->plannedstmt->queryId;
SysInfo sys_info;
PlanInfo plan_info;
PlanInfo *plan_ptr = NULL;
uint64 queryId = queryDesc->plannedstmt->queryId;
SysInfo sys_info;
PlanInfo plan_info;
PlanInfo *plan_ptr = NULL;
/* Extract the plan information in case of SELECT statement */
/* Extract the plan information in case of SELECT statement */
if (queryDesc->operation == CMD_SELECT && PGSM_QUERY_PLAN)
{
{
MemoryContext mct = MemoryContextSwitchTo(TopMemoryContext);
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;
MemoryContextSwitchTo(mct);
}
@ -677,15 +674,17 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
sys_info.utime = time_diff(rusage_end.ru_utime, rusage_start.ru_utime);
sys_info.stime = time_diff(rusage_end.ru_stime, rusage_start.ru_stime);
pgss_store(queryId, /* query id */
queryDesc->sourceText, /* query text */
plan_ptr, /* PlanInfo */
queryDesc->operation, /* CmdType */
&sys_info, /* SysInfo */
NULL, /* ErrorInfo */
queryDesc->totaltime->total * 1000.0, /* totaltime */
queryDesc->estate->es_processed, /* rows */
&queryDesc->totaltime->bufusage, /* bufusage */
pgss_store(queryId, /* query id */
queryDesc->sourceText, /* query text */
queryDesc->plannedstmt->stmt_location, /* query location */
queryDesc->plannedstmt->stmt_len, /* query length */
plan_ptr, /* PlanInfo */
queryDesc->operation, /* CmdType */
&sys_info, /* SysInfo */
NULL, /* ErrorInfo */
queryDesc->totaltime->total * 1000.0, /* totaltime */
queryDesc->estate->es_processed, /* rows */
&queryDesc->totaltime->bufusage, /* bufusage */
#if PG_VERSION_NUM >= 130000
&queryDesc->totaltime->walusage, /* walusage */
#else
@ -829,18 +828,20 @@ pgss_planner_hook(Query *parse, const char *query_string, int cursorOptions, Par
/* calc differences of WAL counters. */
memset(&walusage, 0, sizeof(WalUsage));
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
pgss_store(parse->queryId, /* query id */
query_string, /* query */
NULL, /* PlanInfo */
parse->commandType, /* CmdType */
NULL, /* SysInfo */
NULL, /* ErrorInfo */
INSTR_TIME_GET_MILLISEC(duration), /* totaltime */
0, /* rows */
&bufusage, /* bufusage */
&walusage, /* walusage */
NULL, /* JumbleState */
PGSS_PLAN); /* pgssStoreKind */
pgss_store(parse->queryId, /* query id */
query_string, /* query */
parse->stmt_location, /* query location */
parse->stmt_len, /* query length */
NULL, /* PlanInfo */
parse->commandType, /* CmdType */
NULL, /* SysInfo */
NULL, /* ErrorInfo */
INSTR_TIME_GET_MILLISEC(duration), /* totaltime */
0, /* rows */
&bufusage, /* bufusage */
&walusage, /* walusage */
NULL, /* JumbleState */
PGSS_PLAN); /* pgssStoreKind */
}
else
{
@ -930,7 +931,24 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
char *completionTag)
#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
@ -946,10 +964,8 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
*
* Likewise, we don't track execution of DEALLOCATE.
*/
if (PGSM_TRACK_UTILITY &&
!IsA(parsetree, ExecuteStmt) &&
!IsA(parsetree, PrepareStmt) &&
!IsA(parsetree, DeallocateStmt) && !IsParallelWorker())
if (PGSM_TRACK_UTILITY && PGSM_HANDLED_UTILITY(parsetree) &&
!IsParallelWorker())
{
instr_time start;
instr_time duration;
@ -1012,7 +1028,16 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
INSTR_TIME_SUBTRACT(duration, start);
#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;
#endif
/* calc differences of WAL counters. */
memset(&walusage, 0, sizeof(WalUsage));
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
@ -1027,49 +1052,59 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
/* calc differences of buffer counters. */
memset(&bufusage, 0, sizeof(BufferUsage));
BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
pgss_store_utility(queryString, /* query text */
INSTR_TIME_GET_MILLISEC(duration), /* totaltime */
rows, /* rows */
&bufusage, /* bufusage */
&walusage); /* walusage */
pgss_store(
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 */
&bufusage, /* bufusage */
&walusage, /* walusage */
NULL, /* JumbleState */
PGSS_FINISHED); /* pgssStoreKind */
}
else
{
#if PG_VERSION_NUM >= 140000
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
readOnlyTree,
context, params, queryEnv,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
readOnlyTree,
context, params, queryEnv,
dest,
qc);
else
standard_ProcessUtility(pstmt, queryString,
readOnlyTree,
context, params, queryEnv,
dest,
qc);
else
standard_ProcessUtility(pstmt, queryString,
readOnlyTree,
context, params, queryEnv,
dest,
qc);
#elif PG_VERSION_NUM >= 130000
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
dest,
qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
dest,
qc);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
dest,
qc);
#else
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
dest,
completionTag);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
dest,
completionTag);
else
standard_ProcessUtility(pstmt, queryString,
context, params, queryEnv,
dest,
completionTag);
#endif
}
}
@ -1095,6 +1130,8 @@ BufferUsageAccumDiff(BufferUsage* bufusage, BufferUsage* pgBufferUsage, BufferUs
INSTR_TIME_SUBTRACT(bufusage->blk_write_time, bufusage_start->blk_write_time);
}
#endif
#if PG_VERSION_NUM < 140000
/*
* Given an arbitrarily long query string, produce a hash for the purposes of
* 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,
len, 0));
}
#endif
static PgBackendStatus*
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
pgss_store_error(uint64 queryid,
const char * query,
@ -1397,41 +1369,20 @@ pgss_store_error(uint64 queryid,
snprintf(error_info.message, ERROR_MESSAGE_LEN, "%s", edata->message);
snprintf(error_info.sqlcode, SQLCODE_LEN, "%s", unpack_sql_state(edata->sqlerrcode));
pgss_store(queryid, /* query id */
query, /* query text */
NULL, /* PlanInfo */
0, /* CmdType */
NULL, /* SysInfo */
&error_info, /* ErrorInfo */
0, /* total_time */
0, /* rows */
NULL, /* bufusage */
NULL, /* walusage */
NULL, /* JumbleState */
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 */
pgss_store(queryid, /* query id */
query, /* query text */
0, /* query location */
strlen(query), /* query length */
NULL, /* PlanInfo */
0, /* CmdType */
NULL, /* SysInfo */
&error_info, /* ErrorInfo */
0, /* total_time */
0, /* rows */
NULL, /* bufusage */
NULL, /* walusage */
NULL, /* JumbleState */
PGSS_ERROR); /* pgssStoreKind */
}
/*
@ -1446,17 +1397,19 @@ pgss_store_utility(const char *query,
*/
static void
pgss_store(uint64 queryid,
const char *query,
PlanInfo *plan_info,
CmdType cmd_type,
SysInfo *sys_info,
ErrorInfo *error_info,
double total_time,
uint64 rows,
BufferUsage *bufusage,
WalUsage *walusage,
JumbleState *jstate,
pgssStoreKind kind)
const char *query,
int query_location,
int query_len,
PlanInfo *plan_info,
CmdType cmd_type,
SysInfo *sys_info,
ErrorInfo *error_info,
double total_time,
uint64 rows,
BufferUsage *bufusage,
WalUsage *walusage,
JumbleState *jstate,
pgssStoreKind kind)
{
HTAB *pgss_hash;
pgssHashKey key;
@ -1471,6 +1424,7 @@ pgss_store(uint64 queryid,
uint64 planid;
uint64 appid;
char comments[512] = "";
char *norm_query = NULL;
static bool found_app_name = false;
static bool found_client_addr = false;
static uint client_addr = 0;
@ -1483,6 +1437,34 @@ pgss_store(uint64 queryid,
if (!IsSystemInitialized())
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);
if (kind == PGSS_ERROR)
{
@ -1529,24 +1511,41 @@ pgss_store(uint64 queryid,
if (!entry)
{
pgssQueryEntry *query_entry;
size_t query_len = 0;
bool query_found = false;
uint64 prev_qbuf_len = 0;
HTAB *pgss_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);
if (query_entry == NULL)
{
LWLockRelease(pgss->lock);
if (norm_query)
pfree(norm_query);
pgsm_log_error("pgss_store: out of memory (pgss_query_hash).");
return;
}
else if (!query_found)
{
/* New query, must add it to the buffer, calculate its length. */
query_len = strlen(query);
/* New query, truncate length if necessary. */
if (query_len > PGSM_QUERY_MAX_LEN)
query_len = PGSM_QUERY_MAX_LEN;
}
@ -1557,21 +1556,28 @@ pgss_store(uint64 queryid,
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);
if (norm_query)
pfree(norm_query);
pgsm_log_error("pgss_store: insufficient shared space for query.");
return;
}
/*
* Save current query buffer length, if we fail to add a new
* new entry to the hash table then we must restore the
* original length.
*/
* Save current query buffer length, if we fail to add a new
* new entry to the hash table then we must restore the
* original length.
*/
memcpy(&prev_qbuf_len, pgss_qbuf, sizeof(prev_qbuf_len));
}
/* OK to create a new hashtable entry */
/* OK to create a new hashtable entry */
entry = hash_entry_alloc(pgss, &key, GetDatabaseEncoding());
if (entry == NULL)
{
@ -1581,7 +1587,8 @@ pgss_store(uint64 queryid,
memcpy(pgss_qbuf, &prev_qbuf_len, sizeof(prev_qbuf_len));
}
LWLockRelease(pgss->lock);
elog(DEBUG1, "pg_stat_monitor: out of memory");
if (norm_query)
pfree(norm_query);
return;
}
entry->query_pos = query_entry->query_pos;
@ -1607,6 +1614,8 @@ pgss_store(uint64 queryid,
application_name_len);
LWLockRelease(pgss->lock);
if (norm_query)
pfree(norm_query);
}
/*
* Reset all statement statistics.
@ -2781,6 +2790,45 @@ RecordConstLocation(JumbleState *jstate, int location)
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
/*