prostgles-server 3.0.64 → 3.0.67

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.
Files changed (123) hide show
  1. package/dist/AuthHandler.d.ts +11 -11
  2. package/dist/AuthHandler.d.ts.map +1 -1
  3. package/dist/DBSchemaBuilder.d.ts +3 -3
  4. package/dist/DBSchemaBuilder.d.ts.map +1 -1
  5. package/dist/DboBuilder/QueryBuilder/Functions.d.ts +3 -3
  6. package/dist/DboBuilder/QueryBuilder/Functions.d.ts.map +1 -1
  7. package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts +3 -3
  8. package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts.map +1 -1
  9. package/dist/DboBuilder/TableHandler.d.ts +1 -1
  10. package/dist/DboBuilder/TableHandler.d.ts.map +1 -1
  11. package/dist/DboBuilder/ViewHandler.d.ts +5 -4
  12. package/dist/DboBuilder/ViewHandler.d.ts.map +1 -1
  13. package/dist/DboBuilder/ViewHandler.js +7 -134
  14. package/dist/DboBuilder/ViewHandler.js.map +1 -1
  15. package/dist/DboBuilder/delete.js +1 -1
  16. package/dist/DboBuilder/delete.js.map +1 -1
  17. package/dist/DboBuilder/insert.js +1 -1
  18. package/dist/DboBuilder/insert.js.map +1 -1
  19. package/dist/DboBuilder/insertDataParse.js +1 -1
  20. package/dist/DboBuilder/insertDataParse.js.map +1 -1
  21. package/dist/DboBuilder/runSQL.js +1 -1
  22. package/dist/DboBuilder/runSQL.js.map +1 -1
  23. package/dist/DboBuilder/subscribe.d.ts +11 -0
  24. package/dist/DboBuilder/subscribe.d.ts.map +1 -0
  25. package/dist/DboBuilder/subscribe.js +190 -0
  26. package/dist/DboBuilder/subscribe.js.map +1 -0
  27. package/dist/DboBuilder/update.js +1 -1
  28. package/dist/DboBuilder/update.js.map +1 -1
  29. package/dist/DboBuilder.d.ts +24 -24
  30. package/dist/DboBuilder.d.ts.map +1 -1
  31. package/dist/DboBuilder.js +1 -1
  32. package/dist/DboBuilder.js.map +1 -1
  33. package/dist/FileManager.d.ts +6 -6
  34. package/dist/FileManager.d.ts.map +1 -1
  35. package/dist/FileManager.js +17 -17
  36. package/dist/FileManager.js.map +1 -1
  37. package/dist/Filtering.d.ts +1 -1
  38. package/dist/Filtering.d.ts.map +1 -1
  39. package/dist/PostgresNotifListenManager.d.ts +1 -1
  40. package/dist/PostgresNotifListenManager.d.ts.map +1 -1
  41. package/dist/Prostgles.d.ts +14 -14
  42. package/dist/Prostgles.d.ts.map +1 -1
  43. package/dist/Prostgles.js +12 -12
  44. package/dist/Prostgles.js.map +1 -1
  45. package/dist/{PubSubManager.d.ts → PubSubManager/PubSubManager.d.ts} +24 -21
  46. package/dist/PubSubManager/PubSubManager.d.ts.map +1 -0
  47. package/dist/PubSubManager/PubSubManager.js +754 -0
  48. package/dist/PubSubManager/PubSubManager.js.map +1 -0
  49. package/dist/PubSubManager/initPubSubManager.d.ts +3 -0
  50. package/dist/PubSubManager/initPubSubManager.d.ts.map +1 -0
  51. package/dist/PubSubManager/initPubSubManager.js +616 -0
  52. package/dist/PubSubManager/initPubSubManager.js.map +1 -0
  53. package/dist/PublishParser.d.ts +32 -32
  54. package/dist/PublishParser.d.ts.map +1 -1
  55. package/dist/PublishParser.js +1 -1
  56. package/dist/PublishParser.js.map +1 -1
  57. package/dist/SchemaWatch.d.ts +1 -1
  58. package/dist/SchemaWatch.d.ts.map +1 -1
  59. package/dist/SyncReplication.d.ts +6 -6
  60. package/dist/SyncReplication.d.ts.map +1 -1
  61. package/dist/SyncReplication.js +1 -1
  62. package/dist/SyncReplication.js.map +1 -1
  63. package/dist/TableConfig.d.ts +21 -21
  64. package/dist/TableConfig.d.ts.map +1 -1
  65. package/dist/TableConfig.js +13 -13
  66. package/dist/TableConfig.js.map +1 -1
  67. package/dist/shortestPath.d.ts +1 -1
  68. package/dist/shortestPath.d.ts.map +1 -1
  69. package/dist/validation.d.ts +9 -9
  70. package/dist/validation.d.ts.map +1 -1
  71. package/dist/validation.js +1 -1
  72. package/dist/validation.js.map +1 -1
  73. package/lib/DboBuilder/ViewHandler.d.ts +4 -3
  74. package/lib/DboBuilder/ViewHandler.d.ts.map +1 -1
  75. package/lib/DboBuilder/ViewHandler.js +7 -134
  76. package/lib/DboBuilder/ViewHandler.ts +15 -164
  77. package/lib/DboBuilder/delete.js +1 -1
  78. package/lib/DboBuilder/delete.ts +1 -1
  79. package/lib/DboBuilder/insert.js +1 -1
  80. package/lib/DboBuilder/insert.ts +1 -1
  81. package/lib/DboBuilder/insertDataParse.js +1 -1
  82. package/lib/DboBuilder/insertDataParse.ts +1 -1
  83. package/lib/DboBuilder/runSQL.js +1 -1
  84. package/lib/DboBuilder/runSQL.ts +1 -1
  85. package/lib/DboBuilder/subscribe.d.ts +11 -0
  86. package/lib/DboBuilder/subscribe.d.ts.map +1 -0
  87. package/lib/DboBuilder/subscribe.js +189 -0
  88. package/lib/DboBuilder/subscribe.ts +230 -0
  89. package/lib/DboBuilder/update.js +1 -1
  90. package/lib/DboBuilder/update.ts +1 -1
  91. package/lib/DboBuilder.d.ts +1 -1
  92. package/lib/DboBuilder.d.ts.map +1 -1
  93. package/lib/DboBuilder.js +1 -1
  94. package/lib/DboBuilder.ts +1 -1
  95. package/lib/Prostgles.js +1 -1
  96. package/lib/Prostgles.ts +1 -1
  97. package/lib/{PubSubManager.d.ts → PubSubManager/PubSubManager.d.ts} +20 -17
  98. package/lib/PubSubManager/PubSubManager.d.ts.map +1 -0
  99. package/lib/PubSubManager/PubSubManager.js +776 -0
  100. package/lib/PubSubManager/PubSubManager.ts +998 -0
  101. package/lib/PubSubManager/initPubSubManager.d.ts +3 -0
  102. package/lib/PubSubManager/initPubSubManager.d.ts.map +1 -0
  103. package/lib/PubSubManager/initPubSubManager.js +615 -0
  104. package/lib/PubSubManager/initPubSubManager.ts +630 -0
  105. package/lib/PublishParser.js +1 -1
  106. package/lib/PublishParser.ts +1 -1
  107. package/lib/SyncReplication.d.ts +1 -1
  108. package/lib/SyncReplication.d.ts.map +1 -1
  109. package/lib/SyncReplication.js +1 -1
  110. package/lib/SyncReplication.ts +1 -1
  111. package/lib/TableConfig.js +1 -1
  112. package/lib/TableConfig.ts +1 -1
  113. package/lib/validation.js +1 -1
  114. package/lib/validation.ts +1 -1
  115. package/package.json +6 -5
  116. package/tests/client/PID.txt +1 -1
  117. package/tests/server/package-lock.json +9 -7
  118. package/dist/PubSubManager.d.ts.map +0 -1
  119. package/dist/PubSubManager.js +0 -1398
  120. package/dist/PubSubManager.js.map +0 -1
  121. package/lib/PubSubManager.d.ts.map +0 -1
  122. package/lib/PubSubManager.js +0 -1420
  123. package/lib/PubSubManager.ts +0 -1655
