diff --git a/pg_stat_monitor.c b/pg_stat_monitor.c index 065c684..c7c983f 100644 --- a/pg_stat_monitor.c +++ b/pg_stat_monitor.c @@ -203,6 +203,7 @@ char *unpack_sql_state(int sql_state); static pgsmEntry *pgsm_create_hash_entry(uint64 bucket_id, int64 queryid, PlanInfo *plan_info); static void pgsm_add_to_list(pgsmEntry *entry, char *query_text, int query_len); +static void pgsm_delete_entry(uint64 queryid); static pgsmEntry *pgsm_get_entry_for_query(int64 queryid, PlanInfo *plan_info, const char *query_text, int query_len, bool create, CmdType cmd_type); static int64 get_pgsm_query_id_hash(const char *norm_query, int len); @@ -804,6 +805,8 @@ pgsm_ExecutorEnd(QueryDesc *queryDesc) else standard_ExecutorEnd(queryDesc); + pgsm_delete_entry(queryDesc->plannedstmt->queryId); + num_relations = 0; } @@ -1274,6 +1277,7 @@ pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, PG_END_TRY(); #endif } + pgsm_delete_entry(pstmt->queryId); } /* @@ -1688,6 +1692,43 @@ pgsm_add_to_list(pgsmEntry *entry, char *query_text, int query_len) MemoryContextSwitchTo(oldctx); } +static void +pgsm_delete_entry(uint64 queryid) +{ + pgsmEntry *entry = NULL; + ListCell *lc = NULL; + + if (lentries == NIL) + return; + + entry = (pgsmEntry *) llast(lentries); + if (entry->key.queryid == queryid) + { + pfree(entry->query_text.query_pointer); + entry->query_text.query_pointer = NULL; + lentries = list_delete_last(lentries); + return; + } + + /* + * The rest of the code is just paranoia. In theory this list is a stack, + * and we always want to remove the last item. Similarly, in the getter + * method we are always looking for the last item. + */ + + foreach(lc, lentries) + { + entry = lfirst(lc); + if (entry->key.queryid == queryid) + { + pfree(entry->query_text.query_pointer); + entry->query_text.query_pointer = NULL; + lentries = list_delete_cell(lentries, lc); + return; + } + } +} + static pgsmEntry * pgsm_get_entry_for_query(int64 queryid, PlanInfo *plan_info, const char *query_text, int query_len, bool create, CmdType cmd_type) { diff --git a/regression/expected/top_query.out b/regression/expected/top_query.out index 38f0b37..b3b714f 100644 --- a/regression/expected/top_query.out +++ b/regression/expected/top_query.out @@ -42,6 +42,19 @@ SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C"; SELECT pg_stat_monitor_reset() | (5 rows) +-- make sure that we handle nested queries correctly +BEGIN; +DO $$ +DECLARE + i int; +BEGIN + -- default stack limit is 2000kB, 50000 is much larger than that + FOR i IN 1..50000 LOOP + EXECUTE format('SELECT %s', i); + END LOOP; +END; +$$; +COMMIT; SELECT pg_stat_monitor_reset(); pg_stat_monitor_reset ----------------------- diff --git a/regression/expected/top_query_1.out b/regression/expected/top_query_1.out index de7df82..daebb86 100644 --- a/regression/expected/top_query_1.out +++ b/regression/expected/top_query_1.out @@ -42,6 +42,19 @@ SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C"; SELECT pg_stat_monitor_reset() | (5 rows) +-- make sure that we handle nested queries correctly +BEGIN; +DO $$ +DECLARE + i int; +BEGIN + -- default stack limit is 2000kB, 50000 is much larger than that + FOR i IN 1..50000 LOOP + EXECUTE format('SELECT %s', i); + END LOOP; +END; +$$; +COMMIT; SELECT pg_stat_monitor_reset(); pg_stat_monitor_reset ----------------------- diff --git a/regression/sql/top_query.sql b/regression/sql/top_query.sql index 6f92a4b..851d57b 100644 --- a/regression/sql/top_query.sql +++ b/regression/sql/top_query.sql @@ -16,5 +16,22 @@ $$ language plpgsql; SELECT add2(1,2); SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + +-- make sure that we handle nested queries correctly + +BEGIN; +DO $$ +DECLARE + i int; +BEGIN + -- default stack limit is 2000kB, 50000 is much larger than that + FOR i IN 1..50000 LOOP + EXECUTE format('SELECT %s', i); + END LOOP; +END; +$$; + +COMMIT; + SELECT pg_stat_monitor_reset(); DROP EXTENSION pg_stat_monitor;