{ "version": 3, "sources": ["../../lib/postgres.ts"], "sourcesContent": ["/**\n * Library made to simplify accessing / connecting to postgres databases,\n * and to cleanly handle when the pg module isn't installed.\n * @author mia-pi-git\n */\n\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore in case module doesn't exist\nimport type * as PG from 'pg';\nimport type { SQLStatement } from 'sql-template-strings';\nimport * as Streams from './streams';\nimport { FS } from './fs';\nimport * as Utils from './utils';\n\ninterface MigrationOptions {\n\ttable: string;\n\tmigrationsFolder: string;\n\tbaseSchemaFile: string;\n}\n\nexport class PostgresDatabase {\n\tprivate pool: PG.Pool;\n\tconstructor(config = PostgresDatabase.getConfig()) {\n\t\ttry {\n\t\t\tthis.pool = new (require('pg').Pool)(config);\n\t\t} catch {\n\t\t\tthis.pool = null!;\n\t\t}\n\t}\n\tdestroy() {\n\t\treturn this.pool.end();\n\t}\n\tasync query(statement: string | SQLStatement, values?: any[]) {\n\t\tif (!this.pool) {\n\t\t\tthrow new Error(`Attempting to use postgres without 'pg' installed`);\n\t\t}\n\t\tlet result;\n\t\ttry {\n\t\t\tresult = await this.pool.query(statement, values);\n\t\t} catch (e: any) {\n\t\t\t// postgres won't give accurate stacks unless we do this\n\t\t\tthrow new Error(e.message);\n\t\t}\n\t\treturn result?.rows || [];\n\t}\n\tstatic getConfig() {\n\t\tlet config: AnyObject = {};\n\t\ttry {\n\t\t\tconfig = require(FS.ROOT_PATH + '/config/config').usepostgres;\n\t\t\tif (!config) throw new Error('Missing config for pg database');\n\t\t} catch {}\n\t\treturn config;\n\t}\n\tasync transaction(callback: (conn: PG.PoolClient) => any, depth = 0): Promise {\n\t\tconst conn = await this.pool.connect();\n\t\tawait conn.query(`BEGIN`);\n\t\tlet result;\n\t\ttry {\n\t\t\tresult = await callback(conn);\n\t\t} catch (e: any) {\n\t\t\tawait conn.query(`ROLLBACK`);\n\t\t\t// two concurrent transactions conflicted, try again\n\t\t\tif (e.code === '40001' && depth <= 10) {\n\t\t\t\treturn this.transaction(callback, depth + 1);\n\t\t\t\t// There is a bug in Postgres that causes some\n\t\t\t\t// serialization failures to be reported as failed\n\t\t\t\t// unique constraint checks. Only retrying once since\n\t\t\t\t// it could be our fault (thanks chaos for this info / the first half of this comment)\n\t\t\t} else if (e.code === '23505' && !depth) {\n\t\t\t\treturn this.transaction(callback, depth + 1);\n\t\t\t} else {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t\tawait conn.query(`COMMIT`);\n\t\treturn result;\n\t}\n\tstream(query: string) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-this-alias\n\t\tconst db = this;\n\t\treturn new Streams.ObjectReadStream({\n\t\t\tasync read(this: Streams.ObjectReadStream) {\n\t\t\t\tconst result = await db.query(query) as T[];\n\t\t\t\tif (!result.length) return this.pushEnd();\n\t\t\t\t// getting one row at a time means some slower queries\n\t\t\t\t// might help with performance\n\t\t\t\tthis.buf.push(...result);\n\t\t\t},\n\t\t});\n\t}\n\tasync ensureMigrated(opts: MigrationOptions) {\n\t\tlet value;\n\t\ttry {\n\t\t\tconst stored = await this.query(\n\t\t\t\t`SELECT value FROM db_info WHERE key = 'version' AND name = $1`, [opts.table]\n\t\t\t);\n\t\t\tif (stored.length) {\n\t\t\t\tvalue = stored[0].value || \"0\";\n\t\t\t}\n\t\t} catch {\n\t\t\tawait this.query(`CREATE TABLE db_info (name TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL)`);\n\t\t}\n\t\tif (!value) { // means nothing inserted - create row\n\t\t\tvalue = \"0\";\n\t\t\tawait this.query('INSERT INTO db_info (name, key, value) VALUES ($1, $2, $3)', [opts.table, 'version', value]);\n\t\t}\n\t\tvalue = Number(value);\n\t\tconst files = FS(opts.migrationsFolder)\n\t\t\t.readdirSync()\n\t\t\t.filter(f => f.endsWith('.sql'))\n\t\t\t.map(f => Number(f.slice(1).split('.')[0]));\n\t\tUtils.sortBy(files, f => f);\n\t\tconst curVer = files[files.length - 1] || 0;\n\t\tif (curVer !== value) {\n\t\t\tif (!value) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.query(`SELECT * FROM ${opts.table} LIMIT 1`);\n\t\t\t\t} catch {\n\t\t\t\t\tawait this.query(FS(opts.baseSchemaFile).readSync());\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const n of files) {\n\t\t\t\tif (n <= value) continue;\n\t\t\t\tawait this.query(FS(`${opts.migrationsFolder}/v${n}.sql`).readSync());\n\t\t\t\tawait this.query(\n\t\t\t\t\t`UPDATE db_info SET value = $1 WHERE key = 'version' AND name = $2`, [`${n}`, opts.table]\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n"], "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,cAAyB;AACzB,gBAAmB;AACnB,YAAuB;AAQhB,MAAM,iBAAiB;AAAA,EAE7B,YAAY,SAAS,iBAAiB,UAAU,GAAG;AAClD,QAAI;AACH,WAAK,OAAO,IAAK,SAAQ,IAAI,GAAE,KAAM,MAAM;AAAA,IAC5C,QAAE;AACD,WAAK,OAAO;AAAA,IACb;AAAA,EACD;AAAA,EACA,UAAU;AACT,WAAO,KAAK,KAAK,IAAI;AAAA,EACtB;AAAA,EACA,MAAM,MAAM,WAAkC,QAAgB;AAC7D,QAAI,CAAC,KAAK,MAAM;AACf,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACpE;AACA,QAAI;AACJ,QAAI;AACH,eAAS,MAAM,KAAK,KAAK,MAAM,WAAW,MAAM;AAAA,IACjD,SAAS,GAAP;AAED,YAAM,IAAI,MAAM,EAAE,OAAO;AAAA,IAC1B;AACA,WAAO,QAAQ,QAAQ,CAAC;AAAA,EACzB;AAAA,EACA,OAAO,YAAY;AAClB,QAAI,SAAoB,CAAC;AACzB,QAAI;AACH,eAAS,QAAQ,aAAG,YAAY,gBAAgB,EAAE;AAClD,UAAI,CAAC;AAAQ,cAAM,IAAI,MAAM,gCAAgC;AAAA,IAC9D,QAAE;AAAA,IAAO;AACT,WAAO;AAAA,EACR;AAAA,EACA,MAAM,YAAY,UAAwC,QAAQ,GAAiB;AAClF,UAAM,OAAO,MAAM,KAAK,KAAK,QAAQ;AACrC,UAAM,KAAK,MAAM,OAAO;AACxB,QAAI;AACJ,QAAI;AACH,eAAS,MAAM,SAAS,IAAI;AAAA,IAC7B,SAAS,GAAP;AACD,YAAM,KAAK,MAAM,UAAU;AAE3B,UAAI,EAAE,SAAS,WAAW,SAAS,IAAI;AACtC,eAAO,KAAK,YAAY,UAAU,QAAQ,CAAC;AAAA,MAK5C,WAAW,EAAE,SAAS,WAAW,CAAC,OAAO;AACxC,eAAO,KAAK,YAAY,UAAU,QAAQ,CAAC;AAAA,MAC5C,OAAO;AACN,cAAM;AAAA,MACP;AAAA,IACD;AACA,UAAM,KAAK,MAAM,QAAQ;AACzB,WAAO;AAAA,EACR;AAAA,EACA,OAAgB,OAAe;AAE9B,UAAM,KAAK;AACX,WAAO,IAAI,QAAQ,iBAAoB;AAAA,MACtC,MAAM,OAAwC;AAC7C,cAAM,SAAS,MAAM,GAAG,MAAM,KAAK;AACnC,YAAI,CAAC,OAAO;AAAQ,iBAAO,KAAK,QAAQ;AAGxC,aAAK,IAAI,KAAK,GAAG,MAAM;AAAA,MACxB;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EACA,MAAM,eAAe,MAAwB;AAC5C,QAAI;AACJ,QAAI;AACH,YAAM,SAAS,MAAM,KAAK;AAAA,QACzB;AAAA,QAAiE,CAAC,KAAK,KAAK;AAAA,MAC7E;AACA,UAAI,OAAO,QAAQ;AAClB,gBAAQ,OAAO,CAAC,EAAE,SAAS;AAAA,MAC5B;AAAA,IACD,QAAE;AACD,YAAM,KAAK,MAAM,mFAAmF;AAAA,IACrG;AACA,QAAI,CAAC,OAAO;AACX,cAAQ;AACR,YAAM,KAAK,MAAM,8DAA8D,CAAC,KAAK,OAAO,WAAW,KAAK,CAAC;AAAA,IAC9G;AACA,YAAQ,OAAO,KAAK;AACpB,UAAM,YAAQ,cAAG,KAAK,gBAAgB,EACpC,YAAY,EACZ,OAAO,OAAK,EAAE,SAAS,MAAM,CAAC,EAC9B,IAAI,OAAK,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;AAC3C,UAAM,OAAO,OAAO,OAAK,CAAC;AAC1B,UAAM,SAAS,MAAM,MAAM,SAAS,CAAC,KAAK;AAC1C,QAAI,WAAW,OAAO;AACrB,UAAI,CAAC,OAAO;AACX,YAAI;AACH,gBAAM,KAAK,MAAM,iBAAiB,KAAK,eAAe;AAAA,QACvD,QAAE;AACD,gBAAM,KAAK,UAAM,cAAG,KAAK,cAAc,EAAE,SAAS,CAAC;AAAA,QACpD;AAAA,MACD;AACA,iBAAW,KAAK,OAAO;AACtB,YAAI,KAAK;AAAO;AAChB,cAAM,KAAK,UAAM,cAAG,GAAG,KAAK,qBAAqB,OAAO,EAAE,SAAS,CAAC;AACpE,cAAM,KAAK;AAAA,UACV;AAAA,UAAqE,CAAC,GAAG,KAAK,KAAK,KAAK;AAAA,QACzF;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD;", "names": [] }