prostgles-server 4.2.72 → 4.2.74
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/dist/DboBuilder/DboBuilder.d.ts +2 -1
- package/dist/DboBuilder/DboBuilder.d.ts.map +1 -1
- package/dist/DboBuilder/DboBuilder.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.js +2 -2
- package/dist/DboBuilder/QueryBuilder/getNewQuery.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +2 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.js.map +1 -1
- package/dist/DboBuilder/dboBuilderUtils.d.ts +2 -1
- package/dist/DboBuilder/dboBuilderUtils.d.ts.map +1 -1
- package/dist/DboBuilder/dboBuilderUtils.js.map +1 -1
- package/dist/DboBuilder/getTablesForSchemaPostgresSQL.d.ts +1 -1
- package/dist/DboBuilder/getTablesForSchemaPostgresSQL.d.ts.map +1 -1
- package/dist/DboBuilder/getTablesForSchemaPostgresSQL.js +2 -1
- package/dist/DboBuilder/getTablesForSchemaPostgresSQL.js.map +1 -1
- package/dist/DboBuilder/prepareShortestJoinPaths.d.ts +1 -1
- package/dist/DboBuilder/prepareShortestJoinPaths.d.ts.map +1 -1
- package/dist/DboBuilder/prepareShortestJoinPaths.js +3 -3
- package/dist/DboBuilder/prepareShortestJoinPaths.js.map +1 -1
- package/dist/Prostgles.d.ts +12 -177
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +6 -2
- package/dist/Prostgles.js.map +1 -1
- package/dist/ProstglesTypes.d.ts +177 -0
- package/dist/ProstglesTypes.d.ts.map +1 -0
- package/dist/ProstglesTypes.js +5 -0
- package/dist/ProstglesTypes.js.map +1 -0
- package/dist/PubSubManager/PubSubManager.d.ts +2 -4
- package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager/PubSubManager.js +25 -17
- package/dist/PubSubManager/PubSubManager.js.map +1 -1
- package/dist/PubSubManager/getInitQuery.d.ts.map +1 -1
- package/dist/PubSubManager/getInitQuery.js +5 -2
- package/dist/PubSubManager/getInitQuery.js.map +1 -1
- package/dist/PubSubManager/initPubSubManager.d.ts +1 -0
- package/dist/PubSubManager/initPubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager/initPubSubManager.js +31 -18
- package/dist/PubSubManager/initPubSubManager.js.map +1 -1
- package/dist/PubSubManager/notifListener.d.ts.map +1 -1
- package/dist/PubSubManager/notifListener.js +13 -0
- package/dist/PubSubManager/notifListener.js.map +1 -1
- package/dist/PubSubManager/orphanTriggerCheck.d.ts +3 -0
- package/dist/PubSubManager/orphanTriggerCheck.d.ts.map +1 -0
- package/dist/PubSubManager/orphanTriggerCheck.js +78 -0
- package/dist/PubSubManager/orphanTriggerCheck.js.map +1 -0
- package/dist/SchemaWatch/SchemaWatch.d.ts +4 -1
- package/dist/SchemaWatch/SchemaWatch.d.ts.map +1 -1
- package/dist/SchemaWatch/SchemaWatch.js.map +1 -1
- package/dist/SchemaWatch/getValidatedWatchSchemaType.d.ts +1 -1
- package/dist/SchemaWatch/getValidatedWatchSchemaType.d.ts.map +1 -1
- package/dist/SchemaWatch/getWatchSchemaTagList.d.ts +1 -1
- package/dist/SchemaWatch/getWatchSchemaTagList.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/initProstgles.d.ts +2 -1
- package/dist/initProstgles.d.ts.map +1 -1
- package/dist/initProstgles.js +23 -2
- package/dist/initProstgles.js.map +1 -1
- package/lib/DboBuilder/DboBuilder.ts +2 -3
- package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +5 -6
- package/lib/DboBuilder/ViewHandler/ViewHandler.ts +2 -1
- package/lib/DboBuilder/dboBuilderUtils.ts +1 -1
- package/lib/DboBuilder/getTablesForSchemaPostgresSQL.ts +4 -2
- package/lib/DboBuilder/prepareShortestJoinPaths.ts +1 -1
- package/lib/Prostgles.ts +14 -198
- package/lib/ProstglesTypes.ts +193 -0
- package/lib/PubSubManager/PubSubManager.ts +25 -19
- package/lib/PubSubManager/getInitQuery.ts +5 -2
- package/lib/PubSubManager/initPubSubManager.ts +33 -18
- package/lib/PubSubManager/notifListener.ts +15 -0
- package/lib/PubSubManager/orphanTriggerCheck.ts +79 -0
- package/lib/SchemaWatch/SchemaWatch.ts +2 -1
- package/lib/SchemaWatch/getValidatedWatchSchemaType.ts +1 -1
- package/lib/SchemaWatch/getWatchSchemaTagList.ts +1 -1
- package/lib/index.ts +2 -1
- package/lib/initProstgles.ts +26 -3
- package/package.json +2 -2
- package/tests/isomorphicQueries.spec.ts +4 -4
- package/tests/server/index.ts +1 -0
- package/tests/server/package-lock.json +329 -138
- package/tests/server/package.json +2 -2
- package/tests/server/testTableConfig.ts +1 -0
|
@@ -167,6 +167,10 @@ export class PubSubManager {
|
|
|
167
167
|
return result;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
appInfoWasInserted = false;
|
|
171
|
+
get appId() {
|
|
172
|
+
return this.dboBuilder.prostgles.appId;
|
|
173
|
+
}
|
|
170
174
|
get db(): DB {
|
|
171
175
|
return this.dboBuilder.db;
|
|
172
176
|
}
|
|
@@ -195,12 +199,6 @@ export class PubSubManager {
|
|
|
195
199
|
|
|
196
200
|
log("Created PubSubManager");
|
|
197
201
|
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Used facilitate concurrent prostgles connections to the same database
|
|
201
|
-
*/
|
|
202
|
-
appID?: string;
|
|
203
|
-
|
|
204
202
|
appCheckFrequencyMS = 10 * 1000;
|
|
205
203
|
appCheck?: ReturnType<typeof setInterval>;
|
|
206
204
|
|
|
@@ -227,8 +225,6 @@ export class PubSubManager {
|
|
|
227
225
|
checkedListenerTableCond?: string[];
|
|
228
226
|
|
|
229
227
|
initialiseEventTriggers = async () => {
|
|
230
|
-
if (!this.appID) throw "prepareTriggers failed: this.appID missing";
|
|
231
|
-
|
|
232
228
|
const { watchSchema } = this.dboBuilder.prostgles.opts;
|
|
233
229
|
if (watchSchema && !(await getIsSuperUser(this.db))) {
|
|
234
230
|
console.warn("prostgles watchSchema requires superuser db user. Will not watch using event triggers")
|
|
@@ -268,7 +264,7 @@ export class PubSubManager {
|
|
|
268
264
|
|
|
269
265
|
DROP TABLE IF EXISTS %1$I;
|
|
270
266
|
$q$,
|
|
271
|
-
${asValue('triggers_' + this.
|
|
267
|
+
${asValue('triggers_' + this.appId)}
|
|
272
268
|
);
|
|
273
269
|
|
|
274
270
|
is_super_user := EXISTS (select 1 from pg_user where usename = CURRENT_USER AND usesuper IS TRUE);
|
|
@@ -402,15 +398,14 @@ export class PubSubManager {
|
|
|
402
398
|
|
|
403
399
|
addSub = addSub.bind(this);
|
|
404
400
|
|
|
405
|
-
|
|
406
401
|
getActiveListeners = (): { table_name: string; condition: string }[] => {
|
|
407
|
-
const
|
|
402
|
+
const activeListeners: { table_name: string; condition: string }[] = [];
|
|
408
403
|
const upsert = (t: string, c: string) => {
|
|
409
|
-
if (!
|
|
410
|
-
|
|
404
|
+
if (!activeListeners.find(r => r.table_name === t && r.condition === c)) {
|
|
405
|
+
activeListeners.push({ table_name: t, condition: c });
|
|
411
406
|
}
|
|
412
407
|
}
|
|
413
|
-
(this.syncs
|
|
408
|
+
(this.syncs ?? []).map(s => {
|
|
414
409
|
upsert(s.table_name, s.condition)
|
|
415
410
|
});
|
|
416
411
|
|
|
@@ -420,7 +415,7 @@ export class PubSubManager {
|
|
|
420
415
|
});
|
|
421
416
|
});
|
|
422
417
|
|
|
423
|
-
return
|
|
418
|
+
return activeListeners;
|
|
424
419
|
}
|
|
425
420
|
|
|
426
421
|
/**
|
|
@@ -436,7 +431,7 @@ export class PubSubManager {
|
|
|
436
431
|
FROM prostgles.v_triggers
|
|
437
432
|
WHERE app_id = $1
|
|
438
433
|
ORDER BY table_name, condition
|
|
439
|
-
`, [this.
|
|
434
|
+
`, [this.dboBuilder.prostgles.appId]
|
|
440
435
|
);
|
|
441
436
|
|
|
442
437
|
this._triggers = {};
|
|
@@ -459,7 +454,6 @@ export class PubSubManager {
|
|
|
459
454
|
const { table_name } = { ...params }
|
|
460
455
|
let { condition } = { ...params }
|
|
461
456
|
if (!table_name) throw "MISSING table_name";
|
|
462
|
-
if (!this.appID) throw "MISSING appID";
|
|
463
457
|
|
|
464
458
|
if (!condition || !condition.trim().length) {
|
|
465
459
|
condition = "TRUE";
|
|
@@ -479,8 +473,20 @@ export class PubSubManager {
|
|
|
479
473
|
/* ${ PubSubManager.EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID} */
|
|
480
474
|
LOCK TABLE prostgles.app_triggers IN ACCESS EXCLUSIVE MODE;
|
|
481
475
|
|
|
482
|
-
INSERT INTO prostgles.app_triggers (
|
|
483
|
-
|
|
476
|
+
INSERT INTO prostgles.app_triggers (
|
|
477
|
+
table_name,
|
|
478
|
+
condition,
|
|
479
|
+
app_id,
|
|
480
|
+
related_view_name,
|
|
481
|
+
related_view_def
|
|
482
|
+
)
|
|
483
|
+
VALUES (
|
|
484
|
+
${trgVals.tbl},
|
|
485
|
+
${trgVals.cond},
|
|
486
|
+
${asValue(this.appId)},
|
|
487
|
+
${asValue(viewOptions?.viewName ?? null)},
|
|
488
|
+
${asValue(viewOptions?.definition ?? null)}
|
|
489
|
+
)
|
|
484
490
|
ON CONFLICT DO NOTHING;
|
|
485
491
|
|
|
486
492
|
COMMIT WORK;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { asValue, NOTIF_CHANNEL, NOTIF_TYPE, PubSubManager } from "./PubSubManager";
|
|
3
3
|
const { version } = require("../../package.json");
|
|
4
|
+
import { getAppCheckQuery } from "./orphanTriggerCheck";
|
|
4
5
|
|
|
5
6
|
export const DB_OBJ_NAMES = {
|
|
6
7
|
trigger_add_remove_func: "prostgles.trigger_add_remove_func",
|
|
@@ -279,7 +280,6 @@ BEGIN
|
|
|
279
280
|
err_detail = PG_EXCEPTION_DETAIL,
|
|
280
281
|
err_hint = PG_EXCEPTION_HINT;
|
|
281
282
|
|
|
282
|
-
|
|
283
283
|
END;
|
|
284
284
|
|
|
285
285
|
--RAISE NOTICE 'has_errors: % ', has_errors;
|
|
@@ -307,7 +307,7 @@ BEGIN
|
|
|
307
307
|
THEN concat_ws('; ', 'error', err_text, err_detail, err_hint, 'query: ' || query )
|
|
308
308
|
ELSE COALESCE(v_trigger.cids, '')
|
|
309
309
|
END
|
|
310
|
-
${this.dboBuilder.prostgles.opts.DEBUG_MODE? (", COALESCE(current_query(), 'current_query ??'),
|
|
310
|
+
${this.dboBuilder.prostgles.opts.DEBUG_MODE? (", COALESCE(current_query(), 'current_query ??'), ' ', query") : ""}
|
|
311
311
|
), 7999/4) -- Some chars are 2bytes -> 'Ω'
|
|
312
312
|
);
|
|
313
313
|
END LOOP;
|
|
@@ -323,6 +323,7 @@ BEGIN
|
|
|
323
323
|
END IF;
|
|
324
324
|
END IF;
|
|
325
325
|
|
|
326
|
+
${getAppCheckQuery()}
|
|
326
327
|
|
|
327
328
|
RETURN NULL;
|
|
328
329
|
|
|
@@ -574,6 +575,8 @@ BEGIN
|
|
|
574
575
|
);
|
|
575
576
|
END LOOP;
|
|
576
577
|
|
|
578
|
+
${getAppCheckQuery()}
|
|
579
|
+
|
|
577
580
|
END IF;
|
|
578
581
|
|
|
579
582
|
END;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PostgresNotifListenManager } from "../PostgresNotifListenManager";
|
|
2
2
|
import { getWatchSchemaTagList } from "../SchemaWatch/getWatchSchemaTagList";
|
|
3
3
|
import { asValue, log, NOTIF_CHANNEL, PubSubManager } from "./PubSubManager";
|
|
4
|
-
const REALTIME_TRIGGER_CHECK_QUERY = "prostgles-server internal query used to manage realtime triggers" as const;
|
|
4
|
+
export const REALTIME_TRIGGER_CHECK_QUERY = "prostgles-server internal query used to manage realtime triggers" as const;
|
|
5
5
|
import { getInitQuery } from "./getInitQuery";
|
|
6
6
|
|
|
7
7
|
export async function initPubSubManager(this: PubSubManager): Promise<PubSubManager | undefined> {
|
|
@@ -15,25 +15,37 @@ export async function initPubSubManager(this: PubSubManager): Promise<PubSubMana
|
|
|
15
15
|
if (!this.getIsDestroyed()) return;
|
|
16
16
|
|
|
17
17
|
/* Prepare App id */
|
|
18
|
-
if (!this.
|
|
18
|
+
if (!this.appInfoWasInserted) {
|
|
19
|
+
this.appInfoWasInserted = true;
|
|
19
20
|
const check_frequency_ms = this.appCheckFrequencyMS;
|
|
20
21
|
const watching_schema_tag_names = this.dboBuilder.prostgles.schemaWatch?.type.watchType !== "NONE" ? getWatchSchemaTagList(this.dboBuilder.prostgles.opts.watchSchema) : null;
|
|
21
|
-
|
|
22
|
-
"INSERT INTO prostgles.apps (check_frequency_ms, watching_schema_tag_names, application_name) \
|
|
23
|
-
VALUES($1, $2, current_setting('application_name')) \
|
|
22
|
+
await this.db.one(
|
|
23
|
+
"INSERT INTO prostgles.apps (id, check_frequency_ms, watching_schema_tag_names, application_name) \
|
|
24
|
+
VALUES($1, $2, $3, current_setting('application_name')) \
|
|
24
25
|
RETURNING *; "
|
|
25
26
|
, [
|
|
27
|
+
this.appId,
|
|
26
28
|
check_frequency_ms,
|
|
27
29
|
watching_schema_tag_names
|
|
28
30
|
]
|
|
29
31
|
);
|
|
30
|
-
this.appID = raw.id;
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
const appRecord = await this.db.one("SELECT * FROM prostgles.apps WHERE id = $1", [this.appId]);
|
|
34
|
+
if (!appRecord || !appRecord.application_name?.includes(this.appId)) {
|
|
35
|
+
throw `initPubSubManager error: App record with application_name containing appId (${this.appId}) not found`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await this.db.any(`
|
|
39
|
+
DELETE FROM prostgles.app_triggers
|
|
40
|
+
WHERE app_id = ${asValue(this.appId)}
|
|
41
|
+
`);
|
|
42
|
+
|
|
43
|
+
console.log("REMOVE app check disabled");
|
|
44
|
+
if (!this.appCheck && Math.random() < 12) {
|
|
33
45
|
|
|
34
46
|
this.appCheck = setInterval(async () => {
|
|
35
47
|
let checkForStaleTriggers = "";
|
|
36
|
-
try {
|
|
48
|
+
try {
|
|
37
49
|
|
|
38
50
|
this.appChecking = true;
|
|
39
51
|
|
|
@@ -43,7 +55,7 @@ export async function initPubSubManager(this: PubSubManager): Promise<PubSubMana
|
|
|
43
55
|
SET last_used = CASE WHEN (table_name, condition) IN (
|
|
44
56
|
${listeners.map(l => ` ( ${asValue(l.table_name)}, ${asValue(l.condition)} ) `).join(", ")}
|
|
45
57
|
) THEN NOW() ELSE last_used END
|
|
46
|
-
WHERE app_id = ${asValue(this.
|
|
58
|
+
WHERE app_id = ${asValue(this.appId)};
|
|
47
59
|
`;
|
|
48
60
|
|
|
49
61
|
const checkedListenerTableCond = listeners.map(l => `${l.table_name}.${l.condition}`);
|
|
@@ -62,10 +74,11 @@ export async function initPubSubManager(this: PubSubManager): Promise<PubSubMana
|
|
|
62
74
|
/* UPDATE currently used triggers */
|
|
63
75
|
${updateCurrentlyUsedTriggersQuery}
|
|
64
76
|
|
|
65
|
-
/* DELETE stale triggers for current app. Other triggers will be deleted on app startup
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
/* DELETE stale triggers for current app. Other triggers will be deleted on app startup
|
|
78
|
+
DELETE FROM prostgles.app_triggers
|
|
79
|
+
WHERE app_id = ${asValue(this.appId)}
|
|
80
|
+
AND last_used < NOW() - 4 * ${asValue(this.appCheckFrequencyMS)} * interval '1 millisecond'; -- 10 seconds at the moment
|
|
81
|
+
*/
|
|
69
82
|
|
|
70
83
|
END IF;
|
|
71
84
|
|
|
@@ -94,7 +107,7 @@ export async function initPubSubManager(this: PubSubManager): Promise<PubSubMana
|
|
|
94
107
|
/* Last check used to remove disconnected apps */
|
|
95
108
|
UPDATE prostgles.apps
|
|
96
109
|
SET last_check = NOW()
|
|
97
|
-
WHERE id = ${asValue(this.
|
|
110
|
+
WHERE id = ${asValue(this.appId)};
|
|
98
111
|
|
|
99
112
|
${dataTriggerCheckQuery}
|
|
100
113
|
END IF;
|
|
@@ -106,7 +119,7 @@ export async function initPubSubManager(this: PubSubManager): Promise<PubSubMana
|
|
|
106
119
|
this.db.any(`
|
|
107
120
|
/*
|
|
108
121
|
${queryIdentifier}
|
|
109
|
-
${REALTIME_TRIGGER_CHECK_QUERY}
|
|
122
|
+
${REALTIME_TRIGGER_CHECK_QUERY}
|
|
110
123
|
${PubSubManager.EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID}
|
|
111
124
|
*/
|
|
112
125
|
DO $$
|
|
@@ -129,8 +142,10 @@ export async function initPubSubManager(this: PubSubManager): Promise<PubSubMana
|
|
|
129
142
|
|
|
130
143
|
/** In some cases a query idles and blocks everything else. Terminate all similar queries */
|
|
131
144
|
this.db.any(
|
|
132
|
-
"SELECT state, pg_terminate_backend(pid) from pg_stat_activity
|
|
133
|
-
|
|
145
|
+
"SELECT state, pg_terminate_backend(pid) from pg_stat_activity \
|
|
146
|
+
WHERE query ilike ${qid} \
|
|
147
|
+
AND pid <> pg_backend_pid();",
|
|
148
|
+
{ qid: `%${REALTIME_TRIGGER_CHECK_QUERY}%` }
|
|
134
149
|
);
|
|
135
150
|
|
|
136
151
|
/** If no tries left
|
|
@@ -150,7 +165,7 @@ export async function initPubSubManager(this: PubSubManager): Promise<PubSubMana
|
|
|
150
165
|
}
|
|
151
166
|
}
|
|
152
167
|
|
|
153
|
-
this.postgresNotifListenManager = new PostgresNotifListenManager(this.db, this.notifListener, NOTIF_CHANNEL.getFull(this.
|
|
168
|
+
this.postgresNotifListenManager = new PostgresNotifListenManager(this.db, this.notifListener, NOTIF_CHANNEL.getFull(this.appId));
|
|
154
169
|
|
|
155
170
|
await this.initialiseEventTriggers();
|
|
156
171
|
|
|
@@ -80,6 +80,21 @@ export async function notifListener(this: PubSubManager, data: { payload: string
|
|
|
80
80
|
|
|
81
81
|
state = "ok";
|
|
82
82
|
const conditions = tableTriggers.filter((c, i) => condition_ids.includes(i));
|
|
83
|
+
const orphanedConditions = condition_ids.filter((condId) => typeof tableTriggers.at(condId) !== "string" );
|
|
84
|
+
if(orphanedConditions.length){
|
|
85
|
+
this.db
|
|
86
|
+
.any(" \
|
|
87
|
+
DELETE FROM prostgles.app_triggers \
|
|
88
|
+
WHERE table_name = $1 \
|
|
89
|
+
AND id IN ($2:csv) \
|
|
90
|
+
AND app_id = $3 \
|
|
91
|
+
",
|
|
92
|
+
[table_name, orphanedConditions, this.appId]
|
|
93
|
+
)
|
|
94
|
+
.catch(e => {
|
|
95
|
+
console.error("Error deleting orphaned triggers", e);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
83
98
|
|
|
84
99
|
conditions.map(condition => {
|
|
85
100
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { PubSubManager } from './PubSubManager';
|
|
2
|
+
import { REALTIME_TRIGGER_CHECK_QUERY } from "./initPubSubManager";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Schema and Data watch triggers (DB_OBJ_NAMES.schema_watch_func, DB_OBJ_NAMES.data_watch_func)
|
|
6
|
+
* survive and continue to user resources even after the client disconnects.
|
|
7
|
+
* We must therefore .
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const queryIdentifier = "prostgles query used to keep track of which prgl backend clients are still connected";
|
|
11
|
+
const connectedApplicationNamesQuery = `
|
|
12
|
+
SELECT DISTINCT application_name
|
|
13
|
+
FROM prostgles.apps
|
|
14
|
+
WHERE application_name IN (
|
|
15
|
+
SELECT application_name
|
|
16
|
+
FROM pg_catalog.pg_stat_activity
|
|
17
|
+
WHERE pid = pg_backend_pid()
|
|
18
|
+
)
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const LAST_CHECKED_SETTING_NAME = 'prostgles.last_checked';
|
|
22
|
+
|
|
23
|
+
/** It is a function to prevent undefined EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID */
|
|
24
|
+
export const getAppCheckQuery = () => `
|
|
25
|
+
/*
|
|
26
|
+
${queryIdentifier}
|
|
27
|
+
${REALTIME_TRIGGER_CHECK_QUERY}
|
|
28
|
+
${PubSubManager.EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID}
|
|
29
|
+
*/
|
|
30
|
+
IF
|
|
31
|
+
/* prostgles schema must exist */
|
|
32
|
+
EXISTS (
|
|
33
|
+
SELECT 1
|
|
34
|
+
FROM information_schema.tables
|
|
35
|
+
WHERE table_schema = 'prostgles'
|
|
36
|
+
AND table_name = 'apps'
|
|
37
|
+
)
|
|
38
|
+
/* Ensure we don't check too often */
|
|
39
|
+
AND (
|
|
40
|
+
NULLIF(current_setting('${LAST_CHECKED_SETTING_NAME}', true), '') IS NULL
|
|
41
|
+
OR current_setting('${LAST_CHECKED_SETTING_NAME}', true)::timestamp < NOW() - INTERVAL '1 minute'
|
|
42
|
+
)
|
|
43
|
+
THEN
|
|
44
|
+
EXECUTE format('SET LOCAL ${LAST_CHECKED_SETTING_NAME} TO %L', now());
|
|
45
|
+
--USED FOR DEBUG REMOVE
|
|
46
|
+
PERFORM pg_notify('prostgles', current_setting('${LAST_CHECKED_SETTING_NAME}', true));
|
|
47
|
+
|
|
48
|
+
IF EXISTS (
|
|
49
|
+
${connectedApplicationNamesQuery}
|
|
50
|
+
) THEN
|
|
51
|
+
|
|
52
|
+
/* Remove disconnected apps */
|
|
53
|
+
WITH deleted_apps AS (
|
|
54
|
+
DELETE FROM prostgles.apps a
|
|
55
|
+
WHERE NOT EXISTS (
|
|
56
|
+
SELECT 1
|
|
57
|
+
FROM pg_catalog.pg_stat_activity s
|
|
58
|
+
WHERE s.application_name = a.application_name
|
|
59
|
+
)
|
|
60
|
+
RETURNING a.id
|
|
61
|
+
)
|
|
62
|
+
DELETE FROM prostgles.app_triggers
|
|
63
|
+
WHERE app_id IN (
|
|
64
|
+
SELECT id
|
|
65
|
+
FROM deleted_apps
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
END IF;
|
|
69
|
+
END IF;
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
// /* Ensure we don't check in paralel */
|
|
74
|
+
// AND NOT EXISTS (
|
|
75
|
+
// SELECT 1
|
|
76
|
+
// FROM pg_catalog.pg_stat_activity
|
|
77
|
+
// WHERE query ilike '%${queryIdentifier}%'
|
|
78
|
+
// AND state = 'active'
|
|
79
|
+
// )
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { DboBuilder } from "../DboBuilder/DboBuilder";
|
|
2
2
|
import { EVENT_TRIGGER_TAGS } from "../Event_Trigger_Tags";
|
|
3
|
-
import { OnSchemaChangeCallback } from "../Prostgles";
|
|
4
3
|
import { PubSubManager, log } from "../PubSubManager/PubSubManager";
|
|
5
4
|
import { ValidatedWatchSchemaType, getValidatedWatchSchemaType } from "./getValidatedWatchSchemaType";
|
|
6
5
|
const COMMAND_FIRST_KEYWORDS = EVENT_TRIGGER_TAGS
|
|
@@ -11,6 +10,8 @@ const DB_FALLBACK_COMMANDS = Array.from(new Set(COMMAND_FIRST_KEYWORDS));
|
|
|
11
10
|
|
|
12
11
|
export type VoidFunction = () => void;
|
|
13
12
|
|
|
13
|
+
export type OnSchemaChangeCallback = ((event: { command: string; query: string }) => void);
|
|
14
|
+
|
|
14
15
|
export class SchemaWatch {
|
|
15
16
|
|
|
16
17
|
dboBuilder: DboBuilder;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getKeys, isObject } from "prostgles-types";
|
|
2
2
|
import { EVENT_TRIGGER_TAGS } from "../Event_Trigger_Tags";
|
|
3
|
-
import { ProstglesInitOptions } from "../
|
|
3
|
+
import { ProstglesInitOptions } from "../ProstglesTypes";
|
|
4
4
|
|
|
5
5
|
export const getWatchSchemaTagList = (watchSchema: ProstglesInitOptions["watchSchema"]) => {
|
|
6
6
|
if(!watchSchema) return undefined;
|
package/lib/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SessionUser } from "./AuthHandler";
|
|
2
|
-
import { Prostgles
|
|
2
|
+
import { Prostgles } from "./Prostgles";
|
|
3
|
+
import { ProstglesInitOptions } from "./ProstglesTypes";
|
|
3
4
|
|
|
4
5
|
function prostgles<S = void, SUser extends SessionUser = SessionUser>(params: ProstglesInitOptions<S, SUser>){
|
|
5
6
|
|
package/lib/initProstgles.ts
CHANGED
|
@@ -5,10 +5,11 @@ import { isEmpty, pickKeys } from "prostgles-types";
|
|
|
5
5
|
import { AuthHandler } from "./AuthHandler";
|
|
6
6
|
import { DBEventsManager } from "./DBEventsManager";
|
|
7
7
|
import { DBOFullyTyped } from "./DBSchemaBuilder";
|
|
8
|
-
import { DBHandlerServer, Prostgles,
|
|
8
|
+
import { DBHandlerServer, Prostgles, getIsSuperUser } from "./Prostgles";
|
|
9
9
|
import { DbTableInfo, PublishParser } from "./PublishParser/PublishParser";
|
|
10
10
|
import { sleep } from "./utils";
|
|
11
11
|
import { SchemaWatch } from "./SchemaWatch/SchemaWatch";
|
|
12
|
+
import { ProstglesInitOptions } from "./ProstglesTypes";
|
|
12
13
|
|
|
13
14
|
export type DbConnection = string | pg.IConnectionParameters<pg.IClient>;
|
|
14
15
|
export type DbConnectionOpts = pg.IDefaults;
|
|
@@ -58,9 +59,31 @@ export type InitResult = {
|
|
|
58
59
|
export const initProstgles = async function(this: Prostgles, onReady: OnReadyCallbackBasic, reason: OnInitReason): Promise<InitResult> {
|
|
59
60
|
this.loaded = false;
|
|
60
61
|
|
|
61
|
-
/* 1. Connect to db */
|
|
62
62
|
if (!this.db) {
|
|
63
|
-
|
|
63
|
+
let existingAppName = "";
|
|
64
|
+
let connString = "";
|
|
65
|
+
if(typeof this.opts.dbConnection === "string"){
|
|
66
|
+
connString = this.opts.dbConnection;
|
|
67
|
+
} else if(this.opts.dbConnection.connectionString){
|
|
68
|
+
connString = this.opts.dbConnection.connectionString;
|
|
69
|
+
} else {
|
|
70
|
+
existingAppName = this.opts.dbConnection.application_name ?? "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if(connString){
|
|
74
|
+
try {
|
|
75
|
+
const url = new URL(connString);
|
|
76
|
+
existingAppName = url.searchParams.get("application_name") ?? url.searchParams.get("ApplicationName") ?? "";
|
|
77
|
+
} catch (e) {
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const conObj = typeof this.opts.dbConnection === "string" ? { connectionString: this.opts.dbConnection } : this.opts.dbConnection
|
|
83
|
+
const application_name = `prostgles ${this.appId} ${existingAppName}`;
|
|
84
|
+
|
|
85
|
+
/* 1. Connect to db */
|
|
86
|
+
const { db, pgp } = getDbConnection({ ...conObj, application_name }, this.opts.dbOptions, this.opts.DEBUG_MODE,
|
|
64
87
|
notice => {
|
|
65
88
|
if (this.opts.onNotice) this.opts.onNotice(notice);
|
|
66
89
|
if (this.dbEventsManager) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prostgles-server",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.74",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"@types/pg": "^8.11.5",
|
|
54
54
|
"@types/pg-cursor": "^2.7.2",
|
|
55
55
|
"@types/sharp": "^0.30.4",
|
|
56
|
-
"@types/socket.io": "^3.0.2",
|
|
57
56
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
58
57
|
"@typescript-eslint/parser": "^5.62.0",
|
|
59
58
|
"eslint": "^8.51.0",
|
|
59
|
+
"socket.io": "^4.7.5",
|
|
60
60
|
"typescript": "^5.3.3"
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -487,11 +487,11 @@ export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log
|
|
|
487
487
|
) as { tgname: string; enabled: boolean; }[];
|
|
488
488
|
}
|
|
489
489
|
await sub.unsubscribe();
|
|
490
|
-
let validTriggers = await getTableTriggers(table_name)
|
|
491
|
-
assert.equal(validTriggers.filter(t => t.enabled).length, 3);
|
|
490
|
+
let validTriggers = await getTableTriggers(table_name);
|
|
491
|
+
assert.equal(validTriggers.filter(t => t.enabled).length, 3, '3 Triggers should be enabled');
|
|
492
492
|
await db.sql?.(`DELETE FROM prostgles.app_triggers`, []); // WHERE table_name = $1
|
|
493
|
-
validTriggers = await getTableTriggers(table_name)
|
|
494
|
-
assert.equal(validTriggers.length, 3);
|
|
493
|
+
validTriggers = await getTableTriggers(table_name);
|
|
494
|
+
assert.equal(validTriggers.length, 3, '3 Triggers should exist but be disabled');
|
|
495
495
|
assert.equal(validTriggers.filter(t => t.enabled).length, 0);
|
|
496
496
|
}
|
|
497
497
|
|