diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 9490e2e..480b1e6 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -376,3 +376,44 @@ postgres=# SELECT bucket, substr(query,0, 50) AS query, cmd_type FROM pg_stat_mo 4 | UPDATE pgbench_branches SET bbalance = bbalance + | UPDATE (14 rows) ``` + +#### Function Execution Tracking + +**`parentid`**: Outer layer caller's query id. + +```sql +postgres=# select prosrc from pg_proc where proname = 'getnum'; + prosrc +-------------------------------- + select * from t1 where a >= $1 +(1 row) + +postgresr=# select pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +postgres=# select prosrc from pg_proc where proname = 'getnum'; + prosrc +-------------------------------- + select * from t1 where a >= $1 +(1 row) + +postgres=# select * from getnum(2); + a +--- + 2 + 3 + 4 +(3 rows) + +postgres=# select queryid,parentid,query,calls from pg_stat_monitor; + queryid | parentid | query | calls +------------------+------------------+-----------------------------------------------+------- + 3FEC80684AFE7FC7 | DD2A4843140299C2 | select * from t1 where a >= $1 | 1 + 6ED05FFB78DD52FA | | select pg_stat_monitor_reset() | 1 + DD2A4843140299C2 | | select * from getnum($1) | 1 + 42517D48FB98ACF7 | | select prosrc from pg_proc where proname = $1 | 1 +(4 rows) +``` \ No newline at end of file diff --git a/expected/basic.out b/expected/basic.out index 5f505c1..edf72fb 100644 --- a/expected/basic.out +++ b/expected/basic.out @@ -17,7 +17,7 @@ SELECT 1; 1 (1 row) -SELECT query FROM pg_stat_monitor ORDER BY query; +SELECT query FROM pg_stat_monitor ORDER BY query COLLATE "C"; query -------------------------------- SELECT $1 diff --git a/expected/guc.out b/expected/guc.out index 9c1b2bf..9ef3503 100644 --- a/expected/guc.out +++ b/expected/guc.out @@ -20,9 +20,9 @@ SELECT * FROM pg_stat_monitor_settings; pg_stat_monitor.pgsm_track_utility | 0 | 0 | Selects whether utility commands are tracked. | 0 | 0 | 0 pg_stat_monitor.pgsm_normalized_query | 1 | 1 | Selects whether save query in normalized format. | 0 | 0 | 0 pg_stat_monitor.pgsm_max_buckets | 10 | 10 | Sets the maximum number of buckets. | 1 | 10 | 1 - pg_stat_monitor.pgsm_bucket_time | 300 | 60 | Sets the time in seconds per bucket. | 1 | 2147483647 | 1 + pg_stat_monitor.pgsm_bucket_time | 300 | 300 | Sets the time in seconds per bucket. | 1 | 2147483647 | 1 pg_stat_monitor.pgsm_histogram_min | 0 | 0 | Sets the time in millisecond. | 0 | 2147483647 | 1 - pg_stat_monitor.pgsm_histogram_max | 100000 | 10 | Sets the time in millisecond. | 10 | 2147483647 | 1 + pg_stat_monitor.pgsm_histogram_max | 100000 | 100000 | Sets the time in millisecond. | 10 | 2147483647 | 1 pg_stat_monitor.pgsm_histogram_buckets | 10 | 10 | Sets the maximum number of histogram buckets | 2 | 2147483647 | 1 (10 rows) diff --git a/guc.c b/guc.c index 2de9200..aebaf9f 100644 --- a/guc.c +++ b/guc.c @@ -104,7 +104,7 @@ init_guc(void) conf[i] = (GucVariable) { .guc_name = "pg_stat_monitor.pgsm_bucket_time", .guc_desc = "Sets the time in seconds per bucket.", - .guc_default = 60, + .guc_default = 300, .guc_min = 1, .guc_max = INT_MAX, .guc_restart = true, @@ -128,7 +128,7 @@ init_guc(void) conf[i] = (GucVariable) { .guc_name = "pg_stat_monitor.pgsm_histogram_max", .guc_desc = "Sets the time in millisecond.", - .guc_default = 10, + .guc_default = 100000, .guc_min = 10, .guc_max = INT_MAX, .guc_restart = true, diff --git a/pg_stat_monitor--1.0.sql b/pg_stat_monitor--1.0.sql index e2d4556..57e60d8 100644 --- a/pg_stat_monitor--1.0.sql +++ b/pg_stat_monitor--1.0.sql @@ -31,6 +31,7 @@ CREATE FUNCTION pg_stat_monitor(IN showtext boolean, OUT client_ip int8, OUT queryid text, + OUT parentid text, OUT query text, OUT application_name text, OUT relations text, @@ -125,10 +126,12 @@ CREATE VIEW pg_stat_monitor AS SELECT datname, '0.0.0.0'::inet + client_ip AS client_ip, queryid, + parentid, query, + (SELECT query from pg_stat_monitor(true) s where s.queryid = p.parentid) AS parent_query, application_name, string_to_array(relations, ',') AS relations, - cmd_type, + cmd_type, get_cmd_type(cmd_type) AS cmd_type_text, elevel, sqlcode, diff --git a/pg_stat_monitor.c b/pg_stat_monitor.c index fccfbf6..76a5df3 100644 --- a/pg_stat_monitor.c +++ b/pg_stat_monitor.c @@ -22,7 +22,7 @@ PG_MODULE_MAGIC; #define BUILD_VERSION "0.7.0" -#define PG_STAT_STATEMENTS_COLS 46 /* maximum of above */ +#define PG_STAT_STATEMENTS_COLS 47 /* maximum of above */ #define PGSM_TEXT_FILE "/tmp/pg_stat_monitor_query" #define PGUNSIXBIT(val) (((val) & 0x3F) + '0') @@ -63,6 +63,11 @@ void _PG_fini(void); /* Current nesting depth of ExecutorRun+ProcessUtility calls */ static int nested_level = 0; +/* the current max level a query can nested */ +int cur_max_nested_level; +/* The array to store outer layer query id*/ +uint64 *nested_queryids; + #if PG_VERSION_NUM >= 130000 static int plan_nested_level = 0; static int exec_nested_level = 0; @@ -240,6 +245,9 @@ _PG_init(void) prev_ExecutorCheckPerms_hook = ExecutorCheckPerms_hook; ExecutorCheckPerms_hook = pgss_ExecutorCheckPerms; + cur_max_nested_level = max_stack_depth; + nested_queryids = (uint64*)malloc(sizeof(uint64)*cur_max_nested_level); + system_init = true; } @@ -257,6 +265,9 @@ _PG_fini(void) ExecutorFinish_hook = prev_ExecutorFinish; ExecutorEnd_hook = prev_ExecutorEnd; ProcessUtility_hook = prev_ProcessUtility; + + free(nested_queryids); + hash_entry_reset(); } @@ -389,6 +400,12 @@ static void pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once) { + nested_queryids[nested_level] = queryDesc->plannedstmt->queryId; + if(nested_level + 1 >= cur_max_nested_level) + { + cur_max_nested_level *= 2; + nested_queryids = realloc(nested_queryids, cur_max_nested_level); + } nested_level++; PG_TRY(); { @@ -397,10 +414,12 @@ pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, else standard_ExecutorRun(queryDesc, direction, count, execute_once); nested_level--; + nested_queryids[nested_level] = UINT64CONST(0); } PG_CATCH(); { nested_level--; + nested_queryids[nested_level] = UINT64CONST(0); PG_RE_THROW(); } PG_END_TRY(); @@ -985,6 +1004,11 @@ static void pgss_store(uint64 queryId, _snprintf(e->counters.error.sqlcode, sqlcode, sqlcode_len, SQLCODE_LEN); _snprintf(e->counters.error.message, message, message_len, ERROR_MESSAGE_LEN); + if(nested_level > 0) + e->counters.info.parentid = nested_queryids[nested_level - 1]; + else + e->counters.info.parentid = UINT64CONST(0); + e->counters.calls[kind].rows += rows; e->counters.blocks.shared_blks_hit += bufusage->shared_blks_hit; e->counters.blocks.shared_blks_read += bufusage->shared_blks_read; @@ -1075,6 +1099,7 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, pgssEntry *entry; char *query_txt; char queryid_txt[64]; + char parentid_txt[64]; pgssSharedState *pgss = pgsm_get_ss(); HTAB *pgss_hash = pgsm_get_hash(); @@ -1179,7 +1204,19 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, tmp = e->counters; SpinLockRelease(&e->mutex); } + values[i++] = CStringGetTextDatum(queryid_txt); + + if (tmp.info.parentid != UINT64CONST(0)) + { + sprintf(parentid_txt,"%08lX",tmp.info.parentid); + values[i++] = CStringGetTextDatum(parentid_txt); + } + else + { + nulls[i++] = true; + } + if (is_allowed_role || entry->key.userid == userid) { if (showtext) @@ -1205,6 +1242,9 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, } else { + /*skip the query id and parent id*/ + nulls[i++] = true; + nulls[i++] = true; /* * Don't show query text, but hint as to the reason for not doing * so if it was requested diff --git a/pg_stat_monitor.h b/pg_stat_monitor.h index 4a62e32..84d51a4 100644 --- a/pg_stat_monitor.h +++ b/pg_stat_monitor.h @@ -120,6 +120,8 @@ typedef enum pgssStoreKind PGSS_NUMKIND /* Must be last value of this enum */ } pgssStoreKind; +/* the assumption of query max nested level */ +#define DEFAULT_MAX_NESTED_LEVEL 10 /* * Type of aggregate keys @@ -161,6 +163,7 @@ typedef struct QueryInfo Oid userid; /* user OID */ Oid dbid; /* database OID */ uint host; /* client IP */ + uint64 parentid; /* parent queryid of current query*/ int64 type; /* type of query, options are query, info, warning, error, fatal */ char application_name[APPLICATIONNAME_LEN]; char relations[REL_LST][REL_LEN]; /* List of relation involved in the query */ diff --git a/sql/basic.sql b/sql/basic.sql index 5c35788..e40fb8a 100644 --- a/sql/basic.sql +++ b/sql/basic.sql @@ -2,6 +2,6 @@ CREATE EXTENSION pg_stat_monitor; SELECT pg_stat_monitor_reset(); select pg_sleep(.5); SELECT 1; -SELECT query FROM pg_stat_monitor ORDER BY query; +SELECT query FROM pg_stat_monitor ORDER BY query COLLATE "C"; SELECT pg_stat_monitor_reset(); DROP EXTENSION pg_stat_monitor;