import {QueryResult, QueryResultRow} from "pg" import {UsersTable, UsersTableImpl} from "./users.js" import {BattleTypeTables, BattleTypeTablesImpl} from "./battle_types"; import {GenderTables, GenderTablesImpl} from "./gender"; export interface PoolLike { connect(): Promise } export interface PoolClientLike extends Queryable { release(err?: boolean): void } export function makeTransact(p: PoolLike): TransactType { return async function transact({readonly=false, transaction}: {readonly?: boolean, transaction: (q: QueryType, attempts: number) => Promise}): Promise { const client = await p.connect() const query = client.query.bind(client) let committed = false let attempts = 0 while (!committed) { try { await client.query("BEGIN READ " + (readonly ? "ONLY" : "WRITE")) } catch (err) { client.release() throw err } try { await transaction(query, attempts) } catch (err) { try { await client.query("ROLLBACK") client.release() } catch (err) { client.release(true) } throw err } try { await client.query("COMMIT") committed = true } catch (err) { attempts += 1 } } client.release() } } export interface Database { readonly users: UsersTable readonly gender: GenderTables readonly battleTypes: BattleTypeTables } export interface Queryable { query(queryText: string): Promise> query(queryText: string, params: ParamT): Promise> } export type QueryType = Queryable["query"] export type TransactType = (config: {readonly?: boolean, transaction: (client: QueryType, attempts: number) => Promise}) => Promise export class DatabaseImpl implements Database { readonly users: UsersTableImpl readonly gender: GenderTables readonly battleTypes: BattleTypeTablesImpl constructor({query, transact}: { query: QueryType, transact: TransactType }) { this.users = new UsersTableImpl({query}) this.gender = new GenderTablesImpl({transact}) this.battleTypes = new BattleTypeTablesImpl({transact}) } }