Skip to content

SQL API Reference

All SQL functions live in the ext_memcheck schema unless noted.


Moves all entries from the in-memory shared ring buffer (ViolationLog) into the persistent ext_memcheck.violation_log table and returns the count of rows inserted i.e., the number of violations flushed. It also clears the ring buffer, making it ready to log new violations.

-- flushes all pending violations to the table ext_memcheck.violation_log
-- and returns the count of violations
SELECT ext_memcheck.flush_violations();
-- query the violation log table directly
-- includes all historical violations, not just the flushed ones
SELECT * FROM ext_memcheck.violation_log;

Returns: int — number of violations flushed to the table.

Notes:

  • The ring buffer is bounded by MEMCHECK_MAX_VIOLATIONS (constant 2048, not configurable). When full, oldest entries are overwritten. Call flush_violations() regularly to avoid data loss.
  • Returns zero if no violations have been detected since the last flush.
  • flush_violations() drains the entire ring across all backends; end() drains only the current session's entries. Entries consumed by one function are not visible to the other.
ColumnTypeDescription
idintAuto-incrementing violation ID
tstimestamptzTimestamp of the violation
backend_pidintPID of the backend that detected the violation
check_typetextType of the check: context_leak, wrong_ctx_alloc, ctx_bloat, shmem_overrun, dsm_leak
severitytextSeverity: ERROR, WARNING, or INFO
detailtextHuman-readable description of the violation
source_libtextThe library that triggered the violation (e.g., buggy_pg_ext)
LevelMemory Leak Size
ERROR> 1 MiB
WARNING> 64 KiB and ≤ 1 MiB
INFOmin_leak_bytes and ≤ 64 KiB

Opens a manual test window targeting a specific extension by context-name pattern. The monitoring mode (all / executor / none) is controlled by the pg_ext_memcheck.memcheck_mode GUC. If the mode is none when begin() is called, begin() activates it to all so a window opens without a prior SET; an explicit pre-SET of executor or all is honoured unchanged. The matching end() resets the mode back to none.

-- Scope checks to contexts whose names match the SQL LIKE pattern 'MyExtCtx%'
SET pg_ext_memcheck.memcheck_mode = 'executor';
SELECT ext_memcheck.begin('MyExtCtx%');
-- Monitor all contexts (empty pattern = no filter)
SET pg_ext_memcheck.memcheck_mode = 'all';
SELECT ext_memcheck.begin('');
-- With options: allowlist contexts that are permitted to grow, and opt out of DSM checks
SELECT ext_memcheck.begin(
'MyExtCtx%',
'{"track_shmem": true, "track_dsm": false, "allowed_contexts": ["TopMemoryContext"]}'
);

Parameters:

ParameterTypeDefaultDescription
ext_context_patterntext''SQL LIKE pattern (% wildcard) for context names to monitor. An empty string monitors all contexts. Example: 'MyExtCtx%' monitors only contexts whose names start with MyExtCtx.
optionsjsonbNULLOptional JSON object with the following keys:

options keys:

KeyTypeDefaultDescription
track_shmembooltrueWhether shmem sentinel checks are active for this session.
track_dsmbooltrueWhether DSM leak checks are active for this session.
allowed_contextstext[][]Allowlist of context names explicitly permitted to grow without triggering a wrong_ctx_alloc violation. Extensions that intentionally cache data in TopMemoryContext across queries should list it here to suppress false positives.

Returns: text — confirmation message.

Why scope matters: Without a pattern, every PostgreSQL core context that grows during a query (e.g., CacheMemoryContext, ExprContext) is reported and attributed to whatever hook libraries are loaded. A targeted pattern eliminates these false positives and pinpoints violations to the extension under test.

-- Full targeted session example
SET pg_ext_memcheck.memcheck_mode = 'executor';
SELECT ext_memcheck.begin(
'MyExtCtx%',
'{"allowed_contexts": ["TopMemoryContext"]}'
);
-- Run the function under test
SELECT my_extension.do_work();
-- End the window; returns only violations from contexts matching 'MyExtCtx%'
SELECT * FROM ext_memcheck.end();

Closes the current manual test window, resets pg_ext_memcheck.memcheck_mode to 'none', and returns violations belonging to the current session as a result set.

