Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 20 additions & 19 deletions docs/providers/postgres.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ rather than reimplementing them:

| Member | Purpose |
|--------|---------|
| `escapeIdentifier()` ([sql-base.ts:33](../../src/lib/db/providers/sql/sql-base.ts)) | Dialect-aware quoting — `"ident"` for Postgres, `` `ident` `` for MySQL, `[ident]` for MSSQL; doubles embedded quote chars |
| `escapeIdentifier()` ([`sql-base.ts`](../../src/lib/db/providers/sql/sql-base.ts)) | Dialect-aware quoting — `"ident"` for Postgres, `` `ident` `` for MySQL, `[ident]` for MSSQL; doubles embedded quote chars |
| `positionalPlaceholder()` ([values.ts](../../src/lib/sql/values.ts), shared rather than inherited) | `$1`-style placeholders for Postgres (`?` for MySQL/SQLite/Druid, `:n` Oracle, `@pn` MSSQL, `$n` Couchbase) |
| `shouldEnableSSL()` ([sql-base.ts:75](../../src/lib/db/providers/sql/sql-base.ts)) | Auto-enables SSL for known cloud hosts (supabase, neon, render, planetscale, aws, azure, gcp, …) |
| `getDefaultSchema()` ([sql-base.ts:91](../../src/lib/db/providers/sql/sql-base.ts)) | `public` for Postgres |
| `prepareQuery()` ([sql-base.ts:137](../../src/lib/db/providers/sql/sql-base.ts)) | Injects `LIMIT` into bare `SELECT`s — see [§5.2](#52-automatic-limit-injection) |
| `shouldEnableSSL()` ([`sql-base.ts`](../../src/lib/db/providers/sql/sql-base.ts)) | Auto-enables SSL for known cloud hosts (supabase, neon, render, planetscale, aws, azure, gcp, …) |
| `getDefaultSchema()` ([`sql-base.ts`](../../src/lib/db/providers/sql/sql-base.ts)) | `public` for Postgres |
| `prepareQuery()` ([`sql-base.ts`](../../src/lib/db/providers/sql/sql-base.ts)) | Injects `LIMIT` into bare `SELECT`s — see [§5.2](#52-automatic-limit-injection) |

### 2.3 Registration & lifecycle

Expand Down Expand Up @@ -111,7 +111,8 @@ These are the non-obvious choices. Read this section before changing the provide
### 3.1 `MATERIALIZED` CTEs for schema introspection

This is the single most important detail in the file. All schema-introspection CTEs are declared
`AS MATERIALIZED` ([postgres.ts:86–177](../../src/lib/db/providers/sql/postgres.ts)). PostgreSQL 12+
`AS MATERIALIZED` (the `CTE_*_INFO` consts in
[`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)). PostgreSQL 12+
*inlines* single-reference CTEs by default, which lets the planner re-execute these
`information_schema`-based CTEs inside nested-loop joins (it estimates `rows=1` for them). On a
large schema (100+ tables/constraints/indexes) that explodes into minutes of planning/execution.
Expand Down Expand Up @@ -307,7 +308,7 @@ default.
### 3.2 Schema SQL hoisted to module scope

`SCHEMA_FULL_SQL`, `SCHEMA_LIST_SQL`, and `SCHEMA_RELATIONS_SQL` are module-level `const`s, not
inline template literals inside the methods ([postgres.ts:86–226](../../src/lib/db/providers/sql/postgres.ts)).
inline template literals inside the methods ([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)).
This is a **coverage** workaround: `bun`'s coverage instruments the interior lines of a multi-line
template literal *in a function body* as 0-hit in any test process that imports the file but does
not exercise that method, and the merged lcov then reports those SQL lines as uncovered. Evaluated
Expand All @@ -333,9 +334,9 @@ N+1 pattern of `1 + N*4` queries). The two-phase split is the path the UI actual

Tables in the `public` schema are shown by bare name; tables in any other schema are prefixed
(`reporting.invoices`). The same rule is applied to **foreign-key referenced tables**, so a FK that
points across schemas renders correctly. The FK introspection CTE joins
`constraint_column_usage` on **both** `constraint_name` and `constraint_schema`
([postgres.ts:148–150](../../src/lib/db/providers/sql/postgres.ts)) — joining on name alone
points across schemas renders correctly. The FK introspection CTE (`CTE_FK_INFO`,
[`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)) joins `constraint_column_usage` on
**both** `constraint_name` and `constraint_schema` — joining on name alone
mis-resolves same-named constraints in different schemas (this was a real bug; there is a
regression test for it).

Expand Down Expand Up @@ -376,7 +377,7 @@ Monitoring never hard-fails on a missing optional feature:

### 3.6 Safe maintenance targets

`qualifyMaintenanceTarget()` ([postgres.ts:751](../../src/lib/db/providers/sql/postgres.ts)) quotes
`qualifyMaintenanceTarget()` ([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)) quotes
maintenance targets through `escapeIdentifier()`: a bare name defaults to the `public` schema; a
`schema.table` target is quoted per-part. This prevents identifier injection in `VACUUM`/`ANALYZE`/
`REINDEX` statements (which cannot use bind parameters for object names).
Expand All @@ -387,7 +388,7 @@ maintenance targets through `escapeIdentifier()`: a bare name defaults to the `p

### 4.1 Configuration

Two forms are accepted (`validate()`, [postgres.ts:264](../../src/lib/db/providers/sql/postgres.ts)).
Two forms are accepted (`validate()`, [`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)).
`validate()` requires `host` **and** `database` only when no `connectionString` is given — it does
**not** reject supplying both. If both are present the **connection string wins**: `buildPoolConfig()`
uses it and ignores the discrete fields.
Expand Down Expand Up @@ -448,7 +449,7 @@ server. Paste the certificate content instead.

