Skip to content

Application-owned PostgreSQL adapters ​

Bun.SQL and postgres.js satisfy Postgres from @aotter/mantle/contracts directly. Other drivers need unsafe(text, values) returning a row array with .count, and begin(options, fn) pinning one connection until the callback resolves and COMMIT succeeds. Preserve native SQLSTATE/severity evidence; generated writes classify uncertain commits.

This node-postgres adapter accepts an app-owned pg.Pool through its structural shape:

ts
import type { Postgres } from '@aotter/mantle/contracts';

type Connection = {
  query(text: string, values?: any[]): Promise<{ rows: any[]; rowCount: number | null }>;
};
// Pass a node-postgres Pool; its connections and lifetime stay app-owned.
export function nodePostgres(pool: Connection & {
  connect(): Promise<Connection & { release(discard?: boolean): void }>;
}): Postgres {
  const unsafe = (connection: Connection): Postgres['unsafe'] => async (text, values) => {
    const result = await connection.query(text, values);
    return Object.assign(result.rows, { count: result.rowCount });
  };
  return {
    unsafe: unsafe(pool),
    async begin(options, fn) {
      if (!/^isolation level (serializable|repeatable read|read committed|read uncommitted)$/.test(options))
        throw new Error('Unsupported transaction options');
      const client = await pool.connect();
      let discard = false;
      try {
        await client.query(`BEGIN ${options}`);
        const tx: Postgres = {
          unsafe: unsafe(client),
          begin: async () => { throw new Error('Nested transactions are unsupported'); },
        };
        let value;
        try { value = await fn(tx); }
        catch (error) {
          try { await client.query('ROLLBACK'); } catch { discard = true; }
          throw error;
        }
        await client.query('COMMIT');
        return value;
      } catch (error) { discard = true; throw error; }
      finally { client.release(discard); }
    },
  };
}

The pool stays application-owned. There is no automatic retry. A COMMIT connection failure remains an error with its original evidence; the SDK can report OUTCOME_UNKNOWN. Adapters without interactive transactions must reject in begin before invoking the callback. They cannot run multi-statement Procedures or multi-write batches by independent autocommit calls.

Test on disposable storage with the adapter conformance corpus, covering bound date-time/date/JSON text, SELECT and RETURNING timestamp microseconds, row arrays/counts, safe and unsafe int8 precision (including int64 extrema), aliased selected-page integer/version number-or-string outputs, native numeric text and booleans, unique/OCC failures, commit/rollback, nested handles and uncertain outcomes. This is an application-owned adapter, not an additional supported driver. Mantle's supported PostgreSQL drivers are Bun.SQL and postgres.js.

The driver layer is open; dialects remain postgres and sqlite only.