SELECT * FROM ext_memcheck.end();

Returns: TABLE(check_type TEXT, severity TEXT, detail TEXT, ts TIMESTAMPTZ, source_lib TEXT) — violations detected during this session window.

Session-scoped drain semantics:

  • Only entries where backend_pid matches the calling backend and ts >= begin() call time are returned.
  • Matched slots are zeroed from the ring buffer atomically, so a second call to end() returns 0 rows (non-repeatable read).
  • Violations from other concurrent backends are not affected.
  • The ring buffer is not flushed to violation_log; those entries remain available to flush_violations() unless they were already drained by end().

Runs a named stress scenario. Violations are written to the shared ring buffer; call ext_memcheck.flush_violations() to read them by flushing the buffer.

SELECT ext_memcheck.run_scenario(scenario_name := 'growth_benchmark', iterations := 200, workload := 'SELECT 1');
SELECT ext_memcheck.run_scenario(scenario_name := 'tx_abort_loop', iterations := 50, workload := 'SELECT 1');

Parameters:

ParameterTypeDefaultDescription
scenario_nametextScenario name. Six scenarios are available: growth_benchmark, tx_abort_loop, shmem_sentinel_probe, wrong_context_probe (in-process), plus use_after_reset and oom_simulation (BGWorker crash-isolated). See Stress Scenarios.
iterationsint100Number of iterations to execute within the scenario.
workloadtext'SELECT 1'SQL query to execute within each iteration. Supply a custom query to simulate your extension's workload.

Returns: text — confirmation message.


Utility function to clear all entries from the ext_memcheck.violation_log table. Does not affect the in-memory ring buffer.

SELECT ext_memcheck.clear_violations();

Returns the current contents of the DSM segment tracking table — every segment that has been registered via ext_memcheck.track_dsm_handle() since the last ext_memcheck.clear_dsm_tracking() call.

SELECT * FROM ext_memcheck.dsm_tracking();

Returns: TABLE(segid BIGINT, backend_pid INTEGER, attach_at TIMESTAMPTZ, size_bytes BIGINT, detached BOOLEAN)

ColumnDescription
segidDSM handle (OS-level segment identifier)
backend_pidPID of the backend that recorded the attachment
attach_atTimestamp when the segment was tracked
size_bytesSegment size in bytes
detachedtrue if a matching detach was observed, false if leaked

Registers a DSM handle with the DSM lifecycle tracker so that pg_ext_memcheck can detect whether it is properly detached.

SELECT ext_memcheck.track_dsm_handle(1234);

Parameter: handle BIGINT — the DSM handle to track.

Returns: text — confirmation message.


Resets the DSM tracking table by zeroing all recorded segment entries. Call this between test runs to start with a clean slate.

SELECT ext_memcheck.clear_dsm_tracking();

Returns: void


Registers a shared memory segment for sentinel probing. Use this to probe your own extension's ShmemInitStruct allocation — pg_ext_memcheck will plant a 0xDE sentinel byte immediately after your declared data and verify it is intact after each workload run.

SELECT ext_memcheck.register_shmem_probe('my_ext SomeStruct', 1234);

Parameters:

ParameterTypeDescription
seg_nametextName the segment is registered under in ShmemIndex — must match the first argument to ShmemInitStruct exactly.
allocated_sizebigintExact size in bytes used when the segment was allocated. The sentinel is planted at base_ptr[allocated_size], which must fall within the alignment slack reserved by CACHELINEALIGN(allocated_size).

Returns: text — confirmation message.

Notes:

  • The segment must already exist in ShmemIndex when this function is called. If it does not, a WARNING is issued and no probe is registered.
  • Call this once per test session, before running ext_memcheck.run_scenario('shmem_sentinel_probe', ...).
  • pg_ext_memcheck's own segments (ViolationLog, DsmTrackerState) are registered automatically by the scenario and do not need to be registered manually.
