import {QueryResult, QueryResultRow} from "pg" import {UserTable, UserTableImpl} from "./users.js" import {BattleTypeTable, BattleTypeTableImpl} from "./battle_types"; import {GenderTable, GenderTablesImpl} from "./genders"; import {CharacterTable, CharacterTableImpl} from "./characters"; 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 characters: CharacterTable readonly users: UserTable readonly genders: GenderTable readonly battleTypes: BattleTypeTable } 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 characters: CharacterTableImpl readonly users: UserTableImpl readonly genders: GenderTable readonly battleTypes: BattleTypeTableImpl constructor({query, transact}: { query: QueryType, transact: TransactType }) { this.characters = new CharacterTableImpl({transact}) this.users = new UserTableImpl({query}) this.genders = new GenderTablesImpl({transact}) this.battleTypes = new BattleTypeTableImpl({transact}) } }