commit 6c8b1037e4a9c54c72861d400a9bb4c360a983a6
Author: Ivan Kovmir <i@kovmir.eu>
Date:   Mon Jul 21 10:42:17 2025 +0200

    Fix possible spinlock leakage
    
    * Spinlock is held longer than it is allowed to be.
    * `heap_form_tuple()` may raise errors while the lock is being held,
       which could result in spinlock leakage.
    
    Thanks https://github.com/ocean-dot-li
    Close #46.

--- a/pg_show_plans.c
+++ b/pg_show_plans.c
@@ -68,6 +68,7 @@ typedef struct pgspCtx { /* Used as `fun
 	pgspEntry       *pgsp_tmp_entry; /* PGSP entry currently processing. */
 	int              curr_nest; /* Current nest level porcessing. */
 	bool             is_done; /* Done processing current PGSP entry? */
+	int				 n_plans;
 } pgspCtx;
 
 /* Function Prototypes */
@@ -611,6 +612,7 @@ pg_show_plans(PG_FUNCTION_ARGS)
 		pgsp_ctx = (pgspCtx *)palloc(sizeof(pgspCtx));
 		pgsp_ctx->is_done = true;
 		pgsp_ctx->curr_nest = 0;
+		pgsp_ctx->n_plans = 0;
 		pgsp_ctx->hash_seq = (HASH_SEQ_STATUS *)palloc(sizeof(HASH_SEQ_STATUS));
 		hash_seq_init(pgsp_ctx->hash_seq, pgsp_hash);
 		funcctx->user_fctx = (void *)pgsp_ctx;
@@ -663,6 +665,8 @@ pg_show_plans(PG_FUNCTION_ARGS)
 				call_cntr++;
 			}
 			SpinLockAcquire(&pgsp_tmp_entry->mutex);
+			pgsp_ctx->n_plans = pgsp_tmp_entry->n_plans;
+			SpinLockRelease(&pgsp_tmp_entry->mutex);
 		}
 
 		/* A single hash entry may store multiple (nested) plans, so
@@ -679,7 +683,7 @@ pg_show_plans(PG_FUNCTION_ARGS)
 		values[4] = CStringGetTextDatum(pgsp_tmp_entry->plan + offset);
 		htup = heap_form_tuple(funcctx->tuple_desc, values, nulls);
 
-		if (curr_nest < pgsp_tmp_entry->n_plans-1)
+		if (curr_nest < pgsp_ctx->n_plans - 1)
 		{ /* Still have nested plans. */
 			curr_nest++;
 			call_cntr--; /* May not be legal, but it works. */
@@ -687,7 +691,7 @@ pg_show_plans(PG_FUNCTION_ARGS)
 		} else { /* No more nested plans, get a new entry. */
 			curr_nest = 0;
 			is_done = true;
-			SpinLockRelease(&pgsp_tmp_entry->mutex);
+			pgsp_ctx->n_plans = 0;
 		}
 		/* Save values back to the context. */
 		pgsp_ctx->is_done = is_done;
