-
Notifications
You must be signed in to change notification settings - Fork 222
Expand file tree
/
Copy pathpostgres-connection.ts
More file actions
94 lines (82 loc) · 2.7 KB
/
postgres-connection.ts
File metadata and controls
94 lines (82 loc) · 2.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import pg from 'pg';
import {parse} from 'pg-connection-string';
import {RdbmsConnection} from '@shopify/shopify-app-session-storage';
export class PostgresConnection implements RdbmsConnection {
sessionStorageIdentifier: string;
private ready: Promise<void>;
private pool: pg.Pool;
private connectionString: string;
constructor(dbUrl: string, sessionStorageIdentifier: string) {
this.connectionString = dbUrl;
this.ready = this.init();
this.sessionStorageIdentifier = sessionStorageIdentifier;
}
async query(query: string, params: any[] = []): Promise<any[]> {
await this.ready;
return (await this.pool.query(query, params)).rows;
}
/**
* Runs a series of queries in a transaction - requires the use of a SINGLE client,
* hence we can't use the query method above.
*
* @param queries an array of SQL queries to execute in a transaction
*/
async transaction(queries: string[]): Promise<void> {
await this.ready;
// check if the first and last queries are BEGIN and COMMIT, if not, add them
if (queries[0] !== 'BEGIN') {
queries.unshift('BEGIN');
}
if (queries[queries.length - 1] !== 'COMMIT') {
queries.push('COMMIT');
}
const client = await this.pool.connect();
try {
for (const query of queries) {
await client.query(query);
}
} catch (error) {
// rollback if any of the queries fail
await client.query(`ROLLBACK`);
throw error;
} finally {
client.release();
}
}
async disconnect(): Promise<void> {
// Since no longer using individual client, use disconnect to reset the pool.
await this.ready;
await this.pool.end();
this.ready = this.init();
}
async connect(): Promise<void> {
await this.ready;
}
public getDatabase(): string | undefined {
const database = parse(this.connectionString).database;
return database ? decodeURIComponent(database) : undefined;
}
async hasTable(tablename: string): Promise<boolean> {
await this.ready;
const query = `
SELECT EXISTS (
SELECT tablename FROM pg_catalog.pg_tables
WHERE tablename = ${this.getArgumentPlaceholder(1)}
)
`;
// Allow multiple apps to be on the same host with separate DB and querying the right
// DB for the session table exisitence
const rows = await this.query(query, [tablename]);
return rows[0].exists;
}
getArgumentPlaceholder(position: number): string {
return `$${position}`;
}
private async init(): Promise<void> {
const config = parse(this.connectionString);
if (config.database) {
config.database = decodeURIComponent(config.database);
}
this.pool = new pg.Pool(config as pg.PoolConfig);
}
}