Add an SQL DB abstraction layer

Because what self-respecting Enterprise(R) project doesn't have
one of those?
This commit is contained in:
Tau
2019-10-10 16:57:56 -04:00
parent 2ccd9dc382
commit 3e6b4f4798
32 changed files with 440 additions and 541 deletions

19
src/sql/api.ts Normal file
View File

@@ -0,0 +1,19 @@
import * as sql from "sql-bricks-postgres";
export type Id<T> = bigint & { __id: T };
export interface Row {
[key: string]: any;
}
export interface Transaction {
modify(stmt: sql.Statement): Promise<void>;
fetchRow(stmt: sql.SelectStatement): Promise<Row | undefined>;
fetchRows(stmt: sql.SelectStatement): Promise<Row[]>;
}
export interface DataSource {
transaction<T>(callback: (txn: Transaction) => Promise<T>): Promise<T>;
}

3
src/sql/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from "./api";
export * from "./pg";
export * from "./util";

57
src/sql/pg.ts Normal file
View File

@@ -0,0 +1,57 @@
import { Pool, PoolClient } from "pg";
import * as sql from "sql-bricks-postgres";
import { DataSource, Row, Transaction } from "./api";
class PgTransaction implements Transaction {
constructor(private readonly _conn: PoolClient) {}
async modify(stmt: sql.Statement): Promise<void> {
await this._conn.query(stmt.toParams());
}
async fetchRow(stmt: sql.SelectStatement): Promise<Row | undefined> {
const { rows } = await this._conn.query(stmt.toParams());
return rows[0];
}
async fetchRows(stmt: sql.SelectStatement): Promise<Row[]> {
const { rows } = await this._conn.query(stmt.toParams());
return rows;
}
}
class PgDataSource implements DataSource {
private readonly _pool: Pool;
constructor() {
this._pool = new Pool();
}
async transaction<T>(
callback: (txn: Transaction) => Promise<T>
): Promise<T> {
const conn = await this._pool.connect();
await conn.query("begin");
try {
const txn = new PgTransaction(conn);
const result = await callback(txn);
await conn.query("commit");
return result;
} catch (e) {
await conn.query("rollback");
return Promise.reject(e);
}
}
}
export function openDataSource(): DataSource {
return new PgDataSource();
}

14
src/sql/util.ts Normal file
View File

@@ -0,0 +1,14 @@
import { randomBytes } from "crypto";
export function generateId(): bigint {
const buf = randomBytes(8);
buf[0] &= 0x7f; // Force number to be non-negative
// Let's not depend on Node v12 for the sake of 3 LoC just yet.
const hi = buf.readUInt32BE(0);
const lo = buf.readUInt32BE(4);
return (BigInt(hi) << 32n) | BigInt(lo);
}