-- Register your extension's shmem segment, then run the probe scenario
SELECT ext_memcheck.register_shmem_probe('my_ext SomeStruct', 1234);
SELECT ext_memcheck.run_scenario('shmem_sentinel_probe', 10, 'SELECT my_ext.some_function()');
SELECT ext_memcheck.flush_violations();
SELECT * FROM ext_memcheck.violation_log WHERE check_type = 'shmem_overrun';
-- Clear the registry between test runs
SELECT ext_memcheck.clear_shmem_registry();

Checks whether the 0xDE sentinel byte planted by register_shmem_probe() is still intact after the workload ran. Returns true if the sentinel is unmodified, false if it was overwritten (indicating a boundary overrun).

SELECT ext_memcheck.probe_check('my_ext SomeStruct');

Parameter: seg_name TEXT — must match exactly the name used in the prior register_shmem_probe() call.

Returns: booleantrue if sentinel intact, false if overwritten.

Note: You do not need to call this manually when using run_scenario('shmem_sentinel_probe', …) — the scenario checks all registered probes automatically and writes shmem_overrun violations to the ring buffer. probe_check() is provided for manual, granular inspection outside of a scenario run.


Resets the shmem sentinel probe registry by clearing all registered segment records. Existing sentinel bytes in shared memory are left in place; subsequent run_scenario('shmem_sentinel_probe', ...) calls will re-register and rewrite them.

SELECT ext_memcheck.clear_shmem_registry();

Returns: void


Set these in postgresql.conf or with SET at session scope (PGC_USERSET — no restart required).

ParameterTypeDefaultDescription
pg_ext_memcheck.memcheck_modeenumnoneControls which parts of query execution are instrumented. Set to none until begin() activates a window. See below.
pg_ext_memcheck.min_leak_bytesint8192Minimum context growth in bytes required to log a violation. Smaller growth is silently ignored.
pg_ext_memcheck.bloat_min_bytesint8192Minimum cumulative growth in bytes for a context to be reported as bloating by growth_benchmark. Contexts that grow by less than this amount across checkpoints are silently ignored.

Minimum cumulative memory growth (in bytes) that a MemoryContext must show across growth_benchmark checkpoints before a ctx_bloat violation is emitted. Contexts whose net growth falls below this threshold are silently ignored.

-- Raise the threshold to 64 KiB to reduce noise
SET pg_ext_memcheck.bloat_min_bytes = 65536;
-- Lower to 0 to report every context that grew at all
SET pg_ext_memcheck.bloat_min_bytes = 0;

Default: 8192 (8 KiB)
Range: 0INT_MAX
Scope: PGC_USERSET — effective immediately, no restart required.


The primary control knob for the extension. Determines which execution phases are hooked and checked.

-- Disable all instrumentation (zero overhead)
SET pg_ext_memcheck.memcheck_mode = 'none';
-- Check only executor-phase allocations (default for focused testing)
SET pg_ext_memcheck.memcheck_mode = 'executor';
-- Check all phases: planner + executor
SET pg_ext_memcheck.memcheck_mode = 'all';
ValueHooks activeWhen to use
noneNoneDisable the extension without unloading it; zero runtime overhead.
executorExecutorStart, ExecutorEndFocused testing of executor-phase allocations.
allPlanner planner_hook + Executor ExecutorEndFull sweep — catches leaks that happen during planning and execution.

Recommended workflow:

  1. Start with executor to isolate your function under test.
  2. Promote to all before marking the extension as clean — planner hooks and utility handlers are common sources of leaks that executor-only misses.
  3. Set to none in shared CI environments where another test suite is running, to avoid cross-contamination of violation logs.

TypePhaseStatusDescription
context_leak1AvailableA MemoryContext created during query execution was not freed by ExecutorEnd.
wrong_ctx_alloc1AvailableA palloc() landed in a long-lived context (TopMemoryContext, CacheMemoryContext, or a user-configured forbidden context).
shmem_overrun1AvailableSentinel bytes after a ShmemAlloc() allocation were overwritten.
dsm_leak1AvailableA DSM segment was attached during a test window but not detached before the window closed.
ctx_bloat1AvailableA MemoryContext grew monotonically across two or more log-spaced checkpoints in a growth_benchmark run. Severity reflects total growth; superlinear acceleration bumps severity one level.
use_after_reset2AvailableBGWorker crash confirmed via non-zero exit code after elog(FATAL) in an isolated worker process.