@@ -0,0 +1,616 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initPubSubManager = void 0;
4
+ const PostgresNotifListenManager_1 = require("../PostgresNotifListenManager");
5
+ const PubSubManager_1 = require("./PubSubManager");
6
+ const REALTIME_TRIGGER_CHECK_QUERY = "prostgles-server internal query used to manage realtime triggers";
7
+ async function initPubSubManager() {
8
+ if (!this.canContinue())
9
+ return undefined;
10
+ try {
11
+ const schema_version = 6;
12
+ const initQuery = `
13
+ BEGIN; -- ISOLATION LEVEL SERIALIZABLE;-- TRANSACTION ISOLATION LEVEL SERIALIZABLE;
14
+
15
+ --SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
16
+
17
+ /*
18
+ * ${PubSubManager_1.PubSubManager.EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID}
19
+ */
20
+
21
+ DO
22
+ $do$
23
+ BEGIN
24
+
25
+ /* Reduce deadlocks */
26
+ PERFORM pg_sleep(random());
27
+
28
+ /* Drop older version */
29
+ IF EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'prostgles') THEN
30
+
31
+ IF
32
+ NOT EXISTS (
33
+ SELECT 1
34
+ FROM information_schema.tables
35
+ WHERE table_schema = 'prostgles'
36
+ AND table_name = 'versions'
37
+ )
38
+ THEN
39
+ DROP SCHEMA IF EXISTS prostgles CASCADE;
40
+ ELSE
41
+ IF NOT EXISTS(SELECT 1 FROM prostgles.versions WHERE version >= ${schema_version}) THEN
42
+ DROP SCHEMA IF EXISTS prostgles CASCADE;
43
+ END IF;
44
+ END IF;
45
+
46
+ END IF;
47
+
48
+
49
+ IF NOT EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'prostgles')
50
+ THEN
51
+ --RAISE NOTICE 'CREATE SCHEMA IF NOT EXISTS prostgles';
52
+
53
+ CREATE SCHEMA IF NOT EXISTS prostgles;
54
+
55
+ CREATE TABLE IF NOT EXISTS prostgles.versions(
56
+ version NUMERIC PRIMARY KEY
57
+ );
58
+ INSERT INTO prostgles.versions(version) VALUES(${schema_version}) ON CONFLICT DO NOTHING;
59
+
60
+ CREATE OR REPLACE FUNCTION prostgles.random_string(length INTEGER DEFAULT 33) RETURNS TEXT AS $$
61
+ DECLARE
62
+ chars TEXT[] := '{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}';
63
+ result TEXT := '';
64
+ i INTEGER := 0;
65
+ BEGIN
66
+ IF length < 0 THEN
67
+ RAISE exception 'Given length cannot be less than 0';
68
+ END IF;
69
+ FOR i IN 1..length LOOP
70
+ result := result || chars[1+random()*(array_length(chars, 1)-1)];
71
+ END LOOP;
72
+ RETURN result;
73
+ END;
74
+ $$ language plpgsql;
75
+ COMMENT ON FUNCTION prostgles.random_string IS 'UUIDs without installing pgcrypto';
76
+
77
+
78
+ CREATE OR REPLACE FUNCTION prostgles.debug(VARIADIC args TEXT[]) RETURNS VOID AS $$
79
+ BEGIN
80
+
81
+ --PERFORM pg_notify('debug', concat_ws(' ', args));
82
+ IF
83
+ NOT EXISTS (
84
+ SELECT 1
85
+ FROM information_schema.tables
86
+ WHERE table_schema = 'prostgles'
87
+ AND table_name = 'debug'
88
+ )
89
+ THEN
90
+ CREATE TABLE IF NOT EXISTS prostgles.debug(m TEXT);
91
+ END IF;
92
+
93
+ INSERT INTO prostgles.debug(m) VALUES(concat_ws(' ', args));
94
+
95
+ END;
96
+ $$ LANGUAGE plpgsql;
97
+ COMMENT ON FUNCTION prostgles.debug IS 'Used for internal debugging';
98
+
99
+
100
+ CREATE TABLE IF NOT EXISTS prostgles.apps (
101
+ id TEXT PRIMARY KEY DEFAULT prostgles.random_string(),
102
+ added TIMESTAMP DEFAULT NOW(),
103
+ application_name TEXT,
104
+ last_check TIMESTAMP NOT NULL DEFAULT NOW(),
105
+ last_check_ended TIMESTAMP NOT NULL DEFAULT NOW(),
106
+ watching_schema BOOLEAN DEFAULT FALSE,
107
+ check_frequency_ms INTEGER NOT NULL
108
+ );
109
+ COMMENT ON TABLE prostgles.apps IS 'Keep track of prostgles server apps connected to db to combine common triggers. Heartbeat used due to no logout triggers in postgres';
110
+
111
+ CREATE TABLE IF NOT EXISTS prostgles.app_triggers (
112
+ app_id TEXT NOT NULL,
113
+ table_name TEXT NOT NULL,
114
+ condition TEXT NOT NULL,
115
+
116
+ /* The view from the root subscription, found in the condition.
117
+ We need this because old_table/new_table data is not reflected in the view inside the AFTER trigger
118
+ */
119
+ related_view_name TEXT,
120
+ related_view_def TEXT, /* view definition */
121
+
122
+ inserted TIMESTAMP NOT NULL DEFAULT NOW(),
123
+ last_used TIMESTAMP NOT NULL DEFAULT NOW(),
124
+ PRIMARY KEY (app_id, table_name, condition) /* This unqique index limits the condition column value to be less than 'SELECT current_setting('block_size'); */
125
+ );
126
+ COMMENT ON TABLE prostgles.app_triggers IS 'Tables and conditions that are currently subscribed/synced';
127
+
128
+
129
+ CREATE OR REPLACE VIEW prostgles.v_triggers AS
130
+ SELECT *
131
+ , (ROW_NUMBER() OVER( ORDER BY table_name, condition ))::text AS id
132
+ , ROW_NUMBER() OVER(PARTITION BY app_id, table_name ORDER BY table_name, condition ) - 1 AS c_id
133
+ FROM prostgles.app_triggers;
134
+ COMMENT ON VIEW prostgles.v_triggers IS 'Augment trigger table with natural IDs and per app IDs';
135
+
136
+
137
+ CREATE OR REPLACE FUNCTION ${this.DB_OBJ_NAMES.data_watch_func}() RETURNS TRIGGER
138
+ AS $$
139
+
140
+ DECLARE t_ids TEXT[];
141
+ DECLARE c_ids INTEGER[];
142
+ DECLARE err_c_ids INTEGER[];
143
+ DECLARE unions TEXT := '';
144
+ DECLARE query TEXT := '';
145
+ DECLARE nrw RECORD;
146
+ DECLARE erw RECORD;
147
+ DECLARE has_errors BOOLEAN := FALSE;
148
+
149
+ DECLARE err_text TEXT;
150
+ DECLARE err_detail TEXT;
151
+ DECLARE err_hint TEXT;
152
+
153
+ BEGIN
154
+
155
+ -- PERFORM pg_notify('debug', concat_ws(' ', 'TABLE', TG_TABLE_NAME, TG_OP));
156
+
157
+ SELECT string_agg(
158
+ concat_ws(
159
+ E' UNION \n ',
160
+ CASE WHEN (TG_OP = 'DELETE' OR TG_OP = 'UPDATE') THEN (p1 || ' old_table ' || p2) END,
161
+ CASE WHEN (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN (p1 || ' new_table ' || p2) END
162
+ ),
163
+ E' UNION \n '::text
164
+ )
165
+ INTO unions
166
+ FROM (
167
+ SELECT
168
+ CASE WHEN related_view_name IS NOT NULL
169
+ /* E' is used to ensure line breaks are not escaped */
170
+ THEN format(E'WITH %I AS (\n %s \n) ', 'view_nDme', 'select 1 as view_definition')
171
+ ELSE
172
+ ''
173
+ END ||
174
+ $z$
175
+ SELECT CASE WHEN EXISTS( SELECT 1 FROM
176
+ $z$ AS p1,
177
+ format(
178
+ $c$
179
+ as %I WHERE %s ) THEN %s::text END AS t_ids
180
+ $c$,
181
+ table_name, condition, id
182
+ ) AS p2
183
+ FROM prostgles.v_triggers
184
+ WHERE table_name = TG_TABLE_NAME
185
+ ) t;
186
+
187
+ IF unions IS NOT NULL THEN
188
+ query = format(
189
+ $s$
190
+ SELECT ARRAY_AGG(DISTINCT t.t_ids)
191
+ FROM ( %s ) t
192
+ $s$,
193
+ unions
194
+ );
195
+
196
+ BEGIN
197
+ EXECUTE query INTO t_ids;
198
+
199
+ --RAISE NOTICE 'trigger fired ok';
200
+
201
+ EXCEPTION WHEN OTHERS THEN
202
+
203
+ has_errors := TRUE;
204
+
205
+ GET STACKED DIAGNOSTICS
206
+ err_text = MESSAGE_TEXT,
207
+ err_detail = PG_EXCEPTION_DETAIL,
208
+ err_hint = PG_EXCEPTION_HINT;
209
+
210
+
211
+ END;
212
+
213
+ --RAISE NOTICE 'has_errors: % ', has_errors;
214
+ --RAISE NOTICE 'unions: % , cids: %', unions, c_ids;
215
+
216
+ IF (t_ids IS NOT NULL OR has_errors) THEN
217
+
218
+ FOR nrw IN
219
+ SELECT app_id, string_agg(c_id::text, ',') as cids
220
+ FROM prostgles.v_triggers
221
+ WHERE id = ANY(t_ids)
222
+ OR has_errors
223
+ GROUP BY app_id
224
+ LOOP
225
+
226
+ PERFORM pg_notify(
227
+ ${(0, PubSubManager_1.asValue)(this.NOTIF_CHANNEL.preffix)} || nrw.app_id ,
228
+ concat_ws(
229
+ ${(0, PubSubManager_1.asValue)(PubSubManager_1.PubSubManager.DELIMITER)},
230
+
231
+ ${(0, PubSubManager_1.asValue)(this.NOTIF_TYPE.data)},
232
+ COALESCE(TG_TABLE_NAME, 'MISSING'),
233
+ COALESCE(TG_OP, 'MISSING'),
234
+ CASE WHEN has_errors
235
+ THEN concat_ws('; ', 'error', err_text, err_detail, err_hint )
236
+ ELSE COALESCE(nrw.cids, '')
237
+ END
238
+ ${this.dboBuilder.prostgles.opts.DEBUG_MODE ? ("--, (select json_agg(t)::TEXT FROM (SELECT * from old_table) t), query") : ""}
239
+ )
240
+ );
241
+ END LOOP;
242
+
243
+
244
+ IF has_errors THEN
245
+
246
+ DELETE FROM prostgles.app_triggers;
247
+ RAISE NOTICE 'trigger dropped due to exception: % % %', err_text, err_detail, err_hint;
248
+
249
+ END IF;
250
+
251
+
252
+ END IF;
253
+ END IF;
254
+
255
+
256
+ RETURN NULL;
257
+
258
+ /*
259
+ EXCEPTION WHEN OTHERS THEN
260
+ DELETE FROM prostgles.app_triggers; -- delete all or will need to loop through all conditions to find issue;
261
+ RAISE NOTICE 'trigger dropped due to exception';
262
+ ${"--EXCEPTION_WHEN_COLUMN_WAS_RENAMED_THEN_DROP_TRIGGER"};
263
+
264
+
265
+
266
+ RETURN NULL;
267
+ */
268
+ END;
269
+
270
+ --COMMIT;
271
+ $$ LANGUAGE plpgsql;
272
+ COMMENT ON FUNCTION ${this.DB_OBJ_NAMES.data_watch_func} IS 'Prostgles internal function used to notify when data in the table changed';
273
+
274
+
275
+
276
+ CREATE OR REPLACE FUNCTION ${this.DB_OBJ_NAMES.trigger_add_remove_func}() RETURNS TRIGGER
277
+ AS $$
278
+
279
+ DECLARE operations TEXT[] := ARRAY['insert', 'update', 'delete'];
280
+ DECLARE op TEXT;
281
+ DECLARE query TEXT;
282
+ DECLARE trw RECORD;
283
+
284
+ BEGIN
285
+
286
+
287
+ --RAISE NOTICE 'prostgles.app_triggers % ', TG_OP;
288
+
289
+ /* If no other listeners on table then DROP triggers */
290
+ IF TG_OP = 'DELETE' THEN
291
+
292
+ --RAISE NOTICE 'DELETE trigger_add_remove_func table: % ', ' ' || COALESCE((SELECT concat_ws(' ', string_agg(table_name, ' & '), count(*), min(inserted) ) FROM prostgles.app_triggers) , ' 0 ');
293
+ --RAISE NOTICE 'DELETE trigger_add_remove_func old_table: % ', '' || COALESCE((SELECT concat_ws(' ', string_agg(table_name, ' & '), count(*), min(inserted) ) FROM old_table), ' 0 ');
294
+
295
+
296
+ /* Drop actual triggers if needed */
297
+ FOR trw IN
298
+ SELECT DISTINCT table_name FROM old_table ot
299
+ WHERE NOT EXISTS (
300
+ SELECT 1 FROM prostgles.app_triggers t
301
+ WHERE t.table_name = ot.table_name
302
+ )
303
+ LOOP
304
+
305
+ FOREACH op IN ARRAY operations
306
+ LOOP
307
+ --RAISE NOTICE ' DROP DATA TRIGGER FOR: % ', trw.table_name;
308
+ EXECUTE format(' DROP TRIGGER IF EXISTS %I ON %I ;' , 'prostgles_triggers_' || trw.table_name || '_' || op, trw.table_name);
309
+ END LOOP;
310
+
311
+ END LOOP;
312
+
313
+ /* If newly added listeners on table then CREATE triggers */
314
+ ELSIF TG_OP = 'INSERT' THEN
315
+
316
+
317
+ --RAISE NOTICE 'INSERT trigger_add_remove_func table: % ', ' ' || COALESCE((SELECT concat_ws(' ', string_agg(table_name, ' & '), count(*), min(inserted) ) FROM prostgles.triggers) , ' 0 ');
318
+ --RAISE NOTICE 'INSERT trigger_add_remove_func new_table: % ', '' || COALESCE((SELECT concat_ws(' ', string_agg(table_name, ' & '), count(*), min(inserted) ) FROM new_table), ' 0 ');
319
+
320
+ /* Loop through newly added tables */
321
+ FOR trw IN
322
+
323
+ SELECT DISTINCT table_name
324
+ FROM new_table nt
325
+
326
+ /* Table did not exist prior to this insert */
327
+ WHERE NOT EXISTS (
328
+ SELECT 1
329
+ FROM prostgles.app_triggers t
330
+ WHERE t.table_name = nt.table_name
331
+ AND t.inserted < nt.inserted -- exclude current record (this is an after trigger). Turn into before trigger?
332
+ )
333
+
334
+ /* Table is valid */
335
+ AND EXISTS (
336
+ SELECT 1
337
+ FROM information_schema.tables
338
+ WHERE table_schema = 'public'
339
+ AND table_name = nt.table_name
340
+ )
341
+ LOOP
342
+
343
+ /*
344
+ RAISE NOTICE ' CREATE DATA TRIGGER FOR: % TABLE EXISTS?', trw.table_name, SELECT EXISTS (
345
+ SELECT 1
346
+ FROM information_schema.tables
347
+ WHERE table_schema = 'public'
348
+ AND table_name = nt.table_name
349
+ );
350
+ */
351
+
352
+ query := format(
353
+ $q$
354
+ DROP TRIGGER IF EXISTS %1$I ON %2$I;
355
+ CREATE TRIGGER %1$I
356
+ AFTER INSERT ON %2$I
357
+ REFERENCING NEW TABLE AS new_table
358
+ FOR EACH STATEMENT EXECUTE PROCEDURE ${this.DB_OBJ_NAMES.data_watch_func}();
359
+ COMMENT ON TRIGGER %1$I ON %2$I IS 'Prostgles internal trigger used to notify when data in the table changed';
360
+ $q$,
361
+ 'prostgles_triggers_' || trw.table_name || '_insert', trw.table_name
362
+ ) || format(
363
+ $q$
364
+ DROP TRIGGER IF EXISTS %1$I ON %2$I;
365
+ CREATE TRIGGER %1$I
366
+ AFTER UPDATE ON %2$I
367
+ REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
368
+ FOR EACH STATEMENT EXECUTE PROCEDURE ${this.DB_OBJ_NAMES.data_watch_func}();
369
+ COMMENT ON TRIGGER %1$I ON %2$I IS 'Prostgles internal trigger used to notify when data in the table changed';
370
+ $q$,
371
+ 'prostgles_triggers_' || trw.table_name || '_update', trw.table_name
372
+ ) || format(
373
+ $q$
374
+ DROP TRIGGER IF EXISTS %1$I ON %2$I;
375
+ CREATE TRIGGER %1$I
376
+ AFTER DELETE ON %2$I
377
+ REFERENCING OLD TABLE AS old_table
378
+ FOR EACH STATEMENT EXECUTE PROCEDURE ${this.DB_OBJ_NAMES.data_watch_func}();
379
+ COMMENT ON TRIGGER %1$I ON %2$I IS 'Prostgles internal trigger used to notify when data in the table changed';
380
+ $q$,
381
+ 'prostgles_triggers_' || trw.table_name || '_delete', trw.table_name
382
+ );
383
+
384
+ --RAISE NOTICE ' % ', query;
385
+
386
+
387
+ query := format(
388
+ $q$
389
+ DO $e$
390
+ BEGIN
391
+
392
+ IF EXISTS (
393
+ SELECT 1
394
+ FROM information_schema.tables
395
+ WHERE table_schema = 'public'
396
+ AND table_name = %L
397
+ ) THEN
398
+
399
+ %s
400
+
401
+ END IF;
402
+
403
+ END $e$;
404
+ $q$,
405
+ trw.table_name,
406
+ query
407
+ ) ;
408
+
409
+
410
+ EXECUTE query;
411
+
412
+ END LOOP;
413
+
414
+ END IF;
415
+
416
+
417
+ RETURN NULL;
418
+ END;
419
+
420
+ $$ LANGUAGE plpgsql;
421
+ COMMENT ON FUNCTION ${this.DB_OBJ_NAMES.trigger_add_remove_func} IS 'Used to add/remove table watch triggers concurrently ';
422
+
423
+ DROP TRIGGER IF EXISTS prostgles_triggers_insert ON prostgles.app_triggers;
424
+ CREATE TRIGGER prostgles_triggers_insert
425
+ AFTER INSERT ON prostgles.app_triggers
426
+ REFERENCING NEW TABLE AS new_table
427
+ FOR EACH STATEMENT EXECUTE PROCEDURE ${this.DB_OBJ_NAMES.trigger_add_remove_func}();
428
+
429
+ DROP TRIGGER IF EXISTS prostgles_triggers_delete ON prostgles.app_triggers;
430
+ CREATE TRIGGER prostgles_triggers_delete
431
+ AFTER DELETE ON prostgles.app_triggers
432
+ REFERENCING OLD TABLE AS old_table
433
+ FOR EACH STATEMENT EXECUTE PROCEDURE ${this.DB_OBJ_NAMES.trigger_add_remove_func}();
434
+
435
+
436
+ CREATE OR REPLACE FUNCTION ${this.DB_OBJ_NAMES.schema_watch_func}() RETURNS event_trigger AS $$
437
+
438
+ DECLARE curr_query TEXT := '';
439
+ DECLARE arw RECORD;
440
+
441
+ BEGIN
442
+
443
+ --RAISE NOTICE 'SCHEMA_WATCH: %', tg_tag;
444
+
445
+ /*
446
+ This event trigger will outlive a prostgles app instance.
447
+ Must ensure it only fires if an app instance is running
448
+ */
449
+ IF
450
+ EXISTS (
451
+ SELECT 1
452
+ FROM information_schema.tables
453
+ WHERE table_schema = 'prostgles'
454
+ AND table_name = 'apps'
455
+ )
456
+ THEN
457
+
458
+ SELECT LEFT(COALESCE(current_query(), ''), 5000)
459
+ INTO curr_query;
460
+
461
+ FOR arw IN
462
+ SELECT * FROM prostgles.apps WHERE watching_schema IS TRUE
463
+
464
+ LOOP
465
+ PERFORM pg_notify(
466
+ ${(0, PubSubManager_1.asValue)(this.NOTIF_CHANNEL.preffix)} || arw.id,
467
+ concat_ws(
468
+ ${(0, PubSubManager_1.asValue)(PubSubManager_1.PubSubManager.DELIMITER)},
469
+ ${(0, PubSubManager_1.asValue)(this.NOTIF_TYPE.schema)}, tg_tag , TG_event, curr_query
470
+ )
471
+ );
472
+ END LOOP;
473
+
474
+ END IF;
475
+
476
+ END;
477
+ $$ LANGUAGE plpgsql;
478
+ COMMENT ON FUNCTION ${this.DB_OBJ_NAMES.schema_watch_func} IS 'Prostgles internal function used to notify when schema has changed';
479
+
480
+ END IF;
481
+
482
+ END
483
+ $do$;
484
+
485
+
486
+ COMMIT;
487
+ `;
488
+ // const prgl_exists = await this.db.oneOrNone(`
489
+ // DROP SCHEMA IF EXISTS prostgles CASCADE;
490
+ // SELECT 1 FROM information_schema.schemata WHERE schema_name = 'prostgles'
491
+ // `);
492
+ // if(!prgl_exists){
493
+ // await this.db.any(q);
494
+ // }
495
+ await this.db.any(initQuery);
496
+ if (!this.canContinue())
497
+ return;
498
+ /* Prepare App id */
499
+ if (!this.appID) {
500
+ const raw = await this.db.one("INSERT INTO prostgles.apps (check_frequency_ms, watching_schema, application_name) VALUES($1, $2, current_setting('application_name')) RETURNING *; ", [this.appCheckFrequencyMS, Boolean(this.onSchemaChange)]);
501
+ this.appID = raw.id;
502
+ if (!this.appCheck) {
503
+ this.appCheck = setInterval(async () => {
504
+ let appQ = "";
505
+ try { // drop owned by api
506
+ this.appChecking = true;
507
+ let trgUpdateLastUsed = "", listeners = this.getActiveListeners();
508
+ if (listeners.length) {
509
+ trgUpdateLastUsed = `
510
+ UPDATE prostgles.app_triggers
511
+ SET last_used = CASE WHEN (table_name, condition) IN (
512
+ ${listeners.map(l => ` ( ${(0, PubSubManager_1.asValue)(l.table_name)}, ${(0, PubSubManager_1.asValue)(l.condition)} ) `).join(", ")}
513
+ ) THEN NOW() ELSE last_used END
514
+ WHERE app_id = ${(0, PubSubManager_1.asValue)(this.appID)};
515
+ `;
516
+ }
517
+ appQ = `
518
+
519
+ DO $$
520
+ BEGIN
521
+
522
+ /* ${REALTIME_TRIGGER_CHECK_QUERY} */
523
+ /* prostgles schema must exist */
524
+ IF
525
+ EXISTS (
526
+ SELECT 1
527
+ FROM information_schema.tables
528
+ WHERE table_schema = 'prostgles'
529
+ AND table_name = 'apps'
530
+ )
531
+ THEN
532
+
533
+
534
+ /* Concurrency control to avoid deadlock
535
+ IF NOT EXISTS (
536
+ SELECT 1 FROM prostgles.apps
537
+ WHERE last_check < last_check_ended
538
+ AND last_check_ended > NOW() - interval '5 minutes'
539
+ ) THEN
540
+ */
541
+ UPDATE prostgles.apps
542
+ SET last_check = NOW()
543
+ WHERE id = ${(0, PubSubManager_1.asValue)(this.appID)};
544
+
545
+
546
+
547
+ /* Delete unused triggers. Might deadlock */
548
+ IF EXISTS ( SELECT 1 FROM prostgles.app_triggers)
549
+
550
+ /* If this is the latest app then proceed
551
+ AND (
552
+ SELECT id = ${(0, PubSubManager_1.asValue)(this.appID)}
553
+ FROM prostgles.apps
554
+ ORDER BY last_check DESC
555
+ LIMIT 1
556
+ ) = TRUE
557
+ */
558
+
559
+ THEN
560
+
561
+ /* TODO: Fixed deadlocks */
562
+ --LOCK TABLE prostgles.app_triggers IN ACCESS EXCLUSIVE MODE;
563
+
564
+ /* UPDATE currently used triggers */
565
+ ${trgUpdateLastUsed}
566
+
567
+ /* DELETE stale triggers for current app. Other triggers will be deleted on app startup */
568
+ DELETE FROM prostgles.app_triggers
569
+ WHERE app_id = ${(0, PubSubManager_1.asValue)(this.appID)}
570
+ AND last_used < NOW() - 4 * ${(0, PubSubManager_1.asValue)(this.appCheckFrequencyMS)} * interval '1 millisecond'; -- 10 seconds at the moment
571
+
572
+ END IF;
573
+
574
+
575
+
576
+ UPDATE prostgles.apps
577
+ SET last_check_ended = NOW()
578
+ WHERE id = ${(0, PubSubManager_1.asValue)(this.appID)};
579
+
580
+ /*
581
+ END IF;
582
+ */
583
+
584
+
585
+ END IF;
586
+
587
+ -- must not commit without a lock
588
+ --COMMIT;
589
+ END $$;
590
+ `;
591
+ await this.db.any(appQ);
592
+ (0, PubSubManager_1.log)("updated last_check");
593
+ }
594
+ catch (e) {
595
+ /** In some cases a query idles and blocks everything else. Terminate all similar queries */
596
+ this.db.any("SELECT state, pg_terminate_backend(pid) from pg_stat_activity WHERE query ilike ${qid} and pid <> pg_backend_pid();", { qid: "%" + REALTIME_TRIGGER_CHECK_QUERY + "%" });
597
+ /** If this database was dropped then stop interval */
598
+ if (e?.code === "3D000") { // && e.message.includes(this.db.$cn.database)
599
+ clearInterval(this.appCheck);
600
+ }
601
+ console.error("appCheck FAILED: \n", e, appQ);
602
+ }
603
+ this.appChecking = false;
604
+ }, 0.8 * this.appCheckFrequencyMS);
605
+ }
606
+ }
607
+ this.postgresNotifListenManager = new PostgresNotifListenManager_1.PostgresNotifListenManager(this.db, this.notifListener, this.NOTIF_CHANNEL.getFull());
608
+ await this.prepareTriggers();
609
+ return this;
610
+ }
611
+ catch (e) {
612
+ console.error("PubSubManager init failed: ", e);
613
+ }
614
+ }
615
+ exports.initPubSubManager = initPubSubManager;
616
+ //# sourceMappingURL=initPubSubManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initPubSubManager.js","sourceRoot":"","sources":["../../lib/PubSubManager/initPubSubManager.ts"],"names":[],"mappings":"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"}