{ "version": 3, "sources": ["../../../lib/postgres.ts"], "sourcesContent": ["/**\r\n * Library made to simplify accessing / connecting to postgres databases,\r\n * and to cleanly handle when the pg module isn't installed.\r\n * @author mia-pi-git\r\n */\r\n\r\n// @ts-ignore in case module doesn't exist\r\nimport type * as PG from 'pg';\r\nimport type {SQLStatement} from 'sql-template-strings';\r\nimport * as Streams from './streams';\r\nimport {FS} from './fs';\r\nimport * as Utils from './utils';\r\n\r\ninterface MigrationOptions {\r\n\ttable: string;\r\n\tmigrationsFolder: string;\r\n\tbaseSchemaFile: string;\r\n}\r\n\r\nexport class PostgresDatabase {\r\n\tprivate pool: PG.Pool;\r\n\tconstructor(config = PostgresDatabase.getConfig()) {\r\n\t\ttry {\r\n\t\t\tthis.pool = new (require('pg').Pool)(config);\r\n\t\t} catch (e: any) {\r\n\t\t\tthis.pool = null!;\r\n\t\t}\r\n\t}\r\n\tasync query(statement: string | SQLStatement, values?: any[]) {\r\n\t\tif (!this.pool) {\r\n\t\t\tthrow new Error(`Attempting to use postgres without 'pg' installed`);\r\n\t\t}\r\n\t\tlet result;\r\n\t\ttry {\r\n\t\t\tresult = await this.pool.query(statement, values);\r\n\t\t} catch (e: any) {\r\n\t\t\t// postgres won't give accurate stacks unless we do this\r\n\t\t\tthrow new Error(e.message);\r\n\t\t}\r\n\t\treturn result?.rows || [];\r\n\t}\r\n\tstatic getConfig() {\r\n\t\tlet config: AnyObject = {};\r\n\t\ttry {\r\n\t\t\tconfig = require(FS.ROOT_PATH + '/config/config').usepostgres;\r\n\t\t\tif (!config) throw new Error('Missing config for pg database');\r\n\t\t} catch (e: any) {}\r\n\t\treturn config;\r\n\t}\r\n\tasync transaction(callback: (conn: PG.PoolClient) => any, depth = 0): Promise {\r\n\t\tconst conn = await this.pool.connect();\r\n\t\tawait conn.query(`BEGIN`);\r\n\t\tlet result;\r\n\t\ttry {\r\n\t\t\t// eslint-disable-next-line callback-return\r\n\t\t\tresult = await callback(conn);\r\n\t\t} catch (e: any) {\r\n\t\t\tawait conn.query(`ROLLBACK`);\r\n\t\t\t// two concurrent transactions conflicted, try again\r\n\t\t\tif (e.code === '40001' && depth <= 10) {\r\n\t\t\t\treturn this.transaction(callback, depth + 1);\r\n\t\t\t\t// There is a bug in Postgres that causes some\r\n\t\t\t\t// serialization failures to be reported as failed\r\n\t\t\t\t// unique constraint checks. Only retrying once since\r\n\t\t\t\t// it could be our fault (thanks chaos for this info / the first half of this comment)\r\n\t\t\t} else if (e.code === '23505' && !depth) {\r\n\t\t\t\treturn this.transaction(callback, depth + 1);\r\n\t\t\t} else {\r\n\t\t\t\tthrow e;\r\n\t\t\t}\r\n\t\t}\r\n\t\tawait conn.query(`COMMIT`);\r\n\t\treturn result;\r\n\t}\r\n\tstream(query: string) {\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-this-alias\r\n\t\tconst db = this;\r\n\t\treturn new Streams.ObjectReadStream({\r\n\t\t\tasync read(this: Streams.ObjectReadStream) {\r\n\t\t\t\tconst result = await db.query(query) as T[];\r\n\t\t\t\tif (!result.length) return this.pushEnd();\r\n\t\t\t\t// getting one row at a time means some slower queries\r\n\t\t\t\t// might help with performance\r\n\t\t\t\tthis.buf.push(...result);\r\n\t\t\t},\r\n\t\t});\r\n\t}\r\n\tasync ensureMigrated(opts: MigrationOptions) {\r\n\t\tlet value;\r\n\t\ttry {\r\n\t\t\tconst stored = await this.query(\r\n\t\t\t\t`SELECT value FROM db_info WHERE key = 'version' AND name = $1`, [opts.table]\r\n\t\t\t);\r\n\t\t\tif (stored.length) {\r\n\t\t\t\tvalue = stored[0].value || \"0\";\r\n\t\t\t}\r\n\t\t} catch (e) {\r\n\t\t\tawait this.query(`CREATE TABLE db_info (name TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL)`);\r\n\t\t}\r\n\t\tif (!value) { // means nothing inserted - create row\r\n\t\t\tvalue = \"0\";\r\n\t\t\tawait this.query('INSERT INTO db_info (name, key, value) VALUES ($1, $2, $3)', [opts.table, 'version', value]);\r\n\t\t}\r\n\t\tvalue = Number(value);\r\n\t\tconst files = FS(opts.migrationsFolder)\r\n\t\t\t.readdirSync()\r\n\t\t\t.filter(f => f.endsWith('.sql'))\r\n\t\t\t.map(f => Number(f.slice(1).split('.')[0]));\r\n\t\tUtils.sortBy(files, f => f);\r\n\t\tconst curVer = files[files.length - 1] || 0;\r\n\t\tif (curVer !== value) {\r\n\t\t\tif (!value) {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tawait this.query(`SELECT * FROM ${opts.table} LIMIT 1`);\r\n\t\t\t\t} catch {\r\n\t\t\t\t\tawait this.query(FS(opts.baseSchemaFile).readSync());\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tfor (const n of files) {\r\n\t\t\t\tif (n <= value) continue;\r\n\t\t\t\tawait this.query(FS(`${opts.migrationsFolder}/v${n}.sql`).readSync());\r\n\t\t\t\tawait this.query(\r\n\t\t\t\t\t`UPDATE db_info SET value = $1 WHERE key = 'version' AND name = $2`, [`${n}`, opts.table]\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"], "mappings": "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", "names": [] }