@nymphjs/driver-postgresql 1.0.0-beta.6 → 1.0.0-beta.61
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/CHANGELOG.md +244 -0
- package/dist/PostgreSQLDriver.d.ts +43 -16
- package/dist/PostgreSQLDriver.js +368 -272
- package/dist/PostgreSQLDriver.js.map +1 -1
- package/dist/PostgreSQLDriver.test.js +3 -2
- package/dist/PostgreSQLDriver.test.js.map +1 -1
- package/dist/conf/d.d.ts +26 -0
- package/package.json +16 -18
- package/src/PostgreSQLDriver.test.ts +2 -2
- package/src/PostgreSQLDriver.ts +671 -637
- package/tsconfig.json +1 -1
- package/typedoc.json +4 -0
- package/dist/runPostgresqlSync.js +0 -35
- package/src/runPostgresqlSync.js +0 -35
- package/src/testpostgresql.js +0 -59
package/dist/PostgreSQLDriver.js
CHANGED
|
@@ -3,12 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const child_process_1 = __importDefault(require("child_process"));
|
|
7
6
|
const pg_1 = require("pg");
|
|
8
7
|
const pg_format_1 = __importDefault(require("pg-format"));
|
|
9
8
|
const nymph_1 = require("@nymphjs/nymph");
|
|
10
9
|
const guid_1 = require("@nymphjs/guid");
|
|
11
10
|
const conf_1 = require("./conf");
|
|
11
|
+
/**
|
|
12
|
+
* The PostgreSQL Nymph database driver.
|
|
13
|
+
*/
|
|
12
14
|
class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
13
15
|
static escape(input) {
|
|
14
16
|
return pg_format_1.default.ident(input);
|
|
@@ -41,16 +43,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
41
43
|
this.connect();
|
|
42
44
|
}
|
|
43
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
48
|
+
*
|
|
49
|
+
* @returns A clone of this instance.
|
|
50
|
+
*/
|
|
51
|
+
clone() {
|
|
52
|
+
return new PostgreSQLDriver(this.config, this.link, this.transaction ?? undefined);
|
|
53
|
+
}
|
|
44
54
|
getConnection() {
|
|
45
55
|
if (this.transaction != null && this.transaction.connection != null) {
|
|
46
56
|
return Promise.resolve(this.transaction.connection);
|
|
47
57
|
}
|
|
48
|
-
return new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
58
|
+
return new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
59
|
+
? reject(err)
|
|
60
|
+
: client
|
|
61
|
+
? resolve({ client, done })
|
|
62
|
+
: reject('No client returned from connect.')));
|
|
49
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Connect to the PostgreSQL database.
|
|
66
|
+
*
|
|
67
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
68
|
+
*/
|
|
50
69
|
async connect() {
|
|
70
|
+
// If we think we're connected, try pinging the server.
|
|
51
71
|
try {
|
|
52
72
|
if (this.connected) {
|
|
53
|
-
const connection = await new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
73
|
+
const connection = await new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
74
|
+
? reject(err)
|
|
75
|
+
: client
|
|
76
|
+
? resolve({ client, done })
|
|
77
|
+
: reject('No client returned from connect.')));
|
|
54
78
|
await new Promise((resolve, reject) => connection.client.query('SELECT 1;', [], (err, res) => {
|
|
55
79
|
if (err) {
|
|
56
80
|
reject(err);
|
|
@@ -63,6 +87,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
63
87
|
catch (e) {
|
|
64
88
|
this.connected = false;
|
|
65
89
|
}
|
|
90
|
+
// Connecting, selecting database
|
|
66
91
|
if (!this.connected) {
|
|
67
92
|
try {
|
|
68
93
|
this.link = new pg_1.Pool(this.postgresqlConfig);
|
|
@@ -82,6 +107,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
82
107
|
}
|
|
83
108
|
return this.connected;
|
|
84
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Disconnect from the PostgreSQL database.
|
|
112
|
+
*
|
|
113
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
114
|
+
*/
|
|
85
115
|
async disconnect() {
|
|
86
116
|
if (this.connected) {
|
|
87
117
|
await new Promise((resolve) => this.link.end(() => resolve(0)));
|
|
@@ -92,26 +122,39 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
92
122
|
async inTransaction() {
|
|
93
123
|
return !!this.transaction;
|
|
94
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Check connection status.
|
|
127
|
+
*
|
|
128
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
129
|
+
*/
|
|
95
130
|
isConnected() {
|
|
96
131
|
return this.connected;
|
|
97
132
|
}
|
|
98
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Create entity tables in the database.
|
|
135
|
+
*
|
|
136
|
+
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
137
|
+
* @returns True on success, false on failure.
|
|
138
|
+
*/
|
|
139
|
+
async createTables(etype = null) {
|
|
99
140
|
if (etype != null) {
|
|
100
|
-
|
|
141
|
+
// Create the entity table.
|
|
142
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} (
|
|
101
143
|
"guid" BYTEA NOT NULL,
|
|
102
144
|
"tags" TEXT[],
|
|
103
145
|
"cdate" DOUBLE PRECISION NOT NULL,
|
|
104
146
|
"mdate" DOUBLE PRECISION NOT NULL,
|
|
105
147
|
PRIMARY KEY ("guid")
|
|
106
148
|
) WITH ( OIDS=FALSE );`);
|
|
107
|
-
this.
|
|
108
|
-
this.
|
|
109
|
-
this.
|
|
110
|
-
this.
|
|
111
|
-
this.
|
|
112
|
-
this.
|
|
113
|
-
this.
|
|
114
|
-
|
|
149
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`);
|
|
150
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)};`);
|
|
151
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("cdate");`);
|
|
152
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)};`);
|
|
153
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("mdate");`);
|
|
154
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)};`);
|
|
155
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("tags");`);
|
|
156
|
+
// Create the data table.
|
|
157
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} (
|
|
115
158
|
"guid" BYTEA NOT NULL,
|
|
116
159
|
"name" TEXT NOT NULL,
|
|
117
160
|
"value" TEXT NOT NULL,
|
|
@@ -119,16 +162,17 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
119
162
|
FOREIGN KEY ("guid")
|
|
120
163
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
121
164
|
) WITH ( OIDS=FALSE );`);
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
124
|
-
this.
|
|
125
|
-
this.
|
|
126
|
-
this.
|
|
127
|
-
this.
|
|
128
|
-
this.
|
|
129
|
-
this.
|
|
130
|
-
this.
|
|
131
|
-
|
|
165
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`);
|
|
166
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)};`);
|
|
167
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid");`);
|
|
168
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)};`);
|
|
169
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name");`);
|
|
170
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)};`);
|
|
171
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'user'::text;`);
|
|
172
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)};`);
|
|
173
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'group'::text;`);
|
|
174
|
+
// Create the data comparisons table.
|
|
175
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} (
|
|
132
176
|
"guid" BYTEA NOT NULL,
|
|
133
177
|
"name" TEXT NOT NULL,
|
|
134
178
|
"truthy" BOOLEAN,
|
|
@@ -138,16 +182,17 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
138
182
|
FOREIGN KEY ("guid")
|
|
139
183
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
140
184
|
) WITH ( OIDS=FALSE );`);
|
|
141
|
-
this.
|
|
142
|
-
this.
|
|
143
|
-
this.
|
|
144
|
-
this.
|
|
145
|
-
this.
|
|
146
|
-
this.
|
|
147
|
-
this.
|
|
148
|
-
this.
|
|
149
|
-
this.
|
|
150
|
-
|
|
185
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`);
|
|
186
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}_id_guid`)};`);
|
|
187
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} USING btree ("guid");`);
|
|
188
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}_id_name`)};`);
|
|
189
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} USING btree ("name");`);
|
|
190
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}_id_guid_name_truthy`)};`);
|
|
191
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}_id_guid_name_truthy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} USING btree ("guid", "name") WHERE "truthy" = TRUE;`);
|
|
192
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}_id_guid_name_falsy`)};`);
|
|
193
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}_id_guid_name_falsy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} USING btree ("guid", "name") WHERE "truthy" <> TRUE;`);
|
|
194
|
+
// Create the references table.
|
|
195
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} (
|
|
151
196
|
"guid" BYTEA NOT NULL,
|
|
152
197
|
"name" TEXT NOT NULL,
|
|
153
198
|
"reference" BYTEA NOT NULL,
|
|
@@ -155,21 +200,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
155
200
|
FOREIGN KEY ("guid")
|
|
156
201
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
157
202
|
) WITH ( OIDS=FALSE );`);
|
|
158
|
-
this.
|
|
159
|
-
this.
|
|
160
|
-
this.
|
|
161
|
-
this.
|
|
162
|
-
this.
|
|
163
|
-
this.
|
|
164
|
-
this.
|
|
203
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`);
|
|
204
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)};`);
|
|
205
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid");`);
|
|
206
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)};`);
|
|
207
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name");`);
|
|
208
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference`)};`);
|
|
209
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference");`);
|
|
210
|
+
// Create the unique strings table.
|
|
211
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} (
|
|
212
|
+
"guid" BYTEA NOT NULL,
|
|
213
|
+
"unique" TEXT NOT NULL UNIQUE,
|
|
214
|
+
PRIMARY KEY ("guid", "unique"),
|
|
215
|
+
FOREIGN KEY ("guid")
|
|
216
|
+
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
217
|
+
) WITH ( OIDS=FALSE );`);
|
|
165
218
|
}
|
|
166
219
|
else {
|
|
167
|
-
|
|
220
|
+
// Create the UID table.
|
|
221
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uids`)} (
|
|
168
222
|
"name" TEXT NOT NULL,
|
|
169
223
|
"cur_uid" BIGINT NOT NULL,
|
|
170
224
|
PRIMARY KEY ("name")
|
|
171
225
|
) WITH ( OIDS = FALSE );`);
|
|
172
|
-
this.
|
|
226
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}uids`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`);
|
|
173
227
|
}
|
|
174
228
|
return true;
|
|
175
229
|
}
|
|
@@ -192,9 +246,10 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
192
246
|
}
|
|
193
247
|
catch (e) {
|
|
194
248
|
const errorCode = e?.code;
|
|
195
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
249
|
+
if (errorCode === '42P01' && (await this.createTables())) {
|
|
250
|
+
// If the tables don't exist yet, create them.
|
|
196
251
|
for (let etype of etypes) {
|
|
197
|
-
this.createTables(etype);
|
|
252
|
+
await this.createTables(etype);
|
|
198
253
|
}
|
|
199
254
|
try {
|
|
200
255
|
return await runQuery();
|
|
@@ -203,30 +258,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
203
258
|
throw new nymph_1.QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query);
|
|
204
259
|
}
|
|
205
260
|
}
|
|
206
|
-
else {
|
|
207
|
-
throw
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
querySync(runQuery, query, etypes = []) {
|
|
212
|
-
try {
|
|
213
|
-
return runQuery();
|
|
214
|
-
}
|
|
215
|
-
catch (e) {
|
|
216
|
-
const errorCode = e?.code;
|
|
217
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
218
|
-
for (let etype of etypes) {
|
|
219
|
-
this.createTables(etype);
|
|
220
|
-
}
|
|
221
|
-
try {
|
|
222
|
-
return runQuery();
|
|
223
|
-
}
|
|
224
|
-
catch (e2) {
|
|
225
|
-
throw new nymph_1.QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query);
|
|
226
|
-
}
|
|
261
|
+
else if (errorCode === '23505') {
|
|
262
|
+
throw new nymph_1.EntityUniqueConstraintError(`Unique constraint violation.`);
|
|
227
263
|
}
|
|
228
264
|
else {
|
|
229
|
-
throw e;
|
|
265
|
+
throw new nymph_1.QueryFailedError('Query failed: ' + e?.code + ' - ' + e?.message, query);
|
|
230
266
|
}
|
|
231
267
|
}
|
|
232
268
|
}
|
|
@@ -246,35 +282,6 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
246
282
|
return results.rows;
|
|
247
283
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
248
284
|
}
|
|
249
|
-
queryIterSync(query, { etypes = [], params = {}, } = {}) {
|
|
250
|
-
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
251
|
-
return this.querySync(() => {
|
|
252
|
-
const output = child_process_1.default.spawnSync(process.argv0, [__dirname + '/runPostgresqlSync.js'], {
|
|
253
|
-
input: JSON.stringify({
|
|
254
|
-
postgresqlConfig: this.postgresqlConfig,
|
|
255
|
-
query: newQuery,
|
|
256
|
-
params: newParams,
|
|
257
|
-
}),
|
|
258
|
-
timeout: 30000,
|
|
259
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
260
|
-
encoding: 'utf8',
|
|
261
|
-
windowsHide: true,
|
|
262
|
-
});
|
|
263
|
-
try {
|
|
264
|
-
return JSON.parse(output.stdout).rows;
|
|
265
|
-
}
|
|
266
|
-
catch (e) {
|
|
267
|
-
}
|
|
268
|
-
if (output.status === 0) {
|
|
269
|
-
throw new Error('Unknown parse error.');
|
|
270
|
-
}
|
|
271
|
-
const err = JSON.parse(output.stderr);
|
|
272
|
-
const e = new Error(err.name);
|
|
273
|
-
for (const name in err) {
|
|
274
|
-
e[name] = err[name];
|
|
275
|
-
}
|
|
276
|
-
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
277
|
-
}
|
|
278
285
|
queryGet(query, { etypes = [], params = {}, } = {}) {
|
|
279
286
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
280
287
|
return this.query(async () => {
|
|
@@ -307,36 +314,6 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
307
314
|
return { rowCount: results.rowCount ?? 0 };
|
|
308
315
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
309
316
|
}
|
|
310
|
-
queryRunSync(query, { etypes = [], params = {}, } = {}) {
|
|
311
|
-
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
312
|
-
return this.querySync(() => {
|
|
313
|
-
const output = child_process_1.default.spawnSync(process.argv0, [__dirname + '/runPostgresqlSync.js'], {
|
|
314
|
-
input: JSON.stringify({
|
|
315
|
-
postgresqlConfig: this.postgresqlConfig,
|
|
316
|
-
query: newQuery,
|
|
317
|
-
params: newParams,
|
|
318
|
-
}),
|
|
319
|
-
timeout: 30000,
|
|
320
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
321
|
-
encoding: 'utf8',
|
|
322
|
-
windowsHide: true,
|
|
323
|
-
});
|
|
324
|
-
try {
|
|
325
|
-
const results = JSON.parse(output.stdout);
|
|
326
|
-
return { rowCount: results.rowCount ?? 0 };
|
|
327
|
-
}
|
|
328
|
-
catch (e) {
|
|
329
|
-
}
|
|
330
|
-
if (output.status === 0) {
|
|
331
|
-
throw new Error('Unknown parse error.');
|
|
332
|
-
}
|
|
333
|
-
const err = JSON.parse(output.stderr);
|
|
334
|
-
const e = new Error(err.name);
|
|
335
|
-
for (const name in err) {
|
|
336
|
-
e[name] = err[name];
|
|
337
|
-
}
|
|
338
|
-
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
339
|
-
}
|
|
340
317
|
async commit(name) {
|
|
341
318
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
342
319
|
throw new nymph_1.InvalidParametersError('Transaction commit attempted without a name.');
|
|
@@ -391,16 +368,24 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
391
368
|
guid,
|
|
392
369
|
},
|
|
393
370
|
});
|
|
394
|
-
await this.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
371
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
372
|
+
etypes: [etype],
|
|
373
|
+
params: {
|
|
374
|
+
guid,
|
|
375
|
+
},
|
|
376
|
+
});
|
|
399
377
|
}
|
|
400
378
|
catch (e) {
|
|
379
|
+
this.nymph.config.debugError('postgresql', `Delete entity error: "${e}"`);
|
|
401
380
|
await this.rollback('nymph-delete');
|
|
402
381
|
throw e;
|
|
403
382
|
}
|
|
383
|
+
await this.commit('nymph-delete');
|
|
384
|
+
// Remove any cached versions of this entity.
|
|
385
|
+
if (this.nymph.config.cache) {
|
|
386
|
+
this.cleanCache(guid);
|
|
387
|
+
}
|
|
388
|
+
return true;
|
|
404
389
|
}
|
|
405
390
|
async deleteUID(name) {
|
|
406
391
|
if (!name) {
|
|
@@ -424,6 +409,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
424
409
|
writeLine('# UIDs');
|
|
425
410
|
writeLine('#');
|
|
426
411
|
writeLine('');
|
|
412
|
+
// Export UIDs.
|
|
427
413
|
let uids = await this.queryIter(`SELECT * FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ORDER BY "name";`);
|
|
428
414
|
for (const uid of uids) {
|
|
429
415
|
writeLine(`<${uid.name}>[${uid.cur_uid}]`);
|
|
@@ -433,6 +419,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
433
419
|
writeLine('# Entities');
|
|
434
420
|
writeLine('#');
|
|
435
421
|
writeLine('');
|
|
422
|
+
// Get the etypes.
|
|
436
423
|
const tables = await this.queryIter('SELECT relname FROM pg_stat_user_tables ORDER BY relname;');
|
|
437
424
|
const etypes = [];
|
|
438
425
|
for (const tableRow of tables) {
|
|
@@ -442,6 +429,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
442
429
|
}
|
|
443
430
|
}
|
|
444
431
|
for (const etype of etypes) {
|
|
432
|
+
// Export entities.
|
|
445
433
|
const dataIterator = (await this.queryIter(`SELECT encode(e."guid", 'hex') AS "guid", e."tags", e."cdate", e."mdate", d."name" AS "dname", d."value" AS "dvalue", c."string", c."number"
|
|
446
434
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} e
|
|
447
435
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} d ON e."guid"=d."guid"
|
|
@@ -457,6 +445,8 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
457
445
|
writeLine(`\tcdate=${JSON.stringify(cdate)}`);
|
|
458
446
|
writeLine(`\tmdate=${JSON.stringify(mdate)}`);
|
|
459
447
|
if (datum.value.dname != null) {
|
|
448
|
+
// This do will keep going and adding the data until the
|
|
449
|
+
// next entity is reached. $row will end on the next entity.
|
|
460
450
|
do {
|
|
461
451
|
const value = datum.value.dvalue === 'N'
|
|
462
452
|
? JSON.stringify(Number(datum.value.number))
|
|
@@ -468,12 +458,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
468
458
|
} while (!datum.done && datum.value.guid === guid);
|
|
469
459
|
}
|
|
470
460
|
else {
|
|
461
|
+
// Make sure that datum is incremented :)
|
|
471
462
|
datum = dataIterator.next();
|
|
472
463
|
}
|
|
473
464
|
}
|
|
474
465
|
}
|
|
475
466
|
return;
|
|
476
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* Generate the PostgreSQL query.
|
|
470
|
+
* @param options The options array.
|
|
471
|
+
* @param formattedSelectors The formatted selector array.
|
|
472
|
+
* @param etype
|
|
473
|
+
* @param count Used to track internal params.
|
|
474
|
+
* @param params Used to store internal params.
|
|
475
|
+
* @param subquery Whether only a subquery should be returned.
|
|
476
|
+
* @returns The SQL query.
|
|
477
|
+
*/
|
|
477
478
|
makeEntityQuery(options, formattedSelectors, etype, count = { i: 0 }, params = {}, subquery = false, tableSuffix = '', etypes = []) {
|
|
478
479
|
if (typeof options.class?.alterOptions === 'function') {
|
|
479
480
|
options = options.class.alterOptions(options);
|
|
@@ -484,8 +485,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
484
485
|
const fTable = `f${tableSuffix}`;
|
|
485
486
|
const ieTable = `ie${tableSuffix}`;
|
|
486
487
|
const countTable = `count${tableSuffix}`;
|
|
488
|
+
const sTable = `s${tableSuffix}`;
|
|
487
489
|
const sort = options.sort ?? 'cdate';
|
|
488
|
-
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, (key, value, typeIsOr, typeIsNot) => {
|
|
490
|
+
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, ({ key, value, typeIsOr, typeIsNot }) => {
|
|
489
491
|
const clauseNot = key.startsWith('!');
|
|
490
492
|
let curQuery = '';
|
|
491
493
|
for (const curValue of value) {
|
|
@@ -1255,18 +1257,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1255
1257
|
return curQuery;
|
|
1256
1258
|
});
|
|
1257
1259
|
let sortBy;
|
|
1260
|
+
let sortByInner;
|
|
1261
|
+
let sortJoin = '';
|
|
1262
|
+
let sortJoinInner = '';
|
|
1263
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1258
1264
|
switch (sort) {
|
|
1259
1265
|
case 'mdate':
|
|
1260
|
-
sortBy =
|
|
1266
|
+
sortBy = `${eTable}."mdate"${order}`;
|
|
1267
|
+
sortByInner = `${ieTable}."mdate"${order}`;
|
|
1261
1268
|
break;
|
|
1262
1269
|
case 'cdate':
|
|
1270
|
+
sortBy = `${eTable}."cdate"${order}`;
|
|
1271
|
+
sortByInner = `${ieTable}."cdate"${order}`;
|
|
1272
|
+
break;
|
|
1263
1273
|
default:
|
|
1264
|
-
|
|
1274
|
+
const name = `param${++count.i}`;
|
|
1275
|
+
sortJoin = `LEFT JOIN (
|
|
1276
|
+
SELECT "guid", "string", "number"
|
|
1277
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype)}
|
|
1278
|
+
WHERE "name"=@${name}
|
|
1279
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1280
|
+
) ${sTable} ON ${eTable}."guid"=${sTable}."guid"`;
|
|
1281
|
+
sortJoinInner = `LEFT JOIN (
|
|
1282
|
+
SELECT "guid", "string", "number"
|
|
1283
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype)}
|
|
1284
|
+
WHERE "name"=@${name}
|
|
1285
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1286
|
+
) ${sTable} ON ${ieTable}."guid"=${sTable}."guid"`;
|
|
1287
|
+
sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1288
|
+
sortByInner = sortBy;
|
|
1289
|
+
params[name] = sort;
|
|
1265
1290
|
break;
|
|
1266
1291
|
}
|
|
1267
|
-
if (options.reverse) {
|
|
1268
|
-
sortBy += ' DESC';
|
|
1269
|
-
}
|
|
1270
1292
|
let query;
|
|
1271
1293
|
if (queryParts.length) {
|
|
1272
1294
|
if (subquery) {
|
|
@@ -1302,8 +1324,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1302
1324
|
: `${ieTable}."guid"`;
|
|
1303
1325
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1304
1326
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1327
|
+
${sortJoinInner}
|
|
1305
1328
|
WHERE (${whereClause})
|
|
1306
|
-
ORDER BY ${
|
|
1329
|
+
ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
|
|
1307
1330
|
}
|
|
1308
1331
|
else {
|
|
1309
1332
|
query = `SELECT
|
|
@@ -1318,13 +1341,15 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1318
1341
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1319
1342
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1320
1343
|
INNER JOIN ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} ${cTable} ON ${dTable}."guid"=${cTable}."guid" AND ${dTable}."name"=${cTable}."name"
|
|
1344
|
+
${sortJoin}
|
|
1321
1345
|
INNER JOIN (
|
|
1322
1346
|
SELECT ${ieTable}."guid"
|
|
1323
1347
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1348
|
+
${sortJoinInner}
|
|
1324
1349
|
WHERE (${whereClause})
|
|
1325
|
-
ORDER BY ${
|
|
1350
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1326
1351
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1327
|
-
ORDER BY ${
|
|
1352
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1328
1353
|
}
|
|
1329
1354
|
}
|
|
1330
1355
|
}
|
|
@@ -1359,7 +1384,8 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1359
1384
|
: `${ieTable}."guid"`;
|
|
1360
1385
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1361
1386
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1362
|
-
|
|
1387
|
+
${sortJoinInner}
|
|
1388
|
+
ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
|
|
1363
1389
|
}
|
|
1364
1390
|
else {
|
|
1365
1391
|
if (limit || offset) {
|
|
@@ -1375,12 +1401,14 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1375
1401
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1376
1402
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1377
1403
|
INNER JOIN ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} ${cTable} ON ${dTable}."guid"=${cTable}."guid" AND ${dTable}."name"=${cTable}."name"
|
|
1404
|
+
${sortJoin}
|
|
1378
1405
|
INNER JOIN (
|
|
1379
1406
|
SELECT ${ieTable}."guid"
|
|
1380
1407
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1381
|
-
|
|
1408
|
+
${sortJoinInner}
|
|
1409
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1382
1410
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1383
|
-
ORDER BY ${
|
|
1411
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1384
1412
|
}
|
|
1385
1413
|
else {
|
|
1386
1414
|
query = `SELECT
|
|
@@ -1395,7 +1423,8 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1395
1423
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1396
1424
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1397
1425
|
INNER JOIN ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} ${cTable} ON ${dTable}."guid"=${cTable}."guid" AND ${dTable}."name"=${cTable}."name"
|
|
1398
|
-
|
|
1426
|
+
${sortJoin}
|
|
1427
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1399
1428
|
}
|
|
1400
1429
|
}
|
|
1401
1430
|
}
|
|
@@ -1416,15 +1445,10 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1416
1445
|
result,
|
|
1417
1446
|
};
|
|
1418
1447
|
}
|
|
1419
|
-
performQuerySync(options, formattedSelectors, etype) {
|
|
1420
|
-
const { query, params, etypes } = this.makeEntityQuery(options, formattedSelectors, etype);
|
|
1421
|
-
const result = (this.queryIterSync(query, { etypes, params }) || [])[Symbol.iterator]();
|
|
1422
|
-
return {
|
|
1423
|
-
result,
|
|
1424
|
-
};
|
|
1425
|
-
}
|
|
1426
1448
|
async getEntities(options = {}, ...selectors) {
|
|
1427
|
-
const { result: resultPromise, process } = this.
|
|
1449
|
+
const { result: resultPromise, process } = this.getEntitiesRowLike(
|
|
1450
|
+
// @ts-ignore: options is correct here.
|
|
1451
|
+
options, selectors, ({ options, selectors, etype }) => this.performQuery(options, selectors, etype), () => {
|
|
1428
1452
|
const next = result.next();
|
|
1429
1453
|
return next.done ? null : next.value;
|
|
1430
1454
|
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
@@ -1446,28 +1470,6 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1446
1470
|
}
|
|
1447
1471
|
return value;
|
|
1448
1472
|
}
|
|
1449
|
-
getEntitiesSync(options = {}, ...selectors) {
|
|
1450
|
-
const { result, process } = this.getEntitesRowLike(options, selectors, (options, formattedSelectors, etype) => this.performQuerySync(options, formattedSelectors, etype), () => {
|
|
1451
|
-
const next = result.next();
|
|
1452
|
-
return next.done ? null : next.value;
|
|
1453
|
-
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
1454
|
-
tags: row.tags,
|
|
1455
|
-
cdate: isNaN(Number(row.cdate)) ? null : Number(row.cdate),
|
|
1456
|
-
mdate: isNaN(Number(row.mdate)) ? null : Number(row.mdate),
|
|
1457
|
-
}), (row) => ({
|
|
1458
|
-
name: row.name,
|
|
1459
|
-
svalue: row.value === 'N'
|
|
1460
|
-
? JSON.stringify(Number(row.number))
|
|
1461
|
-
: row.value === 'S'
|
|
1462
|
-
? JSON.stringify(row.string)
|
|
1463
|
-
: row.value,
|
|
1464
|
-
}));
|
|
1465
|
-
const value = process();
|
|
1466
|
-
if (value instanceof Error) {
|
|
1467
|
-
throw value;
|
|
1468
|
-
}
|
|
1469
|
-
return value;
|
|
1470
|
-
}
|
|
1471
1473
|
async getUID(name) {
|
|
1472
1474
|
if (name == null) {
|
|
1473
1475
|
throw new nymph_1.InvalidParametersError('Name not given for UID.');
|
|
@@ -1479,114 +1481,160 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1479
1481
|
});
|
|
1480
1482
|
return result?.cur_uid == null ? null : Number(result.cur_uid);
|
|
1481
1483
|
}
|
|
1482
|
-
async import(filename) {
|
|
1484
|
+
async import(filename, transaction) {
|
|
1483
1485
|
try {
|
|
1484
1486
|
const result = await this.importFromFile(filename, async (guid, tags, sdata, etype) => {
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
etypes: [etype],
|
|
1507
|
-
params: {
|
|
1508
|
-
guid,
|
|
1509
|
-
},
|
|
1510
|
-
}));
|
|
1511
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1512
|
-
etypes: [etype],
|
|
1513
|
-
params: {
|
|
1514
|
-
guid,
|
|
1515
|
-
},
|
|
1516
|
-
}));
|
|
1517
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1518
|
-
etypes: [etype],
|
|
1519
|
-
params: {
|
|
1520
|
-
guid,
|
|
1521
|
-
},
|
|
1522
|
-
}));
|
|
1523
|
-
await Promise.all(promises);
|
|
1524
|
-
delete sdata.cdate;
|
|
1525
|
-
delete sdata.mdate;
|
|
1526
|
-
for (const name in sdata) {
|
|
1527
|
-
const value = sdata[name];
|
|
1528
|
-
const uvalue = JSON.parse(value);
|
|
1529
|
-
if (value === undefined) {
|
|
1530
|
-
continue;
|
|
1531
|
-
}
|
|
1532
|
-
const storageValue = typeof uvalue === 'number'
|
|
1533
|
-
? 'N'
|
|
1534
|
-
: typeof uvalue === 'string'
|
|
1535
|
-
? 'S'
|
|
1536
|
-
: value;
|
|
1487
|
+
try {
|
|
1488
|
+
await this.internalTransaction(`nymph-import-entity-${guid}`);
|
|
1489
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
1490
|
+
delete sdata.cdate;
|
|
1491
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
1492
|
+
delete sdata.mdate;
|
|
1493
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1494
|
+
etypes: [etype],
|
|
1495
|
+
params: {
|
|
1496
|
+
guid,
|
|
1497
|
+
},
|
|
1498
|
+
});
|
|
1499
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate);`, {
|
|
1500
|
+
etypes: [etype],
|
|
1501
|
+
params: {
|
|
1502
|
+
guid,
|
|
1503
|
+
tags,
|
|
1504
|
+
cdate: isNaN(cdate) ? null : cdate,
|
|
1505
|
+
mdate: isNaN(mdate) ? null : mdate,
|
|
1506
|
+
},
|
|
1507
|
+
});
|
|
1537
1508
|
const promises = [];
|
|
1538
|
-
promises.push(this.queryRun(`
|
|
1509
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1539
1510
|
etypes: [etype],
|
|
1540
1511
|
params: {
|
|
1541
1512
|
guid,
|
|
1542
|
-
name,
|
|
1543
|
-
storageValue,
|
|
1544
1513
|
},
|
|
1545
1514
|
}));
|
|
1546
|
-
promises.push(this.queryRun(`
|
|
1515
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1547
1516
|
etypes: [etype],
|
|
1548
1517
|
params: {
|
|
1549
1518
|
guid,
|
|
1550
|
-
name,
|
|
1551
|
-
truthy: !!uvalue,
|
|
1552
|
-
string: `${uvalue}`,
|
|
1553
|
-
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
1554
1519
|
},
|
|
1555
1520
|
}));
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1521
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1522
|
+
etypes: [etype],
|
|
1523
|
+
params: {
|
|
1524
|
+
guid,
|
|
1525
|
+
},
|
|
1526
|
+
}));
|
|
1527
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1528
|
+
etypes: [etype],
|
|
1529
|
+
params: {
|
|
1530
|
+
guid,
|
|
1531
|
+
},
|
|
1532
|
+
}));
|
|
1533
|
+
await Promise.all(promises);
|
|
1534
|
+
for (const name in sdata) {
|
|
1535
|
+
const value = sdata[name];
|
|
1536
|
+
const uvalue = JSON.parse(value);
|
|
1537
|
+
if (value === undefined) {
|
|
1538
|
+
continue;
|
|
1539
|
+
}
|
|
1540
|
+
const storageValue = typeof uvalue === 'number'
|
|
1541
|
+
? 'N'
|
|
1542
|
+
: typeof uvalue === 'string'
|
|
1543
|
+
? 'S'
|
|
1544
|
+
: value;
|
|
1545
|
+
const promises = [];
|
|
1546
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value") VALUES (decode(@guid, 'hex'), @name, @storageValue);`, {
|
|
1559
1547
|
etypes: [etype],
|
|
1560
1548
|
params: {
|
|
1561
1549
|
guid,
|
|
1562
1550
|
name,
|
|
1563
|
-
|
|
1551
|
+
storageValue,
|
|
1564
1552
|
},
|
|
1565
1553
|
}));
|
|
1554
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} ("guid", "name", "truthy", "string", "number") VALUES (decode(@guid, 'hex'), @name, @truthy, @string, @number);`, {
|
|
1555
|
+
etypes: [etype],
|
|
1556
|
+
params: {
|
|
1557
|
+
guid,
|
|
1558
|
+
name,
|
|
1559
|
+
truthy: !!uvalue,
|
|
1560
|
+
string: `${uvalue}`,
|
|
1561
|
+
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
1562
|
+
},
|
|
1563
|
+
}));
|
|
1564
|
+
const references = this.findReferences(value);
|
|
1565
|
+
for (const reference of references) {
|
|
1566
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`, {
|
|
1567
|
+
etypes: [etype],
|
|
1568
|
+
params: {
|
|
1569
|
+
guid,
|
|
1570
|
+
name,
|
|
1571
|
+
reference,
|
|
1572
|
+
},
|
|
1573
|
+
}));
|
|
1574
|
+
}
|
|
1566
1575
|
}
|
|
1576
|
+
const uniques = await this.nymph
|
|
1577
|
+
.getEntityClassByEtype(etype)
|
|
1578
|
+
.getUniques({ guid, cdate, mdate, tags, data: {}, sdata });
|
|
1579
|
+
for (const unique of uniques) {
|
|
1580
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
1581
|
+
etypes: [etype],
|
|
1582
|
+
params: {
|
|
1583
|
+
guid,
|
|
1584
|
+
unique,
|
|
1585
|
+
},
|
|
1586
|
+
}).catch((e) => {
|
|
1587
|
+
if (e instanceof nymph_1.EntityUniqueConstraintError) {
|
|
1588
|
+
this.nymph.config.debugError('postgresql', `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
1589
|
+
}
|
|
1590
|
+
return e;
|
|
1591
|
+
}));
|
|
1592
|
+
}
|
|
1593
|
+
await Promise.all(promises);
|
|
1594
|
+
await this.commit(`nymph-import-entity-${guid}`);
|
|
1595
|
+
}
|
|
1596
|
+
catch (e) {
|
|
1597
|
+
this.nymph.config.debugError('postgresql', `Import entity error: "${e}"`);
|
|
1598
|
+
await this.rollback(`nymph-import-entity-${guid}`);
|
|
1599
|
+
throw e;
|
|
1567
1600
|
}
|
|
1568
|
-
await Promise.all(promises);
|
|
1569
1601
|
}, async (name, curUid) => {
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1602
|
+
try {
|
|
1603
|
+
await this.internalTransaction(`nymph-import-uid-${name}`);
|
|
1604
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
1605
|
+
params: {
|
|
1606
|
+
name,
|
|
1607
|
+
},
|
|
1608
|
+
});
|
|
1609
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
1610
|
+
params: {
|
|
1611
|
+
name,
|
|
1612
|
+
curUid,
|
|
1613
|
+
},
|
|
1614
|
+
});
|
|
1615
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
1616
|
+
}
|
|
1617
|
+
catch (e) {
|
|
1618
|
+
this.nymph.config.debugError('postgresql', `Import UID error: "${e}"`);
|
|
1619
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
1620
|
+
throw e;
|
|
1621
|
+
}
|
|
1581
1622
|
}, async () => {
|
|
1582
|
-
|
|
1623
|
+
if (transaction) {
|
|
1624
|
+
await this.internalTransaction('nymph-import');
|
|
1625
|
+
}
|
|
1583
1626
|
}, async () => {
|
|
1584
|
-
|
|
1627
|
+
if (transaction) {
|
|
1628
|
+
await this.commit('nymph-import');
|
|
1629
|
+
}
|
|
1585
1630
|
});
|
|
1586
1631
|
return result;
|
|
1587
1632
|
}
|
|
1588
1633
|
catch (e) {
|
|
1589
|
-
|
|
1634
|
+
this.nymph.config.debugError('postgresql', `Import error: "${e}"`);
|
|
1635
|
+
if (transaction) {
|
|
1636
|
+
await this.rollback('nymph-import');
|
|
1637
|
+
}
|
|
1590
1638
|
return false;
|
|
1591
1639
|
}
|
|
1592
1640
|
}
|
|
@@ -1595,13 +1643,14 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1595
1643
|
throw new nymph_1.InvalidParametersError('Name not given for UID.');
|
|
1596
1644
|
}
|
|
1597
1645
|
await this.internalTransaction('nymph-newuid');
|
|
1646
|
+
let curUid = undefined;
|
|
1598
1647
|
try {
|
|
1599
1648
|
const lock = await this.queryGet(`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name FOR UPDATE;`, {
|
|
1600
1649
|
params: {
|
|
1601
1650
|
name,
|
|
1602
1651
|
},
|
|
1603
1652
|
});
|
|
1604
|
-
|
|
1653
|
+
curUid = lock?.cur_uid == null ? undefined : Number(lock.cur_uid);
|
|
1605
1654
|
if (curUid == null) {
|
|
1606
1655
|
curUid = 1;
|
|
1607
1656
|
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
@@ -1620,13 +1669,14 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1620
1669
|
},
|
|
1621
1670
|
});
|
|
1622
1671
|
}
|
|
1623
|
-
await this.commit('nymph-newuid');
|
|
1624
|
-
return curUid;
|
|
1625
1672
|
}
|
|
1626
1673
|
catch (e) {
|
|
1674
|
+
this.nymph.config.debugError('postgresql', `New UID error: "${e}"`);
|
|
1627
1675
|
await this.rollback('nymph-newuid');
|
|
1628
1676
|
throw e;
|
|
1629
1677
|
}
|
|
1678
|
+
await this.commit('nymph-newuid');
|
|
1679
|
+
return curUid;
|
|
1630
1680
|
}
|
|
1631
1681
|
async renameUID(oldName, newName) {
|
|
1632
1682
|
if (oldName == null || newName == null) {
|
|
@@ -1659,7 +1709,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1659
1709
|
return true;
|
|
1660
1710
|
}
|
|
1661
1711
|
async saveEntity(entity) {
|
|
1662
|
-
const insertData = async (guid, data, sdata, etype) => {
|
|
1712
|
+
const insertData = async (guid, data, sdata, uniques, etype) => {
|
|
1663
1713
|
const runInsertQuery = async (name, value, svalue) => {
|
|
1664
1714
|
if (value === undefined) {
|
|
1665
1715
|
return;
|
|
@@ -1701,6 +1751,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1701
1751
|
}
|
|
1702
1752
|
await Promise.all(promises);
|
|
1703
1753
|
};
|
|
1754
|
+
for (const unique of uniques) {
|
|
1755
|
+
try {
|
|
1756
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
1757
|
+
etypes: [etype],
|
|
1758
|
+
params: {
|
|
1759
|
+
guid,
|
|
1760
|
+
unique,
|
|
1761
|
+
},
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
catch (e) {
|
|
1765
|
+
if (e instanceof nymph_1.EntityUniqueConstraintError) {
|
|
1766
|
+
this.nymph.config.debugError('postgresql', `Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
1767
|
+
}
|
|
1768
|
+
throw e;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1704
1771
|
for (const name in data) {
|
|
1705
1772
|
await runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
1706
1773
|
}
|
|
@@ -1708,8 +1775,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1708
1775
|
await runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
1709
1776
|
}
|
|
1710
1777
|
};
|
|
1778
|
+
let inTransaction = false;
|
|
1711
1779
|
try {
|
|
1712
|
-
const result = await this.saveEntityRowLike(entity, async (
|
|
1780
|
+
const result = await this.saveEntityRowLike(entity, async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
1781
|
+
if (Object.keys(data).length === 0 &&
|
|
1782
|
+
Object.keys(sdata).length === 0) {
|
|
1783
|
+
return false;
|
|
1784
|
+
}
|
|
1713
1785
|
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @cdate);`, {
|
|
1714
1786
|
etypes: [etype],
|
|
1715
1787
|
params: {
|
|
@@ -1718,9 +1790,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1718
1790
|
cdate,
|
|
1719
1791
|
},
|
|
1720
1792
|
});
|
|
1721
|
-
await insertData(guid, data, sdata, etype);
|
|
1793
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1722
1794
|
return true;
|
|
1723
|
-
}, async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
1795
|
+
}, async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
1796
|
+
if (Object.keys(data).length === 0 &&
|
|
1797
|
+
Object.keys(sdata).length === 0) {
|
|
1798
|
+
return false;
|
|
1799
|
+
}
|
|
1724
1800
|
const promises = [];
|
|
1725
1801
|
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1726
1802
|
etypes: [etype],
|
|
@@ -1746,6 +1822,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1746
1822
|
guid,
|
|
1747
1823
|
},
|
|
1748
1824
|
}));
|
|
1825
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1826
|
+
etypes: [etype],
|
|
1827
|
+
params: {
|
|
1828
|
+
guid,
|
|
1829
|
+
},
|
|
1830
|
+
}));
|
|
1749
1831
|
await Promise.all(promises);
|
|
1750
1832
|
const info = await this.queryRun(`UPDATE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} SET "tags"=@tags, "mdate"=@mdate WHERE "guid"=decode(@guid, 'hex') AND "mdate" <= @emdate;`, {
|
|
1751
1833
|
etypes: [etype],
|
|
@@ -1777,26 +1859,39 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1777
1859
|
guid,
|
|
1778
1860
|
},
|
|
1779
1861
|
}));
|
|
1862
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1863
|
+
etypes: [etype],
|
|
1864
|
+
params: {
|
|
1865
|
+
guid,
|
|
1866
|
+
},
|
|
1867
|
+
}));
|
|
1780
1868
|
await Promise.all(promises);
|
|
1781
|
-
await insertData(guid, data, sdata, etype);
|
|
1869
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1782
1870
|
success = true;
|
|
1783
1871
|
}
|
|
1784
1872
|
return success;
|
|
1785
1873
|
}, async () => {
|
|
1786
1874
|
await this.internalTransaction('nymph-save');
|
|
1875
|
+
inTransaction = true;
|
|
1787
1876
|
}, async (success) => {
|
|
1788
|
-
if (
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1877
|
+
if (inTransaction) {
|
|
1878
|
+
inTransaction = false;
|
|
1879
|
+
if (success) {
|
|
1880
|
+
await this.commit('nymph-save');
|
|
1881
|
+
}
|
|
1882
|
+
else {
|
|
1883
|
+
await this.rollback('nymph-save');
|
|
1884
|
+
}
|
|
1793
1885
|
}
|
|
1794
1886
|
return success;
|
|
1795
1887
|
});
|
|
1796
1888
|
return result;
|
|
1797
1889
|
}
|
|
1798
1890
|
catch (e) {
|
|
1799
|
-
|
|
1891
|
+
this.nymph.config.debugError('postgresql', `Save entity error: "${e}"`);
|
|
1892
|
+
if (inTransaction) {
|
|
1893
|
+
await this.rollback('nymph-save');
|
|
1894
|
+
}
|
|
1800
1895
|
throw e;
|
|
1801
1896
|
}
|
|
1802
1897
|
}
|
|
@@ -1818,23 +1913,25 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1818
1913
|
curUid,
|
|
1819
1914
|
},
|
|
1820
1915
|
});
|
|
1821
|
-
await this.commit('nymph-setuid');
|
|
1822
|
-
return true;
|
|
1823
1916
|
}
|
|
1824
1917
|
catch (e) {
|
|
1825
1918
|
await this.rollback('nymph-setuid');
|
|
1826
1919
|
throw e;
|
|
1827
1920
|
}
|
|
1921
|
+
await this.commit('nymph-setuid');
|
|
1922
|
+
return true;
|
|
1828
1923
|
}
|
|
1829
1924
|
async internalTransaction(name) {
|
|
1830
1925
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1831
1926
|
throw new nymph_1.InvalidParametersError('Transaction start attempted without a name.');
|
|
1832
1927
|
}
|
|
1833
1928
|
if (!this.transaction || this.transaction.count === 0) {
|
|
1929
|
+
// Lock to one connection.
|
|
1834
1930
|
this.transaction = {
|
|
1835
1931
|
count: 0,
|
|
1836
1932
|
connection: await this.getConnection(),
|
|
1837
1933
|
};
|
|
1934
|
+
// We're not in a transaction yet, so start one.
|
|
1838
1935
|
await this.queryRun('BEGIN;');
|
|
1839
1936
|
}
|
|
1840
1937
|
await this.queryRun(`SAVEPOINT ${PostgreSQLDriver.escape(name)};`);
|
|
@@ -1842,14 +1939,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1842
1939
|
return this.transaction;
|
|
1843
1940
|
}
|
|
1844
1941
|
async startTransaction(name) {
|
|
1845
|
-
const inTransaction = this.inTransaction();
|
|
1942
|
+
const inTransaction = await this.inTransaction();
|
|
1846
1943
|
const transaction = await this.internalTransaction(name);
|
|
1847
1944
|
if (!inTransaction) {
|
|
1848
1945
|
this.transaction = null;
|
|
1849
1946
|
}
|
|
1850
1947
|
const nymph = this.nymph.clone();
|
|
1851
|
-
nymph.driver =
|
|
1852
|
-
nymph.driver.init(nymph);
|
|
1948
|
+
nymph.driver.transaction = transaction;
|
|
1853
1949
|
return nymph;
|
|
1854
1950
|
}
|
|
1855
1951
|
}
|