pgsql-client 4.13.0 → 4.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin.d.ts +1 -0
- package/admin.js +43 -13
- package/esm/admin.js +44 -14
- package/package.json +4 -4
package/admin.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare class DbAdmin {
|
|
|
5
5
|
protected verbose: boolean;
|
|
6
6
|
protected roleConfig?: PgTestConnectionOptions;
|
|
7
7
|
constructor(config: PgConfig, verbose?: boolean, roleConfig?: PgTestConnectionOptions);
|
|
8
|
+
private connectionArgs;
|
|
8
9
|
private getEnv;
|
|
9
10
|
private run;
|
|
10
11
|
private safeDropDb;
|
package/admin.js
CHANGED
|
@@ -9,6 +9,8 @@ const pg_env_1 = require("pg-env");
|
|
|
9
9
|
const roles_1 = require("./roles");
|
|
10
10
|
const stream_1 = require("./stream");
|
|
11
11
|
const log = new logger_1.Logger('db-admin');
|
|
12
|
+
const sqlLiteral = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
13
|
+
const quoteIdentifier = (value) => `"${value.replace(/"/g, '""')}"`;
|
|
12
14
|
class DbAdmin {
|
|
13
15
|
config;
|
|
14
16
|
verbose;
|
|
@@ -18,6 +20,16 @@ class DbAdmin {
|
|
|
18
20
|
this.verbose = verbose;
|
|
19
21
|
this.roleConfig = roleConfig;
|
|
20
22
|
}
|
|
23
|
+
connectionArgs() {
|
|
24
|
+
return [
|
|
25
|
+
'-U',
|
|
26
|
+
this.config.user,
|
|
27
|
+
'-h',
|
|
28
|
+
this.config.host,
|
|
29
|
+
'-p',
|
|
30
|
+
String(this.config.port)
|
|
31
|
+
];
|
|
32
|
+
}
|
|
21
33
|
getEnv() {
|
|
22
34
|
return {
|
|
23
35
|
PGHOST: this.config.host,
|
|
@@ -26,9 +38,10 @@ class DbAdmin {
|
|
|
26
38
|
PGPASSWORD: this.config.password
|
|
27
39
|
};
|
|
28
40
|
}
|
|
29
|
-
run(
|
|
41
|
+
run(file, args) {
|
|
42
|
+
const command = [file, ...args].join(' ');
|
|
30
43
|
try {
|
|
31
|
-
(0, child_process_1.
|
|
44
|
+
(0, child_process_1.execFileSync)(file, args, {
|
|
32
45
|
stdio: this.verbose ? 'inherit' : 'pipe',
|
|
33
46
|
env: {
|
|
34
47
|
...process.env,
|
|
@@ -47,7 +60,7 @@ class DbAdmin {
|
|
|
47
60
|
}
|
|
48
61
|
safeDropDb(name) {
|
|
49
62
|
try {
|
|
50
|
-
this.run(
|
|
63
|
+
this.run('dropdb', [name]);
|
|
51
64
|
}
|
|
52
65
|
catch (err) {
|
|
53
66
|
if (err.message.includes('does not exist')) {
|
|
@@ -56,8 +69,11 @@ class DbAdmin {
|
|
|
56
69
|
if (err.message.includes('is being accessed by other users')) {
|
|
57
70
|
log.warn(`Database ${name} has active sessions, terminating backends and retrying drop...`);
|
|
58
71
|
try {
|
|
59
|
-
this.run(
|
|
60
|
-
|
|
72
|
+
this.run('psql', [
|
|
73
|
+
'-c',
|
|
74
|
+
`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${sqlLiteral(name)} AND pid <> pg_backend_pid();`
|
|
75
|
+
]);
|
|
76
|
+
this.run('dropdb', [name]);
|
|
61
77
|
}
|
|
62
78
|
catch (retryErr) {
|
|
63
79
|
log.warn(`Could not drop database ${name} after terminating backends: ${retryErr.message}`);
|
|
@@ -71,22 +87,30 @@ class DbAdmin {
|
|
|
71
87
|
this.safeDropDb(dbName ?? this.config.database);
|
|
72
88
|
}
|
|
73
89
|
dropTemplate(dbName) {
|
|
74
|
-
this.run(
|
|
90
|
+
this.run('psql', [
|
|
91
|
+
'-c',
|
|
92
|
+
`UPDATE pg_database SET datistemplate='false' WHERE datname=${sqlLiteral(dbName)}`
|
|
93
|
+
]);
|
|
75
94
|
this.drop(dbName);
|
|
76
95
|
}
|
|
77
96
|
create(dbName) {
|
|
78
97
|
const db = dbName ?? this.config.database;
|
|
79
|
-
this.run(
|
|
98
|
+
this.run('createdb', [...this.connectionArgs(), db]);
|
|
80
99
|
}
|
|
81
100
|
createFromTemplate(template, dbName) {
|
|
82
101
|
const db = dbName ?? this.config.database;
|
|
83
|
-
this.run(
|
|
102
|
+
this.run('createdb', [...this.connectionArgs(), '-e', '-T', template, db]);
|
|
84
103
|
}
|
|
85
104
|
installExtensions(extensions, dbName) {
|
|
86
105
|
const db = dbName ?? this.config.database;
|
|
87
106
|
const extList = typeof extensions === 'string' ? extensions.split(',') : extensions;
|
|
88
107
|
for (const extension of extList) {
|
|
89
|
-
this.run(
|
|
108
|
+
this.run('psql', [
|
|
109
|
+
'--dbname',
|
|
110
|
+
db,
|
|
111
|
+
'-c',
|
|
112
|
+
`CREATE EXTENSION IF NOT EXISTS ${quoteIdentifier(extension)} CASCADE;`
|
|
113
|
+
]);
|
|
90
114
|
}
|
|
91
115
|
}
|
|
92
116
|
connectionString(dbName) {
|
|
@@ -95,12 +119,18 @@ class DbAdmin {
|
|
|
95
119
|
return `postgres://${user}:${password}@${host}:${port}/${db}`;
|
|
96
120
|
}
|
|
97
121
|
createTemplateFromBase(base, template) {
|
|
98
|
-
this.run(
|
|
99
|
-
this.run(
|
|
122
|
+
this.run('createdb', ['-T', base, template]);
|
|
123
|
+
this.run('psql', [
|
|
124
|
+
'-c',
|
|
125
|
+
`UPDATE pg_database SET datistemplate = true WHERE datname = ${sqlLiteral(template)};`
|
|
126
|
+
]);
|
|
100
127
|
}
|
|
101
128
|
cleanupTemplate(template) {
|
|
102
129
|
try {
|
|
103
|
-
this.run(
|
|
130
|
+
this.run('psql', [
|
|
131
|
+
'-c',
|
|
132
|
+
`UPDATE pg_database SET datistemplate = false WHERE datname = ${sqlLiteral(template)}`
|
|
133
|
+
]);
|
|
104
134
|
}
|
|
105
135
|
catch {
|
|
106
136
|
log.warn(`Skipping failed UPDATE of datistemplate for ${template}`);
|
|
@@ -130,7 +160,7 @@ class DbAdmin {
|
|
|
130
160
|
if (!(0, fs_1.existsSync)(file)) {
|
|
131
161
|
throw new Error(`Missing SQL file: ${file}`);
|
|
132
162
|
}
|
|
133
|
-
this.run(
|
|
163
|
+
this.run('psql', ['-f', file, dbName]);
|
|
134
164
|
}
|
|
135
165
|
async streamSql(sql, dbName) {
|
|
136
166
|
await (0, stream_1.streamSql)({
|
package/esm/admin.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { generateCreateUserWithGrantsSQL, generateGrantRoleSQL } from '@pgpmjs/core';
|
|
2
2
|
import { Logger } from '@pgpmjs/logger';
|
|
3
|
-
import {
|
|
3
|
+
import { execFileSync } from 'child_process';
|
|
4
4
|
import { existsSync } from 'fs';
|
|
5
5
|
import { getPgEnvOptions } from 'pg-env';
|
|
6
6
|
import { getRoleName } from './roles';
|
|
7
7
|
import { streamSql as stream } from './stream';
|
|
8
8
|
const log = new Logger('db-admin');
|
|
9
|
+
const sqlLiteral = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
10
|
+
const quoteIdentifier = (value) => `"${value.replace(/"/g, '""')}"`;
|
|
9
11
|
export class DbAdmin {
|
|
10
12
|
config;
|
|
11
13
|
verbose;
|
|
@@ -15,6 +17,16 @@ export class DbAdmin {
|
|
|
15
17
|
this.verbose = verbose;
|
|
16
18
|
this.roleConfig = roleConfig;
|
|
17
19
|
}
|
|
20
|
+
connectionArgs() {
|
|
21
|
+
return [
|
|
22
|
+
'-U',
|
|
23
|
+
this.config.user,
|
|
24
|
+
'-h',
|
|
25
|
+
this.config.host,
|
|
26
|
+
'-p',
|
|
27
|
+
String(this.config.port)
|
|
28
|
+
];
|
|
29
|
+
}
|
|
18
30
|
getEnv() {
|
|
19
31
|
return {
|
|
20
32
|
PGHOST: this.config.host,
|
|
@@ -23,9 +35,10 @@ export class DbAdmin {
|
|
|
23
35
|
PGPASSWORD: this.config.password
|
|
24
36
|
};
|
|
25
37
|
}
|
|
26
|
-
run(
|
|
38
|
+
run(file, args) {
|
|
39
|
+
const command = [file, ...args].join(' ');
|
|
27
40
|
try {
|
|
28
|
-
|
|
41
|
+
execFileSync(file, args, {
|
|
29
42
|
stdio: this.verbose ? 'inherit' : 'pipe',
|
|
30
43
|
env: {
|
|
31
44
|
...process.env,
|
|
@@ -44,7 +57,7 @@ export class DbAdmin {
|
|
|
44
57
|
}
|
|
45
58
|
safeDropDb(name) {
|
|
46
59
|
try {
|
|
47
|
-
this.run(
|
|
60
|
+
this.run('dropdb', [name]);
|
|
48
61
|
}
|
|
49
62
|
catch (err) {
|
|
50
63
|
if (err.message.includes('does not exist')) {
|
|
@@ -53,8 +66,11 @@ export class DbAdmin {
|
|
|
53
66
|
if (err.message.includes('is being accessed by other users')) {
|
|
54
67
|
log.warn(`Database ${name} has active sessions, terminating backends and retrying drop...`);
|
|
55
68
|
try {
|
|
56
|
-
this.run(
|
|
57
|
-
|
|
69
|
+
this.run('psql', [
|
|
70
|
+
'-c',
|
|
71
|
+
`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${sqlLiteral(name)} AND pid <> pg_backend_pid();`
|
|
72
|
+
]);
|
|
73
|
+
this.run('dropdb', [name]);
|
|
58
74
|
}
|
|
59
75
|
catch (retryErr) {
|
|
60
76
|
log.warn(`Could not drop database ${name} after terminating backends: ${retryErr.message}`);
|
|
@@ -68,22 +84,30 @@ export class DbAdmin {
|
|
|
68
84
|
this.safeDropDb(dbName ?? this.config.database);
|
|
69
85
|
}
|
|
70
86
|
dropTemplate(dbName) {
|
|
71
|
-
this.run(
|
|
87
|
+
this.run('psql', [
|
|
88
|
+
'-c',
|
|
89
|
+
`UPDATE pg_database SET datistemplate='false' WHERE datname=${sqlLiteral(dbName)}`
|
|
90
|
+
]);
|
|
72
91
|
this.drop(dbName);
|
|
73
92
|
}
|
|
74
93
|
create(dbName) {
|
|
75
94
|
const db = dbName ?? this.config.database;
|
|
76
|
-
this.run(
|
|
95
|
+
this.run('createdb', [...this.connectionArgs(), db]);
|
|
77
96
|
}
|
|
78
97
|
createFromTemplate(template, dbName) {
|
|
79
98
|
const db = dbName ?? this.config.database;
|
|
80
|
-
this.run(
|
|
99
|
+
this.run('createdb', [...this.connectionArgs(), '-e', '-T', template, db]);
|
|
81
100
|
}
|
|
82
101
|
installExtensions(extensions, dbName) {
|
|
83
102
|
const db = dbName ?? this.config.database;
|
|
84
103
|
const extList = typeof extensions === 'string' ? extensions.split(',') : extensions;
|
|
85
104
|
for (const extension of extList) {
|
|
86
|
-
this.run(
|
|
105
|
+
this.run('psql', [
|
|
106
|
+
'--dbname',
|
|
107
|
+
db,
|
|
108
|
+
'-c',
|
|
109
|
+
`CREATE EXTENSION IF NOT EXISTS ${quoteIdentifier(extension)} CASCADE;`
|
|
110
|
+
]);
|
|
87
111
|
}
|
|
88
112
|
}
|
|
89
113
|
connectionString(dbName) {
|
|
@@ -92,12 +116,18 @@ export class DbAdmin {
|
|
|
92
116
|
return `postgres://${user}:${password}@${host}:${port}/${db}`;
|
|
93
117
|
}
|
|
94
118
|
createTemplateFromBase(base, template) {
|
|
95
|
-
this.run(
|
|
96
|
-
this.run(
|
|
119
|
+
this.run('createdb', ['-T', base, template]);
|
|
120
|
+
this.run('psql', [
|
|
121
|
+
'-c',
|
|
122
|
+
`UPDATE pg_database SET datistemplate = true WHERE datname = ${sqlLiteral(template)};`
|
|
123
|
+
]);
|
|
97
124
|
}
|
|
98
125
|
cleanupTemplate(template) {
|
|
99
126
|
try {
|
|
100
|
-
this.run(
|
|
127
|
+
this.run('psql', [
|
|
128
|
+
'-c',
|
|
129
|
+
`UPDATE pg_database SET datistemplate = false WHERE datname = ${sqlLiteral(template)}`
|
|
130
|
+
]);
|
|
101
131
|
}
|
|
102
132
|
catch {
|
|
103
133
|
log.warn(`Skipping failed UPDATE of datistemplate for ${template}`);
|
|
@@ -127,7 +157,7 @@ export class DbAdmin {
|
|
|
127
157
|
if (!existsSync(file)) {
|
|
128
158
|
throw new Error(`Missing SQL file: ${file}`);
|
|
129
159
|
}
|
|
130
|
-
this.run(
|
|
160
|
+
this.run('psql', ['-f', file, dbName]);
|
|
131
161
|
}
|
|
132
162
|
async streamSql(sql, dbName) {
|
|
133
163
|
await stream({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgsql-client",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.2",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PostgreSQL client utilities with query helpers, RLS context management, and database administration",
|
|
6
6
|
"main": "index.js",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"makage": "^0.3.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@pgpmjs/core": "^7.27.
|
|
48
|
+
"@pgpmjs/core": "^7.27.2",
|
|
49
49
|
"@pgpmjs/logger": "^2.25.0",
|
|
50
|
-
"@pgpmjs/types": "^2.52.
|
|
50
|
+
"@pgpmjs/types": "^2.52.1",
|
|
51
51
|
"pg": "^8.21.0",
|
|
52
52
|
"pg-env": "^1.31.0"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "608acc891de5197ad3cb4bfd0babe1050dbec24a"
|
|
55
55
|
}
|