@nymphjs/driver-postgresql 1.0.0-beta.8 → 1.0.0-beta.80
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 +353 -0
- package/dist/PostgreSQLDriver.d.ts +63 -18
- package/dist/PostgreSQLDriver.js +616 -473
- 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 +971 -872
- package/tsconfig.json +3 -3
- 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,23 +3,58 @@ 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 {
|
|
15
|
+
config;
|
|
16
|
+
postgresqlConfig;
|
|
17
|
+
prefix;
|
|
18
|
+
connected = false;
|
|
19
|
+
// @ts-ignore: this is assigned in connect(), which is called by the constructor.
|
|
20
|
+
link;
|
|
21
|
+
transaction = null;
|
|
13
22
|
static escape(input) {
|
|
14
23
|
return pg_format_1.default.ident(input);
|
|
15
24
|
}
|
|
16
25
|
static escapeValue(input) {
|
|
17
26
|
return pg_format_1.default.literal(input);
|
|
18
27
|
}
|
|
28
|
+
static escapeNullSequences(input) {
|
|
29
|
+
// Postgres doesn't support null bytes in `text`, and it converts strings
|
|
30
|
+
// in JSON to `text`, so we need to escape the escape sequences for null
|
|
31
|
+
// bytes.
|
|
32
|
+
return (input
|
|
33
|
+
.replace(/\uFFFD/g, () => '\uFFFD\uFFFD')
|
|
34
|
+
// n so that if there's already an escape, it turns into \n
|
|
35
|
+
// - so that it won't match a \uFFFD that got turned into \uFFFD\uFFFD
|
|
36
|
+
.replace(/\\u0000/g, () => 'nu\uFFFD-')
|
|
37
|
+
.replace(/\\x00/g, () => 'nx\uFFFD-'));
|
|
38
|
+
}
|
|
39
|
+
static unescapeNullSequences(input) {
|
|
40
|
+
return input
|
|
41
|
+
.replace(/nu\uFFFD-/g, () => '\\u0000')
|
|
42
|
+
.replace(/nx\uFFFD-/g, () => '\\x00')
|
|
43
|
+
.replace(/\uFFFD\uFFFD/g, () => '\uFFFD');
|
|
44
|
+
}
|
|
45
|
+
static escapeNulls(input) {
|
|
46
|
+
// Postgres doesn't support null bytes in `text`.
|
|
47
|
+
return input
|
|
48
|
+
.replace(/\uFFFD/g, () => '\uFFFD\uFFFD')
|
|
49
|
+
.replace(/\x00/g, () => '-\uFFFD-');
|
|
50
|
+
}
|
|
51
|
+
static unescapeNulls(input) {
|
|
52
|
+
return input
|
|
53
|
+
.replace(/-\uFFFD-/g, () => '\x00')
|
|
54
|
+
.replace(/\uFFFD\uFFFD/g, () => '\uFFFD');
|
|
55
|
+
}
|
|
19
56
|
constructor(config, link, transaction) {
|
|
20
57
|
super();
|
|
21
|
-
this.connected = false;
|
|
22
|
-
this.transaction = null;
|
|
23
58
|
this.config = { ...conf_1.PostgreSQLDriverConfigDefaults, ...config };
|
|
24
59
|
const { host, user, password, database, port, customPoolConfig } = this.config;
|
|
25
60
|
this.postgresqlConfig = customPoolConfig ?? {
|
|
@@ -41,19 +76,40 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
41
76
|
this.connect();
|
|
42
77
|
}
|
|
43
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
81
|
+
*
|
|
82
|
+
* @returns A clone of this instance.
|
|
83
|
+
*/
|
|
44
84
|
clone() {
|
|
45
85
|
return new PostgreSQLDriver(this.config, this.link, this.transaction ?? undefined);
|
|
46
86
|
}
|
|
47
|
-
getConnection() {
|
|
48
|
-
if (this.transaction != null &&
|
|
87
|
+
getConnection(outsideTransaction = false) {
|
|
88
|
+
if (this.transaction != null &&
|
|
89
|
+
this.transaction.connection != null &&
|
|
90
|
+
!outsideTransaction) {
|
|
49
91
|
return Promise.resolve(this.transaction.connection);
|
|
50
92
|
}
|
|
51
|
-
return new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
93
|
+
return new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
94
|
+
? reject(err)
|
|
95
|
+
: client
|
|
96
|
+
? resolve({ client, done })
|
|
97
|
+
: reject('No client returned from connect.')));
|
|
52
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Connect to the PostgreSQL database.
|
|
101
|
+
*
|
|
102
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
103
|
+
*/
|
|
53
104
|
async connect() {
|
|
105
|
+
// If we think we're connected, try pinging the server.
|
|
54
106
|
try {
|
|
55
107
|
if (this.connected) {
|
|
56
|
-
const connection = await new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
108
|
+
const connection = await new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
109
|
+
? reject(err)
|
|
110
|
+
: client
|
|
111
|
+
? resolve({ client, done })
|
|
112
|
+
: reject('No client returned from connect.')));
|
|
57
113
|
await new Promise((resolve, reject) => connection.client.query('SELECT 1;', [], (err, res) => {
|
|
58
114
|
if (err) {
|
|
59
115
|
reject(err);
|
|
@@ -66,6 +122,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
66
122
|
catch (e) {
|
|
67
123
|
this.connected = false;
|
|
68
124
|
}
|
|
125
|
+
// Connecting, selecting database
|
|
69
126
|
if (!this.connected) {
|
|
70
127
|
try {
|
|
71
128
|
this.link = new pg_1.Pool(this.postgresqlConfig);
|
|
@@ -85,6 +142,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
85
142
|
}
|
|
86
143
|
return this.connected;
|
|
87
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Disconnect from the PostgreSQL database.
|
|
147
|
+
*
|
|
148
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
149
|
+
*/
|
|
88
150
|
async disconnect() {
|
|
89
151
|
if (this.connected) {
|
|
90
152
|
await new Promise((resolve) => this.link.end(() => resolve(0)));
|
|
@@ -95,85 +157,116 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
95
157
|
async inTransaction() {
|
|
96
158
|
return !!this.transaction;
|
|
97
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Check connection status.
|
|
162
|
+
*
|
|
163
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
164
|
+
*/
|
|
98
165
|
isConnected() {
|
|
99
166
|
return this.connected;
|
|
100
167
|
}
|
|
101
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Create entity tables in the database.
|
|
170
|
+
*
|
|
171
|
+
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
172
|
+
* @returns True on success, false on failure.
|
|
173
|
+
*/
|
|
174
|
+
async createTables(etype = null) {
|
|
175
|
+
const connection = await this.getConnection(true);
|
|
102
176
|
if (etype != null) {
|
|
103
|
-
|
|
177
|
+
// Create the entity table.
|
|
178
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} (
|
|
104
179
|
"guid" BYTEA NOT NULL,
|
|
105
180
|
"tags" TEXT[],
|
|
106
181
|
"cdate" DOUBLE PRECISION NOT NULL,
|
|
107
182
|
"mdate" DOUBLE PRECISION NOT NULL,
|
|
108
183
|
PRIMARY KEY ("guid")
|
|
109
|
-
) WITH ( OIDS=FALSE )
|
|
110
|
-
this.
|
|
111
|
-
this.
|
|
112
|
-
this.
|
|
113
|
-
this.
|
|
114
|
-
this.
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
"name" TEXT NOT NULL,
|
|
120
|
-
"value" TEXT NOT NULL,
|
|
121
|
-
PRIMARY KEY ("guid", "name"),
|
|
122
|
-
FOREIGN KEY ("guid")
|
|
123
|
-
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
124
|
-
) WITH ( OIDS=FALSE );`);
|
|
125
|
-
this.queryRunSync(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`);
|
|
126
|
-
this.queryRunSync(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)};`);
|
|
127
|
-
this.queryRunSync(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid");`);
|
|
128
|
-
this.queryRunSync(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)};`);
|
|
129
|
-
this.queryRunSync(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name");`);
|
|
130
|
-
this.queryRunSync(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)};`);
|
|
131
|
-
this.queryRunSync(`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;`);
|
|
132
|
-
this.queryRunSync(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)};`);
|
|
133
|
-
this.queryRunSync(`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;`);
|
|
134
|
-
this.queryRunSync(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} (
|
|
184
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
185
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
186
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)};`, { connection });
|
|
187
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("cdate");`, { connection });
|
|
188
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)};`, { connection });
|
|
189
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("mdate");`, { connection });
|
|
190
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)};`, { connection });
|
|
191
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("tags");`, { connection });
|
|
192
|
+
// Create the data table.
|
|
193
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} (
|
|
135
194
|
"guid" BYTEA NOT NULL,
|
|
136
195
|
"name" TEXT NOT NULL,
|
|
137
|
-
"
|
|
196
|
+
"value" CHARACTER(1) NOT NULL,
|
|
197
|
+
"json" JSONB,
|
|
138
198
|
"string" TEXT,
|
|
139
199
|
"number" DOUBLE PRECISION,
|
|
200
|
+
"truthy" BOOLEAN,
|
|
140
201
|
PRIMARY KEY ("guid", "name"),
|
|
141
202
|
FOREIGN KEY ("guid")
|
|
142
203
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
143
|
-
) WITH ( OIDS=FALSE )
|
|
144
|
-
this.
|
|
145
|
-
this.
|
|
146
|
-
this.
|
|
147
|
-
this.
|
|
148
|
-
this.
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
151
|
-
this.
|
|
152
|
-
this.
|
|
153
|
-
this.
|
|
204
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
205
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
206
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)};`, { connection });
|
|
207
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid");`, { connection });
|
|
208
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)};`, { connection });
|
|
209
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid", "name");`, { connection });
|
|
210
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)};`, { connection });
|
|
211
|
+
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;`, { connection });
|
|
212
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)};`, { connection });
|
|
213
|
+
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;`, { connection });
|
|
214
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)};`, { connection });
|
|
215
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name");`, { connection });
|
|
216
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)};`, { connection });
|
|
217
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", LEFT("string", 512));`, { connection });
|
|
218
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)};`, { connection });
|
|
219
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", "number");`, { connection });
|
|
220
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)};`, { connection });
|
|
221
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name") WHERE "truthy" = TRUE;`, { connection });
|
|
222
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_falsy`)};`, { connection });
|
|
223
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_falsy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name") WHERE "truthy" <> TRUE;`, { connection });
|
|
224
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)};`, { connection });
|
|
225
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("string" gin_trgm_ops);`, { connection });
|
|
226
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)};`, { connection });
|
|
227
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("json");`, { connection });
|
|
228
|
+
// Create the references table.
|
|
229
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} (
|
|
154
230
|
"guid" BYTEA NOT NULL,
|
|
155
231
|
"name" TEXT NOT NULL,
|
|
156
232
|
"reference" BYTEA NOT NULL,
|
|
157
233
|
PRIMARY KEY ("guid", "name", "reference"),
|
|
158
234
|
FOREIGN KEY ("guid")
|
|
159
235
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
160
|
-
) WITH ( OIDS=FALSE )
|
|
161
|
-
this.
|
|
162
|
-
this.
|
|
163
|
-
this.
|
|
164
|
-
this.
|
|
165
|
-
this.
|
|
166
|
-
this.
|
|
167
|
-
this.
|
|
236
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
237
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
238
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)};`, { connection });
|
|
239
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid");`, { connection });
|
|
240
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)};`, { connection });
|
|
241
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name");`, { connection });
|
|
242
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)};`, { connection });
|
|
243
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name", "reference");`, { connection });
|
|
244
|
+
// Create the unique strings table.
|
|
245
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} (
|
|
246
|
+
"guid" BYTEA NOT NULL,
|
|
247
|
+
"unique" TEXT NOT NULL UNIQUE,
|
|
248
|
+
PRIMARY KEY ("guid", "unique"),
|
|
249
|
+
FOREIGN KEY ("guid")
|
|
250
|
+
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
251
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
168
252
|
}
|
|
169
253
|
else {
|
|
170
|
-
|
|
254
|
+
// Add trigram extensions.
|
|
255
|
+
try {
|
|
256
|
+
await this.queryRun(`CREATE EXTENSION pg_trgm;`, { connection });
|
|
257
|
+
}
|
|
258
|
+
catch (e) {
|
|
259
|
+
// Ignore errors.
|
|
260
|
+
}
|
|
261
|
+
// Create the UID table.
|
|
262
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uids`)} (
|
|
171
263
|
"name" TEXT NOT NULL,
|
|
172
264
|
"cur_uid" BIGINT NOT NULL,
|
|
173
265
|
PRIMARY KEY ("name")
|
|
174
|
-
) WITH ( OIDS = FALSE )
|
|
175
|
-
this.
|
|
266
|
+
) WITH ( OIDS = FALSE );`, { connection });
|
|
267
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}uids`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
176
268
|
}
|
|
269
|
+
connection.done();
|
|
177
270
|
return true;
|
|
178
271
|
}
|
|
179
272
|
translateQuery(origQuery, origParams) {
|
|
@@ -191,13 +284,15 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
191
284
|
}
|
|
192
285
|
async query(runQuery, query, etypes = []) {
|
|
193
286
|
try {
|
|
287
|
+
this.nymph.config.debugInfo('postgresql:query', query);
|
|
194
288
|
return await runQuery();
|
|
195
289
|
}
|
|
196
290
|
catch (e) {
|
|
197
291
|
const errorCode = e?.code;
|
|
198
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
292
|
+
if (errorCode === '42P01' && (await this.createTables())) {
|
|
293
|
+
// If the tables don't exist yet, create them.
|
|
199
294
|
for (let etype of etypes) {
|
|
200
|
-
this.createTables(etype);
|
|
295
|
+
await this.createTables(etype);
|
|
201
296
|
}
|
|
202
297
|
try {
|
|
203
298
|
return await runQuery();
|
|
@@ -206,30 +301,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
206
301
|
throw new nymph_1.QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query);
|
|
207
302
|
}
|
|
208
303
|
}
|
|
209
|
-
else {
|
|
210
|
-
throw
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
querySync(runQuery, query, etypes = []) {
|
|
215
|
-
try {
|
|
216
|
-
return runQuery();
|
|
217
|
-
}
|
|
218
|
-
catch (e) {
|
|
219
|
-
const errorCode = e?.code;
|
|
220
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
221
|
-
for (let etype of etypes) {
|
|
222
|
-
this.createTables(etype);
|
|
223
|
-
}
|
|
224
|
-
try {
|
|
225
|
-
return runQuery();
|
|
226
|
-
}
|
|
227
|
-
catch (e2) {
|
|
228
|
-
throw new nymph_1.QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query);
|
|
229
|
-
}
|
|
304
|
+
else if (errorCode === '23505') {
|
|
305
|
+
throw new nymph_1.EntityUniqueConstraintError(`Unique constraint violation.`);
|
|
230
306
|
}
|
|
231
307
|
else {
|
|
232
|
-
throw e;
|
|
308
|
+
throw new nymph_1.QueryFailedError('Query failed: ' + e?.code + ' - ' + e?.message, query);
|
|
233
309
|
}
|
|
234
310
|
}
|
|
235
311
|
}
|
|
@@ -249,35 +325,6 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
249
325
|
return results.rows;
|
|
250
326
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
251
327
|
}
|
|
252
|
-
queryIterSync(query, { etypes = [], params = {}, } = {}) {
|
|
253
|
-
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
254
|
-
return this.querySync(() => {
|
|
255
|
-
const output = child_process_1.default.spawnSync(process.argv0, [__dirname + '/runPostgresqlSync.js'], {
|
|
256
|
-
input: JSON.stringify({
|
|
257
|
-
postgresqlConfig: this.postgresqlConfig,
|
|
258
|
-
query: newQuery,
|
|
259
|
-
params: newParams,
|
|
260
|
-
}),
|
|
261
|
-
timeout: 30000,
|
|
262
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
263
|
-
encoding: 'utf8',
|
|
264
|
-
windowsHide: true,
|
|
265
|
-
});
|
|
266
|
-
try {
|
|
267
|
-
return JSON.parse(output.stdout).rows;
|
|
268
|
-
}
|
|
269
|
-
catch (e) {
|
|
270
|
-
}
|
|
271
|
-
if (output.status === 0) {
|
|
272
|
-
throw new Error('Unknown parse error.');
|
|
273
|
-
}
|
|
274
|
-
const err = JSON.parse(output.stderr);
|
|
275
|
-
const e = new Error(err.name);
|
|
276
|
-
for (const name in err) {
|
|
277
|
-
e[name] = err[name];
|
|
278
|
-
}
|
|
279
|
-
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
280
|
-
}
|
|
281
328
|
queryGet(query, { etypes = [], params = {}, } = {}) {
|
|
282
329
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
283
330
|
return this.query(async () => {
|
|
@@ -294,12 +341,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
294
341
|
return results.rows[0];
|
|
295
342
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
296
343
|
}
|
|
297
|
-
queryRun(query, { etypes = [], params = {}, } = {}) {
|
|
344
|
+
queryRun(query, { etypes = [], params = {}, connection, } = {}) {
|
|
298
345
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
299
346
|
return this.query(async () => {
|
|
300
347
|
const results = await new Promise((resolve, reject) => {
|
|
301
348
|
try {
|
|
302
|
-
(this.transaction?.connection?.client ??
|
|
349
|
+
((connection ?? this.transaction?.connection)?.client ??
|
|
350
|
+
this.link)
|
|
303
351
|
.query(newQuery, newParams)
|
|
304
352
|
.then((results) => resolve(results), (error) => reject(error));
|
|
305
353
|
}
|
|
@@ -310,36 +358,6 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
310
358
|
return { rowCount: results.rowCount ?? 0 };
|
|
311
359
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
312
360
|
}
|
|
313
|
-
queryRunSync(query, { etypes = [], params = {}, } = {}) {
|
|
314
|
-
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
315
|
-
return this.querySync(() => {
|
|
316
|
-
const output = child_process_1.default.spawnSync(process.argv0, [__dirname + '/runPostgresqlSync.js'], {
|
|
317
|
-
input: JSON.stringify({
|
|
318
|
-
postgresqlConfig: this.postgresqlConfig,
|
|
319
|
-
query: newQuery,
|
|
320
|
-
params: newParams,
|
|
321
|
-
}),
|
|
322
|
-
timeout: 30000,
|
|
323
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
324
|
-
encoding: 'utf8',
|
|
325
|
-
windowsHide: true,
|
|
326
|
-
});
|
|
327
|
-
try {
|
|
328
|
-
const results = JSON.parse(output.stdout);
|
|
329
|
-
return { rowCount: results.rowCount ?? 0 };
|
|
330
|
-
}
|
|
331
|
-
catch (e) {
|
|
332
|
-
}
|
|
333
|
-
if (output.status === 0) {
|
|
334
|
-
throw new Error('Unknown parse error.');
|
|
335
|
-
}
|
|
336
|
-
const err = JSON.parse(output.stderr);
|
|
337
|
-
const e = new Error(err.name);
|
|
338
|
-
for (const name in err) {
|
|
339
|
-
e[name] = err[name];
|
|
340
|
-
}
|
|
341
|
-
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
342
|
-
}
|
|
343
361
|
async commit(name) {
|
|
344
362
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
345
363
|
throw new nymph_1.InvalidParametersError('Transaction commit attempted without a name.');
|
|
@@ -382,28 +400,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
382
400
|
guid,
|
|
383
401
|
},
|
|
384
402
|
});
|
|
385
|
-
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
403
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
386
404
|
etypes: [etype],
|
|
387
405
|
params: {
|
|
388
406
|
guid,
|
|
389
407
|
},
|
|
390
408
|
});
|
|
391
|
-
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
409
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
392
410
|
etypes: [etype],
|
|
393
411
|
params: {
|
|
394
412
|
guid,
|
|
395
413
|
},
|
|
396
414
|
});
|
|
397
|
-
await this.commit('nymph-delete');
|
|
398
|
-
if (this.nymph.config.cache) {
|
|
399
|
-
this.cleanCache(guid);
|
|
400
|
-
}
|
|
401
|
-
return true;
|
|
402
415
|
}
|
|
403
416
|
catch (e) {
|
|
417
|
+
this.nymph.config.debugError('postgresql', `Delete entity error: "${e}"`);
|
|
404
418
|
await this.rollback('nymph-delete');
|
|
405
419
|
throw e;
|
|
406
420
|
}
|
|
421
|
+
await this.commit('nymph-delete');
|
|
422
|
+
// Remove any cached versions of this entity.
|
|
423
|
+
if (this.nymph.config.cache) {
|
|
424
|
+
this.cleanCache(guid);
|
|
425
|
+
}
|
|
426
|
+
return true;
|
|
407
427
|
}
|
|
408
428
|
async deleteUID(name) {
|
|
409
429
|
if (!name) {
|
|
@@ -416,79 +436,124 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
416
436
|
});
|
|
417
437
|
return true;
|
|
418
438
|
}
|
|
419
|
-
async
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
439
|
+
async *exportDataIterator() {
|
|
440
|
+
if (yield {
|
|
441
|
+
type: 'comment',
|
|
442
|
+
content: `#nex2
|
|
443
|
+
# Nymph Entity Exchange v2
|
|
444
|
+
# http://nymph.io
|
|
445
|
+
#
|
|
446
|
+
# Generation Time: ${new Date().toLocaleString()}
|
|
447
|
+
`,
|
|
448
|
+
}) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (yield {
|
|
452
|
+
type: 'comment',
|
|
453
|
+
content: `
|
|
454
|
+
|
|
455
|
+
#
|
|
456
|
+
# UIDs
|
|
457
|
+
#
|
|
458
|
+
|
|
459
|
+
`,
|
|
460
|
+
}) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
// Export UIDs.
|
|
430
464
|
let uids = await this.queryIter(`SELECT * FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ORDER BY "name";`);
|
|
431
465
|
for (const uid of uids) {
|
|
432
|
-
|
|
466
|
+
if (yield { type: 'uid', content: `<${uid.name}>[${uid.cur_uid}]\n` }) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
433
469
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
470
|
+
if (yield {
|
|
471
|
+
type: 'comment',
|
|
472
|
+
content: `
|
|
473
|
+
|
|
474
|
+
#
|
|
475
|
+
# Entities
|
|
476
|
+
#
|
|
477
|
+
|
|
478
|
+
`,
|
|
479
|
+
}) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
// Get the etypes.
|
|
483
|
+
const tables = await this.queryIter('SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix;', {
|
|
484
|
+
params: {
|
|
485
|
+
db: this.config.database,
|
|
486
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
487
|
+
},
|
|
488
|
+
});
|
|
440
489
|
const etypes = [];
|
|
441
|
-
for (const
|
|
442
|
-
|
|
443
|
-
if (table.startsWith(this.prefix + 'entities_')) {
|
|
444
|
-
etypes.push(table.substr((this.prefix + 'entities_').length));
|
|
445
|
-
}
|
|
490
|
+
for (const table of tables) {
|
|
491
|
+
etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
|
|
446
492
|
}
|
|
447
493
|
for (const etype of etypes) {
|
|
448
|
-
|
|
494
|
+
// Export entities.
|
|
495
|
+
const dataIterator = (await this.queryIter(`SELECT encode(e."guid", 'hex') AS "guid", e."tags", e."cdate", e."mdate", d."name", d."value", d."json", d."string", d."number"
|
|
449
496
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} e
|
|
450
497
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} d ON e."guid"=d."guid"
|
|
451
|
-
INNER JOIN ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} c ON d."guid"=c."guid" AND d."name"=c."name"
|
|
452
498
|
ORDER BY e."guid";`))[Symbol.iterator]();
|
|
453
499
|
let datum = dataIterator.next();
|
|
454
500
|
while (!datum.done) {
|
|
455
501
|
const guid = datum.value.guid;
|
|
456
|
-
const tags = datum.value.tags.join(',');
|
|
502
|
+
const tags = datum.value.tags.filter((tag) => tag).join(',');
|
|
457
503
|
const cdate = datum.value.cdate;
|
|
458
504
|
const mdate = datum.value.mdate;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
505
|
+
let currentEntityExport = [];
|
|
506
|
+
currentEntityExport.push(`{${guid}}<${etype}>[${tags}]`);
|
|
507
|
+
currentEntityExport.push(`\tcdate=${JSON.stringify(cdate)}`);
|
|
508
|
+
currentEntityExport.push(`\tmdate=${JSON.stringify(mdate)}`);
|
|
509
|
+
if (datum.value.name != null) {
|
|
510
|
+
// This do will keep going and adding the data until the
|
|
511
|
+
// next entity is reached. $row will end on the next entity.
|
|
463
512
|
do {
|
|
464
|
-
const value = datum.value.
|
|
513
|
+
const value = datum.value.value === 'N'
|
|
465
514
|
? JSON.stringify(Number(datum.value.number))
|
|
466
|
-
: datum.value.
|
|
467
|
-
? JSON.stringify(datum.value.string)
|
|
468
|
-
: datum.value.
|
|
469
|
-
|
|
515
|
+
: datum.value.value === 'S'
|
|
516
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(datum.value.string))
|
|
517
|
+
: datum.value.value === 'J'
|
|
518
|
+
? PostgreSQLDriver.unescapeNullSequences(JSON.stringify(datum.value.json))
|
|
519
|
+
: datum.value.value;
|
|
520
|
+
currentEntityExport.push(`\t${datum.value.name}=${value}`);
|
|
470
521
|
datum = dataIterator.next();
|
|
471
522
|
} while (!datum.done && datum.value.guid === guid);
|
|
472
523
|
}
|
|
473
524
|
else {
|
|
525
|
+
// Make sure that datum is incremented :)
|
|
474
526
|
datum = dataIterator.next();
|
|
475
527
|
}
|
|
528
|
+
currentEntityExport.push('');
|
|
529
|
+
if (yield { type: 'entity', content: currentEntityExport.join('\n') }) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
476
532
|
}
|
|
477
533
|
}
|
|
478
|
-
return;
|
|
479
534
|
}
|
|
480
|
-
|
|
535
|
+
/**
|
|
536
|
+
* Generate the PostgreSQL query.
|
|
537
|
+
* @param options The options array.
|
|
538
|
+
* @param formattedSelectors The formatted selector array.
|
|
539
|
+
* @param etype
|
|
540
|
+
* @param count Used to track internal params.
|
|
541
|
+
* @param params Used to store internal params.
|
|
542
|
+
* @param subquery Whether only a subquery should be returned.
|
|
543
|
+
* @returns The SQL query.
|
|
544
|
+
*/
|
|
545
|
+
makeEntityQuery(options, formattedSelectors, etype, count = { i: 0 }, params = {}, subquery = false, tableSuffix = '', etypes = [], guidSelector = undefined) {
|
|
481
546
|
if (typeof options.class?.alterOptions === 'function') {
|
|
482
547
|
options = options.class.alterOptions(options);
|
|
483
548
|
}
|
|
484
549
|
const eTable = `e${tableSuffix}`;
|
|
485
550
|
const dTable = `d${tableSuffix}`;
|
|
486
|
-
const cTable = `c${tableSuffix}`;
|
|
487
551
|
const fTable = `f${tableSuffix}`;
|
|
488
552
|
const ieTable = `ie${tableSuffix}`;
|
|
489
553
|
const countTable = `count${tableSuffix}`;
|
|
554
|
+
const sTable = `s${tableSuffix}`;
|
|
490
555
|
const sort = options.sort ?? 'cdate';
|
|
491
|
-
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, (key, value, typeIsOr, typeIsNot) => {
|
|
556
|
+
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, ({ key, value, typeIsOr, typeIsNot }) => {
|
|
492
557
|
const clauseNot = key.startsWith('!');
|
|
493
558
|
let curQuery = '';
|
|
494
559
|
for (const curValue of value) {
|
|
@@ -532,12 +597,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
532
597
|
}
|
|
533
598
|
const name = `param${++count.i}`;
|
|
534
599
|
curQuery +=
|
|
535
|
-
|
|
536
|
-
'
|
|
537
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
538
|
-
'IN (SELECT "guid" FROM ' +
|
|
600
|
+
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
601
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
539
602
|
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
540
|
-
' WHERE "
|
|
603
|
+
' WHERE "guid"=' +
|
|
604
|
+
ieTable +
|
|
605
|
+
'."guid" AND "name"=@' +
|
|
541
606
|
name +
|
|
542
607
|
')';
|
|
543
608
|
params[name] = curVar;
|
|
@@ -569,10 +634,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
569
634
|
const name = `param${++count.i}`;
|
|
570
635
|
curQuery +=
|
|
571
636
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
637
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
638
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
639
|
+
' WHERE "guid"=' +
|
|
572
640
|
ieTable +
|
|
573
|
-
'."guid"
|
|
574
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
575
|
-
' WHERE "name"=@' +
|
|
641
|
+
'."guid" AND "name"=@' +
|
|
576
642
|
name +
|
|
577
643
|
' AND "truthy"=TRUE)';
|
|
578
644
|
params[name] = curVar;
|
|
@@ -619,10 +685,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
619
685
|
const value = `param${++count.i}`;
|
|
620
686
|
curQuery +=
|
|
621
687
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
688
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
689
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
690
|
+
' WHERE "guid"=' +
|
|
622
691
|
ieTable +
|
|
623
|
-
'."guid"
|
|
624
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
625
|
-
' WHERE "name"=@' +
|
|
692
|
+
'."guid" AND "name"=@' +
|
|
626
693
|
name +
|
|
627
694
|
' AND "number"=@' +
|
|
628
695
|
value +
|
|
@@ -638,16 +705,19 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
638
705
|
const value = `param${++count.i}`;
|
|
639
706
|
curQuery +=
|
|
640
707
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
708
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
709
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
710
|
+
' WHERE "guid"=' +
|
|
641
711
|
ieTable +
|
|
642
|
-
'."guid"
|
|
643
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
644
|
-
' WHERE "name"=@' +
|
|
712
|
+
'."guid" AND "name"=@' +
|
|
645
713
|
name +
|
|
646
|
-
' AND "string"
|
|
647
|
-
|
|
714
|
+
' AND "string"=' +
|
|
715
|
+
(curValue[1].length < 512
|
|
716
|
+
? 'LEFT(@' + value + ', 512)'
|
|
717
|
+
: '@' + value) +
|
|
648
718
|
')';
|
|
649
719
|
params[name] = curValue[0];
|
|
650
|
-
params[value] = curValue[1];
|
|
720
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
651
721
|
}
|
|
652
722
|
else {
|
|
653
723
|
if (curQuery) {
|
|
@@ -665,16 +735,17 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
665
735
|
const value = `param${++count.i}`;
|
|
666
736
|
curQuery +=
|
|
667
737
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
668
|
-
|
|
669
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
738
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
670
739
|
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
671
|
-
' WHERE "
|
|
740
|
+
' WHERE "guid"=' +
|
|
741
|
+
ieTable +
|
|
742
|
+
'."guid" AND "name"=@' +
|
|
672
743
|
name +
|
|
673
|
-
' AND "
|
|
744
|
+
' AND "json"=@' +
|
|
674
745
|
value +
|
|
675
746
|
')';
|
|
676
747
|
params[name] = curValue[0];
|
|
677
|
-
params[value] = svalue;
|
|
748
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
678
749
|
}
|
|
679
750
|
break;
|
|
680
751
|
case 'contain':
|
|
@@ -687,7 +758,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
687
758
|
curQuery +=
|
|
688
759
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
689
760
|
ieTable +
|
|
690
|
-
'."cdate"
|
|
761
|
+
'."cdate"=@' +
|
|
691
762
|
cdate;
|
|
692
763
|
params[cdate] = isNaN(Number(curValue[1]))
|
|
693
764
|
? null
|
|
@@ -702,7 +773,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
702
773
|
curQuery +=
|
|
703
774
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
704
775
|
ieTable +
|
|
705
|
-
'."mdate"
|
|
776
|
+
'."mdate"=@' +
|
|
706
777
|
mdate;
|
|
707
778
|
params[mdate] = isNaN(Number(curValue[1]))
|
|
708
779
|
? null
|
|
@@ -714,55 +785,32 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
714
785
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
715
786
|
}
|
|
716
787
|
let svalue;
|
|
717
|
-
let stringValue;
|
|
718
788
|
if (curValue[1] instanceof Object &&
|
|
719
789
|
typeof curValue[1].toReference === 'function') {
|
|
720
790
|
svalue = JSON.stringify(curValue[1].toReference());
|
|
721
|
-
stringValue = `${curValue[1].toReference()}`;
|
|
722
791
|
}
|
|
723
|
-
else
|
|
792
|
+
else if (typeof curValue[1] === 'string' ||
|
|
793
|
+
typeof curValue[1] === 'number') {
|
|
724
794
|
svalue = JSON.stringify(curValue[1]);
|
|
725
|
-
stringValue = `${curValue[1]}`;
|
|
726
|
-
}
|
|
727
|
-
const name = `param${++count.i}`;
|
|
728
|
-
const value = `param${++count.i}`;
|
|
729
|
-
if (typeof curValue[1] === 'string') {
|
|
730
|
-
const stringParam = `param${++count.i}`;
|
|
731
|
-
curQuery +=
|
|
732
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
733
|
-
'(' +
|
|
734
|
-
ieTable +
|
|
735
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
736
|
-
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
737
|
-
' WHERE "name"=@' +
|
|
738
|
-
name +
|
|
739
|
-
' AND position(@' +
|
|
740
|
-
value +
|
|
741
|
-
' IN "value")>0) OR ' +
|
|
742
|
-
ieTable +
|
|
743
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
744
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
745
|
-
' WHERE "name"=@' +
|
|
746
|
-
name +
|
|
747
|
-
' AND "string"=@' +
|
|
748
|
-
stringParam +
|
|
749
|
-
'))';
|
|
750
|
-
params[stringParam] = stringValue;
|
|
751
795
|
}
|
|
752
796
|
else {
|
|
753
|
-
|
|
754
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
755
|
-
ieTable +
|
|
756
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
757
|
-
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
758
|
-
' WHERE "name"=@' +
|
|
759
|
-
name +
|
|
760
|
-
' AND position(@' +
|
|
761
|
-
value +
|
|
762
|
-
' IN "value")>0)';
|
|
797
|
+
svalue = JSON.stringify([curValue[1]]);
|
|
763
798
|
}
|
|
799
|
+
const name = `param${++count.i}`;
|
|
800
|
+
const value = `param${++count.i}`;
|
|
801
|
+
curQuery +=
|
|
802
|
+
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
803
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
804
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
805
|
+
' WHERE "guid"=' +
|
|
806
|
+
ieTable +
|
|
807
|
+
'."guid" AND "name"=@' +
|
|
808
|
+
name +
|
|
809
|
+
' AND "json" @> @' +
|
|
810
|
+
value +
|
|
811
|
+
')';
|
|
764
812
|
params[name] = curValue[0];
|
|
765
|
-
params[value] = svalue;
|
|
813
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
766
814
|
}
|
|
767
815
|
break;
|
|
768
816
|
case 'match':
|
|
@@ -805,16 +853,17 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
805
853
|
const value = `param${++count.i}`;
|
|
806
854
|
curQuery +=
|
|
807
855
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
856
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
857
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
858
|
+
' WHERE "guid"=' +
|
|
808
859
|
ieTable +
|
|
809
|
-
'."guid"
|
|
810
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
811
|
-
' WHERE "name"=@' +
|
|
860
|
+
'."guid" AND "name"=@' +
|
|
812
861
|
name +
|
|
813
862
|
' AND "string" ~ @' +
|
|
814
863
|
value +
|
|
815
864
|
')';
|
|
816
865
|
params[name] = curValue[0];
|
|
817
|
-
params[value] = curValue[1];
|
|
866
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
818
867
|
}
|
|
819
868
|
break;
|
|
820
869
|
case 'imatch':
|
|
@@ -857,16 +906,17 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
857
906
|
const value = `param${++count.i}`;
|
|
858
907
|
curQuery +=
|
|
859
908
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
909
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
910
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
911
|
+
' WHERE "guid"=' +
|
|
860
912
|
ieTable +
|
|
861
|
-
'."guid"
|
|
862
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
863
|
-
' WHERE "name"=@' +
|
|
913
|
+
'."guid" AND "name"=@' +
|
|
864
914
|
name +
|
|
865
915
|
' AND "string" ~* @' +
|
|
866
916
|
value +
|
|
867
917
|
')';
|
|
868
918
|
params[name] = curValue[0];
|
|
869
|
-
params[value] = curValue[1];
|
|
919
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
870
920
|
}
|
|
871
921
|
break;
|
|
872
922
|
case 'like':
|
|
@@ -909,16 +959,17 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
909
959
|
const value = `param${++count.i}`;
|
|
910
960
|
curQuery +=
|
|
911
961
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
962
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
963
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
964
|
+
' WHERE "guid"=' +
|
|
912
965
|
ieTable +
|
|
913
|
-
'."guid"
|
|
914
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
915
|
-
' WHERE "name"=@' +
|
|
966
|
+
'."guid" AND "name"=@' +
|
|
916
967
|
name +
|
|
917
968
|
' AND "string" LIKE @' +
|
|
918
969
|
value +
|
|
919
970
|
')';
|
|
920
971
|
params[name] = curValue[0];
|
|
921
|
-
params[value] = curValue[1];
|
|
972
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
922
973
|
}
|
|
923
974
|
break;
|
|
924
975
|
case 'ilike':
|
|
@@ -961,16 +1012,17 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
961
1012
|
const value = `param${++count.i}`;
|
|
962
1013
|
curQuery +=
|
|
963
1014
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1015
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1016
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1017
|
+
' WHERE "guid"=' +
|
|
964
1018
|
ieTable +
|
|
965
|
-
'."guid"
|
|
966
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
967
|
-
' WHERE "name"=@' +
|
|
1019
|
+
'."guid" AND "name"=@' +
|
|
968
1020
|
name +
|
|
969
1021
|
' AND "string" ILIKE @' +
|
|
970
1022
|
value +
|
|
971
1023
|
')';
|
|
972
1024
|
params[name] = curValue[0];
|
|
973
|
-
params[value] = curValue[1];
|
|
1025
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
974
1026
|
}
|
|
975
1027
|
break;
|
|
976
1028
|
case 'gt':
|
|
@@ -1013,10 +1065,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1013
1065
|
const value = `param${++count.i}`;
|
|
1014
1066
|
curQuery +=
|
|
1015
1067
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1068
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1069
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1070
|
+
' WHERE "guid"=' +
|
|
1016
1071
|
ieTable +
|
|
1017
|
-
'."guid"
|
|
1018
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1019
|
-
' WHERE "name"=@' +
|
|
1072
|
+
'."guid" AND "name"=@' +
|
|
1020
1073
|
name +
|
|
1021
1074
|
' AND "number">@' +
|
|
1022
1075
|
value +
|
|
@@ -1067,10 +1120,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1067
1120
|
const value = `param${++count.i}`;
|
|
1068
1121
|
curQuery +=
|
|
1069
1122
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1123
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1124
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1125
|
+
' WHERE "guid"=' +
|
|
1070
1126
|
ieTable +
|
|
1071
|
-
'."guid"
|
|
1072
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1073
|
-
' WHERE "name"=@' +
|
|
1127
|
+
'."guid" AND "name"=@' +
|
|
1074
1128
|
name +
|
|
1075
1129
|
' AND "number">=@' +
|
|
1076
1130
|
value +
|
|
@@ -1121,10 +1175,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1121
1175
|
const value = `param${++count.i}`;
|
|
1122
1176
|
curQuery +=
|
|
1123
1177
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1178
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1179
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1180
|
+
' WHERE "guid"=' +
|
|
1124
1181
|
ieTable +
|
|
1125
|
-
'."guid"
|
|
1126
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1127
|
-
' WHERE "name"=@' +
|
|
1182
|
+
'."guid" AND "name"=@' +
|
|
1128
1183
|
name +
|
|
1129
1184
|
' AND "number"<@' +
|
|
1130
1185
|
value +
|
|
@@ -1175,10 +1230,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1175
1230
|
const value = `param${++count.i}`;
|
|
1176
1231
|
curQuery +=
|
|
1177
1232
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1233
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1234
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1235
|
+
' WHERE "guid"=' +
|
|
1178
1236
|
ieTable +
|
|
1179
|
-
'."guid"
|
|
1180
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1181
|
-
' WHERE "name"=@' +
|
|
1237
|
+
'."guid" AND "name"=@' +
|
|
1182
1238
|
name +
|
|
1183
1239
|
' AND "number"<=@' +
|
|
1184
1240
|
value +
|
|
@@ -1208,10 +1264,11 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1208
1264
|
const guid = `param${++count.i}`;
|
|
1209
1265
|
curQuery +=
|
|
1210
1266
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1211
|
-
|
|
1212
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1267
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1213
1268
|
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1214
|
-
' WHERE "
|
|
1269
|
+
' WHERE "guid"=' +
|
|
1270
|
+
ieTable +
|
|
1271
|
+
'."guid" AND "name"=@' +
|
|
1215
1272
|
name +
|
|
1216
1273
|
' AND "reference"=decode(@' +
|
|
1217
1274
|
guid +
|
|
@@ -1233,22 +1290,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1233
1290
|
break;
|
|
1234
1291
|
case 'qref':
|
|
1235
1292
|
case '!qref':
|
|
1293
|
+
const referenceTableSuffix = (0, guid_1.makeTableSuffix)();
|
|
1236
1294
|
const [qrefOptions, ...qrefSelectors] = curValue[1];
|
|
1237
1295
|
const QrefEntityClass = qrefOptions.class;
|
|
1238
1296
|
etypes.push(QrefEntityClass.ETYPE);
|
|
1239
|
-
const qrefQuery = this.makeEntityQuery({ ...qrefOptions, return: 'guid', class: QrefEntityClass }, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, (0, guid_1.makeTableSuffix)(), etypes);
|
|
1297
|
+
const qrefQuery = this.makeEntityQuery({ ...qrefOptions, return: 'guid', class: QrefEntityClass }, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, (0, guid_1.makeTableSuffix)(), etypes, 'r' + referenceTableSuffix + '."reference"');
|
|
1240
1298
|
if (curQuery) {
|
|
1241
1299
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1242
1300
|
}
|
|
1243
1301
|
const qrefName = `param${++count.i}`;
|
|
1244
1302
|
curQuery +=
|
|
1245
1303
|
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1246
|
-
|
|
1247
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1304
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1248
1305
|
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1249
|
-
'
|
|
1306
|
+
' r' +
|
|
1307
|
+
referenceTableSuffix +
|
|
1308
|
+
' WHERE r' +
|
|
1309
|
+
referenceTableSuffix +
|
|
1310
|
+
'."guid"=' +
|
|
1311
|
+
ieTable +
|
|
1312
|
+
'."guid" AND r' +
|
|
1313
|
+
referenceTableSuffix +
|
|
1314
|
+
'."name"=@' +
|
|
1250
1315
|
qrefName +
|
|
1251
|
-
' AND
|
|
1316
|
+
' AND EXISTS (' +
|
|
1252
1317
|
qrefQuery.query +
|
|
1253
1318
|
'))';
|
|
1254
1319
|
params[qrefName] = curValue[0];
|
|
@@ -1258,18 +1323,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1258
1323
|
return curQuery;
|
|
1259
1324
|
});
|
|
1260
1325
|
let sortBy;
|
|
1326
|
+
let sortByInner;
|
|
1327
|
+
let sortJoin = '';
|
|
1328
|
+
let sortJoinInner = '';
|
|
1329
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1261
1330
|
switch (sort) {
|
|
1262
1331
|
case 'mdate':
|
|
1263
|
-
sortBy =
|
|
1332
|
+
sortBy = `${eTable}."mdate"${order}`;
|
|
1333
|
+
sortByInner = `${ieTable}."mdate"${order}`;
|
|
1264
1334
|
break;
|
|
1265
1335
|
case 'cdate':
|
|
1336
|
+
sortBy = `${eTable}."cdate"${order}`;
|
|
1337
|
+
sortByInner = `${ieTable}."cdate"${order}`;
|
|
1338
|
+
break;
|
|
1266
1339
|
default:
|
|
1267
|
-
|
|
1340
|
+
const name = `param${++count.i}`;
|
|
1341
|
+
sortJoin = `LEFT JOIN (
|
|
1342
|
+
SELECT "guid", "string", "number"
|
|
1343
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1344
|
+
WHERE "name"=@${name}
|
|
1345
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1346
|
+
) ${sTable} ON ${eTable}."guid"=${sTable}."guid"`;
|
|
1347
|
+
sortJoinInner = `LEFT JOIN (
|
|
1348
|
+
SELECT "guid", "string", "number"
|
|
1349
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1350
|
+
WHERE "name"=@${name}
|
|
1351
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1352
|
+
) ${sTable} ON ${ieTable}."guid"=${sTable}."guid"`;
|
|
1353
|
+
sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1354
|
+
sortByInner = sortBy;
|
|
1355
|
+
params[name] = sort;
|
|
1268
1356
|
break;
|
|
1269
1357
|
}
|
|
1270
|
-
if (options.reverse) {
|
|
1271
|
-
sortBy += ' DESC';
|
|
1272
|
-
}
|
|
1273
1358
|
let query;
|
|
1274
1359
|
if (queryParts.length) {
|
|
1275
1360
|
if (subquery) {
|
|
@@ -1285,18 +1370,21 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1285
1370
|
offset = ` OFFSET ${Math.floor(isNaN(Number(options.offset)) ? 0 : Number(options.offset))}`;
|
|
1286
1371
|
}
|
|
1287
1372
|
const whereClause = queryParts.join(') AND (');
|
|
1373
|
+
const guidClause = guidSelector
|
|
1374
|
+
? `${ieTable}."guid"=${guidSelector} AND `
|
|
1375
|
+
: '';
|
|
1288
1376
|
if (options.return === 'count') {
|
|
1289
1377
|
if (limit || offset) {
|
|
1290
1378
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1291
|
-
SELECT
|
|
1379
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1292
1380
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1293
|
-
WHERE (${whereClause})${limit}${offset}
|
|
1381
|
+
WHERE ${guidClause}(${whereClause})${limit}${offset}
|
|
1294
1382
|
) ${countTable}`;
|
|
1295
1383
|
}
|
|
1296
1384
|
else {
|
|
1297
1385
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1298
1386
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1299
|
-
WHERE (${whereClause})`;
|
|
1387
|
+
WHERE ${guidClause}(${whereClause})`;
|
|
1300
1388
|
}
|
|
1301
1389
|
}
|
|
1302
1390
|
else if (options.return === 'guid') {
|
|
@@ -1305,8 +1393,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1305
1393
|
: `${ieTable}."guid"`;
|
|
1306
1394
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1307
1395
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1308
|
-
|
|
1309
|
-
|
|
1396
|
+
${sortJoinInner}
|
|
1397
|
+
WHERE ${guidClause}(${whereClause})
|
|
1398
|
+
ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
|
|
1310
1399
|
}
|
|
1311
1400
|
else {
|
|
1312
1401
|
query = `SELECT
|
|
@@ -1316,18 +1405,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1316
1405
|
${eTable}."mdate",
|
|
1317
1406
|
${dTable}."name",
|
|
1318
1407
|
${dTable}."value",
|
|
1319
|
-
${
|
|
1320
|
-
${
|
|
1408
|
+
${dTable}."json",
|
|
1409
|
+
${dTable}."string",
|
|
1410
|
+
${dTable}."number"
|
|
1321
1411
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1322
1412
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1323
|
-
|
|
1413
|
+
${sortJoin}
|
|
1324
1414
|
INNER JOIN (
|
|
1325
1415
|
SELECT ${ieTable}."guid"
|
|
1326
1416
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1327
|
-
|
|
1328
|
-
|
|
1417
|
+
${sortJoinInner}
|
|
1418
|
+
WHERE ${guidClause}(${whereClause})
|
|
1419
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1329
1420
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1330
|
-
ORDER BY ${
|
|
1421
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1331
1422
|
}
|
|
1332
1423
|
}
|
|
1333
1424
|
}
|
|
@@ -1344,16 +1435,19 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1344
1435
|
if ('offset' in options) {
|
|
1345
1436
|
offset = ` OFFSET ${Math.floor(isNaN(Number(options.offset)) ? 0 : Number(options.offset))}`;
|
|
1346
1437
|
}
|
|
1438
|
+
const guidClause = guidSelector
|
|
1439
|
+
? ` WHERE ${ieTable}."guid"=${guidSelector}`
|
|
1440
|
+
: '';
|
|
1347
1441
|
if (options.return === 'count') {
|
|
1348
1442
|
if (limit || offset) {
|
|
1349
1443
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1350
|
-
SELECT
|
|
1351
|
-
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${limit}${offset}
|
|
1444
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1445
|
+
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${guidClause}${limit}${offset}
|
|
1352
1446
|
) ${countTable}`;
|
|
1353
1447
|
}
|
|
1354
1448
|
else {
|
|
1355
1449
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1356
|
-
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}`;
|
|
1450
|
+
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${guidClause}`;
|
|
1357
1451
|
}
|
|
1358
1452
|
}
|
|
1359
1453
|
else if (options.return === 'guid') {
|
|
@@ -1362,7 +1456,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1362
1456
|
: `${ieTable}."guid"`;
|
|
1363
1457
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1364
1458
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1365
|
-
|
|
1459
|
+
${sortJoinInner}
|
|
1460
|
+
${guidClause}
|
|
1461
|
+
ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
|
|
1366
1462
|
}
|
|
1367
1463
|
else {
|
|
1368
1464
|
if (limit || offset) {
|
|
@@ -1373,17 +1469,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1373
1469
|
${eTable}."mdate",
|
|
1374
1470
|
${dTable}."name",
|
|
1375
1471
|
${dTable}."value",
|
|
1376
|
-
${
|
|
1377
|
-
${
|
|
1472
|
+
${dTable}."json",
|
|
1473
|
+
${dTable}."string",
|
|
1474
|
+
${dTable}."number"
|
|
1378
1475
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1379
1476
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1380
|
-
|
|
1477
|
+
${sortJoin}
|
|
1381
1478
|
INNER JOIN (
|
|
1382
1479
|
SELECT ${ieTable}."guid"
|
|
1383
1480
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1384
|
-
|
|
1481
|
+
${sortJoinInner}
|
|
1482
|
+
${guidClause}
|
|
1483
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1385
1484
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1386
|
-
ORDER BY ${
|
|
1485
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1387
1486
|
}
|
|
1388
1487
|
else {
|
|
1389
1488
|
query = `SELECT
|
|
@@ -1393,12 +1492,14 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1393
1492
|
${eTable}."mdate",
|
|
1394
1493
|
${dTable}."name",
|
|
1395
1494
|
${dTable}."value",
|
|
1396
|
-
${
|
|
1397
|
-
${
|
|
1495
|
+
${dTable}."json",
|
|
1496
|
+
${dTable}."string",
|
|
1497
|
+
${dTable}."number"
|
|
1398
1498
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1399
1499
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1400
|
-
|
|
1401
|
-
|
|
1500
|
+
${sortJoin}
|
|
1501
|
+
${guidSelector ? `WHERE ${eTable}."guid"=${guidSelector}` : ''}
|
|
1502
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1402
1503
|
}
|
|
1403
1504
|
}
|
|
1404
1505
|
}
|
|
@@ -1419,19 +1520,14 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1419
1520
|
result,
|
|
1420
1521
|
};
|
|
1421
1522
|
}
|
|
1422
|
-
performQuerySync(options, formattedSelectors, etype) {
|
|
1423
|
-
const { query, params, etypes } = this.makeEntityQuery(options, formattedSelectors, etype);
|
|
1424
|
-
const result = (this.queryIterSync(query, { etypes, params }) || [])[Symbol.iterator]();
|
|
1425
|
-
return {
|
|
1426
|
-
result,
|
|
1427
|
-
};
|
|
1428
|
-
}
|
|
1429
1523
|
async getEntities(options = {}, ...selectors) {
|
|
1430
|
-
const { result: resultPromise, process } = this.
|
|
1524
|
+
const { result: resultPromise, process } = this.getEntitiesRowLike(
|
|
1525
|
+
// @ts-ignore: options is correct here.
|
|
1526
|
+
options, selectors, ({ options, selectors, etype }) => this.performQuery(options, selectors, etype), () => {
|
|
1431
1527
|
const next = result.next();
|
|
1432
1528
|
return next.done ? null : next.value;
|
|
1433
1529
|
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
1434
|
-
tags: row.tags,
|
|
1530
|
+
tags: row.tags.filter((tag) => tag),
|
|
1435
1531
|
cdate: isNaN(Number(row.cdate)) ? null : Number(row.cdate),
|
|
1436
1532
|
mdate: isNaN(Number(row.mdate)) ? null : Number(row.mdate),
|
|
1437
1533
|
}), (row) => ({
|
|
@@ -1439,8 +1535,10 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1439
1535
|
svalue: row.value === 'N'
|
|
1440
1536
|
? JSON.stringify(Number(row.number))
|
|
1441
1537
|
: row.value === 'S'
|
|
1442
|
-
? JSON.stringify(row.string)
|
|
1443
|
-
: row.value
|
|
1538
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(row.string))
|
|
1539
|
+
: row.value === 'J'
|
|
1540
|
+
? PostgreSQLDriver.unescapeNullSequences(JSON.stringify(row.json))
|
|
1541
|
+
: row.value,
|
|
1444
1542
|
}));
|
|
1445
1543
|
const result = await resultPromise;
|
|
1446
1544
|
const value = process();
|
|
@@ -1449,28 +1547,6 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1449
1547
|
}
|
|
1450
1548
|
return value;
|
|
1451
1549
|
}
|
|
1452
|
-
getEntitiesSync(options = {}, ...selectors) {
|
|
1453
|
-
const { result, process } = this.getEntitesRowLike(options, selectors, (options, formattedSelectors, etype) => this.performQuerySync(options, formattedSelectors, etype), () => {
|
|
1454
|
-
const next = result.next();
|
|
1455
|
-
return next.done ? null : next.value;
|
|
1456
|
-
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
1457
|
-
tags: row.tags,
|
|
1458
|
-
cdate: isNaN(Number(row.cdate)) ? null : Number(row.cdate),
|
|
1459
|
-
mdate: isNaN(Number(row.mdate)) ? null : Number(row.mdate),
|
|
1460
|
-
}), (row) => ({
|
|
1461
|
-
name: row.name,
|
|
1462
|
-
svalue: row.value === 'N'
|
|
1463
|
-
? JSON.stringify(Number(row.number))
|
|
1464
|
-
: row.value === 'S'
|
|
1465
|
-
? JSON.stringify(row.string)
|
|
1466
|
-
: row.value,
|
|
1467
|
-
}));
|
|
1468
|
-
const value = process();
|
|
1469
|
-
if (value instanceof Error) {
|
|
1470
|
-
throw value;
|
|
1471
|
-
}
|
|
1472
|
-
return value;
|
|
1473
|
-
}
|
|
1474
1550
|
async getUID(name) {
|
|
1475
1551
|
if (name == null) {
|
|
1476
1552
|
throw new nymph_1.InvalidParametersError('Name not given for UID.');
|
|
@@ -1482,115 +1558,128 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1482
1558
|
});
|
|
1483
1559
|
return result?.cur_uid == null ? null : Number(result.cur_uid);
|
|
1484
1560
|
}
|
|
1485
|
-
async
|
|
1561
|
+
async importEntity({ guid, cdate, mdate, tags, sdata, etype, }) {
|
|
1486
1562
|
try {
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1563
|
+
let promises = [];
|
|
1564
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1565
|
+
etypes: [etype],
|
|
1566
|
+
params: {
|
|
1567
|
+
guid,
|
|
1568
|
+
},
|
|
1569
|
+
}));
|
|
1570
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1571
|
+
etypes: [etype],
|
|
1572
|
+
params: {
|
|
1573
|
+
guid,
|
|
1574
|
+
},
|
|
1575
|
+
}));
|
|
1576
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1577
|
+
etypes: [etype],
|
|
1578
|
+
params: {
|
|
1579
|
+
guid,
|
|
1580
|
+
},
|
|
1581
|
+
}));
|
|
1582
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1583
|
+
etypes: [etype],
|
|
1584
|
+
params: {
|
|
1585
|
+
guid,
|
|
1586
|
+
},
|
|
1587
|
+
}));
|
|
1588
|
+
await Promise.all(promises);
|
|
1589
|
+
promises = [];
|
|
1590
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate);`, {
|
|
1591
|
+
etypes: [etype],
|
|
1592
|
+
params: {
|
|
1593
|
+
guid,
|
|
1594
|
+
tags,
|
|
1595
|
+
cdate: isNaN(cdate) ? null : cdate,
|
|
1596
|
+
mdate: isNaN(mdate) ? null : mdate,
|
|
1597
|
+
},
|
|
1598
|
+
});
|
|
1599
|
+
for (const name in sdata) {
|
|
1600
|
+
const value = sdata[name];
|
|
1601
|
+
const uvalue = JSON.parse(value);
|
|
1602
|
+
if (value === undefined) {
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
const storageValue = typeof uvalue === 'number'
|
|
1606
|
+
? 'N'
|
|
1607
|
+
: typeof uvalue === 'string'
|
|
1608
|
+
? 'S'
|
|
1609
|
+
: 'J';
|
|
1610
|
+
const jsonValue = storageValue === 'J'
|
|
1611
|
+
? PostgreSQLDriver.escapeNullSequences(value)
|
|
1612
|
+
: null;
|
|
1613
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`, {
|
|
1495
1614
|
etypes: [etype],
|
|
1496
1615
|
params: {
|
|
1497
1616
|
guid,
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
mdate: isNaN(Number(JSON.parse(sdata.mdate)))
|
|
1617
|
+
name,
|
|
1618
|
+
storageValue,
|
|
1619
|
+
jsonValue,
|
|
1620
|
+
string: storageValue === 'J'
|
|
1503
1621
|
? null
|
|
1504
|
-
:
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
const promises = [];
|
|
1508
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1509
|
-
etypes: [etype],
|
|
1510
|
-
params: {
|
|
1511
|
-
guid,
|
|
1512
|
-
},
|
|
1513
|
-
}));
|
|
1514
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1515
|
-
etypes: [etype],
|
|
1516
|
-
params: {
|
|
1517
|
-
guid,
|
|
1518
|
-
},
|
|
1519
|
-
}));
|
|
1520
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1521
|
-
etypes: [etype],
|
|
1522
|
-
params: {
|
|
1523
|
-
guid,
|
|
1622
|
+
: PostgreSQLDriver.escapeNulls(`${uvalue}`),
|
|
1623
|
+
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
1624
|
+
truthy: !!uvalue,
|
|
1524
1625
|
},
|
|
1525
1626
|
}));
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
for (const name in sdata) {
|
|
1530
|
-
const value = sdata[name];
|
|
1531
|
-
const uvalue = JSON.parse(value);
|
|
1532
|
-
if (value === undefined) {
|
|
1533
|
-
continue;
|
|
1534
|
-
}
|
|
1535
|
-
const storageValue = typeof uvalue === 'number'
|
|
1536
|
-
? 'N'
|
|
1537
|
-
: typeof uvalue === 'string'
|
|
1538
|
-
? 'S'
|
|
1539
|
-
: value;
|
|
1540
|
-
const promises = [];
|
|
1541
|
-
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value") VALUES (decode(@guid, 'hex'), @name, @storageValue);`, {
|
|
1542
|
-
etypes: [etype],
|
|
1543
|
-
params: {
|
|
1544
|
-
guid,
|
|
1545
|
-
name,
|
|
1546
|
-
storageValue,
|
|
1547
|
-
},
|
|
1548
|
-
}));
|
|
1549
|
-
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);`, {
|
|
1627
|
+
const references = this.findReferences(value);
|
|
1628
|
+
for (const reference of references) {
|
|
1629
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`, {
|
|
1550
1630
|
etypes: [etype],
|
|
1551
1631
|
params: {
|
|
1552
1632
|
guid,
|
|
1553
1633
|
name,
|
|
1554
|
-
|
|
1555
|
-
string: `${uvalue}`,
|
|
1556
|
-
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
1634
|
+
reference,
|
|
1557
1635
|
},
|
|
1558
1636
|
}));
|
|
1559
|
-
const references = this.findReferences(value);
|
|
1560
|
-
for (const reference of references) {
|
|
1561
|
-
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`, {
|
|
1562
|
-
etypes: [etype],
|
|
1563
|
-
params: {
|
|
1564
|
-
guid,
|
|
1565
|
-
name,
|
|
1566
|
-
reference,
|
|
1567
|
-
},
|
|
1568
|
-
}));
|
|
1569
|
-
}
|
|
1570
1637
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
1638
|
+
}
|
|
1639
|
+
const uniques = await this.nymph
|
|
1640
|
+
.getEntityClassByEtype(etype)
|
|
1641
|
+
.getUniques({ guid, cdate, mdate, tags, data: {}, sdata });
|
|
1642
|
+
for (const unique of uniques) {
|
|
1643
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
1644
|
+
etypes: [etype],
|
|
1579
1645
|
params: {
|
|
1580
|
-
|
|
1581
|
-
|
|
1646
|
+
guid,
|
|
1647
|
+
unique,
|
|
1582
1648
|
},
|
|
1583
|
-
})
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1649
|
+
}).catch((e) => {
|
|
1650
|
+
if (e instanceof nymph_1.EntityUniqueConstraintError) {
|
|
1651
|
+
this.nymph.config.debugError('postgresql', `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
1652
|
+
}
|
|
1653
|
+
return e;
|
|
1654
|
+
}));
|
|
1655
|
+
}
|
|
1656
|
+
await Promise.all(promises);
|
|
1657
|
+
}
|
|
1658
|
+
catch (e) {
|
|
1659
|
+
this.nymph.config.debugError('postgresql', `Import entity error: "${e}"`);
|
|
1660
|
+
throw e;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
async importUID({ name, value }) {
|
|
1664
|
+
try {
|
|
1665
|
+
await this.internalTransaction(`nymph-import-uid-${name}`);
|
|
1666
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
1667
|
+
params: {
|
|
1668
|
+
name,
|
|
1669
|
+
},
|
|
1588
1670
|
});
|
|
1589
|
-
|
|
1671
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @value);`, {
|
|
1672
|
+
params: {
|
|
1673
|
+
name,
|
|
1674
|
+
value,
|
|
1675
|
+
},
|
|
1676
|
+
});
|
|
1677
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
1590
1678
|
}
|
|
1591
1679
|
catch (e) {
|
|
1592
|
-
|
|
1593
|
-
|
|
1680
|
+
this.nymph.config.debugError('postgresql', `Import UID error: "${e}"`);
|
|
1681
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
1682
|
+
throw e;
|
|
1594
1683
|
}
|
|
1595
1684
|
}
|
|
1596
1685
|
async newUID(name) {
|
|
@@ -1598,13 +1687,14 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1598
1687
|
throw new nymph_1.InvalidParametersError('Name not given for UID.');
|
|
1599
1688
|
}
|
|
1600
1689
|
await this.internalTransaction('nymph-newuid');
|
|
1690
|
+
let curUid = undefined;
|
|
1601
1691
|
try {
|
|
1602
1692
|
const lock = await this.queryGet(`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name FOR UPDATE;`, {
|
|
1603
1693
|
params: {
|
|
1604
1694
|
name,
|
|
1605
1695
|
},
|
|
1606
1696
|
});
|
|
1607
|
-
|
|
1697
|
+
curUid = lock?.cur_uid == null ? undefined : Number(lock.cur_uid);
|
|
1608
1698
|
if (curUid == null) {
|
|
1609
1699
|
curUid = 1;
|
|
1610
1700
|
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
@@ -1623,13 +1713,14 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1623
1713
|
},
|
|
1624
1714
|
});
|
|
1625
1715
|
}
|
|
1626
|
-
await this.commit('nymph-newuid');
|
|
1627
|
-
return curUid;
|
|
1628
1716
|
}
|
|
1629
1717
|
catch (e) {
|
|
1718
|
+
this.nymph.config.debugError('postgresql', `New UID error: "${e}"`);
|
|
1630
1719
|
await this.rollback('nymph-newuid');
|
|
1631
1720
|
throw e;
|
|
1632
1721
|
}
|
|
1722
|
+
await this.commit('nymph-newuid');
|
|
1723
|
+
return curUid;
|
|
1633
1724
|
}
|
|
1634
1725
|
async renameUID(oldName, newName) {
|
|
1635
1726
|
if (oldName == null || newName == null) {
|
|
@@ -1662,7 +1753,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1662
1753
|
return true;
|
|
1663
1754
|
}
|
|
1664
1755
|
async saveEntity(entity) {
|
|
1665
|
-
const insertData = async (guid, data, sdata, etype) => {
|
|
1756
|
+
const insertData = async (guid, data, sdata, uniques, etype) => {
|
|
1666
1757
|
const runInsertQuery = async (name, value, svalue) => {
|
|
1667
1758
|
if (value === undefined) {
|
|
1668
1759
|
return;
|
|
@@ -1671,24 +1762,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1671
1762
|
? 'N'
|
|
1672
1763
|
: typeof value === 'string'
|
|
1673
1764
|
? 'S'
|
|
1674
|
-
:
|
|
1765
|
+
: 'J';
|
|
1766
|
+
const jsonValue = storageValue === 'J'
|
|
1767
|
+
? PostgreSQLDriver.escapeNullSequences(svalue)
|
|
1768
|
+
: null;
|
|
1675
1769
|
const promises = [];
|
|
1676
|
-
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value") VALUES (decode(@guid, 'hex'), @name, @storageValue);`, {
|
|
1770
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`, {
|
|
1677
1771
|
etypes: [etype],
|
|
1678
1772
|
params: {
|
|
1679
1773
|
guid,
|
|
1680
1774
|
name,
|
|
1681
1775
|
storageValue,
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
params: {
|
|
1687
|
-
guid,
|
|
1688
|
-
name,
|
|
1689
|
-
truthy: !!value,
|
|
1690
|
-
string: `${value}`,
|
|
1776
|
+
jsonValue,
|
|
1777
|
+
string: storageValue === 'J'
|
|
1778
|
+
? null
|
|
1779
|
+
: PostgreSQLDriver.escapeNulls(`${value}`),
|
|
1691
1780
|
number: isNaN(Number(value)) ? null : Number(value),
|
|
1781
|
+
truthy: !!value,
|
|
1692
1782
|
},
|
|
1693
1783
|
}));
|
|
1694
1784
|
const references = this.findReferences(svalue);
|
|
@@ -1704,6 +1794,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1704
1794
|
}
|
|
1705
1795
|
await Promise.all(promises);
|
|
1706
1796
|
};
|
|
1797
|
+
for (const unique of uniques) {
|
|
1798
|
+
try {
|
|
1799
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
1800
|
+
etypes: [etype],
|
|
1801
|
+
params: {
|
|
1802
|
+
guid,
|
|
1803
|
+
unique,
|
|
1804
|
+
},
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
catch (e) {
|
|
1808
|
+
if (e instanceof nymph_1.EntityUniqueConstraintError) {
|
|
1809
|
+
this.nymph.config.debugError('postgresql', `Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
1810
|
+
}
|
|
1811
|
+
throw e;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1707
1814
|
for (const name in data) {
|
|
1708
1815
|
await runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
1709
1816
|
}
|
|
@@ -1711,8 +1818,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1711
1818
|
await runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
1712
1819
|
}
|
|
1713
1820
|
};
|
|
1821
|
+
let inTransaction = false;
|
|
1714
1822
|
try {
|
|
1715
|
-
const result = await this.saveEntityRowLike(entity, async (
|
|
1823
|
+
const result = await this.saveEntityRowLike(entity, async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
1824
|
+
if (Object.keys(data).length === 0 &&
|
|
1825
|
+
Object.keys(sdata).length === 0) {
|
|
1826
|
+
return false;
|
|
1827
|
+
}
|
|
1716
1828
|
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @cdate);`, {
|
|
1717
1829
|
etypes: [etype],
|
|
1718
1830
|
params: {
|
|
@@ -1721,9 +1833,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1721
1833
|
cdate,
|
|
1722
1834
|
},
|
|
1723
1835
|
});
|
|
1724
|
-
await insertData(guid, data, sdata, etype);
|
|
1836
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1725
1837
|
return true;
|
|
1726
|
-
}, async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
1838
|
+
}, async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
1839
|
+
if (Object.keys(data).length === 0 &&
|
|
1840
|
+
Object.keys(sdata).length === 0) {
|
|
1841
|
+
return false;
|
|
1842
|
+
}
|
|
1727
1843
|
const promises = [];
|
|
1728
1844
|
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1729
1845
|
etypes: [etype],
|
|
@@ -1737,13 +1853,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1737
1853
|
guid,
|
|
1738
1854
|
},
|
|
1739
1855
|
}));
|
|
1740
|
-
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
1856
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1741
1857
|
etypes: [etype],
|
|
1742
1858
|
params: {
|
|
1743
1859
|
guid,
|
|
1744
1860
|
},
|
|
1745
1861
|
}));
|
|
1746
|
-
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
1862
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1747
1863
|
etypes: [etype],
|
|
1748
1864
|
params: {
|
|
1749
1865
|
guid,
|
|
@@ -1768,38 +1884,45 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1768
1884
|
guid,
|
|
1769
1885
|
},
|
|
1770
1886
|
}));
|
|
1771
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
1887
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1772
1888
|
etypes: [etype],
|
|
1773
1889
|
params: {
|
|
1774
1890
|
guid,
|
|
1775
1891
|
},
|
|
1776
1892
|
}));
|
|
1777
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
1893
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1778
1894
|
etypes: [etype],
|
|
1779
1895
|
params: {
|
|
1780
1896
|
guid,
|
|
1781
1897
|
},
|
|
1782
1898
|
}));
|
|
1783
1899
|
await Promise.all(promises);
|
|
1784
|
-
await insertData(guid, data, sdata, etype);
|
|
1900
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1785
1901
|
success = true;
|
|
1786
1902
|
}
|
|
1787
1903
|
return success;
|
|
1788
1904
|
}, async () => {
|
|
1789
1905
|
await this.internalTransaction('nymph-save');
|
|
1906
|
+
inTransaction = true;
|
|
1790
1907
|
}, async (success) => {
|
|
1791
|
-
if (
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1908
|
+
if (inTransaction) {
|
|
1909
|
+
inTransaction = false;
|
|
1910
|
+
if (success) {
|
|
1911
|
+
await this.commit('nymph-save');
|
|
1912
|
+
}
|
|
1913
|
+
else {
|
|
1914
|
+
await this.rollback('nymph-save');
|
|
1915
|
+
}
|
|
1796
1916
|
}
|
|
1797
1917
|
return success;
|
|
1798
1918
|
});
|
|
1799
1919
|
return result;
|
|
1800
1920
|
}
|
|
1801
1921
|
catch (e) {
|
|
1802
|
-
|
|
1922
|
+
this.nymph.config.debugError('postgresql', `Save entity error: "${e}"`);
|
|
1923
|
+
if (inTransaction) {
|
|
1924
|
+
await this.rollback('nymph-save');
|
|
1925
|
+
}
|
|
1803
1926
|
throw e;
|
|
1804
1927
|
}
|
|
1805
1928
|
}
|
|
@@ -1821,23 +1944,25 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1821
1944
|
curUid,
|
|
1822
1945
|
},
|
|
1823
1946
|
});
|
|
1824
|
-
await this.commit('nymph-setuid');
|
|
1825
|
-
return true;
|
|
1826
1947
|
}
|
|
1827
1948
|
catch (e) {
|
|
1828
1949
|
await this.rollback('nymph-setuid');
|
|
1829
1950
|
throw e;
|
|
1830
1951
|
}
|
|
1952
|
+
await this.commit('nymph-setuid');
|
|
1953
|
+
return true;
|
|
1831
1954
|
}
|
|
1832
1955
|
async internalTransaction(name) {
|
|
1833
1956
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1834
1957
|
throw new nymph_1.InvalidParametersError('Transaction start attempted without a name.');
|
|
1835
1958
|
}
|
|
1836
1959
|
if (!this.transaction || this.transaction.count === 0) {
|
|
1960
|
+
// Lock to one connection.
|
|
1837
1961
|
this.transaction = {
|
|
1838
1962
|
count: 0,
|
|
1839
1963
|
connection: await this.getConnection(),
|
|
1840
1964
|
};
|
|
1965
|
+
// We're not in a transaction yet, so start one.
|
|
1841
1966
|
await this.queryRun('BEGIN;');
|
|
1842
1967
|
}
|
|
1843
1968
|
await this.queryRun(`SAVEPOINT ${PostgreSQLDriver.escape(name)};`);
|
|
@@ -1845,7 +1970,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1845
1970
|
return this.transaction;
|
|
1846
1971
|
}
|
|
1847
1972
|
async startTransaction(name) {
|
|
1848
|
-
const inTransaction = this.inTransaction();
|
|
1973
|
+
const inTransaction = await this.inTransaction();
|
|
1849
1974
|
const transaction = await this.internalTransaction(name);
|
|
1850
1975
|
if (!inTransaction) {
|
|
1851
1976
|
this.transaction = null;
|
|
@@ -1854,6 +1979,24 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1854
1979
|
nymph.driver.transaction = transaction;
|
|
1855
1980
|
return nymph;
|
|
1856
1981
|
}
|
|
1982
|
+
async needsMigration() {
|
|
1983
|
+
const table = await this.queryGet('SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix LIMIT 1;', {
|
|
1984
|
+
params: {
|
|
1985
|
+
db: this.config.database,
|
|
1986
|
+
prefix: this.prefix + 'data_' + '%',
|
|
1987
|
+
},
|
|
1988
|
+
});
|
|
1989
|
+
if (table?.table_name) {
|
|
1990
|
+
const result = await this.queryGet('SELECT 1 AS "exists" FROM "information_schema"."columns" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name"=@table AND "column_name"=\'json\';', {
|
|
1991
|
+
params: {
|
|
1992
|
+
db: this.config.database,
|
|
1993
|
+
table: table.table_name,
|
|
1994
|
+
},
|
|
1995
|
+
});
|
|
1996
|
+
return !result?.exists;
|
|
1997
|
+
}
|
|
1998
|
+
return false;
|
|
1999
|
+
}
|
|
1857
2000
|
}
|
|
1858
2001
|
exports.default = PostgreSQLDriver;
|
|
1859
2002
|
//# sourceMappingURL=PostgreSQLDriver.js.map
|