mirror of
https://github.com/djhackersdev/minime.git
synced 2026-08-21 18:04:04 -05:00
Add an SQL DB abstraction layer
Because what self-respecting Enterprise(R) project doesn't have one of those?
This commit is contained in:
19
src/sql/api.ts
Normal file
19
src/sql/api.ts
Normal 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
3
src/sql/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./api";
|
||||
export * from "./pg";
|
||||
export * from "./util";
|
||||
57
src/sql/pg.ts
Normal file
57
src/sql/pg.ts
Normal 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
14
src/sql/util.ts
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user