# All files / src/drivers pg.ts

100% Statements 22/22
100% Branches 10/10
100% Functions 4/4
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92

    1x
    14x
    14x
    117x
    116x
    116x

    117x

    117x
    117x
    117x

    14x

    12x
    12x

    14x

    11x
    11x
    9x
    8x
    7x

    11x
    14x
    14x
     /**
     * `pg` (node-postgres) adapter for `pgrls-test`.
     *
     * Usage:
     *
     * ```ts
     * import { Client } from 'pg';
     * import { PgrlsTestClient, pgDriver } from 'pgrls-test';
     *
     * const pg = new Client({ connectionString: process.env.DATABASE_URL });
     * await pg.connect();
     * const client = new PgrlsTestClient(pgDriver(pg));
     * ```
     *
     * The adapter is a thin wrapper that:
     * 1. Maps `query(sql, params)` → `client.query(sql, values)` and
     *    normalizes the result shape to `QueryResult`.
     * 2. Maps `rollback()` → `client.query('ROLLBACK')` (works even
     *    in aborted-transaction state — Postgres always accepts
     *    `ROLLBACK`).
     * 3. Maps `isInsufficientPrivilege(err)` → `err.code === '42501'`.
     *
     * `pg` is a peer dependency. Users who don't use it never pull
     * it in; users who do use it get type-checked construction.
     */
    import type { Driver, QueryResult } from './types.js';

    /**
     * Subset of `pg.Client` (or a `pg.Pool` wrapper) that the
     * adapter needs. We deliberately don't `import { Client } from
     * 'pg'` here — that would force `pg` to be a runtime dep. The
     * structural interface is enough for the adapter and lets users
     * pass a Pool client, a connection-checked-out client, or any
     * compatible shape.
     */
    export interface PgQueryable {
      query(
        text: string,
        values?: readonly unknown[],
      ): Promise<{
        rows: Record<string, unknown>[];
        command: string;
        rowCount: number | null;
      }>;
    }

    /**
     * Wrap a `pg.Client` (or any object satisfying `PgQueryable`)
     * into the `Driver` shape the rest of `pgrls-test` consumes.
     */
    export function pgDriver(client: PgQueryable): Driver {
      return {
        async query(sql: string, params?: readonly unknown[]): Promise<QueryResult> {
          const result = await client.query(sql, params);
          return {
            rows: result.rows,
            // pg returns the command verb as the first whitespace-
            // delimited token in the command tag (e.g. "UPDATE 5").
            // It already strips the count, so result.command is
            // exactly the verb like "UPDATE", "SELECT", "INSERT".
            // Upper-case it defensively in case some pg fork or
            // future version changes the casing.
            command: (result.command || '').toUpperCase(),
            // pg returns `rowCount: null` for commands that don't
            // produce a count (rare — utility statements). Map to
            // 0 so callers don't have to null-check.
            rowCount: result.rowCount ?? 0,
          };
        },

        async rollback(): Promise<void> {
          // ROLLBACK is accepted in any transaction state — including
          // the "current transaction is aborted" state that follows
          // a SQL error. Issuing it via the same query path keeps the
          // adapter to one entry point.
          await client.query('ROLLBACK');
        },

        isInsufficientPrivilege(error: unknown): boolean {
          // pg attaches the SQLSTATE as a string `code` property on
          // the thrown error. 42501 is "insufficient_privilege" —
          // the class RLS policy rejections surface as.
          return (
            typeof error === 'object' &&
            error !== null &&
            'code' in error &&
            (error as { code?: unknown }).code === '42501'
          );
        },
      };
    }