@ -67,6 +67,7 @@
# include "tcop/utility.h"
# include "tcop/utility.h"
# include "utils/acl.h"
# include "utils/acl.h"
# include "utils/builtins.h"
# include "utils/builtins.h"
# include "utils/queryjumble.h"
# include "utils/memutils.h"
# include "utils/memutils.h"
# include "utils/timestamp.h"
# include "utils/timestamp.h"
@ -101,6 +102,14 @@ static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
# define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */
# define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */
# define IS_STICKY(c) ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
# define IS_STICKY(c) ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
/*
* Utility statements that pgss_ProcessUtility and pgss_post_parse_analyze
* ignores .
*/
# define PGSS_HANDLED_UTILITY(n) (!IsA(n, ExecuteStmt) && \
! IsA ( n , PrepareStmt ) & & \
! IsA ( n , DeallocateStmt ) )
/*
/*
* Extension version number , for supporting older extension versions ' objects
* Extension version number , for supporting older extension versions ' objects
*/
*/
@ -309,7 +318,6 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context , ParamListInfo params ,
ProcessUtilityContext context , ParamListInfo params ,
QueryEnvironment * queryEnv ,
QueryEnvironment * queryEnv ,
DestReceiver * dest , QueryCompletion * qc ) ;
DestReceiver * dest , QueryCompletion * qc ) ;
static uint64 pgss_hash_string ( const char * str , int len ) ;
static void pgss_store ( const char * query , uint64 queryId ,
static void pgss_store ( const char * query , uint64 queryId ,
int query_location , int query_len ,
int query_location , int query_len ,
pgssStoreKind kind ,
pgssStoreKind kind ,
@ -806,16 +814,14 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
return ;
return ;
/*
/*
* Utility statements get queryId zero . We do this even in cases where
* Clear queryId for prepared statements related utility , as those will
* the statement contains an optimizable statement for which a queryId
* inherit from the underlying statement ' s one ( except DEALLOCATE which is
* could be derived ( such as EXPLAIN or DECLARE CURSOR ) . For such cases ,
* entirely untracked ) .
* 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 ( query - > utilityStmt )
if ( query - > utilityStmt )
{
{
query - > queryId = UINT64CONST ( 0 ) ;
if ( pgss_track_utility & & ! PGSS_HANDLED_UTILITY ( query - > utilityStmt ) )
query - > queryId = UINT64CONST ( 0 ) ;
return ;
return ;
}
}
@ -1057,6 +1063,23 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
DestReceiver * dest , QueryCompletion * qc )
DestReceiver * dest , QueryCompletion * qc )
{
{
Node * parsetree = pstmt - > utilityStmt ;
Node * parsetree = pstmt - > utilityStmt ;
uint64 saved_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 .
*
* Note that this is only done if pg_stat_statements is enabled and
* configured to track utility statements , in the unlikely possibility
* that user configured another extension to handle utility statements
* only .
*/
if ( pgss_enabled ( exec_nested_level ) & & pgss_track_utility )
pstmt - > queryId = UINT64CONST ( 0 ) ;
/*
/*
* 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
@ -1073,9 +1096,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
* Likewise , we don ' t track execution of DEALLOCATE .
* Likewise , we don ' t track execution of DEALLOCATE .
*/
*/
if ( pgss_track_utility & & pgss_enabled ( exec_nested_level ) & &
if ( pgss_track_utility & & pgss_enabled ( exec_nested_level ) & &
! IsA ( parsetree , ExecuteStmt ) & &
PGSS_HANDLED_UTILITY ( parsetree ) )
! IsA ( parsetree , PrepareStmt ) & &
! IsA ( parsetree , DeallocateStmt ) )
{
{
instr_time start ;
instr_time start ;
instr_time duration ;
instr_time duration ;
@ -1130,7 +1151,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
WalUsageAccumDiff ( & walusage , & pgWalUsage , & walusage_start ) ;
WalUsageAccumDiff ( & walusage , & pgWalUsage , & walusage_start ) ;
pgss_store ( queryString ,
pgss_store ( queryString ,
0 , /* signal that it's a utility stmt */
saved_queryId ,
pstmt - > stmt_location ,
pstmt - > stmt_location ,
pstmt - > stmt_len ,
pstmt - > stmt_len ,
PGSS_EXEC ,
PGSS_EXEC ,
@ -1153,23 +1174,12 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
}
}
}
}
/*
* Given an arbitrarily long query string , produce a hash for the purposes of
* identifying the query , without normalizing constants . Used when hashing
* utility statements .
*/
static uint64
pgss_hash_string ( const char * str , int len )
{
return DatumGetUInt64 ( hash_any_extended ( ( const unsigned char * ) str ,
len , 0 ) ) ;
}
/*
/*
* Store some statistics for a statement .
* Store some statistics for a statement .
*
*
* If queryId is 0 then this is a utility statement and we should compute
* If queryId is 0 then this is a utility statement for which we couldn ' t
* a suitable queryId internally .
* compute a queryId during parse analysis , and we should compute a suitable
* queryId internally .
*
*
* If jstate is not NULL then we ' re trying to create an entry for which
* If jstate is not NULL then we ' re trying to create an entry for which
* we have no statistics as yet ; we just want to record the normalized
* we have no statistics as yet ; we just want to record the normalized
@ -1200,52 +1210,18 @@ pgss_store(const char *query, uint64 queryId,
return ;
return ;
/*
/*
* Confine our attention to the relevant part of the string , if the query
* Nothing to do if compute_query_id isn ' t enabled and no other module
* is a portion of a multi - statement source string .
* computed a query identifier .
*
* 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 ] ) )
if ( queryId = = UINT64CONST ( 0 ) )
query + + , query_location + + , query_len - - ;
return ;
while ( query_len > 0 & & scanner_isspace ( query [ query_len - 1 ] ) )
query_len - - ;
/*
/*
* For utility statements , we just hash the query string to get an ID .
* Confine our attention to the relevant part of the string , if the query
* is a portion of a multi - statement source string , and update query
* location and length if needed .
*/
*/
if ( queryId = = UINT64CONST ( 0 ) )
query = CleanQuerytext ( query , & query_location , & query_len ) ;
{
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 ) ;
}
/* Set up key for hashtable search */
/* Set up key for hashtable search */
key . userid = GetUserId ( ) ;
key . userid = GetUserId ( ) ;