### 4.2 Connection pooling

`connect()` builds a `pg.Pool` ([postgres.ts:281](../../src/lib/db/providers/sql/postgres.ts)) and
`connect()` builds a `pg.Pool` ([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)) and
validates it by acquiring and releasing one client. Pool **sizing** comes from `ProviderOptions.pool`
merged over `DEFAULT_POOL_CONFIG`:

Expand Down Expand Up @@ -487,7 +488,7 @@ oracledb expose no pool-level `error` event at all, which is recorded at each pr

### 4.3 SSL

`buildSSLConfig()` ([postgres.ts:342](../../src/lib/db/providers/sql/postgres.ts)) resolves SSL with
`buildSSLConfig()` ([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)) resolves SSL with
this precedence:

1. **Explicit `connection.ssl`** (`SSLConfig`, mode = `disable` | `require` | `verify-system` |
Expand All @@ -511,7 +512,7 @@ this precedence:

### 5.1 Execution

`query(sql, params?, queryId?)` ([postgres.ts:378](../../src/lib/db/providers/sql/postgres.ts))
`query(sql, params?, queryId?)` ([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts))
acquires a pooled client, optionally records its backend PID for cancellation, runs the
(optionally parameterized — `$1`, `$2`, …) statement, and returns the standard envelope:

Expand All @@ -526,7 +527,7 @@ timeout → `TimeoutError`, etc.).
### 5.2 Automatic `LIMIT` injection

`prepareQuery()` (inherited from `SQLBaseProvider`) protects the UI from runaway result sets. It
runs the query through `analyzeQuery()` ([query-limiter.ts:88](../../src/lib/db/utils/query-limiter.ts))
runs the query through `analyzeQuery()` ([`query-limiter.ts`](../../src/lib/db/utils/query-limiter.ts))
and, **only for `SELECT`/CTE-`SELECT` queries that don't already have a `LIMIT`**, appends one via
`applyQueryLimit()`:

Expand Down Expand Up @@ -617,7 +618,7 @@ comment-led final `SELECT`; it now reads `isSelectQuery()` from the same classif
### 5.3 Query cancellation

A query issued with a `queryId` records its backend PID in a `Map`. `cancelQuery(queryId)`
([postgres.ts:412](../../src/lib/db/providers/sql/postgres.ts)) looks the PID up and calls
([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)) looks the PID up and calls
`pg_cancel_backend(pid)` on a fresh pooled client, returning whether the cancel signalled. Exposed
via `POST /api/db/cancel`.

Expand Down Expand Up @@ -756,7 +757,7 @@ the client is not returned to the pool until commit/rollback. Surfaced via `POST

| Method | Behaviour |
|--------|-----------|
| `beginTransaction()` | Acquires a client, runs `BEGIN`, arms a **5-minute auto-rollback** timer ([postgres.ts:461](../../src/lib/db/providers/sql/postgres.ts), duration set by `TX_TIMEOUT_MS`). Throws if one is already active. |
| `beginTransaction()` | Acquires a client, runs `BEGIN`, arms a **5-minute auto-rollback** timer ([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts), duration set by `TX_TIMEOUT_MS`). Throws if one is already active. |
| `queryInTransaction(sql, params?)` | Runs on the transaction's client. Throws if none active. |
| `commitTransaction()` / `rollbackTransaction()` | Ends the transaction, clears the timer, releases the client. Throws if none active. |
| `expireTransaction()` | The timeout callback — auto-`ROLLBACK` to prevent leaked locks if a transaction is abandoned. |
Expand All @@ -775,7 +776,7 @@ connection — including the ten providers that answer HTTP 400.

## 9. Maintenance

`runMaintenance(type, target?)` ([postgres.ts:762](../../src/lib/db/providers/sql/postgres.ts)),
`runMaintenance(type, target?)` ([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts)),
with targets quoted via [§3.6](#36-safe-maintenance-targets):

| Type | With target | Without target |
Expand Down Expand Up @@ -819,7 +820,7 @@ really means `vacuum` here, so `vacuumActionOperation` stays absent.

## 10. Capabilities & labels

### `getCapabilities()` ([postgres.ts:250](../../src/lib/db/providers/sql/postgres.ts))
### `getCapabilities()` ([`postgres.ts`](../../src/lib/db/providers/sql/postgres.ts))

Overrides the SQL base defaults:

Expand Down
15 changes: 15 additions & 0 deletions tests/unit/provider-docs-monitoring-citations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,21 @@ const NAMED_CITATIONS = [
"getActiveSessions",
],
},
{
doc: "docs/providers/postgres.md",
source: "src/lib/db/providers/sql/postgres.ts",
methods: [
"getCapabilities",
"qualifyMaintenanceTarget",
"validate",
"connect",
"buildSSLConfig",
"query",
"cancelQuery",
"beginTransaction",
"runMaintenance",
],
},
] as const;

const SEARCH_DOCS = ["docs/providers/elasticsearch.md", "docs/providers/opensearch.md"] as const;
Expand Down
Loading