Skip to content

pg_stat_statements always reports "installed but could not be queried" (SAVEPOINT fails in autocommit=True connection) #259

@pablocastilla

Description

@pablocastilla

Summary

In v1.22.0 (pgsqltoolsservice v3.2.0), the pg_stat_statements capability probe
always fails for all databases and all PostgreSQL versions. The extension shows
the warning "pg_stat_statements is installed but could not be queried" regardless
of server configuration.

Steps to Reproduce

  1. Install extension v1.22.0 (ms-ossdata.vscode-pgsql@1.22.0)
  2. Connect to any PostgreSQL server with pg_stat_statements loaded
  3. Open the Performance Dashboard
  4. Observe: "pg_stat_statements is installed but could not be queried" banner always shown

Root Cause

The probe in ossdbtoolsservice.metrics.server_configuration executes:

await cur.execute("SAVEPOINT pgss_probe")
await cur.execute("SELECT 1 FROM pg_stat_statements LIMIT 1")
await cur.execute("RELEASE SAVEPOINT pgss_probe")

However, the pooled connections used by the metrics service have autocommit=True (evidenced by 
Query._restore_autocommit_state which "Restores connection to autocommit mode"). PostgreSQL rejects SAVEPOINT
outside a transaction block:

psycopg.errors.NoActiveSqlTransaction: SAVEPOINT can only be used in transaction blocks

This exception is caught and sets queryable=False, reason="query_failed"making pg_stat_statements appear broken
on every database, regardless of server configuration or PostgreSQL version.

Confirmed with: PostgreSQL 18 (TimescaleDB), PG18 + pg_stat_statements 1.11. Affects all databases, including
vanilla ones with no custom objects.

Evidence

Direct reproduction with psycopg3:

import psycopg
from psycopg_pool import ConnectionPool

pool = ConnectionPool(conninfo, min_size=1, kwargs={"autocommit": True})
with pool.connection() as conn:
    cur = conn.cursor()
    cur.execute("SAVEPOINT pgss_probe")
    # → psycopg.errors.NoActiveSqlTransaction: SAVEPOINT can only be used in transaction blocks

With autocommit=False the probe succeeds correctly.

Suggested Fix

Replace SAVEPOINT/RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT with BEGIN/COMMIT/ROLLBACK in the probe. BEGIN works
correctly in autocommit=True mode and achieves the same isolation goal:

try:
    await cur.execute("BEGIN")
    await cur.execute("SELECT 1 FROM pg_stat_statements LIMIT 1")
    await cur.execute("COMMIT")
    queryable = True
except Exception as e:
    await conn.execute("ROLLBACK")
    queryable = False
    reason = "query_failed"

Alternatively, ensure the probe runs on a connection with autocommit=False.

Workaround (Binary Patch)

For users who need an immediate fix, the 3 SQL string constants can be replaced in-place in 
ossdbtoolsservice_main.exe (same byte length, PostgreSQL ignores trailing whitespace):

┌───────────────────────────────────────────────┬───────────────────────────────────────────────┐
│ OriginalReplacement                                   │
├───────────────────────────────────────────────┼───────────────────────────────────────────────┤
│ SAVEPOINT pgss_probe (20 bytes)               │ BEGIN                (20 bytes)               │
├───────────────────────────────────────────────┼───────────────────────────────────────────────┤
│ RELEASE SAVEPOINT pgss_probe (28 bytes)       │ COMMIT                       (28 bytes)       │
├───────────────────────────────────────────────┼───────────────────────────────────────────────┤
│ ROLLBACK TO SAVEPOINT pgss_probe (32 bytes)   │ ROLLBACK                         (32 bytes)   │
└───────────────────────────────────────────────┴───────────────────────────────────────────────┘

The strings are in the ossdbtoolsservice.metrics.server_configuration module, compressed at PYZ offset 13105066
within the embedded PYZ archive.

Environment

- Extension: ms-ossdata.vscode-pgsql v1.22.0
- Tools service: pgsqltoolsservice v3.2.0
- OS: Windows 11 x64
- PostgreSQL: 18 (TimescaleDB)
- VS Code: latest

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions