diff --git a/guc.c b/guc.c index 7ea2c02..8b288ab 100644 --- a/guc.c +++ b/guc.c @@ -19,23 +19,23 @@ #include "pg_stat_monitor.h" /* GUC variables */ -int pgsm_max; -int pgsm_query_max_len; -int pgsm_bucket_time; -int pgsm_max_buckets; -int pgsm_histogram_buckets; -double pgsm_histogram_min; -double pgsm_histogram_max; -int pgsm_query_shared_buffer; -bool pgsm_track_planning; -bool pgsm_extract_comments; -bool pgsm_enable_query_plan; -bool pgsm_enable_overflow; -bool pgsm_normalized_query; -bool pgsm_track_utility; -bool pgsm_enable_pgsm_query_id; -int pgsm_track; -static int pgsm_overflow_target; /* Not used since 2.0 */ +int pgsm_max; +int pgsm_query_max_len; +int pgsm_bucket_time; +int pgsm_max_buckets; +int pgsm_histogram_buckets; +double pgsm_histogram_min; +double pgsm_histogram_max; +int pgsm_query_shared_buffer; +bool pgsm_track_planning; +bool pgsm_extract_comments; +bool pgsm_enable_query_plan; +bool pgsm_enable_overflow; +bool pgsm_normalized_query; +bool pgsm_track_utility; +bool pgsm_enable_pgsm_query_id; +int pgsm_track; +static int pgsm_overflow_target; /* Not used since 2.0 */ /* Check hooks to ensure histogram_min < histogram_max */ static bool check_histogram_min(double *newval, void **extra, GucSource source); @@ -48,230 +48,230 @@ void init_guc(void) { - DefineCustomIntVariable("pg_stat_monitor.pgsm_max", /* name */ - "Sets the maximum size of shared memory in (MB) used for statement's metadata tracked by pg_stat_monitor.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_max, /* value address */ - 256, /* boot value */ - 10, /* min value */ - 10240, /* max value */ - PGC_POSTMASTER, /* context */ - GUC_UNIT_MB, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomIntVariable("pg_stat_monitor.pgsm_max", /* name */ + "Sets the maximum size of shared memory in (MB) used for statement's metadata tracked by pg_stat_monitor.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_max, /* value address */ + 256, /* boot value */ + 10, /* min value */ + 10240, /* max value */ + PGC_POSTMASTER, /* context */ + GUC_UNIT_MB, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomIntVariable("pg_stat_monitor.pgsm_query_max_len", /* name */ - "Sets the maximum length of query.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_query_max_len, /* value address */ - 2048, /* boot value */ - 1024, /* min value */ - INT_MAX, /* max value */ - PGC_POSTMASTER, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomIntVariable("pg_stat_monitor.pgsm_query_max_len", /* name */ + "Sets the maximum length of query.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_query_max_len, /* value address */ + 2048, /* boot value */ + 1024, /* min value */ + INT_MAX, /* max value */ + PGC_POSTMASTER, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomIntVariable("pg_stat_monitor.pgsm_max_buckets", /* name */ - "Sets the maximum number of buckets.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_max_buckets, /* value address */ - 10, /* boot value */ - 1, /* min value */ - 20000, /* max value */ - PGC_POSTMASTER, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomIntVariable("pg_stat_monitor.pgsm_max_buckets", /* name */ + "Sets the maximum number of buckets.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_max_buckets, /* value address */ + 10, /* boot value */ + 1, /* min value */ + 20000, /* max value */ + PGC_POSTMASTER, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomIntVariable("pg_stat_monitor.pgsm_bucket_time", /* name */ - "Sets the time in seconds per bucket.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_bucket_time, /* value address */ - 60, /* boot value */ - 1, /* min value */ - INT_MAX, /* max value */ - PGC_POSTMASTER, /* context */ - GUC_UNIT_S, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomIntVariable("pg_stat_monitor.pgsm_bucket_time", /* name */ + "Sets the time in seconds per bucket.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_bucket_time, /* value address */ + 60, /* boot value */ + 1, /* min value */ + INT_MAX, /* max value */ + PGC_POSTMASTER, /* context */ + GUC_UNIT_S, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomRealVariable("pg_stat_monitor.pgsm_histogram_min", /* name */ - "Sets the time in millisecond.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_histogram_min, /* value address */ - 1, /* boot value */ - 0, /* min value */ - HISTOGRAM_MAX_TIME, /* max value */ - PGC_POSTMASTER, /* context */ - GUC_UNIT_MS, /* flags */ - check_histogram_min, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomRealVariable("pg_stat_monitor.pgsm_histogram_min", /* name */ + "Sets the time in millisecond.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_histogram_min, /* value address */ + 1, /* boot value */ + 0, /* min value */ + HISTOGRAM_MAX_TIME, /* max value */ + PGC_POSTMASTER, /* context */ + GUC_UNIT_MS, /* flags */ + check_histogram_min, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomRealVariable("pg_stat_monitor.pgsm_histogram_max", /* name */ - "Sets the time in millisecond.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_histogram_max, /* value address */ - 100000.0, /* boot value */ - 10.0, /* min value */ - HISTOGRAM_MAX_TIME, /* max value */ - PGC_POSTMASTER, /* context */ - GUC_UNIT_MS, /* flags */ - check_histogram_max, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomRealVariable("pg_stat_monitor.pgsm_histogram_max", /* name */ + "Sets the time in millisecond.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_histogram_max, /* value address */ + 100000.0, /* boot value */ + 10.0, /* min value */ + HISTOGRAM_MAX_TIME, /* max value */ + PGC_POSTMASTER, /* context */ + GUC_UNIT_MS, /* flags */ + check_histogram_max, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomIntVariable("pg_stat_monitor.pgsm_histogram_buckets", /* name */ - "Sets the maximum number of histogram buckets.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_histogram_buckets, /* value address */ - 20, /* boot value */ - 2, /* min value */ - MAX_RESPONSE_BUCKET, /* max value */ - PGC_POSTMASTER, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomIntVariable("pg_stat_monitor.pgsm_histogram_buckets", /* name */ + "Sets the maximum number of histogram buckets.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_histogram_buckets, /* value address */ + 20, /* boot value */ + 2, /* min value */ + MAX_RESPONSE_BUCKET, /* max value */ + PGC_POSTMASTER, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomIntVariable("pg_stat_monitor.pgsm_query_shared_buffer", /* name */ - "Sets the maximum size of shared memory in (MB) used for query tracked by pg_stat_monitor.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_query_shared_buffer, /* value address */ - 20, /* boot value */ - 1, /* min value */ - 10000, /* max value */ - PGC_POSTMASTER, /* context */ - GUC_UNIT_MB, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomIntVariable("pg_stat_monitor.pgsm_query_shared_buffer", /* name */ + "Sets the maximum size of shared memory in (MB) used for query tracked by pg_stat_monitor.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_query_shared_buffer, /* value address */ + 20, /* boot value */ + 1, /* min value */ + 10000, /* max value */ + PGC_POSTMASTER, /* context */ + GUC_UNIT_MB, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - /* deprecated in V 2.0 */ - DefineCustomIntVariable("pg_stat_monitor.pgsm_overflow_target", /* name */ - "Sets the overflow target for pg_stat_monitor. (Deprecated, use pgsm_enable_overflow)", /* short_desc */ - NULL, /* long_desc */ - &pgsm_overflow_target, /* value address */ - 1, /* boot value */ - 0, /* min value */ - 1, /* max value */ - PGC_POSTMASTER, /* context */ - 0, /* flags */ - check_overflow_targer, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + /* deprecated in V 2.0 */ + DefineCustomIntVariable("pg_stat_monitor.pgsm_overflow_target", /* name */ + "Sets the overflow target for pg_stat_monitor. (Deprecated, use pgsm_enable_overflow)", /* short_desc */ + NULL, /* long_desc */ + &pgsm_overflow_target, /* value address */ + 1, /* boot value */ + 0, /* min value */ + 1, /* max value */ + PGC_POSTMASTER, /* context */ + 0, /* flags */ + check_overflow_targer, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomBoolVariable("pg_stat_monitor.pgsm_track_utility", /* name */ - "Selects whether utility commands are tracked.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_track_utility, /* value address */ - true, /* boot value */ - PGC_USERSET, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomBoolVariable("pg_stat_monitor.pgsm_track_utility", /* name */ + "Selects whether utility commands are tracked.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_track_utility, /* value address */ + true, /* boot value */ + PGC_USERSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomBoolVariable("pg_stat_monitor.pgsm_enable_pgsm_query_id", /* name */ - "Enable/disable PGSM specific query id calculation which is very useful in comparing same query across databases and clusters..", /* short_desc */ - NULL, /* long_desc */ - &pgsm_enable_pgsm_query_id, /* value address */ - true, /* boot value */ - PGC_USERSET, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); - - DefineCustomBoolVariable("pg_stat_monitor.pgsm_normalized_query", /* name */ - "Selects whether save query in normalized format.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_normalized_query, /* value address */ - false, /* boot value */ - PGC_USERSET, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomBoolVariable("pg_stat_monitor.pgsm_enable_pgsm_query_id", /* name */ + "Enable/disable PGSM specific query id calculation which is very useful in comparing same query across databases and clusters..", /* short_desc */ + NULL, /* long_desc */ + &pgsm_enable_pgsm_query_id, /* value address */ + true, /* boot value */ + PGC_USERSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomBoolVariable("pg_stat_monitor.pgsm_enable_overflow", /* name */ - "Enable/Disable pg_stat_monitor to grow beyond shared memory into swap space.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_enable_overflow, /* value address */ - true, /* boot value */ - PGC_POSTMASTER, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomBoolVariable("pg_stat_monitor.pgsm_normalized_query", /* name */ + "Selects whether save query in normalized format.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_normalized_query, /* value address */ + false, /* boot value */ + PGC_USERSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomBoolVariable("pg_stat_monitor.pgsm_enable_query_plan", /* name */ - "Enable/Disable query plan monitoring.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_enable_query_plan, /* value address */ - false, /* boot value */ - PGC_USERSET, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomBoolVariable("pg_stat_monitor.pgsm_enable_overflow", /* name */ + "Enable/Disable pg_stat_monitor to grow beyond shared memory into swap space.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_enable_overflow, /* value address */ + true, /* boot value */ + PGC_POSTMASTER, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomBoolVariable("pg_stat_monitor.pgsm_extract_comments", /* name */ - "Enable/Disable extracting comments from queries.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_extract_comments, /* value address */ - false, /* boot value */ - PGC_USERSET, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomBoolVariable("pg_stat_monitor.pgsm_enable_query_plan", /* name */ + "Enable/Disable query plan monitoring.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_enable_query_plan, /* value address */ + false, /* boot value */ + PGC_USERSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); - DefineCustomEnumVariable("pg_stat_monitor.pgsm_track", /* name */ - "Selects which statements are tracked by pg_stat_monitor.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_track, /* value address */ - PGSM_TRACK_TOP, /* boot value */ - track_options, /* enum options */ - PGC_USERSET, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomBoolVariable("pg_stat_monitor.pgsm_extract_comments", /* name */ + "Enable/Disable extracting comments from queries.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_extract_comments, /* value address */ + false, /* boot value */ + PGC_USERSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); + + DefineCustomEnumVariable("pg_stat_monitor.pgsm_track", /* name */ + "Selects which statements are tracked by pg_stat_monitor.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_track, /* value address */ + PGSM_TRACK_TOP, /* boot value */ + track_options, /* enum options */ + PGC_USERSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); #if PG_VERSION_NUM >= 130000 - DefineCustomBoolVariable("pg_stat_monitor.pgsm_track_planning", /* name */ - "Selects whether planning statistics are tracked.", /* short_desc */ - NULL, /* long_desc */ - &pgsm_track_planning, /* value address */ - false, /* boot value */ - PGC_USERSET, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); + DefineCustomBoolVariable("pg_stat_monitor.pgsm_track_planning", /* name */ + "Selects whether planning statistics are tracked.", /* short_desc */ + NULL, /* long_desc */ + &pgsm_track_planning, /* value address */ + false, /* boot value */ + PGC_USERSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); #endif } @@ -280,23 +280,23 @@ init_guc(void) static bool check_histogram_min(double *newval, void **extra, GucSource source) { - /* - * During module initialization PGSM_HISTOGRAM_MIN is initialized before - * PGSM_HISTOGRAM_MAX, in this case PGSM_HISTOGRAM_MAX will be zero. - */ - return (pgsm_histogram_max == 0 || (*newval + 1.0) <= pgsm_histogram_max); + /* + * During module initialization PGSM_HISTOGRAM_MIN is initialized before + * PGSM_HISTOGRAM_MAX, in this case PGSM_HISTOGRAM_MAX will be zero. + */ + return (pgsm_histogram_max == 0 || (*newval + 1.0) <= pgsm_histogram_max); } static bool check_histogram_max(double *newval, void **extra, GucSource source) { - return (*newval >= (pgsm_histogram_min + 1.0)); + return (*newval >= (pgsm_histogram_min + 1.0)); } static bool check_overflow_targer(int *newval, void **extra, GucSource source) { - if (source != PGC_S_DEFAULT) - elog(WARNING,"pg_stat_monitor.pgsm_overflow_target is deprecated, use pgsm_enable_overflow"); - return true; + if (source != PGC_S_DEFAULT) + elog(WARNING, "pg_stat_monitor.pgsm_overflow_target is deprecated, use pgsm_enable_overflow"); + return true; } diff --git a/hash_query.c b/hash_query.c index 3dad1f0..c22aab3 100644 --- a/hash_query.c +++ b/hash_query.c @@ -18,18 +18,18 @@ #include "nodes/pg_list.h" #include "pg_stat_monitor.h" -static pgsmLocalState pgsmStateLocal; -static PGSM_HASH_TABLE_HANDLE pgsm_create_bucket_hash(pgsmSharedState *pgsm, dsa_area *dsa); +static pgsmLocalState pgsmStateLocal; +static PGSM_HASH_TABLE_HANDLE pgsm_create_bucket_hash(pgsmSharedState * pgsm, dsa_area * dsa); static Size pgsm_get_shared_area_size(void); -static void InitializeSharedState(pgsmSharedState *pgsm); +static void InitializeSharedState(pgsmSharedState * pgsm); #if USE_DYNAMIC_HASH /* parameter for the shared hash */ static dshash_parameters dsh_params = { - sizeof(pgsmHashKey), - sizeof(pgsmEntry), - dshash_memcmp, - dshash_memhash + sizeof(pgsmHashKey), + sizeof(pgsmEntry), + dshash_memcmp, + dshash_memhash }; #endif @@ -40,28 +40,26 @@ static dshash_parameters dsh_params = { */ static Size -pgsm_query_area_size(void) -{ - Size sz = MAX_QUERY_BUF; - #if USE_DYNAMIC_HASH - /* Dynamic hash also lives DSA area */ - sz = add_size(sz, MAX_BUCKETS_MEM); - #endif - return MAXALIGN(sz); +pgsm_query_area_size(void) { + Size sz = MAX_QUERY_BUF; +#if USE_DYNAMIC_HASH + /* Dynamic hash also lives DSA area */ + sz = add_size(sz, MAX_BUCKETS_MEM); +#endif + return MAXALIGN(sz); } /* * Total shared memory area required by pgsm */ Size -pgsm_ShmemSize(void) -{ +pgsm_ShmemSize(void) { Size sz = MAXALIGN(sizeof(pgsmSharedState)); sz = add_size(sz, MAX_QUERY_BUF); - #if USE_DYNAMIC_HASH - sz = add_size(sz, MAX_BUCKETS_MEM); - #else - sz = add_size(sz, hash_estimate_size(MAX_BUCKET_ENTRIES, sizeof(pgsmEntry))); - #endif +#if USE_DYNAMIC_HASH + sz = add_size(sz, MAX_BUCKETS_MEM); +#else + sz = add_size(sz, hash_estimate_size(MAX_BUCKET_ENTRIES, sizeof(pgsmEntry))); +#endif return MAXALIGN(sz); } @@ -72,91 +70,90 @@ pgsm_ShmemSize(void) * get allocated as a single shared memory chunk. */ static Size -pgsm_get_shared_area_size(void) -{ - Size sz; - #if USE_DYNAMIC_HASH - sz = pgsm_ShmemSize(); - #else - sz = MAXALIGN(sizeof(pgsmSharedState)); - sz = add_size(sz, pgsm_query_area_size()); - #endif - return sz; +pgsm_get_shared_area_size(void) { + Size sz; +#if USE_DYNAMIC_HASH + sz = pgsm_ShmemSize(); +#else + sz = MAXALIGN(sizeof(pgsmSharedState)); + sz = add_size(sz, pgsm_query_area_size()); +#endif + return sz; } void pgsm_startup(void) { - bool found = false; - pgsmSharedState *pgsm; - /* reset in case this is a restart within the postmaster */ - pgsmStateLocal.dsa = NULL; - pgsmStateLocal.shared_hash = NULL; - pgsmStateLocal.shared_pgsmState = NULL; + bool found = false; + pgsmSharedState *pgsm; + /* reset in case this is a restart within the postmaster */ + pgsmStateLocal.dsa = NULL; + pgsmStateLocal.shared_hash = NULL; + pgsmStateLocal.shared_pgsmState = NULL; + + /* + * Create or attach to the shared memory state, including hash table + */ + LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); + + pgsm = ShmemInitStruct("pg_stat_monitor", pgsm_get_shared_area_size(), &found); + if (!found) { + /* First time through ... */ + dsa_area *dsa; + char *p = (char *) pgsm; + + pgsm->pgsm_oom = false; + pgsm->lock = &(GetNamedLWLockTranche("pg_stat_monitor"))->lock; + SpinLockInit(&pgsm->mutex); + InitializeSharedState(pgsm); + /* the allocation of pgsmSharedState itself */ + p += MAXALIGN(sizeof(pgsmSharedState)); + pgsm->raw_dsa_area = p; + dsa = dsa_create_in_place(pgsm->raw_dsa_area, + pgsm_query_area_size(), + LWLockNewTrancheId(), 0); + dsa_pin(dsa); + dsa_set_size_limit(dsa, pgsm_query_area_size()); + + pgsm->hash_handle = pgsm_create_bucket_hash(pgsm, dsa); /* - * Create or attach to the shared memory state, including hash table + * If overflow is enabled, set the DSA size to unlimited, and allow + * the DSA to grow beyond the shared memory space into the swap area */ - LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); + if (pgsm_enable_overflow) + dsa_set_size_limit(dsa, -1); - pgsm = ShmemInitStruct("pg_stat_monitor", pgsm_get_shared_area_size(), &found); - if (!found) - { - /* First time through ... */ - dsa_area *dsa; - char *p = (char *) pgsm; - - pgsm->pgsm_oom = false; - pgsm->lock = &(GetNamedLWLockTranche("pg_stat_monitor"))->lock; - SpinLockInit(&pgsm->mutex); - InitializeSharedState(pgsm); - /* the allocation of pgsmSharedState itself */ - p += MAXALIGN(sizeof(pgsmSharedState)); - pgsm->raw_dsa_area = p; - dsa = dsa_create_in_place(pgsm->raw_dsa_area, - pgsm_query_area_size(), - LWLockNewTrancheId(), 0); - dsa_pin(dsa); - dsa_set_size_limit(dsa, pgsm_query_area_size()); - - pgsm->hash_handle = pgsm_create_bucket_hash(pgsm,dsa); - - /* If overflow is enabled, set the DSA size to unlimited, - * and allow the DSA to grow beyond the shared memory space - * into the swap area*/ - if (pgsm_enable_overflow) - dsa_set_size_limit(dsa, -1); - - pgsmStateLocal.shared_pgsmState = pgsm; - /* - * Postmaster will never access the dsa again, thus free it's local - * references. - */ - dsa_detach(dsa); - } + pgsmStateLocal.shared_pgsmState = pgsm; + /* + * Postmaster will never access the dsa again, thus free it's local + * references. + */ + dsa_detach(dsa); + } #ifdef BENCHMARK - init_hook_stats(); + init_hook_stats(); #endif - LWLockRelease(AddinShmemInitLock); + LWLockRelease(AddinShmemInitLock); - /* - * If we're in the postmaster (or a standalone backend...), set up a shmem - * exit hook to dump the statistics to disk. - */ - on_shmem_exit(pgsm_shmem_shutdown, (Datum) 0); + /* + * If we're in the postmaster (or a standalone backend...), set up a shmem + * exit hook to dump the statistics to disk. + */ + on_shmem_exit(pgsm_shmem_shutdown, (Datum) 0); } static void -InitializeSharedState(pgsmSharedState *pgsm) +InitializeSharedState(pgsmSharedState * pgsm) { - pg_atomic_init_u64(&pgsm->current_wbucket, 0); - pg_atomic_init_u64(&pgsm->prev_bucket_sec, 0); - memset(&pgsm->bucket_entry, 0, MAX_BUCKETS * sizeof(uint64)); - pgsm->pgsm_mem_cxt = AllocSetContextCreate(TopMemoryContext, - "pg_stat_monitor local store", - ALLOCSET_DEFAULT_SIZES); + pg_atomic_init_u64(&pgsm->current_wbucket, 0); + pg_atomic_init_u64(&pgsm->prev_bucket_sec, 0); + memset(&pgsm->bucket_entry, 0, MAX_BUCKETS * sizeof(uint64)); + pgsm->pgsm_mem_cxt = AllocSetContextCreate(TopMemoryContext, + "pg_stat_monitor local store", + ALLOCSET_DEFAULT_SIZES); } @@ -164,29 +161,28 @@ InitializeSharedState(pgsmSharedState *pgsm) * Create the classic or dshahs hash table for storing the query statistics. */ static PGSM_HASH_TABLE_HANDLE -pgsm_create_bucket_hash(pgsmSharedState *pgsm, dsa_area *dsa) -{ - PGSM_HASH_TABLE_HANDLE bucket_hash; +pgsm_create_bucket_hash(pgsmSharedState * pgsm, dsa_area * dsa) { + PGSM_HASH_TABLE_HANDLE bucket_hash; #if USE_DYNAMIC_HASH - dshash_table *dsh; - pgsm->hash_tranche_id = LWLockNewTrancheId(); - dsh_params.tranche_id = pgsm->hash_tranche_id; - dsh = dshash_create(dsa, &dsh_params, 0); - bucket_hash = dshash_get_hash_table_handle(dsh); - dshash_detach(dsh); + dshash_table *dsh; + pgsm->hash_tranche_id = LWLockNewTrancheId(); + dsh_params.tranche_id = pgsm->hash_tranche_id; + dsh = dshash_create(dsa, &dsh_params, 0); + bucket_hash = dshash_get_hash_table_handle(dsh); + dshash_detach(dsh); #else - HASHCTL info; - memset(&info, 0, sizeof(info)); - info.keysize = sizeof(pgsmHashKey); - info.entrysize = sizeof(pgsmEntry); - bucket_hash = ShmemInitHash("pg_stat_monitor: bucket hashtable", MAX_BUCKET_ENTRIES, MAX_BUCKET_ENTRIES, &info, HASH_ELEM | HASH_BLOBS); + HASHCTL info; + memset(&info, 0, sizeof(info)); + info.keysize = sizeof(pgsmHashKey); + info.entrysize = sizeof(pgsmEntry); + bucket_hash = ShmemInitHash("pg_stat_monitor: bucket hashtable", MAX_BUCKET_ENTRIES, MAX_BUCKET_ENTRIES, &info, HASH_ELEM | HASH_BLOBS); #endif - return bucket_hash; + return bucket_hash; } /* - * Attach to a DSA area created by the postmaster, in the case of + * Attach to a DSA area created by the postmaster, in the case of * USE_DYNAMIC_HASH, also attach the local dshash handle to * the dshash created by the postmaster. * @@ -197,53 +193,54 @@ pgsm_create_bucket_hash(pgsmSharedState *pgsm, dsa_area *dsa) void pgsm_attach_shmem(void) { - MemoryContext oldcontext; - if (pgsmStateLocal.dsa) - return; + MemoryContext oldcontext; + if (pgsmStateLocal.dsa) + return; - /* - * We want the dsa to remain valid throughout the lifecycle of this process. - * so switch to TopMemoryContext before attaching - */ - oldcontext = MemoryContextSwitchTo(TopMemoryContext); + /* + * We want the dsa to remain valid throughout the lifecycle of this + * process. so switch to TopMemoryContext before attaching + */ + oldcontext = MemoryContextSwitchTo(TopMemoryContext); - pgsmStateLocal.dsa = dsa_attach_in_place(pgsmStateLocal.shared_pgsmState->raw_dsa_area, - NULL); - /* pin the attached area to keep the area attached until end of - * session or explicit detach. - */ - dsa_pin_mapping(pgsmStateLocal.dsa); + pgsmStateLocal.dsa = dsa_attach_in_place(pgsmStateLocal.shared_pgsmState->raw_dsa_area, + NULL); + /* + * pin the attached area to keep the area attached until end of session or + * explicit detach. + */ + dsa_pin_mapping(pgsmStateLocal.dsa); #if USE_DYNAMIC_HASH - dsh_params.tranche_id = pgsmStateLocal.shared_pgsmState->hash_tranche_id; - pgsmStateLocal.shared_hash = dshash_attach(pgsmStateLocal.dsa, &dsh_params, - pgsmStateLocal.shared_pgsmState->hash_handle, 0); + dsh_params.tranche_id = pgsmStateLocal.shared_pgsmState->hash_tranche_id; + pgsmStateLocal.shared_hash = dshash_attach(pgsmStateLocal.dsa, &dsh_params, + pgsmStateLocal.shared_pgsmState->hash_handle, 0); #else - pgsmStateLocal.shared_hash = pgsmStateLocal.shared_pgsmState->hash_handle; + pgsmStateLocal.shared_hash = pgsmStateLocal.shared_pgsmState->hash_handle; #endif - MemoryContextSwitchTo(oldcontext); + MemoryContextSwitchTo(oldcontext); } -dsa_area* +dsa_area * get_dsa_area_for_query_text(void) { - pgsm_attach_shmem(); - return pgsmStateLocal.dsa; + pgsm_attach_shmem(); + return pgsmStateLocal.dsa; } -PGSM_HASH_TABLE* +PGSM_HASH_TABLE * get_pgsmHash(void) { - pgsm_attach_shmem(); - return pgsmStateLocal.shared_hash; + pgsm_attach_shmem(); + return pgsmStateLocal.shared_hash; } pgsmSharedState * pgsm_get_ss(void) { - pgsm_attach_shmem(); - return pgsmStateLocal.shared_pgsmState; + pgsm_attach_shmem(); + return pgsmStateLocal.shared_pgsmState; } @@ -256,48 +253,47 @@ pgsm_get_ss(void) void pgsm_shmem_shutdown(int code, Datum arg) { - /* Don't try to dump during a crash. */ - elog(LOG,"[pg_stat_monitor] pgsm_shmem_shutdown: Shutdown initiated."); + /* Don't try to dump during a crash. */ + elog(LOG, "[pg_stat_monitor] pgsm_shmem_shutdown: Shutdown initiated."); - if (code) - return; + if (code) + return; - pgsmStateLocal.shared_pgsmState = NULL; - /* Safety check ... shouldn't get here unless shmem is set up. */ - if (!IsHashInitialize()) - return; + pgsmStateLocal.shared_pgsmState = NULL; + /* Safety check ... shouldn't get here unless shmem is set up. */ + if (!IsHashInitialize()) + return; } -pgsmEntry * -hash_entry_alloc(pgsmSharedState *pgsm, pgsmHashKey *key, int encoding) +pgsmEntry * +hash_entry_alloc(pgsmSharedState * pgsm, pgsmHashKey * key, int encoding) { - pgsmEntry *entry = NULL; - bool found = false; - /* Find or create an entry with desired hash code */ - entry = (pgsmEntry*) pgsm_hash_find_or_insert(pgsmStateLocal.shared_hash, key, &found); - if (entry == NULL) - elog(DEBUG1, "[pg_stat_monitor] hash_entry_alloc: OUT OF MEMORY."); - else if (!found) - { - pgsm->bucket_entry[pg_atomic_read_u64(&pgsm->current_wbucket)]++; - /* New entry, initialize it */ - /* reset the statistics */ - memset(&entry->counters, 0, sizeof(Counters)); - entry->query_text.query_pos = InvalidDsaPointer; - entry->counters.info.parent_query = InvalidDsaPointer; + pgsmEntry *entry = NULL; + bool found = false; + /* Find or create an entry with desired hash code */ + entry = (pgsmEntry *) pgsm_hash_find_or_insert(pgsmStateLocal.shared_hash, key, &found); + if (entry == NULL) + elog(DEBUG1, "[pg_stat_monitor] hash_entry_alloc: OUT OF MEMORY."); + else if (!found) { + pgsm->bucket_entry[pg_atomic_read_u64(&pgsm->current_wbucket)]++; + /* New entry, initialize it */ + /* reset the statistics */ + memset(&entry->counters, 0, sizeof(Counters)); + entry->query_text.query_pos = InvalidDsaPointer; + entry->counters.info.parent_query = InvalidDsaPointer; - /* set the appropriate initial usage count */ - /* re-initialize the mutex each time ... we assume no one using it */ - SpinLockInit(&entry->mutex); - /* ... and don't forget the query text metadata */ - entry->encoding = encoding; - } - #if USE_DYNAMIC_HASH - if(entry) - dshash_release_lock(pgsmStateLocal.shared_hash, entry); - #endif + /* set the appropriate initial usage count */ + /* re-initialize the mutex each time ... we assume no one using it */ + SpinLockInit(&entry->mutex); + /* ... and don't forget the query text metadata */ + entry->encoding = encoding; + } +#if USE_DYNAMIC_HASH + if (entry) + dshash_release_lock(pgsmStateLocal.shared_hash, entry); +#endif - return entry; + return entry; } /* @@ -315,54 +311,52 @@ hash_entry_alloc(pgsmSharedState *pgsm, pgsmHashKey *key, int encoding) void hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer) { - PGSM_HASH_SEQ_STATUS hstat; - pgsmEntry *entry = NULL; - /* Store pending query ids from the previous bucket. */ + PGSM_HASH_SEQ_STATUS hstat; + pgsmEntry *entry = NULL; + /* Store pending query ids from the previous bucket. */ - if (!pgsmStateLocal.shared_hash) - return; + if (!pgsmStateLocal.shared_hash) + return; - /* Iterate over the hash table. */ - pgsm_hash_seq_init(&hstat, pgsmStateLocal.shared_hash, true); + /* Iterate over the hash table. */ + pgsm_hash_seq_init(&hstat, pgsmStateLocal.shared_hash, true); - while ((entry = pgsm_hash_seq_next(&hstat)) != NULL) - { - dsa_pointer pdsa; + while ((entry = pgsm_hash_seq_next(&hstat)) != NULL) { + dsa_pointer pdsa; - /* - * Remove all entries if new_bucket_id == -1. Otherwise remove entry - * in new_bucket_id if it has finished already. - */ - if (new_bucket_id < 0 || - (entry->key.bucket_id == new_bucket_id )) - { - dsa_pointer parent_qdsa = entry->counters.info.parent_query; - pdsa = entry->query_text.query_pos; + /* + * Remove all entries if new_bucket_id == -1. Otherwise remove entry + * in new_bucket_id if it has finished already. + */ + if (new_bucket_id < 0 || + (entry->key.bucket_id == new_bucket_id)) { + dsa_pointer parent_qdsa = entry->counters.info.parent_query; + pdsa = entry->query_text.query_pos; - pgsm_hash_delete_current(&hstat, pgsmStateLocal.shared_hash, &entry->key); + pgsm_hash_delete_current(&hstat, pgsmStateLocal.shared_hash, &entry->key); - if (DsaPointerIsValid(pdsa)) - dsa_free(pgsmStateLocal.dsa, pdsa); + if (DsaPointerIsValid(pdsa)) + dsa_free(pgsmStateLocal.dsa, pdsa); - if (DsaPointerIsValid(parent_qdsa)) - dsa_free(pgsmStateLocal.dsa, parent_qdsa); + if (DsaPointerIsValid(parent_qdsa)) + dsa_free(pgsmStateLocal.dsa, parent_qdsa); - pgsmStateLocal.shared_pgsmState->pgsm_oom = false; - } + pgsmStateLocal.shared_pgsmState->pgsm_oom = false; } - pgsm_hash_seq_term(&hstat); + } + pgsm_hash_seq_term(&hstat); } bool IsHashInitialize(void) { - return (pgsmStateLocal.shared_pgsmState != NULL); + return (pgsmStateLocal.shared_pgsmState != NULL); } bool IsSystemOOM(void) { - return (IsHashInitialize() && pgsmStateLocal.shared_pgsmState->pgsm_oom); + return (IsHashInitialize() && pgsmStateLocal.shared_pgsmState->pgsm_oom); } /* @@ -370,62 +364,62 @@ IsSystemOOM(void) * API and call the appropriate hash table function based on USE_DYNAMIC_HASH */ -void * -pgsm_hash_find_or_insert(PGSM_HASH_TABLE *shared_hash, pgsmHashKey *key, bool* found) -{ - #if USE_DYNAMIC_HASH - void *entry; - entry = dshash_find_or_insert(shared_hash, key, found); - return entry; - #else - return hash_search(shared_hash, key, HASH_ENTER_NULL, found); - #endif -} - -void * -pgsm_hash_find(PGSM_HASH_TABLE *shared_hash, pgsmHashKey *key, bool* found) -{ - #if USE_DYNAMIC_HASH - return dshash_find(shared_hash, key, false); - #else - return hash_search(shared_hash, key, HASH_FIND, found); - #endif -} - -void -pgsm_hash_seq_init(PGSM_HASH_SEQ_STATUS *hstat, PGSM_HASH_TABLE *shared_hash, bool lock) +void * +pgsm_hash_find_or_insert(PGSM_HASH_TABLE * shared_hash, pgsmHashKey * key, bool *found) { #if USE_DYNAMIC_HASH - dshash_seq_init(hstat, shared_hash, lock); + void *entry; + entry = dshash_find_or_insert(shared_hash, key, found); + return entry; #else - hash_seq_init(hstat, shared_hash); + return hash_search(shared_hash, key, HASH_ENTER_NULL, found); #endif } -void* -pgsm_hash_seq_next(PGSM_HASH_SEQ_STATUS *hstat) +void * +pgsm_hash_find(PGSM_HASH_TABLE * shared_hash, pgsmHashKey * key, bool *found) { #if USE_DYNAMIC_HASH - return dshash_seq_next(hstat); + return dshash_find(shared_hash, key, false); #else - return hash_seq_search(hstat); + return hash_search(shared_hash, key, HASH_FIND, found); #endif } void -pgsm_hash_seq_term(PGSM_HASH_SEQ_STATUS *hstat) +pgsm_hash_seq_init(PGSM_HASH_SEQ_STATUS * hstat, PGSM_HASH_TABLE * shared_hash, bool lock) { #if USE_DYNAMIC_HASH - dshash_seq_term(hstat); + dshash_seq_init(hstat, shared_hash, lock); +#else + hash_seq_init(hstat, shared_hash); +#endif +} + +void * +pgsm_hash_seq_next(PGSM_HASH_SEQ_STATUS * hstat) +{ +#if USE_DYNAMIC_HASH + return dshash_seq_next(hstat); +#else + return hash_seq_search(hstat); #endif } void -pgsm_hash_delete_current(PGSM_HASH_SEQ_STATUS *hstat, PGSM_HASH_TABLE *shared_hash, void *key) +pgsm_hash_seq_term(PGSM_HASH_SEQ_STATUS * hstat) { - #if USE_DYNAMIC_HASH - dshash_delete_current(hstat); - #else - hash_search(shared_hash, key, HASH_REMOVE, NULL); - #endif +#if USE_DYNAMIC_HASH + dshash_seq_term(hstat); +#endif +} + +void +pgsm_hash_delete_current(PGSM_HASH_SEQ_STATUS * hstat, PGSM_HASH_TABLE * shared_hash, void *key) +{ +#if USE_DYNAMIC_HASH + dshash_delete_current(hstat); +#else + hash_search(shared_hash, key, HASH_REMOVE, NULL); +#endif } diff --git a/pg_stat_monitor.c b/pg_stat_monitor.c index 78ae2c2..937a4b1 100644 --- a/pg_stat_monitor.c +++ b/pg_stat_monitor.c @@ -28,11 +28,10 @@ /* * Extension version number, for supporting older extension versions' objects */ - typedef enum pgsmVersion - { - PGSM_V1_0 = 0, - PGSM_V2_0 - } pgsmVersion; +typedef enum pgsmVersion { + PGSM_V1_0 = 0, + PGSM_V2_0 +} pgsmVersion; PG_MODULE_MAGIC; @@ -69,51 +68,51 @@ do \ (pgsm_client_ip != PGSM_INVALID_IP_MASK) /*---- Initicalization Function Declarations ----*/ -void _PG_init(void); +void _PG_init(void); /* Current nesting depth of ExecutorRun+ProcessUtility calls */ -static int exec_nested_level = 0; +static int exec_nested_level = 0; volatile bool __pgsm_do_not_capture_error = false; #if PG_VERSION_NUM >= 130000 -static int plan_nested_level = 0; +static int plan_nested_level = 0; #endif /* Histogram bucket variables */ static double hist_bucket_min; static double hist_bucket_max; static double hist_bucket_timings[MAX_RESPONSE_BUCKET + 2][2]; /* Start and end timings */ -static int hist_bucket_count_user; -static int hist_bucket_count_total; +static int hist_bucket_count_user; +static int hist_bucket_count_total; static uint32 pgsm_client_ip = PGSM_INVALID_IP_MASK; /* The array to store outer layer query id*/ uint64 *nested_queryids; -char **nested_query_txts; +char **nested_query_txts; List *lentries = NIL; /* Regex object used to extract query comments. */ static regex_t preg_query_comments; static char relations[REL_LST][REL_LEN]; -static int num_relations; /* Number of relation in the query */ +static int num_relations; /* Number of relation in the query */ static bool system_init = false; static struct rusage rusage_start; static struct rusage rusage_end; /* Application name and length; set each time when an entry is created locally */ -char app_name[APPLICATIONNAME_LEN]; -int app_name_len; +char app_name[APPLICATIONNAME_LEN]; +int app_name_len; /* Query buffer, store queries' text. */ -static char *pgsm_explain(QueryDesc *queryDesc); +static char *pgsm_explain(QueryDesc * queryDesc); static void extract_query_comments(const char *query, char *comments, size_t max_len); static void set_histogram_bucket_timings(void); static void histogram_bucket_timings(int index, double *b_start, double *b_end); -static int get_histogram_bucket(double q_time); +static int get_histogram_bucket(double q_time); static bool IsSystemInitialized(void); static double time_diff(struct timeval end, struct timeval start); @@ -137,7 +136,7 @@ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; static ProcessUtility_hook_type prev_ProcessUtility = NULL; static emit_log_hook_type prev_emit_log_hook = NULL; -DECLARE_HOOK(void pgsm_emit_log_hook, ErrorData *edata); +DECLARE_HOOK(void pgsm_emit_log_hook, ErrorData * edata); static shmem_startup_hook_type prev_shmem_startup_hook = NULL; static ExecutorCheckPerms_hook_type prev_ExecutorCheckPerms_hook = NULL; @@ -150,47 +149,47 @@ PG_FUNCTION_INFO_V1(get_histogram_timings); PG_FUNCTION_INFO_V1(pg_stat_monitor_hook_stats); static uint pg_get_client_addr(bool *ok); -static int pg_get_application_name(char *name, int buff_size); -static PgBackendStatus *pg_get_backend_status(void); +static int pg_get_application_name(char *name, int buff_size); +static PgBackendStatus * pg_get_backend_status(void); static Datum intarray_get_datum(int32 arr[], int len); #if PG_VERSION_NUM < 140000 -DECLARE_HOOK(void pgsm_post_parse_analyze, ParseState *pstate, Query *query); +DECLARE_HOOK(void pgsm_post_parse_analyze, ParseState * pstate, Query * query); #else -DECLARE_HOOK(void pgsm_post_parse_analyze, ParseState *pstate, Query *query, JumbleState *jstate); +DECLARE_HOOK(void pgsm_post_parse_analyze, ParseState * pstate, Query * query, JumbleState * jstate); #endif -DECLARE_HOOK(void pgsm_ExecutorStart, QueryDesc *queryDesc, int eflags); -DECLARE_HOOK(void pgsm_ExecutorRun, QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once); -DECLARE_HOOK(void pgsm_ExecutorFinish, QueryDesc *queryDesc); -DECLARE_HOOK(void pgsm_ExecutorEnd, QueryDesc *queryDesc); -DECLARE_HOOK(bool pgsm_ExecutorCheckPerms, List *rt, bool abort); +DECLARE_HOOK(void pgsm_ExecutorStart, QueryDesc * queryDesc, int eflags); +DECLARE_HOOK(void pgsm_ExecutorRun, QueryDesc * queryDesc, ScanDirection direction, uint64 count, bool execute_once); +DECLARE_HOOK(void pgsm_ExecutorFinish, QueryDesc * queryDesc); +DECLARE_HOOK(void pgsm_ExecutorEnd, QueryDesc * queryDesc); +DECLARE_HOOK(bool pgsm_ExecutorCheckPerms, List * rt, bool abort); #if PG_VERSION_NUM >= 140000 -DECLARE_HOOK(PlannedStmt *pgsm_planner_hook, Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams); -DECLARE_HOOK(void pgsm_ProcessUtility, PlannedStmt *pstmt, const char *queryString, - bool readOnlyTree, - ProcessUtilityContext context, - ParamListInfo params, QueryEnvironment *queryEnv, - DestReceiver *dest, - QueryCompletion *qc); +DECLARE_HOOK(PlannedStmt * pgsm_planner_hook, Query * parse, const char *query_string, int cursorOptions, ParamListInfo boundParams); +DECLARE_HOOK(void pgsm_ProcessUtility, PlannedStmt * pstmt, const char *queryString, + bool readOnlyTree, + ProcessUtilityContext context, + ParamListInfo params, QueryEnvironment * queryEnv, + DestReceiver * dest, + QueryCompletion * qc); #elif PG_VERSION_NUM >= 130000 -DECLARE_HOOK(PlannedStmt *pgsm_planner_hook, Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams); -DECLARE_HOOK(void pgsm_ProcessUtility, PlannedStmt *pstmt, const char *queryString, - ProcessUtilityContext context, - ParamListInfo params, QueryEnvironment *queryEnv, - DestReceiver *dest, - QueryCompletion *qc); +DECLARE_HOOK(PlannedStmt * pgsm_planner_hook, Query * parse, const char *query_string, int cursorOptions, ParamListInfo boundParams); +DECLARE_HOOK(void pgsm_ProcessUtility, PlannedStmt * pstmt, const char *queryString, + ProcessUtilityContext context, + ParamListInfo params, QueryEnvironment * queryEnv, + DestReceiver * dest, + QueryCompletion * qc); #else -static void BufferUsageAccumDiff(BufferUsage *bufusage, BufferUsage *pgBufferUsage, BufferUsage *bufusage_start); +static void BufferUsageAccumDiff(BufferUsage * bufusage, BufferUsage * pgBufferUsage, BufferUsage * bufusage_start); -DECLARE_HOOK(void pgsm_ProcessUtility, PlannedStmt *pstmt, const char *queryString, - ProcessUtilityContext context, ParamListInfo params, - QueryEnvironment *queryEnv, - DestReceiver *dest, - char *completionTag); +DECLARE_HOOK(void pgsm_ProcessUtility, PlannedStmt * pstmt, const char *queryString, + ProcessUtilityContext context, ParamListInfo params, + QueryEnvironment * queryEnv, + DestReceiver * dest, + char *completionTag); #endif -static uint64 pgsm_hash_string(const char *str, int len); +static uint64 pgsm_hash_string(const char *str, int len); char *unpack_sql_state(int sql_state); #define PGSM_HANDLED_UTILITY(n) (!IsA(n, ExecuteStmt) && \ @@ -198,65 +197,65 @@ char *unpack_sql_state(int sql_state); !IsA(n, DeallocateStmt)) -static pgsmEntry *pgsm_create_hash_entry(uint64 bucket_id, uint64 queryid, PlanInfo *plan_info); -static void pgsm_add_to_list(pgsmEntry *entry, char *query_text, int query_len); -static pgsmEntry* pgsm_get_entry_for_query(uint64 queryid, PlanInfo *plan_info, const char* query_text, int query_len, bool create); +static pgsmEntry * pgsm_create_hash_entry(uint64 bucket_id, uint64 queryid, PlanInfo * plan_info); +static void pgsm_add_to_list(pgsmEntry * entry, char *query_text, int query_len); +static pgsmEntry * pgsm_get_entry_for_query(uint64 queryid, PlanInfo * plan_info, const char *query_text, int query_len, bool create); static uint64 get_pgsm_query_id_hash(const char *norm_query, int len); static void pgsm_cleanup_callback(void *arg); -static void pgsm_store_error(const char *query, ErrorData *edata); +static void pgsm_store_error(const char *query, ErrorData * edata); /*---- Local variables ----*/ MemoryContextCallback mem_cxt_reset_callback = - { - .func = pgsm_cleanup_callback, - .arg = NULL - }; +{ + .func = pgsm_cleanup_callback, + .arg = NULL +}; volatile bool callback_setup = false; -static void pgsm_update_entry(pgsmEntry *entry, - const char *query, - char *comments, - int comments_len, - PlanInfo * plan_info, - SysInfo * sys_info, - ErrorInfo * error_info, - double plan_total_time, - double exec_total_time, - uint64 rows, - BufferUsage *bufusage, - WalUsage *walusage, - const struct JitInstrumentation *jitusage, - bool reset, - pgsmStoreKind kind); -static void pgsm_store(pgsmEntry *entry); +static void pgsm_update_entry(pgsmEntry * entry, + const char *query, + char *comments, + int comments_len, + PlanInfo * plan_info, + SysInfo * sys_info, + ErrorInfo * error_info, + double plan_total_time, + double exec_total_time, + uint64 rows, + BufferUsage * bufusage, + WalUsage * walusage, + const struct JitInstrumentation *jitusage, + bool reset, + pgsmStoreKind kind); +static void pgsm_store(pgsmEntry * entry); static void pg_stat_monitor_internal(FunctionCallInfo fcinfo, - pgsmVersion api_version, - bool showtext); + pgsmVersion api_version, + bool showtext); #if PG_VERSION_NUM < 140000 -static void AppendJumble(JumbleState *jstate, - const unsigned char *item, Size size); -static void JumbleQuery(JumbleState *jstate, Query *query); -static void JumbleRangeTable(JumbleState *jstate, List *rtable, CmdType cmd_type); -static void JumbleExpr(JumbleState *jstate, Node *node); -static void RecordConstLocation(JumbleState *jstate, int location); +static void AppendJumble(JumbleState * jstate, + const unsigned char *item, Size size); +static void JumbleQuery(JumbleState * jstate, Query * query); +static void JumbleRangeTable(JumbleState * jstate, List * rtable, CmdType cmd_type); +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); -static uint64 get_query_id(JumbleState *jstate, Query *query); +static uint64 get_query_id(JumbleState * jstate, Query * query); #endif -static char *generate_normalized_query(JumbleState *jstate, const char *query, - int query_loc, int *query_len_p, int encoding); -static void fill_in_constant_lengths(JumbleState *jstate, const char *query, int query_loc); -static int comp_location(const void *a, const void *b); +static char *generate_normalized_query(JumbleState * jstate, const char *query, + int query_loc, int *query_len_p, int encoding); +static void fill_in_constant_lengths(JumbleState * jstate, const char *query, int query_loc); +static int comp_location(const void *a, const void *b); -static uint64 get_next_wbucket(pgsmSharedState *pgsm); +static uint64 get_next_wbucket(pgsmSharedState * pgsm); /* * Module load callback @@ -265,83 +264,82 @@ static uint64 get_next_wbucket(pgsmSharedState *pgsm); void _PG_init(void) { - int rc; + int rc; - elog(DEBUG2, "[pg_stat_monitor] pg_stat_monitor: %s().", __FUNCTION__); + elog(DEBUG2, "[pg_stat_monitor] pg_stat_monitor: %s().", __FUNCTION__); - /* - * In order to create our shared memory area, we have to be loaded via - * shared_preload_libraries. If not, fall out without hooking into any of - * the main system. (We don't throw error here because it seems useful to - * allow the pg_stat_monitor functions to be created even when the - * module isn't active. The functions must protect themselves against - * being called then, however.) - */ - if (!process_shared_preload_libraries_in_progress) - return; + /* + * In order to create our shared memory area, we have to be loaded via + * shared_preload_libraries. If not, fall out without hooking into any of + * the main system. (We don't throw error here because it seems useful to + * allow the pg_stat_monitor functions to be created even when the module + * isn't active. The functions must protect themselves against being + * called then, however.) + */ + if (!process_shared_preload_libraries_in_progress) + return; - /* Inilize the GUC variables */ - init_guc(); + /* Inilize the GUC variables */ + init_guc(); - set_histogram_bucket_timings(); + set_histogram_bucket_timings(); #if PG_VERSION_NUM >= 140000 - /* - * Inform the postmaster that we want to enable query_id calculation if - * compute_query_id is set to auto. - */ - EnableQueryId(); + /* + * Inform the postmaster that we want to enable query_id calculation if + * compute_query_id is set to auto. + */ + EnableQueryId(); #endif - EmitWarningsOnPlaceholders("pg_stat_monitor"); + EmitWarningsOnPlaceholders("pg_stat_monitor"); - /* - * Compile regular expression for extracting out query comments only once. - */ - rc = regcomp(&preg_query_comments, "/\\*([^*]|[\r\n]|(\\*+([^*/]|[\r\n])))*\\*+/", REG_EXTENDED); - if (rc != 0) - { - elog(ERROR, "[pg_stat_monitor] _PG_init: query comments regcomp() failed, return code=(%d).", rc); - } + /* + * Compile regular expression for extracting out query comments only once. + */ + rc = regcomp(&preg_query_comments, "/\\*([^*]|[\r\n]|(\\*+([^*/]|[\r\n])))*\\*+/", REG_EXTENDED); + if (rc != 0) { + elog(ERROR, "[pg_stat_monitor] _PG_init: query comments regcomp() failed, return code=(%d).", rc); + } - /* - * Install hooks. - */ + /* + * Install hooks. + */ #if PG_VERSION_NUM >= 150000 - prev_shmem_request_hook = shmem_request_hook; - shmem_request_hook = pgsm_shmem_request; + prev_shmem_request_hook = shmem_request_hook; + shmem_request_hook = pgsm_shmem_request; #else - request_additional_shared_resources(); + request_additional_shared_resources(); #endif - prev_shmem_startup_hook = shmem_startup_hook; - shmem_startup_hook = pgsm_shmem_startup; - prev_post_parse_analyze_hook = post_parse_analyze_hook; - post_parse_analyze_hook = HOOK(pgsm_post_parse_analyze); - prev_ExecutorStart = ExecutorStart_hook; - ExecutorStart_hook = HOOK(pgsm_ExecutorStart); - prev_ExecutorRun = ExecutorRun_hook; - ExecutorRun_hook = HOOK(pgsm_ExecutorRun); - prev_ExecutorFinish = ExecutorFinish_hook; - ExecutorFinish_hook = HOOK(pgsm_ExecutorFinish); - prev_ExecutorEnd = ExecutorEnd_hook; - ExecutorEnd_hook = HOOK(pgsm_ExecutorEnd); - prev_ProcessUtility = ProcessUtility_hook; - ProcessUtility_hook = HOOK(pgsm_ProcessUtility); + prev_shmem_startup_hook = shmem_startup_hook; + shmem_startup_hook = pgsm_shmem_startup; + prev_post_parse_analyze_hook = post_parse_analyze_hook; + post_parse_analyze_hook = HOOK(pgsm_post_parse_analyze); + prev_ExecutorStart = ExecutorStart_hook; + ExecutorStart_hook = HOOK(pgsm_ExecutorStart); + prev_ExecutorRun = ExecutorRun_hook; + ExecutorRun_hook = HOOK(pgsm_ExecutorRun); + prev_ExecutorFinish = ExecutorFinish_hook; + ExecutorFinish_hook = HOOK(pgsm_ExecutorFinish); + prev_ExecutorEnd = ExecutorEnd_hook; + ExecutorEnd_hook = HOOK(pgsm_ExecutorEnd); + prev_ProcessUtility = ProcessUtility_hook; + ProcessUtility_hook = HOOK(pgsm_ProcessUtility); #if PG_VERSION_NUM >= 130000 - planner_hook_next = planner_hook; - planner_hook = HOOK(pgsm_planner_hook); + planner_hook_next = planner_hook; + planner_hook = HOOK(pgsm_planner_hook); #endif - prev_emit_log_hook = emit_log_hook; - emit_log_hook = HOOK(pgsm_emit_log_hook); - prev_ExecutorCheckPerms_hook = ExecutorCheckPerms_hook; - ExecutorCheckPerms_hook = HOOK(pgsm_ExecutorCheckPerms); + prev_emit_log_hook = emit_log_hook; + emit_log_hook = HOOK(pgsm_emit_log_hook); + prev_ExecutorCheckPerms_hook = ExecutorCheckPerms_hook; + ExecutorCheckPerms_hook = HOOK(pgsm_ExecutorCheckPerms); - nested_queryids = (uint64 *) malloc(sizeof(uint64) * max_stack_depth); - nested_query_txts = (char **) malloc(sizeof(char*) * max_stack_depth); + nested_queryids = (uint64 *) malloc(sizeof(uint64) * max_stack_depth); + nested_query_txts = (char **) malloc(sizeof(char *) * max_stack_depth); - system_init = true; + system_init = true; } /* @@ -353,30 +351,29 @@ _PG_init(void) void pgsm_shmem_startup(void) { - if (prev_shmem_startup_hook) - prev_shmem_startup_hook(); + if (prev_shmem_startup_hook) + prev_shmem_startup_hook(); - pgsm_startup(); + pgsm_startup(); } static void request_additional_shared_resources(void) { - /* - * Request additional shared resources. (These are no-ops if we're not in - * the postmaster process.) We'll allocate or attach to the shared - * resources in pgsm_shmem_startup(). - */ - RequestAddinShmemSpace(pgsm_ShmemSize() + HOOK_STATS_SIZE); - RequestNamedLWLockTranche("pg_stat_monitor", 1); + /* + * Request additional shared resources. (These are no-ops if we're not in + * the postmaster process.) We'll allocate or attach to the shared + * resources in pgsm_shmem_startup(). + */ + RequestAddinShmemSpace(pgsm_ShmemSize() + HOOK_STATS_SIZE); + RequestNamedLWLockTranche("pg_stat_monitor", 1); } /* * Select the version of pg_stat_monitor. */ Datum -pg_stat_monitor_version(PG_FUNCTION_ARGS) -{ - PG_RETURN_TEXT_P(cstring_to_text(BUILD_VERSION)); +pg_stat_monitor_version(PG_FUNCTION_ARGS) { + PG_RETURN_TEXT_P(cstring_to_text(BUILD_VERSION)); } #if PG_VERSION_NUM >= 150000 @@ -387,129 +384,128 @@ pg_stat_monitor_version(PG_FUNCTION_ARGS) static void pgsm_shmem_request(void) { - if (prev_shmem_request_hook) - prev_shmem_request_hook(); - request_additional_shared_resources(); + if (prev_shmem_request_hook) + prev_shmem_request_hook(); + request_additional_shared_resources(); } #endif static void -pgsm_post_parse_analyze_internal(ParseState *pstate, Query *query, JumbleState *jstate) +pgsm_post_parse_analyze_internal(ParseState * pstate, Query * query, JumbleState * jstate) { - pgsmEntry *entry; - const char *query_text; - char *norm_query = NULL; - int norm_query_len; - int location; - int query_len; + pgsmEntry *entry; + const char *query_text; + char *norm_query = NULL; + int norm_query_len; + int location; + int query_len; - /* Safety check... */ - if (!IsSystemInitialized()) - return; - - if (callback_setup == false) - { - /* If MessageContext is valid setup a callback to cleanup - * our local stats list when the MessagContext gets reset - */ - if (MemoryContextIsValid(MessageContext)) - { - MemoryContextRegisterResetCallback(MessageContext, &mem_cxt_reset_callback); - callback_setup = true; - } - } - - if (!pgsm_enabled(exec_nested_level)) - return; + /* Safety check... */ + if (!IsSystemInitialized()) + return; + if (callback_setup == false) { /* - * Clear queryId for prepared statements related utility, as those will - * inherit from the underlying statement's one (except DEALLOCATE which is - * entirely untracked). + * If MessageContext is valid setup a callback to cleanup our local + * stats list when the MessagContext gets reset */ - if (query->utilityStmt) - { - if (pgsm_track_utility && !PGSM_HANDLED_UTILITY(query->utilityStmt)) - query->queryId = UINT64CONST(0); - - return; + if (MemoryContextIsValid(MessageContext)) { + MemoryContextRegisterResetCallback(MessageContext, &mem_cxt_reset_callback); + callback_setup = true; } + } - /* - * Let's calculate queryid for versions 13 and below. We don't have to check - * that jstate is valid, it always will be for these versions. - */ + if (!pgsm_enabled(exec_nested_level)) + return; + + /* + * Clear queryId for prepared statements related utility, as those will + * inherit from the underlying statement's one (except DEALLOCATE which is + * entirely untracked). + */ + if (query->utilityStmt) { + if (pgsm_track_utility && !PGSM_HANDLED_UTILITY(query->utilityStmt)) + query->queryId = UINT64CONST(0); + + return; + } + + /* + * Let's calculate queryid for versions 13 and below. We don't have to + * check that jstate is valid, it always will be for these versions. + */ #if PG_VERSION_NUM < 140000 - query->queryId = get_query_id(jstate, query); + query->queryId = get_query_id(jstate, query); #endif - /* - * If we are unlucky enough to get a hash of zero, use 1 instead, to - * prevent confusion with the utility-statement case. - */ - if (query->queryId == UINT64CONST(0)) - query->queryId = UINT64CONST(1); + /* + * If we are unlucky enough to get a hash of zero, use 1 instead, to + * prevent confusion with the utility-statement case. + */ + if (query->queryId == UINT64CONST(0)) + query->queryId = UINT64CONST(1); - /* - * Let's save the normalized query so that we can save the data without in - * hash later on without the need of jstate which wouldn't be available. - */ - query_text = pstate->p_sourcetext; - location = query->stmt_location; - query_len = query->stmt_len; + /* + * Let's save the normalized query so that we can save the data without in + * hash later on without the need of jstate which wouldn't be available. + */ + query_text = pstate->p_sourcetext; + location = query->stmt_location; + query_len = query->stmt_len; - /* We should always have a valid query. */ - query_text = CleanQuerytext(query_text, &location, &query_len); - Assert(query_text); + /* We should always have a valid query. */ + query_text = CleanQuerytext(query_text, &location, &query_len); + Assert(query_text); - norm_query_len = query_len; + norm_query_len = query_len; - /* Generate a normalized query */ - if (jstate && jstate->clocations_count > 0 && (pgsm_enable_pgsm_query_id || pgsm_normalized_query) ) - { - norm_query = generate_normalized_query(jstate, - query_text, /* query */ - location, /* query location */ - &norm_query_len, - GetDatabaseEncoding()); + /* Generate a normalized query */ + if (jstate && jstate->clocations_count > 0 && (pgsm_enable_pgsm_query_id || pgsm_normalized_query)) { + norm_query = generate_normalized_query(jstate, + query_text, /* query */ + location, /* query location */ + &norm_query_len, + GetDatabaseEncoding()); - Assert(norm_query); - } + Assert(norm_query); + } - /* - * At this point, we don't know which bucket this query will land in, so passing - * 0. The store function MUST later update it based on the current bucket value. - * The correct bucket value will be needed then to search the hash table, or create - * the appropriate entry. - */ - entry = pgsm_create_hash_entry(0, query->queryId, NULL); + /* + * At this point, we don't know which bucket this query will land in, so + * passing 0. The store function MUST later update it based on the current + * bucket value. The correct bucket value will be needed then to search + * the hash table, or create the appropriate entry. + */ + entry = pgsm_create_hash_entry(0, query->queryId, NULL); - /* Update other member that are not counters, so that we don't have to worry about these. */ - entry->pgsm_query_id = get_pgsm_query_id_hash(norm_query ? norm_query : query_text, norm_query_len); - entry->counters.info.cmd_type = query->commandType; + /* + * Update other member that are not counters, so that we don't have to + * worry about these. + */ + entry->pgsm_query_id = get_pgsm_query_id_hash(norm_query ? norm_query : query_text, norm_query_len); + entry->counters.info.cmd_type = query->commandType; - /* - * Add the query text and entry to the local list. - * - * Preserve the normalized query if needed and we got a valid one. - * Otherwise, store the actual query so that we don't have to check - * what query to store when saving into the hash. - * - * In case of query_text, request the function to duplicate it so that - * it is put in the relevant memory context. - */ - if (pgsm_normalized_query && norm_query) - pgsm_add_to_list(entry, norm_query, norm_query_len); - else - { - pgsm_add_to_list(entry, (char *)query_text, query_len); - } + /* + * Add the query text and entry to the local list. + * + * Preserve the normalized query if needed and we got a valid one. + * Otherwise, store the actual query so that we don't have to check what + * query to store when saving into the hash. + * + * In case of query_text, request the function to duplicate it so that it + * is put in the relevant memory context. + */ + if (pgsm_normalized_query && norm_query) + pgsm_add_to_list(entry, norm_query, norm_query_len); + else { + pgsm_add_to_list(entry, (char *) query_text, query_len); + } - /* Check that we've not exceeded max_stack_depth */ - Assert(list_length(lentries) <= max_stack_depth); + /* Check that we've not exceeded max_stack_depth */ + Assert(list_length(lentries) <= max_stack_depth); - if (norm_query) - pfree(norm_query); + if (norm_query) + pfree(norm_query); } #if PG_VERSION_NUM >= 140000 @@ -517,26 +513,26 @@ pgsm_post_parse_analyze_internal(ParseState *pstate, Query *query, JumbleState * * Post-parse-analysis hook: mark query with a queryId */ static void -pgsm_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate) +pgsm_post_parse_analyze(ParseState * pstate, Query * query, JumbleState * jstate) { - if (prev_post_parse_analyze_hook) - prev_post_parse_analyze_hook(pstate, query, jstate); + if (prev_post_parse_analyze_hook) + prev_post_parse_analyze_hook(pstate, query, jstate); - pgsm_post_parse_analyze_internal(pstate, query, jstate); + pgsm_post_parse_analyze_internal(pstate, query, jstate); } #else /* * Post-parse-analysis hook: mark query with a queryId */ static void -pgsm_post_parse_analyze(ParseState *pstate, Query *query) +pgsm_post_parse_analyze(ParseState * pstate, Query * query) { - JumbleState jstate; + JumbleState jstate; - if (prev_post_parse_analyze_hook) - prev_post_parse_analyze_hook(pstate, query); + if (prev_post_parse_analyze_hook) + prev_post_parse_analyze_hook(pstate, query); - pgsm_post_parse_analyze_internal(pstate, query, &jstate); + pgsm_post_parse_analyze_internal(pstate, query, &jstate); } #endif @@ -544,42 +540,40 @@ pgsm_post_parse_analyze(ParseState *pstate, Query *query) * ExecutorStart hook: start up tracking if needed */ static void -pgsm_ExecutorStart(QueryDesc *queryDesc, int eflags) +pgsm_ExecutorStart(QueryDesc * queryDesc, int eflags) { - if (getrusage(RUSAGE_SELF, &rusage_start) != 0) - elog(DEBUG1, "[pg_stat_monitor] pgsm_ExecutorStart: failed to execute getrusage."); + if (getrusage(RUSAGE_SELF, &rusage_start) != 0) + elog(DEBUG1, "[pg_stat_monitor] pgsm_ExecutorStart: failed to execute getrusage."); - if (prev_ExecutorStart) - prev_ExecutorStart(queryDesc, eflags); - else - standard_ExecutorStart(queryDesc, eflags); + if (prev_ExecutorStart) + prev_ExecutorStart(queryDesc, eflags); + else + standard_ExecutorStart(queryDesc, eflags); + /* + * If query has queryId zero, don't track it. This prevents double + * counting of optimizable statements that are directly contained in + * utility statements. + */ + if (pgsm_enabled(exec_nested_level) && + queryDesc->plannedstmt->queryId != UINT64CONST(0)) { /* - * If query has queryId zero, don't track it. This prevents double - * counting of optimizable statements that are directly contained in - * utility statements. + * Set up to track total elapsed time in ExecutorRun. Make sure the + * space is allocated in the per-query context so it will go away at + * ExecutorEnd. */ - if (pgsm_enabled(exec_nested_level) && - queryDesc->plannedstmt->queryId != UINT64CONST(0)) - { - /* - * Set up to track total elapsed time in ExecutorRun. Make sure the - * space is allocated in the per-query context so it will go away at - * ExecutorEnd. - */ - if (queryDesc->totaltime == NULL) - { - MemoryContext oldcxt; + if (queryDesc->totaltime == NULL) { + MemoryContext oldcxt; - oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt); + oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt); #if PG_VERSION_NUM < 140000 - queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL); + queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL); #else - queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false); + queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false); #endif - MemoryContextSwitchTo(oldcxt); - } + MemoryContextSwitchTo(oldcxt); } + } } @@ -587,338 +581,323 @@ pgsm_ExecutorStart(QueryDesc *queryDesc, int eflags) * ExecutorRun hook: all we need do is track nesting depth */ static void -pgsm_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, - bool execute_once) +pgsm_ExecutorRun(QueryDesc * queryDesc, ScanDirection direction, uint64 count, + bool execute_once) { - if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) - { - nested_queryids[exec_nested_level] = queryDesc->plannedstmt->queryId; - nested_query_txts[exec_nested_level] = strdup(queryDesc->sourceText); - } + if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) { + nested_queryids[exec_nested_level] = queryDesc->plannedstmt->queryId; + nested_query_txts[exec_nested_level] = strdup(queryDesc->sourceText); + } - exec_nested_level++; - PG_TRY(); - { - if (prev_ExecutorRun) - prev_ExecutorRun(queryDesc, direction, count, execute_once); - else - standard_ExecutorRun(queryDesc, direction, count, execute_once); - exec_nested_level--; - if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) - { - nested_queryids[exec_nested_level] = UINT64CONST(0); - if(nested_query_txts[exec_nested_level]) - free(nested_query_txts[exec_nested_level]); - nested_query_txts[exec_nested_level] = NULL; - } + exec_nested_level++; + PG_TRY(); + { + if (prev_ExecutorRun) + prev_ExecutorRun(queryDesc, direction, count, execute_once); + else + standard_ExecutorRun(queryDesc, direction, count, execute_once); + exec_nested_level--; + if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) { + nested_queryids[exec_nested_level] = UINT64CONST(0); + if (nested_query_txts[exec_nested_level]) + free(nested_query_txts[exec_nested_level]); + nested_query_txts[exec_nested_level] = NULL; } - PG_CATCH(); - { - exec_nested_level--; - if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) - { - nested_queryids[exec_nested_level] = UINT64CONST(0); - if(nested_query_txts[exec_nested_level]) - free(nested_query_txts[exec_nested_level]); - nested_query_txts[exec_nested_level] = NULL; - } - PG_RE_THROW(); + } + PG_CATCH(); + { + exec_nested_level--; + if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) { + nested_queryids[exec_nested_level] = UINT64CONST(0); + if (nested_query_txts[exec_nested_level]) + free(nested_query_txts[exec_nested_level]); + nested_query_txts[exec_nested_level] = NULL; } - PG_END_TRY(); + PG_RE_THROW(); + } + PG_END_TRY(); } /* * ExecutorFinish hook: all we need do is track nesting depth */ static void -pgsm_ExecutorFinish(QueryDesc *queryDesc) +pgsm_ExecutorFinish(QueryDesc * queryDesc) { - exec_nested_level++; + exec_nested_level++; - PG_TRY(); - { - if (prev_ExecutorFinish) - prev_ExecutorFinish(queryDesc); - else - standard_ExecutorFinish(queryDesc); - exec_nested_level--; - } - PG_CATCH(); - { - exec_nested_level--; - PG_RE_THROW(); - } - PG_END_TRY(); + PG_TRY(); + { + if (prev_ExecutorFinish) + prev_ExecutorFinish(queryDesc); + else + standard_ExecutorFinish(queryDesc); + exec_nested_level--; + } + PG_CATCH(); + { + exec_nested_level--; + PG_RE_THROW(); + } + PG_END_TRY(); } static char * -pgsm_explain(QueryDesc *queryDesc) +pgsm_explain(QueryDesc * queryDesc) { - ExplainState *es = NewExplainState(); + ExplainState *es = NewExplainState(); - es->buffers = false; - es->analyze = false; - es->verbose = false; - es->costs = false; - es->format = EXPLAIN_FORMAT_TEXT; + es->buffers = false; + es->analyze = false; + es->verbose = false; + es->costs = false; + es->format = EXPLAIN_FORMAT_TEXT; - ExplainBeginOutput(es); - ExplainPrintPlan(es, queryDesc); - ExplainEndOutput(es); + ExplainBeginOutput(es); + ExplainPrintPlan(es, queryDesc); + ExplainEndOutput(es); - if (es->str->len > 0 && es->str->data[es->str->len - 1] == '\n') - es->str->data[--es->str->len] = '\0'; - return es->str->data; + if (es->str->len > 0 && es->str->data[es->str->len - 1] == '\n') + es->str->data[--es->str->len] = '\0'; + return es->str->data; } /* * ExecutorEnd hook: store results if needed */ static void -pgsm_ExecutorEnd(QueryDesc *queryDesc) +pgsm_ExecutorEnd(QueryDesc * queryDesc) { - uint64 queryId = queryDesc->plannedstmt->queryId; - SysInfo sys_info; - PlanInfo plan_info; - PlanInfo *plan_ptr = NULL; - pgsmEntry *entry = NULL; + uint64 queryId = queryDesc->plannedstmt->queryId; + SysInfo sys_info; + PlanInfo plan_info; + PlanInfo *plan_ptr = NULL; + pgsmEntry *entry = NULL; - /* Extract the plan information in case of SELECT statement */ - if (queryDesc->operation == CMD_SELECT && pgsm_enable_query_plan) - { - plan_info.plan_len = snprintf(plan_info.plan_text, PLAN_TEXT_LEN, "%s", pgsm_explain(queryDesc)); - plan_info.planid = pgsm_hash_string(plan_info.plan_text, plan_info.plan_len); - plan_ptr = &plan_info; + /* Extract the plan information in case of SELECT statement */ + if (queryDesc->operation == CMD_SELECT && pgsm_enable_query_plan) { + plan_info.plan_len = snprintf(plan_info.plan_text, PLAN_TEXT_LEN, "%s", pgsm_explain(queryDesc)); + plan_info.planid = pgsm_hash_string(plan_info.plan_text, plan_info.plan_len); + plan_ptr = &plan_info; + } + + if (queryId != UINT64CONST(0) && queryDesc->totaltime && pgsm_enabled(exec_nested_level)) { + entry = pgsm_get_entry_for_query(queryId, plan_ptr, (char *) queryDesc->sourceText, strlen(queryDesc->sourceText), true); + if (!entry) { + elog(DEBUG2, "[pg_stat_monitor] pgsm_ExecutorEnd: Failed to find entry for [%lu] %s.", queryId, queryDesc->sourceText); + return; } - if (queryId != UINT64CONST(0) && queryDesc->totaltime && pgsm_enabled(exec_nested_level)) - { - entry = pgsm_get_entry_for_query(queryId, plan_ptr, (char *)queryDesc->sourceText, strlen(queryDesc->sourceText), true); - if(!entry) - { - elog(DEBUG2,"[pg_stat_monitor] pgsm_ExecutorEnd: Failed to find entry for [%lu] %s.",queryId, queryDesc->sourceText); - return; - } + if (entry->key.planid == 0) + entry->key.planid = (plan_ptr) ? plan_ptr->planid : 0; - if (entry->key.planid == 0) - entry->key.planid = (plan_ptr) ? plan_ptr->planid : 0; + /* + * Make sure stats accumulation is done. (Note: it's okay if several + * levels of hook all do this.) + */ + InstrEndLoop(queryDesc->totaltime); - /* - * Make sure stats accumulation is done. (Note: it's okay if several - * levels of hook all do this.) - */ - InstrEndLoop(queryDesc->totaltime); + sys_info.utime = 0; + sys_info.stime = 0; - sys_info.utime = 0; - sys_info.stime = 0; + if (getrusage(RUSAGE_SELF, &rusage_end) != 0) + elog(DEBUG1, "[pg_stat_monitor] pgsm_ExecutorEnd: Failed to execute getrusage."); + else { + 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); + } - if (getrusage(RUSAGE_SELF, &rusage_end) != 0) - elog(DEBUG1, "[pg_stat_monitor] pgsm_ExecutorEnd: Failed to execute getrusage."); - else - { - 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); - } - - pgsm_update_entry(entry, /* entry */ - NULL, /* query */ - NULL, /* comments */ - 0, /* comments length */ - plan_ptr, /* PlanInfo */ - &sys_info, /* SysInfo */ - NULL, /* ErrorInfo */ - 0, /* plan_total_time */ - queryDesc->totaltime->total * 1000.0, /* exec_total_time */ - queryDesc->estate->es_processed, /* rows */ - &queryDesc->totaltime->bufusage, /* bufusage */ + pgsm_update_entry(entry, /* entry */ + NULL, /* query */ + NULL, /* comments */ + 0, /* comments length */ + plan_ptr, /* PlanInfo */ + &sys_info, /* SysInfo */ + NULL, /* ErrorInfo */ + 0, /* plan_total_time */ + queryDesc->totaltime->total * 1000.0, /* exec_total_time */ + queryDesc->estate->es_processed, /* rows */ + &queryDesc->totaltime->bufusage, /* bufusage */ #if PG_VERSION_NUM >= 130000 - &queryDesc->totaltime->walusage, /* walusage */ + &queryDesc->totaltime->walusage, /* walusage */ #else - NULL, + NULL, #endif #if PG_VERSION_NUM >= 150000 - queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL, /* jitusage */ + queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL, /* jitusage */ #else - NULL, + NULL, #endif - false, /* reset */ - PGSM_EXEC); /* kind */ + false, /* reset */ + PGSM_EXEC); /* kind */ - pgsm_store(entry); - } + pgsm_store(entry); + } - if (prev_ExecutorEnd) - prev_ExecutorEnd(queryDesc); - else - standard_ExecutorEnd(queryDesc); + if (prev_ExecutorEnd) + prev_ExecutorEnd(queryDesc); + else + standard_ExecutorEnd(queryDesc); - num_relations = 0; + num_relations = 0; } static bool -pgsm_ExecutorCheckPerms(List *rt, bool abort) +pgsm_ExecutorCheckPerms(List * rt, bool abort) { - ListCell *lr = NULL; - int i = 0; - int j = 0; - Oid list_oid[20]; + ListCell *lr = NULL; + int i = 0; + int j = 0; + Oid list_oid[20]; - num_relations = 0; + num_relations = 0; - foreach(lr, rt) - { - RangeTblEntry *rte = lfirst(lr); + foreach(lr, rt) { + RangeTblEntry *rte = lfirst(lr); - if (rte->rtekind != RTE_RELATION) - continue; + if (rte->rtekind != RTE_RELATION) + continue; - if (i < REL_LST) - { - bool found = false; + if (i < REL_LST) { + bool found = false; - for (j = 0; j < i; j++) - { - if (list_oid[j] == rte->relid) - found = true; - } + for (j = 0; j < i; j++) { + if (list_oid[j] == rte->relid) + found = true; + } - if (!found) - { - char *namespace_name; - char *relation_name; + if (!found) { + char *namespace_name; + char *relation_name; - list_oid[j] = rte->relid; - namespace_name = get_namespace_name(get_rel_namespace(rte->relid)); - relation_name = get_rel_name(rte->relid); - if (rte->relkind == 'v') - snprintf(relations[i++], REL_LEN, "%s.%s*", namespace_name, relation_name); - else - snprintf(relations[i++], REL_LEN, "%s.%s", namespace_name, relation_name); - } - } + list_oid[j] = rte->relid; + namespace_name = get_namespace_name(get_rel_namespace(rte->relid)); + relation_name = get_rel_name(rte->relid); + if (rte->relkind == 'v') + snprintf(relations[i++], REL_LEN, "%s.%s*", namespace_name, relation_name); + else + snprintf(relations[i++], REL_LEN, "%s.%s", namespace_name, relation_name); + } } - num_relations = i; + } + num_relations = i; - if (prev_ExecutorCheckPerms_hook) - return prev_ExecutorCheckPerms_hook(rt, abort); + if (prev_ExecutorCheckPerms_hook) + return prev_ExecutorCheckPerms_hook(rt, abort); - return true; + return true; } #if PG_VERSION_NUM >= 130000 static PlannedStmt * -pgsm_planner_hook(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams) -{ - PlannedStmt *result; - pgsmEntry *entry = NULL; +pgsm_planner_hook(Query * parse, const char *query_string, int cursorOptions, ParamListInfo boundParams) { + PlannedStmt *result; + pgsmEntry *entry = NULL; + /* + * We can't process the query if no query_string is provided, as + * pgsm_store needs it. We also ignore query without queryid, as it would + * be treated as a utility statement, which may not be the case. + * + * Note that planner_hook can be called from the planner itself, so we + * have a specific nesting level for the planner. However, utility + * commands containing optimizable statements can also call the planner, + * same for regular DML (for instance for underlying foreign key queries). + * So testing the planner nesting level only is not enough to detect real + * top level planner call. + */ + if (MemoryContextIsValid(MessageContext)) + entry = pgsm_get_entry_for_query(parse->queryId, NULL, query_string, strlen(query_string), true); + + + if (pgsm_enabled(plan_nested_level + exec_nested_level) && + pgsm_track_planning && query_string && parse->queryId != UINT64CONST(0)) { + instr_time start; + instr_time duration; + BufferUsage bufusage_start; + BufferUsage bufusage; + WalUsage walusage_start; + WalUsage walusage; + + /* We need to track buffer usage as the planner can access them. */ + bufusage_start = pgBufferUsage; + /* - * We can't process the query if no query_string is provided, as - * pgsm_store needs it. We also ignore query without queryid, - * as it would be treated as a utility statement, which may not be the case. - * - * Note that planner_hook can be called from the planner itself, so we - * have a specific nesting level for the planner. However, utility - * commands containing optimizable statements can also call the planner, - * same for regular DML (for instance for underlying foreign key queries). - * So testing the planner nesting level only is not enough to detect real - * top level planner call. + * Similarly the planner could write some WAL records in some cases + * (e.g. setting a hint bit with those being WAL-logged) */ - if (MemoryContextIsValid(MessageContext)) - entry = pgsm_get_entry_for_query(parse->queryId, NULL, query_string, strlen(query_string), true); + walusage_start = pgWalUsage; + INSTR_TIME_SET_CURRENT(start); - - if (pgsm_enabled(plan_nested_level + exec_nested_level) && - pgsm_track_planning && query_string && parse->queryId != UINT64CONST(0)) + plan_nested_level++; + PG_TRY(); { - instr_time start; - instr_time duration; - BufferUsage bufusage_start; - BufferUsage bufusage; - WalUsage walusage_start; - WalUsage walusage; - - /* We need to track buffer usage as the planner can access them. */ - bufusage_start = pgBufferUsage; - - /* - * Similarly the planner could write some WAL records in some cases - * (e.g. setting a hint bit with those being WAL-logged) - */ - walusage_start = pgWalUsage; - INSTR_TIME_SET_CURRENT(start); - - plan_nested_level++; - PG_TRY(); - { - /* - * If there is a previous installed hook, then assume it's going - * to call standard_planner() function, otherwise we call the - * function here. This is to avoid calling standard_planner() - * function twice, since it modifies the first argument (Query *), - * the second call would trigger an assertion failure. - */ - if (planner_hook_next) - result = planner_hook_next(parse, query_string, cursorOptions, boundParams); - else - result = standard_planner(parse, query_string, cursorOptions, boundParams); - } - PG_FINALLY(); - { - plan_nested_level--; - } - PG_END_TRY(); - - INSTR_TIME_SET_CURRENT(duration); - INSTR_TIME_SUBTRACT(duration, start); - - /* calc differences of buffer counters. */ - memset(&bufusage, 0, sizeof(BufferUsage)); - BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start); - - /* calc differences of WAL counters. */ - memset(&walusage, 0, sizeof(WalUsage)); - WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); - - /* The plan details are captured when the query finishes */ - if(entry) - pgsm_update_entry(entry, /* entry */ - NULL, /* query */ - NULL, /* comments */ - 0, /* comments length */ - NULL, /* PlanInfo */ - NULL, /* SysInfo */ - NULL, /* ErrorInfo */ - INSTR_TIME_GET_MILLISEC(duration), /* plan_total_time */ - 0, /* exec_total_time */ - 0, /* rows */ - &bufusage, /* bufusage */ - &walusage, /* walusage */ - NULL, /* jitusage */ - false, /* reset */ - PGSM_PLAN); /* kind */ + /* + * If there is a previous installed hook, then assume it's going + * to call standard_planner() function, otherwise we call the + * function here. This is to avoid calling standard_planner() + * function twice, since it modifies the first argument (Query *), + * the second call would trigger an assertion failure. + */ + if (planner_hook_next) + result = planner_hook_next(parse, query_string, cursorOptions, boundParams); + else + result = standard_planner(parse, query_string, cursorOptions, boundParams); } + PG_FINALLY(); + { + plan_nested_level--; + } + PG_END_TRY(); + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + + /* calc differences of buffer counters. */ + memset(&bufusage, 0, sizeof(BufferUsage)); + BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start); + + /* calc differences of WAL counters. */ + memset(&walusage, 0, sizeof(WalUsage)); + WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); + + /* The plan details are captured when the query finishes */ + if (entry) + pgsm_update_entry(entry, /* entry */ + NULL, /* query */ + NULL, /* comments */ + 0, /* comments length */ + NULL, /* PlanInfo */ + NULL, /* SysInfo */ + NULL, /* ErrorInfo */ + INSTR_TIME_GET_MILLISEC(duration), /* plan_total_time */ + 0, /* exec_total_time */ + 0, /* rows */ + &bufusage, /* bufusage */ + &walusage, /* walusage */ + NULL, /* jitusage */ + false, /* reset */ + PGSM_PLAN); /* kind */ + } else { + /* + * If there is a previous installed hook, then assume it's going to + * call standard_planner() function, otherwise we call the function + * here. This is to avoid calling standard_planner() function twice, + * since it modifies the first argument (Query *), the second call + * would trigger an assertion failure. + */ + plan_nested_level++; + + if (planner_hook_next) + result = planner_hook_next(parse, query_string, cursorOptions, boundParams); else - { - /* - * If there is a previous installed hook, then assume it's going to - * call standard_planner() function, otherwise we call the function - * here. This is to avoid calling standard_planner() function twice, - * since it modifies the first argument (Query *), the second call - * would trigger an assertion failure. - */ - plan_nested_level++; + result = standard_planner(parse, query_string, cursorOptions, boundParams); + plan_nested_level--; - if (planner_hook_next) - result = planner_hook_next(parse, query_string, cursorOptions, boundParams); - else - result = standard_planner(parse, query_string, cursorOptions, boundParams); - plan_nested_level--; - - } - return result; + } + return result; } #endif @@ -927,277 +906,273 @@ pgsm_planner_hook(Query *parse, const char *query_string, int cursorOptions, Par */ #if PG_VERSION_NUM >= 140000 static void -pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, - bool readOnlyTree, - ProcessUtilityContext context, - ParamListInfo params, QueryEnvironment *queryEnv, - DestReceiver *dest, - QueryCompletion *qc) +pgsm_ProcessUtility(PlannedStmt * pstmt, const char *queryString, + bool readOnlyTree, + ProcessUtilityContext context, + ParamListInfo params, QueryEnvironment * queryEnv, + DestReceiver * dest, + QueryCompletion * qc) #elif PG_VERSION_NUM >= 130000 static void -pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, - ProcessUtilityContext context, - ParamListInfo params, QueryEnvironment *queryEnv, - DestReceiver *dest, - QueryCompletion *qc) +pgsm_ProcessUtility(PlannedStmt * pstmt, const char *queryString, + ProcessUtilityContext context, + ParamListInfo params, QueryEnvironment * queryEnv, + DestReceiver * dest, + QueryCompletion * qc) #else static void -pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, - ProcessUtilityContext context, ParamListInfo params, - QueryEnvironment *queryEnv, - DestReceiver *dest, - char *completionTag) +pgsm_ProcessUtility(PlannedStmt * pstmt, const char *queryString, + ProcessUtilityContext context, ParamListInfo params, + QueryEnvironment * queryEnv, + DestReceiver * dest, + char *completionTag) #endif { - Node *parsetree = pstmt->utilityStmt; - uint64 queryId = 0; + Node *parsetree = pstmt->utilityStmt; + uint64 queryId = 0; #if PG_VERSION_NUM < 140000 - int len = strlen(queryString); + int len = strlen(queryString); - queryId = pgsm_hash_string(queryString, len); + queryId = pgsm_hash_string(queryString, len); #else - queryId = pstmt->queryId; + 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 && pgsm_enabled(exec_nested_level)) - pstmt->queryId = UINT64CONST(0); + /* + * 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 && pgsm_enabled(exec_nested_level)) + pstmt->queryId = UINT64CONST(0); #endif - /* - * If it's an EXECUTE statement, we don't track it and don't increment the - * nesting level. This allows the cycles to be charged to the underlying - * PREPARE instead (by the Executor hooks), which is much more useful. - * - * We also don't track execution of PREPARE. If we did, we would get one - * hash table entry for the PREPARE (with hash calculated from the query - * string), and then a different one with the same query string (but hash - * calculated from the query tree) would be used to accumulate costs of - * ensuing EXECUTEs. This would be confusing, and inconsistent with other - * cases where planning time is not included at all. - * - * Likewise, we don't track execution of DEALLOCATE. - */ - if (pgsm_track_utility && pgsm_enabled(exec_nested_level) && - PGSM_HANDLED_UTILITY(parsetree)) + /* + * If it's an EXECUTE statement, we don't track it and don't increment the + * nesting level. This allows the cycles to be charged to the underlying + * PREPARE instead (by the Executor hooks), which is much more useful. + * + * We also don't track execution of PREPARE. If we did, we would get one + * hash table entry for the PREPARE (with hash calculated from the query + * string), and then a different one with the same query string (but hash + * calculated from the query tree) would be used to accumulate costs of + * ensuing EXECUTEs. This would be confusing, and inconsistent with other + * cases where planning time is not included at all. + * + * Likewise, we don't track execution of DEALLOCATE. + */ + if (pgsm_track_utility && pgsm_enabled(exec_nested_level) && + PGSM_HANDLED_UTILITY(parsetree)) { + pgsmEntry *entry; + char *query_text; + int location; + int query_len; + instr_time start; + instr_time duration; + uint64 rows; + SysInfo sys_info; + BufferUsage bufusage; + BufferUsage bufusage_start = pgBufferUsage; +#if PG_VERSION_NUM >= 130000 + WalUsage walusage; + WalUsage walusage_start = pgWalUsage; +#endif + + if (getrusage(RUSAGE_SELF, &rusage_start) != 0) + elog(DEBUG1, "[pg_stat_monitor] pgsm_ProcessUtility: Failed to execute getrusage."); + + INSTR_TIME_SET_CURRENT(start); + exec_nested_level++; + + PG_TRY(); { - pgsmEntry *entry; - char *query_text; - int location; - int query_len; - instr_time start; - instr_time duration; - uint64 rows; - SysInfo sys_info; - BufferUsage bufusage; - BufferUsage bufusage_start = pgBufferUsage; -#if PG_VERSION_NUM >= 130000 - WalUsage walusage; - WalUsage walusage_start = pgWalUsage; -#endif - - if (getrusage(RUSAGE_SELF, &rusage_start) != 0) - elog(DEBUG1, "[pg_stat_monitor] pgsm_ProcessUtility: Failed to execute getrusage."); - - INSTR_TIME_SET_CURRENT(start); - exec_nested_level++; - - PG_TRY(); - { #if PG_VERSION_NUM >= 140000 - if (prev_ProcessUtility) - prev_ProcessUtility(pstmt, queryString, - readOnlyTree, - context, params, queryEnv, - dest, - qc); - else - standard_ProcessUtility(pstmt, queryString, - readOnlyTree, - context, params, queryEnv, - dest, - qc); + if (prev_ProcessUtility) + prev_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, - dest, - qc); - else - standard_ProcessUtility(pstmt, queryString, - context, params, queryEnv, - dest, - qc); + if (prev_ProcessUtility) + prev_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, - context, params, queryEnv, - dest, - completionTag); - else - standard_ProcessUtility(pstmt, queryString, - context, params, queryEnv, - dest, - completionTag); + if (prev_ProcessUtility) + prev_ProcessUtility(pstmt, queryString, + context, params, queryEnv, + dest, + completionTag); + else + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, + dest, + completionTag); #endif - exec_nested_level--; - } - PG_CATCH(); - { - exec_nested_level--; - PG_RE_THROW(); - } + exec_nested_level--; + } + PG_CATCH(); + { + exec_nested_level--; + PG_RE_THROW(); + } - sys_info.utime = 0; - sys_info.stime = 0; + sys_info.utime = 0; + sys_info.stime = 0; - PG_END_TRY(); + PG_END_TRY(); - if (getrusage(RUSAGE_SELF, &rusage_end) != 0) - elog(DEBUG1, "[pg_stat_monitor] pgsm_ProcessUtility: Failed to execute getrusage."); - else - { - 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); - } + if (getrusage(RUSAGE_SELF, &rusage_end) != 0) + elog(DEBUG1, "[pg_stat_monitor] pgsm_ProcessUtility: Failed to execute getrusage."); + else { + 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); + } - INSTR_TIME_SET_CURRENT(duration); - INSTR_TIME_SUBTRACT(duration, start); + INSTR_TIME_SET_CURRENT(duration); + 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; + 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. */ - memset(&walusage, 0, sizeof(WalUsage)); - WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); + /* calc differences of WAL counters. */ + memset(&walusage, 0, sizeof(WalUsage)); + WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); #else - /* parse command tag to retrieve the number of affected rows. */ - if (completionTag && strncmp(completionTag, "COPY ", 5) == 0) - rows = pg_strtouint64(completionTag + 5, NULL, 10); - else - rows = 0; -#endif - - /* calc differences of buffer counters. */ - memset(&bufusage, 0, sizeof(BufferUsage)); - BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start); - - /* Create an entry for this query */ - entry = pgsm_create_hash_entry(0, queryId, NULL); - - location = pstmt->stmt_location; - query_len = pstmt->stmt_len; - query_text = (char *)CleanQuerytext(queryString, &location, &query_len); - - entry->pgsm_query_id = get_pgsm_query_id_hash(query_text, query_len); - entry->counters.info.cmd_type = 0; - - pgsm_add_to_list(entry, query_text, query_len); - - /* Check that we've not exceeded max_stack_depth */ - Assert(list_length(lentries) <= max_stack_depth); - - /* The plan details are captured when the query finishes */ - pgsm_update_entry(entry, /* entry */ - (char *)query_text, /* query */ - NULL, /* comments */ - 0, /* comments length */ - NULL, /* PlanInfo */ - &sys_info, /* SysInfo */ - NULL, /* ErrorInfo */ - 0, /* plan_total_time */ - INSTR_TIME_GET_MILLISEC(duration), /* exec_total_time */ - rows, /* rows */ - &bufusage, /* bufusage */ -#if PG_VERSION_NUM >= 130000 - &walusage, /* walusage */ -#else - NULL, -#endif - NULL, /* jitusage */ - false, /* reset */ - PGSM_EXEC); /* kind */ - - pgsm_store(entry); - } + /* parse command tag to retrieve the number of affected rows. */ + if (completionTag && strncmp(completionTag, "COPY ", 5) == 0) + rows = pg_strtouint64(completionTag + 5, NULL, 10); else - { -#if PG_VERSION_NUM >= 140000 - if (prev_ProcessUtility) - prev_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, - dest, - qc); - else - standard_ProcessUtility(pstmt, queryString, - context, params, queryEnv, - dest, - qc); -#else - if (prev_ProcessUtility) - prev_ProcessUtility(pstmt, queryString, - context, params, queryEnv, - dest, - completionTag); - else - standard_ProcessUtility(pstmt, queryString, - context, params, queryEnv, - dest, - completionTag); + rows = 0; #endif - } + + /* calc differences of buffer counters. */ + memset(&bufusage, 0, sizeof(BufferUsage)); + BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start); + + /* Create an entry for this query */ + entry = pgsm_create_hash_entry(0, queryId, NULL); + + location = pstmt->stmt_location; + query_len = pstmt->stmt_len; + query_text = (char *) CleanQuerytext(queryString, &location, &query_len); + + entry->pgsm_query_id = get_pgsm_query_id_hash(query_text, query_len); + entry->counters.info.cmd_type = 0; + + pgsm_add_to_list(entry, query_text, query_len); + + /* Check that we've not exceeded max_stack_depth */ + Assert(list_length(lentries) <= max_stack_depth); + + /* The plan details are captured when the query finishes */ + pgsm_update_entry(entry, /* entry */ + (char *) query_text, /* query */ + NULL, /* comments */ + 0, /* comments length */ + NULL, /* PlanInfo */ + &sys_info, /* SysInfo */ + NULL, /* ErrorInfo */ + 0, /* plan_total_time */ + INSTR_TIME_GET_MILLISEC(duration), /* exec_total_time */ + rows, /* rows */ + &bufusage, /* bufusage */ +#if PG_VERSION_NUM >= 130000 + &walusage, /* walusage */ +#else + NULL, +#endif + NULL, /* jitusage */ + false, /* reset */ + PGSM_EXEC); /* kind */ + + pgsm_store(entry); + } else { +#if PG_VERSION_NUM >= 140000 + if (prev_ProcessUtility) + prev_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, + dest, + qc); + else + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, + dest, + qc); +#else + if (prev_ProcessUtility) + prev_ProcessUtility(pstmt, queryString, + context, params, queryEnv, + dest, + completionTag); + else + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, + dest, + completionTag); +#endif + } } #if PG_VERSION_NUM < 130000 static void -BufferUsageAccumDiff(BufferUsage *bufusage, BufferUsage *pgBufferUsage, BufferUsage *bufusage_start) +BufferUsageAccumDiff(BufferUsage * bufusage, BufferUsage * pgBufferUsage, BufferUsage * bufusage_start) { - /* calc differences of buffer counters. */ - bufusage->shared_blks_hit = pgBufferUsage->shared_blks_hit - bufusage_start->shared_blks_hit; - bufusage->shared_blks_read = pgBufferUsage->shared_blks_read - bufusage_start->shared_blks_read; - bufusage->shared_blks_dirtied = pgBufferUsage->shared_blks_dirtied - bufusage_start->shared_blks_dirtied; - bufusage->shared_blks_written = pgBufferUsage->shared_blks_written - bufusage_start->shared_blks_written; - bufusage->local_blks_hit = pgBufferUsage->local_blks_hit - bufusage_start->local_blks_hit; - bufusage->local_blks_read = pgBufferUsage->local_blks_read - bufusage_start->local_blks_read; - bufusage->local_blks_dirtied = pgBufferUsage->local_blks_dirtied - bufusage_start->local_blks_dirtied; - bufusage->local_blks_written = pgBufferUsage->local_blks_written - bufusage_start->local_blks_written; - bufusage->temp_blks_read = pgBufferUsage->temp_blks_read - bufusage_start->temp_blks_read; - bufusage->temp_blks_written = pgBufferUsage->temp_blks_written - bufusage_start->temp_blks_written; - bufusage->blk_read_time = pgBufferUsage->blk_read_time; - INSTR_TIME_SUBTRACT(bufusage->blk_read_time, bufusage_start->blk_read_time); - bufusage->blk_write_time = pgBufferUsage->blk_write_time; - INSTR_TIME_SUBTRACT(bufusage->blk_write_time, bufusage_start->blk_write_time); + /* calc differences of buffer counters. */ + bufusage->shared_blks_hit = pgBufferUsage->shared_blks_hit - bufusage_start->shared_blks_hit; + bufusage->shared_blks_read = pgBufferUsage->shared_blks_read - bufusage_start->shared_blks_read; + bufusage->shared_blks_dirtied = pgBufferUsage->shared_blks_dirtied - bufusage_start->shared_blks_dirtied; + bufusage->shared_blks_written = pgBufferUsage->shared_blks_written - bufusage_start->shared_blks_written; + bufusage->local_blks_hit = pgBufferUsage->local_blks_hit - bufusage_start->local_blks_hit; + bufusage->local_blks_read = pgBufferUsage->local_blks_read - bufusage_start->local_blks_read; + bufusage->local_blks_dirtied = pgBufferUsage->local_blks_dirtied - bufusage_start->local_blks_dirtied; + bufusage->local_blks_written = pgBufferUsage->local_blks_written - bufusage_start->local_blks_written; + bufusage->temp_blks_read = pgBufferUsage->temp_blks_read - bufusage_start->temp_blks_read; + bufusage->temp_blks_written = pgBufferUsage->temp_blks_written - bufusage_start->temp_blks_written; + bufusage->blk_read_time = pgBufferUsage->blk_read_time; + INSTR_TIME_SUBTRACT(bufusage->blk_read_time, bufusage_start->blk_read_time); + bufusage->blk_write_time = pgBufferUsage->blk_write_time; + INSTR_TIME_SUBTRACT(bufusage->blk_write_time, bufusage_start->blk_write_time); } #endif @@ -1207,33 +1182,30 @@ BufferUsageAccumDiff(BufferUsage *bufusage, BufferUsage *pgBufferUsage, BufferUs * utility statements. */ static uint64 -pgsm_hash_string(const char *str, int len) -{ - return DatumGetUInt64(hash_any_extended((const unsigned char *) str, - len, 0)); +pgsm_hash_string(const char *str, int len) { + return DatumGetUInt64(hash_any_extended((const unsigned char *) str, + len, 0)); } static PgBackendStatus * -pg_get_backend_status(void) -{ - LocalPgBackendStatus *local_beentry; - int num_backends = pgstat_fetch_stat_numbackends(); - int i; +pg_get_backend_status(void) { + LocalPgBackendStatus *local_beentry; + int num_backends = pgstat_fetch_stat_numbackends(); + int i; - for (i = 1; i <= num_backends; i++) - { - PgBackendStatus *beentry; + for (i = 1; i <= num_backends; i++) { + PgBackendStatus *beentry; - local_beentry = pgstat_fetch_stat_local_beentry(i); - if (!local_beentry) - continue; + local_beentry = pgstat_fetch_stat_local_beentry(i); + if (!local_beentry) + continue; - beentry = &local_beentry->backendStatus; + beentry = &local_beentry->backendStatus; - if (beentry->st_procpid == MyProcPid) - return beentry; - } - return NULL; + if (beentry->st_procpid == MyProcPid) + return beentry; + } + return NULL; } /* @@ -1243,441 +1215,420 @@ pg_get_backend_status(void) static int pg_get_application_name(char *name, int buff_size) { - PgBackendStatus *beentry; + PgBackendStatus *beentry; - /* Try to read application name from GUC directly */ - if (application_name && *application_name) - snprintf(name, buff_size, "%s", application_name); + /* Try to read application name from GUC directly */ + if (application_name && *application_name) + snprintf(name, buff_size, "%s", application_name); + else { + beentry = pg_get_backend_status(); + + if (!beentry) + snprintf(name, buff_size, "%s", "unknown"); else - { - beentry = pg_get_backend_status(); + snprintf(name, buff_size, "%s", beentry->st_appname); + } - if (!beentry) - snprintf(name, buff_size, "%s", "unknown"); - else - snprintf(name, buff_size, "%s", beentry->st_appname); - } - - /* Return length so that others don't have to calculate */ - return strlen(name); + /* Return length so that others don't have to calculate */ + return strlen(name); } static uint -pg_get_client_addr(bool *ok) -{ - PgBackendStatus *beentry = pg_get_backend_status(); - char remote_host[NI_MAXHOST]; - int ret; +pg_get_client_addr(bool *ok) { + PgBackendStatus *beentry = pg_get_backend_status(); + char remote_host[NI_MAXHOST]; + int ret; - remote_host[0] = '\0'; + remote_host[0] = '\0'; - if (!beentry) - return ntohl(inet_addr("127.0.0.1")); + if (!beentry) + return ntohl(inet_addr("127.0.0.1")); - *ok = true; + *ok = true; - ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, - beentry->st_clientaddr.salen, - remote_host, sizeof(remote_host), - NULL, 0, - NI_NUMERICHOST | NI_NUMERICSERV); - if (ret != 0) - return ntohl(inet_addr("127.0.0.1")); + ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, + beentry->st_clientaddr.salen, + remote_host, sizeof(remote_host), + NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV); + if (ret != 0) + return ntohl(inet_addr("127.0.0.1")); - if (strcmp(remote_host, "[local]") == 0) - return ntohl(inet_addr("127.0.0.1")); + if (strcmp(remote_host, "[local]") == 0) + return ntohl(inet_addr("127.0.0.1")); - return ntohl(inet_addr(remote_host)); + return ntohl(inet_addr(remote_host)); } static void -pgsm_update_entry(pgsmEntry *entry, - const char *query, - char *comments, - int comments_len, - PlanInfo * plan_info, - SysInfo * sys_info, - ErrorInfo * error_info, - double plan_total_time, - double exec_total_time, - uint64 rows, - BufferUsage *bufusage, - WalUsage *walusage, - const struct JitInstrumentation *jitusage, - bool reset, - pgsmStoreKind kind) +pgsm_update_entry(pgsmEntry * entry, + const char *query, + char *comments, + int comments_len, + PlanInfo * plan_info, + SysInfo * sys_info, + ErrorInfo * error_info, + double plan_total_time, + double exec_total_time, + uint64 rows, + BufferUsage * bufusage, + WalUsage * walusage, + const struct JitInstrumentation *jitusage, + bool reset, + pgsmStoreKind kind) { - int index; - double old_mean; - int message_len = error_info ? strlen(error_info->message) : 0; - int sqlcode_len = error_info ? strlen(error_info->sqlcode) : 0; - int plan_text_len = plan_info ? plan_info->plan_len : 0; + int index; + double old_mean; + int message_len = error_info ? strlen(error_info->message) : 0; + int sqlcode_len = error_info ? strlen(error_info->sqlcode) : 0; + int plan_text_len = plan_info ? plan_info->plan_len : 0; - /* Start collecting data for next bucket and reset all counters */ - if (reset) - memset(&entry->counters, 0, sizeof(Counters)); + /* Start collecting data for next bucket and reset all counters */ + if (reset) + memset(&entry->counters, 0, sizeof(Counters)); - /* volatile block */ - { - volatile pgsmEntry *e = (volatile pgsmEntry *)entry; + /* volatile block */ + { + volatile pgsmEntry *e = (volatile pgsmEntry *) entry; - if (kind == PGSM_STORE) - SpinLockAcquire(&e->mutex); + if (kind == PGSM_STORE) + SpinLockAcquire(&e->mutex); - /* Extract comments if enabled and only when the query has completed with or without error */ - if (pgsm_extract_comments && kind == PGSM_STORE - && !e->counters.info.comments[0] && comments_len > 0) - _snprintf(e->counters.info.comments, comments, comments_len + 1, COMMENTS_LEN); + /* + * Extract comments if enabled and only when the query has completed + * with or without error + */ + if (pgsm_extract_comments && kind == PGSM_STORE + && !e->counters.info.comments[0] && comments_len > 0) + _snprintf(e->counters.info.comments, comments, comments_len + 1, COMMENTS_LEN); - if (kind == PGSM_PLAN || kind == PGSM_STORE) - { - if (e->counters.plancalls.calls == 0) - e->counters.plancalls.usage = USAGE_INIT; + if (kind == PGSM_PLAN || kind == PGSM_STORE) { + if (e->counters.plancalls.calls == 0) + e->counters.plancalls.usage = USAGE_INIT; - e->counters.plancalls.calls += 1; - e->counters.plantime.total_time += plan_total_time; + e->counters.plancalls.calls += 1; + e->counters.plantime.total_time += plan_total_time; - if (e->counters.plancalls.calls == 1) - { - e->counters.plantime.min_time = plan_total_time; - e->counters.plantime.max_time = plan_total_time; - e->counters.plantime.mean_time = plan_total_time; - } - else - { - /* Increment the counts, except when jstate is not NULL */ - old_mean = e->counters.plantime.mean_time; + if (e->counters.plancalls.calls == 1) { + e->counters.plantime.min_time = plan_total_time; + e->counters.plantime.max_time = plan_total_time; + e->counters.plantime.mean_time = plan_total_time; + } else { + /* Increment the counts, except when jstate is not NULL */ + old_mean = e->counters.plantime.mean_time; - e->counters.plantime.mean_time += (plan_total_time - old_mean) / e->counters.plancalls.calls; - e->counters.plantime.sum_var_time += (plan_total_time - old_mean) * (plan_total_time - e->counters.plantime.mean_time); + e->counters.plantime.mean_time += (plan_total_time - old_mean) / e->counters.plancalls.calls; + e->counters.plantime.sum_var_time += (plan_total_time - old_mean) * (plan_total_time - e->counters.plantime.mean_time); - /* calculate min and max time */ - if (e->counters.plantime.min_time > plan_total_time) - e->counters.plantime.min_time = plan_total_time; + /* calculate min and max time */ + if (e->counters.plantime.min_time > plan_total_time) + e->counters.plantime.min_time = plan_total_time; - if (e->counters.plantime.max_time < plan_total_time) - e->counters.plantime.max_time = plan_total_time; - } - } - - if (kind == PGSM_EXEC || kind == PGSM_STORE) - { - if (e->counters.calls.calls == 0) - e->counters.calls.usage = USAGE_INIT; - - e->counters.calls.calls += 1; - e->counters.time.total_time += exec_total_time; - - if (e->counters.calls.calls == 1) - { - e->counters.time.min_time = exec_total_time; - e->counters.time.max_time = exec_total_time; - e->counters.time.mean_time = exec_total_time; - } - else - { - /* Increment the counts, except when jstate is not NULL */ - old_mean = e->counters.time.mean_time; - e->counters.time.mean_time += (exec_total_time - old_mean) / e->counters.calls.calls; - e->counters.time.sum_var_time += (exec_total_time - old_mean) * (exec_total_time - e->counters.time.mean_time); - - /* calculate min and max time */ - if (e->counters.time.min_time > exec_total_time) - e->counters.time.min_time = exec_total_time; - - if (e->counters.time.max_time < exec_total_time) - e->counters.time.max_time = exec_total_time; - } - - index = get_histogram_bucket(exec_total_time); - e->counters.resp_calls[index]++; - } - - if (plan_text_len > 0 && !e->counters.planinfo.plan_text[0]) - { - e->counters.planinfo.planid = plan_info->planid; - e->counters.planinfo.plan_len = plan_text_len; - _snprintf(e->counters.planinfo.plan_text, plan_info->plan_text, plan_text_len + 1, PLAN_TEXT_LEN); - } - - /* Only should process this once when storing the data */ - if (kind == PGSM_STORE) - { - if (app_name_len > 0 && !e->counters.info.application_name[0]) - _snprintf(e->counters.info.application_name, app_name, app_name_len + 1, APPLICATIONNAME_LEN); - - e->counters.info.num_relations = num_relations; - _snprintf2(e->counters.info.relations, relations, num_relations, REL_LEN); - - if (exec_nested_level > 0 && e->counters.info.parentid == 0 && pgsm_track == PGSM_TRACK_ALL) - { - if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) - { - int parent_query_len = nested_query_txts[exec_nested_level - 1]? - strlen(nested_query_txts[exec_nested_level - 1]): 0; - e->counters.info.parentid = nested_queryids[exec_nested_level - 1]; - e->counters.info.parent_query = InvalidDsaPointer; - /* If we have a parent query, store it in the raw dsa area */ - if (parent_query_len > 0) - { - char *qry_buff; - dsa_area *query_dsa_area = get_dsa_area_for_query_text(); - /* Use dsa_allocate_extended with DSA_ALLOC_NO_OOM flag, as we don't want to get an - * error if memory allocation fails.*/ - dsa_pointer qry = dsa_allocate_extended(query_dsa_area, parent_query_len+1, DSA_ALLOC_NO_OOM | DSA_ALLOC_ZERO); - if (DsaPointerIsValid(qry)) - { - qry_buff = dsa_get_address(query_dsa_area, qry); - memcpy(qry_buff, nested_query_txts[exec_nested_level - 1], parent_query_len); - qry_buff[parent_query_len] = 0; - /* store the dsa pointer for parent query text */ - e->counters.info.parent_query = qry; - } - } - } - } - else - { - e->counters.info.parentid = UINT64CONST(0); - e->counters.info.parent_query = InvalidDsaPointer; - } - } - - if (error_info) - { - e->counters.error.elevel = error_info->elevel; - _snprintf(e->counters.error.sqlcode, error_info->sqlcode, sqlcode_len, SQLCODE_LEN); - _snprintf(e->counters.error.message, error_info->message, message_len, ERROR_MESSAGE_LEN); - } - - e->counters.calls.rows += rows; - - if (bufusage) - { - e->counters.blocks.shared_blks_hit += bufusage->shared_blks_hit; - e->counters.blocks.shared_blks_read += bufusage->shared_blks_read; - e->counters.blocks.shared_blks_dirtied += bufusage->shared_blks_dirtied; - e->counters.blocks.shared_blks_written += bufusage->shared_blks_written; - e->counters.blocks.local_blks_hit += bufusage->local_blks_hit; - e->counters.blocks.local_blks_read += bufusage->local_blks_read; - e->counters.blocks.local_blks_dirtied += bufusage->local_blks_dirtied; - e->counters.blocks.local_blks_written += bufusage->local_blks_written; - e->counters.blocks.temp_blks_read += bufusage->temp_blks_read; - e->counters.blocks.temp_blks_written += bufusage->temp_blks_written; - e->counters.blocks.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time); - e->counters.blocks.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time); - #if PG_VERSION_NUM >= 150000 - e->counters.blocks.temp_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_read_time); - e->counters.blocks.temp_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_write_time); - #endif - - memcpy((void *)&e->counters.blocks.instr_blk_read_time, &bufusage->blk_read_time, sizeof(instr_time)); - memcpy((void *)&e->counters.blocks.instr_blk_write_time, &bufusage->blk_write_time, sizeof(instr_time)); - - #if PG_VERSION_NUM >= 150000 - memcpy((void *)&e->counters.blocks.instr_temp_blk_read_time, &bufusage->temp_blk_read_time, sizeof(bufusage->temp_blk_read_time)); - memcpy((void *)&e->counters.blocks.instr_temp_blk_write_time, &bufusage->temp_blk_write_time, sizeof(bufusage->temp_blk_write_time)); - #endif - } - - e->counters.calls.usage += USAGE_EXEC(exec_total_time + plan_total_time); - - if (sys_info) - { - e->counters.sysinfo.utime += sys_info->utime; - e->counters.sysinfo.stime += sys_info->stime; - } - if (walusage) - { - e->counters.walusage.wal_records += walusage->wal_records; - e->counters.walusage.wal_fpi += walusage->wal_fpi; - e->counters.walusage.wal_bytes += walusage->wal_bytes; - } - if (jitusage) - { - e->counters.jitinfo.jit_functions += jitusage->created_functions; - e->counters.jitinfo.jit_generation_time += INSTR_TIME_GET_MILLISEC(jitusage->generation_counter); - - if (INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter)) - e->counters.jitinfo.jit_inlining_count++; - e->counters.jitinfo.jit_inlining_time += INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter); - - if (INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter)) - e->counters.jitinfo.jit_optimization_count++; - e->counters.jitinfo.jit_optimization_time += INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter); - - if (INSTR_TIME_GET_MILLISEC(jitusage->emission_counter)) - e->counters.jitinfo.jit_emission_count++; - e->counters.jitinfo.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter); - - /* Only do this for local storage scenarios */ - if (kind != PGSM_STORE) - { - memcpy((void *)&e->counters.jitinfo.instr_generation_counter, &jitusage->generation_counter, sizeof(instr_time)); - memcpy((void *)&e->counters.jitinfo.instr_inlining_counter, &jitusage->inlining_counter, sizeof(instr_time)); - memcpy((void *)&e->counters.jitinfo.instr_optimization_counter, &jitusage->optimization_counter, sizeof(instr_time)); - memcpy((void *)&e->counters.jitinfo.instr_emission_counter, &jitusage->emission_counter, sizeof(instr_time)); - } - } - - if (kind == PGSM_STORE) - SpinLockRelease(&e->mutex); + if (e->counters.plantime.max_time < plan_total_time) + e->counters.plantime.max_time = plan_total_time; + } } + + if (kind == PGSM_EXEC || kind == PGSM_STORE) { + if (e->counters.calls.calls == 0) + e->counters.calls.usage = USAGE_INIT; + + e->counters.calls.calls += 1; + e->counters.time.total_time += exec_total_time; + + if (e->counters.calls.calls == 1) { + e->counters.time.min_time = exec_total_time; + e->counters.time.max_time = exec_total_time; + e->counters.time.mean_time = exec_total_time; + } else { + /* Increment the counts, except when jstate is not NULL */ + old_mean = e->counters.time.mean_time; + e->counters.time.mean_time += (exec_total_time - old_mean) / e->counters.calls.calls; + e->counters.time.sum_var_time += (exec_total_time - old_mean) * (exec_total_time - e->counters.time.mean_time); + + /* calculate min and max time */ + if (e->counters.time.min_time > exec_total_time) + e->counters.time.min_time = exec_total_time; + + if (e->counters.time.max_time < exec_total_time) + e->counters.time.max_time = exec_total_time; + } + + index = get_histogram_bucket(exec_total_time); + e->counters.resp_calls[index]++; + } + + if (plan_text_len > 0 && !e->counters.planinfo.plan_text[0]) { + e->counters.planinfo.planid = plan_info->planid; + e->counters.planinfo.plan_len = plan_text_len; + _snprintf(e->counters.planinfo.plan_text, plan_info->plan_text, plan_text_len + 1, PLAN_TEXT_LEN); + } + + /* Only should process this once when storing the data */ + if (kind == PGSM_STORE) { + if (app_name_len > 0 && !e->counters.info.application_name[0]) + _snprintf(e->counters.info.application_name, app_name, app_name_len + 1, APPLICATIONNAME_LEN); + + e->counters.info.num_relations = num_relations; + _snprintf2(e->counters.info.relations, relations, num_relations, REL_LEN); + + if (exec_nested_level > 0 && e->counters.info.parentid == 0 && pgsm_track == PGSM_TRACK_ALL) { + if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) { + int parent_query_len = nested_query_txts[exec_nested_level - 1] ? + strlen(nested_query_txts[exec_nested_level - 1]) : 0; + e->counters.info.parentid = nested_queryids[exec_nested_level - 1]; + e->counters.info.parent_query = InvalidDsaPointer; + /* If we have a parent query, store it in the raw dsa area */ + if (parent_query_len > 0) { + char *qry_buff; + dsa_area *query_dsa_area = get_dsa_area_for_query_text(); + /* + * Use dsa_allocate_extended with DSA_ALLOC_NO_OOM + * flag, as we don't want to get an error if memory + * allocation fails. + */ + dsa_pointer qry = dsa_allocate_extended(query_dsa_area, parent_query_len + 1, DSA_ALLOC_NO_OOM | DSA_ALLOC_ZERO); + if (DsaPointerIsValid(qry)) { + qry_buff = dsa_get_address(query_dsa_area, qry); + memcpy(qry_buff, nested_query_txts[exec_nested_level - 1], parent_query_len); + qry_buff[parent_query_len] = 0; + /* store the dsa pointer for parent query text */ + e->counters.info.parent_query = qry; + } + } + } + } else { + e->counters.info.parentid = UINT64CONST(0); + e->counters.info.parent_query = InvalidDsaPointer; + } + } + + if (error_info) { + e->counters.error.elevel = error_info->elevel; + _snprintf(e->counters.error.sqlcode, error_info->sqlcode, sqlcode_len, SQLCODE_LEN); + _snprintf(e->counters.error.message, error_info->message, message_len, ERROR_MESSAGE_LEN); + } + + e->counters.calls.rows += rows; + + if (bufusage) { + e->counters.blocks.shared_blks_hit += bufusage->shared_blks_hit; + e->counters.blocks.shared_blks_read += bufusage->shared_blks_read; + e->counters.blocks.shared_blks_dirtied += bufusage->shared_blks_dirtied; + e->counters.blocks.shared_blks_written += bufusage->shared_blks_written; + e->counters.blocks.local_blks_hit += bufusage->local_blks_hit; + e->counters.blocks.local_blks_read += bufusage->local_blks_read; + e->counters.blocks.local_blks_dirtied += bufusage->local_blks_dirtied; + e->counters.blocks.local_blks_written += bufusage->local_blks_written; + e->counters.blocks.temp_blks_read += bufusage->temp_blks_read; + e->counters.blocks.temp_blks_written += bufusage->temp_blks_written; + e->counters.blocks.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time); + e->counters.blocks.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time); +#if PG_VERSION_NUM >= 150000 + e->counters.blocks.temp_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_read_time); + e->counters.blocks.temp_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_write_time); +#endif + + memcpy((void *) &e->counters.blocks.instr_blk_read_time, &bufusage->blk_read_time, sizeof(instr_time)); + memcpy((void *) &e->counters.blocks.instr_blk_write_time, &bufusage->blk_write_time, sizeof(instr_time)); + +#if PG_VERSION_NUM >= 150000 + memcpy((void *) &e->counters.blocks.instr_temp_blk_read_time, &bufusage->temp_blk_read_time, sizeof(bufusage->temp_blk_read_time)); + memcpy((void *) &e->counters.blocks.instr_temp_blk_write_time, &bufusage->temp_blk_write_time, sizeof(bufusage->temp_blk_write_time)); +#endif + } + + e->counters.calls.usage += USAGE_EXEC(exec_total_time + plan_total_time); + + if (sys_info) { + e->counters.sysinfo.utime += sys_info->utime; + e->counters.sysinfo.stime += sys_info->stime; + } + if (walusage) { + e->counters.walusage.wal_records += walusage->wal_records; + e->counters.walusage.wal_fpi += walusage->wal_fpi; + e->counters.walusage.wal_bytes += walusage->wal_bytes; + } + if (jitusage) { + e->counters.jitinfo.jit_functions += jitusage->created_functions; + e->counters.jitinfo.jit_generation_time += INSTR_TIME_GET_MILLISEC(jitusage->generation_counter); + + if (INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter)) + e->counters.jitinfo.jit_inlining_count++; + e->counters.jitinfo.jit_inlining_time += INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter); + + if (INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter)) + e->counters.jitinfo.jit_optimization_count++; + e->counters.jitinfo.jit_optimization_time += INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter); + + if (INSTR_TIME_GET_MILLISEC(jitusage->emission_counter)) + e->counters.jitinfo.jit_emission_count++; + e->counters.jitinfo.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter); + + /* Only do this for local storage scenarios */ + if (kind != PGSM_STORE) { + memcpy((void *) &e->counters.jitinfo.instr_generation_counter, &jitusage->generation_counter, sizeof(instr_time)); + memcpy((void *) &e->counters.jitinfo.instr_inlining_counter, &jitusage->inlining_counter, sizeof(instr_time)); + memcpy((void *) &e->counters.jitinfo.instr_optimization_counter, &jitusage->optimization_counter, sizeof(instr_time)); + memcpy((void *) &e->counters.jitinfo.instr_emission_counter, &jitusage->emission_counter, sizeof(instr_time)); + } + } + + if (kind == PGSM_STORE) + SpinLockRelease(&e->mutex); + } } static void -pgsm_store_error(const char *query, ErrorData *edata) +pgsm_store_error(const char *query, ErrorData * edata) { - pgsmEntry *entry; - uint64 queryid = 0; - int len = strlen(query); + pgsmEntry *entry; + uint64 queryid = 0; + int len = strlen(query); - if (!query || len == 0) - return; + if (!query || len == 0) + return; - len = strlen(query); + len = strlen(query); - queryid = pgsm_hash_string(query, len); + queryid = pgsm_hash_string(query, len); - entry = pgsm_create_hash_entry(0, queryid, NULL); - entry->query_text.query_pointer = pnstrdup(query, len); + entry = pgsm_create_hash_entry(0, queryid, NULL); + entry->query_text.query_pointer = pnstrdup(query, len); - entry->counters.error.elevel = edata->elevel; - snprintf(entry->counters.error.message, ERROR_MESSAGE_LEN, "%s", edata->message); - snprintf(entry->counters.error.sqlcode, SQLCODE_LEN, "%s", unpack_sql_state(edata->sqlerrcode)); + entry->counters.error.elevel = edata->elevel; + snprintf(entry->counters.error.message, ERROR_MESSAGE_LEN, "%s", edata->message); + snprintf(entry->counters.error.sqlcode, SQLCODE_LEN, "%s", unpack_sql_state(edata->sqlerrcode)); - pgsm_store(entry); + pgsm_store(entry); } static void -pgsm_add_to_list(pgsmEntry *entry, char *query_text, int query_len) +pgsm_add_to_list(pgsmEntry * entry, char *query_text, int query_len) { - /* Switch to pgsm memory context */ - MemoryContext oldctx = MemoryContextSwitchTo(pgsm_get_ss()->pgsm_mem_cxt); - entry->query_text.query_pointer = pnstrdup(query_text, query_len); - lentries = lappend(lentries, entry); - MemoryContextSwitchTo(oldctx); + /* Switch to pgsm memory context */ + MemoryContext oldctx = MemoryContextSwitchTo(pgsm_get_ss()->pgsm_mem_cxt); + entry->query_text.query_pointer = pnstrdup(query_text, query_len); + lentries = lappend(lentries, entry); + MemoryContextSwitchTo(oldctx); } -static pgsmEntry* -pgsm_get_entry_for_query(uint64 queryid, PlanInfo *plan_info, const char* query_text, int query_len, bool create) -{ - pgsmEntry *entry = NULL; - ListCell *lc = NULL; +static pgsmEntry * +pgsm_get_entry_for_query(uint64 queryid, PlanInfo * plan_info, const char *query_text, int query_len, bool create) { + pgsmEntry *entry = NULL; + ListCell *lc = NULL; - /* First bet is on the last entry */ - if (lentries == NIL && !create) - return NULL; + /* First bet is on the last entry */ + if (lentries == NIL && !create) + return NULL; - if (lentries) - { - entry = (pgsmEntry *)llast(lentries); - if(entry->key.queryid == queryid) - return entry; + if (lentries) { + entry = (pgsmEntry *) llast(lentries); + if (entry->key.queryid == queryid) + return entry; - foreach(lc, lentries) - { - entry = lfirst(lc); - if(entry->key.queryid == queryid) - return entry; - } + foreach(lc, lentries) { + entry = lfirst(lc); + if (entry->key.queryid == queryid) + return entry; } - if (create && query_text) - { - /* - * At this point, we don't know which bucket this query will land in, so passing - * 0. The store function MUST later update it based on the current bucket value. - * The correct bucket value will be needed then to search the hash table, or create - * the appropriate entry. - */ - entry = pgsm_create_hash_entry(0, queryid, plan_info); + } + if (create && query_text) { + /* + * At this point, we don't know which bucket this query will land in, + * so passing 0. The store function MUST later update it based on the + * current bucket value. The correct bucket value will be needed then + * to search the hash table, or create the appropriate entry. + */ + entry = pgsm_create_hash_entry(0, queryid, plan_info); - /* Update other member that are not counters, so that we don't have to worry about these. */ - entry->pgsm_query_id = get_pgsm_query_id_hash(query_text, query_len); - pgsm_add_to_list(entry, (char *)query_text, query_len); - } + /* + * Update other member that are not counters, so that we don't have to + * worry about these. + */ + entry->pgsm_query_id = get_pgsm_query_id_hash(query_text, query_len); + pgsm_add_to_list(entry, (char *) query_text, query_len); + } - return entry; + return entry; } static void pgsm_cleanup_callback(void *arg) { - /* Reset the memory context holding the list */ - MemoryContextReset(pgsm_get_ss()->pgsm_mem_cxt); - lentries = NIL; - callback_setup = false; + /* Reset the memory context holding the list */ + MemoryContextReset(pgsm_get_ss()->pgsm_mem_cxt); + lentries = NIL; + callback_setup = false; } -/* +/* * Function encapsulating some external calls for filling up the hash key data structure. * The bucket_id may not be known at this stage. So pass any value that you may wish. */ static pgsmEntry * -pgsm_create_hash_entry(uint64 bucket_id, uint64 queryid, PlanInfo *plan_info) -{ - pgsmEntry *entry; - int sec_ctx; - bool found_client_addr = false; - char *app_name_ptr = app_name; - MemoryContext oldctx; - char *datname = NULL; - char *username = NULL; +pgsm_create_hash_entry(uint64 bucket_id, uint64 queryid, PlanInfo * plan_info) { + pgsmEntry *entry; + int sec_ctx; + bool found_client_addr = false; + char *app_name_ptr = app_name; + MemoryContext oldctx; + char *datname = NULL; + char *username = NULL; - /* Create an entry in the pgsm memory context */ - oldctx = MemoryContextSwitchTo(pgsm_get_ss()->pgsm_mem_cxt); - entry = palloc0(sizeof(pgsmEntry)); + /* Create an entry in the pgsm memory context */ + oldctx = MemoryContextSwitchTo(pgsm_get_ss()->pgsm_mem_cxt); + entry = palloc0(sizeof(pgsmEntry)); - /* - * Get the user ID. Let's use this instead of GetUserID as this - * won't throw an assertion in case of an error. - */ - GetUserIdAndSecContext((Oid *) &entry->key.userid, &sec_ctx); + /* + * Get the user ID. Let's use this instead of GetUserID as this won't + * throw an assertion in case of an error. + */ + GetUserIdAndSecContext((Oid *) & entry->key.userid, &sec_ctx); - /* Get the application name and set appid */ - app_name_len = pg_get_application_name(app_name, APPLICATIONNAME_LEN); - entry->key.appid = pgsm_hash_string((const char *)app_name_ptr, app_name_len); + /* Get the application name and set appid */ + app_name_len = pg_get_application_name(app_name, APPLICATIONNAME_LEN); + entry->key.appid = pgsm_hash_string((const char *) app_name_ptr, app_name_len); - /* client address */ - if (!pgsm_client_ip_is_valid()) - pgsm_client_ip = pg_get_client_addr(&found_client_addr); + /* client address */ + if (!pgsm_client_ip_is_valid()) + pgsm_client_ip = pg_get_client_addr(&found_client_addr); - entry->key.ip = pgsm_client_ip; + entry->key.ip = pgsm_client_ip; - /* PlanID, if there is one */ - entry->key.planid = plan_info ? plan_info->planid : 0; + /* PlanID, if there is one */ + entry->key.planid = plan_info ? plan_info->planid : 0; - /* Set remaining data */ - entry->key.dbid = MyDatabaseId; - entry->key.queryid = queryid; - entry->key.bucket_id = bucket_id; + /* Set remaining data */ + entry->key.dbid = MyDatabaseId; + entry->key.queryid = queryid; + entry->key.bucket_id = bucket_id; #if PG_VERSION_NUM < 140000 - entry->key.toplevel = 1; + entry->key.toplevel = 1; #else - entry->key.toplevel = ((exec_nested_level + plan_nested_level) == 0); + entry->key.toplevel = ((exec_nested_level + plan_nested_level) == 0); #endif - if (IsTransactionState()) - { - datname = get_database_name(entry->key.dbid); - username = GetUserNameFromId(entry->key.userid, true); - } + if (IsTransactionState()) { + datname = get_database_name(entry->key.dbid); + username = GetUserNameFromId(entry->key.userid, true); + } - if (!datname) - datname = pnstrdup("", sizeof(entry->datname) - 1); + if (!datname) + datname = pnstrdup("", sizeof(entry->datname) - 1); - if (!username) - username = pnstrdup("", sizeof(entry->username) - 1); + if (!username) + username = pnstrdup("", sizeof(entry->username) - 1); - snprintf(entry->datname, sizeof(entry->datname), "%s", datname); - snprintf(entry->username, sizeof(entry->username), "%s", username); + snprintf(entry->datname, sizeof(entry->datname), "%s", datname); + snprintf(entry->username, sizeof(entry->username), "%s", username); - pfree(datname); - pfree(username); + pfree(datname); + pfree(username); - MemoryContextSwitchTo(oldctx); + MemoryContextSwitchTo(oldctx); - return entry; + return entry; } @@ -1692,732 +1643,703 @@ pgsm_create_hash_entry(uint64 bucket_id, uint64 queryid, PlanInfo *plan_info) * query string. total_time, rows, bufusage are ignored in this case. */ static void -pgsm_store(pgsmEntry *entry) +pgsm_store(pgsmEntry * entry) { - pgsmEntry *shared_hash_entry; - pgsmSharedState *pgsm; - bool found; - uint64 bucketid; - uint64 prev_bucket_id; - bool reset = false; /* Only used in update function - HAMID */ - char *query; - int query_len; - BufferUsage bufusage; - WalUsage walusage; - JitInstrumentation jitusage; - char comments[COMMENTS_LEN] = {0}; - int comments_len; + pgsmEntry *shared_hash_entry; + pgsmSharedState *pgsm; + bool found; + uint64 bucketid; + uint64 prev_bucket_id; + bool reset = false; /* Only used in update function - HAMID */ + char *query; + int query_len; + BufferUsage bufusage; + WalUsage walusage; + JitInstrumentation jitusage; + char comments[COMMENTS_LEN] = {0}; + int comments_len; - /* Safety check... */ - if (!IsSystemInitialized()) - return; + /* Safety check... */ + if (!IsSystemInitialized()) + return; - pgsm = pgsm_get_ss(); + pgsm = pgsm_get_ss(); - /* We should lock the hash table here what if the bucket is removed; e.g. reset is called - HAMID */ - prev_bucket_id = pg_atomic_read_u64(&pgsm->current_wbucket); - bucketid = get_next_wbucket(pgsm); + /* + * We should lock the hash table here what if the bucket is removed; e.g. + * reset is called - HAMID + */ + prev_bucket_id = pg_atomic_read_u64(&pgsm->current_wbucket); + bucketid = get_next_wbucket(pgsm); - if (bucketid != prev_bucket_id) - reset = true; + if (bucketid != prev_bucket_id) + reset = true; - entry->key.bucket_id = bucketid; - query = entry->query_text.query_pointer; - query_len = strlen(query); + entry->key.bucket_id = bucketid; + query = entry->query_text.query_pointer; + query_len = strlen(query); - /* Let's do all the leg work here before we acquire any locks */ - extract_query_comments(query, comments, sizeof(comments)); - comments_len = strlen(comments); + /* Let's do all the leg work here before we acquire any locks */ + extract_query_comments(query, comments, sizeof(comments)); + comments_len = strlen(comments); - /* bufusage */ - bufusage.shared_blks_hit = entry->counters.blocks.shared_blks_hit; - bufusage.shared_blks_read = entry->counters.blocks.shared_blks_read; - bufusage.shared_blks_dirtied = entry->counters.blocks.shared_blks_dirtied; - bufusage.shared_blks_written = entry->counters.blocks.shared_blks_written; - bufusage.local_blks_hit = entry->counters.blocks.local_blks_hit; - bufusage.local_blks_read = entry->counters.blocks.local_blks_read; - bufusage.local_blks_dirtied = entry->counters.blocks.local_blks_dirtied; - bufusage.local_blks_written = entry->counters.blocks.local_blks_written; - bufusage.temp_blks_read = entry->counters.blocks.temp_blks_read; - bufusage.temp_blks_written = entry->counters.blocks.temp_blks_written; + /* bufusage */ + bufusage.shared_blks_hit = entry->counters.blocks.shared_blks_hit; + bufusage.shared_blks_read = entry->counters.blocks.shared_blks_read; + bufusage.shared_blks_dirtied = entry->counters.blocks.shared_blks_dirtied; + bufusage.shared_blks_written = entry->counters.blocks.shared_blks_written; + bufusage.local_blks_hit = entry->counters.blocks.local_blks_hit; + bufusage.local_blks_read = entry->counters.blocks.local_blks_read; + bufusage.local_blks_dirtied = entry->counters.blocks.local_blks_dirtied; + bufusage.local_blks_written = entry->counters.blocks.local_blks_written; + bufusage.temp_blks_read = entry->counters.blocks.temp_blks_read; + bufusage.temp_blks_written = entry->counters.blocks.temp_blks_written; - memcpy(&bufusage.blk_read_time, &entry->counters.blocks.instr_blk_read_time, sizeof(instr_time)); - memcpy(&bufusage.blk_write_time, &entry->counters.blocks.instr_blk_write_time, sizeof(instr_time)); + memcpy(&bufusage.blk_read_time, &entry->counters.blocks.instr_blk_read_time, sizeof(instr_time)); + memcpy(&bufusage.blk_write_time, &entry->counters.blocks.instr_blk_write_time, sizeof(instr_time)); - #if PG_VERSION_NUM >= 150000 - memcpy(&bufusage.temp_blk_read_time, &entry->counters.blocks.instr_temp_blk_read_time, sizeof(instr_time)); - memcpy(&bufusage.temp_blk_write_time, &entry->counters.blocks.instr_temp_blk_write_time, sizeof(instr_time)); - #endif +#if PG_VERSION_NUM >= 150000 + memcpy(&bufusage.temp_blk_read_time, &entry->counters.blocks.instr_temp_blk_read_time, sizeof(instr_time)); + memcpy(&bufusage.temp_blk_write_time, &entry->counters.blocks.instr_temp_blk_write_time, sizeof(instr_time)); +#endif - /* walusage */ - walusage.wal_records = entry->counters.walusage.wal_records; - walusage.wal_fpi = entry->counters.walusage.wal_fpi; - walusage.wal_bytes = entry->counters.walusage.wal_bytes; + /* walusage */ + walusage.wal_records = entry->counters.walusage.wal_records; + walusage.wal_fpi = entry->counters.walusage.wal_fpi; + walusage.wal_bytes = entry->counters.walusage.wal_bytes; - /* jit */ - jitusage.created_functions = entry->counters.jitinfo.jit_functions; - memcpy(&jitusage.generation_counter, &entry->counters.jitinfo.instr_generation_counter, sizeof(instr_time)); - memcpy(&jitusage.inlining_counter, &entry->counters.jitinfo.instr_inlining_counter, sizeof(instr_time)); - memcpy(&jitusage.optimization_counter, &entry->counters.jitinfo.instr_optimization_counter, sizeof(instr_time)); - memcpy(&jitusage.emission_counter, &entry->counters.jitinfo.instr_emission_counter, sizeof(instr_time)); + /* jit */ + jitusage.created_functions = entry->counters.jitinfo.jit_functions; + memcpy(&jitusage.generation_counter, &entry->counters.jitinfo.instr_generation_counter, sizeof(instr_time)); + memcpy(&jitusage.inlining_counter, &entry->counters.jitinfo.instr_inlining_counter, sizeof(instr_time)); + memcpy(&jitusage.optimization_counter, &entry->counters.jitinfo.instr_optimization_counter, sizeof(instr_time)); + memcpy(&jitusage.emission_counter, &entry->counters.jitinfo.instr_emission_counter, sizeof(instr_time)); - /* - * Acquire a share lock to start with. We'd have to acquire exclusive - * if we need ot create the entry. - */ - LWLockAcquire(pgsm->lock, LW_SHARED); - shared_hash_entry = (pgsmEntry *) pgsm_hash_find(get_pgsmHash(), &entry->key, &found); + /* + * Acquire a share lock to start with. We'd have to acquire exclusive if + * we need ot create the entry. + */ + LWLockAcquire(pgsm->lock, LW_SHARED); + shared_hash_entry = (pgsmEntry *) pgsm_hash_find(get_pgsmHash(), &entry->key, &found); - if (!shared_hash_entry) - { - dsa_pointer dsa_query_pointer; - dsa_area *query_dsa_area; - char *query_buff; + if (!shared_hash_entry) { + dsa_pointer dsa_query_pointer; + dsa_area *query_dsa_area; + char *query_buff; - /* New query, truncate length if necessary. */ - if (query_len > pgsm_query_max_len) - query_len = pgsm_query_max_len; + /* New query, truncate length if necessary. */ + if (query_len > pgsm_query_max_len) + query_len = pgsm_query_max_len; - /* Save the query text in raw dsa area */ - query_dsa_area = get_dsa_area_for_query_text(); - dsa_query_pointer = dsa_allocate_extended(query_dsa_area, query_len+1, DSA_ALLOC_NO_OOM | DSA_ALLOC_ZERO); - if (!DsaPointerIsValid(dsa_query_pointer)) - { - LWLockRelease(pgsm->lock); - return; - } - - /* Get the memory address from DSA pointer and copy the query text in local variable */ - query_buff = dsa_get_address(query_dsa_area, dsa_query_pointer); - memcpy(query_buff, query, query_len); - - LWLockRelease(pgsm->lock); - LWLockAcquire(pgsm->lock, LW_EXCLUSIVE); - - /* OK to create a new hashtable entry */ - PGSM_DISABLE_ERROR_CAPUTRE(); - { - PG_TRY(); - { - shared_hash_entry = hash_entry_alloc(pgsm, &entry->key, GetDatabaseEncoding()); - } - PG_CATCH(); - { - LWLockRelease(pgsm->lock); - - if (DsaPointerIsValid(dsa_query_pointer)) - dsa_free(query_dsa_area, dsa_query_pointer); - PG_RE_THROW(); - } - PG_END_TRY(); - }PGSM_END_DISABLE_ERROR_CAPTURE(); - - if (shared_hash_entry == NULL) - { - /* Out of memory; report only if the state has changed now. Otherwise we risk filling up the log file with these message. */ - if (!IsSystemOOM()) - { - pgsm->pgsm_oom = true; - - ereport(WARNING, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("[pg_stat_monitor] pgsm_store: Hash table is out of memory and can no longer store queries!"), - errdetail("You may reset the view or when the buckets are deallocated, pg_stat_monitor will resume saving " \ - "queries. Alternatively, try increasing the value of pg_stat_monitor.pgsm_max."))); - } - - LWLockRelease(pgsm->lock); - - if (DsaPointerIsValid(dsa_query_pointer)) - dsa_free(query_dsa_area, dsa_query_pointer); - - return; - } - else - { - /* If we got a new entry, reset the oom value false */ - pgsm->pgsm_oom = false; - } - - /* If we already have the pointer set, free this one */ - if (DsaPointerIsValid(shared_hash_entry->query_text.query_pos)) - dsa_free(query_dsa_area, dsa_query_pointer); - else - shared_hash_entry->query_text.query_pos = dsa_query_pointer; - - shared_hash_entry->pgsm_query_id = entry->pgsm_query_id; - shared_hash_entry->encoding = entry->encoding; - shared_hash_entry->counters.info.cmd_type = entry->counters.info.cmd_type; - - snprintf(shared_hash_entry->datname, sizeof(shared_hash_entry->datname), "%s", entry->datname); - snprintf(shared_hash_entry->username, sizeof(shared_hash_entry->username), "%s", entry->username); + /* Save the query text in raw dsa area */ + query_dsa_area = get_dsa_area_for_query_text(); + dsa_query_pointer = dsa_allocate_extended(query_dsa_area, query_len + 1, DSA_ALLOC_NO_OOM | DSA_ALLOC_ZERO); + if (!DsaPointerIsValid(dsa_query_pointer)) { + LWLockRelease(pgsm->lock); + return; } - pgsm_update_entry(shared_hash_entry, /* entry */ - query, /* query */ - comments, /* comments */ - comments_len, /* comments length */ - &entry->counters.planinfo, /* PlanInfo */ - &entry->counters.sysinfo, /* SysInfo */ - &entry->counters.error, /* ErrorInfo */ - entry->counters.plantime.total_time, /* plan_total_time */ - entry->counters.time.total_time, /* exec_total_time */ - entry->counters.calls.rows, /* rows */ - &bufusage, /* bufusage */ - &walusage, /* walusage */ - &jitusage, /* jitusage */ - reset, /* reset */ - PGSM_STORE); + /* + * Get the memory address from DSA pointer and copy the query text in + * local variable + */ + query_buff = dsa_get_address(query_dsa_area, dsa_query_pointer); + memcpy(query_buff, query, query_len); - memset(&entry->counters, 0, sizeof(entry->counters)); LWLockRelease(pgsm->lock); + LWLockAcquire(pgsm->lock, LW_EXCLUSIVE); + + /* OK to create a new hashtable entry */ + PGSM_DISABLE_ERROR_CAPUTRE(); + { + PG_TRY(); + { + shared_hash_entry = hash_entry_alloc(pgsm, &entry->key, GetDatabaseEncoding()); + } + PG_CATCH(); + { + LWLockRelease(pgsm->lock); + + if (DsaPointerIsValid(dsa_query_pointer)) + dsa_free(query_dsa_area, dsa_query_pointer); + PG_RE_THROW(); + } + PG_END_TRY(); + } PGSM_END_DISABLE_ERROR_CAPTURE(); + + if (shared_hash_entry == NULL) { + /* + * Out of memory; report only if the state has changed now. + * Otherwise we risk filling up the log file with these message. + */ + if (!IsSystemOOM()) { + pgsm->pgsm_oom = true; + + ereport(WARNING, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("[pg_stat_monitor] pgsm_store: Hash table is out of memory and can no longer store queries!"), + errdetail("You may reset the view or when the buckets are deallocated, pg_stat_monitor will resume saving " \ + "queries. Alternatively, try increasing the value of pg_stat_monitor.pgsm_max."))); + } + + LWLockRelease(pgsm->lock); + + if (DsaPointerIsValid(dsa_query_pointer)) + dsa_free(query_dsa_area, dsa_query_pointer); + + return; + } else { + /* If we got a new entry, reset the oom value false */ + pgsm->pgsm_oom = false; + } + + /* If we already have the pointer set, free this one */ + if (DsaPointerIsValid(shared_hash_entry->query_text.query_pos)) + dsa_free(query_dsa_area, dsa_query_pointer); + else + shared_hash_entry->query_text.query_pos = dsa_query_pointer; + + shared_hash_entry->pgsm_query_id = entry->pgsm_query_id; + shared_hash_entry->encoding = entry->encoding; + shared_hash_entry->counters.info.cmd_type = entry->counters.info.cmd_type; + + snprintf(shared_hash_entry->datname, sizeof(shared_hash_entry->datname), "%s", entry->datname); + snprintf(shared_hash_entry->username, sizeof(shared_hash_entry->username), "%s", entry->username); + } + + pgsm_update_entry(shared_hash_entry, /* entry */ + query, /* query */ + comments, /* comments */ + comments_len, /* comments length */ + &entry->counters.planinfo, /* PlanInfo */ + &entry->counters.sysinfo, /* SysInfo */ + &entry->counters.error, /* ErrorInfo */ + entry->counters.plantime.total_time, /* plan_total_time */ + entry->counters.time.total_time, /* exec_total_time */ + entry->counters.calls.rows, /* rows */ + &bufusage, /* bufusage */ + &walusage, /* walusage */ + &jitusage, /* jitusage */ + reset, /* reset */ + PGSM_STORE); + + memset(&entry->counters, 0, sizeof(entry->counters)); + LWLockRelease(pgsm->lock); } /* * Reset all statement statistics. */ Datum -pg_stat_monitor_reset(PG_FUNCTION_ARGS) -{ - pgsmSharedState *pgsm; +pg_stat_monitor_reset(PG_FUNCTION_ARGS) { + pgsmSharedState *pgsm; - /* Safety check... */ - if (!IsSystemInitialized()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries"))); + /* Safety check... */ + if (!IsSystemInitialized()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries"))); - pgsm = pgsm_get_ss(); - LWLockAcquire(pgsm->lock, LW_EXCLUSIVE); - hash_entry_dealloc(-1, -1, NULL); + pgsm = pgsm_get_ss(); + LWLockAcquire(pgsm->lock, LW_EXCLUSIVE); + hash_entry_dealloc(-1, -1, NULL); - LWLockRelease(pgsm->lock); - PG_RETURN_VOID(); + LWLockRelease(pgsm->lock); + PG_RETURN_VOID(); } Datum -pg_stat_monitor_1_0(PG_FUNCTION_ARGS) -{ - pg_stat_monitor_internal(fcinfo, PGSM_V1_0, true); - return (Datum) 0; +pg_stat_monitor_1_0(PG_FUNCTION_ARGS) { + pg_stat_monitor_internal(fcinfo, PGSM_V1_0, true); + return (Datum) 0; } Datum -pg_stat_monitor_2_0(PG_FUNCTION_ARGS) -{ - pg_stat_monitor_internal(fcinfo, PGSM_V2_0, true); - return (Datum) 0; +pg_stat_monitor_2_0(PG_FUNCTION_ARGS) { + pg_stat_monitor_internal(fcinfo, PGSM_V2_0, true); + return (Datum) 0; } /* * Legacy entry point for pg_stat_monitor() API versions 1.0 */ Datum -pg_stat_monitor(PG_FUNCTION_ARGS) -{ - pg_stat_monitor_internal(fcinfo, PGSM_V1_0, true); - return (Datum) 0; +pg_stat_monitor(PG_FUNCTION_ARGS) { + pg_stat_monitor_internal(fcinfo, PGSM_V1_0, true); + return (Datum) 0; } static bool IsBucketValid(uint64 bucketid) { - long secs; - int microsecs; - TimestampTz current_tz = GetCurrentTimestamp(); - pgsmSharedState *pgsm = pgsm_get_ss(); + long secs; + int microsecs; + TimestampTz current_tz = GetCurrentTimestamp(); + pgsmSharedState *pgsm = pgsm_get_ss(); - TimestampDifference(pgsm->bucket_start_time[bucketid], current_tz,&secs, µsecs); + TimestampDifference(pgsm->bucket_start_time[bucketid], current_tz, &secs, µsecs); - if (secs > (pgsm_bucket_time * pgsm_max_buckets)) - return false; - return true; + if (secs > (pgsm_bucket_time * pgsm_max_buckets)) + return false; + return true; } /* Common code for all versions of pg_stat_monitor() */ static void pg_stat_monitor_internal(FunctionCallInfo fcinfo, - pgsmVersion api_version, - bool showtext) + pgsmVersion api_version, + bool showtext) { - ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; - TupleDesc tupdesc; - Tuplestorestate *tupstore; - MemoryContext per_query_ctx; - MemoryContext oldcontext; - PGSM_HASH_SEQ_STATUS hstat; - pgsmEntry *entry; - pgsmSharedState *pgsm; - int expected_columns = (api_version >= PGSM_V2_0)?PG_STAT_MONITOR_COLS_V2_0:PG_STAT_MONITOR_COLS_V1_0; + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + PGSM_HASH_SEQ_STATUS hstat; + pgsmEntry *entry; + pgsmSharedState *pgsm; + int expected_columns = (api_version >= PGSM_V2_0) ? PG_STAT_MONITOR_COLS_V2_0 : PG_STAT_MONITOR_COLS_V1_0; - /* Disallow old api usage */ - if (api_version < PGSM_V2_0) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("[pg_stat_monitor] pg_stat_monitor_internal: API version not supported."), - errhint("Upgrade pg_stat_monitor extension"))); - /* Safety check... */ - if (!IsSystemInitialized()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Must be loaded via shared_preload_libraries."))); + /* Disallow old api usage */ + if (api_version < PGSM_V2_0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("[pg_stat_monitor] pg_stat_monitor_internal: API version not supported."), + errhint("Upgrade pg_stat_monitor extension"))); + /* Safety check... */ + if (!IsSystemInitialized()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Must be loaded via shared_preload_libraries."))); - /* Out of memory? */ - if (IsSystemOOM()) - ereport(WARNING, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Hash table is out of memory and can no longer store queries!"), - errdetail("You may reset the view or when the buckets are deallocated, pg_stat_monitor will resume saving " \ - "queries. Alternatively, try increasing the value of pg_stat_monitor.pgsm_max."))); + /* Out of memory? */ + if (IsSystemOOM()) + ereport(WARNING, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Hash table is out of memory and can no longer store queries!"), + errdetail("You may reset the view or when the buckets are deallocated, pg_stat_monitor will resume saving " \ + "queries. Alternatively, try increasing the value of pg_stat_monitor.pgsm_max."))); - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Set-valued function called in context that cannot accept a set."))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Materialize mode required, but it is not " \ - "allowed in this context."))); + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Set-valued function called in context that cannot accept a set."))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Materialize mode required, but it is not " \ + "allowed in this context."))); - /* Switch into long-lived context to construct returned data structures */ - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; - oldcontext = MemoryContextSwitchTo(per_query_ctx); + /* Switch into long-lived context to construct returned data structures */ + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "[pg_stat_monitor] pg_stat_monitor_internal: Return type must be a row type."); + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "[pg_stat_monitor] pg_stat_monitor_internal: Return type must be a row type."); - if (tupdesc->natts != expected_columns) - elog(ERROR, "[pg_stat_monitor] pg_stat_monitor_internal: Incorrect number of output arguments, received %d, required %d.", tupdesc->natts, expected_columns); + if (tupdesc->natts != expected_columns) + elog(ERROR, "[pg_stat_monitor] pg_stat_monitor_internal: Incorrect number of output arguments, received %d, required %d.", tupdesc->natts, expected_columns); - tupstore = tuplestore_begin_heap(true, false, work_mem); - rsinfo->returnMode = SFRM_Materialize; - rsinfo->setResult = tupstore; - rsinfo->setDesc = tupdesc; + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; - MemoryContextSwitchTo(oldcontext); + MemoryContextSwitchTo(oldcontext); - pgsm = pgsm_get_ss(); - LWLockAcquire(pgsm->lock, LW_SHARED); - pgsm_hash_seq_init(&hstat, get_pgsmHash(), false); + pgsm = pgsm_get_ss(); + LWLockAcquire(pgsm->lock, LW_SHARED); + pgsm_hash_seq_init(&hstat, get_pgsmHash(), false); - while ((entry = pgsm_hash_seq_next(&hstat)) != NULL) - { - Datum values[PG_STAT_MONITOR_COLS] = {0}; - bool nulls[PG_STAT_MONITOR_COLS] = {0}; - int i = 0; - Counters tmp; - double stddev; - uint64 queryid = entry->key.queryid; - int64 bucketid = entry->key.bucket_id; - Oid dbid = entry->key.dbid; - Oid userid = entry->key.userid; - uint64 ip = (uint64)entry->key.ip; - uint64 planid = entry->key.planid; - uint64 pgsm_query_id = entry->pgsm_query_id; - dsa_area *query_dsa_area; - char *query_ptr; - char *query_txt = NULL; - char *parent_query_txt = NULL; + while ((entry = pgsm_hash_seq_next(&hstat)) != NULL) { + Datum values[PG_STAT_MONITOR_COLS] = {0}; + bool nulls[PG_STAT_MONITOR_COLS] = {0}; + int i = 0; + Counters tmp; + double stddev; + uint64 queryid = entry->key.queryid; + int64 bucketid = entry->key.bucket_id; + Oid dbid = entry->key.dbid; + Oid userid = entry->key.userid; + uint64 ip = (uint64) entry->key.ip; + uint64 planid = entry->key.planid; + uint64 pgsm_query_id = entry->pgsm_query_id; + dsa_area *query_dsa_area; + char *query_ptr; + char *query_txt = NULL; + char *parent_query_txt = NULL; - bool toplevel = entry->key.toplevel; + bool toplevel = entry->key.toplevel; #if PG_VERSION_NUM < 140000 - bool is_allowed_role = is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_STATS); + bool is_allowed_role = is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_STATS); #else - bool is_allowed_role = is_member_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS); + bool is_allowed_role = is_member_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS); #endif - /* Load the query text from dsa area */ - if (DsaPointerIsValid(entry->query_text.query_pos)) - { - query_dsa_area = get_dsa_area_for_query_text(); - query_ptr = dsa_get_address(query_dsa_area, entry->query_text.query_pos); - query_txt = pstrdup(query_ptr); - } - else - query_txt = pstrdup("Query string not available");/* Should never happen. Just a safty check*/ + /* Load the query text from dsa area */ + if (DsaPointerIsValid(entry->query_text.query_pos)) { + query_dsa_area = get_dsa_area_for_query_text(); + query_ptr = dsa_get_address(query_dsa_area, entry->query_text.query_pos); + query_txt = pstrdup(query_ptr); + } else + query_txt = pstrdup("Query string not available"); /* Should never happen. + * Just a safty check */ - /* copy counters to a local variable to keep locking time short */ - { - volatile pgsmEntry *e = (volatile pgsmEntry *) entry; + /* copy counters to a local variable to keep locking time short */ + { + volatile pgsmEntry *e = (volatile pgsmEntry *) entry; - SpinLockAcquire(&e->mutex); - tmp = e->counters; - SpinLockRelease(&e->mutex); - } - - /* - * In case that query plan is enabled, there is no need to show 0 - * planid query - */ - if (tmp.info.cmd_type == CMD_SELECT && pgsm_enable_query_plan && planid == 0) - continue; - - if (!IsBucketValid(bucketid)) - { - continue; - } - - /* read the parent query text if any */ - if (tmp.info.parentid != UINT64CONST(0)) - { - if (DsaPointerIsValid(tmp.info.parent_query)) - { - query_dsa_area = get_dsa_area_for_query_text(); - query_ptr = dsa_get_address(query_dsa_area, tmp.info.parent_query); - parent_query_txt = pstrdup(query_ptr); - } - else - parent_query_txt = pstrdup("parent query text not available"); - } - /* bucketid at column number 0 */ - values[i++] = Int64GetDatumFast(bucketid); - - /* userid at column number 1 */ - values[i++] = ObjectIdGetDatum(userid); - - /* userid at column number 1 */ - values[i++] = CStringGetTextDatum(entry->username); - - /* dbid at column number 2 */ - values[i++] = ObjectIdGetDatum(dbid); - - /* userid at column number 1 */ - values[i++] = CStringGetTextDatum(entry->datname); - - /* - * ip address at column number 3, Superusers or members of - * pg_read_all_stats members are allowed - */ - if (is_allowed_role || userid == GetUserId()) - values[i++] = UInt32GetDatum(ip); - else - nulls[i++] = true; - - /* queryid at column number 4 */ - values[i++] = UInt64GetDatum(queryid); - - /* planid at column number 5 */ - if (planid) - { - values[i++] = UInt64GetDatum(planid); - } - else - { - nulls[i++] = true; - } - if (is_allowed_role || userid == GetUserId()) - { - if (showtext) - { - char *enc; - - /* query at column number 6 */ - enc = pg_any_to_server(query_txt, strlen(query_txt), GetDatabaseEncoding()); - values[i++] = CStringGetTextDatum(enc); - if (enc != query_txt) - pfree(enc); - /* plan at column number 7 */ - if (planid && tmp.planinfo.plan_text[0]) - values[i++] = CStringGetTextDatum(tmp.planinfo.plan_text); - else - nulls[i++] = true; - } - else - { - /* query at column number 6 */ - nulls[i++] = true; - /* plan at column number 7 */ - nulls[i++] = true; - } - } - else - { - /* query text at column number 6 */ - values[i++] = CStringGetTextDatum(""); - values[i++] = CStringGetTextDatum(""); - } - - if (pgsm_query_id) - values[i++] = UInt64GetDatum(pgsm_query_id); - else - nulls[i++] = true; - - /* parentid at column number 9 */ - if (tmp.info.parentid != UINT64CONST(0)) - { - values[i++] = UInt64GetDatum(tmp.info.parentid); - values[i++] = CStringGetTextDatum(parent_query_txt); - } - else - { - nulls[i++] = true; - nulls[i++] = true; - } - - /* application_name at column number 10 */ - if (strlen(tmp.info.application_name) > 0) - values[i++] = CStringGetTextDatum(tmp.info.application_name); - else - nulls[i++] = true; - - /* relations at column number 10 */ - if (tmp.info.num_relations > 0) - { - int j; - char *text_str = palloc0(TOTAL_RELS_LENGTH); - char *tmp_str = palloc0(TOTAL_RELS_LENGTH); - bool first = true; - - /* - * Need to calculate the actual size, and avoid unnessary memory - * usage - */ - for (j = 0; j < tmp.info.num_relations; j++) - { - if (first) - { - snprintf(text_str, 1024, "%s", tmp.info.relations[j]); - first = false; - continue; - } - snprintf(tmp_str, 1024, "%s,%s", text_str, tmp.info.relations[j]); - snprintf(text_str, 1024, "%s", tmp_str); - } - pfree(tmp_str); - values[i++] = CStringGetTextDatum(text_str); - } - else - nulls[i++] = true; - - /* cmd_type at column number 11 */ - if (tmp.info.cmd_type == CMD_NOTHING) - nulls[i++] = true; - else - values[i++] = Int64GetDatumFast((int64)tmp.info.cmd_type); - - /* elevel at column number 12 */ - values[i++] = Int64GetDatumFast(tmp.error.elevel); - - /* sqlcode at column number 13 */ - if (strlen(tmp.error.sqlcode) == 0) - nulls[i++] = true; - else - values[i++] = CStringGetTextDatum(tmp.error.sqlcode); - - /* message at column number 14 */ - if (strlen(tmp.error.message) == 0) - nulls[i++] = true; - else - values[i++] = CStringGetTextDatum(tmp.error.message); - - /* bucket_start_time at column number 15 */ - values[i++] = TimestampTzGetDatum(pgsm->bucket_start_time[entry->key.bucket_id]); - - if (tmp.calls.calls == 0) - { - /* Query of pg_stat_monitor itslef started from zero count */ - tmp.calls.calls++; - tmp.resp_calls[0]++; - } - - /* calls at column number 16 */ - values[i++] = Int64GetDatumFast(tmp.calls.calls); - - /* total_time at column number 17 */ - values[i++] = Float8GetDatumFast(tmp.time.total_time); - - /* min_time at column number 18 */ - values[i++] = Float8GetDatumFast(tmp.time.min_time); - - /* max_time at column number 19 */ - values[i++] = Float8GetDatumFast(tmp.time.max_time); - - /* mean_time at column number 20 */ - values[i++] = Float8GetDatumFast(tmp.time.mean_time); - if (tmp.calls.calls > 1) - stddev = sqrt(tmp.time.sum_var_time / tmp.calls.calls); - else - stddev = 0.0; - - /* calls at column number 21 */ - values[i++] = Float8GetDatumFast(stddev); - - /* calls at column number 22 */ - values[i++] = Int64GetDatumFast(tmp.calls.rows); - - if (tmp.calls.calls == 0) - { - /* Query of pg_stat_monitor itslef started from zero count */ - tmp.calls.calls++; - tmp.resp_calls[0]++; - } - - /* calls at column number 23 */ - values[i++] = Int64GetDatumFast(tmp.plancalls.calls); - - /* total_time at column number 24 */ - values[i++] = Float8GetDatumFast(tmp.plantime.total_time); - - /* min_time at column number 25 */ - values[i++] = Float8GetDatumFast(tmp.plantime.min_time); - - /* max_time at column number 26 */ - values[i++] = Float8GetDatumFast(tmp.plantime.max_time); - - /* mean_time at column number 27 */ - values[i++] = Float8GetDatumFast(tmp.plantime.mean_time); - if (tmp.plancalls.calls > 1) - stddev = sqrt(tmp.plantime.sum_var_time / tmp.plancalls.calls); - else - stddev = 0.0; - - /* calls at column number 28 */ - values[i++] = Float8GetDatumFast(stddev); - - /* blocks are from column number 29 - 40 */ - values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_hit); - values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_read); - values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_dirtied); - values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_written); - values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_hit); - values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_read); - values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_dirtied); - values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_written); - values[i++] = Int64GetDatumFast(tmp.blocks.temp_blks_read); - values[i++] = Int64GetDatumFast(tmp.blocks.temp_blks_written); - values[i++] = Float8GetDatumFast(tmp.blocks.blk_read_time); - values[i++] = Float8GetDatumFast(tmp.blocks.blk_write_time); - values[i++] = Float8GetDatumFast(tmp.blocks.temp_blk_read_time); - values[i++] = Float8GetDatumFast(tmp.blocks.temp_blk_write_time); - - /* resp_calls at column number 41 */ - values[i++] = IntArrayGetTextDatum(tmp.resp_calls, hist_bucket_count_total); - - /* utime at column number 42 */ - values[i++] = Float8GetDatumFast(tmp.sysinfo.utime); - - /* stime at column number 43 */ - values[i++] = Float8GetDatumFast(tmp.sysinfo.stime); - { - char buf[256]; - Datum wal_bytes; - - /* wal_records at column number 44 */ - values[i++] = Int64GetDatumFast(tmp.walusage.wal_records); - - /* wal_fpi at column number 45 */ - values[i++] = Int64GetDatumFast(tmp.walusage.wal_fpi); - - snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.walusage.wal_bytes); - - /* Convert to numeric */ - wal_bytes = DirectFunctionCall3(numeric_in, - CStringGetDatum(buf), - ObjectIdGetDatum(0), - Int32GetDatum(-1)); - /* wal_bytes at column number 46 */ - values[i++] = wal_bytes; - - /* application_name at column number 47 */ - if (strlen(tmp.info.comments) > 0) - values[i++] = CStringGetTextDatum(tmp.info.comments); - else - nulls[i++] = true; - - values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_functions); - values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_generation_time); - values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_inlining_count); - values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_inlining_time); - values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_optimization_count); - values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_optimization_time); - values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_emission_count); - values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_emission_time); - } - values[i++] = BoolGetDatum(toplevel); - values[i++] = BoolGetDatum(pg_atomic_read_u64(&pgsm->current_wbucket) != bucketid); - - /* clean up and return the tuplestore */ - tuplestore_putvalues(tupstore, tupdesc, values, nulls); - - if(query_txt) - pfree(query_txt); - if(parent_query_txt) - pfree(parent_query_txt); + SpinLockAcquire(&e->mutex); + tmp = e->counters; + SpinLockRelease(&e->mutex); } + + /* + * In case that query plan is enabled, there is no need to show 0 + * planid query + */ + if (tmp.info.cmd_type == CMD_SELECT && pgsm_enable_query_plan && planid == 0) + continue; + + if (!IsBucketValid(bucketid)) { + continue; + } + + /* read the parent query text if any */ + if (tmp.info.parentid != UINT64CONST(0)) { + if (DsaPointerIsValid(tmp.info.parent_query)) { + query_dsa_area = get_dsa_area_for_query_text(); + query_ptr = dsa_get_address(query_dsa_area, tmp.info.parent_query); + parent_query_txt = pstrdup(query_ptr); + } else + parent_query_txt = pstrdup("parent query text not available"); + } + /* bucketid at column number 0 */ + values[i++] = Int64GetDatumFast(bucketid); + + /* userid at column number 1 */ + values[i++] = ObjectIdGetDatum(userid); + + /* userid at column number 1 */ + values[i++] = CStringGetTextDatum(entry->username); + + /* dbid at column number 2 */ + values[i++] = ObjectIdGetDatum(dbid); + + /* userid at column number 1 */ + values[i++] = CStringGetTextDatum(entry->datname); + + /* + * ip address at column number 3, Superusers or members of + * pg_read_all_stats members are allowed + */ + if (is_allowed_role || userid == GetUserId()) + values[i++] = UInt32GetDatum(ip); + else + nulls[i++] = true; + + /* queryid at column number 4 */ + values[i++] = UInt64GetDatum(queryid); + + /* planid at column number 5 */ + if (planid) { + values[i++] = UInt64GetDatum(planid); + } else { + nulls[i++] = true; + } + if (is_allowed_role || userid == GetUserId()) { + if (showtext) { + char *enc; + + /* query at column number 6 */ + enc = pg_any_to_server(query_txt, strlen(query_txt), GetDatabaseEncoding()); + values[i++] = CStringGetTextDatum(enc); + if (enc != query_txt) + pfree(enc); + /* plan at column number 7 */ + if (planid && tmp.planinfo.plan_text[0]) + values[i++] = CStringGetTextDatum(tmp.planinfo.plan_text); + else + nulls[i++] = true; + } else { + /* query at column number 6 */ + nulls[i++] = true; + /* plan at column number 7 */ + nulls[i++] = true; + } + } else { + /* query text at column number 6 */ + values[i++] = CStringGetTextDatum(""); + values[i++] = CStringGetTextDatum(""); + } + + if (pgsm_query_id) + values[i++] = UInt64GetDatum(pgsm_query_id); + else + nulls[i++] = true; + + /* parentid at column number 9 */ + if (tmp.info.parentid != UINT64CONST(0)) { + values[i++] = UInt64GetDatum(tmp.info.parentid); + values[i++] = CStringGetTextDatum(parent_query_txt); + } else { + nulls[i++] = true; + nulls[i++] = true; + } + + /* application_name at column number 10 */ + if (strlen(tmp.info.application_name) > 0) + values[i++] = CStringGetTextDatum(tmp.info.application_name); + else + nulls[i++] = true; + + /* relations at column number 10 */ + if (tmp.info.num_relations > 0) { + int j; + char *text_str = palloc0(TOTAL_RELS_LENGTH); + char *tmp_str = palloc0(TOTAL_RELS_LENGTH); + bool first = true; + + /* + * Need to calculate the actual size, and avoid unnessary memory + * usage + */ + for (j = 0; j < tmp.info.num_relations; j++) { + if (first) { + snprintf(text_str, 1024, "%s", tmp.info.relations[j]); + first = false; + continue; + } + snprintf(tmp_str, 1024, "%s,%s", text_str, tmp.info.relations[j]); + snprintf(text_str, 1024, "%s", tmp_str); + } + pfree(tmp_str); + values[i++] = CStringGetTextDatum(text_str); + } else + nulls[i++] = true; + + /* cmd_type at column number 11 */ + if (tmp.info.cmd_type == CMD_NOTHING) + nulls[i++] = true; + else + values[i++] = Int64GetDatumFast((int64) tmp.info.cmd_type); + + /* elevel at column number 12 */ + values[i++] = Int64GetDatumFast(tmp.error.elevel); + + /* sqlcode at column number 13 */ + if (strlen(tmp.error.sqlcode) == 0) + nulls[i++] = true; + else + values[i++] = CStringGetTextDatum(tmp.error.sqlcode); + + /* message at column number 14 */ + if (strlen(tmp.error.message) == 0) + nulls[i++] = true; + else + values[i++] = CStringGetTextDatum(tmp.error.message); + + /* bucket_start_time at column number 15 */ + values[i++] = TimestampTzGetDatum(pgsm->bucket_start_time[entry->key.bucket_id]); + + if (tmp.calls.calls == 0) { + /* Query of pg_stat_monitor itslef started from zero count */ + tmp.calls.calls++; + tmp.resp_calls[0]++; + } + + /* calls at column number 16 */ + values[i++] = Int64GetDatumFast(tmp.calls.calls); + + /* total_time at column number 17 */ + values[i++] = Float8GetDatumFast(tmp.time.total_time); + + /* min_time at column number 18 */ + values[i++] = Float8GetDatumFast(tmp.time.min_time); + + /* max_time at column number 19 */ + values[i++] = Float8GetDatumFast(tmp.time.max_time); + + /* mean_time at column number 20 */ + values[i++] = Float8GetDatumFast(tmp.time.mean_time); + if (tmp.calls.calls > 1) + stddev = sqrt(tmp.time.sum_var_time / tmp.calls.calls); + else + stddev = 0.0; + + /* calls at column number 21 */ + values[i++] = Float8GetDatumFast(stddev); + + /* calls at column number 22 */ + values[i++] = Int64GetDatumFast(tmp.calls.rows); + + if (tmp.calls.calls == 0) { + /* Query of pg_stat_monitor itslef started from zero count */ + tmp.calls.calls++; + tmp.resp_calls[0]++; + } + + /* calls at column number 23 */ + values[i++] = Int64GetDatumFast(tmp.plancalls.calls); + + /* total_time at column number 24 */ + values[i++] = Float8GetDatumFast(tmp.plantime.total_time); + + /* min_time at column number 25 */ + values[i++] = Float8GetDatumFast(tmp.plantime.min_time); + + /* max_time at column number 26 */ + values[i++] = Float8GetDatumFast(tmp.plantime.max_time); + + /* mean_time at column number 27 */ + values[i++] = Float8GetDatumFast(tmp.plantime.mean_time); + if (tmp.plancalls.calls > 1) + stddev = sqrt(tmp.plantime.sum_var_time / tmp.plancalls.calls); + else + stddev = 0.0; + + /* calls at column number 28 */ + values[i++] = Float8GetDatumFast(stddev); + + /* blocks are from column number 29 - 40 */ + values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_hit); + values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_read); + values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_dirtied); + values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_written); + values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_hit); + values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_read); + values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_dirtied); + values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_written); + values[i++] = Int64GetDatumFast(tmp.blocks.temp_blks_read); + values[i++] = Int64GetDatumFast(tmp.blocks.temp_blks_written); + values[i++] = Float8GetDatumFast(tmp.blocks.blk_read_time); + values[i++] = Float8GetDatumFast(tmp.blocks.blk_write_time); + values[i++] = Float8GetDatumFast(tmp.blocks.temp_blk_read_time); + values[i++] = Float8GetDatumFast(tmp.blocks.temp_blk_write_time); + + /* resp_calls at column number 41 */ + values[i++] = IntArrayGetTextDatum(tmp.resp_calls, hist_bucket_count_total); + + /* utime at column number 42 */ + values[i++] = Float8GetDatumFast(tmp.sysinfo.utime); + + /* stime at column number 43 */ + values[i++] = Float8GetDatumFast(tmp.sysinfo.stime); + { + char buf[256]; + Datum wal_bytes; + + /* wal_records at column number 44 */ + values[i++] = Int64GetDatumFast(tmp.walusage.wal_records); + + /* wal_fpi at column number 45 */ + values[i++] = Int64GetDatumFast(tmp.walusage.wal_fpi); + + snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.walusage.wal_bytes); + + /* Convert to numeric */ + wal_bytes = DirectFunctionCall3(numeric_in, + CStringGetDatum(buf), + ObjectIdGetDatum(0), + Int32GetDatum(-1)); + /* wal_bytes at column number 46 */ + values[i++] = wal_bytes; + + /* application_name at column number 47 */ + if (strlen(tmp.info.comments) > 0) + values[i++] = CStringGetTextDatum(tmp.info.comments); + else + nulls[i++] = true; + + values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_functions); + values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_generation_time); + values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_inlining_count); + values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_inlining_time); + values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_optimization_count); + values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_optimization_time); + values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_emission_count); + values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_emission_time); + } + values[i++] = BoolGetDatum(toplevel); + values[i++] = BoolGetDatum(pg_atomic_read_u64(&pgsm->current_wbucket) != bucketid); + /* clean up and return the tuplestore */ - pgsm_hash_seq_term(&hstat); - LWLockRelease(pgsm->lock); + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + + if (query_txt) + pfree(query_txt); + if (parent_query_txt) + pfree(parent_query_txt); + } + /* clean up and return the tuplestore */ + pgsm_hash_seq_term(&hstat); + LWLockRelease(pgsm->lock); - tuplestore_donestoring(tupstore); + tuplestore_donestoring(tupstore); } static uint64 -get_next_wbucket(pgsmSharedState *pgsm) -{ - struct timeval tv; - uint64 current_bucket_sec; - uint64 new_bucket_id; - uint64 prev_bucket_id; - struct tm; - bool update_bucket = false; +get_next_wbucket(pgsmSharedState * pgsm) { + struct timeval tv; + uint64 current_bucket_sec; + uint64 new_bucket_id; + uint64 prev_bucket_id; + struct tm; + bool update_bucket = false; + + gettimeofday(&tv, NULL); + current_bucket_sec = pg_atomic_read_u64(&pgsm->prev_bucket_sec); + + /* + * If current bucket expired we loop attempting to update prev_bucket_sec. + * + * pg_atomic_compare_exchange_u64 may fail in two possible ways: 1. + * Another thread/process updated the variable before us. 2. A spurious + * failure / hardware event. + * + * In both failure cases we read prev_bucket_sec from memory again, if it + * was a spurious failure then the value of prev_bucket_sec must be the + * same as before, which will cause the while loop to execute again. + * + * If another thread updated prev_bucket_sec, then its current value will + * definitely make the while condition to fail, we can stop the loop as + * another thread has already updated prev_bucket_sec. + */ + while ((tv.tv_sec - (uint) current_bucket_sec) >= ((uint) pgsm_bucket_time)) { + if (pg_atomic_compare_exchange_u64(&pgsm->prev_bucket_sec, ¤t_bucket_sec, (uint64) tv.tv_sec)) { + update_bucket = true; + break; + } - gettimeofday(&tv, NULL); current_bucket_sec = pg_atomic_read_u64(&pgsm->prev_bucket_sec); + } - /* - * If current bucket expired we loop attempting to update prev_bucket_sec. - * - * pg_atomic_compare_exchange_u64 may fail in two possible ways: 1. - * Another thread/process updated the variable before us. 2. A spurious - * failure / hardware event. - * - * In both failure cases we read prev_bucket_sec from memory again, if it - * was a spurious failure then the value of prev_bucket_sec must be the - * same as before, which will cause the while loop to execute again. - * - * If another thread updated prev_bucket_sec, then its current value will - * definitely make the while condition to fail, we can stop the loop as - * another thread has already updated prev_bucket_sec. - */ - while ((tv.tv_sec - (uint)current_bucket_sec) >= ((uint)pgsm_bucket_time)) - { - if (pg_atomic_compare_exchange_u64(&pgsm->prev_bucket_sec, ¤t_bucket_sec, (uint64)tv.tv_sec)) - { - update_bucket = true; - break; - } + if (update_bucket) { - current_bucket_sec = pg_atomic_read_u64(&pgsm->prev_bucket_sec); - } + new_bucket_id = (tv.tv_sec / pgsm_bucket_time) % pgsm_max_buckets; - if (update_bucket) - { + /* Update bucket id and retrieve the previous one. */ + prev_bucket_id = pg_atomic_exchange_u64(&pgsm->current_wbucket, new_bucket_id); - new_bucket_id = (tv.tv_sec / pgsm_bucket_time) % pgsm_max_buckets; + LWLockAcquire(pgsm->lock, LW_EXCLUSIVE); + hash_entry_dealloc(new_bucket_id, prev_bucket_id, NULL); - /* Update bucket id and retrieve the previous one. */ - prev_bucket_id = pg_atomic_exchange_u64(&pgsm->current_wbucket, new_bucket_id); + LWLockRelease(pgsm->lock); - LWLockAcquire(pgsm->lock, LW_EXCLUSIVE); - hash_entry_dealloc(new_bucket_id, prev_bucket_id, NULL); + /* Allign the value in prev_bucket_sec to the bucket start time */ + tv.tv_sec = (tv.tv_sec) - (tv.tv_sec % pgsm_bucket_time); - LWLockRelease(pgsm->lock); + pg_atomic_exchange_u64(&pgsm->prev_bucket_sec, (uint64) tv.tv_sec); - /* Allign the value in prev_bucket_sec to the bucket start time */ - tv.tv_sec = (tv.tv_sec) - (tv.tv_sec % pgsm_bucket_time); + pgsm->bucket_start_time[new_bucket_id] = (TimestampTz) tv.tv_sec - + ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); + pgsm->bucket_start_time[new_bucket_id] = pgsm->bucket_start_time[new_bucket_id] * USECS_PER_SEC; + return new_bucket_id; + } - pg_atomic_exchange_u64(&pgsm->prev_bucket_sec, (uint64)tv.tv_sec); - - pgsm->bucket_start_time[new_bucket_id] = (TimestampTz) tv.tv_sec - - ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); - pgsm->bucket_start_time[new_bucket_id] = pgsm->bucket_start_time[new_bucket_id] * USECS_PER_SEC; - return new_bucket_id; - } - - return pg_atomic_read_u64(&pgsm->current_wbucket); + return pg_atomic_read_u64(&pgsm->current_wbucket); } /* @@ -2429,75 +2351,75 @@ get_next_wbucket(pgsmSharedState *pgsm) * are also removed before calculating the hash. */ uint64 -get_pgsm_query_id_hash(const char *norm_query, int norm_len) -{ - char *query; - char *q_iter; - char *norm_q_iter = (char *)norm_query; - uint64 pgsm_query_id = 0; +get_pgsm_query_id_hash(const char *norm_query, int norm_len) { + char *query; + char *q_iter; + char *norm_q_iter = (char *) norm_query; + uint64 pgsm_query_id = 0; - if (!pgsm_enable_pgsm_query_id) - return 0; + if (!pgsm_enable_pgsm_query_id) + return 0; - query = palloc(norm_len + 1); - q_iter = query; + query = palloc(norm_len + 1); + q_iter = query; - while (norm_q_iter && *norm_q_iter && norm_q_iter < (norm_query + norm_len)) - { - /* Skip multiline comments, + 1 is safe even if we've reach end of string */ - if (*norm_q_iter == '/' && *(norm_q_iter + 1) == '*') - { - while (*norm_q_iter && *norm_q_iter != '*' && *(norm_q_iter + 1) != '/') - norm_q_iter++; + while (norm_q_iter && *norm_q_iter && norm_q_iter < (norm_query + norm_len)) { + /* + * Skip multiline comments, + 1 is safe even if we've reach end of + * string + */ + if (*norm_q_iter == '/' && *(norm_q_iter + 1) == '*') { + while (*norm_q_iter && *norm_q_iter != '*' && *(norm_q_iter + 1) != '/') + norm_q_iter++; - /* Skip the '/' if the current character is valid. */ - if (*norm_q_iter) - norm_q_iter++; - } - - /* Skip single line comments, + 1 is safe even if we've reach end of string */ - if (*norm_q_iter == '-' && *(norm_q_iter + 1) == '-') - { - while (*norm_q_iter && *norm_q_iter != '\n') - norm_q_iter++; - } - - /* Skip white spaces */ - if (scanner_isspace(*norm_q_iter)) - { - while (scanner_isspace(*++norm_q_iter)); - - /* - * Let's replace it with a simple space. -1 is safe as we - * are making sure we are not at the start of the string. - */ - if (q_iter != query && !scanner_isspace(*(q_iter - 1))) - *q_iter++ = ' '; - - continue; - } - - *q_iter++ = *norm_q_iter++; + /* Skip the '/' if the current character is valid. */ + if (*norm_q_iter) + norm_q_iter++; } - /* Ensure we have a terminating zero at the end */ - *q_iter = '\0'; - - /* Get rid of trailing spaces */ - while (q_iter > query && *q_iter == '\0') - { - q_iter--; - - /* Continue reducing the string size if space is found. */ - if (scanner_isspace(*q_iter)) - *q_iter = '\0'; + /* + * Skip single line comments, + 1 is safe even if we've reach end of + * string + */ + if (*norm_q_iter == '-' && *(norm_q_iter + 1) == '-') { + while (*norm_q_iter && *norm_q_iter != '\n') + norm_q_iter++; } - /* Calcuate the hash. */ - pgsm_query_id = pgsm_hash_string(query, strlen(query)); + /* Skip white spaces */ + if (scanner_isspace(*norm_q_iter)) { + while (scanner_isspace(*++norm_q_iter)); - pfree(query); - return pgsm_query_id; + /* + * Let's replace it with a simple space. -1 is safe as we are + * making sure we are not at the start of the string. + */ + if (q_iter != query && !scanner_isspace(*(q_iter - 1))) + *q_iter++ = ' '; + + continue; + } + + *q_iter++ = *norm_q_iter++; + } + + /* Ensure we have a terminating zero at the end */ + *q_iter = '\0'; + + /* Get rid of trailing spaces */ + while (q_iter > query && *q_iter == '\0') { + q_iter--; + + /* Continue reducing the string size if space is found. */ + if (scanner_isspace(*q_iter)) + *q_iter = '\0'; + } + + /* Calcuate the hash. */ + pgsm_query_id = pgsm_hash_string(query, strlen(query)); + + pfree(query); + return pgsm_query_id; } #if PG_VERSION_NUM < 140000 @@ -2506,35 +2428,33 @@ get_pgsm_query_id_hash(const char *norm_query, int norm_len) * the current jumble. */ static void -AppendJumble(JumbleState *jstate, const unsigned char *item, Size size) +AppendJumble(JumbleState * jstate, const unsigned char *item, Size size) { - unsigned char *jumble = jstate->jumble; - Size jumble_len = jstate->jumble_len; + unsigned char *jumble = jstate->jumble; + Size jumble_len = jstate->jumble_len; - /* - * Whenever the jumble buffer is full, we hash the current contents and - * reset the buffer to contain just that hash value, thus relying on the - * hash to summarize everything so far. - */ - while (size > 0) - { - Size part_size; + /* + * Whenever the jumble buffer is full, we hash the current contents and + * reset the buffer to contain just that hash value, thus relying on the + * hash to summarize everything so far. + */ + while (size > 0) { + Size part_size; - if (jumble_len >= JUMBLE_SIZE) - { - uint64 start_hash; + if (jumble_len >= JUMBLE_SIZE) { + uint64 start_hash; - start_hash = pgsm_hash_string((char *)jumble, JUMBLE_SIZE); - memcpy(jumble, &start_hash, sizeof(start_hash)); - jumble_len = sizeof(start_hash); - } - part_size = Min(size, JUMBLE_SIZE - jumble_len); - memcpy(jumble + jumble_len, item, part_size); - jumble_len += part_size; - item += part_size; - size -= part_size; + start_hash = pgsm_hash_string((char *) jumble, JUMBLE_SIZE); + memcpy(jumble, &start_hash, sizeof(start_hash)); + jumble_len = sizeof(start_hash); } - jstate->jumble_len = jumble_len; + part_size = Min(size, JUMBLE_SIZE - jumble_len); + memcpy(jumble + jumble_len, item, part_size); + jumble_len += part_size; + item += part_size; + size -= part_size; + } + jstate->jumble_len = jumble_len; } /* @@ -2556,86 +2476,84 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size) * of information). */ static void -JumbleQuery(JumbleState *jstate, Query *query) +JumbleQuery(JumbleState * jstate, Query * query) { - Assert(IsA(query, Query)); - Assert(query->utilityStmt == NULL); + Assert(IsA(query, Query)); + Assert(query->utilityStmt == NULL); - APP_JUMB(query->commandType); - /* resultRelation is usually predictable from commandType */ - JumbleExpr(jstate, (Node *) query->cteList); + APP_JUMB(query->commandType); + /* resultRelation is usually predictable from commandType */ + JumbleExpr(jstate, (Node *) query->cteList); - JumbleRangeTable(jstate, query->rtable, query->commandType); - JumbleExpr(jstate, (Node *) query->jointree); - JumbleExpr(jstate, (Node *) query->targetList); - JumbleExpr(jstate, (Node *) query->onConflict); - JumbleExpr(jstate, (Node *) query->returningList); - JumbleExpr(jstate, (Node *) query->groupClause); - JumbleExpr(jstate, (Node *) query->groupingSets); - JumbleExpr(jstate, query->havingQual); - JumbleExpr(jstate, (Node *) query->windowClause); - JumbleExpr(jstate, (Node *) query->distinctClause); - JumbleExpr(jstate, (Node *) query->sortClause); - JumbleExpr(jstate, query->limitOffset); - JumbleExpr(jstate, query->limitCount); - /* we ignore rowMarks */ - JumbleExpr(jstate, query->setOperations); + JumbleRangeTable(jstate, query->rtable, query->commandType); + JumbleExpr(jstate, (Node *) query->jointree); + JumbleExpr(jstate, (Node *) query->targetList); + JumbleExpr(jstate, (Node *) query->onConflict); + JumbleExpr(jstate, (Node *) query->returningList); + JumbleExpr(jstate, (Node *) query->groupClause); + JumbleExpr(jstate, (Node *) query->groupingSets); + JumbleExpr(jstate, query->havingQual); + JumbleExpr(jstate, (Node *) query->windowClause); + JumbleExpr(jstate, (Node *) query->distinctClause); + JumbleExpr(jstate, (Node *) query->sortClause); + JumbleExpr(jstate, query->limitOffset); + JumbleExpr(jstate, query->limitCount); + /* we ignore rowMarks */ + JumbleExpr(jstate, query->setOperations); } /* * Jumble a range table */ static void -JumbleRangeTable(JumbleState *jstate, List *rtable, CmdType cmd_type) +JumbleRangeTable(JumbleState * jstate, List * rtable, CmdType cmd_type) { - ListCell *lc = NULL; + ListCell *lc = NULL; - foreach(lc, rtable) - { - RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc); + foreach(lc, rtable) { + RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc); - if (rte->rtekind != RTE_RELATION && cmd_type == CMD_INSERT) - continue; + if (rte->rtekind != RTE_RELATION && cmd_type == CMD_INSERT) + continue; - APP_JUMB(rte->rtekind); - switch (rte->rtekind) - { - case RTE_RELATION: - APP_JUMB(rte->relid); - JumbleExpr(jstate, (Node *) rte->tablesample); - break; - case RTE_SUBQUERY: - JumbleQuery(jstate, rte->subquery); - break; - case RTE_JOIN: - APP_JUMB(rte->jointype); - break; - case RTE_FUNCTION: - JumbleExpr(jstate, (Node *) rte->functions); - break; - case RTE_TABLEFUNC: - JumbleExpr(jstate, (Node *) rte->tablefunc); - break; - case RTE_VALUES: - JumbleExpr(jstate, (Node *) rte->values_lists); - break; - case RTE_CTE: + APP_JUMB(rte->rtekind); + switch (rte->rtekind) { + case RTE_RELATION: + APP_JUMB(rte->relid); + JumbleExpr(jstate, (Node *) rte->tablesample); + break; + case RTE_SUBQUERY: + JumbleQuery(jstate, rte->subquery); + break; + case RTE_JOIN: + APP_JUMB(rte->jointype); + break; + case RTE_FUNCTION: + JumbleExpr(jstate, (Node *) rte->functions); + break; + case RTE_TABLEFUNC: + JumbleExpr(jstate, (Node *) rte->tablefunc); + break; + case RTE_VALUES: + JumbleExpr(jstate, (Node *) rte->values_lists); + break; + case RTE_CTE: - /* - * Depending on the CTE name here isn't ideal, but it's the - * only info we have to identify the referenced WITH item. - */ - APP_JUMB_STRING(rte->ctename); - APP_JUMB(rte->ctelevelsup); - break; - case RTE_NAMEDTUPLESTORE: - APP_JUMB_STRING(rte->enrname); - break; - default: - elog(ERROR, "[pg_stat_monitor] JumbleRangeTable: unrecognized RTE kind: %d.", (int) rte->rtekind); - break; - } + /* + * Depending on the CTE name here isn't ideal, but it's the only + * info we have to identify the referenced WITH item. + */ + APP_JUMB_STRING(rte->ctename); + APP_JUMB(rte->ctelevelsup); + break; + case RTE_NAMEDTUPLESTORE: + APP_JUMB_STRING(rte->enrname); + break; + default: + elog(ERROR, "[pg_stat_monitor] JumbleRangeTable: unrecognized RTE kind: %d.", (int) rte->rtekind); + break; } + } } /* @@ -2653,490 +2571,486 @@ JumbleRangeTable(JumbleState *jstate, List *rtable, CmdType cmd_type) * about any unrecognized node type. */ static void -JumbleExpr(JumbleState *jstate, Node *node) +JumbleExpr(JumbleState * jstate, Node * node) { - ListCell *temp; + ListCell *temp; - if (node == NULL) - return; + if (node == NULL) + return; - /* Guard against stack overflow due to overly complex expressions */ - check_stack_depth(); + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); - /* - * We always emit the node's NodeTag, then any additional fields that are - * considered significant, and then we recurse to any child nodes. - */ - APP_JUMB(node->type); + /* + * We always emit the node's NodeTag, then any additional fields that are + * considered significant, and then we recurse to any child nodes. + */ + APP_JUMB(node->type); - switch (nodeTag(node)) + switch (nodeTag(node)) { + case T_Var: { - case T_Var: - { - Var *var = (Var *) node; + Var *var = (Var *) node; - APP_JUMB(var->varno); - APP_JUMB(var->varattno); - APP_JUMB(var->varlevelsup); - } - break; - case T_Const: - { - Const *c = (Const *) node; - - /* We jumble only the constant's type, not its value */ - APP_JUMB(c->consttype); - /* Also, record its parse location for query normalization */ - RecordConstLocation(jstate, c->location); - } - break; - case T_Param: - { - Param *p = (Param *) node; - - APP_JUMB(p->paramkind); - APP_JUMB(p->paramid); - APP_JUMB(p->paramtype); - /* Also, track the highest external Param id */ - if (p->paramkind == PARAM_EXTERN && - p->paramid > jstate->highest_extern_param_id) - jstate->highest_extern_param_id = p->paramid; - } - break; - case T_Aggref: - { - Aggref *expr = (Aggref *) node; - - APP_JUMB(expr->aggfnoid); - JumbleExpr(jstate, (Node *) expr->aggdirectargs); - JumbleExpr(jstate, (Node *) expr->args); - JumbleExpr(jstate, (Node *) expr->aggorder); - JumbleExpr(jstate, (Node *) expr->aggdistinct); - JumbleExpr(jstate, (Node *) expr->aggfilter); - } - break; - case T_GroupingFunc: - { - GroupingFunc *grpnode = (GroupingFunc *) node; - - JumbleExpr(jstate, (Node *) grpnode->refs); - } - break; - case T_WindowFunc: - { - WindowFunc *expr = (WindowFunc *) node; - - APP_JUMB(expr->winfnoid); - APP_JUMB(expr->winref); - JumbleExpr(jstate, (Node *) expr->args); - JumbleExpr(jstate, (Node *) expr->aggfilter); - } - break; -#if PG_VERSION_NUM >= 120000 - case T_SubscriptingRef: - { - SubscriptingRef *sbsref = (SubscriptingRef *) node; - - JumbleExpr(jstate, (Node *) sbsref->refupperindexpr); - JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr); - JumbleExpr(jstate, (Node *) sbsref->refexpr); - JumbleExpr(jstate, (Node *) sbsref->refassgnexpr); - } - break; -#else - case T_ArrayRef: - { - ArrayRef *aref = (ArrayRef *) node; - - JumbleExpr(jstate, (Node *) aref->refupperindexpr); - JumbleExpr(jstate, (Node *) aref->reflowerindexpr); - JumbleExpr(jstate, (Node *) aref->refexpr); - JumbleExpr(jstate, (Node *) aref->refassgnexpr); - } - break; -#endif - case T_FuncExpr: - { - FuncExpr *expr = (FuncExpr *) node; - - APP_JUMB(expr->funcid); - JumbleExpr(jstate, (Node *) expr->args); - } - break; - case T_NamedArgExpr: - { - NamedArgExpr *nae = (NamedArgExpr *) node; - - APP_JUMB(nae->argnumber); - JumbleExpr(jstate, (Node *) nae->arg); - } - break; - case T_OpExpr: - case T_DistinctExpr: /* struct-equivalent to OpExpr */ - case T_NullIfExpr: /* struct-equivalent to OpExpr */ - { - OpExpr *expr = (OpExpr *) node; - - APP_JUMB(expr->opno); - JumbleExpr(jstate, (Node *) expr->args); - } - break; - case T_ScalarArrayOpExpr: - { - ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; - - APP_JUMB(expr->opno); - APP_JUMB(expr->useOr); - JumbleExpr(jstate, (Node *) expr->args); - } - break; - case T_BoolExpr: - { - BoolExpr *expr = (BoolExpr *) node; - - APP_JUMB(expr->boolop); - JumbleExpr(jstate, (Node *) expr->args); - } - break; - case T_SubLink: - { - SubLink *sublink = (SubLink *) node; - - APP_JUMB(sublink->subLinkType); - APP_JUMB(sublink->subLinkId); - JumbleExpr(jstate, (Node *) sublink->testexpr); - JumbleQuery(jstate, castNode(Query, sublink->subselect)); - } - break; - case T_FieldSelect: - { - FieldSelect *fs = (FieldSelect *) node; - - APP_JUMB(fs->fieldnum); - JumbleExpr(jstate, (Node *) fs->arg); - } - break; - case T_FieldStore: - { - FieldStore *fstore = (FieldStore *) node; - - JumbleExpr(jstate, (Node *) fstore->arg); - JumbleExpr(jstate, (Node *) fstore->newvals); - } - break; - case T_RelabelType: - { - RelabelType *rt = (RelabelType *) node; - - APP_JUMB(rt->resulttype); - JumbleExpr(jstate, (Node *) rt->arg); - } - break; - case T_CoerceViaIO: - { - CoerceViaIO *cio = (CoerceViaIO *) node; - - APP_JUMB(cio->resulttype); - JumbleExpr(jstate, (Node *) cio->arg); - } - break; - case T_ArrayCoerceExpr: - { - ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node; - - APP_JUMB(acexpr->resulttype); - JumbleExpr(jstate, (Node *) acexpr->arg); - JumbleExpr(jstate, (Node *) acexpr->elemexpr); - } - break; - case T_ConvertRowtypeExpr: - { - ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node; - - APP_JUMB(crexpr->resulttype); - JumbleExpr(jstate, (Node *) crexpr->arg); - } - break; - case T_CollateExpr: - { - CollateExpr *ce = (CollateExpr *) node; - - APP_JUMB(ce->collOid); - JumbleExpr(jstate, (Node *) ce->arg); - } - break; - case T_CaseExpr: - { - CaseExpr *caseexpr = (CaseExpr *) node; - - JumbleExpr(jstate, (Node *) caseexpr->arg); - foreach(temp, caseexpr->args) - { - CaseWhen *when = lfirst_node(CaseWhen, temp); - - JumbleExpr(jstate, (Node *) when->expr); - JumbleExpr(jstate, (Node *) when->result); - } - JumbleExpr(jstate, (Node *) caseexpr->defresult); - } - break; - case T_CaseTestExpr: - { - CaseTestExpr *ct = (CaseTestExpr *) node; - - APP_JUMB(ct->typeId); - } - break; - case T_ArrayExpr: - JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements); - break; - case T_RowExpr: - JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args); - break; - case T_RowCompareExpr: - { - RowCompareExpr *rcexpr = (RowCompareExpr *) node; - - APP_JUMB(rcexpr->rctype); - JumbleExpr(jstate, (Node *) rcexpr->largs); - JumbleExpr(jstate, (Node *) rcexpr->rargs); - } - break; - case T_CoalesceExpr: - JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args); - break; - case T_MinMaxExpr: - { - MinMaxExpr *mmexpr = (MinMaxExpr *) node; - - APP_JUMB(mmexpr->op); - JumbleExpr(jstate, (Node *) mmexpr->args); - } - break; - case T_SQLValueFunction: - { - SQLValueFunction *svf = (SQLValueFunction *) node; - - APP_JUMB(svf->op); - /* type is fully determined by op */ - APP_JUMB(svf->typmod); - } - break; - case T_XmlExpr: - { - XmlExpr *xexpr = (XmlExpr *) node; - - APP_JUMB(xexpr->op); - JumbleExpr(jstate, (Node *) xexpr->named_args); - JumbleExpr(jstate, (Node *) xexpr->args); - } - break; - case T_NullTest: - { - NullTest *nt = (NullTest *) node; - - APP_JUMB(nt->nulltesttype); - JumbleExpr(jstate, (Node *) nt->arg); - } - break; - case T_BooleanTest: - { - BooleanTest *bt = (BooleanTest *) node; - - APP_JUMB(bt->booltesttype); - JumbleExpr(jstate, (Node *) bt->arg); - } - break; - case T_CoerceToDomain: - { - CoerceToDomain *cd = (CoerceToDomain *) node; - - APP_JUMB(cd->resulttype); - JumbleExpr(jstate, (Node *) cd->arg); - } - break; - case T_CoerceToDomainValue: - { - CoerceToDomainValue *cdv = (CoerceToDomainValue *) node; - - APP_JUMB(cdv->typeId); - } - break; - case T_SetToDefault: - { - SetToDefault *sd = (SetToDefault *) node; - - APP_JUMB(sd->typeId); - } - break; - case T_CurrentOfExpr: - { - CurrentOfExpr *ce = (CurrentOfExpr *) node; - - APP_JUMB(ce->cvarno); - if (ce->cursor_name) - APP_JUMB_STRING(ce->cursor_name); - APP_JUMB(ce->cursor_param); - } - break; - case T_NextValueExpr: - { - NextValueExpr *nve = (NextValueExpr *) node; - - APP_JUMB(nve->seqid); - APP_JUMB(nve->typeId); - } - break; - case T_InferenceElem: - { - InferenceElem *ie = (InferenceElem *) node; - - APP_JUMB(ie->infercollid); - APP_JUMB(ie->inferopclass); - JumbleExpr(jstate, ie->expr); - } - break; - case T_TargetEntry: - { - TargetEntry *tle = (TargetEntry *) node; - - APP_JUMB(tle->resno); - APP_JUMB(tle->ressortgroupref); - JumbleExpr(jstate, (Node *) tle->expr); - } - break; - case T_RangeTblRef: - { - RangeTblRef *rtr = (RangeTblRef *) node; - - APP_JUMB(rtr->rtindex); - } - break; - case T_JoinExpr: - { - JoinExpr *join = (JoinExpr *) node; - - APP_JUMB(join->jointype); - APP_JUMB(join->isNatural); - APP_JUMB(join->rtindex); - JumbleExpr(jstate, join->larg); - JumbleExpr(jstate, join->rarg); - JumbleExpr(jstate, join->quals); - } - break; - case T_FromExpr: - { - FromExpr *from = (FromExpr *) node; - - JumbleExpr(jstate, (Node *) from->fromlist); - JumbleExpr(jstate, from->quals); - } - break; - case T_OnConflictExpr: - { - OnConflictExpr *conf = (OnConflictExpr *) node; - - APP_JUMB(conf->action); - JumbleExpr(jstate, (Node *) conf->arbiterElems); - JumbleExpr(jstate, conf->arbiterWhere); - JumbleExpr(jstate, (Node *) conf->onConflictSet); - JumbleExpr(jstate, conf->onConflictWhere); - APP_JUMB(conf->constraint); - APP_JUMB(conf->exclRelIndex); - JumbleExpr(jstate, (Node *) conf->exclRelTlist); - } - break; - case T_List: - foreach(temp, (List *) node) - { - JumbleExpr(jstate, (Node *) lfirst(temp)); - } - break; - case T_IntList: - foreach(temp, (List *) node) - { - APP_JUMB(lfirst_int(temp)); - } - break; - case T_SortGroupClause: - { - SortGroupClause *sgc = (SortGroupClause *) node; - - APP_JUMB(sgc->tleSortGroupRef); - APP_JUMB(sgc->eqop); - APP_JUMB(sgc->sortop); - APP_JUMB(sgc->nulls_first); - } - break; - case T_GroupingSet: - { - GroupingSet *gsnode = (GroupingSet *) node; - - JumbleExpr(jstate, (Node *) gsnode->content); - } - break; - case T_WindowClause: - { - WindowClause *wc = (WindowClause *) node; - - APP_JUMB(wc->winref); - APP_JUMB(wc->frameOptions); - JumbleExpr(jstate, (Node *) wc->partitionClause); - JumbleExpr(jstate, (Node *) wc->orderClause); - JumbleExpr(jstate, wc->startOffset); - JumbleExpr(jstate, wc->endOffset); - } - break; - case T_CommonTableExpr: - { - CommonTableExpr *cte = (CommonTableExpr *) node; - - /* we store the string name because RTE_CTE RTEs need it */ - APP_JUMB_STRING(cte->ctename); - JumbleQuery(jstate, castNode(Query, cte->ctequery)); - } - break; - case T_SetOperationStmt: - { - SetOperationStmt *setop = (SetOperationStmt *) node; - - APP_JUMB(setop->op); - APP_JUMB(setop->all); - JumbleExpr(jstate, setop->larg); - JumbleExpr(jstate, setop->rarg); - } - break; - case T_RangeTblFunction: - { - RangeTblFunction *rtfunc = (RangeTblFunction *) node; - - JumbleExpr(jstate, rtfunc->funcexpr); - } - break; - case T_TableFunc: - { - TableFunc *tablefunc = (TableFunc *) node; - - JumbleExpr(jstate, tablefunc->docexpr); - JumbleExpr(jstate, tablefunc->rowexpr); - JumbleExpr(jstate, (Node *) tablefunc->colexprs); - } - break; - case T_TableSampleClause: - { - TableSampleClause *tsc = (TableSampleClause *) node; - - APP_JUMB(tsc->tsmhandler); - JumbleExpr(jstate, (Node *) tsc->args); - JumbleExpr(jstate, (Node *) tsc->repeatable); - } - break; - default: - /* Only a warning, since we can stumble along anyway */ - elog(INFO, "[pg_stat_monitor] JumbleExpr: unrecognized node type: %d.", - (int) nodeTag(node)); - break; + APP_JUMB(var->varno); + APP_JUMB(var->varattno); + APP_JUMB(var->varlevelsup); } + break; + case T_Const: + { + Const *c = (Const *) node; + + /* We jumble only the constant's type, not its value */ + APP_JUMB(c->consttype); + /* Also, record its parse location for query normalization */ + RecordConstLocation(jstate, c->location); + } + break; + case T_Param: + { + Param *p = (Param *) node; + + APP_JUMB(p->paramkind); + APP_JUMB(p->paramid); + APP_JUMB(p->paramtype); + /* Also, track the highest external Param id */ + if (p->paramkind == PARAM_EXTERN && + p->paramid > jstate->highest_extern_param_id) + jstate->highest_extern_param_id = p->paramid; + } + break; + case T_Aggref: + { + Aggref *expr = (Aggref *) node; + + APP_JUMB(expr->aggfnoid); + JumbleExpr(jstate, (Node *) expr->aggdirectargs); + JumbleExpr(jstate, (Node *) expr->args); + JumbleExpr(jstate, (Node *) expr->aggorder); + JumbleExpr(jstate, (Node *) expr->aggdistinct); + JumbleExpr(jstate, (Node *) expr->aggfilter); + } + break; + case T_GroupingFunc: + { + GroupingFunc *grpnode = (GroupingFunc *) node; + + JumbleExpr(jstate, (Node *) grpnode->refs); + } + break; + case T_WindowFunc: + { + WindowFunc *expr = (WindowFunc *) node; + + APP_JUMB(expr->winfnoid); + APP_JUMB(expr->winref); + JumbleExpr(jstate, (Node *) expr->args); + JumbleExpr(jstate, (Node *) expr->aggfilter); + } + break; +#if PG_VERSION_NUM >= 120000 + case T_SubscriptingRef: + { + SubscriptingRef *sbsref = (SubscriptingRef *) node; + + JumbleExpr(jstate, (Node *) sbsref->refupperindexpr); + JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr); + JumbleExpr(jstate, (Node *) sbsref->refexpr); + JumbleExpr(jstate, (Node *) sbsref->refassgnexpr); + } + break; +#else + case T_ArrayRef: + { + ArrayRef *aref = (ArrayRef *) node; + + JumbleExpr(jstate, (Node *) aref->refupperindexpr); + JumbleExpr(jstate, (Node *) aref->reflowerindexpr); + JumbleExpr(jstate, (Node *) aref->refexpr); + JumbleExpr(jstate, (Node *) aref->refassgnexpr); + } + break; +#endif + case T_FuncExpr: + { + FuncExpr *expr = (FuncExpr *) node; + + APP_JUMB(expr->funcid); + JumbleExpr(jstate, (Node *) expr->args); + } + break; + case T_NamedArgExpr: + { + NamedArgExpr *nae = (NamedArgExpr *) node; + + APP_JUMB(nae->argnumber); + JumbleExpr(jstate, (Node *) nae->arg); + } + break; + case T_OpExpr: + case T_DistinctExpr: /* struct-equivalent to OpExpr */ + case T_NullIfExpr: /* struct-equivalent to OpExpr */ + { + OpExpr *expr = (OpExpr *) node; + + APP_JUMB(expr->opno); + JumbleExpr(jstate, (Node *) expr->args); + } + break; + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + + APP_JUMB(expr->opno); + APP_JUMB(expr->useOr); + JumbleExpr(jstate, (Node *) expr->args); + } + break; + case T_BoolExpr: + { + BoolExpr *expr = (BoolExpr *) node; + + APP_JUMB(expr->boolop); + JumbleExpr(jstate, (Node *) expr->args); + } + break; + case T_SubLink: + { + SubLink *sublink = (SubLink *) node; + + APP_JUMB(sublink->subLinkType); + APP_JUMB(sublink->subLinkId); + JumbleExpr(jstate, (Node *) sublink->testexpr); + JumbleQuery(jstate, castNode(Query, sublink->subselect)); + } + break; + case T_FieldSelect: + { + FieldSelect *fs = (FieldSelect *) node; + + APP_JUMB(fs->fieldnum); + JumbleExpr(jstate, (Node *) fs->arg); + } + break; + case T_FieldStore: + { + FieldStore *fstore = (FieldStore *) node; + + JumbleExpr(jstate, (Node *) fstore->arg); + JumbleExpr(jstate, (Node *) fstore->newvals); + } + break; + case T_RelabelType: + { + RelabelType *rt = (RelabelType *) node; + + APP_JUMB(rt->resulttype); + JumbleExpr(jstate, (Node *) rt->arg); + } + break; + case T_CoerceViaIO: + { + CoerceViaIO *cio = (CoerceViaIO *) node; + + APP_JUMB(cio->resulttype); + JumbleExpr(jstate, (Node *) cio->arg); + } + break; + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node; + + APP_JUMB(acexpr->resulttype); + JumbleExpr(jstate, (Node *) acexpr->arg); + JumbleExpr(jstate, (Node *) acexpr->elemexpr); + } + break; + case T_ConvertRowtypeExpr: + { + ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node; + + APP_JUMB(crexpr->resulttype); + JumbleExpr(jstate, (Node *) crexpr->arg); + } + break; + case T_CollateExpr: + { + CollateExpr *ce = (CollateExpr *) node; + + APP_JUMB(ce->collOid); + JumbleExpr(jstate, (Node *) ce->arg); + } + break; + case T_CaseExpr: + { + CaseExpr *caseexpr = (CaseExpr *) node; + + JumbleExpr(jstate, (Node *) caseexpr->arg); + foreach(temp, caseexpr->args) { + CaseWhen *when = lfirst_node(CaseWhen, temp); + + JumbleExpr(jstate, (Node *) when->expr); + JumbleExpr(jstate, (Node *) when->result); + } + JumbleExpr(jstate, (Node *) caseexpr->defresult); + } + break; + case T_CaseTestExpr: + { + CaseTestExpr *ct = (CaseTestExpr *) node; + + APP_JUMB(ct->typeId); + } + break; + case T_ArrayExpr: + JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements); + break; + case T_RowExpr: + JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args); + break; + case T_RowCompareExpr: + { + RowCompareExpr *rcexpr = (RowCompareExpr *) node; + + APP_JUMB(rcexpr->rctype); + JumbleExpr(jstate, (Node *) rcexpr->largs); + JumbleExpr(jstate, (Node *) rcexpr->rargs); + } + break; + case T_CoalesceExpr: + JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args); + break; + case T_MinMaxExpr: + { + MinMaxExpr *mmexpr = (MinMaxExpr *) node; + + APP_JUMB(mmexpr->op); + JumbleExpr(jstate, (Node *) mmexpr->args); + } + break; + case T_SQLValueFunction: + { + SQLValueFunction *svf = (SQLValueFunction *) node; + + APP_JUMB(svf->op); + /* type is fully determined by op */ + APP_JUMB(svf->typmod); + } + break; + case T_XmlExpr: + { + XmlExpr *xexpr = (XmlExpr *) node; + + APP_JUMB(xexpr->op); + JumbleExpr(jstate, (Node *) xexpr->named_args); + JumbleExpr(jstate, (Node *) xexpr->args); + } + break; + case T_NullTest: + { + NullTest *nt = (NullTest *) node; + + APP_JUMB(nt->nulltesttype); + JumbleExpr(jstate, (Node *) nt->arg); + } + break; + case T_BooleanTest: + { + BooleanTest *bt = (BooleanTest *) node; + + APP_JUMB(bt->booltesttype); + JumbleExpr(jstate, (Node *) bt->arg); + } + break; + case T_CoerceToDomain: + { + CoerceToDomain *cd = (CoerceToDomain *) node; + + APP_JUMB(cd->resulttype); + JumbleExpr(jstate, (Node *) cd->arg); + } + break; + case T_CoerceToDomainValue: + { + CoerceToDomainValue *cdv = (CoerceToDomainValue *) node; + + APP_JUMB(cdv->typeId); + } + break; + case T_SetToDefault: + { + SetToDefault *sd = (SetToDefault *) node; + + APP_JUMB(sd->typeId); + } + break; + case T_CurrentOfExpr: + { + CurrentOfExpr *ce = (CurrentOfExpr *) node; + + APP_JUMB(ce->cvarno); + if (ce->cursor_name) + APP_JUMB_STRING(ce->cursor_name); + APP_JUMB(ce->cursor_param); + } + break; + case T_NextValueExpr: + { + NextValueExpr *nve = (NextValueExpr *) node; + + APP_JUMB(nve->seqid); + APP_JUMB(nve->typeId); + } + break; + case T_InferenceElem: + { + InferenceElem *ie = (InferenceElem *) node; + + APP_JUMB(ie->infercollid); + APP_JUMB(ie->inferopclass); + JumbleExpr(jstate, ie->expr); + } + break; + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *) node; + + APP_JUMB(tle->resno); + APP_JUMB(tle->ressortgroupref); + JumbleExpr(jstate, (Node *) tle->expr); + } + break; + case T_RangeTblRef: + { + RangeTblRef *rtr = (RangeTblRef *) node; + + APP_JUMB(rtr->rtindex); + } + break; + case T_JoinExpr: + { + JoinExpr *join = (JoinExpr *) node; + + APP_JUMB(join->jointype); + APP_JUMB(join->isNatural); + APP_JUMB(join->rtindex); + JumbleExpr(jstate, join->larg); + JumbleExpr(jstate, join->rarg); + JumbleExpr(jstate, join->quals); + } + break; + case T_FromExpr: + { + FromExpr *from = (FromExpr *) node; + + JumbleExpr(jstate, (Node *) from->fromlist); + JumbleExpr(jstate, from->quals); + } + break; + case T_OnConflictExpr: + { + OnConflictExpr *conf = (OnConflictExpr *) node; + + APP_JUMB(conf->action); + JumbleExpr(jstate, (Node *) conf->arbiterElems); + JumbleExpr(jstate, conf->arbiterWhere); + JumbleExpr(jstate, (Node *) conf->onConflictSet); + JumbleExpr(jstate, conf->onConflictWhere); + APP_JUMB(conf->constraint); + APP_JUMB(conf->exclRelIndex); + JumbleExpr(jstate, (Node *) conf->exclRelTlist); + } + break; + case T_List: + foreach(temp, (List *) node) { + JumbleExpr(jstate, (Node *) lfirst(temp)); + } + break; + case T_IntList: + foreach(temp, (List *) node) { + APP_JUMB(lfirst_int(temp)); + } + break; + case T_SortGroupClause: + { + SortGroupClause *sgc = (SortGroupClause *) node; + + APP_JUMB(sgc->tleSortGroupRef); + APP_JUMB(sgc->eqop); + APP_JUMB(sgc->sortop); + APP_JUMB(sgc->nulls_first); + } + break; + case T_GroupingSet: + { + GroupingSet *gsnode = (GroupingSet *) node; + + JumbleExpr(jstate, (Node *) gsnode->content); + } + break; + case T_WindowClause: + { + WindowClause *wc = (WindowClause *) node; + + APP_JUMB(wc->winref); + APP_JUMB(wc->frameOptions); + JumbleExpr(jstate, (Node *) wc->partitionClause); + JumbleExpr(jstate, (Node *) wc->orderClause); + JumbleExpr(jstate, wc->startOffset); + JumbleExpr(jstate, wc->endOffset); + } + break; + case T_CommonTableExpr: + { + CommonTableExpr *cte = (CommonTableExpr *) node; + + /* we store the string name because RTE_CTE RTEs need it */ + APP_JUMB_STRING(cte->ctename); + JumbleQuery(jstate, castNode(Query, cte->ctequery)); + } + break; + case T_SetOperationStmt: + { + SetOperationStmt *setop = (SetOperationStmt *) node; + + APP_JUMB(setop->op); + APP_JUMB(setop->all); + JumbleExpr(jstate, setop->larg); + JumbleExpr(jstate, setop->rarg); + } + break; + case T_RangeTblFunction: + { + RangeTblFunction *rtfunc = (RangeTblFunction *) node; + + JumbleExpr(jstate, rtfunc->funcexpr); + } + break; + case T_TableFunc: + { + TableFunc *tablefunc = (TableFunc *) node; + + JumbleExpr(jstate, tablefunc->docexpr); + JumbleExpr(jstate, tablefunc->rowexpr); + JumbleExpr(jstate, (Node *) tablefunc->colexprs); + } + break; + case T_TableSampleClause: + { + TableSampleClause *tsc = (TableSampleClause *) node; + + APP_JUMB(tsc->tsmhandler); + JumbleExpr(jstate, (Node *) tsc->args); + JumbleExpr(jstate, (Node *) tsc->repeatable); + } + break; + default: + /* Only a warning, since we can stumble along anyway */ + elog(INFO, "[pg_stat_monitor] JumbleExpr: unrecognized node type: %d.", + (int) nodeTag(node)); + break; + } } /* @@ -3144,64 +3058,59 @@ JumbleExpr(JumbleState *jstate, Node *node) * that is currently being walked. */ static void -RecordConstLocation(JumbleState *jstate, int location) +RecordConstLocation(JumbleState * jstate, int location) { - /* -1 indicates unknown or undefined location */ - if (location >= 0) - { - /* enlarge array if needed */ - if (jstate->clocations_count >= jstate->clocations_buf_size) - { - jstate->clocations_buf_size *= 2; - jstate->clocations = (LocationLen *) - repalloc(jstate->clocations, - jstate->clocations_buf_size * - sizeof(LocationLen)); - } - jstate->clocations[jstate->clocations_count].location = location; - /* initialize lengths to -1 to simplify fill_in_constant_lengths */ - jstate->clocations[jstate->clocations_count].length = -1; - jstate->clocations_count++; + /* -1 indicates unknown or undefined location */ + if (location >= 0) { + /* enlarge array if needed */ + if (jstate->clocations_count >= jstate->clocations_buf_size) { + jstate->clocations_buf_size *= 2; + jstate->clocations = (LocationLen *) + repalloc(jstate->clocations, + jstate->clocations_buf_size * + sizeof(LocationLen)); } + jstate->clocations[jstate->clocations_count].location = location; + /* initialize lengths to -1 to simplify fill_in_constant_lengths */ + jstate->clocations[jstate->clocations_count].length = -1; + jstate->clocations_count++; + } } static const char * CleanQuerytext(const char *query, int *location, int *len) { - int query_location = *location; - int query_len = *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)); - } + /* 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 - { - /* If query location is unknown, distrust query_len as well */ - query_location = 0; - query_len = strlen(query); - } + 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--; + /* + * 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; + *location = query_location; + *len = query_len; - return query; + return query; } #endif @@ -3225,83 +3134,81 @@ CleanQuerytext(const char *query, int *location, int *len) * Returns a palloc'd string. */ static char * -generate_normalized_query(JumbleState *jstate, const char *query, - int query_loc, int *query_len_p, int encoding) +generate_normalized_query(JumbleState * jstate, const char *query, + int query_loc, int *query_len_p, int encoding) { - char *norm_query; - int query_len = *query_len_p; - int i, - norm_query_buflen, /* Space allowed for norm_query */ - len_to_wrt, /* Length (in bytes) to write */ - quer_loc = 0, /* Source query byte location */ - n_quer_loc = 0, /* Normalized query byte location */ - last_off = 0, /* Offset from start for previous tok */ - last_tok_len = 0; /* Length (in bytes) of that tok */ + char *norm_query; + int query_len = *query_len_p; + int i, norm_query_buflen, /* Space allowed for norm_query */ + len_to_wrt, /* Length (in bytes) to write */ + quer_loc = 0, /* Source query byte location */ + n_quer_loc = 0, /* Normalized query byte location */ + last_off = 0, /* Offset from start for previous tok */ + last_tok_len = 0; /* Length (in bytes) of that tok */ - /* - * Get constants' lengths (core system only gives us locations). Note - * this also ensures the items are sorted by location. - */ - fill_in_constant_lengths(jstate, query, query_loc); + /* + * Get constants' lengths (core system only gives us locations). Note + * this also ensures the items are sorted by location. + */ + fill_in_constant_lengths(jstate, query, query_loc); - /* - * Allow for $n symbols to be longer than the constants they replace. - * Constants must take at least one byte in text form, while a $n symbol - * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We - * could refine that limit based on the max value of n for the current - * query, but it hardly seems worth any extra effort to do so. - */ - norm_query_buflen = query_len + jstate->clocations_count * 10; + /* + * Allow for $n symbols to be longer than the constants they replace. + * Constants must take at least one byte in text form, while a $n symbol + * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We + * could refine that limit based on the max value of n for the current + * query, but it hardly seems worth any extra effort to do so. + */ + norm_query_buflen = query_len + jstate->clocations_count * 10; - /* Allocate result buffer */ - norm_query = palloc(norm_query_buflen + 1); + /* Allocate result buffer */ + norm_query = palloc(norm_query_buflen + 1); - for (i = 0; i < jstate->clocations_count; i++) - { - int off, /* Offset from start for cur tok */ - tok_len; /* Length (in bytes) of that tok */ + for (i = 0; i < jstate->clocations_count; i++) { + int off, /* Offset from start for cur tok */ + tok_len; /* Length (in bytes) of that tok */ - off = jstate->clocations[i].location; - /* Adjust recorded location if we're dealing with partial string */ - off -= query_loc; + off = jstate->clocations[i].location; + /* Adjust recorded location if we're dealing with partial string */ + off -= query_loc; - tok_len = jstate->clocations[i].length; + tok_len = jstate->clocations[i].length; - if (tok_len < 0) - continue; /* ignore any duplicates */ + if (tok_len < 0) + continue; /* ignore any duplicates */ - /* Copy next chunk (what precedes the next constant) */ - len_to_wrt = off - last_off; - len_to_wrt -= last_tok_len; - - Assert(len_to_wrt >= 0); - memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt); - n_quer_loc += len_to_wrt; - - /* And insert a param symbol in place of the constant token */ - n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d", - i + 1 + jstate->highest_extern_param_id); - - quer_loc = off + tok_len; - last_off = off; - last_tok_len = tok_len; - } - - /* - * We've copied up until the last ignorable constant. Copy over the - * remaining bytes of the original query string. - */ - len_to_wrt = query_len - quer_loc; + /* Copy next chunk (what precedes the next constant) */ + len_to_wrt = off - last_off; + len_to_wrt -= last_tok_len; Assert(len_to_wrt >= 0); memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt); n_quer_loc += len_to_wrt; - Assert(n_quer_loc <= norm_query_buflen); - norm_query[n_quer_loc] = '\0'; + /* And insert a param symbol in place of the constant token */ + n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d", + i + 1 + jstate->highest_extern_param_id); - *query_len_p = n_quer_loc; - return norm_query; + quer_loc = off + tok_len; + last_off = off; + last_tok_len = tok_len; + } + + /* + * We've copied up until the last ignorable constant. Copy over the + * remaining bytes of the original query string. + */ + len_to_wrt = query_len - quer_loc; + + Assert(len_to_wrt >= 0); + memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt); + n_quer_loc += len_to_wrt; + + Assert(n_quer_loc <= norm_query_buflen); + norm_query[n_quer_loc] = '\0'; + + *query_len_p = n_quer_loc; + return norm_query; } /* @@ -3332,104 +3239,100 @@ generate_normalized_query(JumbleState *jstate, const char *query, * reason for a constant to start with a '-'. */ static void -fill_in_constant_lengths(JumbleState *jstate, const char *query, - int query_loc) +fill_in_constant_lengths(JumbleState * jstate, const char *query, + int query_loc) { - LocationLen *locs; - core_yyscan_t yyscanner; - core_yy_extra_type yyextra; - core_YYSTYPE yylval; - YYLTYPE yylloc; - int last_loc = -1; - int i; + LocationLen *locs; + core_yyscan_t yyscanner; + core_yy_extra_type yyextra; + core_YYSTYPE yylval; + YYLTYPE yylloc; + int last_loc = -1; + int i; - /* - * Sort the records by location so that we can process them in order while - * scanning the query text. - */ - if (jstate->clocations_count > 1) - qsort(jstate->clocations, jstate->clocations_count, - sizeof(LocationLen), comp_location); - locs = jstate->clocations; + /* + * Sort the records by location so that we can process them in order while + * scanning the query text. + */ + if (jstate->clocations_count > 1) + qsort(jstate->clocations, jstate->clocations_count, + sizeof(LocationLen), comp_location); + locs = jstate->clocations; - /* initialize the flex scanner --- should match raw_parser() */ - yyscanner = scanner_init(query, - &yyextra, + /* initialize the flex scanner --- should match raw_parser() */ + yyscanner = scanner_init(query, + &yyextra, #if PG_VERSION_NUM >= 120000 - &ScanKeywords, - ScanKeywordTokens); + &ScanKeywords, + ScanKeywordTokens); #else - ScanKeywords, - NumScanKeywords); + ScanKeywords, + NumScanKeywords); #endif - /* we don't want to re-emit any escape string warnings */ - yyextra.escape_string_warning = false; + /* we don't want to re-emit any escape string warnings */ + yyextra.escape_string_warning = false; - /* Search for each constant, in sequence */ - for (i = 0; i < jstate->clocations_count; i++) - { - int loc = locs[i].location; - int tok; + /* Search for each constant, in sequence */ + for (i = 0; i < jstate->clocations_count; i++) { + int loc = locs[i].location; + int tok; - /* Adjust recorded location if we're dealing with partial string */ - loc -= query_loc; + /* Adjust recorded location if we're dealing with partial string */ + loc -= query_loc; - Assert(loc >= 0); + Assert(loc >= 0); - if (loc <= last_loc) - continue; /* Duplicate constant, ignore */ + if (loc <= last_loc) + continue; /* Duplicate constant, ignore */ - /* Lex tokens until we find the desired constant */ - for (;;) - { - tok = core_yylex(&yylval, &yylloc, yyscanner); + /* Lex tokens until we find the desired constant */ + for (;;) { + tok = core_yylex(&yylval, &yylloc, yyscanner); - /* We should not hit end-of-string, but if we do, behave sanely */ - if (tok == 0) - break; /* out of inner for-loop */ + /* We should not hit end-of-string, but if we do, behave sanely */ + if (tok == 0) + break; /* out of inner for-loop */ - /* - * We should find the token position exactly, but if we somehow - * run past it, work with that. - */ - if (yylloc >= loc) - { - if (query[loc] == '-') - { - /* - * It's a negative value - this is the one and only case - * where we replace more than a single token. - * - * Do not compensate for the core system's special-case - * adjustment of location to that of the leading '-' - * operator in the event of a negative constant. It is - * also useful for our purposes to start from the minus - * symbol. In this way, queries like "select * from foo - * where bar = 1" and "select * from foo where bar = -2" - * will have identical normalized query strings. - */ - tok = core_yylex(&yylval, &yylloc, yyscanner); - if (tok == 0) - break; /* out of inner for-loop */ - } - - /* - * We now rely on the assumption that flex has placed a zero - * byte after the text of the current token in scanbuf. - */ - locs[i].length = strlen(yyextra.scanbuf + loc); - break; /* out of inner for-loop */ - } + /* + * We should find the token position exactly, but if we somehow + * run past it, work with that. + */ + if (yylloc >= loc) { + if (query[loc] == '-') { + /* + * It's a negative value - this is the one and only case + * where we replace more than a single token. + * + * Do not compensate for the core system's special-case + * adjustment of location to that of the leading '-' + * operator in the event of a negative constant. It is + * also useful for our purposes to start from the minus + * symbol. In this way, queries like "select * from foo + * where bar = 1" and "select * from foo where bar = -2" + * will have identical normalized query strings. + */ + tok = core_yylex(&yylval, &yylloc, yyscanner); + if (tok == 0) + break; /* out of inner for-loop */ } - /* If we hit end-of-string, give up, leaving remaining lengths -1 */ - if (tok == 0) - break; - - last_loc = loc; + /* + * We now rely on the assumption that flex has placed a zero + * byte after the text of the current token in scanbuf. + */ + locs[i].length = strlen(yyextra.scanbuf + loc); + break; /* out of inner for-loop */ + } } - scanner_finish(yyscanner); + /* If we hit end-of-string, give up, leaving remaining lengths -1 */ + if (tok == 0) + break; + + last_loc = loc; + } + + scanner_finish(yyscanner); } /* @@ -3438,158 +3341,150 @@ fill_in_constant_lengths(JumbleState *jstate, const char *query, static int comp_location(const void *a, const void *b) { - int l = ((const LocationLen *) a)->location; - int r = ((const LocationLen *) b)->location; + int l = ((const LocationLen *) a)->location; + int r = ((const LocationLen *) b)->location; - if (l < r) - return -1; - else if (l > r) - return +1; - else - return 0; + if (l < r) + return -1; + else if (l > r) + return +1; + else + return 0; } #define MAX_STRING_LEN 1024 /* Convert array into Text dataum */ static Datum -intarray_get_datum(int32 arr[], int len) -{ - int j; - char str[1024]; - char tmp[10]; +intarray_get_datum(int32 arr[], int len) { + int j; + char str[1024]; + char tmp[10]; - str[0] = '\0'; + str[0] = '\0'; - /* Need to calculate the actual size, and avoid unnessary memory usage */ - for (j = 0; j < len; j++) - { - if (!str[0]) - { - snprintf(tmp, 10, "%d", arr[j]); - strcat(str, tmp); - continue; - } - snprintf(tmp, 10, ",%d", arr[j]); - strcat(str, tmp); + /* Need to calculate the actual size, and avoid unnessary memory usage */ + for (j = 0; j < len; j++) { + if (!str[0]) { + snprintf(tmp, 10, "%d", arr[j]); + strcat(str, tmp); + continue; } - return CStringGetTextDatum(str); + snprintf(tmp, 10, ",%d", arr[j]); + strcat(str, tmp); + } + return CStringGetTextDatum(str); } Datum -pg_stat_monitor_hook_stats(PG_FUNCTION_ARGS) -{ - return (Datum) 0; +pg_stat_monitor_hook_stats(PG_FUNCTION_ARGS) { + return (Datum) 0; } void -pgsm_emit_log_hook(ErrorData *edata) +pgsm_emit_log_hook(ErrorData * edata) { - if (!IsSystemInitialized() || edata == NULL) - goto exit; + if (!IsSystemInitialized() || edata == NULL) + goto exit; - if (IsParallelWorker()) - goto exit; + if (IsParallelWorker()) + goto exit; - /* Check if PostgreSQL has finished its own bootstraping code. */ - if (MyProc == NULL) - goto exit; + /* Check if PostgreSQL has finished its own bootstraping code. */ + if (MyProc == NULL) + goto exit; - /* Do not store */ - if (PGSM_ERROR_CAPTURE_ENABLED && edata->elevel >= WARNING && IsSystemOOM() == false) - { - pgsm_store_error(debug_query_string ? debug_query_string : "", - edata); - } + /* Do not store */ + if (PGSM_ERROR_CAPTURE_ENABLED && edata->elevel >= WARNING && IsSystemOOM() == false) { + pgsm_store_error(debug_query_string ? debug_query_string : "", + edata); + } exit: - if (prev_emit_log_hook) - prev_emit_log_hook(edata); + if (prev_emit_log_hook) + prev_emit_log_hook(edata); } bool IsSystemInitialized(void) { - return (system_init && IsHashInitialize()); + return (system_init && IsHashInitialize()); } static double time_diff(struct timeval end, struct timeval start) { - double mstart; - double mend; + double mstart; + double mend; - mend = ((double) end.tv_sec * 1000.0 + (double) end.tv_usec / 1000.0); - mstart = ((double) start.tv_sec * 1000.0 + (double) start.tv_usec / 1000.0); - return mend - mstart; + mend = ((double) end.tv_sec * 1000.0 + (double) end.tv_usec / 1000.0); + mstart = ((double) start.tv_sec * 1000.0 + (double) start.tv_usec / 1000.0); + return mend - mstart; } -char * +char * unpack_sql_state(int sql_state) { - static char buf[12]; - int i; + static char buf[12]; + int i; - for (i = 0; i < 5; i++) - { - buf[i] = PGUNSIXBIT(sql_state); - sql_state >>= 6; - } + for (i = 0; i < 5; i++) { + buf[i] = PGUNSIXBIT(sql_state); + sql_state >>= 6; + } - buf[i] = '\0'; - return buf; + buf[i] = '\0'; + return buf; } /* Validate histogram values and find the max number of histogram buckets that can be created */ static void set_histogram_bucket_timings(void) { - double b2_start; - double b2_end; - int b_count; + double b2_start; + double b2_end; + int b_count; - hist_bucket_min = pgsm_histogram_min; - hist_bucket_max = pgsm_histogram_max; - hist_bucket_count_user = pgsm_histogram_buckets; - b_count = hist_bucket_count_user; + hist_bucket_min = pgsm_histogram_min; + hist_bucket_max = pgsm_histogram_max; + hist_bucket_count_user = pgsm_histogram_buckets; + b_count = hist_bucket_count_user; - if (pgsm_histogram_buckets >= 2) - { - for (; hist_bucket_count_user > 0; hist_bucket_count_user--) - { - histogram_bucket_timings(2, &b2_start, &b2_end); + if (pgsm_histogram_buckets >= 2) { + for (; hist_bucket_count_user > 0; hist_bucket_count_user--) { + histogram_bucket_timings(2, &b2_start, &b2_end); - /* - * The first bucket size will always be one or greater as we're doing min value + e^0; and e^0 = 1. - * Checking if histograms buckets overlap. That can only happen if the second bucket size is zero - * as we using exponential bucket sizes. Therefore, if the second bucket size is greater than 1, we'll - * never have overlapping buckets. - */ - if (b2_start != b2_end) - { - break; - } - } - - if (b_count != hist_bucket_count_user) - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("pg_stat_monitor: Histogram buckets are overlapping."), - errdetail("Histogram bucket size is set to %d [not including outlier buckets].", hist_bucket_count_user))); + /* + * The first bucket size will always be one or greater as we're + * doing min value + e^0; and e^0 = 1. Checking if histograms + * buckets overlap. That can only happen if the second bucket size + * is zero as we using exponential bucket sizes. Therefore, if the + * second bucket size is greater than 1, we'll never have + * overlapping buckets. + */ + if (b2_start != b2_end) { + break; + } } - /* - * Important that we keep user bucket count separate for calculations, but must add 1 - * for max outlier queries. However, for min, bucket should only be added - * if the minimum value provided by user is greater than 0 - */ - hist_bucket_count_total = (hist_bucket_count_user + (int)(hist_bucket_max < HISTOGRAM_MAX_TIME) + (int)(hist_bucket_min > 0)); + if (b_count != hist_bucket_count_user) + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_stat_monitor: Histogram buckets are overlapping."), + errdetail("Histogram bucket size is set to %d [not including outlier buckets].", hist_bucket_count_user))); + } - for(b_count = 0; b_count < hist_bucket_count_total; b_count++) - { - histogram_bucket_timings(b_count, &hist_bucket_timings[b_count][HISTOGRAM_START], &hist_bucket_timings[b_count][HISTOGRAM_END]); - } + /* + * Important that we keep user bucket count separate for calculations, but + * must add 1 for max outlier queries. However, for min, bucket should + * only be added if the minimum value provided by user is greater than 0 + */ + hist_bucket_count_total = (hist_bucket_count_user + (int) (hist_bucket_max < HISTOGRAM_MAX_TIME) + (int) (hist_bucket_min > 0)); + + for (b_count = 0; b_count < hist_bucket_count_total; b_count++) { + histogram_bucket_timings(b_count, &hist_bucket_timings[b_count][HISTOGRAM_START], &hist_bucket_timings[b_count][HISTOGRAM_END]); + } } /* @@ -3598,39 +3493,39 @@ set_histogram_bucket_timings(void) static void histogram_bucket_timings(int index, double *b_start, double *b_end) { - double q_min = hist_bucket_min; - double q_max = hist_bucket_max; - int b_count = hist_bucket_count_total; - int b_count_user = hist_bucket_count_user; - double bucket_size; + double q_min = hist_bucket_min; + double q_max = hist_bucket_max; + int b_count = hist_bucket_count_total; + int b_count_user = hist_bucket_count_user; + double bucket_size; - /* - * We must not skip any queries that fall outside the user defined - * histogram buckets. So capturing min/max outliers. - */ - if (index == 0 && q_min > 0) - { - *b_start = 0; - *b_end = q_min; - return; - } - else if (index == (b_count - 1) && q_max < HISTOGRAM_MAX_TIME) - { - *b_start = q_max; - *b_end = -1; - return; - } + /* + * We must not skip any queries that fall outside the user defined + * histogram buckets. So capturing min/max outliers. + */ + if (index == 0 && q_min > 0) { + *b_start = 0; + *b_end = q_min; + return; + } else if (index == (b_count - 1) && q_max < HISTOGRAM_MAX_TIME) { + *b_start = q_max; + *b_end = -1; + return; + } - /* - * Equisized logrithmic values will yield exponential values as required. - * For calculating logrithmic value, we MUST use the number of bucket provided - * by the user. - */ - bucket_size = log(q_max - q_min) / (double) b_count_user; + /* + * Equisized logrithmic values will yield exponential values as required. + * For calculating logrithmic value, we MUST use the number of bucket + * provided by the user. + */ + bucket_size = log(q_max - q_min) / (double) b_count_user; - /* Can't do exp(0) as that returns 1. So handling the case of first entry specifically */ - *b_start = q_min + ((index == 0 || (index == 1 && q_min > 0)) ? 0 : exp(bucket_size * (index - 1 + (q_min == 0)))); - *b_end = q_min + exp(bucket_size * (index + (q_min == 0))); + /* + * Can't do exp(0) as that returns 1. So handling the case of first entry + * specifically + */ + *b_start = q_min + ((index == 0 || (index == 1 && q_min > 0)) ? 0 : exp(bucket_size * (index - 1 + (q_min == 0)))); + *b_end = q_min + exp(bucket_size * (index + (q_min == 0))); } /* @@ -3639,20 +3534,19 @@ histogram_bucket_timings(int index, double *b_start, double *b_end) static int get_histogram_bucket(double q_time) { - int index = 0; - double exec_time = q_time; + int index = 0; + double exec_time = q_time; - for (index = 0; index < hist_bucket_count_total; index++) - { - if (exec_time >= hist_bucket_timings[index][HISTOGRAM_START] && exec_time <= hist_bucket_timings[index][HISTOGRAM_END]) - return index; - } + for (index = 0; index < hist_bucket_count_total; index++) { + if (exec_time >= hist_bucket_timings[index][HISTOGRAM_START] && exec_time <= hist_bucket_timings[index][HISTOGRAM_END]) + return index; + } - /* - * So haven't found a histogram bucket for this query. That's only possible for the - * last bucket as its end time is less than 0. - */ - return (hist_bucket_count_total - 1); + /* + * So haven't found a histogram bucket for this query. That's only + * possible for the last bucket as its end time is less than 0. + */ + return (hist_bucket_count_total - 1); } /* @@ -3660,100 +3554,88 @@ get_histogram_bucket(double q_time) * has ellipses as the end value indication infinity. */ Datum -get_histogram_timings(PG_FUNCTION_ARGS) -{ - double b_start; - double b_end; - int b_count = hist_bucket_count_total; - int index = 0; - char *tmp_str = palloc0(MAX_STRING_LEN); - char *text_str = palloc0(MAX_STRING_LEN); +get_histogram_timings(PG_FUNCTION_ARGS) { + double b_start; + double b_end; + int b_count = hist_bucket_count_total; + int index = 0; + char *tmp_str = palloc0(MAX_STRING_LEN); + char *text_str = palloc0(MAX_STRING_LEN); - for (index = 0; index < b_count; index++) - { - histogram_bucket_timings(index, &b_start, &b_end); + for (index = 0; index < b_count; index++) { + histogram_bucket_timings(index, &b_start, &b_end); - if (index == 0) - { - snprintf(text_str, MAX_STRING_LEN, "{{%.3lf - %.3lf}", b_start, b_end); - } - else if (index == (b_count - 1)) - { - snprintf(tmp_str, MAX_STRING_LEN, "%s, (%.3lf - ...}}", text_str, b_start); - snprintf(text_str, MAX_STRING_LEN, "%s", tmp_str); - } - else - { - snprintf(tmp_str, MAX_STRING_LEN, "%s, (%.3lf - %.3lf}", text_str, b_start, b_end); - snprintf(text_str, MAX_STRING_LEN, "%s", tmp_str); - } + if (index == 0) { + snprintf(text_str, MAX_STRING_LEN, "{{%.3lf - %.3lf}", b_start, b_end); + } else if (index == (b_count - 1)) { + snprintf(tmp_str, MAX_STRING_LEN, "%s, (%.3lf - ...}}", text_str, b_start); + snprintf(text_str, MAX_STRING_LEN, "%s", tmp_str); + } else { + snprintf(tmp_str, MAX_STRING_LEN, "%s, (%.3lf - %.3lf}", text_str, b_start, b_end); + snprintf(text_str, MAX_STRING_LEN, "%s", tmp_str); } + } - pfree(tmp_str); + pfree(tmp_str); - return CStringGetTextDatum(text_str); + return CStringGetTextDatum(text_str); } static void extract_query_comments(const char *query, char *comments, size_t max_len) { - int rc; - size_t nmatch = 1; - regmatch_t pmatch; - regoff_t comment_len, - total_len = 0; - const char *s = query; + int rc; + size_t nmatch = 1; + regmatch_t pmatch; + regoff_t comment_len, total_len = 0; + const char *s = query; - while (total_len < max_len) - { - rc = regexec(&preg_query_comments, s, nmatch, &pmatch, 0); - if (rc != 0) - break; + while (total_len < max_len) { + rc = regexec(&preg_query_comments, s, nmatch, &pmatch, 0); + if (rc != 0) + break; - comment_len = pmatch.rm_eo - pmatch.rm_so; + comment_len = pmatch.rm_eo - pmatch.rm_so; - if (total_len + comment_len > max_len) - break; /* TODO: log error in error view, insufficient - * space for comment. */ + if (total_len + comment_len > max_len) + break; /* TODO: log error in error view, insufficient + * space for comment. */ - total_len += comment_len; + total_len += comment_len; - /* Not 1st iteration, append ", " before next comment. */ - if (s != query) - { - if (total_len + 2 > max_len) - break; /* TODO: log error in error view, insufficient - * space for ", " + comment. */ + /* Not 1st iteration, append ", " before next comment. */ + if (s != query) { + if (total_len + 2 > max_len) + break; /* TODO: log error in error view, insufficient + * space for ", " + comment. */ - memcpy(comments, ", ", 2); - comments += 2; - total_len += 2; - } - - memcpy(comments, s + pmatch.rm_so, comment_len); - comments += comment_len; - s += pmatch.rm_eo; + memcpy(comments, ", ", 2); + comments += 2; + total_len += 2; } + + memcpy(comments, s + pmatch.rm_so, comment_len); + comments += comment_len; + s += pmatch.rm_eo; + } } #if PG_VERSION_NUM < 140000 static uint64 -get_query_id(JumbleState *jstate, Query *query) -{ - uint64 queryid; +get_query_id(JumbleState * jstate, Query * query) { + uint64 queryid; - /* Set up workspace for query jumbling */ - jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE); - jstate->jumble_len = 0; - jstate->clocations_buf_size = 32; - jstate->clocations = (LocationLen *) palloc(jstate->clocations_buf_size * sizeof(LocationLen)); - jstate->clocations_count = 0; - jstate->highest_extern_param_id = 0; + /* Set up workspace for query jumbling */ + jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE); + jstate->jumble_len = 0; + jstate->clocations_buf_size = 32; + jstate->clocations = (LocationLen *) palloc(jstate->clocations_buf_size * sizeof(LocationLen)); + jstate->clocations_count = 0; + jstate->highest_extern_param_id = 0; - /* Compute query ID and mark the Query node with it */ - JumbleQuery(jstate, query); - queryid = pgsm_hash_string((const char *)jstate->jumble, jstate->jumble_len); - return queryid; + /* Compute query ID and mark the Query node with it */ + JumbleQuery(jstate, query); + queryid = pgsm_hash_string((const char *) jstate->jumble, jstate->jumble_len); + return queryid; } #endif - diff --git a/pg_stat_monitor.h b/pg_stat_monitor.h index 6d99d6c..a2b03c1 100644 --- a/pg_stat_monitor.h +++ b/pg_stat_monitor.h @@ -66,13 +66,16 @@ /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */ #define USAGE_EXEC(duration) (1.0) -#define USAGE_INIT (1.0) /* including initial planning */ -#define ASSUMED_LENGTH_INIT 1024 /* initial assumed mean query length */ +#define USAGE_INIT (1.0) /* including initial + * planning */ +#define ASSUMED_LENGTH_INIT 1024 /* initial assumed mean query + * length */ #define USAGE_DECREASE_FACTOR (0.99) /* decreased every entry_dealloc */ #define STICKY_DECREASE_FACTOR (0.50) /* factor for sticky entries */ #define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */ -#define JUMBLE_SIZE 1024 /* query serialization buffer size */ +#define JUMBLE_SIZE 1024 /* query serialization + * buffer size */ #define HISTOGRAM_MAX_TIME 50000000 #define MAX_RESPONSE_BUCKET 50 @@ -82,7 +85,10 @@ #define ERROR_MESSAGE_LEN 100 #define REL_TYPENAME_LEN 64 #define REL_LST 10 -#define REL_LEN 132 /* REL_TYPENAME_LEN * 2 (relname + schema) + 1 (for view indication) + 1 and dot and string terminator */ +#define REL_LEN 132 /* REL_TYPENAME_LEN * 2 + * (relname + schema) + 1 (for + * view indication) + 1 and + * dot and string terminator */ #define CMD_LST 10 #define CMD_LEN 20 #define APPLICATIONNAME_LEN NAMEDATALEN @@ -114,13 +120,13 @@ /* * API for disabling error capture ereport(ERROR,..) by PGSM's error capture hook * pgsm_emit_log_hook() - * + * * Use these macros as follows: * PGSM_DISABLE_ERROR_CAPUTRE(); * { * ... code that might throw ereport(ERROR) ... * }PGSM_END_DISABLE_ERROR_CAPTURE(); - * + * * These macros can be used to error recursion if the error gets * thrown from within the function called from pgsm_emit_log_hook() */ @@ -158,43 +164,41 @@ extern volatile bool __pgsm_do_not_capture_error; * the classic shared memory hash table. */ #ifdef USE_DYNAMIC_HASH - #define PGSM_HASH_TABLE dshash_table - #define PGSM_HASH_TABLE_HANDLE dshash_table_handle - #define PGSM_HASH_SEQ_STATUS dshash_seq_status +#define PGSM_HASH_TABLE dshash_table +#define PGSM_HASH_TABLE_HANDLE dshash_table_handle +#define PGSM_HASH_SEQ_STATUS dshash_seq_status #else - #define PGSM_HASH_TABLE HTAB - #define PGSM_HASH_TABLE_HANDLE HTAB* - #define PGSM_HASH_SEQ_STATUS HASH_SEQ_STATUS +#define PGSM_HASH_TABLE HTAB +#define PGSM_HASH_TABLE_HANDLE HTAB* +#define PGSM_HASH_SEQ_STATUS HASH_SEQ_STATUS #endif #if PG_VERSION_NUM < 130000 -typedef struct WalUsage -{ - long wal_records; /* # of WAL records produced */ - long wal_fpi; /* # of WAL full page images produced */ - uint64 wal_bytes; /* size of WAL records produced */ -} WalUsage; +typedef struct WalUsage { + long wal_records; /* # of WAL records produced */ + long wal_fpi; /* # of WAL full page images produced */ + uint64 wal_bytes; /* size of WAL records produced */ +} WalUsage; #endif -typedef enum pgsmStoreKind -{ - PGSM_INVALID = -1, +typedef enum pgsmStoreKind { + PGSM_INVALID = -1, - /* - * PGSM_PLAN and PGSM_EXEC must be respectively 0 and 1 as they're used to - * reference the underlying values in the arrays in the Counters struct, - * and this order is required in pg_stat_monitor_internal(). - */ - PGSM_PARSE = 0, - PGSM_PLAN, - PGSM_EXEC, - PGSM_STORE, - PGSM_ERROR, + /* + * PGSM_PLAN and PGSM_EXEC must be respectively 0 and 1 as they're used to + * reference the underlying values in the arrays in the Counters struct, + * and this order is required in pg_stat_monitor_internal(). + */ + PGSM_PARSE = 0, + PGSM_PLAN, + PGSM_EXEC, + PGSM_STORE, + PGSM_ERROR, - PGSM_NUMKIND /* Must be last value of this enum */ -} pgsmStoreKind; + PGSM_NUMKIND /* Must be last value of this enum */ +} pgsmStoreKind; /* the assumption of query max nested level */ #define DEFAULT_MAX_NESTED_LEVEL 10 @@ -202,313 +206,297 @@ typedef enum pgsmStoreKind /* * Type of aggregate keys */ -typedef enum AGG_KEY -{ - AGG_KEY_DATABASE = 0, - AGG_KEY_USER, - AGG_KEY_HOST -} AGG_KEY; +typedef enum AGG_KEY { + AGG_KEY_DATABASE = 0, + AGG_KEY_USER, + AGG_KEY_HOST +} AGG_KEY; #define MAX_QUERY_LEN 1024 /* shared memory storage for the query */ -typedef struct CallTime -{ - double total_time; /* total execution time, in msec */ - double min_time; /* minimum execution time in msec */ - double max_time; /* maximum execution time in msec */ - double mean_time; /* mean execution time in msec */ - double sum_var_time; /* sum of variances in execution time in msec */ -} CallTime; +typedef struct CallTime { + double total_time; /* total execution time, in msec */ + double min_time; /* minimum execution time in msec */ + double max_time; /* maximum execution time in msec */ + double mean_time; /* mean execution time in msec */ + double sum_var_time; /* sum of variances in execution time in msec */ +} CallTime; -typedef struct PlanInfo -{ - uint64 planid; /* plan identifier */ - char plan_text[PLAN_TEXT_LEN]; /* plan text */ - size_t plan_len; /* strlen(plan_text) */ -} PlanInfo; +typedef struct PlanInfo { + uint64 planid; /* plan identifier */ + char plan_text[PLAN_TEXT_LEN]; /* plan text */ + size_t plan_len; /* strlen(plan_text) */ +} PlanInfo; -typedef struct pgsmHashKey -{ - uint64 bucket_id; /* bucket number */ - uint64 queryid; /* query identifier */ - uint64 planid; /* plan identifier */ - uint64 appid; /* hash of application name */ - Oid userid; /* user OID */ - Oid dbid; /* database OID */ - uint32 ip; /* client ip address */ - bool toplevel; /* query executed at top level */ -} pgsmHashKey; +typedef struct pgsmHashKey { + uint64 bucket_id; /* bucket number */ + uint64 queryid; /* query identifier */ + uint64 planid; /* plan identifier */ + uint64 appid; /* hash of application name */ + Oid userid; /* user OID */ + Oid dbid; /* database OID */ + uint32 ip; /* client ip address */ + bool toplevel; /* query executed at top level */ +} pgsmHashKey; -typedef struct QueryInfo -{ - uint64 parentid; /* parent queryid of current query */ - dsa_pointer parent_query; - int64 type; /* type of query, options are query, info, - * warning, error, fatal */ - char application_name[APPLICATIONNAME_LEN]; - char comments[COMMENTS_LEN]; - char relations[REL_LST][REL_LEN]; /* List of relation involved - * in the query */ - int num_relations; /* Number of relation in the query */ - CmdType cmd_type; /* query command type - * SELECT/UPDATE/DELETE/INSERT */ -} QueryInfo; +typedef struct QueryInfo { + uint64 parentid; /* parent queryid of current query */ + dsa_pointer parent_query; + int64 type; /* type of query, options are query, info, + * warning, error, fatal */ + char application_name[APPLICATIONNAME_LEN]; + char comments[COMMENTS_LEN]; + char relations[REL_LST][REL_LEN]; /* List of relation involved + * in the query */ + int num_relations; /* Number of relation in the query */ + CmdType cmd_type; /* query command type + * SELECT/UPDATE/DELETE/INSERT */ +} QueryInfo; -typedef struct ErrorInfo -{ - int64 elevel; /* error elevel */ - char sqlcode[SQLCODE_LEN]; /* error sqlcode */ - char message[ERROR_MESSAGE_LEN]; /* error message text */ -} ErrorInfo; +typedef struct ErrorInfo { + int64 elevel; /* error elevel */ + char sqlcode[SQLCODE_LEN]; /* error sqlcode */ + char message[ERROR_MESSAGE_LEN]; /* error message text */ +} ErrorInfo; -typedef struct Calls -{ - int64 calls; /* # of times executed */ - int64 rows; /* total # of retrieved or affected rows */ - double usage; /* usage factor */ -} Calls; +typedef struct Calls { + int64 calls; /* # of times executed */ + int64 rows; /* total # of retrieved or affected rows */ + double usage; /* usage factor */ +} Calls; -typedef struct Blocks -{ - int64 shared_blks_hit; /* # of shared buffer hits */ - int64 shared_blks_read; /* # of shared disk blocks read */ - int64 shared_blks_dirtied; /* # of shared disk blocks dirtied */ - int64 shared_blks_written; /* # of shared disk blocks written */ - int64 local_blks_hit; /* # of local buffer hits */ - int64 local_blks_read; /* # of local disk blocks read */ - int64 local_blks_dirtied; /* # of local disk blocks dirtied */ - int64 local_blks_written; /* # of local disk blocks written */ - int64 temp_blks_read; /* # of temp blocks read */ - int64 temp_blks_written; /* # of temp blocks written */ - double blk_read_time; /* time spent reading, in msec */ - double blk_write_time; /* time spent writing, in msec */ +typedef struct Blocks { + int64 shared_blks_hit; /* # of shared buffer hits */ + int64 shared_blks_read; /* # of shared disk blocks read */ + int64 shared_blks_dirtied; /* # of shared disk blocks dirtied */ + int64 shared_blks_written; /* # of shared disk blocks written */ + int64 local_blks_hit; /* # of local buffer hits */ + int64 local_blks_read; /* # of local disk blocks read */ + int64 local_blks_dirtied; /* # of local disk blocks dirtied */ + int64 local_blks_written; /* # of local disk blocks written */ + int64 temp_blks_read; /* # of temp blocks read */ + int64 temp_blks_written; /* # of temp blocks written */ + double blk_read_time; /* time spent reading, in msec */ + double blk_write_time; /* time spent writing, in msec */ - double temp_blk_read_time; /* time spent reading temp blocks, in msec */ - double temp_blk_write_time; /* time spent writing temp blocks, in - * msec */ + double temp_blk_read_time; /* time spent reading temp blocks, in + * msec */ + double temp_blk_write_time; /* time spent writing temp blocks, in + * msec */ - /* - * Variables for local entry. The values to be passed to pgsm_update_entry - * from pgsm_store. - */ - instr_time instr_blk_read_time; /* time spent reading blocks */ - instr_time instr_blk_write_time; /* time spent writing blocks */ - instr_time instr_temp_blk_read_time; /* time spent reading temp blocks */ - instr_time instr_temp_blk_write_time; /* time spent writing temp blocks */ -} Blocks; + /* + * Variables for local entry. The values to be passed to pgsm_update_entry + * from pgsm_store. + */ + instr_time instr_blk_read_time; /* time spent reading blocks */ + instr_time instr_blk_write_time; /* time spent writing blocks */ + instr_time instr_temp_blk_read_time; /* time spent reading temp + * blocks */ + instr_time instr_temp_blk_write_time; /* time spent writing temp + * blocks */ +} Blocks; -typedef struct JitInfo -{ - int64 jit_functions; /* total number of JIT functions emitted */ - double jit_generation_time; /* total time to generate jit code */ - int64 jit_inlining_count; /* number of times inlining time has been - * > 0 */ - double jit_inlining_time; /* total time to inline jit code */ - int64 jit_optimization_count; /* number of times optimization time - * has been > 0 */ - double jit_optimization_time; /* total time to optimize jit code */ - int64 jit_emission_count; /* number of times emission time has been - * > 0 */ - double jit_emission_time; /* total time to emit jit code */ +typedef struct JitInfo { + int64 jit_functions; /* total number of JIT functions emitted */ + double jit_generation_time; /* total time to generate jit code */ + int64 jit_inlining_count; /* number of times inlining time has + * been > 0 */ + double jit_inlining_time; /* total time to inline jit code */ + int64 jit_optimization_count; /* number of times optimization time + * has been > 0 */ + double jit_optimization_time; /* total time to optimize jit code */ + int64 jit_emission_count; /* number of times emission time has + * been > 0 */ + double jit_emission_time; /* total time to emit jit code */ - /* - * Variables for local entry. The values to be passed to pgsm_update_entry - * from pgsm_store. - */ - instr_time instr_generation_counter; /* generation counter */ - instr_time instr_inlining_counter; /* inlining counter */ - instr_time instr_optimization_counter; /* optimization counter */ - instr_time instr_emission_counter; /* emission counter */ -} JitInfo; + /* + * Variables for local entry. The values to be passed to pgsm_update_entry + * from pgsm_store. + */ + instr_time instr_generation_counter; /* generation counter */ + instr_time instr_inlining_counter; /* inlining counter */ + instr_time instr_optimization_counter; /* optimization counter */ + instr_time instr_emission_counter; /* emission counter */ +} JitInfo; -typedef struct SysInfo -{ - double utime; /* user cpu time */ - double stime; /* system cpu time */ -} SysInfo; +typedef struct SysInfo { + double utime; /* user cpu time */ + double stime; /* system cpu time */ +} SysInfo; -typedef struct Wal_Usage -{ - int64 wal_records; /* # of WAL records generated */ - int64 wal_fpi; /* # of WAL full page images generated */ - uint64 wal_bytes; /* total amount of WAL bytes generated */ -} Wal_Usage; +typedef struct Wal_Usage { + int64 wal_records; /* # of WAL records generated */ + int64 wal_fpi; /* # of WAL full page images generated */ + uint64 wal_bytes; /* total amount of WAL bytes generated */ +} Wal_Usage; -typedef struct Counters -{ - Calls calls; - QueryInfo info; - CallTime time; +typedef struct Counters { + Calls calls; + QueryInfo info; + CallTime time; - Calls plancalls; - CallTime plantime; - PlanInfo planinfo; + Calls plancalls; + CallTime plantime; + PlanInfo planinfo; - Blocks blocks; - SysInfo sysinfo; - JitInfo jitinfo; - ErrorInfo error; - Wal_Usage walusage; - int resp_calls[MAX_RESPONSE_BUCKET]; /* execution time's in - * msec */ -} Counters; + Blocks blocks; + SysInfo sysinfo; + JitInfo jitinfo; + ErrorInfo error; + Wal_Usage walusage; + int resp_calls[MAX_RESPONSE_BUCKET]; /* execution time's in + * msec */ +} Counters; /* Some global structure to get the cpu usage, really don't like the idea of global variable */ /* * Statistics per statement */ -typedef struct pgsmEntry -{ - pgsmHashKey key; /* hash key of entry - MUST BE FIRST */ - uint64 pgsm_query_id; /* pgsm generate normalized query hash */ - char datname[NAMEDATALEN]; /* database name */ - char username[NAMEDATALEN]; /* user name */ - Counters counters; /* the statistics for this query */ - int encoding; /* query text encoding */ - slock_t mutex; /* protects the counters only */ - union - { - dsa_pointer query_pos; /* query location within query buffer */ - char* query_pointer; - }query_text; -} pgsmEntry; +typedef struct pgsmEntry { + pgsmHashKey key; /* hash key of entry - MUST BE FIRST */ + uint64 pgsm_query_id; /* pgsm generate normalized query hash */ + char datname[NAMEDATALEN]; /* database name */ + char username[NAMEDATALEN]; /* user name */ + Counters counters; /* the statistics for this query */ + int encoding; /* query text encoding */ + slock_t mutex; /* protects the counters only */ + union { + dsa_pointer query_pos; /* query location within query buffer */ + char *query_pointer; + } query_text; +} pgsmEntry; /* * Global shared state */ -typedef struct pgsmSharedState -{ - LWLock *lock; /* protects hashtable search/modification */ - slock_t mutex; /* protects following fields only: */ - pg_atomic_uint64 current_wbucket; - pg_atomic_uint64 prev_bucket_sec; - uint64 bucket_entry[MAX_BUCKETS]; - TimestampTz bucket_start_time[MAX_BUCKETS]; /* start time of the bucket */ - int hash_tranche_id; - void *raw_dsa_area; /* DSA area pointer to store query texts. - * dshash also lives in this memory when - * USE_DYNAMIC_HASH is enabled */ - PGSM_HASH_TABLE_HANDLE hash_handle; - /* hash table handle. can be either - * classic shared memory hash or dshash - * (if we are using USE_DYNAMIC_HASH) - */ - MemoryContext pgsm_mem_cxt; - /* context to store stats in local - * memory until they are pushed to shared hash - */ - int resp_calls[MAX_RESPONSE_BUCKET + 2]; /* execution time's in - * msec; including 2 outlier buckets */ - bool pgsm_oom; -} pgsmSharedState; +typedef struct pgsmSharedState { + LWLock *lock; /* protects hashtable search/modification */ + slock_t mutex; /* protects following fields only: */ + pg_atomic_uint64 current_wbucket; + pg_atomic_uint64 prev_bucket_sec; + uint64 bucket_entry[MAX_BUCKETS]; + TimestampTz bucket_start_time[MAX_BUCKETS]; /* start time of the bucket */ + int hash_tranche_id; + void *raw_dsa_area; /* DSA area pointer to store query texts. + * dshash also lives in this memory when + * USE_DYNAMIC_HASH is enabled */ + PGSM_HASH_TABLE_HANDLE hash_handle; + /* + * hash table handle. can be either classic shared memory hash or dshash + * (if we are using USE_DYNAMIC_HASH) + */ + MemoryContext pgsm_mem_cxt; + /* + * context to store stats in local memory until they are pushed to shared + * hash + */ + int resp_calls[MAX_RESPONSE_BUCKET + 2]; /* execution time's in + * msec; including 2 + * outlier buckets */ + bool pgsm_oom; +} pgsmSharedState; -typedef struct pgsmLocalState -{ - pgsmSharedState *shared_pgsmState; - dsa_area *dsa; /* local dsa area for backend attached to the - * dsa area created by postmaster at startup. - */ - PGSM_HASH_TABLE *shared_hash; -}pgsmLocalState; +typedef struct pgsmLocalState { + pgsmSharedState *shared_pgsmState; + dsa_area *dsa; /* local dsa area for backend attached to the + * dsa area created by postmaster at startup. */ + PGSM_HASH_TABLE *shared_hash; +} pgsmLocalState; #if PG_VERSION_NUM < 140000 /* * Struct for tracking locations/lengths of constants during normalization */ -typedef struct LocationLen -{ - int location; /* start offset in query text */ - int length; /* length in bytes, or -1 to ignore */ -} LocationLen; +typedef struct LocationLen { + int location; /* start offset in query text */ + int length; /* length in bytes, or -1 to ignore */ +} LocationLen; /* * Working state for computing a query jumble and producing a normalized * query string */ -typedef struct JumbleState -{ - /* Jumble of current query tree */ - unsigned char *jumble; +typedef struct JumbleState { + /* Jumble of current query tree */ + unsigned char *jumble; - /* Number of bytes used in jumble[] */ - Size jumble_len; + /* Number of bytes used in jumble[] */ + Size jumble_len; - /* Array of locations of constants that should be removed */ - LocationLen *clocations; + /* Array of locations of constants that should be removed */ + LocationLen *clocations; - /* Allocated length of clocations array */ - int clocations_buf_size; + /* Allocated length of clocations array */ + int clocations_buf_size; - /* Current number of valid entries in clocations array */ - int clocations_count; + /* Current number of valid entries in clocations array */ + int clocations_count; - /* highest Param id we've seen, in order to start normalization correctly */ - int highest_extern_param_id; -} JumbleState; + /* highest Param id we've seen, in order to start normalization correctly */ + int highest_extern_param_id; +} JumbleState; #endif /* guc.c */ -void init_guc(void); +void init_guc(void); /* hash_create.c */ -dsa_area *get_dsa_area_for_query_text(void); -PGSM_HASH_TABLE *get_pgsmHash(void); +dsa_area *get_dsa_area_for_query_text(void); +PGSM_HASH_TABLE *get_pgsmHash(void); -void pgsm_attach_shmem(void); -bool IsHashInitialize(void); -bool IsSystemOOM(void); -void pgsm_shmem_startup(void); -void pgsm_shmem_shutdown(int code, Datum arg); -int pgsm_get_bucket_size(void); +void pgsm_attach_shmem(void); +bool IsHashInitialize(void); +bool IsSystemOOM(void); +void pgsm_shmem_startup(void); +void pgsm_shmem_shutdown(int code, Datum arg); +int pgsm_get_bucket_size(void); pgsmSharedState *pgsm_get_ss(void); -void hash_query_entries(); -void hash_query_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer[]); -void hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer); -pgsmEntry *hash_entry_alloc(pgsmSharedState *pgsm, pgsmHashKey *key, int encoding); -Size pgsm_ShmemSize(void); -void pgsm_startup(void); +void hash_query_entries(); +void hash_query_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer[]); +void hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer); +pgsmEntry *hash_entry_alloc(pgsmSharedState * pgsm, pgsmHashKey * key, int encoding); +Size pgsm_ShmemSize(void); +void pgsm_startup(void); /* hash_query.c */ -void pgsm_startup(void); +void pgsm_startup(void); /* guc.c */ -void init_guc(void); +void init_guc(void); /* GUC variables*/ /*---- GUC variables ----*/ -typedef enum -{ - PSGM_TRACK_NONE = 0, /* track no statements */ - PGSM_TRACK_TOP, /* only top level statements */ - PGSM_TRACK_ALL /* all statements, including nested ones */ -} PGSMTrackLevel; +typedef enum { + PSGM_TRACK_NONE = 0, /* track no statements */ + PGSM_TRACK_TOP, /* only top level statements */ + PGSM_TRACK_ALL /* all statements, including nested ones */ +} PGSMTrackLevel; static const struct config_enum_entry track_options[] = { - {"none", PSGM_TRACK_NONE, false}, - {"top", PGSM_TRACK_TOP, false}, - {"all", PGSM_TRACK_ALL, false}, - {NULL, 0, false} + {"none", PSGM_TRACK_NONE, false}, + {"top", PGSM_TRACK_TOP, false}, + {"all", PGSM_TRACK_ALL, false}, + {NULL, 0, false} }; -typedef enum -{ - HISTOGRAM_START, - HISTOGRAM_END, - HISTOGRAM_COUNT -} HistogramTimingType; +typedef enum { + HISTOGRAM_START, + HISTOGRAM_END, + HISTOGRAM_COUNT +} HistogramTimingType; -extern int pgsm_max; -extern int pgsm_query_max_len; -extern int pgsm_bucket_time; -extern int pgsm_max_buckets; -extern int pgsm_histogram_buckets; +extern int pgsm_max; +extern int pgsm_query_max_len; +extern int pgsm_bucket_time; +extern int pgsm_max_buckets; +extern int pgsm_histogram_buckets; extern double pgsm_histogram_min; extern double pgsm_histogram_max; -extern int pgsm_query_shared_buffer; +extern int pgsm_query_shared_buffer; extern bool pgsm_track_planning; extern bool pgsm_extract_comments; extern bool pgsm_enable_query_plan; @@ -516,7 +504,7 @@ extern bool pgsm_enable_overflow; extern bool pgsm_normalized_query; extern bool pgsm_track_utility; extern bool pgsm_enable_pgsm_query_id; -extern int pgsm_track; +extern int pgsm_track; #define DECLARE_HOOK(hook, ...) \ static hook(__VA_ARGS__); @@ -524,9 +512,9 @@ extern int pgsm_track; #define HOOK_STATS_SIZE 0 #endif -void *pgsm_hash_find_or_insert(PGSM_HASH_TABLE *shared_hash, pgsmHashKey *key, bool* found); -void *pgsm_hash_find(PGSM_HASH_TABLE *shared_hash, pgsmHashKey *key, bool* found); -void pgsm_hash_seq_init(PGSM_HASH_SEQ_STATUS *hstat, PGSM_HASH_TABLE *shared_hash, bool lock); -void *pgsm_hash_seq_next(PGSM_HASH_SEQ_STATUS *hstat); -void pgsm_hash_seq_term(PGSM_HASH_SEQ_STATUS *hstat); -void pgsm_hash_delete_current(PGSM_HASH_SEQ_STATUS *hstat, PGSM_HASH_TABLE *shared_hash, void *key); +void *pgsm_hash_find_or_insert(PGSM_HASH_TABLE * shared_hash, pgsmHashKey * key, bool *found); +void *pgsm_hash_find(PGSM_HASH_TABLE * shared_hash, pgsmHashKey * key, bool *found); +void pgsm_hash_seq_init(PGSM_HASH_SEQ_STATUS * hstat, PGSM_HASH_TABLE * shared_hash, bool lock); +void *pgsm_hash_seq_next(PGSM_HASH_SEQ_STATUS * hstat); +void pgsm_hash_seq_term(PGSM_HASH_SEQ_STATUS * hstat); +void pgsm_hash_delete_current(PGSM_HASH_SEQ_STATUS * hstat, PGSM_HASH_TABLE * shared_hash, void *key);