import {QueryResult, QueryResultRow} from "pg" import {UsersTable, UsersTableImpl} from "./users.js" import {CharacterCreationTable, CharacterCreationTableImpl} from "./character_creation.js" export interface PoolLike { connect(): Promise } export interface PoolClientLike extends Queryable { release(err?: boolean): void } export function makeTransactable(p: PoolLike): Transactable { return async function transactable(callback: (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") } catch (err) { client.release() throw err } try { await callback(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 characterCreation: CharacterCreationTable } export interface Queryable { query(queryText: string): Promise> query(queryText: string, params: ParamT): Promise> } export type QueryType = Queryable["query"] export type Transactable = (transaction: (client: QueryType, attempts: number) => Promise) => Promise export class DatabaseImpl implements Database { readonly users: UsersTableImpl readonly characterCreation: CharacterCreationTableImpl constructor({query, transactable}: { query: QueryType, transactable: Transactable }) { this.users = new UsersTableImpl({query}) this.characterCreation = new CharacterCreationTableImpl({query, transactable}) } }